dragonpay-ph
v0.2.0
Published
Production-grade TypeScript SDK for DragonPay Collection and Payout APIs
Maintainers
Readme
dragonpay-ph
Production-grade TypeScript SDK for the DragonPay payment gateway (Philippines). Supports both Collection (accepting payments) and Payout (disbursements) APIs with full postback verification.
Features
- Collection API (create payments, check status by txnId/refNo, cancel, list processors, transaction/settled history)
- Payout API (create payouts, check status, void, ledger balance, available channels, transaction/ledger history)
- Lifetime Id / MUVA — recurring virtual accounts with QR Ph static QR generation
- Postback verification (SHA1 — deprecated, HMAC-SHA256, RSA-SHA256 with key rotation)
- Return URL parsing for browser redirects
- Built-in input validation and error handling
- Automatic retries with exponential backoff on 5xx/network errors
- Request timeouts via
AbortSignal - Zero runtime dependencies (uses native
fetchandcrypto) - Dual CJS/ESM build
- Mock server for local development (
dragonpay-ph/mock)
Requirements
- Node.js >= 18.0.0
Installation
npm install dragonpay-phQuick Start
import { DragonPayClient } from 'dragonpay-ph';
const client = new DragonPayClient({
merchantId: process.env.DRAGONPAY_MERCHANT_ID!,
password: process.env.DRAGONPAY_PASSWORD!,
});
// Create a payment
const txnId = client.generateTxnId();
const payment = await client.createPayment(txnId, {
amount: 1500,
description: 'Premium subscription',
email: '[email protected]',
procId: 'GCSH', // GCash
});
console.log(payment.url); // Redirect the customer hereEnvironment Variables
| Variable | Required | Description |
|---|---|---|
| DRAGONPAY_MERCHANT_ID | Yes | Your DragonPay merchant ID |
| DRAGONPAY_PASSWORD | Yes | Merchant password (Collection API + postback verification) |
| DRAGONPAY_PAYOUT_PASSWORD | If using payouts | Payout API bearer token — not available in the sandbox portal; contact DragonPay directly to request payout API credentials |
Configuration
const client = new DragonPayClient({
// Required
merchantId: 'YOUR_MERCHANT_ID',
password: 'YOUR_PASSWORD',
// Optional — Payout API (separate credentials, request from DragonPay)
payoutPassword: 'YOUR_PAYOUT_TOKEN',
// Optional — URL overrides (defaults to production)
collectUrl: 'https://test.dragonpay.ph/api/collect/v2', // sandbox
payoutUrl: 'https://test.dragonpay.ph/api/payout/merchant/v1', // sandbox
// Optional — tuning
timeoutMs: 30000, // request timeout (default: 30s)
maxRetries: 2, // retries on 5xx/network errors (default: 2)
// Optional — development
skipVerification: false, // skip postback signature verification
});Collection API
Create a Payment
const txnId = client.generateTxnId();
const payment = await client.createPayment(txnId, {
amount: 1500.00,
description: 'Premium subscription',
email: '[email protected]',
procId: 'GCSH', // optional — locks to a specific processor
currency: 'PHP', // optional, default: PHP
param1: 'custom-data', // optional — returned in postback
param2: 'more-data', // optional — returned in postback
expiry: '2026-03-17T00:00:00', // optional
mobileNo: '09171234567', // optional
ipAddress: '127.0.0.1', // optional, recommended
userAgent: 'Mozilla/5.0', // optional
billingDetails: { // optional
firstName: 'Juan',
lastName: 'Cruz',
address1: '123 Rizal St',
address2: 'Unit 4B',
city: 'Makati',
state: 'Metro Manila',
country: 'PH',
zipCode: '1200',
telNo: '09171234567',
email: '[email protected]',
},
});
// payment.url → redirect customer here
// payment.refNo → DragonPay reference number
// payment.txnId → your transaction ID
// payment.status → 'S' (success creating the request)Check Transaction Status
const status = await client.getTransactionStatus('ORDER-001');
// status.status → 'S' | 'F' | 'P' | 'U' | 'V' | ...
// status.message → human-readable messageCancel a Transaction
const result = await client.cancelTransaction('ORDER-001');List Available Processors
const processors = await client.getAvailableProcessors(1000);
// Returns processors available for the given amount
// [{ procId: 'GCSH', shortName: 'GCash', minAmount: 1, maxAmount: 50000,
// mustRedirect: true, surcharge: 0, dayOfWeek: 'Mon-Fri', ... }]Look Up by Reference Number
const status = await client.getTransactionStatusByRefNo('REF123456');Transaction History
const history = await client.getTransactionHistory('2026-07-01', '2026-07-07');
const settled = await client.getSettledTransactionHistory('2026-07-01', '2026-07-07');QR Ph (ABQR)
Pass procId: 'ABQR' to createPayment to get a raw EMV QR string back for rendering as a scannable QR code, in addition to the usual redirect url:
const payment = await client.createPayment(txnId, {
amount: 500,
description: 'QR Ph payment',
email: '[email protected]',
procId: 'ABQR',
});
payment.qrph; // raw EMV QR string — render as a QR codeLifetime Id / MUVA (Recurring Virtual Accounts)
A Lifetime Id (MUVA) is a persistent virtual account customers can pay into repeatedly — useful for recurring dues, top-ups, or subscriptions without re-issuing a new payment link each cycle.
const lid = await client.createLifetimeId({
description: 'Monthly HOA dues',
email: '[email protected]',
procId: 'BOG', // bank/virtual-account processor
});
// lid.id, lid.virtualAccountNo
await client.activateLifetimeId(lid.id);
await client.deactivateLifetimeId(lid.id);
const details = await client.getLifetimeId(lid.id);
await client.updateLifetimeId(lid.id, { email: '[email protected]' });
// Generate a static QR Ph code customers can scan repeatedly
const qr = await client.generateStaticQr(lid.id);
qr.qrph; // raw EMV QR stringPayout API
Requires payoutPassword and payoutUrl in configuration. The payout password is a separate credential from your collection password — contact DragonPay directly to request payout API access, as it is not available in the sandbox merchant portal.
const payout = await client.createPayout('PAYOUT-001', {
firstName: 'Juan',
middleName: 'Santos', // optional
lastName: 'Cruz',
amount: 500,
description: 'Withdrawal',
email: '[email protected]',
procId: 'BOG', // bank payout
procDetail: '1234567890', // account number
runDate: '2026-03-16', // optional, defaults to today
mobileNo: '09171234567', // optional
birthDate: '1990-01-15', // optional
nationality: 'Filipino', // optional
address: { // optional
street1: '123 Rizal St',
street2: 'Unit 4B',
barangay: 'San Lorenzo',
city: 'Makati',
province: 'Metro Manila',
country: 'PH',
},
});
const status = await client.getPayoutStatus('PAYOUT-001');Void a Payout
await client.voidPayout('PAYOUT-001');Ledger Balance & Available Channels
const balance = await client.getPayoutBalance();
// balance.balance, balance.currency
const channels = await client.getPayoutProcessors();
// [{ procId: 'BOG', shortName: 'BDO', merchantFee: 10, userFee: 0, ... }]Payout & Ledger History
const transactions = await client.getPayoutTransactions('2026-07-01', '2026-07-07');
const ledger = await client.getPayoutLedger('2026-07-01', '2026-07-07');
// ledger entries include debit, credit, and running balancePostback Verification
DragonPay sends postback callbacks to your server when a transaction status changes. The SDK supports all three verification methods:
| Method | Field | Status |
|---|---|---|
| RSA-SHA256 | signatures | Current standard |
| HMAC-SHA256 | signature | Sunset March 2026 |
| SHA1 digest | digest | Deprecated by DragonPay for the Collection API — still verified for backward compatibility, but logs a console.warn on every use. Migrate to signature or signatures. Payout postbacks still use SHA1 digest only (no alternative documented by DragonPay). |
Express / Fastify
// POST /webhook/dragonpay
app.post('/webhook/dragonpay', async (req, res) => {
const result = await client.handlePostback(req.body);
// or: client.handlePostback(req.query) for GET postbacks
if (!result.verified) {
return res.status(400).send('Invalid signature');
}
// result.txnId, result.status, result.refNo, result.amount, ...
// Update your order in the database
res.send('OK');
});Payout Postback
app.post('/webhook/dragonpay/payout', (req, res) => {
const result = client.handlePayoutPostback(req.body);
if (!result.verified) {
return res.status(400).send('Invalid signature');
}
// result.txnId, result.status, result.refNo
res.send('OK');
});Return URL (Browser Redirect)
After payment, DragonPay redirects the customer to your return URL. This is not authoritative — always rely on the server-to-server postback for the real status.
// GET /payment/return?txnid=ORDER-001&refno=REF123&status=S&message=OK
app.get('/payment/return', (req, res) => {
const result = client.parseReturnParams(req.query);
// Show a "thank you" or "payment failed" page based on result.status
});Processor Mapping
Map human-readable names to DragonPay processor codes:
import { mapProcessorCode, PROCESSOR_MAP } from 'dragonpay-ph';
mapProcessorCode('gcash'); // 'GCSH'
mapProcessorCode('maya'); // 'PYMY'
mapProcessorCode('bpi'); // 'BPI'
mapProcessorCode('7eleven'); // '7ELE'
mapProcessorCode('GCSH'); // 'GCSH' (pass-through)Supported processors: GCash, Maya, GrabPay, ShopeePay, BPI, BDO, UnionBank, Metrobank, InstaPay, PESONet, 7-Eleven, Bayad, Cebuana, M Lhuillier, ECPay, Credit Card, Bank Transfer.
Transaction ID Generation
import { generateTxnId } from 'dragonpay-ph';
const txnId = generateTxnId(); // 'TXN-m4k8q2-a7b3c9d1'
// or: client.generateTxnId()Status Codes
import { DRAGONPAY_STATUS } from 'dragonpay-ph';
DRAGONPAY_STATUS.SUCCESS; // 'S'
DRAGONPAY_STATUS.FAILURE; // 'F'
DRAGONPAY_STATUS.PENDING; // 'P'
DRAGONPAY_STATUS.UNKNOWN; // 'U'
DRAGONPAY_STATUS.VOID; // 'V'
DRAGONPAY_STATUS.QUEUED; // 'Q'
DRAGONPAY_STATUS.REFUND; // 'R'
DRAGONPAY_STATUS.CHARGEBACK; // 'K'Error Handling
import { DragonPayError, SignatureVerificationError } from 'dragonpay-ph';
try {
await client.createPayment('ORDER-001', { ... });
} catch (err) {
if (err instanceof DragonPayError) {
console.log(err.code); // error code from API
console.log(err.apiMessage); // raw API message
}
}Mock Server
For local development without DragonPay credentials:
import { createMockServer } from 'dragonpay-ph/mock';
const mock = createMockServer({
port: 4010,
merchantId: 'TEST',
password: 'test',
postbackUrl: 'http://localhost:3000/webhook/dragonpay',
});
await mock.start();
// Point your client at the mock
const client = new DragonPayClient({
merchantId: 'TEST',
password: 'test',
collectUrl: 'http://localhost:4010/api/collect/v2',
payoutUrl: 'http://localhost:4010/api/payout/merchant/v1',
payoutPassword: 'any-token',
});
// Use client as normal — mock handles all API endpoints
// Visit http://localhost:4010 for the dashboard
// Visit http://localhost:4010/pay/{txnId} to simulate payment outcomesThe mock server provides:
- All Collection API endpoints (create, status, cancel, processors)
- All Payout API endpoints (create, status)
- A payment page with "Simulate Success/Failure" buttons
- Postback callbacks with valid SHA1 digests
- A dashboard showing all transactions
- Duplicate transaction detection
Running the Demo
cd packages/dragonpay-node
npx tsx examples/demo.tsAPI Reference
DragonPayClient
| Method | Description |
|---|---|
| createPayment(txnId, input) | Create a collection payment request |
| getTransactionStatus(txnId) | Get current transaction status |
| getTransactionStatusByRefNo(refNo) | Get transaction status by DragonPay reference number |
| cancelTransaction(txnId) | Void/cancel a pending transaction |
| getAvailableProcessors(amount) | List available payment processors |
| getTransactionHistory(startDate, endDate) | List transactions in a date range |
| getSettledTransactionHistory(startDate, endDate) | List settled transactions in a date range |
| createLifetimeId(input) | Create a Lifetime Id / MUVA (recurring virtual account) |
| activateLifetimeId(id) | Activate a Lifetime Id |
| deactivateLifetimeId(id) | Deactivate a Lifetime Id |
| getLifetimeId(id) | Retrieve a Lifetime Id's details |
| updateLifetimeId(id, updates) | Update a Lifetime Id's description/email |
| generateStaticQr(id) | Generate a static QR Ph code for a Lifetime Id |
| handlePostback(params) | Verify and parse a collection postback |
| handlePayoutPostback(params) | Verify and parse a payout postback |
| parseReturnParams(params) | Parse return URL params (UX only) |
| createPayout(txnId, input) | Create a payout/disbursement |
| getPayoutStatus(txnId) | Get payout status |
| voidPayout(txnId) | Void a pending payout |
| getPayoutBalance() | Get ledger balance available for payouts |
| getPayoutProcessors() | List available payout channels and fees |
| getPayoutTransactions(startDate, endDate) | List payout transactions in a date range |
| getPayoutLedger(startDate, endDate) | List ledger debit/credit entries in a date range |
| getPublicKeys(forceRefresh?) | Fetch RSA public keys (cached 1hr) |
| generateTxnId() | Generate a unique transaction ID |
| mapProcessorCode(code) | Map processor name to DragonPay code |
License
MIT
