On this page
Payment Flow
Follow an invoice from creation through asset selection, payment detection, confirmation, expiry, and the final webhook your system consumes.
Lifecycle
The invoice ends in one terminal state: paid, paid_over, underpaid, expired, or cancelled. Most transitions fire a webhook event — the per-status table below lists which. Neither pre-payment state (awaiting_client, awaiting_payment) emits one; everything from confirming onward does.
Two creation flows
Both flows open the same way. The invoice is created in awaiting_client, with no address behind it yet. What differs is what you send and how much is left for the customer to decide. The address is allocated — and the invoice moves to awaiting_payment — at the moment the checkout confirms the token.
Direct crypto flow — provide amount + currency + network. The amount is the crypto amount to pay, and both the token and the chain are fixed at creation. The customer has nothing left to decide, so the checkout confirms that pair and the address is allocated straight away.
Fiat flow — provide amount + currency (no network). The amount is a fiat amount. The customer selects a token and network on the hosted checkout; at that moment Paymos locks the FX rate, assigns an address, and the invoice moves to awaiting_payment. Read the locked rate back from payment.exchange_rate on the invoice, alongside the exact payment.expected stablecoin amount.
See Supported currencies for the full list of fiat codes and tokens, and Create invoice for the request body.
Statuses
| Status | Description | Webhook event |
|---|---|---|
awaiting_client |
Opening status for every invoice — no address until the checkout confirms the token | — |
awaiting_payment |
Address assigned, waiting for transfer | — |
confirming |
Payment detected, waiting for confirmations | invoice.confirming |
underpaid_waiting |
Partial payment received, waiting for remaining | invoice.underpaid_waiting |
paid |
Payment confirmed in full | invoice.paid |
paid_over |
Received amount exceeds expected (credited in full) | invoice.paid_over |
underpaid |
Closed with insufficient payment | invoice.underpaid |
expired |
Timer ran out without payment | invoice.expired |
cancelled |
Cancelled by merchant (only from awaiting_client) |
invoice.cancelled |
Cancellation
An invoice can only be cancelled while in awaiting_client — before the checkout confirms the token. After that the address is live and the customer has been quoted a fixed amount on a specific chain, so cancellation is off the table.
Timeouts and expiration
| Setting | Default |
|---|---|
| Token selection window (fiat flow) | Configurable |
| Invoice expiration (after address assigned) | Configurable |
| Underpayment threshold | Configurable per project |
| Overpayment handling | Credited in full |
If the customer sends less than the required amount before the invoice expires, the resulting status depends on allow_multiple_payments:
- —
allow_multiple_payments: true— status becomesunderpaid_waiting, additional payments are accepted until the timer runs out - —
allow_multiple_payments: false— a single insufficient payment immediately closes the invoice withunderpaid
If the timer runs out while in underpaid_waiting, the final status is resolved from the total received amount and the underpayment policy. If the customer sends more than expected, the full amount is credited and the status becomes paid_over.