Invoices
An invoice is a request for one payment. It says what is owed and which assets on which networks you accept, and it comes with a hosted checkout page where your customer pays.
Use an invoice when you know the amount in advance: an order, a subscription period, a top-up of a fixed size. For payments of any amount at any time, use a reusable deposit address instead.
What an invoice contains
Section titled “What an invoice contains”| Part | Meaning |
|---|---|
| Price | For an invoice priced in a currency: the currency and the amount, for example 49.99 USD. Invoices can be priced in USD only for now. The amount is written as a decimal string with up to 8 decimals, such as "49.99". |
| Options | The ways to pay that you accept. Each option is one asset on one network, for example USDT on BNB Smart Chain or ETH on Ethereum. If you list none, the invoice accepts every asset you have enabled. |
| Checkout URL | checkout_url, the page to send your customer to. |
| Your references | order_id, description and metadata (up to 20 keys of your own data), stored with the invoice and returned in every event about it. |
| Customer email | customer_email, optional. It is for your own records: it is returned on the invoice and in its events. It is not shown on the checkout page, and cTpeWay sends no email to it. |
| Redirect URLs | success_url and cancel_url, where the checkout sends the customer afterwards. See Hosted checkout. |
Two ways to set the amount
Section titled “Two ways to set the amount”Priced in a currency. You give a price, such as 49.99 USD, and list the options you accept. No crypto amount exists yet. When the payer chooses an option on the checkout page, cTpeWay converts the price at the current rate and gives that option a quote: the amount to send, which holds for a limited time. If the payer switches to another option, that option gets its own quote and its own address.
Priced in crypto. You leave the price out and give each option its own exact amount, for example 25 USDT on Tron. The amounts never change.
Either way, every option has its own address, created when the option is first selected. The option keeps that address. A payment to an invoice is always in one asset on one network.
The amount the payer sends
Section titled “The amount the payer sends”A converted amount would have a long tail of decimals. So a quote is rounded up to a short, readable number of decimals, and the rounded amount is the amount due. The checkout page shows exactly that amount, and a payer who sends exactly the amount shown has paid the invoice in full.
On the invoice, each quoted option carries amount_due, in the asset’s smallest unit, and display_decimals, the number of decimals the amount is stated with. For an invoice priced in crypto the amount is exact, so display_decimals equals the asset’s decimals.
Statuses
Section titled “Statuses”An invoice has one status. These are the values the API and the webhook events use.
| Status | Meaning | What to do |
|---|---|---|
open |
Created. Waiting for the payer to choose an option and pay. | Nothing yet. |
pending |
A payment has been seen on the network but is not confirmed. | Wait. Do not fulfil the order yet. |
paid |
Confirmed payments cover the amount due. | Fulfil the order. |
underpaid |
Confirmed payments are less than the amount due. | The payer can send the rest to the same address until the invoice expires. Or you accept what was paid: see Underpaid invoices. |
expired |
Time ran out before the invoice was paid. | Create a new invoice if the customer still wants to pay. |
canceled |
You canceled it. | Nothing. |
refunded |
Everything that was paid went back to the payer. This is final. | Nothing. |
An invoice can also carry outcome_flags, which add detail without changing the status:
| Flag | Meaning |
|---|---|
overpaid |
The payer sent more than the amount due. The invoice is paid, and the checkout page tells the payer that you have been told about the extra amount. |
late |
A payment was confirmed after the invoice had expired or been canceled. See Late payments. |
underpaid_accepted |
You accepted less than the amount due as payment in full. |
partially_refunded |
Part of what was paid went back to the payer. The invoice keeps its status. |
How an invoice moves between statuses
Section titled “How an invoice moves between statuses”- A new invoice is
open. - When a payment is detected, it becomes
pending. - When the payment is confirmed, cTpeWay compares what arrived with the amount due:
- enough:
paid, with the flagoverpaidif it was more; - not enough:
underpaid. If the rest arrives before the invoice expires, it becomespaid.
- enough:
- An
openorunderpaidinvoice becomesexpiredwhen its time runs out. Apendinginvoice is given extra time for its payment to confirm.
Several payments to the same invoice add up.
Tolerance
Section titled “Tolerance”Payers sometimes send slightly too little, for example when an exchange deducts a withdrawal charge. You can set a tolerance on an invoice so that a small shortfall still counts as paid. The default is none, and the most you can allow is 2% (200 basis points, tolerance_bps).
An invoice has two clocks, and they are separate.
| Clock | Field | When it runs out |
|---|---|---|
| The invoice | expires_at on the invoice |
The invoice becomes expired, if it has not been paid. |
| The quote | quote_expires_at on the option |
Only the quoted amount lapses. The payer takes a new quote at the current rate, on the same address, and the invoice stays open. |
- The invoice’s time starts when you create it.
expires_insets it for one invoice, to anything from 5 minutes to 30 days. Without it, your account’s setting applies. Selecting an option or renewing a quote does not move it. - The quote’s time is short, because rates move. Only invoices priced in a currency have quotes. A quote never runs past the invoice’s own expiry.
In the v2 design the defaults are 24 hours for an invoice and 15 minutes for a quote. These defaults can change before release.
Underpaid invoices
Section titled “Underpaid invoices”An invoice is underpaid when confirmed payments are less than the amount due, by more than your tolerance. Two things can settle it:
- The payer sends the rest to the same address before the invoice expires. The invoice becomes
paid. - You accept what arrived. POST/v1/invoices/{id}/accept makes the invoice
paid, with the flagunderpaid_accepted. The shortfall is written off.
Late payments
Section titled “Late payments”A payer can send money after an invoice has expired or been canceled. cTpeWay still records the payment, and the funds are settled to you like any other deposit, including the platform fee. The invoice keeps its status, and you receive invoice.late_payment.
You then choose:
- Accept it with POST/v1/invoices/{id}/accept. The invoice becomes
paid, with the flaglate. Nothing else has to be done first. - Refund it. See Refunds.
The same call accepts an underpayment and a late payment. POST/v1/invoices/{id}/accept_late_payment also exists; it accepts a late payment only.
Payments that are reversed
Section titled “Payments that are reversed”Rarely, a network reorganises and a payment that was seen disappears. cTpeWay then re-evaluates the invoice without that payment and sends invoice.payment_reverted. This is why you fulfil on paid, never on pending. See Payments and confirmations.
Refunds
Section titled “Refunds”A refund returns one payment, or part of it, to the payer. It is a transfer from your master wallet to an address the payer gives, and it can be made once the payment has been released to your master wallet. An invoice that was paid with several payments is refunded payment by payment.
You create a refund with POST/v1/refunds. The request needs an Idempotency-Key header, and the refunds of a payment cannot add up to more than the payment. A refund is paid as a payout, so the API key needs the refund and payout permissions, and the same limits apply: the payer’s address must be on the key’s payout allowlist, or, for a key limited by IP address alone, the request must come from one of its IP addresses. See Paying out from your master wallet.
When a refund completes, the invoice follows. If everything that was paid has gone back, it becomes refunded and you receive invoice.refunded. If only part has gone back, it keeps its status, gains the flag partially_refunded, and you receive invoice.partially_refunded once, for the first such refund. Every refund also has its own refund.completed event.
cTpeWay charges no platform fee on a refund. The 0.5% platform fee charged on the original deposit is not returned.
Invoice, payment and settlement are three separate things
Section titled “Invoice, payment and settlement are three separate things”- The invoice status says whether your customer has paid.
- Each payment has its own status: detected, confirmed, or removed from the network. See Payments and confirmations.
- Each confirmed payment has a settlement, which says whether the funds have reached your master wallet. See Settlement and the platform fee.
An invoice can be paid while its settlement is held. Your customer has paid, so you fulfil the order. Held only means the funds have not moved to your master wallet yet.
Endpoints
Section titled “Endpoints”| Endpoint | What it does |
|---|---|
| POST/v1/invoices | Create an invoice. |
| GET/v1/invoices/{id} | Retrieve one invoice. |
| GET/v1/invoices | List invoices. |
| POST/v1/invoices/{id}/cancel | Cancel an invoice that is open or underpaid. A payment that still arrives is recorded as a late payment. |
| POST/v1/invoices/{id}/accept | Accept what was paid as payment in full: an underpayment or a late payment. |
| POST/v1/invoices/{id}/accept_late_payment | Accept a late payment only. |
| GET/v1/invoices/{id}/payments | List the payments made to an invoice. |
Events
Section titled “Events”| Event | Sent when |
|---|---|
| invoice.created | An invoice was created. |
| invoice.option_selected | The payer chose how to pay. The option now has an address and, for an invoice priced in a currency, a locked amount. |
| invoice.payment_detected | A payment was seen on the network. Not final yet. |
| invoice.underpaid | Confirmed payments are below the amount due. |
| invoice.paid | The invoice is paid. Fulfil the order. |
| invoice.expired | The invoice expired unpaid. |
| invoice.canceled | You canceled the invoice. |
| invoice.late_payment | A payment was confirmed after the invoice expired or was canceled. |
| invoice.payment_reverted | A payment was removed from the network and the invoice was re-evaluated without it. |
| invoice.partially_refunded | Part of what was paid went back to the payer. Sent once, when the first such refund completes. |
| invoice.refunded | Everything that was paid went back to the payer. |

