Статьи

Интеграция Stripe в ваше приложение для Android

В этом уроке я покажу, как разрешить пользователям покупать товары или услуги из вашего приложения с помощью Stripe . Stripe — это один из самых простых способов управления вашими онлайн-продуктами, заказами и платежами.

Вы можете найти код для этого урока на Github .

Полоса приборной панели

В конце этого руководства пользователи смогут покупать подписки на планы. Первый шаг — создание простых планов.

Для начала войдите в Stripe (или создайте аккаунт, если вы этого еще не сделали). Убедитесь, что вы находитесь в тестовом режиме, прежде чем создавать планы на приборной панели .

Stripe Dashboard in Test Mode

Stripe Plan information

Ниже я создал 3 плана подписки: еженедельно , ежемесячно и ежегодно . У них есть некоторая объемная информация и цены, которые нужно отличать друг от друга.

All Plans

Настройка проекта Android

Создайте новый проект в Android Studio и добавьте эти строки в зависимости в файле build.gradle :

compile ('com.stripe:stripe-android:1.0.4@aar'){
    transitive=true
}

Поскольку этому приложению потребуется подключение к Интернету, добавьте следующее разрешение пользователя в файл AndroidManifest.xml :

 <uses-permission android:name="android.permission.INTERNET" />

Загрузка продуктов

Чтобы загрузить наши планы с информационной панели Stripe, нам сначала нужно подключить наше приложение к нему с помощью API-ключей .

Всего 4 ключа, но мы собираемся использовать только тестовые ключи. Теперь откройте MainActivity.class и добавьте эти объявления перед onCreate()

 ArrayList<SimplePlan> planArrayList;
RecyclerView recyclerView;
ItemsAdapter adapter;

Затем внутри onCreate()ArrayList :

     planArrayList = new ArrayList<>();

    new Async().execute();

Внутри класса Main нам нужно создать AysncTask Эта задача загрузит данные с чередования и будет запущена в новом потоке, поэтому основной поток не будет нарушен событиями сетевого подключения.

Добавьте этот класс в основной код активности:

 public class Async extends AsyncTask<Void,String,ArrayList<SimplePlan>> {

        @Override
        protected ArrayList<SimplePlan> doInBackground(Void... params) {

            try {
            String line, newjson = "";
            URL url = new URL("[YOUR_SERVER_PLANS_SCRIPT_URL]");
            try (BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream(), "UTF-8"))) {
                while ((line = reader.readLine()) != null) {
                    newjson += line;
                }
                String json = newjson.toString();
                JSONObject jObj = new JSONObject(json);
                Log.e("Obj",jObj.toString());
                JSONArray plans = jObj.getJSONArray("plans");
                for (int i=0;i<plans.length();i++){
                    JSONObject plan = plans.getJSONObject(i);
                    plan.getString("amount");
                    Log.e("Amount",plan.getString("amount"));
                    planArrayList.add(new SimplePlan(plan.getInt("amount"),plan.getString("name"),plan.getString("statement_descriptor")));
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        return planArrayList;
    }

        @Override
        protected void onPostExecute(final ArrayList<SimplePlan> plan) {
            super.onPostExecute(plan);
            new Handler().postDelayed(new Runnable() {
                @Override
                public void run() {
                    showRcv(plan);
                }
            },3000);
        }
    }

Этот код извлекает все планы из API Stripe и добавляет их в ArrayList с именем planArrayList . Все эти инструкции выполняются в фоновом режиме . Эта функция doInBackground Другая функция onPostExecute(final ArrayList<SimplePlan> plans)

В качестве параметра требуется тот же ArrayList, который возвращается из первого метода. Поскольку мы не совсем уверены, сколько может длиться первый вызов в секундах, лучше запустить второй метод через определенный промежуток времени, после которого мы уверены, что вызов завершен. Этот период составляет 3000 мс .

По истечении этого времени эта функция запускает еще одну, которая называется showRcv(plans) Вы можете найти его код ниже:

 public void showRcv(ArrayList<SimplePlan> plans){
        adapter = new ItemsAdapter(this,plans);
        recyclerView = (RecyclerView)findViewById(R.id.recyclerView);
        recyclerView.setLayoutManager(new LinearLayoutManager(this));
        recyclerView.setAdapter(adapter);
    }

До сих пор мы просто перечисляем планы внутри приложения. Я не буду фокусироваться на адаптере RecyclerView и коде ViewHolder, но вы можете найти их в Github .

Вот как будут перечислены планы:

Plan Layout

Каждый элемент, указанный в основном упражнении, имеет кнопку « Купить» справа.

На каждой кнопке покупки есть OnClickListener :

 holder.buy.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent buyIntent = new Intent(activity,PayActivity.class);
                buyIntent.putExtra("plan_id",""+planArrayList.get(i).getId());
                buyIntent.putExtra("plan_price",planArrayList.get(i).getAmount());
                buyIntent.putExtra("plan_name",""+planArrayList.get(i).getName());
                activity.startActivity(buyIntent);
            }
        });

