Lewati ke konten
API QRIS per Bahasa

Integrasi API QRIS dengan PHP Native

Cukup cURL bawaan PHP. Jalan di shared hosting, VPS, maupun cPanel.

Halaman ini bagian dari layanan API QRIS gratis APIQris: pakai QRIS milik Anda sendiri, tanpa potongan dari kami, dana langsung ke rekening Anda.

  • 0% fee APIQris
  • Dana langsung ke Anda
  • Webhook HMAC

Jawaban singkatUntuk memakai API QRIS di PHP, kirim POST ke endpoint /invoices dengan header Authorization Bearer berisi API key, lalu tampilkan qr_image_url ke pembeli. Untuk konfirmasi lunas, buat file webhook yang memverifikasi header X-APIQris-Signature dengan hash_hmac dan hash_equals.

Ingin tahu gambaran besarnya dulu? Baca juga pengertian QRIS API dan bagaimana API QRIS gratis APIQris menghubungkan QRIS Anda ke website, aplikasi, dan chatbot.

Rp0potongan dari APIQris
24/7konfirmasi otomatis
100%dana ke rekening Anda
Masalah

Tanpa API QRIS PHP, ini yang biasa terjadi

Menulis generator QRIS dari nol

Parsing TLV, urutan tag, dan CRC-16 rawan salah dan memakan waktu.

Polling mutasi bank

Scraping internet banking rapuh, sering diblokir, dan butuh menyimpan password.

Webhook tanpa tanda tangan

Endpoint callback yang tidak diverifikasi bisa dipalsukan siapa saja.

Sulit diuji

Tanpa sandbox, developer harus bayar sungguhan setiap kali mengetes alur checkout.

Solusinya: API QRIS PHP dari APIQris mengotomatiskan semuanya, tanpa mengganti QRIS yang sudah Anda punya.

Panduan

Memahami API QRIS PHP secara lengkap

01

Persiapan

Pastikan ekstensi curl dan json aktif (bawaan hampir semua hosting). Simpan API key dan secret webhook di variabel lingkungan atau file konfigurasi di luar folder publik.

02

Alur di aplikasi PHP

Urutan yang disarankan:

  • Simpan pesanan dengan status pending
  • Panggil API untuk membuat QRIS dinamis
  • Tampilkan gambar QR dan total yang harus dibayar
  • Terima webhook, verifikasi, tandai lunas
  • Halaman pembeli polling status atau redirect otomatis
03

Tips produksi

Gunakan HTTPS untuk URL webhook, set timeout cURL 15 detik, dan catat setiap webhook yang masuk ke log agar mudah ditelusuri saat ada keluhan pembeli.

Cara kerja

Alur API QRIS PHP dari scan sampai lunas

Dana dan data berjalan di jalur berbeda. Uang pembeli langsung ke QRIS Anda, APIQris hanya mengurus QRIS dinamis, pencocokan, dan webhook.

Infografis cara kerja API QRIS APIQris Dana pembeli mengalir dari pembeli ke penyedia QRIS lalu langsung ke rekening Anda. Jalur data terpisah: notifikasi dari HP merchant Anda dibaca APIQris lalu dikirim sebagai webhook ke website atau bot Anda. Rp Rp bayar dana langsung notifikasi kirim mutasi webhook buat tagihan → tampilkan QRIS dinamis ke pembeli Pembeli Scan & bayar Penyedia QRISAnda Bank / e-wallet Rekening Anda Langsung ke Anda HP MerchantAnda Notif dana masuk APIQris Cocokkan tagihan Website /Bot Anda Webhook lunas APIQris tidak menyentuh dana
Jalur dana: pembeli → penyedia QRIS → rekening Anda Jalur data: notifikasi → APIQris → webhook ke sistem Anda
  1. 1

    Daftar gratis

    Buat akun APIQris dengan email, tanpa biaya dan tanpa dokumen perusahaan.

  2. 2

    Upload QRIS Anda

    Unggah gambar QRIS merchant dari bank atau e-wallet Anda. Sistem membaca data QRIS secara otomatis.

  3. 3

    Pasang aplikasi pendamping

    Pasang aplikasi APIQris di HP Android yang menerima notifikasi dana masuk, lalu pindai kode pairing.

  4. 4

    Pakai API key

    Salin API key, buat tagihan QRIS dinamis dari website atau chatbot, dan terima webhook saat lunas.

Contoh skenario

Sebelum dan sesudah memakai API QRIS PHP

Developer freelance yang membangun toko online untuk klien. Ilustrasi alur kerja, bukan testimoni pelanggan.

Sebelum

Setiap proyek dimulai dari nol: mencari gateway, mengurus dokumen klien, dan menulis ulang modul pembayaran.

Sesudah

