Звірка платежів і звітів PSP
TL; DR
Звірка - це щоденне автоматизоване зшивання вашого леджера і подій (auth/capture/refund/payout) зі звітами PSP/еквайєрів/банків. Ключ до успіху: єдина модель даних, детерміновані ключі зіставлення, сувора ідемпотентність, толеранси за сумами/FX/часу, черга DLQ для спірних кейсів і плейбуки автокорекцій. KPI: Recon Mismatch Rate↓, Aging of Unreconciled↓, Auto-match%↑.
1) Навіщо і що звіряємо
Цілі: підтвердження виручки і комісій, виявлення дублів/втрат, коректний settlement по днях і валютах, контроль refund-to-source, відповідність аудиту/регулятору.
Об'єкти звірки:- Deposits: `auth → capture → settle`
- Refunds: full/partial, статуси та суми
- Payouts/Withdrawals: вихідні платежі за методами
- Fees & Adjustments: комісії PSP, схеми інтерчейнджа, корекції
- Chargebacks/Disputes: поза вашою ініціативою
- FX/Конвертації: курси, спреди, момент фіксації
2) Джерела даних
Internal Events (ваші): шина подій/Kafka,'payments _ flat','refunds','payouts', ваш Ledger.
PSP Reports (SFTP/API/webhook dump):- Transactions (операційні виписки)
- Settlements/Batches (розбивки зарахувань T + N)
- Fees/Statements (комісії, коригування)
- Chargebacks/Disputes
- Payouts/OCT/RTP/SEPA реєстри
- Bank Statements: MT940/CSV/ISO20022 CAMT, підтяжка зарахувань.
3) Ключі зіставлення (matching keys)
Пріоритетне дерево ключів (за спаданням точності):1. provider_txid ↔ provider_txid (унікальний ID PSP)
2. idempotency_key/ merchant_reference (якщо стабільні у PSP)
3. (amount, currency, timestamp_bucket, last4/bin, auth_code)
4. Fuzzy-шар: ± толеранси за сумою/часом, BIN/issuer country, status family
Рекомендації:- Зберігайте у себе обидва: `payment_id` и `provider_txid`.
- Для partial/refund - додавайте'sequence _ index'або'refund _ line _ id'.
- Для payouts — `payout_batch_id + line_id`.
- Для FX -'exec _ id'( конвертація) і джерело курсу.
4) Модель даних (нормалізований шар)
json
{
"source": "INTERNAL PSP_TX PSP_SETTLEMENT BANK",
"provider": "Acquirer_A",
"payment_id": "pay_123",
"provider_txid": "psp_tx_789",
"kind": "AUTH CAPTURE REFUND PAYOUT FEE SETTLEMENT CHARGEBACK",
"sequence": 0,
"amount": 100. 00,
"currency": "EUR",
"fee_amount": 1. 20,
"fx_rate": 1. 0000,
"fx_src": "PSP ECB BANK",
"status": "APPROVED CAPTURED SUCCESS FAILED SETTLED",
"event_ts": "2025-11-03T12:00:00Z",
"settlement_date": "2025-11-05",
"account": "PSP_MERCHANT_CARD_A",
"matching_keys": {
"provider_txid": "psp_tx_789",
"merchant_ref": "mr_456",
"idem_key": "idem_abc"
},
"hash_row": "sha256(...)"
}
5) Процес звірки (ETL/оркестрація)
1. Ingest: забираємо звіти PSP (SFTP/API), валідуємо схему/підписи, зберігаємо в'raw'.
2. Normalize: маппім поля в уніфікований формат (currency ISO, decimals, таймзона UTC).
3. Match: алгоритм зіставлення по дереву ключів з толерансами.
4. Post-match: формуємо diff (розбіжності) і journal entries для леджера/корекцій.
5. Settle: зшиваємо'PSP _ SETTLEMENT ↔ BANK'( зарахування на рахунок), розкидаємо по днях/батчах.
6. Report: дашборд, алерти; спірні в DLQ на ручний розбір/автопереігрування.
Ідемпотентність: на кожен файл/сторінку -'ingest _ id'. Повторне завантаження не змінює результат.
6) Толеранси (tolerances) і правила
Час: '± 15 хв'для транзакцій,'± 1 дн'для settlement.
Сума: `≤ 0. 01'базової валюти або'≤ 10 bps'для FX/fee-різниць.
FX: допускаємо розбіжність з банком, якщо джерело курсу різне; фіксуємо'fx _ src'.
Partial/Multiple: сума по лініях partial/refund повинна дорівнювати внутрішньому залишку.
7) Обробка розбіжностей (diff taxonomy)
8) Ledger & Accounting (проводки)
Capture: `DR Accounts Receivable / CR Revenue` и `DR Cash (upon settle) / CR Accounts Receivable`
Fee: `DR Fees / CR Cash or Payable`
Refund: зворотні проводки пропорційно partial
Chargeback: окремі рахунки та резерв під спори
FX reval: щоденна переоцінка залишку AR/кешу за курсом'fx _ src _ policy '
9) KPI і цілі
Auto-match% = автоматично зіставлені рядки/всі рядки (мета ≥ 95%)
Recon Mismatch Rate = diff-рядки/всі рядки (≤ 1-2%)
Aging of Unreconciled: p50/p95 днів перебування в DLQ (p95 ≤ 3 дн)
Settlement Timeliness: частка батчів, зшитих з банком D-день (≥ 99%)
Fee Accuracy: розбіжності комісій з провайдерів (≤ 0. 1% обороту)
Duplicate/Orphan Incidents: прагне до 0
10) SQL-зрізи
10. 1 Базове зіставлення за provider_txid
sql
WITH i AS (
SELECT provider, provider_txid, kind, amount, currency, event_ts
FROM internal_norm
),
p AS (
SELECT provider, provider_txid, kind, amount, currency, event_ts
FROM psp_norm
)
SELECT
COALESCE(i. provider_txid, p. provider_txid) AS txid,
COALESCE(i. provider, p. provider) AS provider,
i.kind AS kind_internal, p. kind AS kind_psp,
i.amount AS amount_internal, p. amount AS amount_psp,
i.currency, p. currency,
CASE
WHEN i.provider_txid IS NULL THEN 'MISSING_INTERNAL'
WHEN p. provider_txid IS NULL THEN 'MISSING_PSP'
WHEN ABS(i. amount - p. amount) > 0. 01 THEN 'AMOUNT_MISMATCH'
ELSE 'MATCHED'
END AS recon_status
FROM i
FULL OUTER JOIN p USING (provider, provider_txid);
10. 2 Settlement ↔ Bank
sql
SELECT s. settlement_date, s. batch_id, s. currency,
s. amount_settled, b. amount_bank,
(b. amount_bank - s. amount_settled) AS diff
FROM psp_settlements s
LEFT JOIN bank_statements b
ON b. value_date = s. settlement_date
AND b. currency = s. currency
AND ABS(b. amount_bank - s. amount_settled) <= 0. 5;
10. 3 Aging DLQ
sql
SELECT diff_type,
COUNT() AS cnt,
PERCENTILE_CONT(0. 5) WITHIN GROUP (ORDER BY AGE(NOW(), created_at)) AS p50_age,
PERCENTILE_CONT(0. 95) WITHIN GROUP (ORDER BY AGE(NOW(), created_at)) AS p95_age
FROM recon_dlq
GROUP BY diff_type
ORDER BY cnt DESC;
11) Особливості по рейках/кейсах
Карти: відмінності між'auth'і'capture', пізні'settlement'коригування, інтерчейндж/схемні fee - окремими лініями.
A2A/Open Banking/RTP: миттєві підтвердження, але можливі'reversal'; звіряйте'payout'і повернення.
Гаманці: часто ідеальні'provider _ txid', швидкі'refund'; слідкуйте за fee-лініями.
Ваучери: немає симетричного refund - відображайте коректно в політиці і звітах.
Крипто: он-чейн hash ↔ provider_txid; конфірми N; облік комісій мережі та можливих ребейтів; курс - на момент конвертації.
12) Операційні плейбуки
Сплеск MISSING_INTERNAL: перевірити втрату вебхуків/ретраїв, переграти ingestion, включити полінг API.
AMOUNT_MISMATCH у одного PSP: порівняти заокруглення/ПДВ/fee-модель, запитати коригувальний statement.
Settlement не зшивається з банком: перевірити value date, комісії банку, затримки T + N; тимчасово ставити в «Suspense Account».
Масові REFUND_OVER: негайний стоп авто-рефандів, аудит ідемпотентності, ручна корекція.
FX_DRIFT: зафіксувати policy джерела курсу (ECB/PSP/BANK), перерахувати P & L-різниці.
13) Контроль і безпека
Ідемпотентність ingestion: 'file _ id + checksum'і журнал завантажень.
Доступи (RBAC) і 4-очний контроль: на ручні корекції/журнальні проводки.
Аудит-трейл: всі матчі/дифи/корекції - в незмінному журналі.
Якість даних: схеми, обов'язкові поля, валідація валют/скейла.
14) Дашборд і алерти
Віджети: Auto-match%, Mismatch Rate, Aging DLQ, Settlement Timeliness, Fee Accuracy, топ-PSP по дифам, карта diff-типів.
Алерти:- 'Auto-match% <90%'по провайдеру/дню → P1
- 'Aging p95> 3 дн'→ P2
- `AMOUNT_MISMATCH spike` → P1
- 'Bank≠Settlement'по сумі/валюті → P0
15) Тест-кейси (UAT/Prod)
1. Повторне завантаження того ж файлу → 0 побічних ефектів (ідемпотентність).
2. Часткові рефанди (3 лінії) → сума збігається, матч по sequence.
3. FX-конвертація: розбіжність курсу в межах толерансу → коректний match.
4. Дублікати provider_txid у звіті → дедуп і алерт.
5. Зниклий webhook capture → полінг закрив gap, статус вирівняний.
6. Settlement батч з fee-лінією → правильна розбивка на Revenue/Fee/Net.
16) Часті помилки і як уникнути
Змішування баз порівняння (compare'attempt'vs'capture') → тримайте однакову гранулярність.
Відсутність'provider _ txid'в лозі → втрачається точність матчу.
Ігнор таймзон → зміщення по датах settlement.
Немає DLQ/ретраїв → «вічні» розбіжності.
Ручні правки без журналу → несхідність з аудитом.
Нечіткі толеранси → або пере-матч, або «все в DLQ».
17) Контрольний чек-лист впровадження
- Єдина схема нормалізації та довідники PSP/методів/акаунтів
- Дерево ключів зіставлення (txid → merchant_ref → fuzzy)
- Толеранси за сумою/часом/FX, політика джерела курсу
- Ідемпотентний ingestion, DLQ, ретраї, алерти
- Звірка Settlement↔Bank, Suspense Account політика
- Дашборд KPI, звітність для фінансів/аудиту
- Плейбуки і SLA розбору diff-кейсів
Резюме
Звірка - це інженерна дисципліна: нормалізація, надійні ключі, толеранси, автоматичні матчі і прозорі корекції. З таким контуром ви стабілізуєте виручку і комісії, мінімізуєте «чорні діри», прискорюєте закриття періоду і проходите аудит без болю: Auto-match%↑, Mismatch↓, Aging↓.