Dokumentasi API untuk Developer

Panduan lengkap integrasi QRIS dinamis, webhook, dan Payment API FN CREATIVE — ringkas, jelas, dan siap diterapkan.

1. Kredensial API

Setiap request wajib menyertakan header berikut — nilai di bawah ini milik akun Anda setelah login:

Login merchant agar docs menampilkan x-api-key, id_merchant, dan webhook_secret valid (bukan contoh).
Header
x-api-key
API Key merchant
— login dulu —
Header
id_merchant
ID merchant 12 digit
Webhook
webhook_secret
Secret tanda tangan webhook

Base URL

https://pay.fncreative.org/api

2. Create Transaction

POST/qris/create

Membuat QRIS dinamis dengan kode unik. Gunakan nilai total_amount dari response sebagai nominal yang wajib dibayar pelanggan.

Kode unik kecil per tier — nilai live dari GET /api/docs/meta.

Base amountKode unik (+)
Memuat…
FieldKeterangan
order_id *ID unik transaksi (INV-1001)
amount *Nominal dasar integer (min 500)
keteranganDeskripsi opsional
callback_urlOverride webhook URL (opsional)
curl_setopt_array($curl, array(
  CURLOPT_URL => 'https://pay.fncreative.org/api/qris/create',
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_CUSTOMREQUEST => 'POST',
  CURLOPT_POSTFIELDS => '{
    "order_id": "INV-123",
    "amount": 10000,
    "keterangan": "Pembayaran Invoice #123"
  }',
  CURLOPT_HTTPHEADER => array(
    'Content-Type: application/json',
    'x-api-key: YOUR_API_KEY',
    'id_merchant: YOUR_MERCHANT_ID'
  ),
));
Ingat: Customer wajib bayar tepat total_amount, bukan nominal dasar amount.
Masa berlaku QR: default 5 menit sejak dibuat. Lihat field expired_at dan expired_menit di response — jangan hardcode 60 menit di UI merchant.

Response (201)

{
  "status": true,
  "code": "transaction_created",
  "message": "Transaction Created Successfully",
  "data": {
    "order_id": "INV-123",
    "amount": "10000.00",
    "base_amount": 10000,
    "unique_code": 45,
    "amount_uniq": "45.00",
    "total_amount": "10045.00",
    "status": "PENDING",
    "expired_at": "2026-06-13 16:05:01 WIB",
    "expired_menit": "5",
    "signature": "...",
    "keterangan": "Pembayaran Invoice #123",
    "qris_image": "data:image/png;base64,...",
    "qr_string": "00020101...",
    "customer_instruction": "Scan QRIS, bayar sesuai total sampai 3 digit terakhir, lalu tunggu status SUCCESS."
  }
}

3. Masa Berlaku QR & Kode Unik

Setiap transaksi QRIS dinamis memiliki batas waktu pembayaran. Nilai aktual platform saat ini (live dari GET /api/docs/meta):

ParameterNilaiArti
unique_code.slot_minutes5 menitQR PENDING mengunci slot kode unik selama ini
unique_code.expire_minutes5 menitQR otomatis EXPIRED jika belum dibayar
unique_code.match_window_minutes180 menitTransaksi SUCCESS tetap mengunci slot (anti bentrok)
unique_code.payment_match_grace_minutes10 menitToleransi backend: pembayaran telat setelah QR EXPIRED masih bisa dicocokkan otomatis

Tampilan vs backend

  • UI merchant menampilkan countdown ±5 menit (sesuai expired_menit di response create).
  • Backend tetap memantau ±10 menit sejak QR dibuat untuk mencocokkan mutasi bank — customer yang bayar menit ke-6–10 setelah create masih bisa masuk saldo otomatis.
  • Lewat toleransi backend → status tetap EXPIRED, uang terdeteksi masuk ke log Tidak Masuk Saldo — merchant bisa minta tinjauan atau admin kredit manual.

Alur merchant

  1. Panggil POST /qris/create → tampilkan QR + total_amount + countdown dari expired_at.
  2. Customer bayar dalam waktu aktif → webhook PAID atau polling GET /qris/status/{order_id}SUCCESS.
  3. Lewat waktu tanpa bayar → status EXPIRED → buat QR baru (disarankan order_id baru).
