Matematika di Balik QRIS: Kenapa QR Rusak Tetap Terbaca
Kenapa QRIS yang tergores tetap terbaca? Bedah matematika QR code: koreksi galat Reed-Solomon, level L-M-Q-H, dan checksum CRC-16 lengkap dengan Python.
- Mapel
- Pemrograman
- Jenjang
- SMA, Kuliah
- Waktu baca
- 9 menit
- Terbit
- 31 Oktober 2026
- Penyusun
- Risyad Raflan
Stiker QRIS di warung sering sudah kusam, tergores, atau terlipat, tetapi kamera ponsel tetap bisa membacanya. Rahasianya adalah matematika: setiap QR code menyimpan data cadangan hasil perhitungan Reed–Solomon, sehingga bagian yang rusak bisa "dihitung ulang" dari bagian yang masih utuh. Di atas itu, standar pembayaran yang dipakai QRIS menambahkan satu lapis pengaman lagi berupa checksum CRC untuk memastikan isi pesan tidak berubah. Artikel ini membedah keduanya, lengkap dengan contoh hitungan dan kode Python yang bisa kamu jalankan sendiri.
Dari Pabrik Mobil ke Warung Kopi
QR code (quick response code) diciptakan pada 1994 oleh Masahiro Hara dari perusahaan Jepang Denso Wave untuk menandai komponen mobil. Label kertas di pabrik sering terkena noda oli dan gesekan, sehingga sejak awal kode ini dirancang agar tetap terbaca walaupun sebagian permukaannya rusak.
Di Indonesia, QR code menjadi alat bayar sehari-hari lewat QRIS (Quick Response Code Indonesian Standard, dibaca "kris"). Menurut Bank Indonesia, QRIS diluncurkan pada 17 Agustus 2019 dan wajib dipakai mulai 31 Desember 2019. Sebelum ada QRIS, aplikasi pembayaran hanya bisa membayar ke merchant yang memakai penyedia jasa pembayaran yang sama. Kini satu kode QRIS bisa dipindai oleh aplikasi dari penyedia mana pun. Bank Indonesia juga menyebut bahwa QRIS disusun memakai standar internasional EMVCo, standar yang sama yang dipakai di sejumlah negara lain.
Jadi, ketika kamu memindai QRIS, ada dua lapis matematika yang bekerja:
| Lapisan | Tugas | Alat matematikanya |
|---|---|---|
| Gambar QR code | Memulihkan modul (kotak kecil) yang rusak atau tertutup | Kode Reed–Solomon |
| Isi pesan pembayaran | Mendeteksi bila isi pesan berubah | Checksum CRC-16 |
Anatomi QR Code: Modul, Versi, dan Pola Penanda
QR code adalah kisi persegi berisi modul hitam (bit 1) dan putih (bit 0). Ukurannya ditentukan oleh versi , dari 1 sampai 40. Panjang sisi QR code dalam modul adalah
Jadi versi 1 berukuran modul dan versi 40 berukuran modul.
Tidak semua modul berisi data. Sebagian dipakai sebagai pola penanda. Perhatikan gambar di bawah: tiga kotak besar di pojok kiri atas, kanan atas, dan kiri bawah adalah pola posisi (finder pattern) yang membantu kamera menentukan arah dan kemiringan kode. Garis putus-putus hitam-putih adalah pola pengatur waktu (timing), kotak kecil di tengah adalah pola penjajaran (alignment), dan area abu-abu adalah tempat data serta kunci koreksi galat disimpan. Bingkai hijau menandai zona tenang (quiet zone), yaitu tepi kosong yang harus dibiarkan polos.

