Сверка платежей и отчетов 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↓.