documentation the QRIS Hook project
QRIS Hook upgrades a static QRIS payload into a dynamic one by injecting a random three-digit suffix into the transaction amount, recalculating the checksum, and returning a ready to scan QR code image. It is built with the Next.js App Router and meant for merchant teams who want a quick way to validate incoming payments.
- Browser form that accepts a static payment amount and calls the API in real time.
- API endpoint
POST /api/generatethat transforms a static QRIS payload into a dynamic payload. - Automatic CRC-16/CCITT-FALSE checksum recalculation aligned with EMVCo guidelines.
- Smart reassembly of QRIS tags (switching tag
01to dynamic, rebuilding tag54, preserving locality tags). - QR code rendering to a PNG Data URL using the
qrcodepackage.
- The user enters a payment amount in the web form (
app/page.tsx). - The form posts to
POST /api/generatewith the amount. - The API handler (
app/api/generate/route.ts):- Validates the request payload.
- Reads the original static QRIS payload from the
inputQrisenvironment variable. - Generates a randomized amount using helpers in
lib/qris.ts. - Rebuilds the QRIS payload and recomputes the CRC.
- Produces a QR code image (PNG Data URL) and returns it along with metadata.
- Framework: Next.js 14 (App Router) on top of React 18.
- Language: TypeScript.
- QR Generator:
qrcodefor PNG Data URLs. - Hosting Targets: Vercel, Netlify, Render, or any Next.js-compatible platform.
- Node.js 18 LTS or newer.
- npm 9+ (or a compatible pnpm/yarn release).
- A valid static QRIS payload string that you are allowed to use.
-
Clone the repository
git clone https://github.com/kdandy/QRIS-Hook.git cd QRIS-Hook -
Install dependencies
npm install
-
Create a local environment file
cp .env-development to .env
Set the static QRIS payload via environment variables:
inputQris="<your_static_qris_payload_here>"Guidelines:
- The payload must be the full QRIS string with no whitespace.
- Keep this value secret; do not commit
.env.localto version control.
-
Development server
npm run dev
Then open
http://localhost:3000. -
Production build
npm run build npm run start
-
Lint check
npm run lint
-
Request body
{ "amount": "10000" }
Notes:
-
amountaccepts either a string or a number. -
Provide at least four digits so the helper can randomize the last three.
-
Successful response (200)
{ "originalNominal": "10000", "randomizedNominal": "10795", "randomSuffix": "795", "qris": "010212...6304ABCD", "qrImage": "data:image/png;base64,iVBORw0..." }
Fields:
-
originalNominal: sanitized amount received from the request. -
randomizedNominal: amount after the random suffix is applied. -
randomSuffix: the generated three-digit suffix. -
qris: the final QRIS payload ready for validation or storage. -
qrImage: PNG Data URL of the QR code, convenient for<img>tags. -
Error responses
400: malformed JSON, missing/invalidamount, nominal too short, or other validation issues.500: environment variableinputQrismissing or unexpected server error.
All located in lib/qris.ts:
randomizeNominal(input): sanitizes the amount and swaps its last three digits with a random value (000-999).prosesQris(qrString, nominal): converts tag01from static to dynamic (010211→010212), rebuilds tag54with the new nominal, and recalculates CRC usingcrc16CcittFalse.generateQrPngData(qrisData): wraps the payload into a PNG Data URL viaQRCode.toDataURLwith error correction levelL.
- Automated tests are not yet included. Consider adding unit tests for
randomizeNominalandprosesQristo lock down the business logic. - Perform manual QA by generating multiple amounts and scanning the resulting QR codes with a QRIS-compatible payment app.
- Set
inputQrisin your hosting platform's environment variable settings. - No
NEXT_PUBLIC_prefix is required because the variable is only read on the server side. - Restart the deployment whenever you rotate the QRIS payload.
- "Environment variable 'inputQris' belum diisi."
- Ensure
.env.localexists locally, or the production env var is configured.
- Ensure
- Unexpected nominal value
- The API always randomizes the last three digits; use
randomizedNominalfor actual reconciliation.
- The API always randomizes the last three digits; use
- QR code fails to scan
- Verify your static QRIS payload is valid and complete before conversion.
- Allow configuring the number of randomized digits.
- Persist conversion history for auditing.
- Expose a verification endpoint to confirm that incoming payments match generated suffixes.
Maintained and documented by Dandy.