pg-router
v1.0.1
Published
Smart Open-Source Indonesian Payment Gateway Router & Fee Optimizer
Maintainers
Readme
PG Router ⚡
TypeScript payment-routing SDK for Indonesian payment gateways.
PG Router provides fee-based and priority-based routing, safe fallback semantics, a unified adapter contract, and verified webhook normalization.
Maturity: Pakasir QRIS is the first documentation-audited adapter. Tripay and Midtrans are reserved adapter surfaces and intentionally do not create payments until their official contracts are implemented and tested. Sandbox is simulation-only.
Installation
npm install pg-routerSupported providers
| Provider | Create payment | Webhook/status verification | Maturity | | --- | --- | --- | --- | | Pakasir | QRIS | Server-to-server transaction-detail confirmation | Mock-tested against official docs | | Sandbox | Simulated methods | Simulated | Development only | | Tripay | Not yet implemented | Not yet implemented | Planned | | Midtrans | Not yet implemented | Not yet implemented | Planned | | Duitku, Xendit, iPaymu, Paydisini | Not yet implemented | Not yet implemented | Planned |
Pakasir has no documented webhook signature. PG Router therefore treats its webhook as an untrusted notification and confirms the transaction through Pakasir's authenticated Transaction Detail API. signatureVerified remains false even when isValid is true.
Quick start
import { PGRouter } from 'pg-router';
const router = new PGRouter({
strategy: 'lowest_fee',
gateways: {
pakasir: {
enabled: true,
slug: process.env.PAKASIR_SLUG!,
apiKey: process.env.PAKASIR_API_KEY!,
priority: 1,
// Optional override when your merchant fee differs from the default:
customFees: {
QRIS: { percent: 0.7, flat: 0 },
},
},
},
});
const payment = await router.createPayment({
orderId: 'INV-2026-0801',
amount: 50_000,
method: 'QRIS',
returnUrl: 'https://merchant.example/payments/return',
});
console.log(payment.gateway, payment.qrString, payment.totalAmount);Routing strategies
lowest_fee: selects the enabled adapter with the lowest calculated fee.priority: selects the lowest numericpriorityvalue.fallback: tries providers in priority order only after a definitive rejection.
An ambiguous timeout or transport failure raises PaymentCreationUnknownError and stops fallback. This prevents two providers from creating active payments for the same order.
Webhook handling
Pass the provider's original body to handleWebhook:
const result = await router.handleWebhook({
gateway: 'pakasir',
rawHeaders: req.headers,
rawBody: req.body,
});
if (result.isValid && result.status === 'PAID') {
await markOrderAsPaid(result.orderId, result.amount);
}Your application must still enforce durable idempotency for order fulfillment.
Custom adapters
Implement IGatewayAdapter and pass adapters as the second constructor argument. A custom adapter with the same provider name replaces the built-in adapter.
const router = new PGRouter(options, [myTripayAdapter]);CLI fee simulation
npx pg-router simulate --amount 25000 --method QRISThe bundled CLI currently demonstrates the documentation-audited Pakasir route. It does not include sandbox in fee comparisons.
Development
npm ci
npm test
npm run lint
npm run buildSee CONTRIBUTING.md and the Pakasir API contract.
