๐Ÿ“– Panduan & Contoh Integrasi API (PHP & JS)

Panduan lengkap integrasi sistem pembayaran QRIS GS SNAP untuk backend PHP dan JavaScript (Node.js & Frontend)

← Kembali ke Portal Dashboard

๐Ÿ” 1. Otentikasi & Format Header Request

Semua request API dari server toko/member Anda ke Payment Hub wajib menyertakan 3 header keamanan utama untuk verifikasi identitas dan integritas data:

Nama Header Tipe Data Wajib? Keterangan
X-API-KEY String Wajib Kunci API toko Anda. Gunakan mk_test_... untuk Sandbox (Uji Coba) atau mk_live_... untuk Produksi.
X-TIMESTAMP String Wajib Waktu saat request dikirim dalam format ISO 8601 UTC (contoh: 2026-09-18T10:30:00Z).
X-SIGNATURE String Wajib Signature HMAC-SHA256 dari teks {X-TIMESTAMP}:{RAW_JSON_BODY} menggunakan SECRET_KEY yang sesuai (gunakan sk_test_... saat mode sandbox atau sk_live_... saat live).
Content-Type String Wajib Wajib bernilai application/json.

๐Ÿงช Panduan Mode Sandbox (Uji Coba Toko)

Mode Sandbox memungkinkan Anda menguji seluruh alur integrasi pembayaran QRIS (pembuatan QRIS, tampilan di checkout toko, dan pengiriman notifikasi Webhook) tanpa perlu memotong uang nyata.

๐ŸŸก Kredensial Uji Coba (Sandbox)

  • API Key: mk_test_xxxxxx
  • Secret Key: sk_test_xxxxxx
  • Callback URL Sandbox: Terpisah dari URL live (bisa localhost/ngrok)
  • QRIS Dummy tanpa memanggil DOKU production

๐ŸŸข Kredensial Produksi (Live)

  • API Key: mk_live_xxxxxx
  • Secret Key: sk_live_xxxxxx
  • Callback URL Live: Domain resmi toko Anda
  • QRIS Asli resmi BI & DOKU SNAP dengan uang nyata
โšก Fitur Simulasi Pembayaran Instan:

Ketika transaksi QRIS dibuat dengan mk_test_..., pada halaman tampilan QRIS pembeli (/api/v1/payment/render-qr/:reference_no) akan muncul banner kuning MODE SANDBOX dan tombol โšก Simulasi Bayar Lunas. Mengklik tombol tersebut akan langsung melunasi transaksi dan menembakkan notifikasi Webhook ke server toko Anda secara instan.

2 Membuat Transaksi QRIS Baru

POST /api/v1/payment/qris

Endpoint ini dipanggil dari backend toko Anda saat pembeli memilih pembayaran QRIS di checkout.

Parameter Body Tipe Wajib? Deskripsi
partner_reference_no string Ya ID Order/Invoice unik dari database toko Anda (misal: ORD-100234).
amount number Ya Nominal belanja (misal: 50000).
customer_name string Opsional Nama lengkap pembeli (misal: Budi Santoso).
customer_phone string Opsional Nomor WhatsApp / HP pembeli.
customer_email string Opsional Alamat email pembeli.
keterangan string Opsional Catatan pesanan / rincian item produk (misal: "Topup 100 Diamond - ID: 88219").
expired_minutes number Opsional Masa berlaku QRIS dalam menit (default: 60 menit).
fee_type string Opsional "CUSTOMER" (fee dibebankan ke pembeli) atau "MERCHANT" (fee ditanggung toko). Default: mengikuti pengaturan toko Anda.

Pilih Bahasa Pemrograman:

<?php
/**
 * Contoh Pembuatan QRIS di PHP (cURL murni tanpa dependensi)
 */

$serverUrl = "https://pg.gscon.net"; // Ganti dengan URL Payment Hub Anda
$apiKey    = "mk_live_xxxxxxxxx";                // Ganti dengan X-API-KEY Toko Anda
$secretKey = "sk_live_xxxxxxxxx";                // Ganti dengan SECRET_KEY Toko Anda

// 1. Susun Data Pesanan
$orderData = [
    "partner_reference_no" => "ORD-" . time(), // Order ID unik toko Anda
    "amount"               => 50000,            // Nominal belanja (Rp 50.000)
    "customer_name"        => "Budi Santoso",
    "customer_phone"       => "08123456789",
    "customer_email"       => "[email protected]",
    "expired_minutes"      => 60,
    "fee_type"             => "CUSTOMER"        // "CUSTOMER" atau "MERCHANT"
];

$jsonBody = json_encode($orderData);