При нажатии кнопки две переменные передаются как дополнительные функции : plan_price и plan_name . Это самые важные для создания заряда.

Прием платежей

Щелкните правой кнопкой мыши на каталоге вашего пакета и создайте новое пустое действие . Это действие откроется после того, как любой из планов будет выбран для покупки.
Его код xml

 <?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.theodhor.stripeandroid.PayActivity">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="20dp">


        <EditText
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:id="@+id/cardNumber"
            android:layout_alignParentTop="true"
            android:layout_alignParentLeft="true"
            android:layout_alignParentStart="true"
            android:layout_alignParentRight="true"
            android:layout_alignParentEnd="true"
            android:text="4242 4242 4242 4242" />

        <EditText
            android:layout_width="30dp"
            android:layout_height="wrap_content"
            android:inputType="number"
            android:ems="10"
            android:id="@+id/month"
            android:layout_below="@+id/cardNumber"
            android:layout_alignParentLeft="true"
            android:layout_alignParentStart="true"
            android:text="12" />

        <EditText
            android:layout_width="30dp"
            android:layout_height="wrap_content"
            android:inputType="number"
            android:ems="10"
            android:id="@+id/year"
            android:text="19"
            android:layout_below="@+id/cardNumber"
            android:layout_toRightOf="@+id/textView"
            android:layout_toEndOf="@+id/textView" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="36dp"
            android:textAppearance="?android:attr/textAppearanceLarge"
            android:text="/"
            android:id="@+id/textView"
            android:layout_alignBottom="@+id/month"
            android:layout_toRightOf="@+id/month"
            android:layout_toEndOf="@+id/month" />

        <EditText
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:id="@+id/cvc"
            android:text="123"
            android:layout_below="@+id/cardNumber"
            android:layout_toRightOf="@+id/year"
            android:layout_toEndOf="@+id/year"
            android:layout_marginLeft="49dp"
            android:layout_marginStart="49dp" />

        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Submit"
            android:id="@+id/submitButton"
            android:layout_below="@+id/cvc"
            android:layout_alignParentRight="true"
            android:layout_alignParentEnd="true"
            android:onClick="submitCard" />

    </RelativeLayout>

</RelativeLayout>

Платная деятельность

Нам нужно объявить некоторые переменные внутри этого класса.

 Stripe stripe;
Integer amount;
String name;
Card card;
Token tok;

Теперь внутри onCreate()Buy ранее. Сумма и имя цены плана будут использованы позже, когда будет создан сбор.

 Bundle extras = getIntent().getExtras();
amount = extras.getInt("plan_price");
name = extras.getString("plan_name");


    Also, a Stripe instance will be created using the *Publishable Test Key*:

try {
        stripe = new Stripe("[YOUR_PK_TEST_KEY_HERE]");
                } catch (AuthenticationException e) {
        e.printStackTrace();
}

