Рекуррентные платежи, или как мы доработали функционал платных подписок во Flutter приложении

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

Что мы имели и что необходимо было сделать?

На момент старта доработок уже существовало боевое Flutter приложение с интегрированной TinkoffSDK. В приложении была возможность оплатить PRO-аккаунт по одному из трех тарифов: на месяц, на год, навсегда. По окончании оплаченного периода PRO-аккаунт отключался.

Исходный флоу:

  1. Пользователь выбирает тариф и переходит к оплате;

  2. Мобильное приложение инициирует создание заказа на нашем сервере и получает идентификатор заказа;

  3. Мобильное приложение открывает нативное окно оплаты через TinkoffSDK и передает в нее данные о заказе: стоимость, описание, идентификатор заказа;  

  4. Пользователь заполняет платежные данные и производит оплату;

  5. API Тинькова стучится на наш сервер с результатом транзакции и идентификатором заказа;

  6. В зависимости от статуса оплаты сервер присваивает пользователю выбранный тариф или статус ошибки оплаты;

  7. Раз в сутки сервер отключает подписки с истекшим оплаченным периодом.

Цели:

  1. Реализовать автоматическое списание средств при продлении подписки;

  2. Дать пользователю возможность ознакомиться с PRO-функциями во время бесплатного пробного периода;

  3. Реализовать автоматическое списание средств по окончании пробного периода.

План:

  1. Изучить тему использования рекуррентных платежей через Tinkoff;

  2. Доработать структуру БД;

  3. Доработать API;

  4. Доработать мобильное приложение.

Погружение в предметную область

Средствами гугления и чтения документации Tinkoff-кассы удалось нарыть, что один из вариантов решения задачи — воспользоваться RebillId из данных, которые Tinkoff передает в уведомлении после успешной авторизации пользователя.

RebillId — идентификатор родительского платежа, на основе которого будет произведено списание средств.

 По умолчанию RebillId не приходит. Чтобы его получить, необходимо пометить первый платёж пользователя как родительский. В этом заключалась первая доработка на стороне мобильного приложения. Чтобы в уведомлении от Tinkoff приходил RebillId, достаточно при открытии нативного модального окна TinkoffSDK передавать в опциях заказа »recurrentPayment: true».

Следующими этапами стали — переработка алгоритма взаимодействия с TinkoffSDK на стороне мобильного приложения и перенос оплаты самих тарифов на сторону нашего сервера. Связано это с тем, что одна из бизнес задач — реализация пробного периода с последующей автоматической оплатой. Необходимо сделать процесс для пользователя максимально линейным и одинаковым вне зависимости от выбранного тарифа. Приняли решение реализовать отдельно процесс привязки карты с помощью TinkoffSDK, и отдельно процесс оформления тарифа через наш сервер.

Процесс привязки карты и оплаты на бумаге выглядит так:

  1. Перед оформлением заказа просим пользователя привязать карту. Для проверки подлинности карты списываем и возвращаем 10₽;

  2. Приложение получает у нашего сервера идентификатор для специального типа заказа «Привязка карты»;

  3. Мобильное приложение открывает нативное модальное окно TinkoffSDK для оплаты 10₽;

  4. Пользователь производит оплату;

  5. После окончания процесса оплаты на сервер приходит уведомление от Tinkoff;

  6. В случае успеха сервер привязывает к пользователю RebillId и Pan (замаскированный номер карты для отображения пользователю):

    a. Наш сервер вызывает метод cancel для возврата 10₽;
    b. Пользователь выбирает необходимый тариф и нажимает «Оформить»;
    c. Мобильное приложение отправляет на наш сервер запрос для оформления тарифа;
    d. Наш сервер либо активирует пробный период, если он предусмотрен тарифом и доступен пользователю, либо инициирует оплату через Tinkoff.

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

ТЗ описано, берем в работу. 

Добавили в БД платежную информацию пользователя. Добавили метод API для создания OrderId, доработали Callback, через который Tinkoff взаимодействует с нашим сервером. Доработали приложение для привязки карты. 

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

Сохраненный в БД статус платежа — REFUNDED. Значит проблема не в коде. Открываем личный кабинет Tinkoff. Данный платеж — в списке возвратов. В деталях платежа видим комиссию эквайринга и понимаем: это не то, что нам нужно.

В личном кабинете Tinkoff есть два режима списания средств:

9f85fc43c85450ccab38a62013c07310.jpeg

По умолчанию выбран режим «Списывать сразу». В таком режиме статус платежа в случае успеха автоматически переходит из статуса AUTHORIZED в статус CONFIRMED, происходит списание средств с карты пользователя и их перевод на наш счет. Возврат в таком случае подразумевает комиссию и занимает продолжительное время.

Режим «Блокировать на счету покупателя» предполагает, что платеж остается в статусе AUTHORIZED в течение 7 дней. Средства пользователя остаются на счету в заблокированном состоянии. Наш сервер подтверждает платеж методом confirm, осуществляя списание средств со счета пользователя, либо отменяет платеж методом cancel, осуществляя разблокировку средств. По истечении 7 дней платеж в статусе AUTHORIZED отменяется автоматически на стороне Tinkoff. Отмена такого платежа не подразумевает комиссии и происходит моментально.

