Как принимать платежи в мобильном приложении: токенизация, NFC, оптическое сканирование и другие плюшки в одном SDK / Хабр

Что такое мобильные платежи

В двух словах, мобильный платеж – это платёжная транзакция, осуществляемая через ваше мобильное устройство. Например, вы добавляете свои дебетовые и кредитные карты в мобильное платежное приложение на своем телефоне или планшете.

Мобильные платежные приложения могут использоваться для совершения покупок, но у них есть и другие функции. Например, Venmo – это приложение для мобильных платежей в социальных сетях, которое вы можете использовать для отправки денег друзьям и родственникам, используя адрес электронной почты.

Что такое мобильное платежное приложение

Три самых крупных мобильных платежных приложения – это Google Pay, Samsung Pay и Apple Pay, и каждое из них имеет свои особенности.

Например, в Google Pay вы можете использовать мобильное устройство для оплаты в магазине, через партнерские приложения и веб-сайты.

Samsung Pay принимается практически везде, где вы можете провести или приложить свою кредитную или дебетовую карту, и вы также можете использовать его для оплаты в приложениях или у онлайн-продавцов.

Apple Pay можно использовать для оплаты услуг у торговых партнеров и совершения покупок у участвующих розничных продавцов в приложениях, в магазинах и в Интернете.

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

Где принимаются мобильные платежи

Не каждый продавец принимает мобильные платежи, но всё больше и больше предоставляют такую услугу. Согласно отчету Kount Mobile Payments and Fraud 2022, 71% продавцов поддерживают мобильные платежи в точках продаж, а еще 21% торговцев планируют внедрить поддержку мобильных платежей в будущем.

Читайте про операторов:  Тарифный план супер джинс мтс. Сообщения и мобильный интернет на тарифе «Джинс»

Как использовать мобильные платежи для оплаты

Первым шагом является загрузка приложения для мобильных платежей, которое вы хотите использовать, на ваше устройство. Оттуда вы можете загрузить информацию о своих кредитных и дебетовых картах.

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

Если вы производите оплату в магазине, произвести платеж так же просто, как открыть приложение, выбрать, какой картой вы хотите оплатить, и поднести мобильное устройство к платежному терминалу. Мобильные платежи основаны на технологии Near-Field Communication, которая позволяет терминалу «разговаривать» с вашим мобильным устройством и безопасно получать платежную информацию вашей карты.

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

Кейс 1. привязываем карту клиента к бэкенду для регулярных списаний или платежей в 1 клик.

Тут важно понимать, что если ваш бэкенд не сертифицирован по PCI DSS, то номер карты и ее срок действия вы не можете хранить в своей базе данных. Поэтому, прежде чем привязать идентификатор карты к аккаунту клиента, необходимо сначала карту токенизировать.

Для этого вам необходимо осуществить через мобильное приложение первый платеж с участием клиента, и желательно с 3D-Secure, заблокировав на карте небольшую сумму, например 1 единицу валюты. 3D-secure в данном случае необходим в первую очередь, чтобы обезопасить себя, как торговую точку, от финансовых претензий (чарджбеков) по будущим рекурентным списаниям, а во вторую очередь — чтобы улучшить конверсию, так как например по картам Сбербанка в России и Приватбанка в Украине в большинстве случаев транзакция без 3D-Secure не пройдет.

Итак, чтобы получить токен карты, необходимо передать параметры

requiredRecTokenverification

(более подробно как создать мобильное приложение смотрите в статье, ссылку на которую я указал в начале, а также в коде

на github):

order.setRequiredRecToken(true)
order.setVerification(true)


Параметр

requiredRecToken

требует возвратить токен карты при успешной авторизации карты, а

verification

— что средства с карты списывать не нужно, а достаточно их заблокировать, а потом вернуть (платежный шлюз возвращает их автоматически).

В ответ платежный шлюз вернет параметры

recToken

— токен карты,

recTokenLifeTime

— срок действия токена (по сути срок действия карты) и maskedCard — маскированный номер карты, который необходимо привязать в бекенде к токену для дальнейшего отображения клиенту при выборе способа оплаты.


Теперь, имея токен карты вы можете в любой момент по требованию клиента или при наступлении срока оплаты, вызвать метод

через server-to-server API и списать необходимую сумму.

Подводные камни:

По нашей статистике у довольно значимой части картодержателей не получается оплатить через 3DSecure на мобильном устройстве по ряду причин, от него и шлюза не зависящих:

— может не приходить SMS, или пользователь переключаясь между SMS-приложением и вашим, потерял форму с вводом пароля 3D-Secure, так как она открывается в WebView или системном браузере

— полезла верстка 3D-Secure страницы банка на смартфоне или планшете (банки очень редко адаптируют такие страницы)

— веб-сервер банка отключил поддержку небезопасного протокола TSL 1.0, что делает 3D-Secure недоступным для Android версии <4.1

Лайфхак:


