Я уже сказал ранее на примере Android SDK, как встроить нативную форму приема платежей по банковской карте в мобильное приложение, не ограничиваясь фреймом и WebView, и не попасть под аудит PCI DSS. С тех пор наш SDK достаточно существенно расширился и к привычной форме ввода карты в Android и iOS добавился следующий функционал: — Библиотека React Native для Android и iOS — настройка макета формы с реквизитами карты — функция оптического сканирования карт — прием бесконтактных платежей в Android по технологии NFC В этой публикации я расскажу, что можно делать с платежами в мобильных приложениях, какие есть лайфхаки и подводные камни, и напоследок приведу пример кода демо-приложения и расскажу, как списать долг по карте с друг, использующий NFC-считыватель вашего смартфона.
Кейс 1. Привязка карты клиента к бэкенду для регулярных списаний или платежей в 1 клик.
Здесь важно понимать, что если ваш бэкенд не сертифицирован PCI DSS, то вы не сможете хранить номер карты и срок ее действия в своей базе данных.
Поэтому, прежде чем привязать идентификатор карты к учетной записи клиента, карту необходимо сначала токенизировать.
Для этого вам необходимо совершить первый платеж через мобильное приложение при участии клиента и желательно с 3D-Secure, заблокировав на карте небольшую сумму, например 1 единицу валюты.
3D-secure в данном случае необходим, во-первых, чтобы защитить себя, как торговую точку, от финансовых претензий (чарджбэков) при будущих периодических списаниях, а во-вторых, для улучшения конверсии, как, например, с картами от Сбербанк в России и Приватбанк в Украине в большинстве случаев транзакция не пройдет без 3D-Secure. Итак, чтобы получить токен карты, вам нужно передать параметры требуетсяRecToken И проверка (подробнее о том, как создать мобильное приложение, смотрите в статье, ссылку на которую я указал в начале, а также в коде демонстрационные приложения на гитхабе):
order.setRequiredRecToken(true)
order.setVerification(true)
Параметр требуетсяRecToken требует, чтобы токен карты был возвращен после успешной авторизации карты, и проверка — что не нужно списывать средства с карты, а просто заблокировать их и затем вернуть (платежный шлюз возвращает их автоматически).
В ответ платежный шлюз вернет параметры RecToken — жетон карты, RecTokenLifeTime — срок действия токена (по сути срок действия карты) и MaskedCard — замаскированный номер карты, который необходимо привязать к токену в бэкенде для дальнейшего отображения клиенту при выборе способа оплаты.
Теперь, имея токен карты, вы можете вызвать метод в любой момент по запросу клиента или при наступлении срока оплаты.
списания по жетонам через межсерверное API и спишите необходимую сумму.
Подводные камни: По нашей статистике, довольно значительная часть держателей карт не имеет возможности оплатить через 3DSecure на мобильном устройстве по ряду причин, не зависящих от устройства и шлюза: — СМС может не прийти, либо пользователь, переключаясь между приложением СМС и вашим, потерял форму ввода пароля 3D-Secure, так как она открывается в WebView или системном браузере — теперь доступна верстка 3D-Secure страницы банка на смартфоне или планшете (банки очень редко адаптируют такие страницы) — веб-сервер банка отключил поддержку небезопасного протокола TSL 1.0, что делает 3D-Secure недоступным для версии Android <4.1 Ухищрение: На платежном шлюзе мы можем на лету включить/отключить 3D-Secure, а если клиент все равно не может заплатить, мы подстраиваемся под него и пытаемся совершить платеж без пароля 3D-Secure. Также стоит помнить, что если вы сохраните в своей системе токены от одного платежного провайдера, то вы не сможете использовать их на другом провайдере, если только провайдеры не договорятся между собой о миграции токенов, что, в принципе, уже произошло.
в нашей практике было несколько раз.
Кейс 2. Настройка макета формы ввода номера карты.
Часто возникает необходимость разместить поля для ввода номера карты, срока действия и cvv2 в другой последовательности, чем предусмотрено стандартной раскладкой в SDK. Но из-за требований PCI DSS нельзя просто заменить поле ввода номера карты стандартным компонентом EditText. Для этих целей мы разработали гибкую планировку.
Гибкая верстка наследует стили вашего мобильного приложения и позволяет расположить элементы формы в любой последовательности и в любом дизайне, предотвращая при этом случайную передачу данных карты на ваш бэкенд. Существует два механизма организации входа карты в SDK: CardInputView — готовое к использованию представление; CardInputLayout — это просто оболочка макета для создания представления в вашем собственном стиле разметки.
По сути CardInputView = CardInputLayout + CardNumberEdit + CardExpMmEdit + CardExpYyEdit + CardCvvEdit.
Упрощенную структуру CardInputView в XML можно записать следующим образом: <com.cloudipsp.android.CardInputLayout>
<com.cloudipsp.android.CardNumberEdit/>
<LinearLayout android:orientation="horizontal">
<com.cloudipsp.android.CardExpMmEdit />
<com.cloudipsp.android.CardExpYyEdit />
</LinearLayout>
<com.cloudipsp.android.CardCvvEdit />
<com.cloudipsp.android.CardInputLayout>
Поэтому вы можете абсолютно свободно настраивать и располагать элементы ввода по своему воображению.
Необходимо соблюдать только одно правило — каждый из входных элементов (CardNumberEdit, CardExpMmEdit, CardExpYyEdit, CardCvvEdit) должен находиться в CardInputLayout один раз, при этом уровень вложенности View не имеет значения.
Вот как это может выглядеть:
Подводные камни:
При настройке полей ввода стоит помнить:
- cvv2 может состоять из 3 или 4 символов.
— номер карты может содержать от 14 до 19 символов — вы можете добиться максимально точной настройки вашего дизайна, разветвив SDK и внеся изменения в реализацию вашего макета (это не запрещено, если вы не начнете передавать данные карты через свой бэкенд).
Но при форке вы теряете поддержку обновлений SDK со шлюза и интеграцию новых функций.
Ухищрение: Часто на форме ввода реквизитов карты можно встретить поля для ввода имени и фамилии держателя карты и его почтового индекса.
Для платежей внутри СНГ практической необходимости делать это в 99% случаев нет – только некоторые банки США, Канады и Великобритании поддерживают данную технологию, которая называется Система проверки адреса , и чтобы чек работал, он должен поддерживаться как банком-эквайером, так и банком-эмитентом.
Кейс 3. Подключение возможности сканирования карты через камеру и NFC
Для Android в библиотеке реализована функция сканирования оптических карт. Android-SDK-оптический , для iOS в библиотеке CloudipspОптический с использованием SDK карты.io
.NFC-сканирование реализовано с помощью библиотек Android-SDK-NFC И реакция-native-cloudipsp-nfc и доступно только для Android. Хотя Apple открывается начиная с iOS 11+ Сторонние разработчики умеют читать 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("[email protected]"); 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, "Failure\nErrorCode: " + 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, протокол финансовой авторизации карты по-прежнему отсутствует. Те.
Для полноценной работы данного функционала карта должна быть открыта для онлайн-платежей.
Ухищрение: Написав простое приложение, вы сможете с его помощью перевести средства с чужой карты на свою, поднеся чужую карту к своему телефону.
Например, это может быть удобно, если вам нужно списать с друга небольшую сумму за долг по карте.
С одной стороны, это будет практично и удобно, с другой – весьма эффектно.
Чтобы воспользоваться услугой перевода с карты на карту, вам необходимо сначала зарегистрироваться на сайте платежной платформы Fondy и привязать банковскую карту, на которую будут переведены средства, к вашим финансовым настройкам.
В целях безопасности сумма, которую можно списать через NFC без поддержки 3D-Secure, не может превышать эквивалент 4 долларов США.
Теги: #Android #iOS #платежи #sdk #платежные системы #Разработка для iOS #Разработка мобильных приложений #Разработка для Android
-
Как Работает Vpn Для Франции
19 Oct, 24 -
Суборбитальная Осень
19 Oct, 24 -
Смысл И Качество Жизни Программиста
19 Oct, 24 -
Валидация В Php. Красота Или Лапша?
19 Oct, 24 -
Старший Перекрёсток: Куда Идти Дальше?
19 Oct, 24 -
Виртуальная Прогулка По Метро
19 Oct, 24