Идеей для написания статьи стал интересный для нас первый опыт интеграции рекуррентных платежей. Путь шел через расстрел гугла вопросами, изучение документации, эксперименты, ошибки и общение со службой поддержки Tinkoff. В этой статье собрали воедино все шаги, которые предпринимали, обозначили тонкости и нюансы, с которыми столкнулись, добавили конкретные примеры доработок.
Что мы имели и что необходимо было сделать?
На момент старта доработок уже существовало боевое Flutter приложение с интегрированной TinkoffSDK. В приложении была возможность оплатить PRO-аккаунт по одному из трех тарифов: на месяц, на год, навсегда. По окончании оплаченного периода PRO-аккаунт отключался.
Исходный флоу:
Пользователь выбирает тариф и переходит к оплате;
Мобильное приложение инициирует создание заказа на нашем сервере и получает идентификатор заказа;
Мобильное приложение открывает нативное окно оплаты через TinkoffSDK и передает в нее данные о заказе: стоимость, описание, идентификатор заказа;
Пользователь заполняет платежные данные и производит оплату;
API Тинькова стучится на наш сервер с результатом транзакции и идентификатором заказа;
В зависимости от статуса оплаты сервер присваивает пользователю выбранный тариф или статус ошибки оплаты;
Раз в сутки сервер отключает подписки с истекшим оплаченным периодом.
Цели:
Реализовать автоматическое списание средств при продлении подписки;
Дать пользователю возможность ознакомиться с PRO-функциями во время бесплатного пробного периода;
-
Реализовать автоматическое списание средств по окончании пробного периода.
План:
Изучить тему использования рекуррентных платежей через Tinkoff;
Доработать структуру БД;
Доработать API;
Доработать мобильное приложение.
Погружение в предметную область
Средствами гугления и чтения документации Tinkoff-кассы удалось нарыть, что один из вариантов решения задачи - воспользоваться RebillId из данных, которые Tinkoff передает в уведомлении после успешной авторизации пользователя.
RebillId - идентификатор родительского платежа, на основе которого будет произведено списание средств.
По умолчанию RebillId не приходит. Чтобы его получить, необходимо пометить первый платёж пользователя как родительский. В этом заключалась первая доработка на стороне мобильного приложения. Чтобы в уведомлении от Tinkoff приходил RebillId, достаточно при открытии нативного модального окна TinkoffSDK передавать в опциях заказа “recurrentPayment: true”.
Следующими этапами стали - переработка алгоритма взаимодействия с TinkoffSDK на стороне мобильного приложения и перенос оплаты самих тарифов на сторону нашего сервера. Связано это с тем, что одна из бизнес задач - реализация пробного периода с последующей автоматической оплатой. Необходимо сделать процесс для пользователя максимально линейным и одинаковым вне зависимости от выбранного тарифа. Приняли решение реализовать отдельно процесс привязки карты с помощью TinkoffSDK, и отдельно процесс оформления тарифа через наш сервер.
Процесс привязки карты и оплаты на бумаге выглядит так:
Перед оформлением заказа просим пользователя привязать карту. Для проверки подлинности карты списываем и возвращаем 10₽;
Приложение получает у нашего сервера идентификатор для специального типа заказа “Привязка карты”;
Мобильное приложение открывает нативное модальное окно TinkoffSDK для оплаты 10₽;
Пользователь производит оплату;
После окончания процесса оплаты на сервер приходит уведомление от Tinkoff;
-
В случае успеха сервер привязывает к пользователю RebillId и Pan (замаскированный номер карты для отображения пользователю):
a. Наш сервер вызывает метод cancel для возврата 10₽;
b. Пользователь выбирает необходимый тариф и нажимает “Оформить”;
c. Мобильное приложение отправляет на наш сервер запрос для оформления тарифа;
d. Наш сервер либо активирует пробный период, если он предусмотрен тарифом и доступен пользователю, либо инициирует оплату через Tinkoff.
Первая доработка системы и первые нюансы. Привязка карты пользователя
ТЗ описано, берем в работу.
Добавили в БД платежную информацию пользователя. Добавили метод API для создания OrderId, доработали Callback, через который Tinkoff взаимодействует с нашим сервером. Доработали приложение для привязки карты.
Тестируем. Деньги с карты ушли, обещали вернуться. Ошибок нет. Карта привязана. Но увы, спустя минуту, десять и даже час - деньги на карту не вернулись. Лезем разбираться.
Сохраненный в БД статус платежа - REFUNDED. Значит проблема не в коде. Открываем личный кабинет Tinkoff. Данный платеж - в списке возвратов. В деталях платежа видим комиссию эквайринга и понимаем: это не то, что нам нужно.
В личном кабинете Tinkoff есть два режима списания средств:
Списывать сразу;
Блокировать на счету покупателя.
По умолчанию выбран режим “Списывать сразу”. В таком режиме статус платежа в случае успеха автоматически переходит из статуса AUTHORIZED в статус CONFIRMED, происходит списание средств с карты пользователя и их перевод на наш счет. Возврат в таком случае подразумевает комиссию и занимает продолжительное время.
Режим “Блокировать на счету покупателя” предполагает, что платеж остается в статусе AUTHORIZED в течение 7 дней. Средства пользователя остаются на счету в заблокированном состоянии. Наш сервер подтверждает платеж методом confirm, осуществляя списание средств со счета пользователя, либо отменяет платеж методом cancel, осуществляя разблокировку средств. По истечении 7 дней платеж в статусе AUTHORIZED отменяется автоматически на стороне Tinkoff. Отмена такого платежа не подразумевает комиссии и происходит моментально.
Тестируем. Ошибок нет. Карта привязана. Деньги с карты ушли и тут же пришли обратно. Задача привязки карты решена!
Следующая задача - используя сохраненный RebillId совершить платеж на стороне нашего API без участия пользователя.
Проведение платежа на стороне нашего сервера, общение с поддержкой Tinkoff
Для проведения платежа без участия пользователя на стороне нашего сервера, необходимо вызвать метод init, затем вызвать метод charge и передать в него ранее сохраненный RebillId. Метод charge по умолчанию заблокирован. Для его разблокировки необходимо написать в поддержку Tinkoff, пройти согласование и доработать систему в соответствии со списком требований.
Список требований:
Подробное описание услуг и их стоимость;
Тарифы прописаны в рублях и иностранной валюте (если применимо), указана сумма списания и периодичность;
Уведомление о конвертации курса (если применимо). Например: «конвертация осуществляется по курсу банка-эмитента на момент списания»;
При оформлении оплаты и выбора тарифа, покупатель должен понимать, что оформляет подписку. Это должно указываться на сайте или должна быть техническая возможность выбора оформления подписки/рекуррентных платежей;
У вас в оферте должны быть прозрачно расписаны условия автоплатежа: платежи регулярны и будут списываться безакцептно с карты раз в неделю/месяц/год и т.п.;
Согласие на обработку персональных данных;
Согласие на сохранение учетных данных для будущих транзакций. Продавец должен заключить соглашение с владельцем карты, в котором содержится следующее:
Сокращенная версия сохраненных учетных данных (например, последние четыре цифры кредитной карты);
Способ получения держателем карты уведомления о любых изменениях в платежном соглашении;
Как сохраненные учетные данные будут использованы;
Дата истечения срока действия соглашения, если применимо.
Соглашение с подпиской (отдельно от общей оферты!), которое содержит:
Сумму сделки (включая все налоги, сборы и др. расходы). Если точная сумма недоступна на момент заключения соглашения, соглашение должно содержать объяснение того, как будет рассчитываться сумма транзакции;
Тип валюты, используемой в транзакции;
Правила отмены и возврата, указание контактных данных для обращений клиентов.
Вы должны разместить чекбокс в поле, где проставляется согласие с офертой. Чек-бокс для установки согласия клиентом с условиями оферты рекуррентных платежей и политики обработки персональных данных.
Согласие в чек-боксе не должно быть установлено по умолчанию;
Текст должен быть прописан читабельным шрифтом;
Должна быть указана сумма списания и регулярность.
Вы должны направлять смс и/или email–уведомления клиентам об успешных списаниях;
Ссылка для отмены подписки должна быть размещена:
На главной странице сайта;
В смс и/или email-уведомлениях о списании.
Согласование рекуррентный платежей для первого приложения прошло за 5 итераций и растянулось на несколько недель. Требования со стороны Tinkoff дополнялись и уточнялись. Со вторым приложением всё прошло быстрее, и заняло один день.
По каждому из 11 пунктов мы дали комментарий. Там где требовалось приложили скриншот из приложения.
1, 2 пункты:
3 пункт: У нас нет конвертации
4, 5 пункты:
6 пункт:
7 пункт:
8 пункт:
9 пункт:
10 пункт: Уведомления не предусмотрены. Это указанно в оферте
11 пункт:
Следующие этапы:
доработка БД для использования пробного периода,
доработка API для оформления подписки,
доработка мобильного приложения.
Некоторые выводы
В рамках данной задачи удалось больше погрузиться в работу платежей в различных режимах. Выяснить как работают рекуррентные платежи, к чему стоит быть готовым заранее и как реализовать автоматическое продление подписки.
Вкратце, для подключения рекуррентных платежей в приложении, необходимо:
На стороне Tinkoff:
a. Согласовать с поддержкой Tinkoff открытие метода charge;
b. Переключить режим списания денежных средств на “Блокировать на счету покупателя”.На стороне БД:
a. Предусмотреть структуры для хранения RebillId и Pan привязанных платёжных данных пользователя.На стороне сервера:
a. Реализовать методы для создания заказа на привязку карты;
b. Реализовать методы для оформления и оплаты тарифа;
c. Реализовать метод отмены подписки;
d. Реализовать функцию проверки состояния подписки, запускающейся по CRON;
e. Реализовать callback для получения уведомлений о платежах со стороны Tinkoff.На стороне мобильного приложения:
a. Реализовать оплату родительского заказа для привязки карты;
b. Подключить методы API;
c. Доработать UI приложения в соответствии с требованиями поддержки Tinkoff.
Рекуррентные платежи - тема достаточно простая. Но когда занимаешься этой задачей впервые, при подключении возникают нюансы, которые отнимают много времени. Были бы рады найти подобную статью, когда начали реализовать автопродление подписки в своем приложении. Теперь такая статья есть. Надеемся, она будет полезна и позволит вам сэкономить ни один рабочий час.
Дима, Flutter-разработчик, Progressive Mobile [ссылка удалена мод.]
Комментарии (5)
milast
19.04.2024 11:22+1Только причем тут Flutter?!
evkorm Автор
19.04.2024 11:22Приложения, в рамках которых мы подключали реккуренты, написаны на Flutter. Для работы с тиньковской мобильной sdk использовался Flutter пакет - https://pub.dev/packages/tinkoff_sdk
Ivan22
Ох давно я хотел взглянуть в лицо этим ......... людям, которые подписывают тебя на реккурентные платежи по дефолту.
evkorm Автор
Статья про разработку, а не правильность/неправильность использования реккурентов. Эти вопросы - к бизнесу, а не к тому, кто пишет приложения.
Ну и на счёт включения автосписаний по дефолту... Тиньков со своей стороны выдвигает кучу требований к приложению, чтобы для пользователя было максимально прозрачно за что, сколько и когда с него спишут денег. Соглашаться или не соглашаться с автоплатежами - это выбор пользователя.