← Blog
Artikel

QRIS Parser: Cara Parse String QRIS ke Objek (Merchant, Nominal, Tips)

Panduan parse string QRIS (EMVCo TLV) ke objek: nama merchant, nominal, kota, dan konversi QRIS statis ke dinamis dengan CRC yang dihitung otomatis.

String QRIS itu terlihat seperti deretan angka acak, tapi sebenarnya struktur yang rapi (format EMVCo TLV). Kalau Anda bikin aplikasi POS, dashboard pembayaran, atau sistem rekonsiliasi, biasanya Anda perlu mengurai string itu: siapa merchant-nya, berapa nominalnya, apakah ada tips. Di sinilah QRIS parser berguna, dan API Indonesia menyediakan dua endpoint untuk kebutuhan itu.

Anatomi String QRIS

QRIS memakai format EMVCo: setiap field terdiri dari ID dua digit, panjang dua digit, lalu nilai sepanjang itu. Contoh tag yang paling sering dibutuhkan:

Tag Isi
00 Payload Format Indicator
01 Point of Initiation (11 = statis, 12 = dinamis)
26 Merchant Account Information (berisi NMID, nama merchant, kriteria)
53 Currency (360 = IDR)
54 Amount / nominal transaksi
58 Country code
59 Merchant name
60 Merchant city
63 CRC (checksum)

Mau "qris to string" sebaliknya, dari data ke string yang valid dengan CRC benar? Itu pekerjaan yang rawan salah kalau dihitung manual, karena CRC-16/CCITT harus dihitung ulang setiap kali payload berubah. Endpoint kedua (/qris/to-dynamic) mengurus itu.

Endpoint

Base URL: https://use.apiindonesia.id/api/v1/util/qris

Method Endpoint Keterangan
POST /parse Dekode payload QRIS jadi objek terstruktur
POST /to-dynamic Konversi QRIS statis jadi dinamis dengan nominal tertentu

1. Parse String QRIS

Kita pakai contoh payload statis (angka saja, sudah dengan CRC valid):

curl -X POST "https://use.apiindonesia.id/api/v1/util/qris/parse" \
  -H "x-api-key: aip_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{"payload":"00020101021126520015ID10203456789010107UMI57730211TOKO MAKMUR0303UMI5204411153033605405500005802ID5911TOKO MAKMUR6015JAKARTA SELATAN6105121906304D99D"}'

Responsnya:

{
  "data": {
    "valid": true,
    "fields": [
      { "id": "00", "length": 2, "value": "01" },
      { "id": "01", "length": 2, "value": "11" },
      { "id": "26", "length": 52, "value": "0015ID10203456789010107UMI57730211TOKO MAKMUR0303UMI" },
      { "id": "52", "length": 4, "value": "4111" },
      { "id": "53", "length": 3, "value": "360" },
      { "id": "54", "length": 5, "value": "50000" },
      { "id": "58", "length": 2, "value": "ID" },
      { "id": "59", "length": 11, "value": "TOKO MAKMUR" },
      { "id": "60", "length": 15, "value": "JAKARTA SELATAN" },
      { "id": "61", "length": 5, "value": "12190" },
      { "id": "63", "length": 4, "value": "D99D" }
    ],
    "merchant_name": "TOKO MAKMUR",
    "merchant_city": "JAKARTA SELATAN",
    "amount": "50000"
  }
}

Beberapa hal yang diperiksa parser ini sebelum menyatakan valid:

  • Payload hanya boleh berisi digit.
  • Harus diawali 000201 (penanda QRIS EMVCo).
  • Field 63 (CRC) harus ada dan CRC-16/CCITT-nya harus cocok dengan perhitungan ulang.

Kalau CRC tidak cocok, Anda dapat error CRC tidak valid. Ini berguna untuk mendeteksi payload yang diketik ulang manual atau terpotong.

2. QRIS Statis ke Dinamis (Sisipkan Nominal)

