Оповещения о платежах Stripe: звонок, когда деньги под угрозой

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

Обновлено

Содержание

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

Решение — не больше уведомлений. Решение — дать каждому событию Stripe ту срочность, которой оно действительно заслуживает: тихий push, когда кто-то заплатил, срочное оповещение, когда не прошло продление, и телефонный звонок, когда приходит спор со сроком ответа.

Какие события Stripe действительно требуют вас

Не каждое событие достойно уведомления, а те, что достойны, срочны по-разному.

Событие StripeПочему это важноСрочность
payment_intent.succeededКто-то заплатил. Приятно знать, никогда не срочно.Обычная
invoice.payment_failedПродление не прошло. До окончания подписки есть окно повторных попыток.Срочная
charge.dispute.createdЧарджбэк. Stripe называет срок ответа, заданный платёжной системой, и его пропуск проигрывает дело автоматически.Звонок
payment_intent.payment_failedРазовый платёж не прошёл. Полезно в объёме, шумно поштучно.Обычная или никакой

Звонка заслуживает только спор. Всё остальное подождёт, пока вы сами возьмёте телефон; чарджбэк, замеченный через неделю, — это деньги, которые вы и так не удержали бы.

Шаг 1 — По каналу на каждый уровень срочности

Создайте в Echobell три канала, потому что срочность задаётся на уровне канала:

  • Stripe · Выручка — обычные уведомления об успешных платежах
  • Stripe · Платёж не прошёл — срочные
  • Stripe · Спор — звонок

Скопируйте webhook-URL каждого канала из его настроек. Stripe будет указывать на все три.

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

Шаг 2 — Добавьте эндпоинты в Stripe

В панели Stripe откройте Developers → Webhooks → Add endpoint, вставьте URL канала и выберите только те события, которые должен обрабатывать этот канал.

Делайте по эндпоинту на канал, а не один на всё. Stripe позволяет выбирать события для каждого эндпоинта, так что маршрутизация остаётся на его стороне, а каналы — простыми.

Если хочется обойтись одним эндпоинтом, направьте его в один канал и отфильтруйте условием:

type == "charge.dispute.created"

Условия пишутся без фигурных скобок {{ }}, которые используются в шаблонах.

Шаг 3 — Превратите payload в читаемое

Stripe присылает JSON такой формы:

{
  "type": "charge.dispute.created",
  "data": {
    "object": {
      "amount": 4900,
      "currency": "usd",
      "reason": "fraudulent",
      "status": "warning_needs_response"
    }
  }
}

Поэтому шаблоны канала лезут внутрь data.object:

Заголовок

Спор: {{data.object.reason}}

Текст

{{data.object.amount}} {{data.object.currency}} оспорено
Статус: {{data.object.status}}

Ловушка, в которую попадают все: суммы Stripe указаны в наименьшей единице валюты. 4900 — это 49,00 $, а не 4 900 $. Шаблоны Echobell подставляют значение как есть, поэтому либо делите перед отправкой, либо пишите валюту следом и читайте как центы. Если число должно быть верным с первого взгляда, поместите деление в то, что стоит между Stripe и Echobell: Worker, автоматизацию, собственный эндпоинт.

Для канала выручки та же мысль, но короче:

Заголовок

+{{data.object.amount}} {{data.object.currency}}

Текст

{{data.object.description}}

Шаг 4 — Проверьте, не дожидаясь настоящего платежа

Stripe CLI умеет отправлять на ваш эндпоинт настоящие payload-ы событий:

stripe trigger payment_intent.succeeded
stripe trigger charge.dispute.created

Сделайте это до того, как начнёте полагаться. Это ещё и самый быстрый способ увидеть, что отрисует шаблон, когда поле, которое вы считали обязательным, придёт как null.

Чего эта схема не даёт

Два ограничения, о которых стоит знать, прежде чем ставить её перед своей выручкой.

Echobell не проверяет подпись Stripe. Stripe подписывает каждый вебхук заголовком Stripe-Signature, и для проверки нужен секрет эндпоинта. Вебхук-эндпоинт Echobell такой проверки не делает, поэтому канал защищает только его URL. Относитесь к нему как к секрету, оставьте Только POST включённым и не вставляйте его в публичный репозиторий или скриншот.

Слабую проверку можно добавить условием:

header["stripe-signature"] != ""

Оно подтверждает, что нечто шлёт запрос в форме Stripe. Оно не подтверждает, что это был Stripe. Если поддельное уведомление нанесёт реальный ущерб, а не просто на минуту собьёт с толку, поставьте впереди собственный эндпоинт, проверяйте подпись там и уже оттуда вызывайте Echobell.

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

Частые вопросы

Может ли Stripe позвонить мне напрямую?

Нет. Stripe отправляет письма и вебхуки. Звонок означает передачу вебхука тому, кто умеет его совершать.

Разбудит ли меня оповещение о споре?

Если канал в режиме звонка — да: оно приходит входящим вызовом и звонит сквозь режим фокусирования и «Не беспокоить». См. как пробить режим фокусирования iOS для критичных оповещений.

Может ли сооснователь получать те же оповещения?

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

А тестовый режим?

События тестового режима Stripe уходят на эндпоинты, зарегистрированные в тестовом режиме. Зарегистрируйте URL канала в обоих, если хотите видеть этот трафик, или только в тестовом, пока не начнёте доверять схеме.

Итог

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

Скачайте Echobell для iPhone или возьмите в Google Play, затем выполните stripe trigger charge.dispute.created и дайте телефону зазвонить один раз намеренно.


Связанные материалы