qris-saurus
v1.0.0
Published
Bun/TypeScript SDK to parse, validate, and transform static QRIS into dynamic QRIS.
Downloads
247
Maintainers
Readme
qris-saurus

Bun/TypeScript SDK untuk parse, validasi, deteksi provider, dan transformasi QRIS statis menjadi QRIS dinamis.
English | Indonesian
Table of Contents
- Apa itu QRIS?
- Bagaimana QRIS bekerja?
- Static vs dynamic QRIS
- Bagaimana qris-saurus bekerja?
- Goals
- How It Works
- Install
- Configure Environment
- Quick start
- Implementasi Sederhana
- Contoh Lengkap
- Error Handling
- Gateway & Custom Providers
- CLI
- Rendering dari library
- Available API
- CLI input priority
- Development
- Documentation
Apa itu QRIS?
QRIS adalah standar QR payment di Indonesia yang menyatukan banyak metode pembayaran di bawah satu format QR. Secara teknis, payload QRIS adalah string TLV (Tag-Length-Value) berbasis spesifikasi EMVCo. Setiap segmen punya:
Tag: identitas field, misalnya54untuk amountLength: panjang isi fieldValue: isi field itu sendiri
Contoh sederhananya:
540812500.00Artinya:
54= transaction amount08= panjang value12500.00= nilai amount
Bagaimana QRIS bekerja?
Secara umum, alurnya seperti ini:
- Merchant memiliki QRIS payload
- bisa QRIS statis dari acquirer/gateway
- bisa QRIS dinamis yang sudah digenerate gateway
- Customer scan QR dengan app seperti ShopeePay, GoPay, mobile banking, atau aplikasi lain yang mendukung QRIS
- App membaca payload TLV dan menampilkan informasi merchant/transaksi
- Switching dan routing dilakukan oleh ekosistem pembayaran sesuai identifier merchant dan acquirer
- Issuer memproses pembayaran
- Merchant menerima notifikasi/settlement dari gateway atau acquirer
Library ini bekerja di lapisan payload construction/manipulation, bukan di lapisan settlement atau switching network.
sequenceDiagram
participant M as Merchant
participant C as Customer
participant A as Payment App
participant N as Payment Network
participant I as Issuer
M->>C: 1. Tampilkan QR Code
C->>A: 2. Scan QR
A->>A: 3. Baca payload TLV
A->>N: 4. Routing & switching
N->>I: 5. Proses pembayaran
I-->>N: 6. Hasil
N-->>A: 7. Settlement
A-->>M: 8. NotifikasiStatic vs dynamic QRIS
QRIS statis
Biasanya dipakai untuk merchant display tetap. Nominal tidak tertanam di payload, sehingga customer mengisi nominal sendiri atau nominal ditentukan dari flow di sisi aplikasi pembayaran.
Ciri umumnya:
- point of initiation method
11 - bisa dipakai berkali-kali
- tidak spesifik ke satu transaksi
QRIS dinamis
Dibuat untuk transaksi tertentu. Nominal dan data tambahan bisa disematkan ke payload.
Ciri umumnya:
- point of initiation method
12 - nominal transaksi ada di tag
54 - dapat membawa reference tambahan di tag
62 - lebih cocok untuk checkout, invoice, POS, dan order-based payments
Bagaimana qris-saurus bekerja?
qris-saurus mengikuti alur berikut:
- parse payload QRIS ke struktur TLV
- validate struktur dasar dan CRC
- detectProvider bila identifier provider dikenali
- transform QRIS statis menjadi dinamis
- serialize payload baru dan hitung ulang CRC
flowchart TD
A[String QRIS Statis] --> B["parse() → TLV Nodes"]
B --> C{"validate()\nCRC & tag valid?"}
C -- Invalid --> X[Throw Error]
C -- Valid --> D["Tag 01: 11 → 12\n(static → dynamic)"]
D --> E["Sisipkan Tag 54 (amount)"]
E --> F["Sisipkan Tag 62\n(merchant ref, terminal)"]
F --> G["Recalculate CRC\n(Tag 63)"]
G --> H["serialize()\n→ String QRIS Dinamis"]Untuk fase sekarang, fokus utama library ini adalah transformasi lokal dari QRIS statis menjadi QRIS dinamis yang valid. Integrasi API gateway seperti Midtrans/Xendit/Duitku bisa ditambahkan kemudian sebagai layer terpisah.
Goals
- Mengubah QRIS statis menjadi QRIS dinamis secara lokal
- Memastikan payload tetap valid dengan CRC yang benar
- Menyediakan fondasi provider-aware untuk ShopeePay, GoPay, Midtrans, Xendit, dan Duitku
- Mudah di-import dari project Bun/TypeScript lain
How It Works
QRIS mengikuti EMVCo QR Code Specification menggunakan encoding TLV (Tag-Length-Value):
[Tag: 2 digit][Length: 2 digit][Value: variable]Contoh annotasi payload nyata:
00020101021126360014ID.CO.QRIS.WWW0114GENERICSTORE01520458125303360
│ │ │ │
│ │ │ └─ 26: merchant account info (length 36)
│ │ └─────── 01: initiation method (length 2, value "11" = static)
│ └──────────── 00: format indicator (length 2, value "01")
│
5802ID5911QRIS SAURUS6007JAKARTA63041669
│ │ │ │
│ │ │ └─ 63: CRC (length 4)
│ │ └──────────── 60: city (length 7)
│ └────────────────────────── 59: merchant name (length 11)
└──────────────────────────────────────── 58: country code (length 2)Proses konversi static → dynamic
Saat staticToDynamic() dipanggil, library melakukan:
- Parse — payload dipecah menjadi array TLV nodes
- Validasi — cek kehadiran dan validitas CRC (tag
63) - Ubah initiation method — tag
01dari11→12 - Sisipkan amount — tambahkan tag
54dengan nilai amount - Sisipkan additional data — tag
62berisi sub-tag:05= merchant reference (bila ada)07= terminal label (bila ada)
- Sisipkan tip — bila
tipTypediberikan, sisipkan tag root:55= tip indicator (02= fixed,03= percent)56= nominal tip fixed57= persentase tip
- Hitung ulang CRC — CRC16/CCITT atas seluruh payload kecuali 4 char terakhir
- Serialize — nodes dikembalikan ke string payload
Key QRIS Tags
| Tag | Nama | Contoh nilai |
| --------- | ---------------------------- | ------------------------- |
| 00 | Format indicator | 01 |
| 01 | Initiation method | 11 statis, 12 dinamis |
| 26–51 | Merchant account info | per provider |
| 52 | Merchant category code (MCC) | 5812 |
| 53 | Currency code | 360 (IDR) |
| 54 | Transaction amount | 25000.00 |
| 55 | Tip or convenience indicator | 02 fixed, 03 percent |
| 56 | Fixed convenience fee | 1000.00 |
| 57 | Percentage convenience fee | 2.00 |
| 58 | Country code | ID |
| 59 | Merchant name | QRIS SAURUS |
| 60 | Merchant city | JAKARTA |
| 62 | Additional data field | sub-tag 05, 07, 08 |
| 63 | CRC | 4 char hex |
Install
Install from your preferred package manager:
npm install qris-sauruspnpm add qris-saurusbun add qris-saurusIf you are working on this repository locally:
bun installConfigure Environment
Buat file .env dari template:
cp .env.example .env.env.example:
# Midtrans
MIDTRANS_SERVER_KEY=SB-Mid-server-xxxxxxxxxxxxxxxxxxxx
MIDTRANS_SANDBOX=true
# Xendit
XENDIT_SECRET_KEY=xnd_development_xxxxxxxxxxxxxxxxxxxxxxxx
# Duitku
DUITKU_MERCHANT_CODE=Dxxxxx
DUITKU_MERCHANT_KEY=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
DUITKU_SANDBOX=trueGunakan dalam kode:
import { midtransAdapter, xenditAdapter, duitkuAdapter } from "qris-saurus";
const midtransConfig = {
serverKey: process.env.MIDTRANS_SERVER_KEY!,
sandbox: process.env.MIDTRANS_SANDBOX === "true",
};
const xenditConfig = {
secretKey: process.env.XENDIT_SECRET_KEY!,
};
const duitkuConfig = {
merchantCode: process.env.DUITKU_MERCHANT_CODE!,
merchantKey: process.env.DUITKU_MERCHANT_KEY!,
sandbox: process.env.DUITKU_SANDBOX === "true",
};Quick start
import { makeDynamic, staticToDynamic, validate } from "qris-saurus";
const dynamicQris = staticToDynamic(staticQrisString, {
amount: 12500,
merchantRef: "INV-001",
terminalLabel: "POS-A",
});
const result = makeDynamic(staticQrisString, {
amount: 12500,
});
console.log(dynamicQris);
console.log(result.provider);
console.log(validate(dynamicQris));Implementasi Sederhana
Ada dua cara menggunakan qris-saurus: sebagai package (SDK) di project TypeScript/Bun kamu, atau langsung via CLI di terminal.
flowchart LR
subgraph SDK["Paket SDK"]
direction TB
S1["bun add qris-saurus"] --> S2["import & transform"]
S2 --> S3["render / gateway"]
end
subgraph Terminal["CLI Terminal"]
direction TB
C1["bun run build"] --> C2["validate → dynamic"]
C2 --> C3["render → PNG"]
endPackage (SDK)
bun add qris-saurusimport { makeDynamic, renderQrToDataUrl, validate } from "qris-saurus";
const STATIC_QRIS = "00020101021126610016ID.CO.SHOPEE.WWW...";
// 1. Transformasi statis → dinamis
const { qrisString, provider } = makeDynamic(STATIC_QRIS, {
amount: 25000,
merchantRef: "INV-001",
});
// 2. Validasi hasil
validate(qrisString); // { valid: true, errors: [] }
// 3. Render ke gambar
const qrImage = await renderQrToDataUrl(qrisString, { width: 320 });
// <img src={qrImage} />CLI
# Build CLI
bun run build
# Validasi payload
bun run dist/cli.js validate "000201010211..."
# Transformasi statis → dinamis
bun run dist/cli.js dynamic "000201010211..." --amount 25000 --merchant-ref INV-001
# Render ke file PNG
bun run dist/cli.js render "000201010211..." --output ./qris.pngContoh Lengkap
Contoh standalone tersedia di folder examples:
bun run examples/basic.ts # Core API: parse, validate, detect, transform
bun run examples/render.ts # Render QR ke file dan data URL
bun run examples/gateway.ts # Integrasi gateway (Midtrans, Xendit, Duitku)Parse payload QRIS
import { parse } from "qris-saurus";
const qris =
"00020101021126610016ID.CO.SHOPEE.WWW01189360091800230223530208230223530303UMI51440014ID.CO.QRIS.WWW0215ID10265163524850303UMI5204581753033605802ID5913Chick n booth6010PEKALONGAN61055118262070703A016304B9ED";
const parsed = parse(qris);
parsed.nodes.find((n) => n.id === "59")?.value; // "Chick n booth"
parsed.nodes.find((n) => n.id === "60")?.value; // "PEKALONGAN"
parsed.nodes.find((n) => n.id === "53")?.value; // "360" (IDR)
parsed.crc; // "B9ED"Validasi payload
import { validate } from "qris-saurus";
const result = validate(qris);
// { valid: true, errors: [] }
const tampered = qris.slice(0, -4) + "0000";
const invalid = validate(tampered);
// { valid: false, errors: ["Invalid CRC value"] }Deteksi provider
import { detectProvider, listProviders } from "qris-saurus";
const provider = detectProvider(qris);
console.log(provider?.info.code); // "shopeepay"
console.log(provider?.info.name); // "ShopeePay"
console.log(provider?.info.supportsApiDynamic); // false
const all = listProviders();
for (const p of all) {
console.log(`${p.info.code}: ${p.info.name}`);
}Transformasi statis ke dinamis
import { staticToDynamic } from "qris-saurus";
const dynamic = staticToDynamic(qris, {
amount: 75000,
merchantRef: "ORD-2024-001",
terminalLabel: "POS-01",
});Transformasi dengan tip
import { staticToDynamic } from "qris-saurus";
// Tip tetap (Rp 2.000)
const withFixedTip = staticToDynamic(qris, {
amount: 50000,
tipType: "fixed",
tipValue: 2000,
});
// Tip persen (5%)
const withPercentTip = staticToDynamic(qris, {
amount: 50000,
tipType: "percent",
tipValue: 5,
});makeDynamic dengan deteksi provider
import { makeDynamic } from "qris-saurus";
const result = makeDynamic(qris, {
amount: 25000,
merchantRef: "INV-001",
});
console.log(result.source); // "local" (transformasi lokal)
console.log(result.provider); // "shopeepay"
console.log(result.amount); // 25000
console.log(result.qrisString); // payload dinamis baruCRC dan serialisasi
import { computeCrc, verifyCrc, parse, serialize } from "qris-saurus";
// Hitung CRC dari payload (termasuk "6304" di akhir)
const payload = qris.slice(0, -4); // buang 4 char CRC, simpan "6304"
const crc = computeCrc(payload);
console.log(crc); // "1669"
// Verifikasi CRC pada string QRIS
const isValid = verifyCrc(qris);
console.log(isValid); // true
// Parse lalu serialize — payload harus sama
const parsed = parse(qris);
const reserialized = serialize(parsed);
console.log(qris === reserialized); // trueRender QR ke gambar
import { renderQrToDataUrl, renderQrToFile, makeDynamic } from "qris-saurus";
const { qrisString } = makeDynamic(qris, { amount: 50000 });
// Base64 data URL — langsung bisa dipakai di HTML
const dataUrl = await renderQrToDataUrl(qrisString, { width: 320 });
// data:image/png;base64,...
// Simpan ke file
await renderQrToFile(qrisString, "./qris.png", { width: 400, margin: 3 });Error handling
import { validate, parse, makeDynamic, staticToDynamic } from "qris-saurus";
// validate() tidak pernah throw — kembalikan { valid, errors }
const check = validate("bukan-qris");
// { valid: false, errors: ["QRIS payload too short"] }
// parse() throw bila CRC hilang/salah
try {
parse("invalid");
} catch (err) {
console.error(err.message); // "QRIS payload is missing CRC tag"
}
// staticToDynamic() throw bila sudah dinamis
const dynamic = staticToDynamic(qris, { amount: 10000 });
try {
staticToDynamic(dynamic, { amount: 10000 });
} catch (err) {
console.error(err.message); // "QRIS payload is already dynamic"
}
// staticToDynamic() throw bila amount negatif
try {
staticToDynamic(qris, { amount: -500 });
} catch (err) {
console.error(err.message); // "Amount must be a positive number"
}Error Handling
validate() bersifat sinkron dan tidak pernah throw — hasil dikembalikan via ValidationResult. Namun parse(), staticToDynamic(), dan makeDynamic() dapat throw bila input tidak valid (CRC hilang/salah, amount tidak valid, dsb). Gateway adapters menggunakan async/await dan dapat throw bila request gagal.
import { validate, makeDynamic, parse, midtransAdapter } from "qris-saurus";
// validate() — tidak throw, cek .valid
const check = validate(qrisString);
if (!check.valid) {
console.error("Invalid QRIS:", check.errors);
// errors: ["Invalid CRC value", "Missing required tag 00", ...]
}
// parse() / makeDynamic() / staticToDynamic() — dapat throw, gunakan try/catch
try {
const dynamic = makeDynamic(qrisString, { amount: 25000 });
console.log(dynamic.source); // "local"
} catch (err) {
// Input tidak valid, CRC salah, atau amount tidak valid
console.error("Transform error:", err);
}
// Gateway adapter — dapat throw, tangkap dengan try/catch
try {
const result = await midtransAdapter.createDynamicQr(
{ orderId: "INV-001", amount: 25000 },
midtransConfig,
{ overrideNotificationUrl: "https://merchant.example/webhooks/midtrans" },
);
console.log(result.qrisString); // payload QRIS mentah
console.log(result.qrImageUrl); // URL PNG QR bila Midtrans mengembalikannya
} catch (err) {
// Network error, auth error, atau response tidak valid
console.error("Gateway error:", err);
}
// Cek status pembayaran
try {
const status = await midtransAdapter.checkPaymentStatus("INV-001", midtransConfig);
// status.status: "pending" | "paid" | "expired" | "failed" | "cancelled"
if (status.status === "paid") {
console.log("Lunas pada:", status.paidAt);
}
} catch (err) {
console.error("Status check error:", err);
}Gateway & Custom Providers
SDK ini menyediakan gateway singleton untuk mempermudah integrasi berbagai provider (Midtrans, Xendit, Duitku) melalui satu interface yang terpusat. Gateway mendelegasikan panggilan ke adapter tanpa perlu pengecekan provider secara manual di kodemu:
import { gateway } from "qris-saurus";
// 1. Configure
gateway.configure({
provider: "midtrans",
serverKey: "SB-Mid-server-xxx", // atau set environment variable MIDTRANS_SERVER_KEY
sandbox: true
});
// 2. Gunakan method abstrak (charge, verify, status) tanpa peduli provider yang sedang aktif
const chargeResult = await gateway.charge("INV-001", 50000);
const statusResult = await gateway.status("INV-001");
const verifyResult = gateway.verify(webhookPayload, headers); // verify bersifat sinkronMendukung Custom Provider (Scaling)
Arsitektur gateway sangat scalable. Kamu bisa dengan mudah membawa provider-mu sendiri (misal Biller lain atau gateway internal) tanpa perlu memodifikasi core library. Cukup implementasikan interface GatewayAdapter yang wajib menyertakan 4 operasi inti: createDynamicQr, checkPaymentStatus, parseWebhook, dan pollPaymentStatus.
Ada dua pendekatan untuk memasang custom adapter:
1. gateway.useAdapter() (Direct Injection)
Gunakan cara ini untuk melempar instance adapter langsung ke singleton. Sangat cocok jika kamu membuat instance di module sendiri:
import { gateway, type GatewayAdapter } from "qris-saurus";
class FinpayAdapter implements GatewayAdapter {
// ...implementasi 4 operasi inti
}
// Pasang adapter langsung
gateway.useAdapter("finpay", new FinpayAdapter(), { apiKey: "secret" });
// Langsung bisa dipakai
await gateway.charge("INV-FIN", 10000); 2. Gateway.registerProvider() (Factory Registration)
Gunakan cara ini jika kamu membuat library atau helper yang mendaftarkan provider secara global, sehingga nantinya aplikasi kamu hanya perlu memanggil gateway.configure():
import { Gateway, gateway } from "qris-saurus";
// Daftarkan ke factory bawaan SDK
Gateway.registerProvider("finpay", () => new FinpayAdapter());
// Sekarang bisa dipakai selayaknya provider bawaan
gateway.configure({
provider: "finpay",
apiKey: "secret"
} as any); // custom provider belum ada di tipe GatewayConfigCLI
Setelah build, CLI tersedia sebagai qris-saurus.
Build CLI
bun run buildHelp
bun run dist/cli.js --helpqris-saurus CLI
Usage:
qris-saurus validate [<qris>] [--input-file <file>]
qris-saurus parse [<qris>] [--input-file <file>]
qris-saurus detect [<qris>] [--input-file <file>]
qris-saurus dynamic [<qris>] --amount <number> [--merchant-ref <text>] [--terminal-label <text>] [--input-file <file>]
qris-saurus render [<qris>] --output <file.png> [--width <number>] [--margin <number>] [--input-file <file>]
Input priority:
1. positional <qris>
2. --input-file <file>
3. stdin pipevalidate
Memeriksa CRC dan tag wajib pada payload.
bun run dist/cli.js validate "<QRIS_PAYLOAD>"
# atau
bun run dist/cli.js validate --input-file ./payload.txtOutput:
{
"valid": true,
"errors": []
}Jika ada masalah:
{
"valid": false,
"errors": [
"Invalid CRC value"
]
}parse
Mem-parse payload menjadi struktur TLV.
bun run dist/cli.js parse "<QRIS_PAYLOAD>"
# atau
cat ./payload.txt | bun run dist/cli.js parseOutput:
{
"raw": "00020101021126360014ID.CO.QRIS.WWW0114GENERICSTORE01520458125303605802ID5911QRIS SAURUS6007JAKARTA63041669",
"nodes": [
{ "id": "00", "length": 2, "value": "01" },
{ "id": "01", "length": 2, "value": "11" },
{
"id": "26",
"length": 36,
"value": "0014ID.CO.QRIS.WWW0114GENERICSTORE01",
"children": [
{ "id": "00", "length": 14, "value": "ID.CO.QRIS.WWW" },
{ "id": "01", "length": 14, "value": "GENERICSTORE01" }
]
},
{ "id": "52", "length": 4, "value": "5812" },
{ "id": "53", "length": 3, "value": "360" },
{ "id": "58", "length": 2, "value": "ID" },
{ "id": "59", "length": 11, "value": "QRIS SAURUS" },
{ "id": "60", "length": 7, "value": "JAKARTA" }
],
"crc": "1669"
}detect
Mendeteksi provider dari merchant account identifier.
bun run dist/cli.js detect "<QRIS_PAYLOAD>"
# atau
bun run dist/cli.js detect --input-file ./payload.txtJika provider dikenali (contoh ShopeePay):
{
"code": "shopeepay",
"name": "ShopeePay",
"aliases": ["shopeepay", "shopee pay"],
"merchantInfoTagIds": ["26", "27", "28", "..."],
"identifiers": ["shopee"],
"supportsApiDynamic": false,
"notes": "Standalone public dynamic QRIS API evidence is limited; use local QRIS transformation by default."
}Jika tidak dikenali:
nulldynamic
Mengubah QRIS statis menjadi dinamis dengan nominal transaksi.
bun run dist/cli.js dynamic "<QRIS_PAYLOAD>" --amount 25000 --merchant-ref INV-001 --terminal-label POS-A
# atau
cat ./payload.txt | bun run dist/cli.js dynamic --amount 25000 --merchant-ref INV-001Output adalah string payload QRIS dinamis baru, siap dirender:
00020101021226360014ID.CO.QRIS.WWW0114GENERICSTORE01520458125303360540825000.005802ID5911QRIS SAURUS6007JAKARTA62200507INV-0010705POS-A6304391FPerbedaan dari payload asli:
- tag
01berubah dari11→12(static → dynamic) - tag
54ditambahkan dengan nominal25000.00 - tag
62ditambahkan denganmerchantRefdanterminalLabel - tag
63(CRC) dihitung ulang
render
Membuat file PNG dari payload QRIS.
bun run dist/cli.js render "<QRIS_PAYLOAD>" --output ./qris.png --width 320 --margin 2
# atau
cat ./payload.txt | bun run dist/cli.js render --output ./qris.pngOutput adalah path file PNG yang berhasil dibuat:
./qris.pngRendering dari library
Simpan ke file
import { renderQrToFile } from "qris-saurus";
await renderQrToFile(qrisPayload, "./qris.png", { width: 320, margin: 2 });
// → file ./qris.png tersimpanOutput sebagai Base64 data URL
import { renderQrToDataUrl } from "qris-saurus";
const dataUrl = await renderQrToDataUrl(qrisPayload, { width: 320 });
console.log(dataUrl);
// data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAUAAAAFACAYAAADNkKWqAAAAAklEQVR4Ae...Hasil dataUrl langsung bisa dipakai di HTML:
<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAUAAAAFACAYAAADNkKWqAAAAAklEQVR4Ae..." />Atau dikirim sebagai JSON response:
return Response.json({ qrImage: dataUrl });Helper ini berguna kalau kamu ingin:
- menampilkan preview QR di web/app internal
- menyimpan QR image ke file
- mengirim hasil render ke pipeline lain setelah payload selesai dibentuk
Available API
Core:
parse(qrisString)— string → TLV nodesserialize(qrisData)— TLV nodes → stringvalidate(qrisString)— cek CRC + tag wajibcomputeCrc(input)/verifyCrc(qrisString)
Transform:
staticToDynamic(qrisString, options)— local transform, return stringmakeDynamic(qrisString, options)— local transform + provider detection, returnDynamicResult
Providers:
detectProvider(qrisString)— returnProviderAdapter | nulllistProviders()— return semua provider terdaftar
Gateway adapters:
midtransAdapter.createDynamicQr(options, config, notificationOptions?)— buat QR via Midtrans API, dengan opsi override/append webhook per transaksimidtransAdapter.checkPaymentStatus(orderId, config)— cek status pembayaranmidtransAdapter.verifyWebhook(payload, config)/parseWebhook(payload, config)/getWebhookStatus(payload)— validasi dan normalisasi webhook MidtransxenditAdapter.createDynamicQr(options, config)— buat QR via Xendit APIxenditAdapter.checkPaymentStatus(gatewayOrderId, config)— cek status pembayaranduitkuAdapter.createDynamicQr(options, config)— buat QR via Duitku APIduitkuAdapter.checkPaymentStatus(orderId, config)— cek status pembayaran
Render:
renderQrToDataUrl(qrisString, options?)— return Base64 PNG data URLrenderQrToFile(qrisString, outputPath, options?)— simpan ke file PNG
CLI input priority
CLI menerima input dengan urutan prioritas:
- argumen langsung
--input-file <file>- stdin / pipe
# 1 — argumen langsung
bun run dist/cli.js validate "00020101021126..."
# 2 — dari file
bun run dist/cli.js dynamic --input-file payload.txt --amount 25000
# 3 — dari stdin / pipe
cat payload.txt | bun run dist/cli.js render --output qris.pngDevelopment
bun install
bun test
bun run typecheck
bun run buildDocumentation
Lihat folder docs:
docs/sdk/index.md— overview SDK & quick startdocs/sdk/api.md— full API referencedocs/sdk/workflow.md— panduan alur end-to-enddocs/sdk/gateway.md— gateway adapters & cek status pembayarandocs/architecture.md— desain internal librarydocs/qris-dynamic.md— teknis QRIS dinamis & TLVdocs/providers.md— catatan per providerdocs/cli.md— panduan CLI lengkap
