npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@qrismu/sdk

v1.1.0

Published

Official Node.js SDK for QRISMU Payment Gateway

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/sdk

Quick 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-000001

Configuration

| 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 | failed

listTransactions(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); // paid

Withdrawals

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_flat

getDashboard()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_secret

updateWebhookConfig(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_key

Webhook 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