@net.christianto/qris-mpm
v0.0.3
Published
QRIS merchant-presented mode QR payload parser and encoder.
Maintainers
Readme
@net.christianto/qris-mpm
Parse, validate and encode QRIS (Indonesia) merchant-presented QR payloads. It
extends @net.christianto/emvco-mpm with the QRIS templates and
rules. Spec notes: docs/tags.md.
Disclaimer: this library is vibe-coded and has only been reviewed very lightly. Expect API instability while the code is being combed through. Versions stay at
0.x(pre-release) until then.
Sources:
- EMVCo: EMV® QR Codes: the EMV QRCPS Merchant-Presented Mode specification (v1.1) and its bulletins, free to download from EMVCo.
- ASPI Bulletin No. 3/III/BASPI/2021, Tampilan QRIS MPM (PDF): the public QRIS display bulletin. The QRIS MPM specification itself is not public.
pnpm add @net.christianto/qris-mpmUsage
import { Qris, QrisPayload } from "@net.christianto/qris-mpm";
const qr = Qris.parse(input); // throws EmvcoMpmValidationError
const result = Qris.safeParse(input, { mode: "lenient" }); // { success, data?, issues }
qr.nmid; // 51 -> 02
qr.merchantCriteria; // "UMI" | "UKE" | "UME" | "UBE" | "URE"
qr.terminalId; // 62 -> 07
qr.nns; // string[], first 8 digits of each PJSP Merchant PAN
qr.pjsps; // QrisPjspTemplate[] (tags 26-45)
qr.isDynamic; // 01 === "12"
Qris.encode(qr); // string with a fresh CRC
const built = QrisPayload.create({
payloadFormatIndicator: "01",
pointOfInitiationMethod: "11",
merchantAccountInformation: [
{
tag: "26",
gui: "ID.EXAMPLE.WWW",
merchantPan: "...",
merchantId: "...",
merchantCriteria: "UMI",
},
{
tag: "51",
gui: "ID.CO.QRIS.WWW",
nmid: "ID...",
merchantCriteria: "UMI",
},
],
// 52, 53, 58, 59, 60 ...
});Parsed payloads encode back to the input. Static to dynamic conversion is not provided.
Modes and issues
strict (default) makes QRIS rules errors; lenient makes them warnings. The
NMID format check (15 upper-case letters or digits) is a warning in both modes. Issues have the same shape as in emvco-mpm
(path, code, severity, message, offset) and QRIS codes start
with qris.observed. (see QRIS_ISSUE_CODES).
The normative QRIS specification is not public. Rules labelled Observed in docs/tags.md are reverse-engineered from real payloads and the ASPI bulletin, not normative.
Standard Schema
Qris.schema() works like EmvcoMpm.schema() but outputs a QrisPayload:
const Order = z.object({ id: z.string(), qr: Qris.schema() });
Order.parse(body).qr.nmid;Qris.fieldsSchema() validates a plain fields object. Details:
emvco-mpm docs/standard-schema.md.
Data
import { merchantCriteria } from "@net.christianto/qris-mpm/data/merchant-criteria";Extending
qrisRegistry is defaultRegistry.extend(...); emvco-mpm itself is not
changed by importing this package. Subclassing models with decorators (@Tag)
needs "experimentalDecorators": true in your tsconfig.
License
MIT.
