# When the money arrives but can't be recorded

> A payment can succeed at the provider yet fail to record in your system — the invoice it belongs to was already paid, voided, or missing. That gap must raise an operator alert, while still returning success to the provider so it does not retry a charge it already captured.

URL: https://biloh.com.au/docs/engineering-notes/money-received-but-unrecorded
Category: Engineering notes | Audience: builder | Updated: 2026-06-26

A payment provider confirms a charge succeeded. Your system tries to record it against an invoice — and can't, because the invoice was already fully paid, voided, or not found. **What happens next must never be silence.**

The trap is easy to fall into: log a warning, mark the event "skipped," return `200 OK`, and move on. The customer has been charged, the money is sitting in the account, the invoice still reads *unpaid*, and **no human has been told**. That is a silent revenue-reconciliation gap — the kind that surfaces weeks later as an angry "I already paid this."

## The two-part rule

1. **Alert a human.** Emit a distinct "payment received but not recorded" event that drives an operator notification, carrying the invoice reference, the amount received, and the failure reason. Someone has to reconcile it by hand.
2. **Still acknowledge the provider.** Return success so the provider stops retrying. A retry would re-attempt the same record against the same unresolved cause and fail identically — and the money is already captured. Resolution is operator-side, not provider-side.

Acknowledging the provider is **not** the same as pretending it worked. The acknowledgement is for the provider's retry logic; the alert is for the human who must fix the books.

## Why this needs its own path, separate from "payment failed"

It is tempting to fold this into the generic "payment failed" alert, but the two are opposites. "Payment failed" means the customer was **not** charged — reassure them and let them retry. "Received but unrecorded" means the customer **was** charged but the books disagree — investigate immediately, and do **not** assume the invoice is unpaid. Different message, different urgency, different action. Collapsing them into one alert tells the operator the wrong story at the worst moment.

## Related

- [Trust the signed account, not the webhook's metadata](/docs/engineering-notes/trusting-signed-webhook-fields)
- [A handler nothing subscribes to does nothing](/docs/engineering-notes/wiring-an-event-to-its-handler)
