Встраиваем прием платежей в мобильное приложение, или почему можно забыть про 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 решает две задачи одновременно — дает торговцу инструмент для приема платежей по платежным картам и избавляет его от необходимости проходить сертификацию на стандарты безопасности.
Поделиться публикацией

Комментарии 16

    0
    Для UWP бы еще запилили.
      0
      Вопрос1. Банки как-то планируют (или уже) стилизуют свой 3D-secure странички под мобильные устройства, например, сматрфоны или к примеру можно отправлять зарпос на открытие именно 3D secure под мобильные устройства (сейчас не всегда удобно).

      Вопрос2 (более общий). Сейчас бывает так, что карта привязывается к SIM карте, есть ли уже что-то рабочее которое позволяет «взять данные» с SIM без необходимости ввода данных о карте?
        0
        Вопрос1.

        select count(distinct name) from allbanks 
        
         count    
         -------- 
         12703    
        

        вот столько у меня в базе банков по всем странам
        как вы думаете, смогут все банки в едином порыве адаптировать свои 3DS страницы? я думаю эту проблему не побороть с учетом сколько банков в мире технологически отсталые, 3DS должен умереть сам с распостранением мобильных технологий или трансформироваться в новый стандарт. Сейчас с ним много проблем: TLS 1.2 обязателен для iOS но поддерживается не всеми банками, а если банк принудительно переходит на TLS 1.2, то у Android версии <4.3 начинаются свои проблемы

        Вопрос2 (более общий).

        Протокол 3DSecure устарел еще до того, как его начали банки использовать массово, к тому же он разрабатывался без расчета на мобильные устройства вообще. Если какой-нибудь крупный игрок на рынке, например Apple или Google предложит Visa/MasterCard свой стандарт, то есть шанс, что 3DSecure привяжется к устройству или sim, но я думаю что это маловероятно
          –1
          Благодарю за развернутый ответ. Я думал конечно не самим банкам менять, разработчикам ихнего ПО. Но не знаю так хорошо внутрянку, так что не буду утверждать.

          По вопросу2, вот пример из жизни — хочу заказать пиццы и оплатить в мобильном приложении сразу (так как у курьера нет терминала к примеру), то пока придется работать с тем чем есть… 3d secure же?
            0
            3ds можно использовать опционально, если риск мошенничества небольшой, к тому же есть физический адрес получателя услуги, то 3ds лучше не использовать — это право магазина/банка-эквайера
              0
              Про разработчиков вы в целом правы, банки эмитенты для 3-d secure как правило используют готовое решение, которое предоставил им вендор процессингового ПО. И поменять на странице ввода пароля они могут разве что только картинки с логотипом банка.
              Сама же страница описана в спецификации протокола, есть даже расширение спецификации для мобильных устройств — 3-D Secure: Protocol Specification – Extension for Mobile Internet Device, но, либо соответствующий функционал не реализован вендором, либо банк не настроил…
          +1
          Мне не особо понятно, что именно мешает автору приложения воспользоваться интроспекцией и утащить введённые в компонент платёжные данные себе. Схема по сути ничем не отличается от внедрения на сайт продавца HTML-формы без использования iframe. Так же мне не особо понятно, как именно вы убедили в безопасности предложенной схемы аудитора.
            –1
            эквайер такого автора забанит и впаяет штраф
            0
            А как вы будите аудит проходить с CloudipspWebView ? Да и где и есть подозрения, что доступ все можно получить к данным карт через дамп/рефлексия/внутреннее api?

            Анекдот на новый лад

            — А вот компания Х говорит, что не обрабатывает данные банковских карт
            — Так и вы говорите, что не обрабатываете… :-)
              +2
              так и к фрейму в браузере можно получить доступ в определенных условиях
              еще можно трафик перехватить у клиента через MITM и расшифровать, если клиент не обращает внимание на предупреждения браузера

              вы не поверите, но PCI DSS не гарантирует защиту данных от перехвата, дампа, вторжения, атаки
              это набор требований и рекомендаций, которые минимизируют риск компроментации и определяют ответственных за возможные инциденты
              0
              Проясните, пожалуйста, такой момент. Допустим, у меня есть интернет-магазин, я реализовал мобильное приложение (клиент к своему магазину) и вставил в него ваш SDK, чтобы принимать карты к оплате прямо в приложении. Сертифицировать по стандарту PA-DSS мое приложение мне необязательно (он носит рекомендательный характер, о чем сказано в статье). Подпадает ли это приложение под PCI DSS?
              Как написано в стандарте, PCI DSS распространяется на информационные инфраструктуры организаций, которые передают, хранят и обрабатывают карточные данные. Приложение стало частью моей информационной архитектуры? Думаю, да. Передаются ли карточные данные с помощью приложения? Точно да.
              Соответственно, как я понимаю, приложение попадает в scope, который подлежит сертификации и должен проверяться на аудите. Более того, в scope попадают и сами устройства (т.е. смартфоны и планшеты) всех моих клиентов (ведь то, что устройство «не ворует» данные карточки, никак не подтверждено). Если что, я имею ввиду не ваш scope, как посредника (потому что вы предоставили только лишь SDK), а scope моего магазина. Означает ли это, что использование вашего SDK в моем мобильном приложении накладывает на меня обязанность проходить дорогостоящий аудит PCI DSS?
              Так вот вопрос в том, прав ли я в своих рассуждениях, а если нет, то в чем ошибка? Только укажите, пожалуйста, ссылки на выдержки из стандартов или разъяснения этих стандартов, чтобы точно развеять заблуждения.
                0
                в статье я ссылаюсь пост https://habrahabr.ru/company/dataart/blog/274325/ — здесь детально затрагивается эта тема
                  0
                  В статье сказано, что мое приложение не обязано сертифицироваться по PA DSS. Но про соответствие инфраструктуры моего магазина, в которой работает такое приложение, стандарту PCI DSS и необходимости прохождения аудита ничего не сказано. Можете пояснить?
                    0
                    если ваше приложение не передает карточные данные через вашу инфрастуктуру (а для этого есть такая функциональность, как токенизация карты), то ваша инфрастуктура не попадает под scope
                      0
                      Т.е., получается, если данные карты передаются с помощью вашего SDK только вашему серверу, то приложение как бы «выпадает» из моей инфраструктуры и мне ничего сертифицировать не надо?
                      Тогда в чей scope попадает передача карточных данных, введенных в моем мобильном приложении? В ваш? А как вы тогда проходите аудит на соответствие PCI DSS? Вряд ли вы можете показать аудитору все приложения, использующие указанный SDK. P2PE и токенизацию в API вы не используете. На чьи плечи ложится ответственность за мошеннические транзакции по номерам карт, украденным из приложений, использующих ваш SDK?
                        0
                        В область аудита PCI DSS торгово-сервисного предприятия (мерчанта), который использует мобильное приложение для оплаты картой, входит процесс разработки данного платежного приложения (6 раздел PCI DSS).
                        Если своей информационной инфраструктуры нет и из мобильного приложения отправляются карточные данные напрямую сертифицированному поставщику услуг эквайринга, то область аудита мерчанта будет ограничена только 6 и 12 разделами PCI DSS.

                        В зависимости от количества транзакций, обрабатываемых в год, мерчант должен подтвердить соответствие либо через QSA-аудит (если количество транзакций в год более 1 000 000), либо через заполнение листа самооценки (если количество транзакций в год менее 1 000 000).

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

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

              Самое читаемое