Menu utama

Developer · API

Pembayaran QRIS

Buat dan pulihkan satu top-up aktif tanpa menggandakan pembayaran atau mengkreditkan saldo sebelum konfirmasi provider.

Unduh OpenAPI 3.1

Sebelum mengirim request

Minimum top-up diatur admin (default Rp 10.000), dengan maksimum Rp 1.000.000. Baca limits.min dari GET /api/payments. Perubahan minimum hanya berlaku untuk pembayaran baru; retry dengan kunci yang sudah tersimpan tetap memulihkan pembayaran semula. UI polling setiap 5 detik selama CREATING/PENDING. Hanya status PAID terkonfirmasi yang menambah saldo; totalAmount adalah nominal bayar, requestedAmount adalah nominal kredit. feeAmount dan uniqueAmount bukan saldo. Tagihan dibuat melalui mitra QRIS yang dipilih server. Perubahan mitra hanya berlaku untuk tagihan baru, tanpa failover otomatis saat timeout. limits.min juga memperhitungkan batas mitra pembayaran. Endpoint stabil /api/payments tidak bergantung pada mitra.

Kontrak nyata, nilai contoh

Endpoint, field, dan status HTTP mengikuti implementasi. Nilai ID, nomor, OTP, waktu, saldo, serta quote di contoh adalah ilustrasi, bukan data akun atau pilihan yang bisa langsung dibeli. Ganti dengan data dari instalasimu. Halaman ini tidak mengirim request atau menjalankan transaksi.

JavaScript dijalankan pada origin aplikasi setelah login. Contoh cURL memakai PING_ORIGIN dan cookie jar PING_COOKIE_JAR dari panduan autentikasi. Sukses memakai { ok: true, data }; create juga HTTP 200, bukan 201. Semua endpoint di halaman ini memerlukan sesi aktif dan dapat mengembalikan 401, 403, atau 500.

Baca pembayaran aktif

GET/api/paymentsSession cookie

Membaca pembayaran CREATING/PENDING terakhir dari database dan batas top-up efektif. Bentuk data adalah { payment: Payment | null, limits: { min, max } }, bukan Payment langsung. Gunakan limits.min untuk nominal baru dan endpoint detail untuk pemeriksaan provider.

GET /api/payments · JavaScript
const response = await fetch("/api/payments", {
  credentials: "same-origin",
  cache: "no-store",
});
const body = await response.json();

if (!response.ok || !body.ok) {
  throw Object.assign(
    new Error(body.error?.message ?? "Request gagal"),
    { code: body.error?.code, status: response.status },
  );
}

const data = body.data;

Buat top-up QRIS

POST/api/paymentsSession cookie

Membuat top-up QRIS idempotent melalui mitra pembayaran yang dipilih server. Biaya tambahan pemrosesan tidak dikreditkan ke saldo. Callback diverifikasi oleh server sebelum status transaksi diperbarui. Tidak ada failover otomatis saat respons hilang. Satu QRIS terbaru per akun. Kirim replacePaymentId untuk mengganti pembayaran PENDING dengan nominal baru tanpa menunggu expiry. QR lama tidak dibatalkan provider dan pembayaran masuk tetap diverifikasi. CREATING tidak boleh diganti. Tanpa replacePaymentId, pembayaran aktif dengan nominal sama dapat dikembalikan. QRIS baru (termasuk pengganti) dibatasi 4 per 10 menit dan 12 per jam per akun, serta 12 per 10 menit dan 30 per jam per jaringan; respons RATE_LIMITED menyertakan Retry-After.

Perhatikan sebelum mengirim

Menjalankan request ini membuat transaksi pembayaran nyata pada instalasi yang terhubung provider. Respons hilang tidak boleh memicu pembuatan transaksi baru; pulihkan melalui GET pembayaran aktif/detail.

Buat top-up QRIS
Body JSONTipeWajibKeterangan
amountintegerYaRupiah utuh, maksimum 1000000. Minimum efektif ditentukan admin; baca limits.min dari GET /api/payments sebelum membuat pembayaran baru.
clientRequestIdstring · uuidYaUUID untuk satu top-up. Jangan ganti UUID karena respons jaringan hilang.
replacePaymentIdstring · uuidTidakOpsional: ID pembayaran PENDING milik akun yang hendak diganti. Membuat QR baru tanpa menunggu expiry; tidak membatalkan QR lama di provider.
POST /api/payments · JavaScript
const response = await fetch("/api/payments", {
  method: "POST",
  credentials: "same-origin",
  cache: "no-store",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    "amount": 10000,
    "clientRequestId": "f41c29d8-967d-469d-ae4f-0114c38bf715"
  }),
});
const body = await response.json();

if (!response.ok || !body.ok) {
  throw Object.assign(
    new Error(body.error?.message ?? "Request gagal"),
    { code: body.error?.code, status: response.status },
  );
}

