TL;DR
An overpayment on a crypto invoice is credited in full and closes the invoice as paid, but it reports invoice.paid_over — so a handler that matches the string invoice.paid drops a payment it was paid. A shortfall meets the project's underpayment tolerance: up to 2%, in tenths, and 0.1% on a new project. Past that, an invoice that takes several payments stays open for the rest and one that does not closes underpaid. Money that reaches the address without paying an invoice is a third case with no event at all.
Someone sends 49.6 USDT against an invoice for 50. Nobody made a mistake, the transfer is confirmed, and there is no reversal to ask for. What happens next was decided before the payer opened the page: an overpayment lands on your balance whole and still closes the invoice as paid, while a shortfall has two possible endings and a project setting picks between them. Neither case is rare. Exchanges take their withdrawal fee out of the amount they send, quoted amounts carry decimal tails, and a payer topping up an order rounds to something that looks tidy.
This is the amount side of an invoice. Creating one is its own guide, and the state machine with every transition on it sits in the payment flow reference. What follows is what each outcome does to your balance, your order, and the events your server hears about.
What happens to money above the invoice amount?
Nothing is held back. The full amount that arrived is credited to your balance, the surplus with it, and the invoice closes as paid. The processing fee follows the same rule as on any other payment: it is charged on what came in rather than on what you asked for, so the extra carries the fee the rest of the payment carries. The rate behind it sits on the pricing page.
Where the two cases separate is the event. A payment that lands on the number reports invoice.paid. A payment that goes over reports invoice.paid_over, and that name is the entire difference your system is handed.
Sending the surplus back is a transfer you make yourself. Nothing leaves on its own, and the decision is commercial rather than mechanical — a rounding tail is not worth a transaction, while a payer who typed one zero too many will be in touch before you are.
Why does a paid invoice leave the order unpaid?
Because the handler was written to match one string.
The event does reach you. Subscription works by category, with no way to pick single names out of one, so an endpoint on the invoice category takes all eight of its events — invoice.paid_over among them. It arrives, the handler compares the type to invoice.paid, finds something else, and files it under events it does not care about.
The failure is quiet by construction. The dashboard shows a paid invoice, the balance shows the credit, the order sits unfulfilled, and nothing anywhere returned an error. It surfaces days later as a customer asking where the goods are — and it selects for the customers who paid you too much.
Branch on a set rather than on equality:
// Both of these close the invoice. Only the second carries a surplus.
if (eventType is "invoice.paid" or "invoice.paid_over")
await FulfilAsync(invoiceId);
Then keep the two figures apart in your own record. The amount requested and the amount received are separate numbers, and the surplus is the difference between them — not something to infer from an event name later.
How much short still counts as paid?
Every project carries its own Underpayment tolerance, and nobody is asked when a payment lands under the line. The ceiling is 2%, the slider moves a tenth of a per cent at a time, and a project you create today opens on the first of those steps, at 0.1%. Drag it down to zero and matching goes strict: the payment has to reach the invoice amount or pass it. There is no per-invoice override — the create-invoice request carries no tolerance field, so what the project says is what every invoice under it gets.
Inside the tolerance the invoice completes and reports invoice.paid, like any other paid invoice. You settle the amount that arrived; the gap is not made up by anyone.
One detail decides what you can still fix afterwards. An invoice is judged by the tolerance its project held at the moment the invoice was created, and that copy is frozen on the invoice for the rest of its life. Move the slider and you have changed the invoices you issue next, not the ones already sitting open on a checkout page.
Why do payments arrive short in the first place?
The network fee is the usual suspect, and it is the wrong one. Gas on Ethereum is paid in ether — "Gas fees have to be paid in Ethereum's native currency, ether (ETH)" — and the sending wallet covers it out of its own coin balance. The token amount travels whole. A payer who sends 50 USDT from a wallet they control sends 50 USDT.
The shortfall almost always comes from paying out of an exchange account. Binance.US states it for its own withdrawals: both the network fee and the exchange fee "are deducted from your withdrawal amount before it reaches your destination wallet". The payer typed the invoice amount, the exchange sent that amount minus its fee, and your invoice is short by exactly the fee.
Which is why the tolerance is a percentage and the cause is not. A flat deduction takes a meaningful bite out of a small invoice and a negligible one out of a large invoice, so no single percentage covers both. At 0.1% a fifty-unit invoice absorbs five hundredths of a unit: a rounding tail, and nothing like an exchange fee. Set the tolerance for the tail and treat the exchange case as what it is — something to tell the payer about, on the checkout page or in the order confirmation.
What are the two endings for a short payment?
allow_multiple_payments picks between them, it is fixed when the invoice is created, and the API defaults it to true.
With several payments allowed, a short one closes nothing. The invoice moves to underpaid_waiting, stays open for the remainder, and the checkout does the arithmetic for the payer: the QR code and every wallet link rebuild around the amount still owed, computed on the server rather than subtracted in the browser. In the Telegram card the copy-amount button renames itself and copies the shortfall instead of the original total.
With them switched off, one short payment is the end of it. The invoice closes underpaid and reports invoice.underpaid.
The clock finishes an open one. When the payment window closes on an invoice still below the line, it settles as underpaid — though a transfer already detected and confirming blocks that outright, because an invoice cannot expire out from under money that is on its way.
Does an underpaid invoice still pay you?
It does, and that is the part that reads strangely on a statement. Each transfer is credited as it confirms and the fee is charged on that transfer's own amount, so a part payment is money on your balance while the invoice is still open. An invoice that ends underpaid leaves you holding real funds against an order that never closed.
The event stream is quieter than the money. invoice.underpaid_waiting fires when the invoice enters that state, once. A second short transfer that still does not cover the invoice moves your balance again and produces no new event, because the status did not change and a webhook is enqueued on a change of status. A support screen built on the event stream will show one arrival where two happened. Read the invoice for the running total.
What is left is the decision nobody can make for you: ship anyway, ship part of it, ask for the difference, or send the money back. Returning it is an ordinary transfer out of your balance, the same as in the overpayment case.
When is money on your balance not a payment at all?
An invoice can come up short. Money can also land that was never payment toward an invoice at all, and on the balance the two sit side by side looking similar. They behave nothing alike.
The causes are recorded with each credit, and the everyday ones are these: a transfer that arrives once the window has shut, a transfer against an invoice that closed earlier, a payer who sends USDC where the invoice was priced in USDT, and a deposit onto an address with no invoice on it. Every one of those is credited to your balance with the ordinary processing fee taken, and the invoice never registers it: not as paid, not as underpaid, not at all.
There is also no event. Not a different event — none at all: a credit of this kind has nothing in the webhook stream to carry it. An underpayment has a status your integration can read and an event it can subscribe to. This has a line on the Balance page, an email, a dashboard notification, and a Telegram message where the contact is linked, and nothing your server can listen for.
The difference decides where you look when the books disagree. A short invoice is an order to chase. A credit with no invoice on it is money somebody sent you that no order is waiting on, and only the balance will tell you it is there.
How do you rehearse this before a customer does it?
In Sandbox, with the payment simulator, which emits the same lifecycle webhooks a real payment produces. One signed call drives a confirmed sandbox invoice to an outcome:
POST /v1/sandbox/invoices/inv_7Qk2.../simulate-payment
Content-Type: application/json
{ "stage": "overpaid" }
overpaid pays 10% above the expected amount, underpay pays 40% of it, and every figure is derived from the invoice on the server — the client never sends a number. The stages add up across calls, so two underpay calls put 80% against one invoice and it is still short.
Three runs are worth making before the first real one:
overpaidagainst an invoice your fulfilment path is watching. The order has to ship, and your own record has to show received above requested.underpayagainst an invoice that allows several payments. The order must not ship, and whatever your support team looks at must show the shortfall rather than the original total.underpayagainst an invoice created withallow_multiple_paymentsset tofalse. The order must not ship and the invoice must close.
The tolerance branch is the one you cannot rehearse this way: there is no stage for a payment landing a fraction short, so that setting is verified by reading it on the project rather than by firing a test at it. Everything above it is one signed request away.
| What arrived | What the invoice does | Event you receive | |
|---|---|---|---|
| More than the invoice asked for | Closes as paid, surplus credited | invoice.paid_over | |
| Short by less than the project tolerance | Closes as paid | invoice.paid | |
| Short, and the invoice takes several payments | Stays open for the remainder | invoice.underpaid_waiting | |
| Short, on a single-payment invoice | Closes short | invoice.underpaid | |
| Still short when the window ends | Closes short | invoice.underpaid | |
| Right address, no live invoice behind it | Does not move | None |
Frequently asked questions
What happens if a customer overpays a crypto invoice?
The whole amount is credited to your balance, surplus included, and the invoice closes as paid. Nothing is held back and nothing is returned on its own. The transition reports invoice.paid_over rather than invoice.paid.
Is invoice.paid_over a separate event from invoice.paid?
Yes, and that is the part integrations get wrong. An endpoint subscribed to invoice events receives both, but a handler comparing the type to the single string invoice.paid ignores the overpayment and leaves the order unfulfilled.
What is underpayment tolerance and where do I set it?
It is the share of the invoice a payment may fall short by and still complete it. You set it per project. The range is zero to 2%, the step is a tenth of a per cent, and a project created today starts at 0.1%. At zero, the payment has to reach the invoice amount or go past it.
Can a customer make a second payment on the same invoice?
When the invoice was created with allow_multiple_payments true — the API default — a short payment leaves it open at underpaid_waiting and further transfers count toward the same invoice until the payment window closes.
Does an underpaid invoice still credit my balance?
Yes. Each transfer is credited as it confirms, with the processing fee charged on that transfer, so a short invoice leaves you holding real funds against an order that never closed.
What happens to a payment that arrives after the invoice expired?
It lands on your balance and nowhere else. The ordinary processing fee comes off it, the invoice is untouched, and nothing enters the webhook stream — so the credit shows on the Balance page and in an email, never in your integration.
When NOT to use underpayment tolerance
- If every invoice is paid from a self-custody wallet in the token you quoted, the amount arrives exact and any tolerance above zero is a standing discount. Set it to 0 and keep matching strict.
- If your margin on an order is thinner than the tolerance you are considering, the setting is a price cut applied to every customer who rounds down. Absorb the tail somewhere it is visible instead.
- If the shortfall you keep seeing is a flat exchange deduction, a percentage will not cover it evenly — it covers your large invoices and misses the small ones. That one is a conversation with the payer, not a slider.
- If you sell one indivisible thing, a licence or a seat or a ticket, an invoice that stays open for a second payment mostly produces half-paid orders for support to chase. Create those with multiple payments off.
Sources
- 1. Paymos — Payment flow (accessed 2026-09-15)
- 2. Paymos — Simulate Invoice Payment (accessed 2026-09-15)
- 3. ethereum.org — Gas and fees (accessed 2026-09-15)
- 4. Binance.US Help Center — Understanding network fees vs. exchange fees (accessed 2026-09-15)
Last reviewed Sep 15, 2026


