Skip to content

Introduction

A self-hosted payment gateway sitting between the uncle-z product portfolio and the actual PSPs. Products call one HTTP API; the gateway routes to the right PSP, verifies inbound webhooks, and forwards canonical events back.

uncle-z products. Currently:

  • book.uncle-z.com — productized dev services (USD, PayPal — services are AUP-rejected by Polar)
  • fotoyu — photo product (IDR + USD)
  • hrdex — HR SaaS (USD subscriptions via Polar, migrating off Paddle)
  • Quay (dbclient) — desktop database client (USD subscriptions via Polar)
  • posz — IDR retail POS (QRIS via NICEPAY)

If you’re building a new uncle-z product, this is your payment integration target. If you’re an external partner, you’re in the wrong place — this is internal infrastructure.

  • PSP-specific signing. NICEPAY’s SHA-256 over timeStamp+iMid+referenceNo+amt+merchantKey, Polar’s polar_whs_<secret> raw HMAC, PayPal’s async verify-webhook-signature API — all behind the gateway.
  • Currency routing. Send currency: "IDR" method: "qris" and the gateway picks the IDR rail. Send method: "polar" and the gateway routes to Polar regardless of currency.
  • Webhook delivery retries. The gateway retries with exponential backoff on 5xx, dead-letters on 4xx, and tracks delivery state.
  • Idempotency. Pass an Idempotency-Key; the gateway dedupes within 24 hours.
  • PSP onboarding quirks. NICEPAY’s userIP requirement, Polar’s mitraCd (only some paths), QRIS’s WIB timezone constraint, PayPal’s webhook ID per app — all canonicalized.
  • Subscription billing engine. Polar handles renewals; the gateway records the first charge and any explicit cancel events but does not run dunning logic itself. Your product is the source of truth for “is this user on the Pro tier.”
  • Tax engine. Polar / Paddle are MoR — they handle global VAT for non-IDR. IDR rails are domestic only. Don’t try to back-fill VAT on top of the gateway.
  • Wallet / account-balance system. Each POST /v1/payments is independent. There’s no “account” concept on the gateway side.
  • Invoicing system. The gateway issues invoice_number to the PSP for receipt-traceability only; product-facing invoices (PDFs, emails) are the product’s responsibility.

If your use case looks like one of those, build it on top of the gateway, not into it.

Single Go binary on a single Linux host (Coolify-managed Docker compose). Postgres for state. River for background jobs (webhook delivery + subscription renewals). Public at https://payment.uncle-z.com. These docs at https://docs.payment.uncle-z.com.

  1. Quickstart — get a payment through end-to-end.
  2. Modes — the most important concept in the gateway.
  3. Authentication — the HMAC scheme.
  4. Pick a guide that matches your product type.
  5. Reference docs when you’re writing the code.