const data = body.data;
Buat top-up QRIS
Kode errorHTTPTindakan
VALIDATION_ERROR400Periksa tipe, field wajib, format UUID, dan field tambahan.
IDEMPOTENCY_CONFLICT409Pulihkan transaksi awal dengan payload yang sama. UUID baru hanya untuk niat transaksi baru setelah hasil awal terkonfirmasi.
CONFLICT409Baca pembayaran aktif, jangan terus membuat top-up baru. Untuk konflik profil, pilih username lain; kapitalisasi berbeda tetap dianggap username yang sama.
RATE_LIMITED429Hormati Retry-After sebelum meminta ulang. Jangan retry pembelian otomatis atau membuat clientRequestId baru saat hasil awal belum pasti.
PAYMENT_NOT_CONFIGURED503Pengelola memeriksa konfigurasi mitra pembayaran di server. Perubahan konfigurasi hanya menentukan tagihan baru; tagihan tersimpan tetap diperiksa melalui mitra asalnya.
PAYMENT_PROVIDER_ERROR502Baca pembayaran aktif atau detail dengan ID yang sama.

Periksa pembayaran

GET/api/payments/{id}Session cookie

Membaca pembayaran milik sesi dan melakukan poll-through terbatas untuk pembayaran aktif. UI polling 5 detik, sedangkan klaim pemeriksaan provider dibatasi sekitar 10 detik. Respons langsung Payment, bukan { payment }. Pemeriksaan selalu memakai provider tagihan yang tersimpan: mitra pembayaran asal tagihan. Perubahan konfigurasi tidak memindahkan tagihan yang sudah dibuat.

Periksa pembayaran
ParameterTipeWajibKeterangan
id (path)string · uuidYaUUID pembayaran milik pengguna bersesi.
GET /api/payments/{id} · JavaScript
const response = await fetch("/api/payments/5d81627b-4c2b-4cab-bd07-2a5ed675fe5f", {
  credentials: "same-origin",
  cache: "no-store",
});
const body = await response.json();

if (!response.ok || !body.ok) {
  throw Object.assign(
    new Error(body.error?.message ?? "Request gagal"),
    { code: body.error?.code, status: response.status },
  );
}

const data = body.data;
Periksa pembayaran
Kode errorHTTPTindakan
VALIDATION_ERROR400Periksa tipe, field wajib, format UUID, dan field tambahan.
NOT_FOUND404Periksa ID dan akun; ID resource tidak memberi akses lintas pengguna.
PAYMENT_PROVIDER_ERROR502Baca pembayaran aktif atau detail dengan ID yang sama.
CONFLICT409Baca pembayaran aktif, jangan terus membuat top-up baru. Untuk konflik profil, pilih username lain; kapitalisasi berbeda tetap dianggap username yang sama.

Schema · Payment

Schema · Payment
FieldTipeWajibKeterangan
idstring · uuidYaSesuai tipe yang ditampilkan.
providerqrisYaMetode pembayaran publik, bukan identitas mitra pemroses.
requestedAmountintegerYaJumlah top-up yang dikreditkan jika PAID terkonfirmasi. Untuk QRIS bisa 1.000 atau 2.000 lebih besar dari amount saat antrean QRIS ramai; selisih itu ikut dikreditkan.
totalAmountinteger | nullYaTotal yang harus dibayar ke provider; bisa berbeda dari requestedAmount.
feeAmountinteger | nullYaBiaya admin dalam rupiah jika disediakan terpisah, bukan kredit saldo; null jika tidak diketahui. Biaya berbentuk persentase tidak dianggap nominal rupiah.
uniqueAmountinteger | nullYaKode unik dalam rupiah jika disediakan terpisah, bukan kredit saldo; null jika tidak diketahui.
statusCREATING | PENDING | PAID | EXPIRED | FAILEDYaSesuai tipe yang ditampilkan.
qrisUrlstring | nullYaMateri QR dari provider; tersedia hanya untuk pembayaran aktif.
qrisImagestring | nullYaSesuai tipe yang ditampilkan.
paymentUrlstring | nullYaSesuai tipe yang ditampilkan.
expiresAtstring | nullYaTimestamp provider tanpa zona waktu, disimpan apa adanya. Bukan waktu ISO UTC.
createdAtstring · date-timeYaISO 8601 dengan zona waktu.
paidAtstring | null · date-timeYaISO 8601 dengan zona waktu.
supersededAtstring | null · date-timeYaWaktu QR diganti. Bukan pembatalan provider; callback PAID tetap diverifikasi dan dikreditkan sekali.
messagestring | nullYaSesuai tipe yang ditampilkan.

Schema · ActivePayment

Schema · ActivePayment
FieldTipeWajibKeterangan
paymentPayment | nullYaPembayaran aktif, atau null jika tidak ada.
limitsobjectYaSesuai tipe yang ditampilkan.

ping! · Panduan yang tumbuh bersama produknya.