Как мы интегрировали платежную систему в российский проект

    image

    Пять месяцев назад мы опубликовали NPM модуль для работы с новой версией Яндекс.Касса, которая вышла в октябре 2017 года. Наш модуль попал в официальную документацию, и его уже скачали более 1300 раз.

    image

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

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

    Как работали с платёжными системами


    Раньше мы работали только с платежными системами зарубежных клиентов. У нас был опыт интеграции с Authorize.net, Paypal, Braintree, Stripe, Payoneer, Wirecard и Svea.

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

    Лучшие представители платёжных систем позволяют начать разработку в песочнице даже без регистрации. У многих — отличная документация, и почти все помогают решать задачи без активного участия поддержки. Разработчики могут из личного кабинета управлять правами и доступами. Платёжные системы имеют SDK для большинства популярных платформ, что уже сейчас — негласный стандарт.

    Чтобы интегрировать платёжную систему, разработчику нужно:

    1. Зарегистрироваться на сайте и получить доступ к песочнице.
    2. Скачать SDK и подключить его. Вставить в нужных местах приложения вызовы функций платежной системы.
    3. Протестировать все сценарии и убедиться, что всё работает правильно.
    4. Заниматься другими задачами, пока заказчик решает финансовые и юридические вопросы.
    5. В нужный момент времени переключить приём платежей в «боевой» режим.

    Что поменялось


    Год назад мы начали работать и на отечественном рынке. Одним из первых проектов стало приложение Sellsay. Оно помогает пользователям развивать навыки в переговорах, а также является площадкой для взаимной профессиональной помощи.

    У приложения много возможностей помимо стандартных функций: авторизации и регистрации, создания и редактирования проектов. Через сервис вы можете обратиться
    к бизнес-тренеру. Если вы обладаете необходимыми знаниями, вы можете получить сертификат и стать самостоятельным тренером, участвовать в событиях и тренингах
    от создателей приложения, зарабатывать деньги.

    Чтобы в сервисе могли расплачиваться, было необходимо интегрировать платёжную систему — такую, где можно платить рублями и долларами, переносить срок платежа и делать его возврат, сохранять карты и не вводить данные при повторных покупках, использовать Apple и Android Pay. Чтобы это реализовать, мы выбрали систему Braintree.

    И тут мы допустили огромный фейл. Мы согласовали и реализовали основной функционал работы с платёжной системой, но не учли одной важной детали. Узнали о ней за две недели до релиза, когда готовили проект к запуску. Мы не учли, что Braintree не работает с компаниями из России. Зарегистрировать компанию в штатах — не вариант. У нас остался один выход: в кратчайшие сроки заменить систему оплаты.

    Яндекс.Касса: работа с первой версией API


    После анализа систем мы остановились на Яндекс.Кассе: сервис позволял реализовать почти весь функционал, подходил клиенту по цене и решал проблему 54-ФЗ при подключении онлайн-кассы.

    Чуть позже мы поняли, что наше видение интеграции в корне отличается от видения Яндекс.Кассы и её службы безопасности. Весь этап интеграции запомнился больше бесконечными договорами, бланками и созвонами, нежели написанием кода.

    Так, чтобы получить тестовые ключи, необходимо предоставить ссылку на приложение в Apple Store или Google Play. Откуда у нас ссылка, если приём платежей — обязательное требование для публикации приложения? Эту проблему удалось решить, но и потом возникали ситуации, когда необходимо было реализовать и опубликовать функционал для его активации менеджером.

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

    Ещё одно неудобство: Яндекс поддерживает API нескольких поколений. Даже у Яндекс.Кассы несколько подсистем, которые используют разную документацию, аутентификацию и так далее. В итоге приходится одновременно реализовывать несвязанные между собой API. Например, реализуете приём платежей, а для отмены необходимо начинать всё сначала: заявки, договор, звонки, изучение и интеграцию API.

    Финальные недели перед запуском превратились в ад. Чтобы запустить проект вовремя, нам пришлось усилить команду и работать сверхурочно, а Яндекс.Касса тем временем не упускала возможность дать нам понервничать.

    Сравнение Яндекс.Касса со Stripe


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

    У Яндекс.Кассы и Stripe можно найти много общего. Например, обе системы построены на REST-like принципах, используют коды ответов HTTP, поддерживают CORS (и ответы приходят в JSON), обе поддерживают идемпотентность. Есть «песочница» — полная копия реального магазина, и переход между режимами производится лишь заменой ключей: не нужно менять API или URL.

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

    Поскольку на данном проекте реализовали серверную часть на Node.js, то и примеры буду приводить для него.

    Подключаем к проекту.

    Stripe:

    var stripe = require('stripe')('sk_test_...');
    

    Яндекс.Касса:

    Подключения нет, ибо нет основной библиотеки; есть хелпер
    

    Запрашиваем данные карты

    Stripe:

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

    var stripe = Stripe('pk_test_6pRNASCoBOKtIshFeQd4XMUh');
    var elements = stripe.elements();
    
    // Create an instance of the card Element
    var card = elements.create('card', { style: style });
    
    // Add an instance of the card Element into the `card-element` <div>
    card.mount('#card-element');
    
    // On form submit
    stripe.createToken(card).then(function(result) {
          // Send the token to your server
          stripeTokenHandler(result.token);
        }
     );
    

    На сервере

    stripe.charges.create({
      amount: 2000,
      currency: "usd",
      source: "token from previous step", // obtained with Stripe.js
      description: "Charge for william.brown@example.com"
    }, function(err, charge) {
      // asynchronously called
    });
    

    Яндекс.Касса:

    Она действует иначе: здесь нет ни клиентской, ни серверной части. Просто формируем ссылку: 
    <a href="https://money.yandex.ru/eshop.xml?shopId=12345&scid=1234566&sum=3000&customerNumber=73">https://money.yandex.ru/eshop.xml?shopId=12345&scid=1234566&sum=3000&customerNumber=73</a>
    

    По правде говоря, на сайте должна быть платёжная форма. Когда пользователь нажимает «Оплатить», эта форма отправляется на указанный выше адрес и передаёт параметры методом POST. Но у нас мобильное приложение, поэтому пришлось отказаться от формы и формировать ссылку на стороне сервера с открытием в WebView.

    Эта ссылка отдаётся клиенту для ввода данных карты и подтверждения платежа. Но тут нас ждал очередной сюрприз: в конце 2017 года Яндекс.Касса не поддерживает адаптивный дизайн. Всевозможные манипуляции с настройками не давали результата.

    image
    Дизайн не адаптируется под мобильное приложение

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

    image
    Ура, адаптированный дизайн под мобилку в 2017!

    Но только для оплаты банковскими картами


    Проверяем факт оплаты

    Stripe:

    Оплата проверяется в процессе формирования платежа и в случае ошибки функция API вернет соответствующее исключение.

    Яндекс.Касса:

    Пользователи производят оплату на стороне Яндекс.Кассы, но приложение пользователя ещё не знает о факте оплаты. Поэтому Яндекс.Касса должна сообщить серверу, что оплата была осуществлена. Для этого разработчики должны создать WebHook и настроить Яндекс.Кассу, чтобы она могла использовать адреса из WebHook для передачи факта оплаты.

    Уведомление сервера об оплате происходит в 2 этапа:

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

    Но и тут нас ждут сюрпризы:

    1. Чтобы изменить адреса, нужно отправить запрос менеджеру и опять ждать.
    2. Необходимо реализовать специальный алгоритм проверки валидности данных от Яндекс.Кассы.
    3. Сервер должен возвращать специально сформированный XML. Мы смогли найти готовый модуль для формирования правильного ответа.

    Отменяем платёж

    Stripe:

    stripe.refunds.create({
      charge: "ch_1BTuEo2eZvKYlo2CSGqKz76n"
    }, function(err, refund) {
      // asynchronously called
    });
    

    Яндекс.Касса:

    Магазину необходимо получить сертификат X.509 для работы с MWS. Сертификат выдаётся удостоверяющим центром Яндекс.Денег, с помощью которого магазин формирует запросы к Яндекс.Кассе.
    

    Как можно догадаться, согласование и подключение заняло некоторое время. Оно плавно вышло за границы дедлайна.

    Сохраняем карты

    Stripe:

    stripe.customers.create({
      email: "paying.user@example.com",
      source: "src_18eYalAHEMiOZZp1l9ZTjSU0",
    }, function(err, customer) {
      // asynchronously called
    });
    

    Яндекс.Касса:

    Описываем правила отмены сохранения карт и реализуем возможность удаления. Затем отправляем приложение на ревью в службу безопасности Яндекс.Кассы и ждём ответа.

    Если ответ положительный, появляется возможность сохранять карты после успешной оплаты и совершать повторные платежи при помощи протокола MWS.

    Пример запроса из документации:

    POST https://server:port/webservice/mws/api/repeatCardPayment
    DATA: clientOrderId=123456789&invoiceId=2000000123&amount=10.00&cvv=643
    

    Из примеров видно, что Stripe более приспособлен для разработчиков и обладает большим разнообразием функций и возможностей. Но есть одно большое НО: Stripe, как и Braintree, не работает в России, хоть и может принимать платежи из разных стран. Владелец аккаунта на Stripe должен быть резидентом доступных ему стран — Россия не в их числе.

    Что в результате


    Даже с учетом того, что нам пришлось делать много дополнительной работы и общаться с поддержкой, мы не только успели почти все завершить в срок, но и провели еще и дополнительную работу. За неделю до релиза вышла новая версия API Яндекс.Кассы и мы создали NPM-модуль и переписали код.

    Когда мы разобрались в новой версии API, то приятно удивились: разработчики явно смотрели на зарубежные аналоги и применили лучшие практики. Некоторые проблемы предыдущей версии были решены. В документации по новой API не было SDK для Node.js. Мы планируем работать на российском рынке и дальше, и нам понадобится такой инструмент. Поэтому мы решили создать NPM-модуль, который любой желающий может самостоятельно интегрировать в свой проект.
    • +17
    • 4,2k
    • 3

    Lodoss Team

    59,00

    Сложные системы, мобильные приложения, дизайн и AR

    Поделиться публикацией
    Комментарии 3
      0
      Stripe поддерживает 3D-secure и умел это как минимум год назад.

      3D-secure, это не подтверждение через SMS-сообщение, это подтверждение через страницу банка.
        0
        Коллеги, спасибо за подробный рассказ!

        Мы внимательно всё прочитали и поняли, что написанное актуально для старого протокола и старой версии API, к которой мы не подключаем мерчантов с сентября 2017 года. Скорее переходите на новую — kassa.yandex.ru/docs/guides — и делитесь впечатлениями!
          +1
          Мы в конце сентября-начале октября вели переговоры с техподдержкой на тему подключения различных версий API и нам сообщили, что к новой версии пока не подключают и есть только первая версия, к ней и подключили

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

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