One API, three rails
POST /v1/payments works the same whether the buyer pays with QRIS, a credit card via Polar, or PayPal. The gateway picks the rail based on currency + method + the app’s mode.
payment.uncle-z.com is the single payment surface that every uncle-z product talks to. It abstracts the different PSPs (NICEPAY, Polar, PayPal) behind a uniform HMAC-signed HTTP API so each product writes the integration once.
One API, three rails
POST /v1/payments works the same whether the buyer pays with QRIS, a credit card via Polar, or PayPal. The gateway picks the rail based on currency + method + the app’s mode.
Three modes per app
Every app is test, sandbox, or live. Test routes to a mock provider; sandbox hits real PSP sandboxes; live hits production. Two products at different modes can run side by side on the same gateway.
Webhooks with HMAC verification
Inbound PSP events become canonical payment.succeeded / subscription.canceled events delivered to your product, signed with your app’s webhook secret. Idempotent and retried with exponential backoff.
No PSP coupling on your side
Your code never imports a NICEPAY SDK or a Polar SDK. PSP quirks (NICEPAY’s userIP/shopId, Polar’s polar_product_id, signature schemes) all live behind the gateway.
test vs sandbox vs live. This is the first thing every integrator gets confused about.The gateway routes traffic through:
| Currency | Product type | method | PSP |
|---|---|---|---|
| IDR | Anything | qris / va_bca / va_mandiri / va_bni / va_bri / va_permata | NICEPAY |
| USD/EUR/GBP | SaaS / digital products / one-time licenses | polar (override) | Polar.sh (Merchant of Record) |
| USD/EUR/GBP | Services (productized human work) | paypal (override) | PayPal — Polar’s AUP rejects services |
Older docs / commits may mention DOKU and Paddle — those rails are out of scope going forward; ignore them.