Dokumentasi API
Integrasikan pembayaran QRIS dinamis ke aplikasi Anda: buat invoice, pantau status, tarik riwayat, cek saldo, batalkan invoice, dan terima notifikasi real-time lewat webhook.
Pendahuluan
Base URL produksi: https://utuhpay.my.id. Semua endpoint menerima POST dengan body JSON (form-data juga didukung) dan selalu membalas JSON dengan struktur status, message, dan data.
Ambil api_id dan secret_api_key pada halaman API Credentials. Setiap endpoint punya alias /api/public/... untuk pemanggilan server-to-server dari luar.
| Endpoint | Method | Fungsi |
|---|---|---|
| /api/invoice/create | POST | Membuat invoice QRIS |
| /api/invoice/status | POST | Cek status pembayaran |
| /api/invoice/history | POST | Riwayat transaksi |
| /api/invoice/balance | POST | Rekap saldo merchant |
| /api/invoice/cancel | POST | Batalkan invoice pending |
Autorisasi
Kirim secret_api_key pada header Authorization: Bearer <secret_api_key>. Token divalidasi terhadap api_id di body request. Jangan pernah menaruh secret key di kode frontend — panggil API dari server Anda.
Authorization: Bearer sk_live_xxxxxxxxxxxx
Content-Type: application/json{
"status": "401",
"message": "Unauthorized: api_id atau secret_api_key tidak valid",
"data": []
}Signature
Signature adalah HMAC-SHA256 hex lowercase yang dikunci secret_api_key. Payload berbeda per endpoint:
create→ api_id + merchant_ref + amountstatus/cancel→ api_id + trx_idhistory/balance→ api_id
<?php
$api_id = "LIVE-90976113";
$secret = "sk_live_xxxxxxxxxxxx";
// 1) Create Invoice -> api_id + merchant_ref + amount
$sig_create = hash_hmac('sha256', $api_id . "INV-0001" . 25000, $secret);
// 2) Status / Cancel -> api_id + trx_id
$sig_status = hash_hmac('sha256', $api_id . "TRX1723456789ABCD", $secret);
// 3) History / Balance -> api_id
$sig_balance = hash_hmac('sha256', $api_id, $secret);import crypto from "node:crypto";
const sign = (payload, secret) =>
crypto.createHmac("sha256", secret).update(payload).digest("hex");
const apiId = "LIVE-90976113";
const secret = "sk_live_xxxxxxxxxxxx";
sign(`${apiId}INV-000125000`, secret); // create
sign(`${apiId}TRX1723456789ABCD`, secret); // status / cancel
sign(apiId, secret); // history / balanceCreate Invoice
/api/invoice/createMembuat invoice QRIS dinamis. Response berisi string qr siap dirender serta checkout_url bawaan bila Anda tidak ingin membuat halaman pembayaran sendiri.
| Parameter | Tipe | Wajib | Keterangan |
|---|---|---|---|
| api_id | string | Ya | API ID merchant, contoh LIVE-90976113. |
| method | string | Ya | Metode pembayaran. Saat ini hanya QRIS. |
| amount | integer | Ya | Nominal tagihan dalam Rupiah (tanpa desimal). |
| merchant_ref | string | Ya | Nomor referensi order dari sistem Anda. |
| signature | string | Ya | HMAC-SHA256 dari api_id + merchant_ref + amount. |
| name | string | Opsional | Nama pembeli. |
| string | Opsional | Email pembeli. | |
| phone | string | Opsional | Nomor HP pembeli. |
| merchant_fee | integer | Opsional | Biaya tambahan yang dibebankan ke pembeli. |
| expired | integer | Opsional | Masa berlaku invoice dalam menit. Default 60. |
| produk | array | Opsional | Daftar item: { produk, qty, harga }. |
| callback_url | string | Opsional | URL tujuan webhook payment_status. |
| return_url | string | Opsional | URL redirect setelah pembayaran sukses. |
curl -X POST https://utuhpay.my.id/api/invoice/create \
-H "Authorization: Bearer sk_live_xxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"api_id": "LIVE-90976113",
"method": "QRIS",
"name": "Budi Santoso",
"email": "budi@mail.com",
"phone": "08123456789",
"amount": 25000,
"merchant_fee": 0,
"merchant_ref": "INV-0001",
"expired": 5,
"produk": [{ "produk": "Netflix 1 Bulan", "qty": 1, "harga": 25000 }],
"callback_url": "https://toko.com/callback",
"return_url": "https://toko.com/thanks",
"signature": "<hmac_sha256>"
}'{
"status": "200",
"message": "success",
"data": [
{
"via": "QRIS",
"payment_kode": "QRIS",
"trx_id": "TRX1723456789ABCD",
"merchant_ref": "INV-0001",
"total": 25000,
"expired": "2026-08-13 21:05:00",
"payment_status": "pending",
"qr": "00020101021226...6304ABCD",
"checkout_url": "https://utuhpay.my.id/checkout/TRX1723456789ABCD"
}
]
}Check Status
/api/invoice/statusCek status terkini satu transaksi. Invoice pending yang sudah melewati expired otomatis diperbarui menjadi expired saat endpoint ini dipanggil.
| Parameter | Tipe | Wajib | Keterangan |
|---|---|---|---|
| api_id | string | Ya | API ID merchant. |
| trx_id | string | Ya | ID transaksi dari response create invoice. |
| signature | string | Ya | HMAC-SHA256 dari api_id + trx_id. |
curl -X POST https://utuhpay.my.id/api/invoice/status \
-H "Authorization: Bearer sk_live_xxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"api_id": "LIVE-90976113",
"trx_id": "TRX1723456789ABCD",
"signature": "<hmac_sha256(api_id + trx_id)>"
}'{
"status": "200",
"message": "success",
"data": [
{
"trx_id": "TRX1723456789ABCD",
"merchant_ref": "INV-0001",
"payment_kode": "QRIS",
"total": 25000,
"merchant_fee": 0,
"status": "berhasil",
"status_kode": 1,
"created_at": "2026-08-13T14:00:00.000Z",
"expired": "2026-08-13T14:05:00.000Z",
"paid_at": "2026-08-13T14:02:11.000Z",
"qr": "00020101021226...6304ABCD"
}
]
}Riwayat Transaksi
/api/invoice/historyMengambil daftar transaksi terbaru milik merchant, terurut dari yang paling baru. Gunakan status untuk memfilter dan limit untuk membatasi jumlah data.
| Parameter | Tipe | Wajib | Keterangan |
|---|---|---|---|
| api_id | string | Ya | API ID merchant. |
| signature | string | Ya | HMAC-SHA256 dari api_id. |
| limit | integer | Opsional | Jumlah data, 1–100. Default 20. |
| status | string | Opsional | Filter: pending, paid, expired, failed. |
curl -X POST https://utuhpay.my.id/api/invoice/history \
-H "Authorization: Bearer sk_live_xxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"api_id": "LIVE-90976113",
"limit": 20,
"status": "paid",
"signature": "<hmac_sha256(api_id)>"
}'{
"status": "200",
"message": "success",
"total": 2,
"data": [
{
"trx_id": "TRX1723456789ABCD",
"merchant_ref": "INV-0002",
"payment_kode": "QRIS",
"total": 50000,
"merchant_fee": 0,
"status": "berhasil",
"status_kode": 1,
"created_at": "2026-08-13T14:00:00.000Z",
"expired": "2026-08-13T14:05:00.000Z",
"paid_at": "2026-08-13T14:02:11.000Z"
},
{
"trx_id": "TRX1723456700ZZZZ",
"merchant_ref": "INV-0001",
"payment_kode": "QRIS",
"total": 25000,
"merchant_fee": 0,
"status": "expired",
"status_kode": 2,
"created_at": "2026-08-13T12:00:00.000Z",
"expired": "2026-08-13T12:05:00.000Z",
"paid_at": null
}
]
}Check Balance
/api/invoice/balanceRekap nominal transaksi merchant: balance adalah total transaksi berhasil, pending_balance total invoice yang masih menunggu pembayaran.
| Parameter | Tipe | Wajib | Keterangan |
|---|---|---|---|
| api_id | string | Ya | API ID merchant. |
| signature | string | Ya | HMAC-SHA256 dari api_id. |
curl -X POST https://utuhpay.my.id/api/invoice/balance \
-H "Authorization: Bearer sk_live_xxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"api_id": "LIVE-90976113",
"signature": "<hmac_sha256(api_id)>"
}'{
"status": "200",
"message": "success",
"data": [
{
"api_id": "LIVE-90976113",
"balance": 1750000,
"pending_balance": 75000,
"total_fee": 0,
"total_transaksi_berhasil": 42,
"total_transaksi_pending": 3,
"currency": "IDR"
}
]
}Cancel Invoice
/api/invoice/cancelMembatalkan invoice yang masih pending sehingga QR tidak dapat dibayar lagi. Transaksi yang sudah berhasil tidak bisa dibatalkan dan akan membalas 404.
| Parameter | Tipe | Wajib | Keterangan |
|---|---|---|---|
| api_id | string | Ya | API ID merchant. |
| trx_id | string | Ya | ID transaksi dari response create invoice. |
| signature | string | Ya | HMAC-SHA256 dari api_id + trx_id. |
curl -X POST https://utuhpay.my.id/api/invoice/cancel \
-H "Authorization: Bearer sk_live_xxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"api_id": "LIVE-90976113",
"trx_id": "TRX1723456789ABCD",
"signature": "<hmac_sha256(api_id + trx_id)>"
}'{
"status": "200",
"message": "success",
"data": [
{
"trx_id": "TRX1723456789ABCD",
"merchant_ref": "INV-0001",
"payment_kode": "QRIS",
"total": 25000,
"status": "gagal",
"status_kode": 3
}
]
}Callback / Webhook
Jika callback_url diisi saat membuat invoice, sistem mengirim POST JSON begitu status pembayaran berubah. Balas 200 OK secepatnya; proses berat dilakukan asinkron. Selalu verifikasi header X-Callback-Signature terhadap raw body sebelum memproses.
POST <callback_url>
X-Callback-Event: payment_status
X-Callback-Signature: <hmac_sha256 dari raw body JSON, kunci secret_api_key>
Content-Type: application/json
{
"trx_id": "TRX1723456789ABCD",
"merchant_ref": "INV-0001",
"payment_method": "QRIS",
"total": 25000,
"merchant_fee": 0,
"status": "berhasil",
"status_kode": 1
}<?php
$raw = file_get_contents('php://input');
$sig = $_SERVER['HTTP_X_CALLBACK_SIGNATURE'] ?? '';
$secret = 'sk_live_xxxxxxxxxxxx';
if (!hash_equals(hash_hmac('sha256', $raw, $secret), $sig)) {
http_response_code(401);
exit('invalid signature');
}
$data = json_decode($raw, true);
if ((int) $data['status_kode'] === 1) {
// tandai order lunas — pastikan idempoten terhadap trx_id
}
http_response_code(200);
echo 'OK';Kode Status
Invoice dibuat, menunggu pembayaran.
Pembayaran diterima (settlement).
Melewati masa berlaku invoice.
Dibatalkan atau gagal diproses.
Penanganan Error
Semua kegagalan memakai format yang sama: status berisi kode HTTP, message penjelasan, dan data array kosong.
| HTTP | Pesan | Solusi |
|---|---|---|
| 400 | Parameter wajib kosong / tidak valid | Pastikan api_id, merchant_ref, dan amount terkirim. |
| 401 | Unauthorized atau signature tidak valid | Cek Bearer secret_api_key dan urutan payload signature. |
| 404 | Transaksi tidak ditemukan | Pastikan trx_id milik api_id yang sama. |
| 405 | Method not allowed | Semua endpoint hanya menerima POST. |
| 503 | Merchant belum mengatur QRIS | Isi QRIS statis di Dashboard → API Credentials. |
| 500 | Kesalahan internal | Coba ulang dengan backoff; hubungi support bila berulang. |