Invoice integration guide
This guide walks through a complete invoice integration — from creating an invoice through handling every payment outcome correctly. It covers the on-chain and Binance Pay payment methods and applies equally if you're integrating NFT payments. For NFT-specific setup, see the NFT invoice integration guide alongside this one.
Before you start
Check these before writing any integration code:
Supported currency pairs — not every
billedCurrencyandchargedCurrencycombination is valid. Verify yours against the Listing invoice currency pairs endpoint.Amount limits — each
billedCurrencyhas a minimum and maximum. Requests outside the range return HTTP 422. Contact Txn support for the limits per currency.Refund handling — if you want Txn to automatically issue refunds for underpayments, overpayments, or late payments, this is configured at the account level. Contact Txn to set it up before going live. See Invoice refunds for the available options.
Hosted page vs. custom UI — decide upfront. The hosted page requires almost no frontend work; a custom UI gives you full control but requires you to handle more edge cases (XRP destination tags, Binance Pay deeplinks, countdown timers).
Step 1: Create the invoice
Send a POST to /api/public/v1/invoices for each payment your end user needs to make. One invoice, one payment attempt.
Key parameters
billedCurrency is the currency you display to the end user — typically the currency in which they hold a balance on your platform (e.g. TRY, USD, CNY). It does not have to be a cryptocurrency.
chargedCurrency and payNetwork define what the end user actually pays in and on which blockchain network. These must be a supported cryptocurrency/token and network combination — see Supported currencies for valid codes.
targetCurrency is the currency that lands in your Txn account after the payment is converted. It must be a tradable fiat currency or cryptocurrency — display fiat currencies are not accepted here. If omitted, it defaults to billedCurrency, which must itself be tradable.
This matters in practice: many common billedCurrency values — such as TRY, CNY, and BRL — are display fiat and cannot hold a balance on Txn. If you invoice in one of these currencies, you must supply targetCurrency explicitly (for example, EUR or USD) so Txn knows which account to settle the funds into. Omitting it with a display fiat billedCurrency will return an error.
reference is your internal correlation key. Set it to your order ID, user ID, or any unique value that lets you match the invoice back to a record on your side. It must be unique per invoice. You will see it on every webhook and API response for the invoice's lifetime.
successRedirectUrl and unsuccessRedirectUrl apply only to the hosted page flow. They are the URLs Txn redirects the end user to after a successful payment or after the invoice expires or is cancelled.
What the response gives you
data.attributes.address
The wallet address the end user must send crypto to
data.attributes.amountCharged
The exact crypto amount they need to send
data.attributes.expiresAt
When the exchange rate and invoice expire (20 minutes from creation)
data.attributes.hostedPageUrl
Ready-to-use hosted payment page URL
included[].type(addresses)
Full address entity, including the destination tag for XRP
included[].type(binanceOrders)
Binance Pay order details, when paymentMethods includes binance_pay
The exchange rate is locked for 20 minutes from invoice creation. Create the invoice only when the end user is ready to pay, and present the payment details immediately — do not create the invoice in advance.
Step 2: Present the invoice to the end user
Option A — Hosted page
Redirect the end user to hostedPageUrl. Txn renders the full payment UI: wallet address, QR code, countdown timer, and Binance Pay instructions where applicable. The end user is redirected to your successRedirectUrl or unsuccessRedirectUrl when the invoice reaches a terminal state.
This is the fastest path to production and requires no frontend work beyond the redirect.
Option B — Custom UI
Build your own payment screen using the invoice response data. At minimum, display:
The wallet address and a scannable QR code
The exact amount to send (
amountCharged+chargedCurrency)The payment network by its full name (
networkName) — for example, "Tron (TRC20)" rather than the network codettrx:usdt. Users select the network in their wallet app when initiating a withdrawal; showing the recognisable display name reduces the risk of them choosing the wrong network and sending funds that cannot be recovered.A countdown timer to
expiresAt
XRP invoices: the address is returned in the format rwCQVZLSMNY6DgMH61317qvH3nHYqm68PF?dt=123456, where the value after ?dt= is the destination tag. Display the address and the destination tag separately, and ensure both are clearly visible and copyable. If the end user sends XRP without specifying the destination tag, the payment will not be processed.
Binance Pay: if paymentMethods includes binance_pay, the binanceOrders entity in included[] contains:
qrcodeLink— a hosted QR code image to displaydeeplink— opens the Binance app directly on mobileuniversalUrl— works across both web and mobile browsers
Step 3: Track invoice status
Webhooks (recommended)
Configure your webhook endpoint in the Txn dashboard. Txn sends a POST request to your endpoint on every status change. The payload is the same shape as the Read invoice response.
Two implementation requirements:
Verify the signature on every incoming webhook before processing it. See Webhooks for signature verification details.
Process webhooks idempotently. The same status transition can be delivered more than once. Use the invoice
idandstatustogether as a deduplication key.
See Invoice webhooks for example payloads for each status.
Polling (alternative)
Poll Read invoice by invoice id. A reasonable interval is every 5–10 seconds while the end user is on the payment screen. Reduce the frequency after expiresAt — the invoice can still complete via late payment for up to 7 days, but changes become infrequent.
Status reference
status
Final?
What it means
What to do
pending
No
Awaiting a crypto transaction
No action needed
expired
No
No payment received within 20 minutes
Do not cancel your order — late payment is still possible for 7 days
processing
No
Transaction detected, awaiting confirmations
Optionally show "payment detected" to the end user
on_hold
No
Compliance review in progress
Do not settle — wait for the next status update
completed
Yes
Payment processed and funds credited to your account
Settle based on statusContext — see Step 4
cancelled
Yes
No payment received within 7 days, or payment rejected under refund rules
Check coinDeposits — if non-empty, a refund link may be present
rejected
Yes
Payment declined by Txn compliance
Do not settle
Step 4: Handle payment outcomes
The statusContext and paymentStatus fields give you the full picture of what happened inside a completed or cancelled status. Handle each combination explicitly — do not fall through to a default case.
Full payment, on time
status: completed · statusContext: full · paymentStatus: on_time
Happy path. The end user sent the correct amount within 20 minutes. Proceed to Step 5.
Full payment, late
status: completed · statusContext: full · paymentStatus: late
The end user paid after the invoice expired but within the 7-day monitoring window. Txn processed the payment at the spot exchange rate applicable at the time of receipt. Proceed to credit the end user.
Underpayment
status: completed · statusContext: underpaid
The end user sent less crypto than quoted. Txn applied the spot exchange rate to the amount actually received and settled the result to your account. invoiceTransactions.amountBilled reflects the actual credited amount — it will be less than the original invoice amount. Credit the end user accordingly.
If refund rules are configured for underpayments, Txn may instead reject the payment and issue a refund link. The invoice status will be cancelled in that case — see Cancelled with a refund below.
Overpayment
status: completed · statusContext: overpaid
The end user sent more crypto than quoted. Txn applied the spot exchange rate to the full amount received. invoiceTransactions.amountBilled reflects what was actually processed — it will exceed the original invoice amount. Credit the end user accordingly.
Depending on your refund configuration, Txn may instead process only the original invoice amount and issue a refund link for the excess, or reject the entire payment. See Invoice refunds for the configured behaviour options.
On hold
status: on_hold
Txn's compliance team is reviewing the payment, typically because the source wallet is associated with elevated risk. Do not settle the end user. The invoice will resolve to either completed (payment cleared) or rejected (payment declined). Wait for the next webhook.
Rejected
status: rejected — final
The payment was declined by Txn compliance. Txn returns the funds to the sender. Do not settle the end user.
Cancelled with a refund
status: cancelled · coinDeposits relationship non-empty
A payment was received but rejected under your account's refund rules (underpayment, overpayment, or late payment). The refundLinks relationship will contain a payment link entry. Extract the hostedPageUrl from the corresponding entity in included[] and share it with the end user so they can claim their funds. See Refunds.
No payment received
status: cancelled · statusContext: unpaid · coinDeposits empty
No crypto was ever detected at the invoice address within the 7-day monitoring window. Safe to treat as abandoned — cancel the corresponding order on your side.
Step 5: Credit the end user
When the invoice reaches completed, read invoiceTransactions.amountBilled from the included[] array. This is the amount in billedCurrency that the end user's payment actually resulted in, and it is the value to credit to the end user's balance on your platform.
In a full, on-time payment, invoiceTransactions.amountBilled equals the original invoice amount. In underpayment and overpayment scenarios, Txn applies the spot exchange rate to the crypto actually received, so the value will differ.
Do not use data.attributes.amountBilled (the original quoted invoice amount) or data.attributes.targetAmount (the amount that lands in your Txn account) for crediting the end user. Both will produce incorrect results in underpayment and overpayment scenarios.
For fee reporting: invoiceTransactions.transactionFee and invoiceTransactions.targetTransactionFee give you the fee breakdown in billedCurrency and targetCurrency respectively.
Refunds
When Txn's refund handling is enabled for your account and a qualifying payment comes in (underpaid, overpaid, or late), Txn rejects the payment and creates a refund link instead of crediting your account. The invoice status becomes cancelled and the refundLinks relationship in the API response and webhook payload contains the refund payment link.
To handle a refund in your integration:
Detect
status: cancelledwith a non-emptyrefundLinksrelationship.Find the corresponding
paymentLinksentity inincluded[].Extract its
hostedPageUrl.Deliver that URL to your end user — by email, in-app notification, or however you communicate with them.
The end user visits the URL, enters their wallet address, and Txn sends the crypto back to them directly. The refund link can only be used once. Funds are returned in the same currency and on the same network as the original payment. Network and service fees are deducted from the refund amount — if the refund amount is smaller than the combined fees, Txn cannot process the refund.
See Invoice refunds for a full breakdown of each scenario and the available configuration options.
Subsequent payments to a completed invoice address
After an invoice completes, Txn continues to monitor its receiving address. If the end user sends another crypto transaction to the same address — for example, paying twice by mistake — Txn can automatically credit the incoming funds to your crypto account (for example, your USDT account) rather than leaving them unprocessed.
This behaviour is optional and configured at the account level. Contact Txn support to enable it.
When a subsequent payment is credited, it appears as a coinTransaction in your account's transaction history. The transaction inherits the reference of the original invoice, so your existing reconciliation logic can correlate it back to the originating order using the same reference matching you already apply to invoices. The original invoice is not affected — it remains in its completed state.
Reconciliation
Correlate with your records — the
referenceyou set on creation is returned on every invoice response and webhook. Use it to look up the corresponding order or user in your system.Blockchain proof —
coinTransactions.txHash(inincluded[]) is the on-chain transaction hash. Store it and share it with the end user if they need confirmation that the payment was sent.Batch reconciliation — use the List invoices endpoint with the
created_at_fromandcreated_at_toparameters to pull all invoices in a given date range.Fee accounting —
invoiceTransactions.transactionFeeandinvoiceTransactions.targetTransactionFeeare available for each completed invoice.
Last updated