Платежі — це завжди асинхронність і винятки
Навіть якщо користувач бачить “успішно”, фактичне підтвердження часто приходить webhook‑подією. Якщо будувати логіку як синхронний запит “списали/не списали”, ви отримаєте розбіжності й ручні розбори.
1) Модель станів транзакції
- created → requires_action (3DS) → pending → succeeded/failed.
- Окремо: canceled/expired/refunded.
2) 3DS і redirect‑сценарії
- Повернення користувача (return_url) не гарантує успіху.
- Після повернення завжди перевіряй статус по API провайдера.
3) Webhooks як джерело підтвердження
- Валідація підпису/секрету.
- Дедуплікація по eventId.
- ACK швидко, обробка — через чергу.
4) Ідемпотентність
- Idempotency‑Key на створення платежу/інвойсу.
- Заборона дублюючих списань при повторі запиту/перезавантаженні сторінки.
5) “Оплата пройшла, а замовлення ні”
Рішення: відокремити транзакцію від бізнес‑об’єкта, мати reconciliation і можливість повторно “прикріпити” успішний платіж до замовлення.
Підсумок
Надійний платіжний flow — це станова модель + webhooks + ідемпотентність + reconciliation. Тоді платежі масштабуються без інцидентів.