Gambar: Contoh struktur QR code dengan elemen fungsional ditandai oleh Bobmath, via Wikimedia Commons, lisensi CC BY-SA 3.0 (diubah).
Empat Level Koreksi Galat: L, M, Q, dan H
Standar QR code menyediakan empat level koreksi galat. Makin tinggi levelnya, makin banyak data cadangan yang ditambahkan, sehingga kode makin tahan rusak, tetapi ruang untuk data asli makin sedikit.
| Level | Perkiraan bagian kode yang bisa dipulihkan | Kapasitas versi 1 (mode byte) | Contoh penggunaan |
|---|---|---|---|
| L (low) | sekitar 7% | 17 byte | Layar digital yang bersih |
| M (medium) | sekitar 15% | 14 byte | Pilihan paling umum |
| Q (quartile) | sekitar 25% | 11 byte | Lingkungan pabrik atau luar ruang |
| H (high) | sekitar 30% | 7 byte | Kode yang diberi logo di tengah |
Denso Wave sendiri menyebut level M (15%) sebagai level yang paling sering dipilih, sedangkan level Q atau H cocok untuk lingkungan pabrik yang membuat kode mudah kotor.
Persentase itu dihitung terhadap seluruh codeword (satu codeword = 8 bit = 1 byte), bukan hanya terhadap data asli. Denso Wave memberi contoh begini: bila ada 100 codeword data dan kita ingin bisa memperbaiki 50 di antaranya, dibutuhkan codeword Reed–Solomon, karena kode Reed–Solomon memerlukan dua kali jumlah codeword yang akan diperbaiki. Totalnya 200 codeword, sehingga tingkat pemulihannya
yang sesuai dengan level Q.
Ide Reed–Solomon: Titik Cadangan pada Sebuah Polinomial
Inti kode Reed–Solomon bisa dipahami dengan fakta sederhana dari pelajaran aljabar: sebuah garis lurus ditentukan oleh dua titik. Lebih umum, polinomial berderajat ditentukan oleh titik. Bila kita mengirim lebih banyak titik daripada yang dibutuhkan, titik tambahan itu menjadi cadangan: kalau ada titik yang rusak, titik yang lain masih "sepakat" tentang polinomial yang benar.
Pada contoh ini kita menambah dua nilai cadangan untuk memperbaiki satu kesalahan, persis aturan "dua codeword cadangan untuk satu codeword rusak" dari Denso Wave. Aturan ini berlaku bila letak kesalahan tidak diketahui. Bila letak bagian yang hilang sudah diketahui (disebut erasure), satu codeword cadangan sudah cukup untuk memulihkan satu codeword.
Bedanya dengan QR code sungguhan
QR code tidak memakai bilangan biasa, melainkan aritmetika pada medan hingga yang berisi tepat 256 elemen, satu untuk setiap kemungkinan nilai byte. Medan ini dibangun dari polinomial primitif
yang jika ditulis sebagai bilangan biner bernilai 285. Karena hasil setiap operasi tetap berupa satu byte, komputer bisa menghitungnya dengan cepat dan tanpa pembulatan.
Contoh nyatanya adalah QR code versi 1 level L. Isinya 26 codeword: 19 untuk data dan 7 untuk koreksi galat. Pada blok sekecil ini, standar ISO/IEC 18004 menyisihkan 3 dari 7 codeword cadangan khusus untuk mendeteksi galat agar kode yang rusak tidak salah terbaca. Sisanya 4 codeword bisa memperbaiki codeword yang rusak, atau
Cek: , jadi memang sekitar 2 dari 26 codeword. Angka 7,7% ini sesuai dengan label "sekitar 7%" untuk level L.
Lapisan Kedua QRIS: Checksum CRC pada Isi Pesan
Setelah gambar QR berhasil dipulihkan, yang keluar adalah sederet teks. Dalam spesifikasi EMV untuk QR pembayaran yang ditampilkan merchant (Merchant Presented Mode), teks itu tersusun dari potongan berformat ID–panjang–nilai (TLV): dua digit ID, dua digit panjang, lalu nilainya. Misalnya 5303986 berarti ID 53 (mata uang), panjang 03, nilai 986.
Potongan terakhir wajib berupa ID 63 dengan panjang 04, berisi CRC-16: angka pemeriksa 16 bit yang dihitung dari seluruh teks sebelumnya. Manual BR Code dari Bank Sentral Brasil, yang juga mengadopsi spesifikasi EMV ini, menjelaskan bahwa CRC dihitung dengan polinomial 1021 (heksadesimal) dan nilai awal FFFF.
Kode Python berikut membaca struktur TLV menjadi dictionary, lalu menghitung ulang CRC-nya. Contoh payload diambil dari manual BR Code tersebut (panjangnya 270 karakter).
def crc16_ccitt(teks):
crc = 0xFFFF # nilai awal 'FFFF'
for b in teks.encode("utf-8"):
crc ^= b << 8
for _ in range(8):
if crc & 0x8000:
crc = ((crc << 1) ^ 0x1021) & 0xFFFF # polinomial '1021'
else:
crc = (crc << 1) & 0xFFFF
return f"{crc:04X}"
def baca_tlv(teks):
data = {}
i = 0
while i < len(teks):
tag = teks[i:i+2]
panjang = int(teks[i+2:i+4])
data[tag] = teks[i+4:i+4+panjang]
i += 4 + panjang
return data
payload = ("00020104141234567890123426580014BR.GOV.BCB.PIX"
"0136123e4567-e12b-12d1-a456-426655440000"
"27300012BR.COM.OUTRO01100123456789"
"5204000053039865406123.45"
"5802BR5917NOME DO RECEBEDOR"
"6008BRASILIA61087007490062190515RP12345678-2019"
"80390012BR.COM.OUTRO01190123.ABCD.3456.WXYZ6304AD38")
data = baca_tlv(payload)
print(len(payload)) # 270
print(data["54"], data["59"]) # 123.45 NOME DO RECEBEDOR
print(crc16_ccitt(payload[:-4])) # AD38 -> sama dengan nilai ID 63
palsu = payload.replace("123.45", "923.45")
print(crc16_ccitt(palsu[:-4])) # 0B39 -> tidak cocok, pesan ditolak
Perhatikan bahwa CRC dihitung dari teks sampai dengan 6304 (ID dan panjang CRC ikut dihitung), lalu hasilnya ditempel sebagai empat karakter terakhir. Saat nominal diubah dari 123.45 menjadi 923.45, CRC berubah dari AD38 menjadi 0B39, sehingga aplikasi tahu ada yang tidak beres.
Cek: program di atas menghasilkan AD38, sama persis dengan nilai CRC yang tercantum di contoh manual BR Code, dan panjang payload 270 karakter juga sesuai dengan manual.
Penting untuk membedakan dua lapisan ini. Reed–Solomon memperbaiki kesalahan baca pada gambar. CRC hanya mendeteksi perubahan pada teks dan tidak bisa memperbaikinya. CRC juga bukan pengaman dari penipuan: siapa pun yang membuat QR palsu bisa menghitung CRC yang benar untuk pesan palsunya. Karena itu Bank Indonesia tetap menyarankan pengguna memeriksa nama merchant sebelum membayar dan memastikan notifikasi transaksi diterima.
Kenapa QR Code Berlogo atau Tergores Tetap Bisa Dipindai?
Banyak QR code diberi logo di tengah. Pemindai memperlakukan modul yang tertutup logo sebagai bagian yang rusak, lalu memulihkannya dengan kode Reed–Solomon. Itulah sebabnya kode berlogo biasanya memakai level H. Namun, logo sudah "memakan" sebagian jatah pemulihan sejak awal, sehingga kode itu jadi kurang tahan terhadap kerusakan tambahan.
Ada dua batasan yang perlu kamu ingat:
- Pola posisi harus utuh. Koreksi galat hanya melindungi area data. Bila salah satu dari tiga kotak besar di pojok rusak, pemindai bisa gagal menentukan arah kode walaupun total kerusakannya masih di bawah batas level koreksi.
- Data disebar (interleaving). Kerusakan nyata biasanya berupa satu noda atau sobekan yang berkumpul di satu tempat. QR code berukuran besar memecah pesan ke beberapa blok Reed–Solomon dan menyelang-nyeling byte-nya di seluruh kisi, sehingga satu noda hanya merusak sedikit bagian dari tiap blok.
Ringkasan
- QR code diciptakan pada 1994 oleh Masahiro Hara dari Denso Wave, dan QRIS adalah standar QR pembayaran nasional dari Bank Indonesia yang disusun dengan standar EMVCo.
- Ukuran QR code mengikuti rumus modul per sisi, dari (versi 1) sampai (versi 40).
- Kode Reed–Solomon menambahkan codeword cadangan; dibutuhkan dua codeword cadangan untuk memperbaiki satu codeword rusak yang letaknya tidak diketahui.
- Empat level koreksi galat L, M, Q, dan H masing-masing memulihkan sekitar 7%, 15%, 25%, dan 30% dari seluruh codeword.
- Isi QR pembayaran EMV diakhiri CRC-16 (polinomial
1021, nilai awalFFFF) yang mendeteksi perubahan isi pesan, tetapi tidak memperbaikinya dan tidak menggantikan kewaspadaan pengguna.
Ingin mencoba kode di atas? Pelajari dulu cara kerja dictionary di List dan Dictionary Python, lalu latih logika perulangan dan fungsi lewat 10 Contoh Program Python Sederhana. Bila baru mulai, panduan Belajar Python dari Nol bisa jadi titik awal.
Sumber belajar
- Error Correction Feature – QRcode.com (DENSO WAVE)
- Quick Response Code Indonesian Standard (QRIS) – Bank Indonesia
- QR code – Wikipedia
- BR Code Manual v2.0.0 – Banco Central do Brasil
- Reliability on QR Codes and Reed-Solomon Codes (arXiv:2407.17364)
Penjelasan dan contoh disusun ulang untuk materi ini; sumber dapat dibuka untuk belajar lebih lanjut.
