Skip to content

DHCertainty/DuitNowQR

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

DuitNowQR

Parse, modify, and output Malaysia DuitNow QR code strings for POS and invoicing integration.

Built on the EMVCo QR Code Specification — the same standard behind Singapore PayNow, Thailand PromptPay, and other national QR payment schemes.

npm License: MIT

Developed by DH Certainty — the team behind PaynowQR for Singapore.

Have any questions or need help with a custom implementation? Drop us an email at partnerships@dhcertainty.com.


The Problem

Your bank (Maybank, CIMB, RHB, etc.) generates a static DuitNow QR for your business — but it has no fixed amount. Customers have to key in the amount manually, which is error-prone.

DuitNowQR lets you take that bank-generated QR string and programmatically set the amount, reference number, and other fields — perfect for:

  • 🧾 Invoicing systems — generate a QR per invoice with the exact amount
  • 🏪 POS terminals — embed the QR with the cart total
  • 🔁 Recurring payments — set different amounts per billing cycle

Installation

npm install duitnowqr

Quick Start

import { DuitNowQR } from 'duitnowqr';

// 1. Paste the QR string from your bank-generated QR code
const bankQR = '00020201021126580014A000000615000101065887340212MBBQR1872379031000000000005204581253034585802MY5921MORLIVLY WELLNESS PLT6008SELANGOR63042CDE';

// 2. Parse it
const qr = new DuitNowQR(bankQR);

// 3. Set an amount → outputs a new QR string ready for QR code generation
const qrString = qr.setAmount(25.50).toString();
// → '00020201021226580014A0000006150001...540525.50...630445B4'

// Pass this string to any QR code generator (e.g. `qrcode` npm package)

How to Get Your Base QR String

  1. Open your Malaysian banking app (MAE by Maybank, CIMB Clicks, etc.)
  2. Navigate to your DuitNow QR merchant code
  3. Take a screenshot or scan the QR code
  4. Use any QR code reader to extract the raw string — it starts with 000202
  5. Use that string as the input to new DuitNowQR(string)

API Reference

new DuitNowQR(qrString)

Parse a raw EMVCo QR string into a modifiable object.

const qr = new DuitNowQR('000202010211265800...');

Setters (all return this for chaining)

.setAmount(amount)

Set the transaction amount. Automatically toggles between static and dynamic QR:

Input Behaviour
Positive number (50, 25.50) Sets amount, switches to dynamic QR
Numeric string ('100') Sets amount, switches to dynamic QR
Falsey (null, undefined, 0, '', false) Removes amount, switches to static QR
qr.setAmount(50);       // RM 50.00, dynamic QR
qr.setAmount('25.50');  // RM 25.50, dynamic QR
qr.setAmount(null);     // no amount, static QR
qr.setAmount(0);        // no amount, static QR

.setMerchantName(name)

Set the merchant name (max 25 characters).

.setMerchantCity(city)

Set the merchant city (max 15 characters).

.setMerchantCategoryCode(mcc)

Set the 4-digit Merchant Category Code.

.setReferenceNumber(ref)

Set a bill/reference number (stored in Tag 62). Pass null to remove.

.setPostalCode(code)

Set the postal code. Pass null to remove.


Output

.toString()

Rebuild the full QR string with a recalculated CRC checksum. Pass this to a QR code generator.

const qrString = qr.setAmount(50).toString();

.toJSON()

Get a structured, human-readable object of all parsed fields.

const data = qr.toJSON();
// {
//   payloadFormatIndicator: '02',
//   pointOfInitiationType: 'dynamic',
//   merchantAccountInfo: {
//     guid: 'A0000006150001',
//     acquirerId: '588734',
//     merchantAccountNumber: 'MBBQR1872379',
//     ...
//   },
//   transactionAmount: '50.00',
//   merchantName: 'MORLIVLY WELLNESS PLT',
//   ...
// }

Getters

Getter Description
.payloadFormatIndicator '02' for DuitNow
.pointOfInitiation '11' (static) or '12' (dynamic)
.isStatic true if no fixed amount
.guid DuitNow AID (A0000006150001)
.acquirerId Bank/switching institution ID
.merchantAccountNumber Merchant account (e.g. MBBQR1872379)
.merchantCategoryCode 4-digit MCC
.transactionCurrency '458' for MYR
.amount Amount string or null if static
.countryCode 'MY'
.merchantName Business name
.merchantCity City
.postalCode Postal code or null
.referenceNumber Bill/reference number or null

Usage Examples

POS Integration

import { DuitNowQR } from 'duitnowqr';
import QRCode from 'qrcode';

const BASE_QR = '00020201021126580014A0000006150001...63042CDE';

async function generatePaymentQR(cartTotal: number, orderId: string) {
  const qrString = new DuitNowQR(BASE_QR)
    .setAmount(cartTotal)
    .setReferenceNumber(orderId)
    .toString();

  // Generate QR image (using 'qrcode' npm package)
  const dataUrl = await QRCode.toDataURL(qrString);
  return dataUrl;
}

// Usage
const qrImage = await generatePaymentQR(45.90, 'ORD-20240304-001');

Invoice System

import { DuitNowQR } from 'duitnowqr';

const BASE_QR = '00020201021126580014A0000006150001...63042CDE';

function generateInvoiceQR(invoiceNumber: string, amount: number) {
  return new DuitNowQR(BASE_QR)
    .setAmount(amount)
    .setReferenceNumber(invoiceNumber)
    .toString();
}

// Generate QR strings for multiple invoices
const invoices = [
  { id: 'INV-001', amount: 150.00 },
  { id: 'INV-002', amount: 89.90 },
  { id: 'INV-003', amount: 320.00 },
];

for (const inv of invoices) {
  const qr = generateInvoiceQR(inv.id, inv.amount);
  console.log(`${inv.id}: ${qr}`);
}

Inspect a QR Code

import { DuitNowQR } from 'duitnowqr';

const qr = new DuitNowQR('000202010211265800...');
const data = qr.toJSON();

console.log(`Merchant: ${data.merchantName}`);
console.log(`Bank Account: ${data.merchantAccountInfo.merchantAccountNumber}`);
console.log(`Type: ${data.pointOfInitiationType}`);
console.log(`Amount: ${data.transactionAmount ?? 'Open (customer enters amount)'}`);

DuitNow QR Field Reference

Tag Field Example Value
00 Payload Format Indicator 02
01 Point of Initiation 11 (static) / 12 (dynamic)
26 Merchant Account Info (nested template)
26.00 GUID (PayNet AID) A0000006150001
26.01 Acquirer Institution ID 588734
26.02 Merchant Account Number MBBQR1872379
26.03 Reserved Field 0000000000
52 Merchant Category Code 5812
53 Transaction Currency 458 (MYR)
54 Transaction Amount 50.00
58 Country Code MY
59 Merchant Name MORLIVLY WELLNESS PLT
60 Merchant City SELANGOR
62.01 Reference / Bill Number INV-001
63 CRC Checksum 2CDE

About

DuitNowQR is developed and maintained by DH Certainty, a fintech solutions company specialising in payment integrations across Southeast Asia.

Other projects by DH Certainty:

  • PaynowQR — Singapore PayNow QR code generator for Node.js and JavaScript
  • XeroPayNowQR — Xero invoicing integration with PayNow QR

Feel free to report any issues and feature requests!

License

MIT

About

DuitNow QR code parser and generator for Malaysia — parse, modify, and output EMVCo-compliant QR strings for POS and invoicing

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors