Self-hosted QRIS gateway with automated bank notification reconciliation.
XMPayment bridges DANA Bisnis push notifications captured by a dedicated Android device into a persistent Node.js backend — no bank API access, no per-transaction fees, no serverless time limits.
Unique nominal engine
Each invoice gets a 1–999 anti-collision suffix with automatic 4-digit overflow, so a bank notification alone identifies the payer.
Dynamic EMVCo QRIS
Static merchant payloads are mutated per transaction (tag 01 → 12, tag 54 injected) with a recomputed CRC16-CCITT checksum.
Idempotent ingestion
SHA-256 hashing over a one-minute bucket collapses duplicate Android notifications without ever double-crediting a wallet.
Wallet ledger + payouts
Every balance mutation writes a paired ledger entry. Payouts move funds to a locked balance until the Super Admin settles them.
Heartbeat kill switch
No ping from the Android bridge for 45 minutes flips the gateway to MAINTENANCE and refuses new live invoices.
Persistent WhatsApp bot
zapo-js pairing credentials are stored on disk, so reboots never invalidate the KYC verification session.
Create an invoice
POST /api/v1/invoices
curl -X POST $BASE/api/v1/invoices \
-H "Authorization: Bearer xmp_test_…" \
-H "Content-Type: application/json" \
-d '{ "baseAmount": 50000 }'Returns a dynamic QRIS payload and a checkout URL. The customer transfers the exact unique nominal, e.g. Rp 50,023.
Reconcile automatically
POST /api/internal/webhook-macrodroid
curl -X POST $BASE/api/internal/webhook-macrodroid \
-H "x-macrodroid-secret: $SECRET" \
-H "Content-Type: application/json" \
-d '{ "text": "DANA: Berhasil menerima uang sebesar Rp50.023" }'The wallet is credited inside a single transaction; the merchant webhook fires afterwards.