Мы на платежном шлюзе умеем включать/отключать налету 3D-Secure, и если все-таки у клиента не получается оплатить, мы под него подстраиваемся, и пытаемся сделать оплату без 3D-Secure пароля.

Также стоит помнить, что если вы сохраняете токены одного платежного провайдера в своей системе, то использовать их на другом провайдере уже не получится, разве что если провайдеры не договорятся между собой о миграции токенов, что в принципе в нашей практике уже было несколько раз.

Кейс 2. кастомизируем верстку формы ввода номера карты.

Часто возникает необходимость разместить поля для ввода номера карты, срока действия и cvv2 в другой последовательности, чем это предусмотрено стандартным layout в SDK. Но из-за требований PCI DSS вы не можете просто взять, и заменить поле ввода номера карты на стандартный компонент EditText.

Для этих целей мы разработали flexible layout. Flexible layout наследует стили вашего мобильного приложения и позволяет располагать элементы формы в любой последовательности и в любом дизайне и при этом предотвращает случайную передачу карточных данных на сторону вашего бекэнда.

Для организации ввода карты в SDK есть два механизма:CardInputView — готовый view для использования;CardInputLayout — лишь layout wrapper для потроение view в собственном стиле разметки.

По сути CardInputView = CardInputLayout CardNumberEdit CardExpMmEdit CardExpYyEdit CardCvvEdit.Упрощенную структуру CardInputView в XML можно запиться так:

Следовательно можно абсолютно свободно кастомизировать и располагать элементы ввода на сколько хватит фантазии. Есть лишь одно правило которое нужно соблюдать — каждый из элементов ввода (CardNumberEdit,CardExpMmEdit,CardExpYyEdit,CardCvvEdit) должен быть в CardInputLayout один раз, при этом не играет роли уровень вложенности View.
Вот как это может выглядеть:
Как принимать платежи в мобильном приложении: токенизация, NFC, оптическое сканирование и другие плюшки в одном SDK / Хабр
Подводные камни:
Кастомизируя поля ввода стоит помнить:
— cvv2 может быть длиной как 3, так и 4 символа
— номер карты может быть от 14 до 19 символов
— можно добиться максимально точной кастомизации к вашему дизайну, сделав форк SDK и внеся изменения уже в своей реализации layout (это не запрещено делать, если вы не начинаете пропускать реквизиты карты через свой бэкенд). Но сделав форк вы теряете поддержку обновлений SDK со стороны шлюза и интеграцию новых фич
Лайфхак:
Часто можно встретить на форме ввода реквизитов карты инпуты для ввода имени и фамилии картодержателя и его ZIP кода. Для платежей по СНГ нет практической необходимости это делать в 99% случаев — только некоторые банки США, Канады и Великобритании поддерживают эту технологию, которая называется Address Verification System, при этом чтобы проверка сработала, ее должны поддерживать как банк-эквайер, так и банк-эмитент
Как принимать платежи в мобильном приложении: токенизация, NFC, оптическое сканирование и другие плюшки в одном SDK / Хабр

Кейс 3. подключаем возможность сканирования карты через камеру и nfc

Функция оптического сканирования карты реализована для Android в библиотеке

, для iOS в библиотеке

с использованием


NFC сканирование реализовано при помощи библиотек

и доступно только для Android. Хотя Apple и

сторонним разработчикам возможность читать RFID метки, но чтение EMV тегов с банковских карт по прежнему остается недоступным.

Пример демо-приложения для использования NFC


package com.cloudipsp.nfcexample;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Patterns;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.Spinner;
import android.widget.Toast;

import com.cloudipsp.android.Card;
import com.cloudipsp.android.CardInputView;
import com.cloudipsp.android.Cloudipsp;
import com.cloudipsp.android.CloudipspWebView;
import com.cloudipsp.android.Currency;
import com.cloudipsp.android.Order;
import com.cloudipsp.android.Receipt;
import com.cloudipsp.nfc.NfcCardBridge;

public class MainActivity extends Activity implements View.OnClickListener {
    private static final int MERCHANT_ID = 1396424;

    private EditText editAmount;
    private Spinner spinnerCcy;
    private EditText editEmail;
    private EditText editDescription;
    private CardInputView cardInput;
    private CloudipspWebView webView;

    private Cloudipsp cloudipsp;
    private NfcCardBridge nfcCardBridge;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        nfcCardBridge = new NfcCardBridge(this);

        findViewById(R.id.btn_amount).setOnClickListener(this);
        editAmount = (EditText) findViewById(R.id.edit_amount);
        spinnerCcy = (Spinner) findViewById(R.id.spinner_ccy);
        editEmail = (EditText) findViewById(R.id.edit_email);
        editDescription = (EditText) findViewById(R.id.edit_description);
        cardInput = (CardInputView) findViewById(R.id.card_input);
        cardInput.setHelpedNeeded(true);
        findViewById(R.id.btn_pay).setOnClickListener(this);

        webView = (CloudipspWebView) findViewById(R.id.web_view);
        cloudipsp = new Cloudipsp(MERCHANT_ID, webView);

