Reading invoices
Fetch and list invoices and track their lifecycle.
After creating an invoice with POST /v1/payment, you can read it back at any time using the merchant API key. Both endpoints require the x-api-key header.
GET /v1/payment/:id
Fetch a single invoice by its payzum ID or by your own order identifier.
# By payzum payment_id
curl -s -H "x-api-key: $PAYZUM_API_KEY" \
"$PAYZUM_BASE/v1/payment/pzi_abc123" | jq .
# By your order_id
curl -s -H "x-api-key: $PAYZUM_API_KEY" \
"$PAYZUM_BASE/v1/payment/ORDER-12345" | jq .The :id segment accepts either value. If no invoice matches, the endpoint returns 404 PAYMENT_NOT_FOUND. (order_id uniqueness is not enforced at creation — if you reused an order_id, the lookup returns an arbitrary matching invoice, so always send unique values.)
Response fields
| Field | Description |
|-------|-------------|
| payment_id | Payzum invoice ID |
| payment_status | Current status (see lifecycle below) |
| pay_address | Deposit address the buyer must send to |
| price_amount | Original price as submitted |
| price_currency | Fiat (or crypto) currency of the price |
| pay_amount | Exact amount the buyer must send |
| pay_currency | Crypto currency code |
| amount_received | Amount received on-chain so far |
| actually_paid | Mirrors amount_received |
| order_id | Your order identifier (nullable) |
| network | Chain identifier |
| created_at | ISO 8601 creation timestamp |
| invoice_url | Hosted checkout URL |
GET /v1/payment
List invoices for the authenticated merchant, paginated and sortable.
Query parameters:
| Parameter | Default | Description |
|-----------|---------|-------------|
| limit | 10 | Results per page (clamped to 1–100) |
| page | 0 | Page number, 0-based |
| sortBy | created_at | created_at or updated_at (any other value falls back to created_at) |
| orderBy | desc | desc or asc |
There are no status or order_id filters (and no offset parameter). To
look up an invoice by your order identifier, use GET /v1/payment/{order_id}.
# Most recent 10 invoices
curl -s -H "x-api-key: $PAYZUM_API_KEY" \
"$PAYZUM_BASE/v1/payment" | jq .
# Page 2, 20 per page
curl -s -H "x-api-key: $PAYZUM_API_KEY" \
"$PAYZUM_BASE/v1/payment?limit=20&page=1" | jq .
# Look up by your order identifier
curl -s -H "x-api-key: $PAYZUM_API_KEY" \
"$PAYZUM_BASE/v1/payment/ORDER-12345" | jq '.payment_status'The response envelope is { "data": [...], "limit", "page", "pagesCount", "total" } — pagesCount is always at least 1 and total is the number of matching invoices. Unresolved pay_currency: "all" drafts do not appear in the listing (they remain retrievable via GET /v1/payment/:id).
Invoice status lifecycle
Invoices move through statuses in one direction. Terminal statuses are final. The API emits exactly five payment_status values — the full normative mapping lives in Invoice lifecycle.
waiting
│
├─→ finished ✓ terminal — full payment confirmed
│
├─→ partially_paid (under-payment; buyer can top up)
│ │
│ └─→ finished ✓ terminal
│
├─→ expired ✗ terminal — buyer did not pay before deadline
└─→ failed ✗ terminal — processing error or cancellation
Status values
| Status | Meaning |
|--------|---------|
| waiting | Invoice created; deposit address issued; no confirmed payment yet. |
| partially_paid | Received amount is less than pay_amount. The invoice stays open for the buyer to top up. |
| finished | Full payment confirmed. Funds are credited to the merchant. |
| expired | Expiry deadline passed before payment was confirmed. |
| failed | Processing error — a buyer cancellation also surfaces as failed. |
Two things payment_status does not encode:
- There is no
overpaidstatus — an overpaid invoice is reported asfinished. Detect overpayment by comparingactually_paid > pay_amount. - There is no
cancelledstatus — a cancellation surfaces asfailed.
Polling example
If you cannot receive webhooks, poll GET /v1/payment/:id on an interval until the status is terminal:
async function waitForPayment(invoiceId, intervalMs = 5000) {
while (true) {
const res = await fetch(
`${process.env.PAYZUM_BASE}/v1/payment/${invoiceId}`,
{ headers: { 'x-api-key': process.env.PAYZUM_API_KEY } },
)
const invoice = await res.json()
const status = invoice.payment_status
console.log('Status:', status)
if (['finished', 'expired', 'failed'].includes(status)) {
return invoice
}
await new Promise((r) => setTimeout(r, intervalMs))
}
}Polling is fine for low-volume scenarios and development, but for production fulfilment prefer push-based delivery. Configure a webhook URL on your merchant and payzum will POST a signed notification when the invoice reaches finished or expired — intermediate transitions like partially_paid do not emit an IPN, so track those by polling. See Webhooks overview for signature verification and retry behaviour.
Public buyer status endpoint
There is also a public, unauthenticated status endpoint intended for buyer-facing UIs: GET /v1/invoices/{payment_id}/status (the pzi_ id is the bearer; rate limited at 60 req/min per IP, Cache-Control: no-store, CORS *). Its response uses camelCase fields, decimal-string amounts, and the buyer status vocabulary (pending, partial, paid, overpaid, expired, cancelled) — different from the merchant payment_status values above. See Hosted checkout → Polling status yourself for the full schema.