API QRIS Parser: Cara Baca dan Validasi QRIS Toko dari Gambar Sebelum Dicetak
Oleh Tim APIPedia, Tim Engineering APIPedia · Terbit · Diperbarui · 10 menit baca
API QRIS Parser APIPedia membaca QRIS dari gambar, mengurai nama merchant, NMID, dan nominal, lalu memeriksa CRC lewat dua endpoint POST.

API QRIS Parser APIPedia mengurai string QRIS menjadi nama merchant, kota, MCC, NMID, dan nominal, lalu menghitung ulang CRC untuk memastikan payload-nya utuh. Anda mengirim payload ke POST /v1/qris/parse, atau mengirim gambar QR ke POST /v1/qr/decode dan menerima hasil urai QRIS di respons yang sama.
Tutorial ini memakai kasus nyata: merchant mengunggah gambar QRIS statis ke aplikasi kasir atau marketplace Anda, lalu backend memeriksanya sebelum gambar itu dicetak menjadi stiker atau ditampilkan di halaman pembayaran. Semua contoh memakai kunci apk_xxx dan data merchant fiktif. Satu hal perlu jelas sejak awal: pemeriksaan ini berjalan offline atas struktur payload, bukan verifikasi bahwa merchant terdaftar.
Ringkasan cepat: apa yang perlu Anda tahu?
Alur ini memakai dua endpoint POST dari dua produk di katalog: QR Code Generator untuk membaca gambar dan QRIS Parser untuk mengurai payload. Tabel berikut merangkum intinya.
| Item | Detail |
|---|---|
| Baca gambar QR | POST https://api.apipedia.id/v1/qr/decode (PNG, JPEG, WebP, GIF, BMP; maks. 8 MB) |
| Urai payload | POST https://api.apipedia.id/v1/qris/parse dengan body {"payload": "000201..."} |
| Autentikasi | Header Authorization: Bearer apk_xxx |
| Yang diurai | Nama, kota, kode pos, negara, MCC, NMID, kriteria merchant, mata uang, nominal, data tambahan tag 62 |
| Pemeriksaan keutuhan | CRC-16/CCITT-FALSE dihitung ulang, hasilnya di crc_valid |
| Jenis QRIS | metode.keterangan: statis (kode 11) atau dinamis (kode 12) |
| Yang tidak dicek | Apakah merchant terdaftar atau aktif di penyelenggara jasa pembayaran |
| Tier | Free pada katalog, versi QRIS Parser 1.0.0 |
Masalah apa yang diselesaikan API QRIS Parser?
API ini menangkap gambar QRIS yang rusak, salah jenis, atau bukan milik merchant yang bersangkutan sebelum gambar itu dipakai menerima pembayaran. Tanpa pemeriksaan, tim operasional baru tahu ada masalah ketika pembeli gagal memindai atau uang masuk ke merchant lain.
Tiga kesalahan paling sering muncul saat merchant mengunggah QRIS sendiri:
- Gambar bukan QRIS. Yang diunggah ternyata QR tautan toko atau QR kontak.
- QRIS dinamis dijadikan stiker. QRIS dinamis dibuat untuk satu tagihan dengan nominal tertentu, jadi tidak cocok dipajang permanen.
- Nama merchant tidak cocok. QRIS milik toko lain terunggah, sengaja atau tidak.
Dengan dua request, backend Anda bisa menolak ketiganya secara otomatis dan menyimpan data merchant yang sudah terstruktur.
Bagaimana struktur payload QRIS dibaca?
Payload QRIS adalah deretan potongan TLV (tag, length, value) mengikuti standar EMVCo: dua digit tag, dua digit panjang, lalu nilainya. Contohnya 5921TOKO CONTOH SEJAHTERA berarti tag 59 (nama merchant), panjang 21 karakter, dengan nilai "TOKO CONTOH SEJAHTERA".

