@qrismu/sdk
v1.1.0
Published
Official Node.js SDK for QRISMU Payment Gateway
Maintainers
Readme
@qrismu/sdk
Official Node.js SDK for the QRISMU QRIS Payment Gateway.
Zero dependencies. Pure Node.js built-ins. Works with Node.js >= 14.
Installation
npm install @qrismu/sdkQuick Start
const QrismuClient = require('@qrismu/sdk');
const qrismu = new QrismuClient({
apiKey: 'QRMU_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
secretKey: 'QRMUS_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
});
// Create a transaction
const tx = await qrismu.createTransaction({
order_id: 'ORDER-001',
amount: 50000,
customer_name: 'Budi Santoso',
customer_email: '[email protected]',
});
console.log(tx.qris_url); // Payment page URL
console.log(tx.transaction_id); // QRM-20260305-000001Configuration
| Option | Type | Required | Default | Description |
|-------------|----------|----------|--------------------------------------|------------------------------------|
| apiKey | string | Yes | — | Public API key (QRMU_live_…) |
| secretKey | string | Yes | — | Secret API key (QRMUS_…) |
| baseUrl | string | No | https://api.qrismu.app/api/v1 | Override API base URL |
| timeout | number | No | 30000 | Request timeout in ms |
API Reference
Transactions
createTransaction(params) → Promise<Object>
Create a new QRIS payment transaction.
| Parameter | Type | Required | Description |
|-------------------|----------|----------|----------------------------------------------------|
| order_id | string | Yes | Unique order ID from your system |
| amount | number | Yes | Amount in IDR (integer, min 1000) |
| customer_name | string | No | Customer full name |
| customer_email | string | No | Customer email address |
| customer_phone | string | No | Customer phone number |
| expiry_minutes | number | No | Expiry in minutes (1–1440, default: 5) |
| description | string | No | Transaction description |
| webhook_url | string | No | Webhook URL override for this transaction |
| redirect_url | string | No | Redirect URL after successful payment |
const tx = await qrismu.createTransaction({
order_id: 'INV-2026-001',
amount: 150000,
});
// tx.qris_url, tx.qris_base64, tx.expiry, tx.transaction_id, tx.fee, ...getTransaction(transactionId) → Promise<Object>
Get a single transaction by its QRISMU ID.
const tx = await qrismu.getTransaction('QRM-20260305-000001');
console.log(tx.status); // pending | paid | expired | failedlistTransactions(params?) → Promise<Object>
List transactions with optional filters.
| Parameter | Type | Description |
|--------------|----------|--------------------------------------------------|
| page | number | Page number (default: 1) |
| limit | number | Items per page (default: 20, max: 100) |
| status | string | Filter: pending | paid | expired | failed |
| start_date | string | ISO 8601 start date (inclusive) |
| end_date | string | ISO 8601 end date (inclusive) |
const result = await qrismu.listTransactions({ status: 'paid', limit: 50 });
// result.transactions → array
// result.pagination → { page, limit, total, total_pages }pollTransaction(transactionId, opts?) → Promise<Object>
Poll a transaction until it reaches a terminal status (paid, expired, or failed), or until the timeout is exceeded.
| Option | Type | Default | Description |
|--------------|----------|-----------|---------------------------|
| intervalMs | number | 3000 | Polling interval in ms |
| timeoutMs | number | 600000 | Total timeout in ms |
const tx = await qrismu.pollTransaction('QRM-20260305-000001', {
intervalMs: 5000,
timeoutMs: 120000,
});
console.log(tx.status); // paidWithdrawals
createWithdrawal(params) → Promise<Object>
Request a withdrawal to a registered bank account.
| Parameter | Type | Required | Description |
|-------------------|----------|----------|---------------------------------------------|
| amount | number | Yes | Amount to withdraw in IDR |
| bank_account_id | string | Yes | ID of the destination bank account |
| notes | string | No | Optional notes for this withdrawal |
const withdrawal = await qrismu.createWithdrawal({
amount: 500000,
bank_account_id: 'ba_xxxx',
notes: 'Penarikan mingguan',
});listWithdrawals(params?) → Promise<Object>
List withdrawals with optional filters.
| Parameter | Type | Description |
|--------------|----------|--------------------------------------------------------------------|
| page | number | Page number (default: 1) |
| limit | number | Items per page (default: 20, max: 100) |
| status | string | Filter: pending | processing | completed | rejected |
| start_date | string | ISO 8601 start date |
| end_date | string | ISO 8601 end date |
const result = await qrismu.listWithdrawals({ status: 'completed' });getWithdrawal(withdrawalId) → Promise<Object>
const withdrawal = await qrismu.getWithdrawal('WD-20260305-000001');Bank Accounts
listBankAccounts() → Promise<Object[]>
List all registered bank accounts / e-wallets for your merchant account.
const accounts = await qrismu.listBankAccounts();createBankAccount(params) → Promise<Object>
Register a new bank account or e-wallet for withdrawals.
| Parameter | Type | Required | Description |
|------------------|----------|----------|-----------------------------------------------------------|
| bank_name | string | Yes | Bank or e-wallet name (e.g. "BCA", "DANA", "OVO") |
| account_number | string | Yes | Account number or e-wallet phone number |
| account_name | string | Yes | Account holder full name |
const account = await qrismu.createBankAccount({
bank_name: 'BCA',
account_number: '1234567890',
account_name: 'Budi Santoso',
});deleteBankAccount(bankAccountId) → Promise<Object>
await qrismu.deleteBankAccount('ba_xxxx');Profile & Balance
getProfile() → Promise<Object>
const profile = await qrismu.getProfile();updateProfile(params) → Promise<Object>
| Parameter | Type | Description |
|-----------------|----------|----------------------|
| business_name | string | Merchant / business name |
| business_type | string | Business type / category |
| address | string | Business address |
| phone | string | Contact phone |
| website | string | Website URL |
const updated = await qrismu.updateProfile({ business_name: 'Toko Budi' });getBalance() → Promise<Object>
Returns current balance and fee configuration.
const bal = await qrismu.getBalance();
// bal.balance, bal.fee_percentage, bal.fee_flatgetDashboard() → Promise<Object>
Returns a summary for your merchant dashboard: balance, recent transactions, and stats.
const dash = await qrismu.getDashboard();Settings
getWebhookConfig() → Promise<Object>
const config = await qrismu.getWebhookConfig();
// config.webhook_url, config.webhook_enabled, config.webhook_secretupdateWebhookConfig(params) → Promise<Object>
| Parameter | Type | Description |
|-------------------|-----------|--------------------------------------|
| webhook_url | string | HTTPS URL to receive webhook events |
| webhook_enabled | boolean | Enable or disable webhook delivery |
await qrismu.updateWebhookConfig({
webhook_url: 'https://yourapp.com/webhooks/qrismu',
webhook_enabled: true,
});listWhitelistIPs() → Promise<Object[]>
const ips = await qrismu.listWhitelistIPs();addWhitelistIP(params) → Promise<Object>
When at least one entry exists, requests from non-whitelisted IPs are rejected with 403.
| Parameter | Type | Required | Description |
|-----------|----------|----------|-----------------------------------------------|
| ip | string | Yes | IPv4 address or CIDR range (e.g. 10.0.0.0/24) |
| label | string | No | Human-readable label |
await qrismu.addWhitelistIP({ ip: '203.0.113.5', label: 'Production Server' });removeWhitelistIP(whitelistId) → Promise<Object>
await qrismu.removeWhitelistIP('wl_xxxx');regenerateApiKey() → Promise<Object>
Generates a new API key pair. The old API key is invalidated immediately.
const keys = await qrismu.regenerateApiKey();
// keys.api_key, keys.secret_keyWebhook Verification
QRISMU signs every webhook delivery with HMAC-SHA256(webhookSecret, rawBody) and sends the hex digest in the X-Webhook-Signature header.
QrismuClient.verifyWebhook(opts) — static
| Parameter | Type | Description |
|-----------------|----------|------------------------------------------------|
| signature | string | Value of X-Webhook-Signature header |
| rawBody | string | Raw request body as a string (not parsed JSON) |
| webhookSecret | string | Your webhook secret from Settings > Webhook |
// Express example
app.post('/webhooks/qrismu', express.raw({ type: 'application/json' }), (req, res) => {
const valid = QrismuClient.verifyWebhook({
signature: req.headers['x-webhook-signature'],
rawBody: req.body.toString(),
webhookSecret: process.env.QRISMU_WEBHOOK_SECRET,
});
if (!valid) return res.status(401).send('Invalid signature');
const event = JSON.parse(req.body.toString());
if (event.event === 'payment.success') {
// fulfill the order
}
res.sendStatus(200);
});Instance method alias
// Falls back to your API secretKey if webhookSecret is omitted
const valid = qrismu.verifyWebhook({
signature: req.headers['x-webhook-signature'],
rawBody: req.body.toString(),
});Webhook Payload
{
"event": "payment.success",
"transaction_id": "QRM-20260305-000001",
"order_id": "ORDER-001",
"amount": 50000,
"fee": 350,
"net_amount": 49650,
"status": "paid",
"paid_at": "2026-03-05T10:30:00.000Z",
"customer_name": "Budi Santoso",
"customer_email": "[email protected]"
}Retry policy: 3 attempts at 0 s, 30 s, and 5 min. Any 2xx response is treated as success.
Error Handling
All failed requests throw a QrismuError.
const { QrismuError } = require('@qrismu/sdk');
try {
await qrismu.createTransaction({ order_id: 'ORD-1', amount: 50000 });
} catch (err) {
if (err instanceof QrismuError) {
console.error(err.message); // Human-readable message
console.error(err.code); // HTTP status code
console.error(err.raw); // Full response body or null
}
}| Code | Meaning |
|------|-----------------------------------------------|
| 400 | Validation error / bad request |
| 401 | Invalid or missing API key / signature |
| 403 | IP not whitelisted |
| 404 | Resource not found |
| 409 | Duplicate order_id |
| 429 | Rate limit exceeded (100 req/min per API key) |
| 500 | Internal server error |
| 408 | Request timed out (client-side) |
| 0 | Network error (no HTTP response) |
License
MIT © QRISMU