Webhook hanya dikirim saat pembayaran sukses (PAID). Untuk QR expired, pantau via polling status atau UI merchant sendiri.
Jika response error berisi code dan next_step, tampilkan next_step ke operator/merchant agar penanganannya jelas.
Pesan “Slot kode unik sedang penuh” jarang terjadi: slot pending dipulihkan otomatis setelah masa berlaku QR. Jika muncul, tunggu beberapa menit atau gunakan nominal/tier berbeda.

4. Webhook Callback

Wajib — idempotency webhook

Gateway dapat mengirim ulang callback yang sama (retry otomatis/admin). Selalu cek order_id sudah diproses sebelum mengaktifkan produk/layanan.

// Node.js contoh handler
app.post("/payment/callback", async (req, res) => {
  const { order_id, status, total_amount, payment_date, signature, delivery_id, signature_v2 } = req.body;
  if (await db.isOrderPaid(order_id)) {
    return res.status(200).json({ ok: true, duplicate: true });
  }
  const legacy = hmacLegacy(order_id, status, total_amount, payment_date);
  const v2 = delivery_id ? hmacV2(delivery_id, order_id, status, total_amount, payment_date) : null;
  if (signature !== legacy && (!v2 || signature_v2 !== v2)) {
    return res.status(401).json({ ok: false, error: "invalid_signature" });
  }
  if (status !== "PAID") return res.status(200).json({ ok: true, ignored: true });
  await db.markPaid(order_id);
  return res.status(200).json({ ok: true });
});

Field baru: delivery_id (unik per pengiriman), delivered_at (ISO UTC), signature_v2. Field signature lama tetap ada untuk kompatibilitas.

Server merchant harus merespon HTTP 200 OK. Callback dikirim saat pembayaran sukses — field status bernilai PAID (bukan SUCCESS seperti di API status/history).

Webhook PAID vs API SUCCESS

Keduanya berarti pembayaran berhasil — hanya beda penamaan karena channel berbeda (push webhook vs pull API).
ChannelField statusArti
Webhook callback ke server merchantPAIDBayar sukses — proses order / kirim produk di sistem Anda
GET /qris/status/{order_id}SUCCESSArti sama dengan PAID — gunakan untuk polling
GET /qris/historySUCCESSTransaksi sukses di riwayat & laporan
Sebelum bayarPENDINGMenunggu pembayaran
Lewat masa berlaku QREXPIREDQR kadaluarsa — buat transaksi baru
Integrasi webhook: terima PAID sebagai trigger sukses. Integrasi polling: cek sampai status SUCCESS. Jangan menunggu SUCCESS di webhook — field itu tidak ada di callback.
Webhook URL tidak wajib saat daftar. Isi callback_url di body /qris/create jika ingin notifikasi otomatis. Tanpa URL, gunakan polling GET /qris/status/{order_id}.

Validasi Signature

Secret webhook akun Anda:

signature = HMAC_SHA256(
  YOUR_WEBHOOK_SECRET,
  order_id + "|" + status + "|" + total_amount + "|" + payment_date
)
{
  "order_id": "INV-123",
  "status": "PAID",
  "amount": 10000,
  "total_amount": 10045,
  "payment_date": "2026-06-01 21:48:01",
  "created_at": "2026-06-01 21:18:01 WIB",
  "updated_at": "2026-06-01 21:48:01",
  "keterangan": "Pembayaran Invoice #123",
  "direct_url": null,
  "signature": "..."
}

5. Check Status

GET/qris/status/{order_id}

Header sama seperti create: x-api-key + id_merchant. Status di response API: PENDING, SUCCESS, atau EXPIRED.

6. Transaction History

GET/qris/history

Header integrasi: x-api-key + id_merchant. Mendukung filter dan paginasi.

QueryKeterangan
pageHalaman (default 1)
per_pageItem per halaman, max 50 (default 30)
statusALL · SUCCESS · PENDING · EXPIRED
q atau searchCari order ID / keterangan
date_fromTanggal awal filter (YYYY-MM-DD, WIB)
date_toTanggal akhir filter (YYYY-MM-DD, WIB)
GET https://pay.fncreative.org/api/qris/history?page=1&per_page=30&status=SUCCESS&date_from=2026-06-01&date_to=2026-06-26
x-api-key: YOUR_API_KEY
id_merchant: YOUR_MERCHANT_ID

Response mencakup status_counts (jumlah per status), total_pages, dan array data berisi transaksi.

Export CSV

GET/qris/history/export