        spinnerCcy.setAdapter(new ArrayAdapter<Currency>(this, android.R.layout.simple_spinner_item, Currency.values()));

        if (savedInstanceState == null) {
            processIntent(getIntent());
        }
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btn_amount:
                fillTest();
                break;
            case R.id.btn_pay:
                processPay();
                break;
        }
    }

    private void fillTest() {
        editAmount.setText("1");
        editEmail.setText("test@test.com");
        editDescription.setText("test payment");
    }

    private void processPay() {
        editAmount.setError(null);
        editEmail.setError(null);
        editDescription.setError(null);

        final int amount;
        try {
            amount = Integer.valueOf(editAmount.getText().toString());
        } catch (Exception e) {
            editAmount.setError(getString(R.string.e_invalid_amount));
            return;
        }

        final String email = editEmail.getText().toString();
        final String description = editDescription.getText().toString();
        if (TextUtils.isEmpty(email) || !Patterns.EMAIL_ADDRESS.matcher(email).matches()) {
            editEmail.setError(getString(R.string.e_invalid_email));
        } else if (TextUtils.isEmpty(description)) {
            editDescription.setError(getString(R.string.e_invalid_description));
        } else {
            final Currency currency = (Currency) spinnerCcy.getSelectedItem();
            final Order order = new Order(amount, currency, "vb_"   System.currentTimeMillis(), description, email);
            order.setLang(Order.Lang.ru);
            final Card card;
            if (nfcCardBridge.hasCard()) {
                card = nfcCardBridge.getCard(order);
                cardInput.display(null);
            } else {
                card = cardInput.confirm();
            }

            cloudipsp.pay(card, order, new Cloudipsp.PayCallback() {
                @Override
                public void onPaidProcessed(Receipt receipt) {
                    Toast.makeText(MainActivity.this, "Paid "   receipt.status.name()   "nPaymentId:"   receipt.paymentId, Toast.LENGTH_LONG).show();
                }

                @Override
                public void onPaidFailure(Cloudipsp.Exception e) {
                    if (e instanceof Cloudipsp.Exception.Failure) {
                        Cloudipsp.Exception.Failure f = (Cloudipsp.Exception.Failure) e;

                        Toast.makeText(MainActivity.this, "FailurenErrorCode: "  
                                f.errorCode   "nMessage: "   f.getMessage()   "nRequestId: "   f.requestId, Toast.LENGTH_LONG).show();
                    } else if (e instanceof Cloudipsp.Exception.NetworkSecurity) {
                        Toast.makeText(MainActivity.this, "Network security error: "   e.getMessage(), Toast.LENGTH_LONG).show();
                    } else if (e instanceof Cloudipsp.Exception.ServerInternalError) {
                        Toast.makeText(MainActivity.this, "Internal server error: "   e.getMessage(), Toast.LENGTH_LONG).show();
                    } else if (e instanceof Cloudipsp.Exception.NetworkAccess) {
                        Toast.makeText(MainActivity.this, "Network error", Toast.LENGTH_LONG).show();
                    } else {
                        Toast.makeText(MainActivity.this, "Payment Failed", Toast.LENGTH_LONG).show();
                    }
                    e.printStackTrace();
                }
            });
        }
    }

    @Override
    public void onBackPressed() {
        if (webView.waitingForConfirm()) {
            webView.skipConfirm();
        } else {
            super.onBackPressed();
        }
    }

    @Override
    public void onNewIntent(Intent intent) {
        super.onNewIntent(intent);

        processIntent(intent);
    }

    private void processIntent(Intent intent) {
        if (nfcCardBridge.readCard(intent)) {
            Toast.makeText(this, "NFC Card read success", Toast.LENGTH_LONG).show();
            nfcCardBridge.displayCard(cardInput);
        }
    }
}


Отличается от обычной реализации наличием NfcCardBridge и навешиванием Intent на него для ожидания события, что карта прочитана (readCard)

Подводные камни:

Хотя считывание карты и выполняется посредством NFC, протоколом финансовой авторизации карты по-прежнему служит обычный card not present. Т.е. для полноценной работы этой функциональности, карта должна быть открыта для платежей в интернет.

Лайфхак:

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

Для того, чтобы воспользоваться сервисом переводов с карты на карту, необходимо будет предварительно зарегистрироваться на сайте платежной платформы Fondy и привязать банковскую карту, на которую будут поступать средства, в свои финансовые настройки.

Пора прощаться с наличными и картами?

Согласно данным WorldPay, мобильные платежи набирают силу, но они не вытесняет наличные, кредитные или дебетовые карты полностью. И даже если они это сделают, это может произойти только через несколько лет.

Это означает, что носить хотя бы одну кредитную или дебетовую карту или небольшую сумму наличных денег всё же целесообразно, если вы беспокоитесь о том, что окажетесь где-то, где не принимают мобильные платежи. В то же время, вы можете начать переходный период, исследуя различные приложения для мобильных кошельков, чтобы решить, какое из них подходит именно вам.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *