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 1100) | | 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 overpaid status — an overpaid invoice is reported as finished. Detect overpayment by comparing actually_paid > pay_amount.
  • There is no cancelled status — a cancellation surfaces as failed.

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.