Query filter sama seperti history. Mengembalikan file CSV (maks. 5.000 baris) dengan kolom: order_id, created_at, base_amount, unique_code, total_amount, status, paid_at, expired_at, keterangan.

Export juga memakai header x-api-key + id_merchant — cocok untuk rekonsiliasi otomatis di sistem merchant.

7. Ringkasan Harian

GET/qris/summary?date=2026-06-01

Menampilkan total transaksi sukses dan pendapatan per hari (zona Asia/Jakarta). Parameter date opsional — default hari ini.

FieldArti
revenue.gross_amountTotal uang masuk (nominal dibayar customer, termasuk kode unik)
revenue.base_amountTotal harga dasar produk (tanpa kode unik)
revenue.success_countJumlah transaksi SUCCESS di tanggal tersebut
activity.pending_todayQR dibuat hari itu yang masih PENDING
Atau buka Dashboard Merchant — login dengan email + password untuk melihat pendapatan, saldo, dan API key.

8. Login & Dashboard

POST/merchants/login

Dashboard web memakai session token (Bearer). API integrasi tetap memakai x-api-key.

{
  "email": "dev@tokoabc.com",
  "password": "password-anda"
}

Response berisi session_token. Kredensial API lihat via GET /merchants/me setelah login (bukan di body login).

GET/merchants/me

Header: Authorization: Bearer <session_token>

9. Dompet & Penarikan

Fee platform: 1,2% dari total pembayaran (termasuk kode unik) per transaksi SUCCESS. Penarikan minimum Rp 25.000, maksimal 3 kali per hari (zona WIB).

Notifikasi Telegram & email merchant diatur lewat dashboard web. Admin platform tetap menerima alert operasional.

EndpointAuthFungsi
GET /merchants/walletBearerSaldo, fee, kuota penarikan hari ini, rekening bank
PUT /merchants/bank-accountBearerSimpan rekening penarikan
POST /merchants/withdrawBearerAjukan penarikan (amount atau "all")
GET /merchants/withdrawalsBearerRiwayat penarikan
GET /merchants/wallet/ledgerBearerMutasi saldo (kredit, fee, hold penarikan)
PUT /merchants/callback-urlBearerSimpan/hapus webhook URL default
GET /merchants/telegram-notifyBearerStatus notifikasi Telegram
PUT /merchants/telegram-notifyBearerAktif/nonaktif + Chat ID
POST /merchants/telegram-notify/linkBearerBuat link hubung bot Telegram
POST /merchants/telegram-notify/testBearerKirim pesan tes Telegram
GET /merchants/email-notifyBearerStatus notifikasi email merchant
PUT /merchants/email-notifyBearerAktif/nonaktif notifikasi email
POST /merchants/email-notify/testBearerKirim email tes ke alamat merchant
callback_url harus HTTPS publik (bukan localhost/IP privat). Isi per transaksi di body /qris/create atau di dashboard.
Field withdraw_daily_limit dan withdraw_today_count di response wallet menunjukkan kuota penarikan harian.

10. Pembayaran Tidak Masuk Saldo (Payment Issues)

Kasus di mana mutasi bank terdeteksi sistem tetapi tidak otomatis masuk saldo merchant. Biasanya terjadi karena customer transfer setelah QR EXPIRED di tampilan (±5 menit) dan melewati toleransi auto-match backend (±10 menit sejak QR dibuat).

Auth: semua endpoint di bagian ini memakai Authorization: Bearer <session_token> (login dashboard). Bukan x-api-key.

Alur kasus

  1. Sistem mendeteksi transfer masuk tetapi tidak bisa dicocokkan otomatis → kasus tercatat (needs_action: true).
  2. Merchant melihat kasus di dashboard atau via GET /merchants/payment-issues.
  3. Jika customer sudah bayar, merchant ajukan tinjauan → POST .../claim.
  4. Tim FN CREATIVE memproses dari panel admin: kredit manual ke order atau tutup kasus (refund/ditutup).
  5. Setelah selesai, merchant menerima notifikasi Telegram + Gmail (jika diaktifkan) dan riwayat tersedia di notification_history.

Jenis kasus (result)

NilaiLabelArti
no_matchTidak cocokTransfer terdeteksi, tidak ada order PENDING/EXPIRED dengan nominal yang sama dalam jendela toleransi.
ambiguous_graceNominal kembarAda ≥2 order dengan nominal identik — sistem tidak bisa memastikan order mana yang dibayar.

