Skip to content

Invoice numbers

Every payment has a 32-char hex payment_id (the gateway’s internal UUID) AND a human-readable invoice_number shown to PSPs and surfaced in dashboards / receipts / support tickets. Set invoice_prefix on POST /v1/payments to control the readable form.

<PREFIX>-<6-digit zero-padded global seq>

Examples:

  • FTY-PRO-000042 — fotoyu Pro plan, 42nd payment ever
  • BOOK-000043 — book.uncle-z.com booking
  • QUAY-MO-000044 — Quay monthly subscription
  • HRDEX-T2-000045 — HRDex Tier-2 subscription
  • POS-000046 — posz retail transaction

The 6-digit suffix is a single global sequence shared across the entire gateway, not per-product. Intentional — a typo’d prefix can’t accidentally collide with another product’s space.

^[A-Z][A-Z0-9-]{0,11}$
  • Uppercase letters / digits / dashes only
  • Must start with a letter
  • 1–12 characters total

The 6-digit suffix is appended by the gateway. Keep your prefix under 12 chars to leave headroom.

Use these — don’t invent new ones without checking ops first:

ProductPrefix shapeExamples
book.uncle-z.comBOOK (single, no tier)BOOK-000042
fotoyuFTY-<tier>FTY-PRO-000042, FTY-POWER-000043, FTY-B-000044
hrdexHRDEX-<tier>HRDEX-T1-000042, HRDEX-T2-000043
QuayQUAY-<plan>QUAY-MO-000042 (monthly), QUAY-YR-000043 (yearly)
poszPOSPOS-000042

The gateway falls back to bare 32-char hex (payment.id formatted). Works, but unreadable in PSP dashboards / support tickets. Always set invoice_prefix for live traffic.

PSPField
NICEPAYreferenceNo (max 40 chars)
Polarpassed via metadata.gateway_invoice_number; visible in Polar’s order detail view
PayPalinvoice_id (max 127 chars)
  • Cross-product collisions are impossible by construction.
  • Operators searching dashboards see BOOK-000042 and immediately know it’s payment #42 across the entire portfolio, regardless of which product issued it.
  • Sequence is in Postgres (payment_invoice_seq) — atomic, no race conditions.
  • 6 digits = 1M payments. We’ll widen if we get there.

Older payments created before the invoice_prefix feature use bare hex. The gateway’s webhook resolver accepts BOTH shapes — first hex-decodes, fallback to FindByInvoiceNumber. So legacy and new can coexist on the same merchant.