QR Code Tiket Acara: Cara Membuat dan Scan QR lewat API (Node.js dan Python)
Oleh Tim APIPedia, Tim Engineering APIPedia · Terbit · Diperbarui · 10 menit baca
Buat QR tiket acara bertanda tangan HMAC lewat POST /v1/qr/generate, lalu scan saat check-in dengan /v1/qr/decode. Ada contoh curl, Node.js, dan Python.

QR code tiket acara bisa dibuat dan dipindai dengan dua endpoint QR Code Generator APIPedia: POST /v1/qr/generate menghasilkan gambar QR dari nomor tiket, dan POST /v1/qr/decode membaca isinya kembali dari foto saat check-in. Produk ini tercatat di tier Free pada katalog, menerima JSON, dan mengembalikan PNG sebagai data URL.
Tutorial ini memandu Anda membangun alur tiket dari awal sampai pintu masuk: menyusun isi QR yang tidak bisa dipalsukan, membuat gambarnya dengan curl dan Node.js, membaca foto QR dengan Node.js dan Python, lalu menolak tiket ganda. Semua contoh memakai kunci apk_xxx dan data fiktif, dan bentuk contoh responsnya mengikuti panggilan nyata ke API.
Ringkasan cepat: apa yang perlu Anda tahu?
QR Code Generator APIPedia punya dua endpoint POST di bawah https://api.apipedia.id/v1/qr. Tabel berikut merangkum parameter dan batas yang paling sering dicari.
| Item | Detail |
|---|---|
| Buat QR | POST https://api.apipedia.id/v1/qr/generate |
| Baca QR | POST https://api.apipedia.id/v1/qr/decode |
| Autentikasi | Header Authorization: Bearer apk_xxx |
| Isi QR | Field text, maksimal sekitar 2.900 karakter; content_type juga mendukung url, vcard, dan wifi |
| Ukuran gambar | size 64 sampai 2048 piksel, default 512 |
| Koreksi galat | error_correction: L, M (default), Q, atau H |
| Output generate | PNG di dalam data.image_data_url (data URL base64, bukan tautan CDN) |
| Input decode | image_base64: PNG, JPEG, WebP, GIF, atau BMP, maksimal 8 MB |
| Simbol yang dibaca | QR Code, Micro QR, dan rMQR; sampai 50 simbol per gambar lewat max_symbols |
| Tier | Free di katalog APIPedia |
Masalah apa yang diselesaikan QR tiket lewat API?
QR tiket lewat API menyelesaikan dua pekerjaan sekaligus: membuat gambar QR tanpa memasang pustaka gambar di server, dan membaca foto QR tanpa aplikasi pemindai khusus. Panitia seminar, workshop kampus, atau konser komunitas biasanya hanya punya formulir pendaftaran dan grup WhatsApp. Daftar hadir dicentang manual, antrean panjang, dan tangkapan layar tiket mudah diteruskan ke orang lain.
Dengan dua endpoint ini, backend Anda cukup mengirim teks untuk mendapat PNG, lalu mengirim foto untuk mendapat teksnya kembali. Logika penting seperti tanda tangan dan status "sudah dipakai" tetap berada di server Anda, sehingga Anda memegang kendali penuh atas siapa yang boleh masuk.