Status penyelesaian (resolution_status)

NilaiLabelArti
(kosong)Belum diproses — needs_action: true
creditedSudah dikreditAdmin mengkredit manual ke order — saldo merchant bertambah + webhook PAID dikirim.
refundedSudah direfundKasus ditutup — dana direfund ke merchant (tanpa kredit saldo).
dismissedDitutupKasus ditutup tanpa kredit (duplikat / false alarm).

Daftar kasus

GET/merchants/payment-issues
QueryKeterangan
pageHalaman (default 1)
per_pageItem per halaman, maks. 50 (default 20)
include_resolved1 = sertakan kasus yang sudah dikredit/ditutup/refund. Default: hanya yang masih perlu tindakan.
GET https://pay.fncreative.org/api/merchants/payment-issues?page=1&per_page=20&include_resolved=1
Authorization: Bearer YOUR_SESSION_TOKEN

Response:

{
  "status": true,
  "data": {
    "current_page": 1,
    "per_page": 20,
    "total": 2,
    "total_pages": 1,
    "grace_minutes": 10,
    "display_expire_minutes": 5,
    "data": [
      {
        "id": 218,
        "amount": 237063,
        "created_at": "2026-06-26 20:15:42 WIB",
        "result": "no_match",
        "result_label": "Tidak cocok",
        "reason": "Tidak ada order PENDING yang cocok.",
        "recommendation": "Pembayaran terdeteksi tetapi tidak masuk saldo Anda — ...",
        "needs_action": false,
        "is_resolved": true,
        "resolution_status": "refunded",
        "resolution_label": "Sudah direfund",
        "resolution_note": "Sudah direfund ke merchant / tidak jadi kredit.",
        "resolved_at": "2026-06-26 20:22:15 WIB",
        "resolved_order_id": "",
        "merchant_claimed_at": "2026-06-26 20:18:00 WIB",
        "merchant_claim_note": "Customer transfer jam 20:14 WIB, Order ORDER-76250626114",
        "related_orders": [
          {
            "order_id": "ORDER-76250626114",
            "status": "EXPIRED",
            "base_amount": 237000,
            "unique_code": 63,
            "expected_amount": 237063,
            "total_amount": 237063,
            "created_at": "2026-06-26 20:05:12 WIB",
            "expired_at": "2026-06-26 20:10:12 WIB",
            "keterangan": "Topup saldo"
          }
        ],
        "notification_history": [
          {
            "id": 12,
            "log_id": 218,
            "audience": "merchant",
            "channel": "telegram",
            "event": "Pembayaran sudah direfund",
            "recipient_masked": "***8546",
            "status": "sent",
            "created_at": "2026-06-26 20:31:05 WIB"
          },
          {
            "id": 13,
            "log_id": 218,
            "audience": "merchant",
            "channel": "email",
            "event": "Pembayaran sudah direfund",
            "recipient_masked": "pi***@gmail.com",
            "status": "sent",
            "created_at": "2026-06-26 20:31:06 WIB"
          }
        ]
      }
    ]
  }
}

Field objek kasus

FieldTipeKeterangan
idnumberID kasus (log) — dipakai di claim dan notifications
amountnumberNominal transfer terdeteksi (integer Rupiah)
created_atstringWaktu deteksi pembayaran (WIB)
resultstringno_match atau ambiguous_grace
result_labelstringLabel UI: Tidak cocok / Nominal kembar
reasonstringAlasan teknis sistem
recommendationstringSaran tindakan untuk merchant
needs_actionbooleantrue jika masih perlu tindakan merchant/admin
is_resolvedbooleantrue jika sudah dikredit/ditutup
resolution_statusstringcredited / refunded / dismissed / kosong
resolution_labelstringLabel penyelesaian (UI)
resolution_notestringCatatan admin saat menutup/mengkredit
resolved_atstringWaktu penyelesaian (WIB)
resolved_order_idstringOrder tujuan jika dikredit manual
merchant_claimed_atstringWaktu merchant ajukan tinjauan (kosong jika belum)
merchant_claim_notestringCatatan merchant saat claim
related_ordersarrayOrder terkait milik merchant ini (lihat tabel di bawah)
notification_historyarrayRiwayat notifikasi Telegram/Gmail untuk kasus selesai (hanya jika is_resolved: true)

Field related_orders[]

