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

@nds-stack/qris-utils

v1.0.0

Published

QRIS utilities - decode, parse, convert, and validate Indonesian QRIS payment codes

Readme

@nds-stack/qris-utils

npm version License: MIT

QRIS utilities for Node.js - decode, parse, convert, and validate Indonesian QRIS payment codes.

✨ Features

  • 📷 QR Decoder - Extract QRIS string from images (base64, buffer, file, URL)
  • 🔍 TLV Parser - Parse QRIS TLV structure to readable object
  • 🔄 Static → Dynamic Converter - Convert static QRIS to dynamic with amount
  • CRC16 Validator - Validate QRIS checksum
  • 📝 TypeScript Ready - Full type definitions included
  • 🚀 Zero Config - Works out of the box

📦 Installation

npm install @nds-stack/qris-utils

🚀 Quick Start

const qris = require('@nds-stack/qris-utils');

// If you already have a QRIS string
const qrisString = '00020101021126650013ID...';

// Parse QRIS string
const parsed = qris.parseQRIS(qrisString);
console.log(parsed.merchantName);  // "Warung Sayur"
console.log(parsed.method);        // "static"

// Convert static to dynamic (Rp 50.000)
const dynamicQRIS = qris.convertQRIS(qrisString, { amount: 50000 });

// Validate QRIS
const isValid = qris.validateCRC16(dynamicQRIS);

📚 API Reference

CRC16

| Method | Description | |--------|-------------| | calculateCRC16(str) | Calculate CRC16-CCITT checksum | | validateCRC16(qrisString) | Validate QRIS string CRC |

Parser

| Method | Description | |--------|-------------| | parseTLV(data) | Parse raw TLV string to elements | | parseQRIS(qrisString) | Parse QRIS string to structured object |

Converter

| Method | Description | |--------|-------------| | convertQRIS(qrisString, { amount }) | Convert static → dynamic QRIS | | convertQRISWithFee(qrisString, { amount, fee }) | Convert with service fee |

Decoder

| Method | Description | |--------|-------------| | decodeQRFromBase64(base64Image) | Decode QR from base64 string | | decodeQRFromBuffer(imageBuffer) | Decode QR from Buffer | | decodeQRFromFile(filePath) | Decode QR from local file | | decodeQRFromUrl(imageUrl, fetchOptions) | Download & decode QR from URL | | isValidQRISString(str) | Check if string is valid QRIS |

Generator

| Method | Description | |--------|-------------| | generateQRDataURL(qrisString, options) | Generate QR as data URL (base64 image) | | generateQRBuffer(qrisString, options) | Generate QR as Buffer (PNG) | | generateQRBase64(qrisString, options) | Generate QR as base64 string | | generateQRFile(qrisString, filePath, options) | Save QR to file | | generateQRSVG(qrisString, options) | Generate QR as SVG string | | generateQRAscii(qrisString, options) | Generate QR as ASCII/terminal output |

📖 Examples

Parse QRIS String

const qris = require('@nds-stack/qris-utils');

const qrisString = '00020101021126650013ID...';
const parsed = qris.parseQRIS(qrisString);

console.log('Merchant:', parsed.merchantName);
console.log('City:', parsed.merchantCity);
console.log('MCC:', parsed.merchantCategoryCode);
console.log('Method:', parsed.method);

Decode QRIS from Local File

const qris = require('@nds-stack/qris-utils');

const qrisString = await qris.decodeQRFromFile('./qris-static.png');
console.log('QRIS String:', qrisString);

Decode QRIS from URL

const qris = require('@nds-stack/qris-utils');

const qrisString = await qris.decodeQRFromUrl('https://example.com/qris.png', {
  headers: { 'Authorization': 'Bearer token' } // optional
});

Decode QRIS from Base64

const qris = require('@nds-stack/qris-utils');

const base64Image = 'data:image/png;base64,iVBORw0KGgo...';
const qrisString = await qris.decodeQRFromBase64(base64Image);

Decode QRIS from Buffer

const qris = require('@nds-stack/qris-utils');
const fs = require('fs');

const buffer = fs.readFileSync('./qris.png');
const qrisString = await qris.decodeQRFromBuffer(buffer);

Convert Static to Dynamic QRIS

const qris = require('@nds-stack/qris-utils');

const staticQRIS = '00020101021126650013ID...';
const dynamicQRIS = qris.convertQRIS(staticQRIS, { amount: 50000 });

console.log('Dynamic QRIS:', dynamicQRIS);

Convert with Service Fee

const qris = require('@nds-stack/qris-utils');

const staticQRIS = '00020101021126650013ID...';

// Fixed fee Rp 1.000
const withFixedFee = qris.convertQRISWithFee(staticQRIS, {
  amount: 50000,
  fee: { type: 'fixed', value: 1000 }
});

// Percentage fee 2.5%
const withPercentFee = qris.convertQRISWithFee(staticQRIS, {
  amount: 50000,
  fee: { type: 'percentage', value: 2.5 }
});

Generate QR Image

const qris = require('@nds-stack/qris-utils');

const qrisString = '000201010212...';

// Generate as data URL (bisa langsung tampil di browser)
const dataUrl = await qris.generateQRDataURL(qrisString);

// Save to file
await qris.generateQRFile(qrisString, './qris-dynamic.png');

// Generate base64
const base64 = await qris.generateQRBase64(qrisString);

Full Flow: From Image to Dynamic QRIS (Complete)

const qris = require('@nds-stack/qris-utils');

// Step 1: Decode QR from image file
const staticQRIS = await qris.decodeQRFromFile('./qris-static.png');

// Step 2: Parse to see merchant info (optional)
const parsed = qris.parseQRIS(staticQRIS);
console.log('Merchant:', parsed.merchantName);
console.log('Original method:', parsed.method);

// Step 3: Convert static to dynamic with amount
const amount = 75000;
const dynamicQRIS = qris.convertQRIS(staticQRIS, { amount });

// Step 4: Validate the dynamic QRIS
if (qris.validateCRC16(dynamicQRIS)) {
  console.log('✅ Dynamic QRIS is valid!');
} else {
  console.log('❌ Invalid QRIS!');
}

// Step 5: Generate QR image from dynamic QRIS
// Option A: Save to file
await qris.generateQRFile(dynamicQRIS, './qris-dynamic.png');
console.log('✅ QR saved to qris-dynamic.png');

// Option B: Get as data URL (for web display)
const dataUrl = await qris.generateQRDataURL(dynamicQRIS);
console.log('✅ QR data URL ready');

// Option C: Get as base64 (for API response)
const base64 = await qris.generateQRBase64(dynamicQRIS);
console.log('✅ QR base64 ready');

// Step 6: Ready to be scanned and paid!
console.log(`📱 Scan the QR to pay Rp ${amount.toLocaleString('id-ID')}`);

🔧 Requirements

  • Node.js >= 18.0.0

📄 License

MIT © nds-stack

⚠️ Disclaimer

This project is for educational purposes only. Use at your own risk.