Developer Docs

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.

REST JSONHMAC-SHA256WebhookQRIS Dinamis

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.

EndpointMethodFungsi
/api/invoice/createPOSTMembuat invoice QRIS
/api/invoice/statusPOSTCek status pembayaran
/api/invoice/historyPOSTRiwayat transaksi
/api/invoice/balancePOSTRekap saldo merchant
/api/invoice/cancelPOSTBatalkan 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.

Header wajibhttp
Authorization: Bearer sk_live_xxxxxxxxxxxx
Content-Type: application/json
Response gagal autorisasijson
{
  "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 + amount
  • status / cancel → api_id + trx_id
  • history / balance → api_id
PHPphp
<?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);
Node.jsjavascript
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 / balance

Create Invoice

POST/api/invoice/create

Membuat invoice QRIS dinamis. Response berisi string qr siap dirender serta checkout_url bawaan bila Anda tidak ingin membuat halaman pembayaran sendiri.

ParameterTipeWajibKeterangan
api_idstringYaAPI ID merchant, contoh LIVE-90976113.
methodstringYaMetode pembayaran. Saat ini hanya QRIS.
amountintegerYaNominal tagihan dalam Rupiah (tanpa desimal).
merchant_refstringYaNomor referensi order dari sistem Anda.
signaturestringYaHMAC-SHA256 dari api_id + merchant_ref + amount.
namestringOpsionalNama pembeli.
emailstringOpsionalEmail pembeli.
phonestringOpsionalNomor HP pembeli.
merchant_feeintegerOpsionalBiaya tambahan yang dibebankan ke pembeli.
expiredintegerOpsionalMasa berlaku invoice dalam menit. Default 60.
produkarrayOpsionalDaftar item: { produk, qty, harga }.
callback_urlstringOpsionalURL tujuan webhook payment_status.
return_urlstringOpsionalURL redirect setelah pembayaran sukses.
Requestbash
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>"
  }'
Response 200json
{
  "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

POST/api/invoice/status

Cek status terkini satu transaksi. Invoice pending yang sudah melewati expired otomatis diperbarui menjadi expired saat endpoint ini dipanggil.

ParameterTipeWajibKeterangan
api_idstringYaAPI ID merchant.
trx_idstringYaID transaksi dari response create invoice.
signaturestringYaHMAC-SHA256 dari api_id + trx_id.
Requestbash
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)>"
  }'
Response 200json
{
  "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

POST/api/invoice/history

Mengambil daftar transaksi terbaru milik merchant, terurut dari yang paling baru. Gunakan status untuk memfilter dan limit untuk membatasi jumlah data.

ParameterTipeWajibKeterangan
api_idstringYaAPI ID merchant.
signaturestringYaHMAC-SHA256 dari api_id.
limitintegerOpsionalJumlah data, 1–100. Default 20.
statusstringOpsionalFilter: pending, paid, expired, failed.
Requestbash
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)>"
  }'
Response 200json
{
  "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

POST/api/invoice/balance

Rekap nominal transaksi merchant: balance adalah total transaksi berhasil, pending_balance total invoice yang masih menunggu pembayaran.

ParameterTipeWajibKeterangan
api_idstringYaAPI ID merchant.
signaturestringYaHMAC-SHA256 dari api_id.
Requestbash
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)>"
  }'
Response 200json
{
  "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

POST/api/invoice/cancel

Membatalkan invoice yang masih pending sehingga QR tidak dapat dibayar lagi. Transaksi yang sudah berhasil tidak bisa dibatalkan dan akan membalas 404.

ParameterTipeWajibKeterangan
api_idstringYaAPI ID merchant.
trx_idstringYaID transaksi dari response create invoice.
signaturestringYaHMAC-SHA256 dari api_id + trx_id.
Requestbash
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)>"
  }'
Response 200json
{
  "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.

Payload webhookhttp
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
}
Verifikasi signature (PHP)php
<?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

0pending

Invoice dibuat, menunggu pembayaran.

1berhasil

Pembayaran diterima (settlement).

2expired

Melewati masa berlaku invoice.

3gagal

Dibatalkan atau gagal diproses.

Penanganan Error

Semua kegagalan memakai format yang sama: status berisi kode HTTP, message penjelasan, dan data array kosong.

HTTPPesanSolusi
400Parameter wajib kosong / tidak validPastikan api_id, merchant_ref, dan amount terkirim.
401Unauthorized atau signature tidak validCek Bearer secret_api_key dan urutan payload signature.
404Transaksi tidak ditemukanPastikan trx_id milik api_id yang sama.
405Method not allowedSemua endpoint hanya menerima POST.
503Merchant belum mengatur QRISIsi QRIS statis di Dashboard → API Credentials.
500Kesalahan internalCoba ulang dengan backoff; hubungi support bila berulang.