FieldKeterangan
order_idID order merchant
statusBiasanya EXPIRED saat kasus muncul
base_amountNominal dasar (tanpa kode unik)
unique_codeKode unik QRIS
expected_amount / total_amountTotal yang harus dibayar customer
created_at / expired_atWaktu QR dibuat & expired (WIB)
keteranganDeskripsi order

Ajukan tinjauan (claim)

POST/merchants/payment-issues/{log_id}/claim

Merchant melaporkan bahwa customer sudah transfer. Satu kasus hanya bisa di-claim sekali. Wajib sertakan Order ID & kapan customer transfer di note.

POST https://pay.fncreative.org/api/merchants/payment-issues/218/claim
Authorization: Bearer YOUR_SESSION_TOKEN
Content-Type: application/json

{
  "note": "Customer transfer jam 22:05 WIB, Order ORDER-76250626114, bukti sudah dikirim via WA"
}

Response sukses:

{
  "status": true,
  "message": "Permintaan tinjauan dikirim. Tim FN CREATIVE akan mengecek bukti transfer.",
  "data": {
    "log_id": 218,
    "amount": 237063,
    "detected_at": "2026-06-26 20:15:42 WIB",
    "merchant_claimed_at": "2026-06-26 22:06:10 WIB",
    "merchant_claim_note": "Customer transfer jam 22:05 WIB, ..."
  }
}

Error umum (HTTP 400):

  • Kasus ini bukan milik merchant Anda.
  • Kasus ini sudah diselesaikan.
  • Anda sudah mengajukan tinjauan untuk kasus ini.
Setelah claim: merchant menerima konfirmasi Permintaan tinjauan diterima via Telegram/Gmail; admin/ops juga diberi tahu.

Riwayat notifikasi kasus

GET/merchants/payment-issues/{log_id}/notifications

Mengembalikan riwayat pengiriman notifikasi untuk merchant yang login (Telegram + Gmail). Notifikasi admin/ops tidak ditampilkan di endpoint ini.

GET https://pay.fncreative.org/api/merchants/payment-issues/218/notifications
Authorization: Bearer YOUR_SESSION_TOKEN
FieldKeterangan
channeltelegram atau email
eventJudul notifikasi, mis. Pembayaran sudah direfund, Pembayaran dikredit manual
recipient_maskedChat ID / email tujuan (disamarkan)
statussent / skipped (notif nonaktif) / failed
created_atWaktu pengiriman (WIB)
Jika include_resolved=1 pada daftar kasus, field notification_history sudah disertakan per item — endpoint terpisah ini berguna untuk refresh tanpa memuat ulang seluruh daftar.

Notifikasi otomatis

Merchant menerima Telegram dan/atau Gmail (jika diaktifkan di dashboard) pada event berikut:

EventPemicu
Pembayaran tidak masuk saldo ⚠️Kasus baru terdeteksi (order terkait merchant)
Permintaan tinjauan diterima ✅Merchant berhasil POST .../claim
Pembayaran dikredit manual ✅Admin mengkredit kasus ke order
Pembayaran sudah direfundAdmin menutup kasus (refund ke merchant)
Kasus pembayaran ditutupAdmin menutup kasus tanpa kredit

Aktifkan notifikasi: Webhook Log & Email (Telegram + Gmail).

Endpoint admin (FN CREATIVE Ops)

Memerlukan header x-admin-key. Bukan untuk integrasi merchant — dokumentasi referensi internal.
EndpointFungsi
GET /admin/payment-match-logsDaftar log pembayaran. Query: filter=unmatched, resolution=pending|resolved|all, q, page, limit
POST /admin/payment-match-logs/{id}/creditKredit manual. Body: order_id, note
POST /admin/payment-match-logs/{id}/dismissTutup kasus. Body: type (refund / dismissed / duplicate), note
GET /admin/payment-match-logs/{id}/notificationsRiwayat notifikasi lengkap (merchant + admin)

11. Webhook Log & Notifikasi Email

Log pengiriman webhook

GET/merchants/webhook-log?page=1&per_page=20

Header: Authorization: Bearer <session_token>. Menampilkan riwayat callback ke URL merchant: order_id, target_url, status_code, success, error_text, created_at.

Gunakan log ini untuk debug jika webhook PAID tidak diterima server merchant — pastikan endpoint merespon HTTP 200 dan signature valid.

Notifikasi email merchant

