Вбудовуємо прийом платежів в мобільний додаток, або чому можна забути про PCI DSS і PA DSS

А чи потрібен PCI DSS?

Рано чи пізно більшість власників і розробників інтернет-магазинів і мобільних додатків, які приймають платежі в онлайні, задаються питанням: «чи має мій проект відповідати вимогам стандартів PCI DSS?».

PCI DSS — це стандарт безпеки, який застосовується для всіх організацій сфери обробки платіжних карт: торгових точок, процесингових центрів, фінансових установ та постачальників послуг, а також інших організацій, які зберігають, обробляють або передають дані власників карток і (або) критичні аутентифікаційні дані.
Стандарт PA-DSS поширюється на постачальників додатків і інших розробників додатків, які зберігають, обробляють або передають дані власників карток і (або) критичні аутентифікаційні дані.


image

З веб-сайтом все досить просто: при інтеграції достатньо скористатися технічним рішенням, яке перенаправляє платника на форму введення даних карти, розташованої на сайті PCI DSS сертифікованого платіжного шлюзу або завантажує цю сторінку у фреймі також з сертифікованого сайту. У цьому випадку торговець не підпадає під дії стандарту безпеки, так дані карти не зберігаються і не передаються через його сервера, а до фрейму платіжного шлюзу сайт торговця не має доступу в силу політик безпеки web-браузерів.

З мобільним додатком все трохи складніше. Існує популярна думка, що якщо мобільний додаток запитує дані карти, то воно автоматом підпадає під дію стандарту PCI DSS. Але, насправді, організація, яка розробляє стандарти PCI DSS (PCI SSC — Payment Card Industry Security Standards Council) досі не випустила окремих вимог стандартів для мобільних додатків. А це означає, що стандарт як і раніше несе не обов'язковий, а рекомендаційний характер для самої популярної категорії мобільних додатків, а саме:

Категорія 3. Платіжні додатки, що працюють на будь-яких побутових кишенькових пристроях (наприклад, смартфонах, планшетах, КПК), функціонал яких обмежений не тільки прийняттям платежів.


Але оскільки мобільний додаток не може існувати без бекенду (серверної сторони, обслуговуючої біллінг і основну бізнес-логіку), то, так чи інакше, інформацію, необхідну для обробки платежу воно передає на сервер торговця. Тут і криється нюанс — щоб навмисно або випадково розробник мобільного додатка не запрограмував додаток на передачу даних платіжних карт на який-небудь несертифікований сервер, платіжне мобільний SDK має зробити дані карти недоступними для зчитування. Таке обмеження забезпечує скасування дії вимог PCI DSS:

PCI DSS може не поширюватися безпосередньо на постачальників платіжних додатків, якщо вони не зберігають, не обробляють або не передають дані власників карток, або не мають доступу до даних власників карток своїх клієнтів.


Розглянемо як це реалізовано в мобільному SDK платіжного сервісу Fondy на прикладі Android-рішення (є також і iOS SDK).

Рішення полягає в тому, щоб дані карти вводилися в View, створених бібліотекою SDK, а мобільний додаток використовувало публічні методи цього View, для ініціалізації платежу, стилізації форми і отримання інформації про завершення оплати.

Приклад demo-додатки для Android

Для початку створимо візуальну структуру нашої платіжної форми — layout (до речі, весь вихідний код demo-програми можна знайти на github):

activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="10dp">

<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">

<TextView
android:id="@+id/btn_amount"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/lbl_amount" />

<EditText
android:id="@+id/edit_amount"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="7dp"
android:maxLength="7"
android:inputType="number" />

<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="7dp"
android:text="@string/lbl_ccy" />

<Spinner
android:id="@+id/spinner_ccy"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="7dp" />

<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="7dp"
android:text="@string/lbl_email" />

<EditText
android:id="@+id/edit_email"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="7dp"
android:inputType="textEmailAddress" />

<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="7dp"
android:text="@string/lbl_description" />

<EditText
android:id="@+id/edit_description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="7dp" />

<com.cloudipsp.android.CardInputView
android:id="@+id/card_input"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>

<TextView
android:id="@+id/text_card_type"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:visibility="gone" />

<Button
android:id="@+id/btn_pay"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:gravity="center"
android:text="@string/btn_pay" />
</LinearLayout>
</ScrollView>

<com.cloudipsp.android.CloudipspWebView
android:id="@+id/web_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone"/>

</RelativeLayout>



Зверніть увагу, що всі елементи, крім карткових даних у додатку свої, а форма для введення номеру картки, строку дії та CVV2 інкапсульовані в класі com.cloudipsp.android.CardInputView. Виглядає це приблизно так (для тестів можна використовувати реквізити, зазначені у документації: https://www.fondy.eu/ru/info/api/v1.0/2):

image

При цьому всі елементи, в тому числі і належать класу com.cloudipsp.android.CardInputView, можуть бути легко стилізовані під дизайн основного додатка. Також для подальшої роботи з картами, підключеними до 3DSecure, нам знадобиться елемент класу com.cloudipsp.android.CloudipspWebView — це WebView, в якому платник буде перенаправлено на сайт банку-емітента для введення персонального пароля (на цій картинці — сторінка эмулирующая роботу 3dsecure сервера банку емітента картки:

image

Тепер перейдемо до основного нашого класу, який буде реалізовувати логіку програми: public class MainActivity. Ініціалізуємо об'єкт класу Cloudipsp:

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

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(BuildConfig.DEBUG);
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()));
}


Далі навішуємо на об'єкт класу com.cloudipsp.android.Card хендлер для отримання результату введення номеру картки:

@Override
public void onCardInputErrorClear(CardInputView view, EditText editText) {

}

@Override
public void onCardInputErrorCatched(CardInputView view, EditText editText, String error) {
editText.getText();
}


Тепер ми будемо знати, коли користувач завершить введення даних, або, у випадку помилки — отримаємо інформацію про проблему. Після того, як дані карти введені, ми можемо створити замовлення:

if (card != null) {
final Currency currency = (Currency) spinnerCcy.getSelectedItem();
final Order order = new Order(amount, currency, "vb_" + System.currentTimeMillis(), description, email);

cloudipsp.pay(card, order, new Cloudipsp.PayCallback() {
@Override
public void onPaidProcessed(Receipt receipt) {
Toast.makeText(MainActivity.this, "Paid " + receipt.status.name() + "\nPaymentId:" + receipt.paymentId+"\n Signature:"+receipt.signature, 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();
}
});
}

Як можна бачити, інтеграція задоволена проста і не вимагає особливих зусиль з боку розробника програми. При цьому SDK вирішує два завдання одночасно — дає торговцю інструмент для прийому платежів за платіжними картками і позбавляє його від необхідності проходити сертифікацію на стандарти безпеки.
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.