Solutions / Fintech and payments

Money moves in more places than your tests do.

In fintech, the costly bugs rarely crash anything. A retry posts twice, a balance drifts from its ledger, a payout leaves before a refund. Invarly checks the rules your money depends on, on live data, and shows the deploy that broke one.

PaymentsWalletsLendingPayouts

Why fintech breaks quietly

A single customer action in a payments product touches more systems than almost anywhere else: your API, a payment provider, webhooks, a ledger, a balance cache, fee and FX logic, and a payout job that runs on its own schedule. Each one is correct on its own. The bugs live in the gaps between them.

Those gaps don’t throw errors. A duplicated transfer returns 200 twice. A drifting balance is still a valid number. Monitoring sees healthy services; tests see the happy path. The first signal is usually a reconciliation report, a support ticket, or an auditor.

Five places it breaks

Double posting after a retry

A payment provider times out, your service retries, and both calls succeed. Without an idempotency key enforced all the way to the ledger, one customer action becomes two transfers.

rulecount(transfer by idempotency_key) == 1

Balance drifting from the ledger

Most products keep a cached balance next to an append-only ledger. Every code path that writes one must write the other. One missed path, often a new admin tool or a migration, and the number the customer sees is no longer the number you owe them.

rulewallet.balance == sum(ledger.amount)

Payouts before refunds are netted

If a refund lands after the payout job reads its totals but before it pays out, the money leaves twice: once to the seller and once back to the buyer.

rulepayout.amount <= settled - refunds - fees

Your status and the provider’s disagree

Webhooks arrive late, out of order, or not at all. A payment captured at the provider stays pending in your system, or a failed one is marked paid.

rulepayment.status == psp.status after 15 min

Fees and FX leak cents

Rounding at the wrong step, or applying a quoted rate after it expired, loses a fraction per transaction. It never shows up in one order and always shows up at month end.

ruleabs(fx.applied - fx.quoted) <= 0.0001

A walkthrough

Composite example, combined from common wallet bugs.

A team adds automatic retries for failed transfers. The retry reuses the request but not the idempotency key. During a provider slowdown, 14 transfers post twice.

postgres.main · walletslive
walletbalanceledgerΔ
88256801.33801.330.00
88258476.42476.420.00
88260735.16722.16+13.00
88261529.80529.800.00
88264570.35562.35+8.00
88265465.04451.04+14.00
8826840.8632.86+8.00
88270757.90757.900.00
88272391.54391.540.00
wallet-balance-matches-ledger≠ open
rule wallet.balance == sum(ledger.amount)
wallets out of sync
4
limit
≠ incident opened, 4 records
after deploy 3c9e1aa “retry failed transfers”
ai investigation
  • all 14 wallets have two transfers with the same request id
  • traced to 3c9e1aa “retry failed transfers” payments/retry.go:57
  • total over-credited € 2,318.40

The alert fires on the first run after the drift starts, lists every affected wallet, and points at the deploy that changed the retry path.

The starter rule pack

The rules most teams in this space start with. Each one is a query against your own tables, adjusted to your schema during setup.

rule protects runs
wallet-balance-matches-ledger Customer balances 5 min
transfer-idempotent Duplicate money movement 1 min
payout-within-settled Over-payouts to merchants before each payout
payment-status-matches-psp Webhook drift 15 min
fx-within-quote Rate and rounding leaks 1 hour
no-negative-balance Overdrafts 5 min

How it fits your stack

The agent runs next to your ledger database, usually against a read replica, and only sends results: which records broke a rule and the columns you choose to include. Card numbers and personal data never need to leave your network.

Deploys come from GitHub, alerts go to Slack or a webhook, and every check is kept with its result, so you have a record of what was verified and when.

Common questions

Do you need access to card or personal data?

No. Rules run on your own tables inside your network. You decide which columns, if any, are sent with a violation.

Can it check against our payment provider?

Yes, if the provider’s data is in your database, for example from webhooks or settlement files.

How often does a rule run?

From every minute to once a day, per rule. Money-moving rules usually run every one to five minutes.

Next industry
E-commerce and marketplaces
Read →

Start with your ledger.

We’re onboarding a few fintech teams first. Tell us where your money moves.

Join waitlist