Native XRP on the XRP Ledger — what you are actually taking
When a customer pays this invoice, real XRP moves on the XRP Ledger. XRP is a native asset with its own ledger, not a token riding on a smart-contract chain — there is no token contract to verify, no wrapped stand-in issued somewhere else, and no second issuer to vet. The payer sends from whatever wallet holds their XRP, the transaction is validated by the ledger's consensus process, and halfin's XRP gate watches that ledger for it. That directness is the whole appeal for a customer who already holds XRP: they pay with the asset they have, on its home ledger, without a detour through an exchange.
The XRP Ledger was built around moving value, and the experience reflects that. A submitted payment reaches consensus and becomes a validated, settled transaction in roughly the time it takes to read a confirmation screen. For a checkout that timescale is the point — the customer is not watching a spinner, and you are not holding an order open for an hour waiting on confirmations. Fast finality is a property of the ledger, not a corner halfin cuts: the payment still moves through a visible sequence of states, and only the final one should trigger fulfilment.
The rate is locked, so XRP's volatility is not your problem
XRP is a volatile asset, not a stablecoin, so pricing the sale in XRP directly is a trap: quote an XRP amount at draft time, the customer pays a few minutes later, and the dollar value of that XRP has already moved. halfin sidesteps this the same way it does on every rail — you state the amount in fiat, and halfin converts to XRP and locks that quote when the invoice activates. The fiat figure is the source of truth; the XRP quantity is just how this particular customer settles it.
From activation the payable XRP amount is fixed for the life of the invoice. The customer sees a concrete number to send to a specific address and a countdown. If they pay inside the window, the XRP they send settles to the exact fiat amount you billed — you carry no FX exposure between quote and payment, and neither do they. If the window elapses, the invoice is marked expired instead of silently re-pricing against a newer rate, and you decide whether to re-issue at the current quote.
- You bill in USD or EUR — the fiat amount is the source of truth, not the XRP quantity.
- The XRP quote locks at activation and stays fixed until the invoice is paid or expires.
- An expired invoice is marked expired, never quietly re-quoted at a newer rate.
- Underpaid and overpaid amounts are recorded against the invoice, not stranded.
Fast finality, still credited safely
Fast settlement does not mean halfin skips its crediting discipline. XRP runs through the same path as every other rail: the deposit is matched to your invoice, crediting is reorg-aware, and a per-chain confirmation policy decides when the network's settlement condition is satisfied. Only then is the invoice marked paid and the balance credited. On the XRP Ledger that condition is met quickly because validated ledgers close in seconds — but it is the same rule set, not a faster, looser one.
The reason this matters is consistency in your own backend. An invoice reaching paid looks identical whether the customer paid in XRP, BTC, or a stablecoin — same lifecycle stages, same event shape, same "paid" that represents an amount that actually held. You are not writing a special-case fulfilment branch because XRP confirms differently. It confirms faster; it does not confirm by a different set of rules. That is what lets you add the XRP rail without touching the code that already handles your other assets.
How the payment moves through its states
Because the asset is fast but the crediting is still staged, the value of this rail is that each step is observable. Your backend reacts to the same canonical events on an XRP payment that it does on any invoice — you do not poll and hope. The table below is the lifecycle for a single XRP invoice, from the moment the rate is locked to the moment the funds are yours.
The events listed are the real ones an XRP invoice emits. There is no invoice.activated event — activation is the point at which the XRP quote locks, and the first event you receive about an in-flight payment is invoice.confirming. Whatever the event, verify its HMAC signature before you act on it.
| Stage | What is happening | What you do |
|---|---|---|
| Activated | Fiat-to-XRP rate locks; the payable XRP amount and address are pinned | Present the invoice via hosted checkout or your own UI |
| invoice.confirming | The XRP deposit is seen and being validated against the confirmation policy | Show the customer the payment is in flight; do not fulfil yet |
| invoice.paid | Settlement condition met; the amount held and is credited to your balance | Verify the signature, then fulfil the order and update records |
| invoice.underpaid / invoice.overpaid | The customer sent less or more than the locked XRP amount | Reconcile against the recorded amount; refund or top-up as your policy dictates |
| invoice.expired | The window elapsed with no qualifying payment | Re-issue a fresh invoice at the current quote if the customer still wants to pay |
Present XRP through hosted checkout or your own UI
The shortest path to taking XRP is hosted checkout: halfin renders the payment page, shows the customer the exact XRP amount, the destination address, and the countdown, and tracks the payment to completion. You point the customer at the page and react to the result. Nothing about the XRP Ledger leaks into your frontend — you are presenting an invoice that happens to be payable in XRP.
If you render your own payment UI, the same invoice object is available against the API and you display the XRP amount and address yourself. Either way the lifecycle is identical, and either way your backend learns the outcome through a signed webhook. Verify the HMAC signature, then fulfil, email, or update your records. A static deposit address is the wrong tool here — XRP invoices are rate-locked and time-boxed, which a persistent receive address is not.
Create an XRP-payable invoice with one call
Taking XRP is not a separate endpoint or a special flag. You create a fiat-anchored invoice exactly as you would for any asset; the supported assets — XRP included — are drawn from the platform's real gate list. Authenticate with a scoped API key in the X-API-Key header, post the fiat amount and currency, and pass an idempotency_key field in the request body so a retried request never creates a duplicate invoice.
Monetary values are strings end to end — never floats — so the amount goes over the wire as a string. The exact response shape, and how to scope an invoice to XRP on the XRP Ledger specifically, are defined in the docs and the @halfin/sdk-merchant types. The point here is that one authenticated call returns a payable invoice the customer can settle in native XRP, with the rate locked and the lifecycle wired to signed webhooks.
curl -X POST https://api.thehalfin.com/api/v1/invoices \
-H "X-API-Key: $HALFIN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"amount_fiat": "49.00",
"fiat_currency": "USD",
"deferred": true,
"description": "Pro plan — March",
"idempotency_key": "00000000-0000-4000-8000-000000000001"
}'
# halfin locks the fiat-to-XRP rate at activation, pins the payable
# XRP amount and destination, and returns an invoice you present via
# hosted checkout and then track with signed webhook events.
# See docs.thehalfin.com for the full request and response schema.