Abdullah Ali — Portfolio

Building software that solves real-world problems.

000
Skip to content
DynQRIS Flutter library for generating Dynamic QRIS offline

DynQRIS

Generate Dynamic QRIS from Static QRIS entirely offline in Flutter.Library2026completed
01

DynQRIS is an open-source Flutter library that converts a static QRIS into a fully compliant Dynamic QRIS completely offline. It was born from a real problem faced by many Indonesian developers: payment providers often supply only static QRIS without offering APIs for generating dynamic QRIS. DynQRIS eliminates that dependency by handling EMV TLV manipulation, transaction amount insertion, and CRC16 checksum calculation directly on the device.

Role
Creator & Maintainer
Client
Open Source
Duration
2 Weeks
Team
Solo
Year
2026
Category
Library
Status
completed

Stack

DartFlutterQRISEMVCoCRC16-CCITTOpen SourceMIT License
02

What it moved.

100%

Offline

No backend required

1

Function Call

Simple developer API

EMV

Compatible

QRIS Specification

MIT

License

Open Source

03

The problem.

Many QRIS providers only distribute static QRIS and do not provide APIs for generating Dynamic QRIS.

Integrating dynamic QRIS usually requires partnering with payment gateways or commercial providers, which increases cost and implementation complexity.

While developing Flutter payment systems, I encountered this limitation myself, and many fellow developers experienced the same frustration.

04

What I built.

Designed a lightweight Dart library capable of converting a static QRIS into a valid Dynamic QRIS entirely offline.

Implemented a complete EMV TLV parser and builder that safely modifies QRIS payloads without relying on external services.

Automatically inserts or updates the transaction amount (Tag 54), ensures required fields are present, and recalculates the CRC16-CCITT checksum.

Exposes a single, developer-friendly API so integration only requires one function call.

05

How it fits together.

DynQRIS is a pure Dart package that performs all QRIS manipulation locally. No backend, payment gateway, or internet connection is required.

01

Flutter Application

Applications provide a static QRIS string and transaction amount before displaying the generated QR code.

FlutterDart
02

QRIS Engine

Parses the EMV TLV structure, updates required tags, reconstructs the payload, and prepares it for validation.

TLV ParserEMV BuilderTransaction Amount Injection
03

Validation

Generates a new CRC16-CCITT checksum to ensure the modified QRIS remains valid according to the EMV specification.

CRC16-CCITTChecksum Validation
06

What it does.

01

Offline Dynamic QRIS

Generate Dynamic QRIS without requiring any server or payment gateway API.

02

Automatic CRC16 Calculation

Recalculates the checksum automatically whenever the QRIS payload changes.

03

EMV TLV Processing

Safely parses, updates, and rebuilds QRIS payloads according to the EMVCo specification.

04

Simple API

Generate Dynamic QRIS using a single function call: QrisHelper.modifyQRIS().

05

Flutter Friendly

Designed specifically for Flutter applications and compatible with popular QR rendering packages.

06

Open Source

Released under the MIT License for both personal and commercial projects.

07

A look at the code.

Generate Dynamic QRIS

dartlib/src/qris_helper.dart
static String modifyQRIS(String qrString, int amount) {
  List<Map<String, dynamic>> fields = _parseEMV(qrString);

  // Remove previous CRC
  fields.removeWhere((f) => f['id'] == '63');

  String amountStr = amount.toString();
  int idx54 = fields.indexWhere((f) => f['id'] == '54');

  if (idx54 != -1) {
    fields[idx54]['value'] = amountStr;
  } else {
    int idx53 = fields.indexWhere((f) => f['id'] == '53');
    int insertAt = idx53 != -1 ? idx53 + 1 : fields.length;

    fields.insert(insertAt, {
      'id': '54',
      'value': amountStr,
    });
  }

  String payload = _buildEMV(fields) + '6304';

  String crc = _crc16ccitt(payload);

  return payload + crc;
}
The primary API developers use. It updates the transaction amount, rebuilds the EMV payload, and generates a new CRC16 checksum completely offline.

CRC16-CCITT

dartlib/src/crc.dart
static String _crc16ccitt(String str) {
  int crc = 0xFFFF;

  for (int i = 0; i < str.length; i++) {
    crc ^= (str.codeUnitAt(i) << 8);

    for (int j = 0; j < 8; j++) {
      if ((crc & 0x8000) != 0) {
        crc = (crc << 1) ^ 0x1021;
      } else {
        crc <<= 1;
      }

      crc &= 0xFFFF;
    }
  }

  return crc
      .toRadixString(16)
      .toUpperCase()
      .padLeft(4, '0');
}
Implements the CRC16-CCITT checksum algorithm required by the EMVCo specification to keep generated QRIS payloads valid.
08

Where it got hard.

01

Understanding EMV QRIS

What broke

QRIS payloads follow the EMV TLV specification, making manual modifications error-prone and difficult to implement correctly.

How it was fixed

Built a reusable parser capable of reading and reconstructing EMV TLV structures while preserving field integrity.

02

Maintaining QR Validity

What broke

Any modification to a QRIS payload invalidates its checksum, causing payment applications to reject the QR code.

How it was fixed

Implemented the CRC16-CCITT algorithm to automatically generate a valid checksum after every modification.

03

Removing Provider Dependency

What broke

Most existing solutions rely on proprietary APIs or payment gateways to generate Dynamic QRIS.

How it was fixed

Designed DynQRIS to work entirely offline using only the original static QRIS string.

09

What I owned.

01

Designed the library architecture.

02

Implemented the EMV TLV parser and builder.

03

Developed the CRC16-CCITT checksum algorithm.

04

Designed the public Dart API.

05

Published and maintained the GitHub repository.

06

Created documentation and integration examples.

10

How it ran.

Research

Week 1

Studied the EMV QRIS specification and reverse-engineered the required TLV structure.

Core Development

Week 1

Implemented the parser, payload builder, transaction amount injection, and CRC generation.

Testing

Week 2

Validated generated QRIS with multiple Indonesian payment applications to ensure compatibility.

Open Source Release

Week 2

Published DynQRIS on GitHub with complete documentation and integration examples.

11

What I took from it.

The best open-source projects often come from solving real developer pain points.

Understanding a technical specification deeply enables building solutions without proprietary APIs.

Keeping APIs simple significantly improves developer experience and adoption.

Offline-first architectures reduce infrastructure costs and increase reliability.

12

What I'd do next.

Publish the package on pub.dev.

Support additional EMV tags and advanced QRIS customization.

Provide QRIS validation and debugging utilities.

Improve automated testing and benchmark performance.

Related

Next case study

DARFIN

DARFIN — A Modern, Multi-threaded Desktop Download Manager built with Golang & Wails.