// 2. Format Timestamp ISO-8601 UTC
$timestamp = gmdate("Y-m-d\TH:i:s\Z");

// 3. Buat Signature HMAC-SHA256
$stringToSign = $timestamp . ":" . $jsonBody;
$signature = hash_hmac("sha256", $stringToSign, $secretKey);

// 4. Eksekusi Request cURL
$ch = curl_init("$serverUrl/api/v1/payment/qris");
curl_setopt_array($ch, [
    CURLOPT_POST           => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER     => [
        "Content-Type: application/json",
        "X-API-KEY: $apiKey",
        "X-SIGNATURE: $signature",
        "X-TIMESTAMP: $timestamp"
    ],
    CURLOPT_POSTFIELDS     => $jsonBody,
    CURLOPT_TIMEOUT        => 30
]);

$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

$result = json_decode($response, true);

if ($httpCode === 201 && isset($result['success']) && $result['success']) {
    $data = $result['data'];
    $invoiceRef  = $data['reference_no'];   // ID Invoice Gateway (GSN-xxx)
    $qrString    = $data['qr_content'];     // String EMVCo QRIS GS
    $qrImageUrl  = $data['qr_image_url'];   // URL Gambar QRIS Siap Render
    $qrDataUrl   = $data['qr_data_url'];    // Base64 Data Image
    $totalAmount = $data['total_amount'];   // Total Tagihan Pembeli

    echo "โœ… QRIS Berhasil Dibuat!\n";
    echo "Invoice: $invoiceRef\n";
    echo "Total Bayar: Rp " . number_format($totalAmount, 0, ',', '.') . "\n";
    echo "URL Gambar QR: $qrImageUrl\n";
} else {
    echo "โŒ Gagal Membuat QRIS: " . ($result['message'] ?? $response) . "\n";
}
?>

3 Menampilkan QRIS & Auto-Polling Status di Web Checkout

Gunakan contoh kode HTML & JavaScript berikut di halaman checkout toko Anda agar otomatis memeriksa status pembayaran setiap 3 detik dan melakukan redirect saat pembayaran lunas.

<!-- Halaman Pembayaran Checkout (checkout.html / bayar.php) -->
<div style="text-align: center; max-width: 400px; margin: 40px auto; padding: 24px; border: 1px solid #ddd; border-radius: 16px; font-family: sans-serif;">
  <h3>Scan QRIS untuk Membayar</h3>
  <p style="color: #666; font-size: 14px;">Buka aplikasi GoPay, OVO, Dana, BCA, atau Mobile Banking Anda.</p>

  <!-- Gambar QRIS dari Payment Hub -->
  <div style="background: #fff; padding: 12px; display: inline-block; border-radius: 12px; margin: 16px 0;">
    <img id="qris-image" src="https://pg.gscon.net/api/v1/payment/render-qr/GSN-xxxxxx" alt="QRIS" width="220" height="220">
  </div>

  <h2 id="total-bayar" style="color: #10B981; margin: 0 0 8px;">Rp 50.850</h2>
  <div id="payment-status-badge" style="color: #F59E0B; font-weight: 600; font-size: 13px;">โณ Menunggu Pembayaran...</div>
</div>

<script>
  const invoiceRef = "GSN-xxxxxx"; // Ganti dengan nomor invoice transaksi
  const serverUrl = "https://pg.gscon.net";

  // Auto-polling status transaksi setiap 3 detik
  const pollInterval = setInterval(async () => {
    try {
      const res = await fetch(`${serverUrl}/api/v1/payment/status/${invoiceRef}`);
      const json = await res.json();

      if (json.success && json.data.status === 'PAID') {
        clearInterval(pollInterval);
        document.getElementById('payment-status-badge').innerText = 'โœ… Pembayaran Berhasil!';
        document.getElementById('payment-status-badge').style.color = '#10B981';
        
        alert('๐ŸŽ‰ Terima kasih! Pembayaran Anda telah diterima.');
        window.location.href = '/success.html'; // Redirect ke halaman sukses toko Anda
      }
    } catch (err) {
      console.error('Polling error:', err);
    }
  }, 3000);
</script>

4 Menangani Webhook Notifikasi & Verifikasi Signature

Ketika pelanggan menyelesaikan pembayaran QRIS, Payment Hub akan mengirimkan HTTP POST secara instan ke callback_url toko Anda. Wajib verifikasi signature untuk memastikan data valid dan tidak dimanipulasi.

๐Ÿงญ Alur Identifikasi Transaksi Toko & Webhook

Bagaimana toko Anda mengetahui pesanan mana yang dibayar saat notifikasi webhook diterima:

Field Payload Tipe Fungsi Identifikasi di Sistem Toko
partner_reference_no string KUNCI UTAMA TOKO: Nomor Invoice / Order ID yang dikirim toko Anda saat membuat QRIS. Gunakan field ini untuk WHERE order_id = ... di database toko.
reference_no string ID GATEWAY: Nomor unik transaksi dari Payment Hub (contoh: GSN-1789719317036-777). Simpan sebagai referensi bukti gateway.
status string Bernilai "PAID" jika pembayaran berhasil diselesaikan oleh pembeli.
customer object IDENTITAS PEMBELI & KETERANGAN: Detail data pembeli dan catatan pesanan untuk identifikasi & notifikasi otomatis:
  • customer.name: Nama lengkap pembeli (untuk dicetak pada nota/struk invoice).
  • customer.phone: Nomor WhatsApp / HP pembeli (untuk kirim notifikasi lunas via WA Gateway).
  • customer.email: Alamat email pembeli (untuk kirim e-tiket / bukti bayar via SMTP).
  • customer.keterangan: Catatan pesanan / rincian item produk yang dibeli.
net_amount number Dana bersih yang masuk ke saldo toko Anda setelah potongan biaya QRIS.
total_amount number Total nominal yang dibayarkan oleh pembeli di aplikasi e-wallet / mobile banking.
๐Ÿ’ก Ketentuan Respon Webhook: Server toko Anda wajib membalas request webhook dengan HTTP status 200 OK dan JSON {"status":"OK"}. Jika server toko merespon selain 200 (misal timeout atau error 500), Payment Hub akan melakukan retry otomatis hingga 3 kali.
<?php
/**
 * File: webhook.php (Ditaruh di server toko Anda, misal: https://tokoanda.com/webhook.php)
 */

$secretKey = "sk_live_xxxxxxxxx"; // Ganti dengan SECRET_KEY Toko Anda

// 1. Ambil Raw Payload JSON dan Headers
$rawPayload = file_get_contents('php://input');
$headers    = getallheaders();

$incomingTimestamp = $headers['X-TIMESTAMP'] ?? $headers['x-timestamp'] ?? '';
$incomingSignature = $headers['X-SIGNATURE'] ?? $headers['x-signature'] ?? '';

// 2. Hitung Expected Signature HMAC-SHA256
$stringToSign = $incomingTimestamp . ":" . $rawPayload;
$expectedSignature = hash_hmac('sha256', $stringToSign, $secretKey);

// 3. Verifikasi Keaslian Notifikasi
if (!hash_equals($expectedSignature, $incomingSignature)) {
    http_response_code(401);
    echo json_encode(["status" => "ERROR", "message" => "Invalid Signature"]);
    exit;
}

// 4. Decode Data Notifikasi
$data = json_decode($rawPayload, true);

if ($data && $data['event'] === 'PAYMENT_SUCCESS' && $data['status'] === 'PAID') {
    $orderId            = $data['partner_reference_no']; // ID Order toko Anda
    $invoiceRef         = $data['reference_no'];        // ID Invoice Gateway
    $amount             = $data['amount'];              // Nominal asli belanja
    $feeAmount          = $data['fee_amount'];          // Biaya fee gateway
    $feeChargedTo       = $data['fee_charged_to'];      // "CUSTOMER" atau "MERCHANT"
    $netAmount          = $data['net_amount'];          // Bersih yang diterima toko
    $paymentDate        = $data['payment_date'];

    // DATA IDENTITAS PEMBELI & KETERANGAN PESANAN
    $customerName       = $data['customer']['name'] ?? 'Pelanggan';
    $customerPhone      = $data['customer']['phone'] ?? '';
    $customerEmail      = $data['customer']['email'] ?? '';
    $customerKeterangan = $data['customer']['keterangan'] ?? '';

    // 5. UPDATE STATUS ORDER DI DATABASE TOKO ANDA
    // Contoh query database toko Anda:
    // mysqli_query($conn, "UPDATE orders SET status = 'PAID', note = '$customerKeterangan', payment_date = NOW() WHERE order_id = '$orderId'");

    // 6. Wajib Balas HTTP 200 OK ke Payment Hub
    http_response_code(200);
    echo json_encode([
        "status"      => "OK",
        "message"     => "Webhook processed successfully",
        "order_id"    => $orderId,
        "received_at" => date('Y-m-d H:i:s')
    ]);
    exit;
}

http_response_code(400);
echo json_encode(["status" => "IGNORED", "message" => "Event is not PAYMENT_SUCCESS"]);
?>