Skip to main content

Quick Start (5 Minutes)

This guide walks you through accepting your first QRIS payment using the V1 API.

Prerequisites

  1. An active merchant account (Nusio Saka or Flypay)
  2. Your API key from the merchant dashboard: Settings → API Credentials
  3. A server that can receive HTTP POST requests (for webhooks)

Step 1: Create an Order

Send a POST to /v1/orders with your item amount and customer details.

cURL
curl -X POST https://api.nusio-saka.com/v1/orders \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: order-$(date +%s)" \
-d '{
"merchant_order_id": "INV-001",
"amount_minor": 5000000,
"payment_method": "QRIS",
"customer_name": "Budi Santoso",
"notify_url": "https://your-site.com/webhooks/payment",
"expire_minutes": 15
}'

amount_minor: 5000000 = IDR 50,000 (100 minor units per 1 IDR).

Response:

{
"success": true,
"data": {
"ref_code": "ORD-260601103045-A1B2",
"status": "PENDING",
"qris_string": "00020101021226...",
"qr_url": "https://cdn.nusio-saka.com/qr/ORD-260601103045-A1B2.png",
"expires_at": "2026-06-01T10:45:45Z"
}
}

Step 2: Display the QR Code

Render qris_string as a QR code in your checkout UI, or display qr_url as an image:

<!-- Simple: use the pre-rendered image -->
<img src="https://cdn.nusio-saka.com/qr/ORD-260601103045-A1B2.png" alt="Scan to Pay" width="300" />
JavaScript — render QRIS string with a library
import QRCode from 'qrcode';

const canvas = document.getElementById('qr-canvas');
await QRCode.toCanvas(canvas, data.qris_string, { width: 300 });

The QR code is valid until expires_at. Show a countdown timer and disable payment after expiry.

Step 3: Receive the Payment Webhook

When the customer pays, the system POSTs to your notify_url:

{
"event": "order.PAID",
"ref_code": "ORD-260601103045-A1B2",
"merchant_order_id": "INV-001",
"amount_minor": 5000000,
"currency": "IDR",
"status": "PAID",
"paid_at": "2026-06-01T10:38:12Z"
}

Your handler must return HTTP 200. Process the event asynchronously:

PHP — webhook handler
<?php
$rawBody = file_get_contents('php://input');

// Optional: verify signature
$webhookSecret = getenv('WEBHOOK_SECRET');
if ($webhookSecret) {
$expected = 'sha256=' . hash_hmac('sha256', $rawBody, $webhookSecret);
if (!hash_equals($expected, $_SERVER['HTTP_X_SIGNATURE'] ?? '')) {
http_response_code(401);
exit;
}
}

// Acknowledge immediately
http_response_code(200);
echo json_encode(['received' => true]);
if (function_exists('fastcgi_finish_request')) fastcgi_finish_request();

$payload = json_decode($rawBody, true);

if ($payload['status'] === 'PAID') {
$orderId = $payload['merchant_order_id']; // your INV-001
// mark order as paid in your database
// trigger fulfillment
}

Step 4: Poll for Status (Optional Fallback)

If your webhook endpoint was unavailable, poll the order:

cURL
curl https://api.nusio-saka.com/v1/orders/ORD-260601103045-A1B2 \
-H "X-API-Key: YOUR_API_KEY"

Check data.status: PAID = success, EXPIRED/FAILED = unsuccessful.


What's Next?