Перед созданием платежа информация Карты должна быть проверена.
Этот метод sumbitCard(View view)токен карты, который будет использоваться для оплаты.

 public void submitCard(View view) {
        // TODO: replace with your own test key
        TextView cardNumberField = (TextView) findViewById(R.id.cardNumber);
        TextView monthField = (TextView) findViewById(R.id.month);
        TextView yearField = (TextView) findViewById(R.id.year);
        TextView cvcField = (TextView) findViewById(R.id.cvc);

        card = new Card(
                cardNumberField.getText().toString(),
                Integer.valueOf(monthField.getText().toString()),
                Integer.valueOf(yearField.getText().toString()),
                cvcField.getText().toString()
        );

        card.setCurrency("usd");
        card.setName("[NAME_SURNAME]");
        card.setAddressZip("[ZIP]");
        /*
        card.setNumber("4242424242424242");
        card.setExpMonth(12);
        card.setExpYear(19);
        card.setCVC("123");
        */


        stripe.createToken(card, "[YOUR_PK_TEST_KEY_HERE]", new TokenCallback() {
            public void onSuccess(Token token) {
                // TODO: Send Token information to your backend to initiate a charge
                Toast.makeText(getApplicationContext(), "Token created: " + token.getId(), Toast.LENGTH_LONG).show();
                tok = token;
                new StripeCharge(token.getId()).execute();
            }

            public void onError(Exception error) {
                Log.d("Stripe", error.getLocalizedMessage());
            }
        });

Информация о тестировании по умолчанию для действительной карты показана ниже:

Valid Test Card Information

но вы можете установить значения с помощью:

 card.setNumber("4242424242424242");
card.setExpMonth(12);
card.setExpYear(19);
card.setCVC("123"); 

Таким образом, после проверки карты и генерирования токена можно создать заряд. Заряд создается вашим серверным скриптом charge.php Поэтому нам нужно отправить данные на сервер, включая токен проверки карты , и они не должны выполняться в главном потоке .

Внутри PayActivity.class создайте новый класс AsyncTask с именем StripeCharge :

 public class StripeCharge extends AsyncTask<String, Void, String> {
    String token;

    public StripeCharge(String token) {
        this.token = token;
    }

    @Override
    protected String doInBackground(String... params) {
        new Thread() {
            @Override
            public void run() {
                postData(name,token,""+amount);
            }
        }.start();
        return "Done";
    }

    @Override
    protected void onPostExecute(String s) {
        super.onPostExecute(s);
        Log.e("Result",s);
    }
}

Наиболее важными параметрами модели оплаты являются сумма , валюта и карта, идентифицируемая идентификатором токена .

Следующий метод выполняет вызов на сервер, публикуя необходимые данные для создания платы:

 public void postData(String description, String token,String amount) {
        // Create a new HttpClient and Post Header
        try {
            URL url = new URL("[YOUR_SERVER_CHARGE_SCRIPT_URL]");
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setReadTimeout(10000);
            conn.setConnectTimeout(15000);
            conn.setRequestMethod("POST");
            conn.setDoInput(true);
            conn.setDoOutput(true);

            List<NameValuePair> params = new ArrayList<NameValuePair>();
            params.add(new NameValuePair("method", "charge"));
            params.add(new NameValuePair("description", description));
            params.add(new NameValuePair("source", token));
            params.add(new NameValuePair("amount", amount));

            OutputStream os = null;

            os = conn.getOutputStream();
            BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(os, "UTF-8"));
            writer.write(getQuery(params));
            writer.flush();
            writer.close();
            os.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

Если зарядка прошла успешно, вы должны увидеть сумму, переведенную в общий объем Stripe Dashboard, как показано ниже:

Stripe Total Volume

Серверный скрипт (PHP)

Быть осторожен! Ваш секретный ключ API должен существовать только в ваших серверных сценариях. В целях безопасности не включайте его в код на стороне клиента.

retrieveplans.php

 <?php
define('STRIPE_SECRET_KEY','[YOUR_SECRET_API_KEY]');
define('STRIPE_PUBLIC_KEY','[YOUR_PUBLISHEABLE_API_KEY]');
header('Content-Type: application/json');
$results = array();
require 'vendor/autoload.php';
\Stripe\Stripe::setApiKey(STRIPE_SECRET_KEY);
try{
    $products  = \Stripe\Plan::all();
    $results['response'] = "Success";
    $results['plans'] = $products->data;

}catch (Exception $e){
    $results['response'] = "Error";
    $results['plans'] = $e->getMessage();
}
echo json_encode($results);

и другой сценарий, который использует token idamountплаты .

charge.php

 <?php
define('STRIPE_SECRET_KEY','[YOUR_SECRET_API_KEY]');
define('STRIPE_PUBLIC_KEY','[YOUR_PUBLISHEABLE_API_KEY]');
header('Content-Type: application/json');
$results = array();
require 'vendor/autoload.php';
\Stripe\Stripe::setApiKey(STRIPE_SECRET_KEY);
if(isset($_POST['method'])){
    $method = $_POST['method'];
    if($method =="charge"){
        $amount = $_POST['amount'];
        $currency = $_POST['currency'];
        $source = $_POST['source'];
        $description = $_POST['description'];
        try {
            $charge = \Stripe\Charge::create(array(
                "amount" => $amount, // Amount in cents
                "currency" => $currency,
                "source" => $source,
                "description" => $description
            ));
            if($charge!=null){
                $results['response'] = "Success";
                $results['message'] = "Charge has been completed";
            }
        } catch(\Stripe\Error\Card $e) {
            $results['response'] = "Error";
            $results['message'] = $e->getMessage();
        }
        echo json_encode($results);
    }else {
        $results['response'] = "Error";
        $results['messsage'] = "Method name is not correct";
        echo json_encode($results);
    }
}else {
    $results['response'] = "Error";
    $results['message'] = "No method has been set";
    echo json_encode($results);
}

Вывод

Stripe — одна из лучших платформ онлайн-платежей. Его уникальная панель инструментов позволяет создавать приложения для электронной коммерции и продавать продукты даже без единой строки внутреннего кода.
Я надеюсь, что вы найдете его таким же простым в использовании, как и я, и если у вас есть какие-либо комментарии или вопросы, пожалуйста, дайте мне знать ниже.