Cara Mengubah QRIS Statis Menjadi Dinamis dengan Nominal Otomatis Menggunakan Python

Pernahkah Anda memperhatikan saat memindai QRIS di kasir minimarket modern, nominal pembayarannya langsung muncul tanpa perlu diketik manual? Sedangkan QRIS yang dicetak di stiker toko Anda mengharuskan pelanggan memasukkan angka sendiri (yang rawan typo atau salah bayar).

Sebenarnya, QRIS statis (stiker) dan dinamis (mesin EDC/POS) memiliki basis teknologi yang sama. Perbedaannya hanya pada satu baris data di dalam kode tersebut yang menyimpan informasi nominal transaksi.

Dalam tutorial ini, kita akan membongkar struktur data QRIS dan menulis skrip Python untuk menyuntikkan nominal harga ke dalam kode QRIS statis Anda. Hasilnya, Anda bisa membuat tagihan unik untuk pelanggan secara otomatis.

Persiapan Tempur

Sebelum masuk ke koding, pastikan Anda memiliki perelengkapan berikut. Kita tidak membutuhkan software berbayar, cukup Python yang bersifat open source.

  1. Python 3.x: Pastikan sudah terinstal di komputer Anda.
  2. Code Editor: VS Code atau Notepad++ (pilih yang nyaman bagi Anda).
  3. Data String QRIS Statis: Anda bisa mendapatkan ini dengan men-scan kode QRIS toko Anda menggunakan aplikasi QR Scanner (bukan aplikasi bank) dan menyalin teks mentahnya. Teks biasanya diawali dengan 0002010102....
  4. Library Python: Kita butuh qrcode untuk membuat gambar dan Pillow untuk pemrosesan gambar.

Buka terminal atau CMD, lalu jalankan perintah instalasi berikut:

Bash

pip install qrcode[pil]

Memahami Logika QRIS (EMVCo)

QRIS mengadopsi standar internasional EMVCo. Datanya tersusun dalam format TLV (Tag-Length-Value).

Untuk mengubah QRIS statis menjadi dinamis, kita perlu memanipulasi dua bagian:

  1. Tag 54 (Transaction Amount): Ini adalah bagian yang memberitahu aplikasi bank berapa uang yang harus ditransfer. Pada QRIS statis, tag ini biasanya tidak ada. Kita harus menambahkannya.
  2. Tag 63 (CRC Checksum): Ini adalah 4 karakter terakhir di string QRIS yang berfungsi sebagai validasi keamanan. Jika kita mengubah satu karakter saja di dalam string (misalnya menambah nominal), kode CRC di belakang akan berubah total. Jika CRC tidak dihitung ulang, QRIS akan dianggap Invalid oleh bank.

Langkah 1: Menulis Script Generator QRIS Dinamis

Kita akan membuat satu file Python lengkap yang berfungsi untuk:

  1. Menerima string QRIS mentah milik Anda.
  2. Membersihkan checksum lama.
  3. Menyuntikkan nominal harga yang diinginkan.
  4. Menghitung ulang algoritma CRC-16 (CCITT-FALSE) agar valid.
  5. Menghasilkan gambar QR Code baru.

Buat file baru bernama bikin_qris.py dan salin kode berikut secara utuh:

Python

import qrcode
from PIL import Image

def calculate_crc16(data: str) -> str:
    """
    Menghitung Checksum CRC-16/CCITT-FALSE sesuai standar EMVCo.
    Standar QRIS menggunakan polinomial 0x1021 dengan initial value 0xFFFF.
    """
    crc = 0xFFFF
    polynomial = 0x1021
    
    # Konversi string ke bytes
    data_bytes = data.encode('utf-8')
    
    for byte in data_bytes:
        crc ^= (byte << 8)
        for _ in range(8):
            if (crc & 0x8000):
                crc = (crc << 1) ^ polynomial
            else:
                crc = (crc << 1)
    
    # Format hasil ke 4 digit hex uppercase (misal: 9A2F)
    return hex(crc & 0xFFFF)[2:].upper().zfill(4)

def inject_amount(qris_raw, amount):
    """
    Menyuntikkan Tag 54 (Amount) ke dalam string QRIS dan hitung ulang CRC.
    """
    # 1. Validasi input
    amount_str = str(amount)
    if not amount_str.isdigit():
        raise ValueError("Nominal harus berupa angka tanpa titik/koma")

    # 2. Persiapkan Tag 54
    # Format: ID (54) + Panjang Data (2 digit) + Nilai
    tag_id = "54"
    tag_len = f"{len(amount_str):02}" # Zero padding, misal panjang 5 jadi '05'
    tag_amount_full = f"{tag_id}{tag_len}{amount_str}"
    
    # 3. Bongkar QRIS Lama
    # Cari posisi Tag 63 (CRC). Biasanya ada di akhir dengan format '6304' + 4 char CRC.
    # Kita potong string tepat sebelum '6304'.
    try:
        # Mencari index dimulainya blok CRC (6304)
        # Catatan: Ini metode sederhana. Untuk produksi massal, parsing TLV lebih disarankan.
        payload_end_index = qris_raw.rfind("6304")
        if payload_end_index == -1:
             raise ValueError("Format QRIS tidak valid: Tag CRC 6304 tidak ditemukan.")
             
        payload_without_crc = qris_raw[:payload_end_index]
        
    except Exception as e:
        print(f"Error parsing: {e}")
        return None

    # 4. Hapus Tag 54 lama jika ada (untuk menghindari duplikasi)
    # Ini langkah preventif jika QRIS sumber sebenarnya sudah dinamis
    # Namun untuk tutorial ini, kita asumsikan menyisipkan baru di akhir payload sebelum CRC
    # karena standar EMVCo memperbolehkan urutan tag tertentu.
    
    # Gabungkan: Payload Awal + Tag 54 Baru + Tag ID CRC (6304)
    qris_modified_payload = f"{payload_without_crc}{tag_amount_full}6304"
    
    # 5. Hitung CRC Baru
    new_crc = calculate_crc16(qris_modified_payload)
    
    # 6. Finalisasi String QRIS
    final_qris = f"{qris_modified_payload}{new_crc}"
    
    return final_qris

