For payments engineers
The payments engineer’s field guide to Connect reconciliation
Someone has to say it plainly: yes, you could build this. The arithmetic fits in a gist. What follows is the honest technical scope — every event, race, tolerance and durability concern a production system must handle — so your estimate includes the boring parts that eat weekends. Then you can decide with eyes open between owning those concerns forever and adopting them read-only in ten minutes.
The event surface
Eight events carry the full signal: charge.refunded, application_fee.refunded, charge.dispute.created, charge.dispute.closed, transfer.reversed, payout.failed, balance.available, account.application.deauthorized. Subscribe to all eight; partial subscriptions create blind spots misread as clean data. Verify signature on ingress; claim events durably before work; expect at-least-once delivery and design for replay.
The races, precisely
Refund-versus-reversal ordering is unguaranteed within seconds. Mitigations pair: delay refund-event processing ~5 seconds, and lock per charge so concurrent events serialise. Payload snapshots go stale between queue and worker — route from payload, decide from live fetches. Skip either mitigation and false positives manufacture distrust that kills the project socially regardless of technical merit.
The arithmetic, with its teeth showing
Proportional expectation with half-up rounding, ratio clamped at 1, two-minor-unit tolerance. Net-margin guard preventing double-count across levers. Zero-decimal currencies (JPY, KRW…) and three-decimal outliers (BHD, JOD…). Standalone fee refunds reasoned from charge outward. Partial wins read from dispute balance transactions. Each clause exists because ignoring it produced real false signals somewhere.
const expected = Math.min(1, refunded / amount) * transfer.amount;
const missing = Math.round(expected) - (transfer.amount_reversed ?? 0);
if (missing > 2 && netMargin(charge) < 0) flag(charge.id, missing);The durability wrapper
Historical backfill paginates 100-per-call through years of data inside serverless limits via self-re-enqueue. Recovery actions demand intent-scoped idempotency keys plus live-state double-guards. Alerting thresholds need mute-resistance design. Multi-tenancy needs key isolation (envelope encryption per tenant) if SaaS-shaped. Audit trail needs append-only storage. Each bullet is a quarter of subtle work in disguise.
Decision rubric
Bespoke ledger integration or regulatory constraints → build, informed by everything above. General detection feeding existing ops → adopt read-only, integrate outputs via CSV/API. Either way the free scanner validates the math independently first — paste a week of events and check whose numbers agree.
Common questions
Can I verify FeeGuard’s math independently?
Yes — formulas are published, the scanner runs them on pasted data with no account, and the mock-mode self-test asserts false positives as hard as positives.
Does the scanner accept API keys?
Never, by design. Unauthenticated credential collection has no revocation story. Key-based monitoring happens only inside accounts with encrypted, revocable keys.