Apa yang perlu disiapkan sebelum mulai?
Anda hanya perlu satu kunci API APIPedia dan satu rahasia milik server sendiri. Daftar atau masuk di console.apipedia.id, buka menu Token API, lalu buat kunci baru yang berawalan apk_.
APIPEDIA_KEY: kunciapk_xxxuntuk memanggil API. Simpan di variabel lingkungan server, jangan di kode browser atau aplikasi petugas.TICKET_SECRET: string acak panjang buatan Anda sendiri, misalnya hasilopenssl rand -hex 32. Rahasia ini dipakai menandatangani tiket dan tidak pernah dikirim ke APIPedia.- Node.js 18 ke atas atau Python 3 dengan pustaka
requests.
Langkah 1: bagaimana menyusun isi QR yang tidak bisa dipalsukan?
Isi QR yang aman memuat nomor tiket ditambah tanda tangan HMAC, bukan nomor tiket saja. QR hanyalah teks yang digambar, jadi siapa pun bisa membuat QR berisi A-0043 kalau polanya tertebak. Tanda tangan membuat tiket palsu ditolak karena pemalsu tidak tahu TICKET_SECRET.
Format yang dipakai di tutorial ini adalah empat bagian dipisah titik:
TIKET.<id-acara>.<nomor-tiket>.<tanda-tangan>
TIKET.EVT-DEMO-2026.A-0042.ddaa8042e43d8de0
Tanda tangan adalah 16 karakter pertama dari HMAC-SHA256 atas EVT-DEMO-2026.A-0042. Hindari titik di dalam id acara dan nomor tiket supaya pemisahnya tidak ambigu. Jangan menaruh nama, email, atau nomor telepon peserta di dalam QR, karena isinya bisa dibaca oleh pemindai mana pun.
Langkah 2: bagaimana membuat gambar QR tiket?
Gambar QR dibuat dengan mengirim teks tiket ke POST /v1/qr/generate. Pakai error_correction bernilai Q agar QR tetap terbaca walau layar ponsel peserta retak atau cetakannya sedikit kotor, dan size 512 supaya tajam di layar maupun kertas.
curl -X POST https://api.apipedia.id/v1/qr/generate \
-H "Authorization: Bearer apk_xxx" \
-H "Content-Type: application/json" \
-d '{
"text": "TIKET.EVT-DEMO-2026.A-0042.ddaa8042e43d8de0",
"size": 512,
"error_correction": "Q"
}'
Contoh respons generate
Respons berikut berasal dari panggilan nyata; isi image_data_url dipotong karena panjangnya ribuan karakter.
{
"is_success": true,
"message": "QR code berhasil dibuat",
"data": {
"content_type": "text",
"format": "png",
"size": 512,
"error_correction": "Q",
"image_data_url": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA..."
}
}
Potong awalan data:image/png;base64,, dekode sisanya, lalu simpan sebagai berkas PNG atau tempel langsung di atribut src gambar pada halaman tiket. Kalau tiketnya ingin dikirim sebagai dokumen, alurnya bisa dilanjutkan dengan tutorial HTML ke PDF.
Versi Node.js: buat dan simpan PNG
Modul berikut memuat seluruh alur, termasuk fungsi check-in yang dibahas di langkah berikutnya. Logika tanda tangan dan check-in-nya sudah diuji dengan data contoh di atas.
// tiket.mjs - Node.js 18+ (fetch bawaan)
import crypto from "node:crypto";
import fs from "node:fs/promises";
const API = "https://api.apipedia.id/v1/qr";
const KEY = process.env.APIPEDIA_KEY; // apk_xxx
const SECRET = process.env.TICKET_SECRET; // rahasia milik server Anda
function tandaTangan(eventId, nomor) {
return crypto.createHmac("sha256", SECRET)
.update(`${eventId}.${nomor}`)
.digest("hex")
.slice(0, 16);
}
async function panggil(path, body) {
const res = await fetch(`${API}${path}`, {
method: "POST",
headers: {
Authorization: `Bearer ${KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify(body),
});
const json = await res.json();
if (!json.is_success) throw new Error(`${res.status}: ${json.message}`);
return json.data;
}
// Langkah 1 + 2: susun isi tiket, minta gambar QR, simpan PNG
export async function buatQrTiket(eventId, nomor) {
const text = `TIKET.${eventId}.${nomor}.${tandaTangan(eventId, nomor)}`;
const data = await panggil("/generate", {
text,
size: 512,
error_correction: "Q",
});
const base64 = data.image_data_url.split(",")[1];
await fs.writeFile(`tiket-${nomor}.png`, Buffer.from(base64, "base64"));
return text;
}
// Langkah 3 + 4: baca foto QR, periksa tanda tangan, tandai terpakai
export async function checkIn(fotoPath, sudahDipakai) {
const image_base64 = (await fs.readFile(fotoPath)).toString("base64");
const data = await panggil("/decode", { image_base64, max_symbols: 1 });
if (data.count === 0) return { ok: false, alasan: "QR tidak terbaca" };
const [awalan, eventId, nomor, sig] = data.symbols[0].text.split(".");
if (awalan !== "TIKET" || !sig) return { ok: false, alasan: "Bukan tiket acara ini" };
const benar = tandaTangan(eventId, nomor);
const cocok = sig.length === benar.length &&
crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(benar));
if (!cocok) return { ok: false, alasan: "Tanda tangan tidak cocok" };
if (sudahDipakai.has(nomor)) return { ok: false, alasan: "Tiket sudah dipakai" };
sudahDipakai.add(nomor);
return { ok: true, eventId, nomor };
}
Langkah 3: bagaimana membaca QR saat check-in?
QR dibaca dengan mengirim foto dalam bentuk base64 ke POST /v1/qr/decode. Petugas memotret layar ponsel atau tiket cetak peserta dari halaman check-in milik Anda, backend meneruskan fotonya, dan API mengembalikan teks di dalam data.symbols[0].text.
# ubah foto QR menjadi base64 satu baris, lalu kirim
B64=$(base64 -w0 tiket-A-0042.png)
curl -X POST https://api.apipedia.id/v1/qr/decode \
-H "Authorization: Bearer apk_xxx" \
-H "Content-Type: application/json" \
-d "{\"image_base64\": \"$B64\", \"max_symbols\": 1}"
Contoh respons decode
Respons berikut mengikuti bentuk respons nyata untuk QR di atas dan sudah dipangkas: koordinat position, bytes_hex, dan beberapa field identitas simbol dihilangkan agar ringkas.
{
"is_success": true,
"message": "1 barcode berhasil dibaca",
"data": {
"count": 1,
"symbols": [
{
"format": "QRCode",
"format_label": "QR Code",
"text": "TIKET.EVT-DEMO-2026.A-0042.ddaa8042e43d8de0",
"content_type": "Text",
"encoding": "ascii",
"encoding_source": "guessed",
"has_eci": false,
"text_is_binary": false,
"bytes_length": 43,
"bytes_base64": "VElLRVQuRVZULURFTU8tMjAyNi5BLTAwNDIuZGRhYTgwNDJlNDNkOGRlMA==",
"orientation": 0,
"is_mirrored": false,
"is_inverted": false,
"parsed": { "type": "text" }
}
],
"formats_searched": ["QRCode", "MicroQRCode", "RMQRCode"],
"image": { "mime": "image/png", "bytes": 3660 }
}
}
Dua field yang paling penting adalah count dan symbols[0].text. Gambar tanpa QR bukan error: API tetap menjawab 200 dengan count 0, symbols kosong, dan sebuah hint. Field try_harder aktif secara default, sehingga foto miring atau terbalik tetap dicoba dibaca.
Langkah 4: bagaimana memverifikasi tiket dan menolak tiket ganda?
Tiket diverifikasi dengan menghitung ulang tanda tangan di server lalu mencocokkan nomor tiket dengan catatan "sudah dipakai" di database. API hanya membaca gambar; keputusan boleh masuk atau tidak sepenuhnya ada di kode Anda. Fungsi checkIn pada modul Node.js di atas sudah melakukannya, dan berikut padanannya dalam Python.
# checkin.py - pip install requests
import base64, hashlib, hmac, os, requests
API = "https://api.apipedia.id/v1/qr/decode"
KEY = os.environ["APIPEDIA_KEY"] # apk_xxx
SECRET = os.environ["TICKET_SECRET"].encode()
def tanda_tangan(event_id, nomor):
pesan = f"{event_id}.{nomor}".encode()
return hmac.new(SECRET, pesan, hashlib.sha256).hexdigest()[:16]
def check_in(foto_path, sudah_dipakai):
with open(foto_path, "rb") as f:
image_base64 = base64.b64encode(f.read()).decode()
res = requests.post(
API,
headers={"Authorization": f"Bearer {KEY}"},
json={"image_base64": image_base64, "max_symbols": 1},
timeout=15,
)
body = res.json()
if not body["is_success"]:
raise RuntimeError(f"{res.status_code}: {body['message']}")
data = body["data"]
if data["count"] == 0:
return {"ok": False, "alasan": "QR tidak terbaca"}
bagian = data["symbols"][0]["text"].split(".")
if len(bagian) != 4 or bagian[0] != "TIKET":
return {"ok": False, "alasan": "Bukan tiket acara ini"}
_, event_id, nomor, sig = bagian
if not hmac.compare_digest(sig, tanda_tangan(event_id, nomor)):
return {"ok": False, "alasan": "Tanda tangan tidak cocok"}
if nomor in sudah_dipakai:
return {"ok": False, "alasan": "Tiket sudah dipakai"}
sudah_dipakai.add(nomor)
return {"ok": True, "event_id": event_id, "nomor": nomor}
print(check_in("tiket-A-0042.png", set()))
Di produksi, ganti set() dengan kolom checked_in_at di tabel tiket dan lakukan pembaruan secara atomik, misalnya UPDATE ... WHERE checked_in_at IS NULL. Dengan begitu dua petugas yang memindai tiket yang sama pada detik yang sama tidak sama-sama meloloskannya.

Kesalahan apa yang paling sering terjadi?
Kesalahan paling sering adalah mengirim body yang tidak lengkap atau gambar yang tidak bisa dibaca. Semua error memakai amplop yang sama dengan is_success bernilai false dan data bernilai null, dan pesannya menyebut field yang bermasalah.
{
"is_success": false,
"message": "Field \"text\" wajib diisi.",
"data": null
}
- 400 saat generate: field
textkosong,sizedi luar 64 sampai 2048, atau isi melebihi kapasitas QR. Pendekkan isi tiket; cukup id dan tanda tangan. - 400 saat decode: base64 rusak, format gambar tidak didukung, atau berkas di atas 8 MB. Perkecil foto di sisi klien sebelum dikirim.
- 401: kunci salah, kedaluwarsa, atau header
Authorizationtidak terkirim. - 403: kunci valid tetapi scope-nya tidak mencakup endpoint QR. Ubah scope di halaman Token.
count0: bukan error. Foto terlalu gelap, buram, atau terpotong. Minta peserta menaikkan kecerahan layar lalu foto ulang.- Kunci di aplikasi petugas: jangan memanggil API langsung dari browser petugas. Lewatkan foto ke backend agar
apk_xxxdanTICKET_SECRETtidak bocor. - Membandingkan tanda tangan dengan
==: pakaicrypto.timingSafeEqualatauhmac.compare_digestseperti di contoh.
Pertanyaan yang sering diajukan
Apakah QR Code Generator APIPedia gratis?
Ya, produk QR Code Generator tercatat di tier Free pada katalog APIPedia. Anda tetap perlu kunci API dari console untuk memanggilnya, dan setiap panggilan tercatat di pemakaian akun.
Apakah gambar QR disimpan di server APIPedia?
Tidak ada tautan gambar yang dikembalikan. PNG dikirim langsung di dalam respons sebagai data URL pada image_data_url, jadi Anda yang memutuskan menyimpannya di storage sendiri atau membuatnya ulang saat dibutuhkan.
Bisakah endpoint decode membaca QRIS atau barcode produk?
Endpoint /v1/qr/decode membaca QR Code, Micro QR, dan rMQR, dan mengenali isi seperti URL, Wi-Fi, vCard, dan QRIS pada field parsed. Untuk barcode batang seperti EAN-13 atau Code 128, pakai produk Barcode Generator & Reader di /v1/barcode/decode.
Tingkat koreksi galat mana yang cocok untuk tiket?
Tingkat Q cocok untuk kebanyakan tiket karena menyeimbangkan ketahanan dan kepadatan pola. Pakai H bila QR dicetak kecil di gelang atau kartu yang mudah tergores, dan M bila isinya panjang dan hanya ditampilkan di layar.
Apakah tiket tetap aman kalau pesertanya membagikan tangkapan layar?
Tanda tangan mencegah tiket palsu, bukan tiket yang disalin. Salinan ditangani oleh pencatatan "sudah dipakai": orang pertama yang memindai masuk, pemindaian berikutnya dengan nomor yang sama ditolak.
Lanjut ke mana setelah ini?
Langkah berikutnya adalah mencoba kedua endpoint dengan kunci Anda sendiri di console.apipedia.id; spesifikasi lengkapnya ada di halaman produk QR Code Generator. Untuk alur acara yang lebih lengkap, baca juga OCR KTP untuk verifikasi identitas peserta, cara kerja webhook untuk menerima notifikasi pembayaran, dan menghubungkan APIPedia ke Claude lewat MCP bila Anda ingin agen AI yang membuat QR-nya.
Topik: #qr code #tiket acara #qr code generator #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.