def generate_qr_image(qris_data, filename="qris_dinamis.png"):
    """
    Membuat file gambar dari string QRIS.
    """
    qr = qrcode.QRCode(
        version=1,
        error_correction=qrcode.constants.ERROR_CORRECT_H, # High correction level
        box_size=10,
        border=4,
    )
    qr.add_data(qris_data)
    qr.make(fit=True)

    img = qr.make_image(fill_color="black", back_color="white")
    img.save(filename)
    print(f"Sukses! Gambar QRIS tersimpan sebagai: {filename}")
    print(f"String QRIS Baru: {qris_data}")

# --- KONFIGURASI PENGGUNA ---
if __name__ == "__main__":
    print("=== GENERATOR QRIS DINAMIS ===")
    
    # MASUKKAN STRING QRIS MENTAH ANDA DI BAWAH INI
    # Contoh format: "00020101021126570014ID.CO.GOPAY..."
    my_static_qris = input("Masukkan String QRIS Statis Anda: ")
    
    # MASUKKAN NOMINAL YANG DIINGINKAN
    target_amount = input("Masukkan Nominal (misal 15000): ")
    
    try:
        # Proses pembuatan
        new_qris_string = inject_amount(my_static_qris, target_amount)
        
        if new_qris_string:
            generate_qr_image(new_qris_string, "tagihan_otomatis.png")
            
    except Exception as e:
        print(f"Terjadi kesalahan: {e}")

Langkah 2: Menjalankan Program

Setelah kode di atas disimpan, ikuti langkah berikut untuk mencobanya:

  1. Buka terminal di lokasi tempat Anda menyimpan file bikin_qris.py.
  2. Jalankan perintah: python bikin_qris.py.
  3. Program akan meminta String QRIS Statis. Tempel (paste) kode panjang yang Anda dapatkan dari hasil scan QRIS toko Anda.
  4. Program akan meminta Nominal. Masukkan angka saja, misalnya 50000 (untuk Rp 50.000).
  5. Tekan Enter.

Jika berhasil, sebuah file bernama tagihan_otomatis.png akan muncul di folder yang sama. Coba scan gambar tersebut menggunakan aplikasi e-wallet (GoPay, OVO, Dana, atau Mobile Banking). Anda akan melihat nominal Rp 50.000 sudah terisi otomatis dan tidak bisa diubah oleh pembayar.

Penjelasan Teknis Bagian Kode

Agar Anda tidak sekadar copy-paste, berikut penjelasan logikanya:

  • Fungsi calculate_crc16: Ini adalah jantung dari program ini. QRIS tidak menggunakan MD5 atau SHA, melainkan CRC-16 standar CCITT. Jika Anda salah menghitung bit di sini, aplikasi bank akan menolak QR code dengan pesan “QR Code Invalid”.
  • Manipulasi String: Kita memotong string tepat sebelum angka 6304. Angka 63 adalah ID untuk Checksum, dan 04 adalah panjang checksum (selalu 4 digit).
  • Tag 54: Kita menyusun string 54 (ID) + Panjang Digit + Nominal. Contoh untuk Rp 10.000: 540510000. Ini disisipkan ke badan data sebelum checksum dihitung ulang.

Solusi Jika Error

Terkadang hal-hal tidak berjalan mulus pada percobaan pertama. Berikut solusinya:

  1. QRIS Invalid saat discan:
    • Penyebab utama adalah perhitungan CRC yang salah atau penempatan Tag 54 yang keliru.
    • Pastikan string QRIS awal Anda lengkap dan benar (dimulai dengan 000201...). Jangan sampai ada karakter spasi yang ikut ter-copy.
  2. Nominal Tidak Muncul:
    • Beberapa aplikasi perbankan tua mungkin mengabaikan Tag 54 jika posisinya tidak sesuai urutan standar EMVCo (meskipun jarang terjadi). Script di atas menaruh nominal di akhir data (sebelum checksum) yang mana aman untuk 99% aplikasi modern.
  3. Error “ModuleNotFoundError: No module named ‘qrcode'”:
    • Ini berarti Anda belum menginstal library. Ulangi langkah instalasi: pip install qrcode[pil].

Kesimpulan

Mengubah QRIS statis menjadi dinamis bukanlah sihir, melainkan manipulasi string sederhana yang mematuhi standar EMVCo. Dengan skrip Python di atas, Anda sekarang bisa membuat sistem tagihan sendiri, mengirim QR code via WhatsApp dengan nominal spesifik, dan meminimalisir kesalahan bayar oleh pelanggan. Selamat mencoba bereksperimen dengan proyek pembayaran digital Anda!

Leave a Reply

Your email address will not be published. Required fields are marked *