Тестируем. Ошибок нет. Карта привязана. Деньги с карты ушли и тут же пришли обратно. Задача привязки карты решена!  

Следующая задача — используя сохраненный RebillId совершить платеж на стороне нашего API без участия пользователя.

Проведение платежа на стороне нашего сервера, общение с поддержкой Tinkoff

Для проведения платежа без участия пользователя на стороне нашего сервера, необходимо вызвать метод init, затем вызвать метод charge и передать в него ранее сохраненный RebillId. Метод charge по умолчанию заблокирован. Для его разблокировки необходимо написать в поддержку Tinkoff, пройти согласование и доработать систему в соответствии со списком требований.

Список требований:

  1. Подробное описание услуг и их стоимость;

  1. Тарифы прописаны в рублях и иностранной валюте (если применимо), указана сумма списания и периодичность;

  1. Уведомление о конвертации курса (если применимо). Например: «конвертация осуществляется по курсу банка-эмитента на момент списания»;

  1. При оформлении оплаты и выбора тарифа, покупатель должен понимать, что оформляет подписку. Это должно указываться на сайте или должна быть техническая возможность выбора оформления подписки/рекуррентных платежей;

  1. У вас в оферте должны быть прозрачно расписаны условия автоплатежа: платежи регулярны и будут списываться безакцептно с карты раз в неделю/месяц/год и т.п.;

  1. Согласие на обработку персональных данных;

  1. Согласие на сохранение учетных данных для будущих транзакций. Продавец должен заключить соглашение с владельцем карты, в котором содержится следующее:

  • Сокращенная версия сохраненных учетных данных (например, последние четыре цифры кредитной карты);

  • Способ получения держателем карты уведомления о любых изменениях в платежном соглашении;

  • Как сохраненные учетные данные будут использованы;

  • Дата истечения срока действия соглашения, если применимо.

  1. Соглашение с подпиской (отдельно от общей оферты!), которое содержит:

  • Сумму сделки (включая все налоги, сборы и др. расходы). Если точная сумма недоступна на момент заключения соглашения, соглашение должно содержать объяснение того, как будет рассчитываться сумма транзакции;

  • Тип валюты, используемой в транзакции;

  • Правила отмены и возврата, указание контактных данных для обращений клиентов.

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

  • Согласие в чек-боксе не должно быть установлено по умолчанию;

  • Текст должен быть прописан читабельным шрифтом;

  • Должна быть указана сумма списания и регулярность.

  1. Вы должны направлять смс и/или email–уведомления клиентам об успешных списаниях;

  2. Ссылка для отмены подписки должна быть размещена:

Согласование рекуррентный платежей для первого приложения прошло за 5 итераций и растянулось на несколько недель. Требования со стороны Tinkoff дополнялись и уточнялись. Со вторым приложением всё прошло быстрее, и заняло один день. 

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

1, 2 пункты:

c54728ab0650facb17bca495290f5e84.jpg

3 пункт: У нас нет конвертации

4, 5 пункты:

73791c1b4e6cadc326c330a0976d5271.jpg

6 пункт:

8882255cae650308a9e56457ef20d2b6.jpg

7 пункт:

b26db20fd6dcd5727a9894bfa94e7027.jpga91bfcc0cb84e2d3993ef0d01f6903ac.jpg

8 пункт:

d08a5cf7c5120a082a438bd42d7ad1f3.jpg

9 пункт:

e7be94a2a618fec507b61595dd36a186.jpgb7f08d8448999fcba67a60896691fee2.jpg

10 пункт: Уведомления не предусмотрены. Это указанно в оферте

11 пункт:

50715121f6fbedc398cdff68ad979fc1.jpg276e781649cd51a8582380e49c0bd51b.jpg

Следующие этапы:

  • доработка БД для использования пробного периода,  

  • доработка API для оформления подписки,  

  • доработка мобильного приложения.

Некоторые выводы

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

Вкратце, для подключения рекуррентных платежей в приложении, необходимо:

  1. На стороне Tinkoff:
    a. Согласовать с поддержкой Tinkoff открытие метода charge;
    b. Переключить режим списания денежных средств на «Блокировать на счету покупателя».

  2. На стороне БД:
    a. Предусмотреть структуры для хранения RebillId и Pan привязанных платёжных данных пользователя.

  3. На стороне сервера:
    a. Реализовать методы для создания заказа на привязку карты;
    b. Реализовать методы для оформления и оплаты тарифа;
    c. Реализовать метод отмены подписки;
    d. Реализовать функцию проверки состояния подписки, запускающейся по CRON;
    e. Реализовать callback для получения уведомлений о платежах со стороны Tinkoff.

  4. На стороне мобильного приложения:
    a. Реализовать оплату родительского заказа для привязки карты;
    b. Подключить методы API;
    c. Доработать UI приложения в соответствии с требованиями поддержки Tinkoff.

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

Дима, Flutter-разработчик, Progressive Mobile [ссылка удалена мод.]

© Habrahabr.ru