Повтори і backoff в платежах
Повтори і backoff в платежах
1) Навіщо потрібні повтори
Конверсія: м'які відмови (timeouts, 3DS-помилки, мережеві збої) часто відновлюються при повторі: + 2-7 п.п. до Auth Rate.
Стійкість: локальні збої PSP/ACS/банку згладжуються ретраями з маршрутами-альтернативами.
Досвід гравця: коректно вибудувані повтори приховують «шум» інфраструктури без подвійних списань.
2) Базові принципи
1. Ідемпотентність на рівні «payment intent» (PI): одна операція = один'idempotency _ key'; будь-який повторний обіг не змінює грошовий стан.
2. Розділення помилок:- Hard decline (напр.'Don't honor'при жорсткій політиці емітента,'Insufficient funds') → зазвичай не ретраїмо відразу.
- Soft decline/технічні (timeout,'Issuer unavailable','Try again') → дозволений ретрай.
- 3. Backoff + обмеження спроб: експоненціально збільшуємо затримку, додаємо джиттер і не перевищуємо ліміти (зазвичай 2-3 спроби).
- 4. Маршрутизація в зв'язці: ретрай - це не тільки «повтор у того ж PSP», а й зміна PSP/MID/3DS-режиму/методу.
- 5. Спостережуваність: кожен hop фіксується в Route Journal (PSP, reason, latency, 3DS-режим, fee, результат).
3) Класифікація помилок для рішення про ретрай
4) Backoff-стратегії (практика)
4. 1 Експоненціальний backoff з джиттером (рекомендований)
База: `delay_n = min(base 2^n, max_delay)`
Джиттер: 'delay = rand (0, delay_n)'- знижує «стампеди», коли багато запитів повторюються одночасно.
Типові параметри: `base=200–500 мс`, `max_delay=5–10 с`, `n≤2–3`.
4. 2 Лінійний backoff
Простий, але гірше при «хвилюваннях» в мережі. Поступається експоненціальному + джиттер.
4. 3 Політика тайм-аутів
Client timeout (ваш) ≤ PSP SLA (наприклад, 3-5 с), інакше зростає ризик дублікатів/зависань.
Окремо задавайте час очікування webhook/confirm: якщо підтвердження не прийшло → компенсуюча звірка (ledger/PSP).
5) Ідемпотентність і захист від дублів
Payment Intent (PI) зберігає статус, суму, метод,'idempotency _ key', історію маршрутів.
Кожен hop і retry використовують один і той же ключ.
Компенсуючі транзакції: при розсинхроні (approve в PSP, а у вас timeout) - «reconcile-pull» + коригування леджера.
Виключайте повторну авторизацію при повторній доставці webhook: перевіряйте'transaction _ id '/' PSP reference'на унікальність.
6) 3DS/SCA і повтори
Soft decline після frictionless → ретрай з challenge.
ACS timeout/unavailable → експоненційний backoff, потім альтернативний канал (open banking/APM) або інший PSP.
При масовій деградації ACS - circuit-breaker, зростання'challenge rate', тимчасові ліміти по сумах.
7) Повтори для APM/open banking
Open banking/instant rails (SEPA Instant/FPS/Pix/UPI):- Ретраї обмежені: перевіряйте ідемпотентність на стороні провайдера і статуси у відкладених webhook'ax.
- При невизначеному статусі - polling з backoff і строгі звірки.
- Ваучери/готівка: ретраї не застосовуються як до «онлайн-транзакції», але діє контроль терміну оплати і «status refresh».
8) Payouts (висновки): повтори та черги
Технічний збій банку/PSP → queued payouts з backoff-дреном.
KYT/velocity fail → не ретраїмо, переведення в ручну перевірку.
Пріоритизація черги: VIP/дрібні суми/давність заявки; дедлайни SLA та авто-ескалації.
Альтернативні рейки (RTP/FPS/SEPA Instant/Pix) на другому кроці ретрая.
9) Circuit-breaker і ретраї
Локальний (на PSP/MID/BIN): при сплеску помилок → зупиняємо ретраї на цьому маршруті, перемикаємося на альтернативний.
Глобальний (на метод/регіон): системна деградація → відключаємо метод, пропонуємо APM/open banking.
Half-open: повертаємо частину трафіку (1-5%) для перевірки відновлення перед повним поверненням.
10) Псевдокод стратегії ретраїв
python def pay_with_retries(pi):
ensure_idempotency(pi.key)
if not compliance_pass(pi): return REJECT
routes = rank_candidates(pi) # по вероятности approve, fee, health attempts = 0 for route in routes:
policy3ds = select_3ds(pi, route)
res = call_psp(route, pi, policy3ds, pi.key, timeout=3.0)
log_attempt(pi, route, res)
if res.approved: return APPROVED
if is_soft_decline(res) or is_transient_error(res):
while attempts < MAX_ATTEMPTS and not breaker_open(route):
delay = backoff_with_jitter(base=0.3, attempt=attempts, cap=8.0)
sleep(delay)
policy3ds = maybe_toggle_3ds(policy3ds, res)
res = call_psp(route, pi, policy3ds, pi.key, timeout=3.0)
log_attempt(pi, route, res)
attempts += 1 if res.approved: return APPROVED if is_hard_decline(res): break перейти к следующему маршруту (PSP-B/APM/open banking)
return DECLINED
11) KPI та цільові орієнтири
Incremental Approvals from Retries: + 2-7 п. п. до базової конверсії.
Avg Retry Attempts per Approved Tx: 1. 2–1. 5 (тримайте нижче 1. 7).
Retry Success Rate (soft/tech): ≥ 25–40%.
Duplicate Rate: 0 при коректній ідемпотентності.
P95 Latency (з урахуванням ретраїв): <7 з до фінальної відповіді.
Payout SLA (instant share): ≥ 70% легких чеків, прострочення <цільового порогу.
12) Плейбуки інцидентів
A. Масові timeouts на PSP-A
1. Відкрити локальний breaker для PSP-A.
2. Перерозподілити ретраї на PSP-B/APM.
3. Експоненціальний backoff з джиттером, ліміт 2-3 спроби.
4. Канарка half-open через 10-15 хв.
B. деградація ACS/3DS
1. Детект по зростанню'soft decline', timeouts.
2. Підвищити challenge rate; частина трафіку → open banking.
3. Відкласти важкі чеки, включити ліміти velocity.
C. Затримки по payouts
1. Переклад в чергу, пріоритизація VIP/малих сум.
2. Рераут на альтернативні rails (RTP/FPS/SEPA Instant/Pix).
3. Комунікація гравцям + авто-ескалації.
13) Спостережуваність і дані
Route Journal: PSP/MID, BIN/issuer, reason, latency, 3DS-режим, retry chain, підсумок, fee.
Дашборди: Auth Rate (по банкам), Retry Success, Avg Attempts, Decline Mix, p95 latency, Payout Queue Depth.
Алерти: spikes за reason-кодами, зростання спроб/latency, переповнення черг висновків.
14) Чек-листи впровадження
Архітектура/дані
- Payment Intent + `idempotency_key` на все hops.
- Конфіг-матриця reason-кодів: retryable vs non-retryable.
- Підписані webhooks, дедуплікація по PSP reference.
Backoff/правила
- Експоненціальний backoff з джиттером; ліміт спроб і час вікна.
- Smart retry: зміна 3DS/MID/PSP/методу; відмінність для карток vs APM/open banking.
- Circuit-breakers (локальні/глобальні), half-open-канарки.
Леджер/звірки
- Компенсуючі транзакції при «підвішених» статусах.
- T + 0/T + 1 звірки: PSP ↔ банк ↔ грошовий леджер.
- Політика тайм-аутів і SLA на confirm/webhook.
Операції/комплаєнс
- RG/санкції/PEP/вік - до ретраїв.
- KYT/velocity на payouts; Правила ручного рев'ю.
- Runbooks і RACI для інцидентів/ескалацій.
15) Економіка і ризик
Рахуйте effective rate з урахуванням 3DS-фії, FX, чарджбек-вартості, ретрай-оверхеда.
Жорстко лімітуйте ретраї по high-risk сегментах, щоб не розганяти chargeback exposure і резерви.
16) Підсумок
Повтори працюють, коли вони керовані: ідемпотентність, ясна матриця reason-кодів, експоненціальний backoff з джиттером, обмеження спроб і зв'язка з маршрутизацією (зміна PSP/3DS/методу). Додайте circuit-breaker, черги для payouts і сильні звірки - і ви стабільно піднімете конверсію, не створюючи дублів і касових «дірок».