Skip to main content

Hosted Checkout

The fastest way to get paid: create a charge and redirect the customer to a branded ZaroPay payment page. No payment UI to build.

Flow​

Your backend Customer ZaroPay
──────────── ──────── ───────
POST /v1/deposit-addresses ─────► opens checkout_url ─────► shows QR + live status
(amount, orderRef, pays USDT on TRON detects + confirms on-chain
successUrl, cancelUrl) │
▲ ▼
└──────────── deposit.confirmed webhook (verify!) ◄──────────┘
customer redirected to successUrl

1. Create the charge​

POST /v1/deposit-addresses with an amount (and optionally orderRef, successUrl, cancelUrl). The response includes a checkout_token and a ready-to-use checkout_url (https://checkout.zaropay.com/c/<token>). See Deposit Addresses.

2. Redirect the customer​

Send them to data.checkout_url. The page is driven by the public, unauthenticated endpoint GET /v1/checkout/:token — the unguessable token is the capability (no API key needed in the browser). It returns a safe, whitelisted projection:

{
"state": "awaiting|confirming|paid|underpaid|overpaid|expired|error",
"merchant": { "name": "…", "logoUrl": "…", "accentColor": "#…" },
"order": { "ref": "INV-1042", "amount": "100", "payAmount": null, "currency": "USDT",
"feeMode": "merchant", "successUrl": "…", "cancelUrl": "…",
"fiatCurrency": null, "fiatAmount": null, "quoteRate": null, "quoteExpiresAt": null },
"payment": { "address": "TXyz…", "chain": "tron", "expiresAt": "…" },
"status": { "depositStatus": "CONFIRMING", "confirmations": 5, "requiredConfirmations": 19,
"amountReceived": "100", "paymentState": "exact", "txHash": "0x…", "confirmedAt": null }
}

payAmount is the exact figure the payer must send in client fee mode (amount plus the platform fee); in merchant mode it is null and the payer sends amount — use payAmount ?? amount as the figure to display (see Fee modes). status.paymentState compares what arrived with that same figure in either mode, and state becomes underpaid / overpaid accordingly. The page updates live (websocket + polling fallback) as the payment is detected and confirmed.

USD-priced orders​

If you created the charge with pricingCurrency: "USD", the projection's order block carries the fiat denomination — fiatCurrency ("USD"), fiatAmount (the dollar figure), quoteRate (the locked USDT-per-USD rate), and quoteExpiresAt (when the lock ends). amount / payAmount stay the USDT the customer pays. The hosted page shows the USD amount alongside the USDT total and a countdown to quoteExpiresAt; when the lock lapses it auto-refreshes the quote via POST /v1/checkout/:token/requote (same token, no auth — only while the order is still unpaid), so the displayed USDT amount always reflects a fresh rate.

3. Return redirect​

On a terminal state the customer is redirected to your successUrl / cancelUrl with query params appended:

?ref=<orderRef>&status=<paid|cancelled|expired>
The redirect is not proof of payment

The browser redirect is for UX only and is customer-controllable. Confirm fulfilment from the signed deposit.confirmed webhook — never from the status query parameter.

Customizing the page​

The checkout page shows your merchant name, logo, and accent color (merchant block above), configured in the Merchant Dashboard. The orderRef you pass is shown to the customer.