Selain Telegram, merchant bisa menerima email untuk event pembayaran, penarikan, kasus tidak masuk saldo, dan penyelesaian kasus (kredit/refund/tutup). Aktifkan di dashboard atau via API:

PUT https://pay.fncreative.org/api/merchants/email-notify
Authorization: Bearer YOUR_SESSION_TOKEN
Content-Type: application/json

{ "enabled": true }

Tes kirim: POST /merchants/email-notify/test

12. Kredensial & Registrasi OTP

Manajemen API key (session)

EndpointFungsi
POST /merchants/claim-api-keyMasukkan API key lama yang masih aktif agar bisa ditampilkan lagi di dashboard
POST /merchants/regenerate-api-keyGanti API key — setelah berhasil, API key lama langsung tidak bisa dipakai lagi
POST /merchants/regenerate-webhook-secretRotasi webhook_secret untuk validasi HMAC
POST https://pay.fncreative.org/api/merchants/regenerate-api-key
Authorization: Bearer YOUR_SESSION_TOKEN
Simpan API key & webhook secret baru segera — tidak ditampilkan ulang kecuali regenerate/claim.

Registrasi dengan OTP email

Jika server mengaktifkan verifikasi OTP (lihat email.otp_register_required di GET /api/docs/meta):

  1. POST /merchants/register/send-otp — kirim kode ke email (body sama seperti register)
  2. POST /merchants/register/confirm — konfirmasi dengan verification_id + otp_code
POST/merchants/register/confirm
{
  "verification_id": "dari response send-otp",
  "otp_code": "123456"
}

Jika OTP tidak aktif, gunakan POST /merchants/register langsung (lihat Onboarding Partner).

Best Practice Integrasi (Produksi)

  • Auth ganda: integrasi bot/website pakai x-api-key + id_merchant; dashboard, kasus tidak masuk saldo & riwayat notifikasi pakai Bearer session_token.
  • Jika customer klaim sudah transfer tetapi saldo belum masuk, cek Payment Issues → ajukan claim dengan Order ID & bukti.
  • Tampilkan total_amount besar di checkout — bukan hanya amount.
  • Countdown dari expired_at — semua timestamp API dalam format WIB (Asia/Jakarta).
  • Satu order_id = satu invoice. Jangan create ulang berkali-kali tanpa perlu.
  • Jika masih PENDING, API mengembalikan QR yang sama — cek status dulu sebelum create baru.
  • Setelah EXPIRED, create dengan order_id baru atau hapus record expired (reuse order_id diperbolehkan setelah expired).
  • Validasi webhook dengan webhook_secret + HMAC — abaikan callback tanpa signature valid.
  • Fee platform (1,2%) hanya pada transaksi SUCCESS — EXPIRED tidak dikenai fee.

Status Types

StatusArti
PENDINGMenunggu pembayaran — QR masih aktif sampai expired_at
SUCCESSPembayaran berhasil (API status/history) — saldo merchant dikredit (setelah fee)
PAIDSama artinya dengan SUCCESS — hanya dipakai di webhook callback ke server merchant
EXPIREDQR kadaluarsa (±5 menit default) — buat QR baru untuk customer

Onboarding Partner

User bisa daftar sendiri tanpa hubungi admin:

  1. Buka https://pay.fncreative.org/register/
  2. Isi nama, email, password, dan opsional callback_url webhook HTTPS
  3. Setujui Syarat & Ketentuan dan Kebijakan Privasi
  4. Akun menunggu persetujuan admin — setelah disetujui, kredensial API tersedia di dashboard
  5. Login kapan saja di /login/ — API key yang sama terlihat di dashboard
Webhook URL default bisa diatur di Dashboard → Webhook URL Default atau per transaksi via callback_url di body /qris/create.
Integrasi bot/website: informasikan customer bahwa QR berlaku ±5 menit dan nominal wajib tepat total_amount. Detail teknis: bagian Masa Berlaku QR.

API Registrasi (opsional)

POST/merchants/register
POST/merchants/register/send-otp · /merchants/register/confirm

Detail OTP & manajemen kredensial: lihat Kredensial & Registrasi OTP.

{
  "name": "Toko ABC",
  "email": "dev@tokoabc.com",
  "password": "password-min-8",
  "password_confirm": "password-min-8",
  "store_name": "Toko ABC",
  "callback_url": "https://tokoabc.com/webhook/fn-payment",
  "terms_accepted": true
}