Вам нужна банка для чаевых. Вам не хочется отдавать платформе 5 % вечера уличного музыканта, и вы прекрасно умеете разговаривать с API. Значит, вопрос не в том, в какой банке для чаевых зарегистрироваться, а в том, сколько на самом деле надо построить.
Меньше, чем кажется. В Stripe работающий ответ — три вызова API: ни сервера, ни бэкенда, ни вебхук-эндпойнта. Остальная часть этого поста — как раз эта сборка, плюс две вещи, в которых все ошибаются.
Весь фокус — это Price «плати сколько хочешь»
В Stripe есть режим цены, где сумму вводит сам фанат. Он называется
pay what you want — и это вся
функциональность. Вы создаёте Product, вешаете на него Price с
custom_unit_amount[enabled]=true, а сверху —
Payment Link.
# 1. то, что вы "продаёте"
curl https://api.stripe.com/v1/products \
-u "$RK:" \
-d name="Tips — Mira" \
-d "metadata[managed_by]"=my-tip-jar
# 2. цена, которую выбирает фанат
curl https://api.stripe.com/v1/prices \
-u "$RK:" \
-d product=prod_... \
-d currency=eur \
-d "custom_unit_amount[enabled]"=true \
-d "custom_unit_amount[preset]"=500 \
-d "custom_unit_amount[minimum]"=200
# 3. страница
curl https://api.stripe.com/v1/payment_links \
-u "$RK:" \
-d "line_items[0][price]"=price_... \
-d "line_items[0][quantity]"=1 \
-d submit_type=pay
Третий вызов возвращает url. Этот URL и есть ваша банка для чаевых. Это страница, которую
хостит Stripe: она PCI-совместима без вашего участия, локализована и показывает Apple Pay или
Google Pay каждому фанату, у кого они настроены на телефоне, —
динамические способы оплаты
решают это за вас, исходя из устройства и страны. Вы не написали ни строчки фронтенда.
Закодируйте URL в QR-код любой библиотекой — это просто строка — распечатайте, приклейте к футляру. Код никогда не протухнет и не указывает ни на какой ваш сервер, потому что сервера у вас нет.
Два параметра, которые стоит знать:
custom_unit_amount[preset]— сумма, с которой открывается страница.500означает, что фанат видит уже вписанные 5,00 € и может их изменить. Это число делает для вашего среднего чаевого больше, чем всё остальное на странице.custom_unit_amount[minimum]— пол. Поставьте его. Причина — в разделе о комиссиях ниже, и это не погрешность округления.
Можно также собирать имя и сообщение. Payment Links принимают до трёх custom_fields — так вы
получаете «а от кого это было» без постройки формы:
-d "custom_fields[0][key]"=nickname \
-d "custom_fields[0][type]"=text \
-d "custom_fields[0][label][type]"=custom \
-d "custom_fields[0][label][custom]"="Ваше имя или ник" \
-d "custom_fields[0][optional]"=true
У Stripe есть требования к приёму чаевых и пожертвований — прочитайте их один раз. «Плати сколько хочешь» также не сочетается с другими line items, скидками и регулярными платежами. Для банки с чаевыми ничто из этого не мешает.
Эту разницу стоит понимать правильно. Stripe формулирует так: чаевые дают за уже оказанный
товар или услугу, а пожертвование должно быть привязано к благотворительной цели. Вы
отыграли сет; чаевые за него и платят. Поэтому вызов выше отправляет submit_type=pay, а
не donate — donate разместил бы вашу ссылку на donate.stripe.com и напечатал бы на
кнопке Пожертвовать. Это другой бизнес, и такой, который Stripe проверяет заметно
строже.
Ключ: считайте, что он утечёт, — и сделайте это скучным
Не кладите секретный ключ (sk_live_…) на устройство, которое стоит на сцене. Возьмите
ограниченный ключ (rk_live_…): вы выбираете
право для каждого ресурса, а всё, что не выбрали, остаётся на None.
Для сборки выше полный список — пять строк:
| Ресурс | Право | Что это даёт |
|---|---|---|
| Products | Write | создать Product |
| Prices | Write | создать Price «плати сколько хочешь» |
| Payment Links | Write | создать ссылку |
| Checkout Sessions | Read | видеть пришедшие чаевые |
| Events | Read | живая лента (следующий раздел) |
Всё остальное — Balance, Payouts, Refunds, Customers, PaymentIntents, весь Connect — остаётся на None.
А теперь сделайте упражнение, ради которого всё это и затевалось. В час ночи ваш планшет уводят со столика с мерчем. Что вор сделает с ключом в кейчейне? Прочитает историю чаевых и создаст ещё несколько ссылок на чаевые в вашем аккаунте. Это весь радиус поражения. Он не увидит баланс, не запустит выплату, не оформит возврат на свою карту, не прочитает список клиентов. Вы отзываете ключ с телефона в такси по дороге домой — и устройство гаснет. Из ваших денег не сдвинулось ничего.
Эта асимметрия — запись в банку для чаевых и ноль доступа к деньгам — единственная причина, по которой бессерверный дизайн со своим ключом вообще можно защищать. Она же — причина, почему «Login with Stripe» здесь не ответ: OAuth требует сервера разработчика приложения, чтобы держать ваш токен, — а сервер это ровно то, чего мы не строим.
(Странность, на которую вы наткнётесь: право Prices внутри называется plan_write, поэтому сообщение
об ошибке Stripe называет scope, которого под таким именем в дашборде нет. Речь про Prices.)
Читать чаевые без вебхука
Здесь большинство руководств либо останавливается, либо хватается за вебхук — и здесь сцена по-настоящему отличается от веб-приложения.
Вебхук — это входящий HTTP-запрос. Планшет за микрофонной стойкой не может его принять. Он сидит в гостевом Wi-Fi заведения за NAT, у него нет публичного адреса и TLS-сертификата — и незачем им быть. Если пойти путём вебхука, придётся поднять сервер, который ловит события, и сокет, который пропихивает их на устройство: бэкенд, эксплуатационная нагрузка и место, где теперь живут имена ваших фанатов. Вы только что заново построили ту самую платформу, которой хотели избежать.
Поэтому тяните, а не ждите, пока толкнут. Эндпойнт Stripe List all events публичный, задокументированный и возвращает события, начиная с самых новых:
curl -G https://api.stripe.com/v1/events \
-u "$RK:" \
-d "types[]"=checkout.session.completed \
-d "types[]"=checkout.session.async_payment_succeeded \
-d ending_before=evt_ПОСЛЕДНЕЕ_ВИДЕННОЕ \
-d limit=100
ending_before — это весь дизайн. Храните id самого свежего обработанного события; каждый опрос просит всё строго
более новое, а вы двигаете курсор. Никаких таймстемпов, никакого рассинхрона часов, никакой дедупликации по сумме.
На первом опросе сета попросите limit=1 без курсора, чтобы зацепиться за то, что уже есть, — иначе на саундчеке
переиграете чаевые сегодняшнего утра.
Дальше фильтруйте то, что вернулось. Оба типа событий могут выстрелить на один платёж, поэтому дедуплицируйте по id
Checkout Session. Проверяйте payment_status == "paid" — завершённая сессия не обязательно оплаченная. И проверяйте,
что payment_link совпадает с вашей ссылкой, потому что /v1/events работает на уровне всего аккаунта и с радостью
подсунет вам трафик от всего остального, что этот аккаунт Stripe делает.
Будьте честны насчёт компромиссов, они настоящие:
- Stripe рекомендует вебхуки. Опрос — не благословенный путь; это задокументированный эндпойнт, который применяют осознанно. Напишите об этом в README и идите дальше.
- События уходят на 30 дней назад. Слова самого Stripe: «List events, going back up to 30 days.» Это живая лента, а не ваша бухгалтерская книга. Ваша книга — Checkout Sessions, а настоящая книга — дашборд Stripe.
- Следите за квотой на чтение. Все смотрят на лимит в секунду (rate limits: 100 запросов/с в live) и никто — на другой: Stripe выделяет примерно 500 запросов на чтение на одну транзакцию за скользящие 30 дней, с полом в 10 000 чтений в месяц. Опрашивайте раз в 4 секунды — и трёхчасовой сет это ~2 700 чтений. Четыре длинных концерта за месяц, и вы на полу. Чаевые докупают запас по мере поступления, но тот, кто опрашивает раз в секунду, потому что «так бодрее», найдёт потолок. Четыре секунды — не ленивое число; это то самое число.
Вот честная форма дела: опрос стоит вам пары тысяч GET-запросов и покупает удаление целого бэкенда.
Арифметика комиссий, сделанная как следует
Платформа, рекламирующая 0 %, не бесплатна — и это тоже. Собственная комиссия Stripe за обработку применяется к каждому чаевому, и Stripe выставляет её вам напрямую. Сегодня, по евро-прайсу Stripe, стандартная карта ЕЭЗ стоит 1,5 % + 0,25 €. Премиальные карты ЕЭЗ — 1,9 % + 0,25 €, британские — 2,5 % + 0,25 €, всё остальное — 3,25 % + 0,25 € плюс ещё 2 %, если нужна конвертация валюты. (В США это 2,9 % + 0,30 $, что хуже ровно по причине ниже.)
Проблема не в проценте. Проблема в двадцати пяти центах.
| Чаевые | Stripe забирает | Артисту остаётся | Реальная доля |
|---|---|---|---|
| 2 € | 0,28 € | 1,72 € | 14,0 % |
| 5 € | 0,33 € | 4,67 € | 6,5 % |
| 10 € | 0,40 € | 9,60 € | 4,0 % |
| 20 € | 0,55 € | 19,45 € | 2,8 % |
| 50 € | 1,00 € | 49,00 € | 2,0 % |
Фиксированная комиссия — это процент в маскировке, и на малых деньгах маскировка сползает. Те же 0,25 €, невидимые на чаевых в 50 €, съедают восьмую часть чаевых в 2 €. Чаевые малы по своей природе — именно это и делает их чаевыми — так что это не краевой случай, а медианный.
Именно поэтому вы ставите custom_unit_amount[minimum]. Где-то около 2 € транзакция перестаёт быть достойной обработки;
карточные чаевые в 0,50 € придут как 0,24 € и обойдутся Stripe в переводе дороже, чем стоят. Выбирайте пол осознанно, а не
обнаруживайте его при первой выплате.
И заметьте, что это делает со сравнением, с которого вы начали. Платформа, берущая 0 % поверх Stripe, берёт с вас 0 % поверх вот этого. Их 0 % настоящие — и это 0 % от того, что оставил процессинг. Ничья карточная рельса не бесплатна: честная формулировка — «никакой доли сверх той, что берёт процессинг», а кто утверждает больше, тот либо врёт, либо не использует карты.
Что у вас теперь есть — и чего нет
Три вызова API и QR-код — и настоящая банка для чаевых: хостируемая, PCI-совместимая, Apple Pay, Google Pay, чаевые падают на ваш собственный баланс Stripe по вашему собственному графику выплат, и никакого сервера по дороге. Для многих это искренне конец проекта, и вы вполне можете остановиться здесь и выпустить это.
Чего у вас нет — так это сцены. У вас есть страница оплаты. Между ними стоят скучные вещи: цикл опроса с курсором и бэкоффом;
экран, который видит публика, с целью и последним сообщением; место для ключа, которое не называется localStorage; блокировка,
чтобы чужой не тыкал в планшет между сетами; и слой из тысячи мелких решений о том, что делать, когда Wi-Fi заведения падает
посреди сета.
Именно это и есть live.tips — ровно эта архитектура, доведённая до конца, под лицензией
MIT. Ограниченный ключ с теми пятью правами, курсорный цикл по /v1/events, создание Product/Price/Payment Link — всё работает на
устройстве артиста, против его собственного аккаунта. В пути к Stripe нет сервера live.tips и нигде нет баланса live.tips — об этом
мы написали отдельно в как live.tips обращается с деньгами.
Читайте исходники, забирайте нужные куски или просто пользуйтесь. Смысл этого поста в том, что архитектура — не секрет и не сложность: Stripe захостит вашу банку для чаевых бесплатно, а ограниченный ключ плюс цикл опроса — это всё, что стоит между артистом и его собственными деньгами. Нам важнее, чтобы вы это знали, чем чтобы вы где-то регистрировались.