Skip to main content

Quickstart

Take your first USDT payment end-to-end. Use test credentials (zp_test_…) first — they behave identically to live but settle on the TRON testnet.

1. Get your API key​

API keys are issued from the Merchant Dashboard → API Keys. A key belongs to the environment it was created on: the staging dashboard (staging-merchant.zaropay.com) issues zp_test_sk_… keys for https://staging-rest.zaropay.com; the live dashboard (merchant.zaropay.com) issues zp_live_sk_… keys for https://api.zaropay.com. For this walkthrough use staging. Copy the secret key when it's shown — it is revealed only once. See Authentication for the key format and scopes.

2. Create a charge​

Send the order amount to POST /v1/deposit-addresses on the staging base URL. The response includes a deposit address and a hosted checkout_url. (In client fee mode it also carries pay_amount — the order plus the platform fee, which is what the customer must send; see Fee modes.)

curl -X POST https://staging-rest.zaropay.com/v1/deposit-addresses \
-H "X-Api-Key: zp_test_sk_xxxxxxxxxxxxxxxx_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"amount": "100",
"currency": "USDT",
"orderRef": "INV-1042",
"successUrl": "https://shop.example.com/thanks?order=1042",
"cancelUrl": "https://shop.example.com/cart"
}'

Response (201 Created):

{
"success": true,
"message": "Created",
"data": {
"id": "5f1c9d2e-7a4b-4c0a-9e21-3b8f0a1d2c34",
"address": "TXyz...payTo",
"chain": "tron",
"currency": "USDT",
"status": "ACTIVE",
"fee_mode": "merchant",
"expected_amount": "100",
"pay_amount": null,
"order_ref": "INV-1042",
"checkout_token": "k7H2…",
"checkout_url": "https://staging-checkout.zaropay.com/c/k7H2…"
}
}

On live the link is https://checkout.zaropay.com/c/<token>.

3. Collect the payment​

Redirect the customer to data.checkout_url for a branded payment page (QR + live status), or display data.address and the amount to send in your own UI — data.pay_amount in client fee mode, otherwise data.expected_amount. See Hosted Checkout for the full flow.

4. Receive & verify the webhook​

When the deposit confirms, ZaroPay POSTs deposit.confirmed to your endpoint with an x-zaropay-signature header. You must verify it before fulfilling. Minimal Node.js receiver:

const crypto = require("crypto");
const express = require("express");
const app = express();

// Capture the RAW body — signature is computed over the exact bytes.
app.post("/webhooks/zaropay", express.raw({ type: "application/json" }), (req, res) => {
const raw = req.body.toString("utf8");
if (!verify(raw, req.get("x-zaropay-signature"), process.env.ZAROPAY_WEBHOOK_SECRET)) {
return res.status(400).send("bad signature");
}
const event = JSON.parse(raw);
if (event.event === "deposit.confirmed") {
const d = event.data; // dedupe on event.id first (deliveries are at-least-once)
if (d.netAmount == null) return res.sendStatus(200); // not credited yet (rare) — re-fetch via GET /v1/deposits/:id
if (d.paymentState === "underpaid") {
// partial payment: book d.netAmount (USDT) — or d.fiatNetAmount for a USD order — don't fulfil in full
} else if (d.paymentState === null) {
// open address, nothing was quoted (d.orderAmount is null): fulfil for d.grossAmount, book d.netAmount
} else {
// "exact" | "overpaid": fulfil for d.orderAmount (d.fiatAmount for a USD order), book d.netAmount
}
}
res.sendStatus(200); // 2xx = delivered; anything else is retried
});

// full verify() in /webhooks/signatures

Register your endpoint URL + grab its whsec_… signing secret in the dashboard, or via POST /v1/webhooks/endpoints. The complete, copy-paste verification code for every language is on Signature verification.

5. Go live​

Swap your zp_test_sk_… key for a zp_live_sk_… key and point at the live base URL (https://api.zaropay.com) instead of https://staging-rest.zaropay.com. Nothing else changes. Then test with a tiny real amount before announcing.