Dengan API QRIS PHP, satu modul yang sama dipakai di semua proyek. Klien cukup upload QRIS miliknya, dana langsung ke rekening klien.

Perbandingan

QRIS statis manual vs API QRIS PHP vs payment gateway

AspekQRIS statis manualAPI QRIS PHP (APIQris)Payment gateway
NominalDiketik pembeliTerkunci otomatisTerkunci otomatis
Konfirmasi bayarCek mutasi manualWebhook otomatisWebhook otomatis
Penerima danaRekening AndaRekening AndaRekening gateway
Potongan perantaraRp0Rp0±0,7% + biaya tarik
Bekerja 24 jamTergantung adminYaYa
Contoh kode

Buat tagihan dengan API QRIS PHP

Kirim nominal dan order ID, terima QR dan total yang sudah ditambah kode unik.

Buat tagihan QRIS
<?php
$ch = curl_init('https://api.apiqris.com/v1/invoices');
curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('APIQRIS_KEY'),
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'amount'   => 50000,
        'order_id' => 'INV-1024',
    ]),
]);
$qris = json_decode(curl_exec($ch), true);
echo $qris['qr_image_url']; // tampilkan ke pembeli
curl -X POST https://api.apiqris.com/v1/invoices \
  -H "Authorization: Bearer AQ_LIVE_xxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{"amount":50000,"order_id":"INV-1024"}'
use Illuminate\Support\Facades\Http;

$qris = Http::withToken(config('services.apiqris.key'))
    ->post('https://api.apiqris.com/v1/invoices', [
        'amount'   => $order->total,
        'order_id' => $order->number,
    ])
    ->throw()
    ->json();

return view('checkout.qris', ['qris' => $qris]);
const res = await fetch('https://api.apiqris.com/v1/invoices', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.APIQRIS_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ amount: 50000, order_id: 'INV-1024' }),
});
const qris = await res.json();
console.log(qris.total_amount, qris.qr_image_url);
import os, requests

r = requests.post(
    "https://api.apiqris.com/v1/invoices",
    headers={"Authorization": f"Bearer {os.environ['APIQRIS_KEY']}"},
    json={"amount": 50000, "order_id": "INV-1024"},
    timeout=15,
)
r.raise_for_status()
qris = r.json()
print(qris["total_amount"], qris["qr_image_url"])
body := strings.NewReader(`{"amount":50000,"order_id":"INV-1024"}`)
req, _ := http.NewRequest("POST", "https://api.apiqris.com/v1/invoices", body)
req.Header.Set("Authorization", "Bearer "+os.Getenv("APIQRIS_KEY"))
req.Header.Set("Content-Type", "application/json")

res, err := http.DefaultClient.Do(req)
if err != nil {
    log.Fatal(err)
}
defer res.Body.Close()
Webhook

Verifikasi kabar lunas

Hitung HMAC SHA-256 dari timestamp dan body mentah, bandingkan secara constant-time.

Verifikasi webhook
<?php
$body = file_get_contents('php://input');
parse_str(str_replace(',', '&', $_SERVER['HTTP_X_APIQRIS_SIGNATURE'] ?? ''), $sig);
$expected = hash_hmac('sha256', $sig['t'] . '.' . $body, getenv('APIQRIS_SECRET'));

if (!hash_equals($expected, $sig['v1'] ?? '') || abs(time() - (int) $sig['t']) > 300) {
    http_response_code(401);
    exit;
}
$event = json_decode($body, true);
if ($event['event'] === 'invoice.paid') {
    tandai_lunas($event['order_id']); // fungsi milik aplikasi Anda
}
http_response_code(200);
# Header yang dikirim APIQris ke callback_url Anda
X-APIQris-Signature: t=1760670000,v1=5f2b...e9
Content-Type: application/json

{"event":"invoice.paid","id":"qr_9KX2T7","order_id":"INV-1024","total_amount":50123,"status":"paid"}
Route::post('/webhook/qris', function (Request $request) {
    parse_str(str_replace(',', '&', $request->header('X-APIQris-Signature')), $sig);
    $expected = hash_hmac('sha256', $sig['t'].'.'.$request->getContent(), config('services.apiqris.secret'));
    abort_unless(hash_equals($expected, $sig['v1'] ?? ''), 401);

    if ($request->input('event') === 'invoice.paid') {
        Order::where('number', $request->input('order_id'))->update(['status' => 'paid']);
    }
    return response()->noContent();
});
import crypto from 'node:crypto';
import express from 'express';