QRIS statis tidak membawa nominal, kasir lalu menghitung sendiri saat konsumen scan. Untuk menampilkan nominal tetap (misalnya checkout e-commerce), konversikan statis ke dinamis dengan tag 54 diisi dan point of initiation berubah jadi 12:

curl -X POST "https://use.apiindonesia.id/api/v1/util/qris/to-dynamic" \
  -H "x-api-key: aip_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{"payload":"00020101021126520015ID10203456789010107UMI57730211TOKO MAKMUR0303UMI5204411153033605405500005802ID5911TOKO MAKMUR6015JAKARTA SELATAN6105121906304D99D","amount":75000}'
{
  "data": { "payload": "00020101021226520015ID10203456789010107UMI57730211TOKO MAKMUR0303UMI5204411153033605405750005802ID5911TOKO MAKMUR6015JAKARTA SELATAN6105121906304E768" }
}

Perhatikan: tag 01 berubah dari 11 ke 12, tag 54 sekarang 75000, dan CRC dihitung ulang otomatis. Payload ini siap di-render jadi QR image. Untuk biaya layanan (tips/service fee), tambahkan field fee dan parser akan menyisipkan tag 55 (indicator) dan 56 (nilai fee).

Contoh Integrasi: Rekonsiliasi Pembayaran di Node.js

const BASE = "https://use.apiindonesia.id/api/v1/util/qris";

export async function qrisDetail(payload) {
  const res = await fetch(`${BASE}/parse`, {
    method: "POST",
    headers: {
      "x-api-key": process.env.API_INDONESIA_KEY,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ payload }),
  });
  const body = await res.json();
  if (!res.ok) {
    throw new Error(body.error?.message ?? "Gagal parse QRIS");
  }
  return {
    merchant: body.data.merchant_name,
    city: body.data.merchant_city,
    amount: body.data.amount ? Number(body.data.amount) : null,
  };
}

const detail = await qrisDetail(rawPayload);
console.log(`${detail.merchant} (${detail.city}): Rp${detail.amount ?? "statis"}`);

Versi Python: Batch Parse untuk Rekonsiliasi

import os
import requests

BASE = "https://use.apiindonesia.id/api/v1/util/qris"
HEADERS = {"x-api-key": os.environ["API_INDONESIA_KEY"], "Content-Type": "application/json"}

def parse_many(payloads: list[str]):
    out = []
    for p in payloads:
        r = requests.post(f"{BASE}/parse", headers=HEADERS, json={"payload": p})
        if r.ok:
            d = r.json()["data"]
            out.append({
                "merchant": d["merchant_name"],
                "amount": int(d["amount"]) if d.get("amount") else 0,
                "crc_ok": True,
            })
        else:
            out.append({"merchant": None, "crc_ok": False, "error": r.json()["error"]["message"]})
    return out

Tips Praktis

  • Simpan hasil parse, bukan string mentahnya saja. Objek hasil parse jauh lebih gampang di-query saat audit.
  • Selalu cek CRC untuk payload dari sumber tak terpercaya. Payload yang diedit manual hampir pasti gagal CRC.
  • Nominal dari tag 54 itu string. Konversi ke integer sebelum dihitung, dan hati-hati dengan format desimal.
  • Bedakan statis vs dinamis dari tag 01. 11 berarti statis (nominal bebas), 12 berarti dinamis (nominal terkunci).
  • Validasi nominal sebelum generate QR dinamis. Pastikan amount sudah melewati validasi format Rupiah di aplikasi Anda, atau pakai endpoint utilitas format Rupiah di API Utilities yang sama.

Mulai Sekarang

Coba parser QRIS langsung di halaman API Utilities dan dapatkan API key gratis di dashboard.apiindonesia.id/register.

Mulai Sekarang

Mulai Integrasi
Hari Ini

Daftar gratis, ambil API key, dan langsung call endpoint pertama Anda — tanpa kartu kredit, tanpa proses approval yang ribet.

Visualisasi gelombang titik