![image](https://habrastorage.org/webt/ca/5_/u0/ca5_u0gido4r9fn57nwtdj_upn8.png)
Пять месяцев назад мы опубликовали NPM модуль для работы с новой версией Яндекс.Касса, которая вышла в октябре 2017 года. Наш модуль попал в официальную документацию, и его уже скачали более 1300 раз.
![image](https://habrastorage.org/webt/h3/4k/bk/h34kbkhro2v4dpeoz20fjsjcvdy.png)
На Хабре, совместно с коллегами из Яндекса, мы уже рассказывали об опыте создания модуля, но за кадром остался наш опыт, который мы получили, когда столкнулись с интеграцией платежной системы для российских клиентов.
Поэтому сегодня мы хотим рассказать, как для одного из отечественных клиентов мы переходили от интеграции привычных нам заграничных платежных систем на русские аналоги, с какими трудностями столкнулись и как их решали.
Как работали с платёжными системами
Раньше мы работали только с платежными системами зарубежных клиентов. У нас был опыт интеграции с Authorize.net, Paypal, Braintree, Stripe, Payoneer, Wirecard и Svea.
С этими платёжными системами легко взаимодействовать и реализовать разные функции. Например, когда мы работали над сервисом грузоперевозок по Европе, мы интегрировали в проект несколько систем оплаты и написали алгоритм — он реализовывал сложную логику денежных операций. Когда пользователь заказывал услугу, система переводила деньги со счёта заказчика на счёт сервиса, а алгоритм проводил транзакции: пересчитывал оплату в случае проблем с доставкой, конвертировал в валюту, проводил возврат и перенос сроков платежей, списывал процент за использование сервиса.
Лучшие представители платёжных систем позволяют начать разработку в песочнице даже без регистрации. У многих — отличная документация, и почти все помогают решать задачи без активного участия поддержки. Разработчики могут из личного кабинета управлять правами и доступами. Платёжные системы имеют SDK для большинства популярных платформ, что уже сейчас — негласный стандарт.
Чтобы интегрировать платёжную систему, разработчику нужно:
- Зарегистрироваться на сайте и получить доступ к песочнице.
- Скачать SDK и подключить его. Вставить в нужных местах приложения вызовы функций платежной системы.
- Протестировать все сценарии и убедиться, что всё работает правильно.
- Заниматься другими задачами, пока заказчик решает финансовые и юридические вопросы.
- В нужный момент времени переключить приём платежей в «боевой» режим.
Что поменялось
Год назад мы начали работать и на отечественном рынке. Одним из первых проектов стало приложение 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](https://habrastorage.org/webt/ni/kp/zg/nikpzg_dcuxheonn8kb1apzufgg.png)
Дизайн не адаптируется под мобильное приложение
Оказалось, чтобы переключить внешний вид страницы оплаты в адаптивный режим, нужно оставить заявку в личном кабинете. Нам пришлось обращаться в техподдержку снова.
![image](https://habrastorage.org/webt/hv/j8/tf/hvj8tfzk4wys_-57hltqz0oxdro.png)
Ура, адаптированный дизайн под мобилку в 2017!
Но только для оплаты банковскими картами
Проверяем факт оплаты
Stripe:
Оплата проверяется в процессе формирования платежа и в случае ошибки функция API вернет соответствующее исключение.
Яндекс.Касса:
Пользователи производят оплату на стороне Яндекс.Кассы, но приложение пользователя ещё не знает о факте оплаты. Поэтому Яндекс.Касса должна сообщить серверу, что оплата была осуществлена. Для этого разработчики должны создать WebHook и настроить Яндекс.Кассу, чтобы она могла использовать адреса из WebHook для передачи факта оплаты.
Уведомление сервера об оплате происходит в 2 этапа:
- Проверка возможности оплаты. Наш сервер всё проверяет, разрешает или запрещает списать средства, например, проверяет остатки товара, либо корректность цен.
- Выполнение оплаты. Яндекс.Касса сообщает нашему серверу, что деньги списаны и оплата прошла успешно. В этот момент необходимо зафиксировать в базе факт оплаты.
Но и тут нас ждут сюрпризы:
- Чтобы изменить адреса, нужно отправить запрос менеджеру и опять ждать.
- Необходимо реализовать специальный алгоритм проверки валидности данных от Яндекс.Кассы.
- Сервер должен возвращать специально сформированный 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-модуль, который любой желающий может самостоятельно интегрировать в свой проект.