Tag terakhir, 63, berisi CRC-16/CCITT-FALSE yang dihitung dari seluruh payload sebelumnya. Bila satu karakter saja berubah, CRC yang tertera tidak lagi sama dengan CRC hasil hitung ulang. API melaporkan keduanya lewat crc_tertera dan crc_dihitung.
Apa yang perlu disiapkan sebelum mulai?
Anda hanya butuh satu kunci API APIPedia. Daftar atau masuk di console.apipedia.id, buka menu Token API, lalu buat kunci baru. Kunci berawalan apk_ dan dikirim lewat header Authorization: Bearer apk_xxx.
Simpan kunci di variabel lingkungan server, misalnya APIPEDIA_KEY, dan panggil API dari backend, bukan dari browser.
Langkah 1: bagaimana mengurai payload QRIS dengan curl?
Kirim string QRIS lengkap, yang diawali 000201, sebagai field payload ke POST /v1/qris/parse. Payload di bawah adalah QRIS dinamis fiktif senilai Rp 393.000.
curl -s -X POST https://api.apipedia.id/v1/qris/parse \
-H "Authorization: Bearer apk_xxx" \
-H "Content-Type: application/json" \
-d '{"payload": "00020101021226660016ID.CO.CONTOH.WWW01189360000000000000010213CONTOH00000010303UMI51440014ID.CO.QRIS.WWW0215ID10200000000010303UMI52045411530336054063930005802ID5921TOKO CONTOH SEJAHTERA6007BANDUNG61054011162220111INV202600420703K016304A7D0"}'
Seperti apa contoh respons API QRIS Parser?
Respons memakai amplop standar APIPedia { is_success, message, data }, dengan ringkasan merchant dan transaksi di bagian atas dan daftar tag mentah di tags. Berikut respons nyata untuk payload di atas, dengan isi tags dipersingkat:
{
"is_success": true,
"message": "Payload QRIS berhasil diuraikan",
"data": {
"valid_tlv": true,
"crc_valid": true,
"crc_tertera": "A7D0",
"crc_dihitung": "A7D0",
"payload_format": "01",
"metode": { "kode": "12", "keterangan": "dinamis" },
"merchant": {
"nama": "TOKO CONTOH SEJAHTERA",
"kota": "BANDUNG",
"kode_pos": "40111",
"negara": "ID",
"mcc": "5411",
"nmid": "CONTOH0000001",
"kriteria": "UMI"
},
"transaksi": { "mata_uang": "360", "nominal": 393000 },
"tags": [
{ "tag": "00", "nama": "Payload Format Indicator", "panjang": 2, "nilai": "01" },
{ "tag": "62", "nama": "Additional Data Field Template", "panjang": 22,
"nilai": "0111INV202600420703K01",
"sub": [
{ "tag": "01", "nama": "Bill Number", "panjang": 11, "nilai": "INV20260042" },
{ "tag": "07", "nama": "Terminal Label", "panjang": 3, "nilai": "K01" }
] },
{ "tag": "63", "nama": "CRC", "panjang": 4, "nilai": "A7D0" }
],
"catatan": "Hasil ini adalah analisis struktur payload secara offline, bukan verifikasi merchant ke ASPI atau penyelenggara jasa pembayaran. crc_valid hanya menandakan payload utuh, bukan bahwa merchant terdaftar atau aktif."
}
}
| Field | Arti |
|---|---|
valid_tlv | true bila seluruh payload bisa dibaca sebagai TLV. |
crc_valid | true bila crc_tertera sama dengan crc_dihitung. |
metode | Kode 11 berarti statis, kode 12 berarti dinamis. |
merchant | Nama, kota, kode pos, negara, MCC, NMID, dan kriteria merchant. |
transaksi.nominal | Angka nominal pada QRIS dinamis, atau null pada QRIS statis. |
tags | Semua tag mentah beserta sub-TLV, berguna untuk audit atau debugging. |
Langkah 2: bagaimana membaca QRIS langsung dari gambar?
Kirim gambar sebagai base64 ke POST /v1/qr/decode, dan Anda tidak perlu memanggil parser secara terpisah. Bila isi QR dikenali sebagai QRIS, endpoint ini langsung menyertakan hasil urainya di symbols[].parsed dengan type: "qris".
jq -n --arg img "$(base64 -w0 qris-toko.png)" '{image_base64: $img}' \
| curl -s -X POST https://api.apipedia.id/v1/qr/decode \
-H "Authorization: Bearer apk_xxx" \
-H "Content-Type: application/json" \
-d @-
Kami menguji endpoint ini dengan gambar QRIS statis fiktif berukuran 160 piksel. Respons nyatanya, dipersingkat:
{
"is_success": true,
"message": "1 barcode berhasil dibaca",
"data": {
"count": 1,
"symbols": [
{
"format": "QRCode",
"text": "00020101021126660016ID.CO.CONTOH.WWW...5921TOKO CONTOH SEJAHTERA6007BANDUNG610540111630410F7",
"encoding": "ascii",
"parsed": {
"type": "qris",
"valid_tlv": true,
"crc_valid": true,
"metode": { "kode": "11", "keterangan": "statis" },
"merchant": { "nama": "TOKO CONTOH SEJAHTERA", "kota": "BANDUNG", "mcc": "5411", "nmid": "CONTOH0000001" },
"transaksi": { "mata_uang": "360", "nominal": null }
}
}
],
"image": { "mime": "image/png", "bytes": 2206 }
}
}
Gambar tanpa QR bukan error. Responsnya tetap sukses dengan count: 0, jadi periksa count sebelum membaca symbols[0]. Nama field body bisa dicek ulang di spesifikasi OpenAPI produk QR Code Generator pada konsol.
Langkah 3: bagaimana memasangnya di backend Node.js?
Buat satu fungsi yang menerima berkas gambar dan nama merchant terdaftar, lalu mengembalikan keputusan terima atau tolak. Contoh ini berjalan di Node.js 18 ke atas tanpa dependensi tambahan.
import { readFile } from "node:fs/promises";
const BASE = "https://api.apipedia.id/v1";
const headers = {
Authorization: `Bearer ${process.env.APIPEDIA_KEY}`, // apk_xxx
"Content-Type": "application/json",
};
const rapikan = (s) => String(s ?? "").toUpperCase().replace(/[^A-Z0-9]/g, "");
export async function periksaQrisToko(pathGambar, namaTerdaftar) {
const image_base64 = (await readFile(pathGambar)).toString("base64");
const res = await fetch(`${BASE}/qr/decode`, {
method: "POST",
headers,
body: JSON.stringify({ image_base64, max_symbols: 1 }),
});
const body = await res.json();
if (!body.is_success) throw new Error(`HTTP ${res.status}: ${body.message}`);
if (body.data.count === 0) return { ok: false, alasan: "Tidak ada QR pada gambar." };
const q = body.data.symbols[0].parsed;
if (q?.type !== "qris") return { ok: false, alasan: "QR terbaca, tetapi isinya bukan QRIS." };
if (!q.crc_valid) return { ok: false, alasan: "Payload QRIS tidak utuh (CRC tidak cocok)." };
if (q.metode.keterangan !== "statis") {
return { ok: false, alasan: "Ini QRIS dinamis untuk satu tagihan. Unggah QRIS statis." };
}
if (rapikan(q.merchant.nama) !== rapikan(namaTerdaftar)) {
return { ok: false, alasan: `Nama di QRIS "${q.merchant.nama}" berbeda dari data pendaftaran.` };
}
// Simpan data terstruktur dan payload aslinya, bukan hanya gambarnya.
return {
ok: true,
merchant: q.merchant,
payload: body.data.symbols[0].text,
};
}
console.log(await periksaQrisToko("qris-toko.png", "Toko Contoh Sejahtera"));
Fungsi ini memeriksa lima hal secara berurutan: ada QR, isinya QRIS, CRC cocok, jenisnya statis, dan namanya sama dengan data pendaftaran. Perbandingan nama mengabaikan huruf besar-kecil, spasi, dan tanda baca. Untuk nama yang disingkat di QRIS, arahkan ke tinjauan manual, jangan langsung ditolak.
Langkah 4: bagaimana mengurai payload di Python?
Di Python, kirim payload dengan requests ke /qris/parse lalu baca crc_valid dan metode. Pola ini cocok bila payload sudah Anda dapat dari pemindai di aplikasi mobile.
import os
import requests # pip install requests
BASE = "https://api.apipedia.id/v1"
HEADERS = {"Authorization": f"Bearer {os.environ['APIPEDIA_KEY']}"} # apk_xxx
def urai_qris(payload):
res = requests.post(f"{BASE}/qris/parse", headers=HEADERS, json={"payload": payload}, timeout=10)
body = res.json()
if res.status_code == 400:
return {"ok": False, "alasan": body.get("message")}
if not body.get("is_success"):
raise RuntimeError(f"HTTP {res.status_code}: {body.get('message')}")
data = body["data"]
if not data["crc_valid"]:
return {"ok": False, "alasan": "CRC tidak cocok, payload tidak utuh."}
return {
"ok": True,
"nama": data["merchant"]["nama"],
"nmid": data["merchant"]["nmid"],
"jenis": data["metode"]["keterangan"],
"nominal": data["transaksi"]["nominal"],
}
if __name__ == "__main__":
contoh = (
"00020101021126660016ID.CO.CONTOH.WWW01189360000000000000010213CONTOH0000001"
"0303UMI51440014ID.CO.QRIS.WWW0215ID10200000000010303UMI5204541153033605802ID"
"5921TOKO CONTOH SEJAHTERA6007BANDUNG610540111630410F7"
)
print(urai_qris(contoh))
print(urai_qris("https://contoh.co.id/bayar"))
Seperti apa respons untuk QRIS yang rusak atau bukan QRIS?
Payload yang diubah tetap dijawab status 200 dengan crc_valid: false, sedangkan teks yang bukan TLV dijawab 400. Kami menguji empat masukan terhadap POST /v1/qris/parse pada 12 November 2026.

