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

  • Списывать сразу;

  • Блокировать на счету покупателя.

По умолчанию выбран режим “Списывать сразу”. В таком режиме статус платежа в случае успеха автоматически переходит из статуса 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. Ссылка для отмены подписки должна быть размещена:

  • На главной странице сайта;

  • В смс и/или email-уведомлениях о списании.

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

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

1, 2 пункты:

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

4, 5 пункты:

6 пункт:

7 пункт:

8 пункт:

9 пункт:

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

11 пункт:

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

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

  • доработка 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 [ссылка удалена мод.]

Комментарии (5)


  1. Ivan22
    19.04.2024 11:22
    +3

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


    1. evkorm Автор
      19.04.2024 11:22

      Статья про разработку, а не правильность/неправильность использования реккурентов. Эти вопросы - к бизнесу, а не к тому, кто пишет приложения.
      Ну и на счёт включения автосписаний по дефолту... Тиньков со своей стороны выдвигает кучу требований к приложению, чтобы для пользователя было максимально прозрачно за что, сколько и когда с него спишут денег. Соглашаться или не соглашаться с автоплатежами - это выбор пользователя.


  1. milast
    19.04.2024 11:22
    +1

    Только причем тут Flutter?!


    1. evkorm Автор
      19.04.2024 11:22

      Приложения, в рамках которых мы подключали реккуренты, написаны на Flutter. Для работы с тиньковской мобильной sdk использовался Flutter пакет - https://pub.dev/packages/tinkoff_sdk


      1. MiT_73
        19.04.2024 11:22

        А зачем создали свою реализацию? Почему не использовали что-то готовое?

        https://pub.dev/packages/tinkoff_acquiring