PayGate API
Base URL https://pay.clik.blog/api/v1. Authenticate with Authorization: Bearer pg_live_… (create keys in Dashboard → API keys). Requests and responses are JSON. Rate limit: per key per minute (HTTP 429 with Retry-After).
Create an invoice
POST /api/v1/invoices
Idempotency-Key: order-1001 (optional, recommended — retries return the same invoice)
{
"amount": "25.00", "currency": "USD", // USD, EUR, GBP
"coin": "BTC", // BTC, LTC, ETH, USDT_ERC20, TRX, USDT_TRC20
"order_id": "1001", "description": "Pro plan",
"redirect_url": "https://shop.example.com/thanks",
"ttl_minutes": 30, "metadata": {"customer": 42}
}
Response 201:
{ "id": "inv_…", "status": "new", "coin": "BTC", "address": "bc1q…",
"amount": "0.0004123", "amount_received": "0", "amount_confirmed": "0",
"fiat_amount": "25.00", "fiat_currency": "USD", "rate": "60634.12",
"confirmations_required": 2, "checkout_url": "https://pay.clik.blog/pay/inv_…",
"payment_uri": "bitcoin:bc1q…?amount=0.0004123", "expires_at": "2026-11-04T05:30:00+00:00", … }
Get / list
GET /api/v1/invoices/inv_… → invoice + "payments": [{txid, index, amount, confirmations}]
GET /api/v1/invoices?status=paid&limit=20&starting_after=inv_… → {"data": [...], "has_more": true}
Statuses
new | Waiting for payment. |
partial | Less than the amount received; the customer can top up before expiry. |
pending | Full amount seen, waiting for confirmations. |
paid | Confirmed. Check overpaid and late (paid after expiry, at the old rate). |
underpaid | Expired with only part of the amount. Still watched for the grace window. |
expired | Nothing received in time. Still watched for the grace window (late payments become paid with late: true). |
cancelled | Cancelled by the merchant before any payment. |
Webhooks
We POST events as JSON: {"id":"evt_…","type":"invoice.paid","created":1730000000,"data":{…invoice…}}. Respond with any 2xx within 10 s. Non-2xx responses are retried after 1m, 5m, 30m, 2h, 6h, 12h and 24h. Deduplicate by id.
Verify the PayGate-Signature: t=<unix>,v1=<hex> header: compute HMAC-SHA256(secret, t + "." + raw_body), compare in constant time, and reject timestamps older than 5 minutes.
// PHP
[$t, $sig] = sscanf($_SERVER['HTTP_PAYGATE_SIGNATURE'], 't=%d,v1=%s');
$ok = abs(time() - $t) < 300 && hash_equals(hash_hmac('sha256', $t . '.' . file_get_contents('php://input'), $secret), $sig);
// Node.js
const [t, sig] = req.get('PayGate-Signature').match(/t=(\d+),v1=([0-9a-f]+)/).slice(1);
const ok = Math.abs(Date.now()/1000 - t) < 300 && crypto.timingSafeEqual(
Buffer.from(crypto.createHmac('sha256', secret).update(t + '.' + rawBody).digest('hex')), Buffer.from(sig));
Always confirm the final state with GET /api/v1/invoices/{id} before shipping goods.
Exporting a watch-only key
- Electrum (BTC; Electrum-LTC for Litecoin): create a native SegWit wallet → Wallet → Information → copy the Master Public Key (
zpub…). - Sparrow: Settings → Script type Native Segwit (P2WPKH) → Keystores → copy the xpub/zpub shown for
m/84'/0'/0'. - Trezor Suite: select the SegWit (native) account → Details → Show public key (zpub/xpub). For ETH use the Ethereum account's public key only if it is an account xpub; otherwise use a single receive address.
- Ledger: Ledger Live does not show xpubs for every coin. Use Sparrow or Electrum with your Ledger connected (they export the xpub without exposing keys), or use a single receive address for ETH/TRX.
- ETH / TRON: the simplest option is a single receive address; each invoice then gets a unique exact amount. An account xpub (
m/44'/60'/0'orm/44'/195'/0') gives one address per invoice, but tokens on many addresses need gas to consolidate.
After pasting, PayGate shows the first address it will use — confirm it matches the first receive address in your wallet before saving.