| Masukan | Status | crc_valid | Catatan |
|---|---|---|---|
| QRIS statis utuh | 200 | true | metode statis, nominal null |
| QRIS dinamis | 200 | true | metode dinamis, nominal 393000 |
| Satu huruf nama diubah | 200 | false | crc_tertera 10F7, crc_dihitung DE2B |
| URL biasa | 400 | - | Bukan TLV EMVCo |
Respons 400 untuk masukan yang bukan QRIS berbentuk seperti ini:
{
"is_success": false,
"message": "Payload QRIS bukan TLV EMVCo yang sah: tag \"ht\" atau panjang \"tp\" pada posisi 0 bukan dua digit angka.",
"data": null
}
Apa batas pemeriksaan ini?
Pemeriksaan ini hanya menjawab "apakah payload-nya utuh dan apa isinya", bukan "apakah merchant ini terdaftar dan aktif". Seluruh pekerjaan dilakukan offline dan tidak memanggil pihak mana pun, termasuk ASPI atau penyelenggara jasa pembayaran.
Artinya, crc_valid: true tidak membuktikan keaslian. Siapa pun yang menyusun payload dengan benar bisa menghitung CRC yang cocok. Karena itu, pakai hasilnya sebagai saringan pertama, cocokkan nama dan NMID dengan dokumen pendaftaran merchant, dan lakukan transaksi uji bernominal kecil sebelum QRIS dipakai luas.
Kesalahan umum apa yang perlu dihindari?
Kesalahan paling sering adalah menganggap is_success: true sebagai tanda QRIS lolos. Periksa juga hal-hal berikut.
- Hanya membaca
is_success. Nilainyatruewalau CRC tidak cocok. Keputusan ada dicrc_valid. - Menganggap CRC cocok berarti merchant asli. CRC hanya menandakan payload utuh.
- Tidak membedakan statis dan dinamis. Baca
metode.keterangansebelum menjadikan QRIS sebagai stiker tetap. - Mengirim payload yang terpotong. Spasi di nama merchant adalah bagian dari payload. Jangan di-trim atau diubah hurufnya.
- Tidak mengecek
count. Gambar tanpa QR dijawab sukses dengancount: 0. - Mengunggah foto buram. Minta gambar asli dari aplikasi merchant, bukan foto stiker dari jauh.
- Memanggil dari browser. Kunci
apk_xxxakan terbaca siapa pun. Panggil dari backend.
Kode status yang perlu ditangani
| Status | Arti | Tindakan |
|---|---|---|
| 200 | Payload berhasil diurai | Baca crc_valid dan metode |
| 400 | Payload bukan TLV EMVCo yang sah | Tampilkan pesan "ini bukan QRIS" ke pengguna |
| 401 | Token tidak ada, tidak dikenal, atau kedaluwarsa | Periksa header Authorization |
Pertanyaan yang sering diajukan
Apakah API ini bisa memastikan merchant QRIS terdaftar?
Tidak. API ini menganalisis struktur payload secara offline. Ia tidak menghubungi ASPI atau penyelenggara jasa pembayaran, sehingga tidak tahu apakah merchant terdaftar atau aktif.
Apa bedanya QRIS statis dan dinamis di respons?
QRIS statis memiliki metode.kode 11 dan transaksi.nominal bernilai null, karena pembeli mengetik nominalnya sendiri. QRIS dinamis memiliki kode 12 dan biasanya membawa nominal serta nomor tagihan di tag 62.
Apa arti crc_valid: false?
Artinya CRC yang tertera di tag 63 berbeda dari hasil hitung ulang atas payload. Payload itu terpotong, salah salin, atau sudah diubah. Minta merchant mengunggah ulang gambar aslinya.
Apakah saya harus memanggil dua endpoint untuk gambar QRIS?
Tidak. POST /v1/qr/decode sudah mengembalikan hasil urai QRIS di symbols[].parsed. Endpoint /v1/qris/parse dipakai bila Anda sudah memegang string payload-nya.
Bisakah asisten AI memeriksa QRIS lewat API ini?
Bisa. Server MCP APIPedia menyediakan tool parse_qris dan qr_decode yang memanggil endpoint yang sama. Cara menyambungkannya ada di panduan menghubungkan APIPedia ke Claude lewat MCP.
Langkah berikutnya: coba di konsol
Pemeriksaan QRIS toko cukup butuh satu request POST /v1/qr/decode dan lima pengecekan di backend. Buat kunci di console.apipedia.id, jalankan contoh curl di atas dengan gambar QRIS Anda sendiri, lalu pasang fungsi periksaQrisToko di handler unggah.
Untuk melengkapi alur pendaftaran merchant, baca API OCR KTP untuk membaca e-KTP dan memeriksa NIK. Setelah pembayaran masuk, kirim buktinya dengan invoice PDF otomatis dari template HTML.
Topik: #qris parser #qris #qr code #tutorial #nodejs #python
Tentang penulis
Tim APIPedia — Tim Engineering APIPedia
Tim engineering di balik APIPedia yang membangun dan memelihara API data Indonesia - ongkir, OCR KTP, data wilayah, BMKG, hingga server MCP - dan menulis panduan integrasinya dari pengalaman langsung.