const app = express();
app.post('/webhook/qris', express.raw({ type: 'application/json' }), (req, res) => {
  const sig = Object.fromEntries(req.get('X-APIQris-Signature').split(',').map((p) => p.split('=')));
  const expected = crypto.createHmac('sha256', process.env.APIQRIS_SECRET)
    .update(`${sig.t}.${req.body}`).digest('hex');
  if (!crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(sig.v1 ?? ''))) return res.sendStatus(401);

  const event = JSON.parse(req.body);
  if (event.event === 'invoice.paid') markPaid(event.order_id);
  res.sendStatus(200);
});
import hmac, hashlib, os
from flask import Flask, request, abort

app = Flask(__name__)

@app.post("/webhook/qris")
def webhook():
    sig = dict(p.split("=", 1) for p in request.headers["X-APIQris-Signature"].split(","))
    msg = f"{sig['t']}.".encode() + request.get_data()
    expected = hmac.new(os.environ["APIQRIS_SECRET"].encode(), msg, hashlib.sha256).hexdigest()
    if not hmac.compare_digest(expected, sig.get("v1", "")):
        abort(401)
    event = request.get_json()
    if event["event"] == "invoice.paid":
        mark_paid(event["order_id"])
    return "", 200
func webhook(w http.ResponseWriter, r *http.Request) {
    body, _ := io.ReadAll(r.Body)
    sig := map[string]string{}
    for _, p := range strings.Split(r.Header.Get("X-APIQris-Signature"), ",") {
        if k, v, ok := strings.Cut(p, "="); ok {
            sig[k] = v
        }
    }
    mac := hmac.New(sha256.New, []byte(os.Getenv("APIQRIS_SECRET")))
    mac.Write([]byte(sig["t"] + "." + string(body)))
    if !hmac.Equal([]byte(hex.EncodeToString(mac.Sum(nil))), []byte(sig["v1"])) {
        http.Error(w, "invalid signature", http.StatusUnauthorized)
        return
    }
    w.WriteHeader(http.StatusOK)
}
Keamanan

Aman sejak baris kode pertama

HTTPS di semua endpoint

Data tagihan dan API key selalu terenkripsi saat dikirim.

API key bisa dirotasi

Kunci bocor? Buat kunci baru dari dashboard, kunci lama langsung mati.

Webhook HMAC SHA-256

Setiap callback ditandatangani agar server Anda bisa menolak yang palsu.

Dana tidak lewat kami

Tidak ada saldo yang bisa dibekukan, karena uang langsung ke QRIS Anda.

Sebelum go-live

Checklist API QRIS PHP

  • QRIS merchant resmi sudah diupload dan terbaca benar
  • Aplikasi pendamping di HP Android berstatus online
  • Optimasi baterai dimatikan untuk aplikasi merchant & pendamping
  • Uji bayar Rp1.000 berhasil terdeteksi lunas
  • URL webhook memakai HTTPS dan membalas 200
  • Tanda tangan HMAC diverifikasi sebelum memproses pesanan
  • Proses webhook idempoten berdasarkan order_id
  • API key disimpan di server, tidak di frontend
Istilah

Glosarium singkat

QRIS
Quick Response Code Indonesian Standard, standar QR pembayaran nasional dari Bank Indonesia.
QRIS dinamis
QRIS yang membawa nominal untuk satu transaksi, berbeda dengan QRIS statis tanpa nominal.
NMID
National Merchant ID, nomor identitas merchant yang tertanam di QRIS Anda.
MDR
Merchant Discount Rate, biaya yang dipotong penyedia QRIS atas transaksi sesuai ketentuan.
Kode unik
Angka kecil yang ditambahkan ke nominal agar setiap tagihan bisa dibedakan.
Webhook
HTTP POST otomatis dari APIQris ke server Anda saat status tagihan berubah.
FAQ

Pertanyaan umum tentang API QRIS PHP

Versi PHP berapa yang didukung?

Contoh kode berjalan di PHP 7.4 hingga 8.x.

Apakah ada library composer?

Contoh cURL di halaman ini sudah cukup. SDK resmi tersedia di dashboard setelah mendaftar.

Apakah dana dari API QRIS PHP ditampung APIQris?

Tidak. Pembeli membayar ke QRIS milik Anda sendiri, sehingga dana langsung masuk ke rekening atau saldo merchant Anda.

Berapa biaya memakai APIQris?

Daftar gratis dan APIQris tidak memotong fee per transaksi. MDR dari penyedia QRIS Anda (bila ada) mengikuti ketentuan penyedia.

Apakah perlu HP khusus?

Cukup satu HP Android yang terpasang aplikasi merchant penerbit QRIS Anda dan aplikasi pendamping APIQris. HP lama pun bisa selama notifikasinya stabil.

Mulai API QRIS PHP hari ini

Gratis, tanpa potongan per transaksi, dana langsung ke rekening Anda. Pelajari dulu API QRIS selengkapnya atau langsung daftar.