PHPackages                             ahr-ahr/qris-dynamic - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Payment Processing](/categories/payments)
4. /
5. ahr-ahr/qris-dynamic

ActiveLibrary[Payment Processing](/categories/payments)

ahr-ahr/qris-dynamic
====================

PHP library for parsing and generating dynamic QRIS payloads.

0.1.0(2mo ago)02MITPHPPHP ^8.1

Since May 13Pushed 2mo agoCompare

[ Source](https://github.com/ahr-ahr/qris-dynamic)[ Packagist](https://packagist.org/packages/ahr-ahr/qris-dynamic)[ Docs](https://github.com/ahr-ahr/qris-dynamic)[ RSS](/packages/ahr-ahr-qris-dynamic/feed)WikiDiscussions main Synced 2w ago

READMEChangelog (1)Dependencies (4)Versions (2)Used By (0)

 English | [Bahasa Indonesia](./README.id.md)

QRIS Dynamic
============

[](#qris-dynamic)

> PHP library for parsing, validating, decoding, and generating dynamic QRIS payloads.

[![PHP](https://camo.githubusercontent.com/233addbd8bc6c491cd7a929fd0305ab4d0144a078f8f346dfeb9421d929ac128/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5048502d382e322b2d3737374242343f7374796c653d666f722d7468652d6261646765266c6f676f3d706870)](https://camo.githubusercontent.com/233addbd8bc6c491cd7a929fd0305ab4d0144a078f8f346dfeb9421d929ac128/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5048502d382e322b2d3737374242343f7374796c653d666f722d7468652d6261646765266c6f676f3d706870)[![Tests](https://camo.githubusercontent.com/578055875c86317a4da2b49716336758090bdaf27ef1cfdeca24c2a6a548e9ef/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f74657374732d323525323070617373696e672d737563636573733f7374796c653d666f722d7468652d6261646765)](https://camo.githubusercontent.com/578055875c86317a4da2b49716336758090bdaf27ef1cfdeca24c2a6a548e9ef/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f74657374732d323525323070617373696e672d737563636573733f7374796c653d666f722d7468652d6261646765)[![License](https://camo.githubusercontent.com/7b032738aeb4a8dd7ec78c4402e078ca8c196b0e528c117bbd78088c9a036988/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d4d49542d626c75653f7374796c653d666f722d7468652d6261646765)](https://camo.githubusercontent.com/7b032738aeb4a8dd7ec78c4402e078ca8c196b0e528c117bbd78088c9a036988/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d4d49542d626c75653f7374796c653d666f722d7468652d6261646765)

---

Why This Library Exists
-----------------------

[](#why-this-library-exists)

Most QRIS-related repositories only do one thing:

- Inject amount using regex
- Replace strings blindly
- Ignore TLV structure
- Ignore CRC validation
- Fail on nested QRIS tags

This library was built differently.

`qris-dynamic` understands the actual QRIS payload structure using a recursive TLV parser, CRC16 validation, payload abstraction, and dynamic QRIS generation.

---

What is QRIS Internally?
========================

[](#what-is-qris-internally)

QRIS payload is basically:

```
TAG + LENGTH + VALUE
```

Example:

```
540510000
```

Can be read as:

PartMeaning`54`Transaction Amount`05`Length`10000`ValueSo mathematically:

```
TLV = T + L + V
```

Where:

```
T = Tag
L = Length
V = Value
```

---

QRIS Anatomy
============

[](#qris-anatomy)

Example QRIS payload:

```
00020101021126630016COM.EXAMPLE.PSP011212345678905204481253033605802ID5915SAMPLE MERCHANT6013MERCHANT CITY6304ABCD
```

---

Visual Breakdown
----------------

[](#visual-breakdown)

 ```
graph TD

QRIS[QRIS Payload]

QRIS --> T00[00 - Payload Format]
QRIS --> T01[01 - QR Type]
QRIS --> T26[26 - Merchant Info]
QRIS --> T52[52 - MCC]
QRIS --> T53[53 - Currency]
QRIS --> T54[54 - Amount]
QRIS --> T58[58 - Country]
QRIS --> T59[59 - Merchant Name]
QRIS --> T60[60 - City]
QRIS --> T63[63 - CRC]
```

      Loading ---

Important QRIS Tags
-------------------

[](#important-qris-tags)

TagMeaningExample`00`Payload Format Indicator`01``01`QR Type`11` = Static`26`Merchant Account InfoPayment Provider`52`Merchant Category Code`4812``53`Currency`360` = IDR`54`Transaction Amount`10000``58`Country Code`ID``59`Merchant Name`SAMPLE MERCHANT``60`Merchant City`MERCHANT CITY``63`CRC16 Checksum`ABCD`---

Static vs Dynamic QRIS
======================

[](#static-vs-dynamic-qris)

Static QRIS
-----------

[](#static-qris)

Static QRIS usually does not include tag `54` (transaction amount).

The customer enters the amount manually during payment.

---

Dynamic QRIS
------------

[](#dynamic-qris)

Dynamic QRIS contains transaction amount.

Example:

```
540510000
```

Can be interpreted as:

PartMeaning`54`Amount Tag`05`Length`10000`AmountThis library injects the amount automatically and recalculates CRC16 safely.

---

CRC16 Visualization
===================

[](#crc16-visualization)

CRC is the integrity protection layer of QRIS.

If payload changes:

```
Merchant Name
Amount
City
Any Tag
```

then CRC becomes invalid.

---

Simplified CRC Flow
-------------------

[](#simplified-crc-flow)

 ```
flowchart LR

A[Raw TLV Payload] --> B[CRC16 Calculation]
B --> C[Hex Checksum]
C --> D[Append to Tag 63]
```

      Loading ---

Nested Merchant Information
===========================

[](#nested-merchant-information)

Some QRIS tags contain nested TLV values.

Example:

```
26
 ├── 00 → COM.EXAMPLE.PSP
 ├── 01 → Merchant ID
 ├── 02 → Merchant Criteria
 └── 03 → UMI
```

This library recursively parses nested structures automatically.

---

TLV Structure Visualization
===========================

[](#tlv-structure-visualization)

 ```
graph TD

A[54] --> B[05]
B --> C[10000]

A:::tag
B:::length
C:::value

classDef tag fill:#ffb703,color:#000;
classDef length fill:#8ecae6,color:#000;
classDef value fill:#90be6d,color:#000;
```

      Loading ---

How Parsing Works
=================

[](#how-parsing-works)

QRIS parsing is essentially a sequential string slicing process.

The parser reads:

```
TAG → LENGTH → VALUE
```

repeatedly until the payload ends.

---

Example
-------

[](#example)

Payload:

```
540510000
```

Parser reads it like this:

 ```
flowchart LR

A["54"] --> B["05"]
B --> C["10000"]

A:::tag
B:::length
C:::value

classDef tag fill:#ffb703,color:#000;
classDef length fill:#8ecae6,color:#000;
classDef value fill:#90be6d,color:#000;
```

      Loading ---

Mathematical Interpretation
---------------------------

[](#mathematical-interpretation)

If:

```
T = Tag
L = Length
V = Value
```

then:

```
TLV = T + L + V
```

and:

```
Length(V) = L
```

Example:

```
54 05 10000
```

means:

```
Length("10000") = 5
```

which is valid.

---

Sequential Parsing Algorithm
----------------------------

[](#sequential-parsing-algorithm)

The parser works using offsets.

Pseudo flow:

```
1. Read 2 characters  → TAG
2. Read next 2 chars  → LENGTH
3. Read next N chars  → VALUE
4. Move offset
5. Repeat
```

---

Offset Visualization
--------------------

[](#offset-visualization)

 ```
flowchart LR

A["Read TagOffset 0"]
--> B["Read LengthOffset 2"]
--> C["Read ValueOffset 4"]
--> D["Move Offset"]
```

      Loading ---

Parser Complexity
-----------------

[](#parser-complexity)

The TLV parser operates in linear time complexity.

```
Time Complexity: O(n)
Space Complexity: O(n) worst case
```

because the payload is scanned sequentially using offsets.

---

Nested TLV Parsing
------------------

[](#nested-tlv-parsing)

Some QRIS tags contain another TLV structure internally.

Example:

```
26
 ├── 00 → Provider
 ├── 01 → Merchant ID
 └── 03 → Category
```

This means the parser must recursively parse child payloads.

---

Recursive Parsing Visualization
-------------------------------

[](#recursive-parsing-visualization)

 ```
graph TD

A[Root Payload]
A --> B[Tag 26]
B --> C[Subtag 00]
B --> D[Subtag 01]
B --> E[Subtag 03]
```

      Loading ---

Internal Parsing Flow
=====================

[](#internal-parsing-flow)

 ```
flowchart TD

A[QR Image]
--> B[Decode Image]
--> C[Extract Payload]
--> D[Parse TLV]
--> E[Build Payload Object]
--> F[Modify Tags]
--> G[Rebuild TLV]
--> H[Generate CRC16]
--> I[Dynamic QRIS]
```

      Loading ---

Why Recursive Parsing Matters
=============================

[](#why-recursive-parsing-matters)

Many QRIS implementations fail because merchant account information itself contains nested TLV payloads.

Naive parsers usually:

- split incorrectly
- fail on nested tags
- break CRC generation
- generate invalid QRIS payloads

This library recursively parses nested structures safely before rebuilding the payload and recalculating CRC16.

---

Features
========

[](#features)

- Decode QRIS image
- Parse TLV payloads
- Recursive nested TLV parser
- Generate dynamic QRIS
- CRC16 generation &amp; validation
- Fluent API
- Payload abstraction
- QRIS validation
- Exception hierarchy
- PHPUnit tested

---

Installation
============

[](#installation)

```
composer require ahr-ahr/qris-dynamic
```

---

Basic Usage
===========

[](#basic-usage)

```
use AhrAhr\QRIS\QRIS;

$qris = QRIS::fromImage(
    'images/sample-qris.png'
);

$payload = $qris
    ->amount(10000)
    ->generate();

echo $payload;
```

---

Generate Dynamic QRIS
=====================

[](#generate-dynamic-qris)

```
