Konvert.id
← Konvert.id

1. Mulai Dari Nol

1.8 Custom Checkout API - Bikin Sistem Pembayaran dari Storefront Sendiri

Bikin sistem pembayaran dari storefront atau aplikasimu sendiri, pelanggan bayar lewat payment gateway Konvert

~20 menit

Prasyarat

  • Akun Konvert aktif dan kamu berperan sebagai pemilik toko (owner)
  • Paket toko Pro atau Elite — fitur ini tidak tersedia di paket Free/Starter
  • Verifikasi akun pembayaran sudah selesai, supaya toko bisa menerima pembayaran
  • Ada developer atau sistem yang bisa memanggil API dari sisi server

Hasil akhir: Sistemmu sendiri — aplikasi kasir, ERP, storefront, atau server billing — bisa membuat tagihan lewat Konvert, dan pelanggan membayarnya pakai QRIS, virtual account, atau e-wallet seperti pesanan biasa. Semua tagihan itu masuk ke halaman Pesanan. Butuh: Paket Pro atau Elite, peran pemilik toko (owner), akun pembayaran yang sudah terverifikasi, dan sistem yang bisa memanggil API dari server.

1.8.1Apa itu storefront?

Storefront adalah etalase jualan online-mu sendiri — bisa berupa toko di aplikasi kasir, aplikasi mobile custom, marketplace internal, atau sistem checkout yang kamu bangun sendiri di luar landing page Konvert. Bedanya dengan landing page Konvert: tampilannya, alur belanjanya, dan logikanya sepenuhnya kamu yang kontrol. Yang kamu tidak perlu bangun sendiri adalah bagian tersulitnya — menerima pembayaran dengan aman (QRIS, virtual account, e-wallet, kartu kredit) dan mengelola dana yang masuk.

Custom Checkout API menjembatani dua hal itu: storefront-mu tetap 100% milikmu, tapi bagian pembayarannya dipegang Konvert. Storefront-mu cukup mengirim daftar barang dan data pelanggan lewat API, Konvert yang urus sisanya — instrumen pembayaran, halaman bayar, webhook status, sampai dana masuk ke saldomu.

1.8.2Ringkasan

Landing page Konvert cocok kalau pelanggan datang, memilih produk, lalu checkout di halaman yang dibangun lewat builder Konvert. Tapi kadang tagihannya lahir di tempat lain: kamu sudah punya storefront sendiri, aplikasi kasir, sistem ERP, atau proses penawaran manual, dan yang kamu butuhkan cuma satu hal — cara menagih.

Itu yang dikerjakan Custom Checkout API. Storefront atau sistemmu mengirim daftar barang dan data pelanggan ke Konvert, Konvert mengembalikan link pembayaran. Kamu kirim link itu ke pelanggan lewat WhatsApp, email, atau apa pun — atau, kalau kamu pilih Opsi 2 di bawah, bangun sendiri tampilan pembayarannya tanpa pelanggan pernah meninggalkan storefront-mu. Begitu dibayar, tagihannya muncul di halaman Pesanan persis seperti pesanan lain — lengkap dengan status, biaya, dan dana bersih yang masuk ke saldomu.

Isi tagihannya bebas. Tidak perlu dibuat dulu sebagai produk di Konvert: nama barang, jumlah, dan harganya kamu tentukan sendiri saat memanggil API.

Fitur paket Pro & Elite. Custom Checkout API tidak tersedia di paket Free maupun Starter. Menunya tetap terlihat di Pengaturan, tapi isinya ajakan upgrade sampai tokomu pindah ke paket Pro atau Elite.


1.8.3Dua cara menerima pembayaran

Ada dua cara memakai Custom Checkout API, tergantung seberapa jauh kamu mau membangun checkout sendiri.

Opsi 1 — halaman pembayaran Konvert. Ini cara paling gampang, dan yang dijelaskan di langkah-langkah di bawah. Kamu memanggil endpoint pembuatan tagihan, Konvert mengembalikan payment_url, dan kamu kirim link itu ke pelanggan lewat WhatsApp, email, atau apa pun. Pelanggan membuka link itu dan menyelesaikan pembayarannya di halaman pembayaran Konvert sendiri, di subdomain tokomu — bukan halaman gateway pihak ketiga. Tidak ada tampilan yang perlu kamu bangun.

Opsi 2 — checkout sendiri. Kalau kamu mau pelanggan tidak pernah meninggalkan halamanmu sama sekali, kamu bisa membangun sendiri tampilan pemilihan metode bayar dan menyelesaikan pembayarannya lewat API penuh. Ini butuh lebih banyak kerja di sisi sistemmu, dan dijelaskan di langkah 6.

Pilih Opsi 1 kalau kamu cuma butuh cara menagih. Pilih Opsi 2 kalau pelanggan tidak boleh pernah keluar dari situsmu sendiri.


1.8.41. Buka menu Custom Checkout API

Masuk ke Pengaturan, lalu pilih Custom Checkout API di bagian Pengaturan Toko.

Menu ini ada di bagian Pengaturan Toko, bukan Pengaturan Umum, karena semuanya melekat per toko: tiap toko punya API key dan tarifnya sendiri. Key yang dibuat di satu toko tidak bisa dipakai untuk toko lain. Kalau kamu punya beberapa toko, ganti toko aktif dulu lewat pemilih toko di atas daftar menu.

Halaman Pengaturan Custom Checkout API dengan kartu biaya, daftar API key, dan daftar endpoint

Halaman ini isinya empat bagian:

  • Biaya Konvert — berapa yang Konvert ambil dari setiap tagihan yang dibayar
  • Watermark Konvert — atur apakah tulisan "Powered by Konvert" muncul di halaman pembayaran (lihat di bawah)
  • API key — kunci yang dipakai sistemmu untuk memanggil API, khusus toko ini
  • Endpoint — dokumentasi API lengkap: autentikasi, isi permintaan, contoh balasan, dan kode error

Bagian Endpoint memuat referensi penuh: setiap field beserta tipe dan batasnya, contoh permintaan yang bisa langsung disalin, bentuk balasan asli, dan daftar kode error. Isinya dibagi jadi beberapa bagian yang bisa diklik satu per satu — Autentikasi, enam endpoint (buat tagihan, daftar tagihan, detail tagihan, batalkan tagihan, metode pembayaran, dan pilih metode bayar), Webhook, dan Kode error — dan semuanya tertutup secara default. Klik judul bagian yang kamu butuhkan untuk membukanya.

Tabel isi permintaan dan contoh permintaan di dokumentasi endpoint

Catatan: Menu ini hanya muncul untuk pemilik toko. Anggota tim dengan peran lain tidak melihatnya.

Watermark Konvert di halaman pembayaran

Secara default, halaman pembayaran yang dibuka pelanggan (link dari payment_url) menampilkan tulisan kecil "Powered by Konvert" di bagian bawah.

Screenshot akan ditambahkan

kartu Watermark Konvert di halaman Pengaturan → Custom Checkout API, dengan tombol switch

  • Paket Pro — watermark selalu tampil. Tombol switch-nya nonaktif (abu-abu), dengan keterangan bahwa mematikan watermark cuma tersedia di paket Elite. (Paket Free dan Starter belum bisa memakai Custom Checkout API sama sekali, jadi kartu ini tidak muncul.)
  • Paket Elite — kamu bisa mematikan watermark lewat tombol switch di kartu ini. Perubahan berlaku langsung untuk halaman pembayaran yang dibuka pelanggan sesudahnya.

Kalau paket Elite-mu berakhir setelah watermark dimatikan. Watermark otomatis tampil lagi di halaman pembayaran, walaupun tombol switch-nya masih tersimpan dalam posisi mati — jadi tokomu tidak pernah kehilangan atribusi Konvert hanya karena langganan lupa diperpanjang. Tombol switch-nya tetap bisa kamu nyalakan ulang secara manual (supaya preferensinya sudah benar begitu Elite aktif lagi), tapi tidak bisa dimatikan lagi sampai paket Elite aktif kembali.


1.8.52. Pahami biayanya dulu

Konvert mengambil 2,5% dari setiap tagihan yang dibayar lewat Custom Checkout API. Tarif ini sama untuk semua paket — Free, Starter, Pro, maupun Elite.

Yang perlu diperhatikan: 2,5% itu dihitung dari nilai tagihan setelah dipotong biaya payment gateway, bukan dari nilai kotor. Biaya payment gateway sendiri tergantung metode bayar yang dipilih pelanggan — QRIS beda dengan virtual account, virtual account beda antar bank.

Contoh nyata, tagihan Rp1.000.000 dibayar lewat QRIS:

Komponen Jumlah
Nilai tagihan Rp1.000.000
Biaya payment gateway Rp11.817
Biaya Konvert (2,5%) Rp24.825
Masuk ke saldomu Rp963.358

Biaya payment gateway itu dua komponen: biaya tarik dana dari metode bayarnya (QRIS 0,7%) plus biaya transfer bagi hasil ke rekening tokomu (0,5%, maksimal Rp5.000).

Kalau tokomu punya kesepakatan tarif khusus, angka di kartu Biaya Konvert akan berubah mengikuti kesepakatan itu dan diberi label Nego. Tarif khusus ini diatur oleh tim Konvert, bukan dari dashboard toko.

Tarif Custom Checkout API berdiri sendiri. Kalau tokomu sudah punya tarif khusus untuk pesanan landing page, tarif itu tidak otomatis berlaku untuk Custom Checkout API, dan sebaliknya. Keduanya kesepakatan terpisah.


1.8.63. Buat API key

Klik Buat key, lalu isi namanya. Pakai nama yang menjelaskan sistem mana yang memakainya — nanti kalau ada masalah, kamu tahu key mana yang harus dicabut.

Jendela buat API key dengan kolom nama key dan pilihan masa berlaku

Pilih Masa berlaku. Kalau ragu, pilih tanpa batas waktu dulu; key selalu bisa dicabut kapan saja.

Klik Buat key, dan kuncinya muncul.

Jendela key dibuat, menampilkan kunci sekali saja sebelum ditutup

Salin sekarang juga. Di jendela ini kuncinya tampil utuh — salin dan simpan sebelum ditutup. Kalau nanti hilang, kunci yang sama masih bisa diambil lagi dari daftar key (lihat catatan di bawah), tapi jangan mengandalkan itu.

Simpan kunci itu di tempat rahasia sistemmu (environment variable atau secret manager), bukan di dalam kode yang bisa dibuka orang lain.

Kunci ini bisa dilihat lagi — hati-hati. Di daftar key, barisnya berlabel API key dan disamarkan secara default, misalnya lpb_sk_••••. Klik ikon mata di sampingnya dan Konvert menampilkan kunci utuhnya, bukan sekadar penanda; tombol salin di sebelahnya juga menyalin kunci utuh. Yang dipakai mencocokkan permintaan tetap sidik jari acak (hash), tapi salinan kunci aslinya ikut disimpan terenkripsi supaya bisa disalin ulang. Artinya: siapa pun yang bisa membuka Pengaturan tokomu bisa membaca kunci itu — perlakukan akses dashboard seketat kunci itu sendiri. Key lama yang dibuat sebelum fitur ini ada tidak tersimpan, jadi ikon matanya akan menjawab bahwa key tidak bisa dipulihkan.

Key ini untuk server, bukan browser. Siapa pun yang punya kuncinya bisa membuat tagihan atas nama tokomu. Jangan pernah menaruhnya di kode website, aplikasi mobile, atau tempat lain yang bisa dibaca pengunjung.

Key yang dibuat di halaman ini hanya bisa tiga hal: membuat, membaca, dan membatalkan tagihan. Key ini tidak bisa menyentuh produk, landing page, atau data lain di tokomu.

Ada batas jumlah permintaan. Secara default tiap key dibatasi 60 permintaan per menit. Kalau terlampaui, permintaanmu ditolak dengan status 429 — Konvert tidak mengirim header khusus penanda sisa kuota, jadi atur sendiri jeda antar panggilan di sistemmu alih-alih mengandalkan header dari balasannya.

Klik nama salah satu key untuk membuka halaman statistiknya, di Pengaturan → Custom Checkout API → (nama key).

Halaman statistik satu API key: total permintaan, grafik 30 hari, rincian per endpoint

Halaman ini isinya:

  • Tiga kartu ringkasan di atas — total permintaan, jumlah permintaan 30 hari terakhir, dan kapan key terakhir dipakai
  • Grafik batang harian untuk 30 hari terakhir
  • Rincian per endpoint — jumlah permintaan tiap endpoint, ditampilkan dalam bentuk batang
  • Panel Hasil permintaan — memecah permintaan jadi Berhasil, Ditolak, dan Gagal, lengkap dengan persentase masing-masing
  • Kartu Detail key — kapan dibuat, berlaku sampai kapan, batas laju, izin, dan ID key

Total permintaan dihitung sejak key itu dibuat, tapi riwayat rinci (grafik harian dan rincian per endpoint) hanya disimpan 90 hari. Jadi key lama bisa punya total besar sementara grafiknya cuma menampilkan sebagian pemakaian terakhir.


1.8.74. Kirim tagihan pertama

Sistemmu memanggil endpoint pembuatan tagihan, dengan kunci tadi di header Authorization.

curl -X POST https://konvert.id/api/v1/invoices \
  -H "Authorization: Bearer KUNCI_KAMU_DI_SINI" \
  -H "Content-Type: application/json" \
  -d '{
    "external_id": "TAGIHAN-2026-001",
    "customer": {
      "name": "Andi Pratama",
      "email": "andi@email.com",
      "phone": "08123456789"
    },
    "items": [
      { "name": "Jasa desain logo", "quantity": 1, "unit_price": 1000000 }
    ],
    "expires_in_hours": 24,
    "payment_methods": ["va_bca", "va_bni"]
  }'

Yang perlu diketahui soal isiannya:

  • external_id — nomor tagihan versi sistemmu sendiri. Wajib unik. Ini juga pengaman: kalau permintaan yang sama terkirim dua kali (misalnya jaringan putus lalu sistemmu mengulang), Konvert mengembalikan tagihan yang sudah ada, bukan membuat tagihan kedua.
  • items — daftar barang bebas, maksimal 50 baris. Nama, jumlah, dan harga satuan kamu tentukan sendiri.
  • Total dihitung Konvert, bukan dikirim olehmu. Tidak ada kolom "total" yang bisa diisi, jadi total tagihan tidak akan pernah berbeda dari daftar barangnya. Harga satuan ditulis dalam rupiah utuh (bukan sen). Batas Rp10.000 dan Rp500.000.000 berlaku untuk total tagihannya, bukan per baris barang — beberapa barang murah boleh, asal totalnya tidak di bawah Rp10.000.
  • expires_in_hours — berapa lama tagihannya sendiri berlaku sebelum kedaluwarsa (default 24 jam, maksimal 168 jam/7 hari). Ini beda dari masa berlaku instrumen pembayaran (QRIS, VA, dll.) yang dipilih pelanggan — soal itu ada di bagian 6.
  • payment_methods — opsional. Batasi tagihan ini saja ke sebagian metode, misalnya tagihan perusahaan yang hanya boleh dibayar lewat virtual account. Kalau tidak diisi, pelanggan bisa memakai semua metode yang tokomu nyalakan. Batasan ini berlaku di mana-mana: halaman pembayaran Konvert hanya menampilkan metode yang kamu izinkan, dan /pay menolak metode di luar daftarnya.

payment_methods hanya bisa mempersempit, tidak pernah menambah. Menyebut metode yang tokomu matikan — atau yang payment gateway-nya memang tidak punya kanalnya — ditolak dengan 422 METHOD_NOT_AVAILABLE saat tagihan dibuat, lengkap dengan daftar metode yang sebenarnya bisa dipakai. Ini disengaja: lebih baik gagal saat kamu membuat tagihan daripada tagihannya terbit rapi lalu tidak ada satu pun metode yang bisa dipakai pelanggan.

Mengulang external_id dengan isi yang berbeda akan ditolak, bukan diam-diam ditukar. Kirim ulang external_id yang sama dengan isi persis sama (misalnya karena jaringan putus lalu sistemmu mencoba lagi) → kamu tetap dapat tagihan pertama, status 200. Tapi kalau total, items, atau customer (nama/email/telepon) pada permintaan yang diulang berbeda dari yang pertama — misalnya kamu memperbaiki harga yang salah atau membetulkan email pelanggan yang salah ketik — Konvert menolaknya dengan 409 IDEMPOTENCY_CONFLICT dan menyebutkan field mana yang beda, alih-alih diam-diam mempertahankan tagihan lama yang sudah usang. Perbedaan di notes, expires_in_hours, success_redirect_url, atau payment_methods tidak dianggap konflik, karena tidak mengubah jumlah yang harus dibayar — tagihan lama tetap dikembalikan apa adanya. Kalau tagihannya memang berbeda, pakai external_id baru.

Balasannya berisi payment_url. Itulah link yang kamu kirim ke pelanggan. Pelanggan membukanya, memilih metode bayar, dan menyelesaikan pembayaran.

success_redirect_url bukan sinyal pembayaran berhasil. URL ini dipakai untuk mengarahkan pelanggan kembali ke situsmu — tapi dipakai sama persis baik pembayarannya berhasil, gagal, maupun kedaluwarsa. Redirect ini terjadi di browser pelanggan, sebelum Konvert sempat memastikan hasil pembayarannya ke servermu. Jangan pernah menganggap pesanan lunas hanya karena pelanggan sampai di URL ini — selalu cek status sebenarnya lewat GET /api/v1/invoices/{id} atau tunggu webhook invoice.paid sebelum memproses/mengirim barang. Mengandalkan redirect saja cepat atau lambat akan membuatmu mengirim barang untuk pembayaran yang sebenarnya gagal.


1.8.85. Pantau tagihannya

Semua tagihan dari Custom Checkout API masuk ke halaman Pesanan. Pakai filter Tipe: Invoice untuk melihat khusus tagihan dari API.

Halaman Pesanan difilter ke tipe Invoice, menampilkan biaya gateway, biaya Konvert, dan dana bersih per tagihan

Ciri tagihan dari Custom Checkout API: ada label INVOICE menempel di nomor pesanannya. Nomornya sendiri sama polanya dengan pesanan landing page (INV-...), jadi labelnya — bukan awalan nomornya — yang membedakan.

Di baris yang sudah lunas kamu bisa lihat rinciannya langsung: Gateway Fee, Konvert Fee, dan Net — dana bersih yang masuk ke saldomu. Selama badge est masih menempel di kolom Gateway Fee, angkanya masih perkiraan — Pivot tidak membuka biaya per transaksi lewat API, jadi Konvert menghitungnya dari tarif kontrak. Begitu biaya sebenarnya masuk, badge-nya hilang. Konvert Fee dan Net dikunci sejak pembayaran dibuat dan tidak ikut berubah. Kolom Pending Settlement di bagian atas menampung dana yang sudah terkumpul tapi belum bisa ditarik; biasanya butuh sekitar satu hari kerja sampai payment gateway menyelesaikan settlement.

Kalau mau memantau dari sistemmu sendiri tanpa membuka dashboard, tersedia tiga endpoint lain:

Kegunaan Endpoint
Lihat daftar tagihan GET /api/v1/invoices
Lihat detail satu tagihan GET /api/v1/invoices/{id}
Batalkan tagihan POST /api/v1/invoices/{id}/cancel

Tagihan hanya bisa dibatalkan selama statusnya masih menunggu pembayaran, dan selama belum ada pembayaran yang sedang berjalan. Begitu pelanggan membuka kode QRIS, virtual account, atau instrumen lain apa pun, pembatalan ditolak walaupun tagihannya masih berstatus pending — instrumen yang sudah terbit tidak bisa ditarik kembali, jadi tagihannya harus dibiarkan kedaluwarsa sendiri. Tagihan yang sudah lunas juga tidak bisa dibatalkan lewat API.

Untuk metode yang mengarahkan pelanggan ke halaman penyedia (kartu kredit dan e-wallet), pembatalan juga ditolak sampai kira-kira satu jam sejak pelanggan memulai pembayaran. Alasannya: pembayaran seperti itu tidak menerbitkan instrumen yang punya masa berlaku, jadi kami tidak bisa tahu pelanggan sudah selesai atau belum — dan membatalkan saat pelanggan masih di halaman pembayaran berarti uangnya tetap masuk untuk tagihan yang sudah kamu batalkan.

GET /api/v1/invoices/{id} membaca status yang sudah tersimpan di Konvert, bukan mengecek langsung ke payment gateway setiap kali dipanggil. Statusnya baru berubah begitu Konvert sendiri memprosesnya — webhook dari payment gateway, sweep kedaluwarsa, atau pemanggilan /cancel-mu sendiri — jadi pengecekan tepat setelah pembayaran kadang masih menunjukkan pending selama beberapa detik. Kalau kamu polling, mulai dari jeda sekitar 5–10 detik lalu naikkan bertahap (misalnya dua kali lipat, sampai maksimal sekitar 1 menit) semakin lama tagihannya menunggu.

Kalau itu terlanjur terjadi, kamu tetap diberi tahu. Pembayaran yang mendarat di tagihan yang sudah dibatalkan tetap kami catat — uangnya nyata, tidak kami buang diam-diam. Tandanya ada di webhook invoice.paid: field previous_status akan berisi cancelled. Perlakukan itu sebagai perlu tindakan, bukan penjualan biasa — kirim barangnya, atau refund.

Atau, biar Konvert yang kasih tahu (webhook)

Daripada polling terus-terusan, kamu bisa daftarkan URL di menu Pengaturan → Webhook dan Konvert akan mengirim POST JSON ke server-mu setiap kali status tagihan berubah. Cara mendaftarkan endpoint dan mengambil signing secret-nya dijelaskan lengkap di panduan Integrasi (Meta Pixel, TikTok, GTM, API Key & Webhook) — bagian ini hanya membahas yang khusus berlaku untuk tagihan dari Custom Checkout API.

Event yang dikirim untuk tagihan Custom Checkout API:

Event Kapan terkirim
invoice.created Tagihan baru dibuat, belum dibayar
invoice.paid Tagihan sudah lunas
invoice.expired Tagihan kedaluwarsa tanpa dibayar
invoice.cancelled Tagihan dibatalkan lewat /cancel
invoice.failed Percobaan pembayaran gagal
invoice.refunded Dana tagihan yang sudah lunas dikembalikan (refund)

Semua perubahan status di atas sekarang mengirim webhook — kamu tidak perlu lagi polling GET /api/v1/invoices/{id} khusus untuk menangkap kejadian gagal atau refund. Polling tetap berguna kalau kamu cuma butuh cek status di satu titik waktu tertentu.

Setiap pengiriman membawa empat header:

X-Konvert-Event: invoice.paid
X-Konvert-Delivery: 8f14e45f-ceea-467e-bd3d-6c7d6b1a9f00
X-Konvert-Timestamp: 1798434000
X-Konvert-Signature: v1=5257a869e7bfbe00e5119ac2ed4c1d1c...

Cara memverifikasi tanda tangannya: gabungkan {timestamp}.{isi request mentah}, hitung HMAC-SHA256-nya pakai signing secret webhook-mu, ubah ke hex, lalu bandingkan dengan nilai hex setelah v1= di X-Konvert-Signature — pakai constant-time comparison, bukan === biasa. Penting: pakai isi request mentah persis seperti yang diterima, bukan hasil JSON.stringify() ulang dari body yang sudah di-parse — spasinya beda dan tanda tangannya jadi tidak cocok.

const rawBody = await request.text(); // mentah, sebelum JSON.parse
const timestamp = request.headers.get('x-konvert-timestamp');
const [, receivedHex] = request.headers.get('x-konvert-signature').split('v1=');

const expectedHex = crypto
  .createHmac('sha256', WEBHOOK_SIGNING_SECRET)
  .update(`${timestamp}.${rawBody}`)
  .digest('hex');

const valid = crypto.timingSafeEqual(
  Buffer.from(expectedHex, 'hex'),
  Buffer.from(receivedHex, 'hex'),
);

Kalau endpoint-mu tidak membalas 2xx dalam 10 detik, Konvert mencoba ulang pengiriman itu sampai 5 kali total, dengan jeda 1 menit, 5 menit, 30 menit, lalu 2 jam — setelah percobaan ke-5 tetap gagal, pengiriman itu ditandai gagal dan tidak dicoba lagi. Setiap percobaan yang gagal (bukan cuma tiap pengiriman yang menyerah total) menambah hitungan gagal berturut-turut pada endpoint-nya; begitu hitungan itu mencapai 10, endpoint otomatis dinonaktifkan (muncul sebagai Nonaktif otomatis) dan harus diaktifkan lagi secara manual dari menu Webhook. Karena satu pengiriman yang gagal total saja sudah memakai 5 percobaan, cukup 2 pengiriman yang gagal — bukan 10 pengiriman terpisah — untuk memicu nonaktif otomatis.

Hindari memproses event dua kali. X-Konvert-Delivery tetap sama di setiap percobaan ulang pengiriman yang sama, termasuk saat dikirim ulang manual lewat dashboard. Simpan id yang sudah kamu proses dan lewati kalau muncul lagi.

Contoh payload lengkap untuk invoice.paid:

{
  "id": "8f14e45f-ceea-467e-bd3d-6c7d6b1a9f00",
  "event": "invoice.paid",
  "created_at": "2026-08-27T14:22:11.000Z",
  "data": {
    "order_number": "INV-4PRGQNBD-270826",
    "external_id": "TAGIHAN-2026-001",
    "status": "paid",
    "previous_status": "pending",
    "total": 1000000,
    "currency": "IDR",
    "customer": { "name": "Andi Pratama", "email": "andi@email.com", "phone": "08123456789" },
    "items": [
      {
        "product_id": "SKU-123",
        "name": "Jasa desain logo",
        "image_url": "https://cdn.tokokamu.id/logo.png",
        "unit_price": 1000000,
        "quantity": 1,
        "subtotal": 1000000
      }
    ],
    "payment_method": "qris",
    "expires_at": "2026-08-28T03:16:59.000Z",
    "created_at": "2026-08-27T03:16:59.000Z",
    "paid_at": "2026-08-27T14:22:10.000Z"
  }
}

1.8.96. Opsi 2: bangun checkout sendiri

Langkah ini cuma perlu diikuti kalau kamu memilih Opsi 2 — pelanggan menyelesaikan pembayaran tanpa pernah meninggalkan halamanmu. Kalau kamu pakai payment_url seperti langkah 4, lewati bagian ini.

a. Tanya metode apa saja yang aktif

curl https://konvert.id/api/v1/payment-methods \
  -H "Authorization: Bearer KUNCI_KAMU_DI_SINI"

Balasannya daftar metode yang tokomu sudah nyalakan, masing-masing dengan type (qr, virtual_account, ewallet, atau card) dan completion (inline atau redirect). Pakai daftar ini untuk menampilkan pilihan metode bayar di halamanmu sendiri.

b. Minta instrumen pembayaran

Setelah pelanggan memilih metodenya, panggil:

curl -X POST https://konvert.id/api/v1/invoices/{id}/pay \
  -H "Authorization: Bearer KUNCI_KAMU_DI_SINI" \
  -H "Content-Type: application/json" \
  -d '{ "method": "qris" }'

Bentuk balasannya beda tergantung metodenya:

  • QRIS — instrument.type nya qr, isinya qr_string mentah. Kodenya kamu render sendiri di halamanmu.
  • Virtual account — instrument.type nya virtual_account, isinya bank dan account_number. Tampilkan sebagai instruksi transfer.
  • E-wallet dan kartu kredit — instrument.type nya redirect, isinya url. Arahkan pelanggan ke sana untuk menyelesaikan pembayaran.

Balasannya juga membawa expires_at — dan ini beda dari expires_in_hours yang kamu kirim saat membuat tagihan. expires_in_hours itu masa berlaku tagihannya; expires_at di sini masa berlaku instrumen pembayaran yang baru diterbitkan. Instrumen ini tidak akan pernah melewati masa berlaku tagihannya — yang dipakai selalu mana yang lebih pendek antara sisa waktu tagihan dan batas dari payment gateway-nya:

Metode Masa berlaku instrumen
QRIS Maksimal 60 menit, atau sisa waktu tagihan kalau itu lebih pendek
DANA Maksimal 15 menit, atau sisa waktu tagihan kalau itu lebih pendek
Virtual account (semua bank), kartu kredit Maksimal 24 jam, atau sisa waktu tagihan kalau itu lebih pendek

Perhatikan baris terakhir kalau kamu memakai expires_in_hours lebih dari 24: tagihannya memang berlaku sampai 7 hari, tapi virtual account atau halaman kartunya mati setelah 24 jam. Panggil /pay lagi untuk instrumen baru selama tagihannya masih pending.

Jangan asumsikan pelanggan masih punya waktu sampai tagihan kedaluwarsa — selalu cek expires_at di balasan /pay, bukan expires_in_hours di tagihannya.

Kalau tagihan tinggal kurang dari 5 menit lagi saat kamu memanggil /pay, Konvert menolak dengan kode error INVOICE_EXPIRING_SOON — bukannya menerbitkan instrumen yang keburu kedaluwarsa sebelum sempat dibayar. Buat tagihan baru kalau ini terjadi.

c. Pantau pembayarannya

Sama seperti Opsi 1: cek statusnya lewat GET /api/v1/invoices/{id}, atau tunggu webhook invoice.paid.

Tiga hal yang perlu kamu tahu

Kartu kredit tidak pernah inline. Metode credit_card selalu mengembalikan instrument.type redirect, ke halaman kartu milik payment gateway. Kamu tidak bisa membangun form kartu sendiri.

Instrumen QRIS dan virtual account yang sudah terbit tidak bisa dibatalkan. Memanggil /pay lagi dengan metode yang sama selama instrumen itu masih berlaku tidak menerbitkan yang baru — kamu menerima instrumen yang sama. Ini sengaja, bukan cache: kode QRIS dan nomor VA yang sudah keluar tidak bisa ditarik kembali oleh payment gateway, jadi /cancel pun ikut menolak selama instrumen itu masih berlaku (lihat bagian 5). E-wallet dan kartu kredit beda: keduanya cuma link ke halaman penyedia, tidak ada instrumen yang tersimpan, jadi /pay selalu membuat sesi pembayaran baru.

Ganti metode terkunci sementara. Selama satu instrumen QRIS atau virtual account masih berlaku, meminta metode lain akan ditolak dengan kode error METHOD_LOCKED sampai waktu switch_locked_until lewat. Alasannya sama seperti soal virtual account di atas: instrumen yang sudah terbit tidak bisa ditarik, jadi kalau ganti metode dibebaskan begitu saja, satu tagihan bisa punya beberapa instrumen yang sama-sama bisa dibayar sekaligus. Untuk e-wallet dan kartu kredit tidak ada kunci ini — switch_locked_until selalu null.


1.8.10Kalau ada masalah

Muncul pesan izin permission invoices Key yang kamu pakai bukan key dari halaman Custom Checkout API. Key lama dari halaman API Keys tidak punya izin untuk tagihan. Buat key baru lewat menu Pengaturan → Custom Checkout API.

Muncul pesan toko belum siap menerima pesanan Verifikasi akun pembayaran belum selesai. Selesaikan dulu lewat menu Pengaturan → Verifikasi KYC, baru coba lagi.

Tagihan yang sama terbuat dua kali Cek external_id-nya. Kalau nilainya berbeda, Konvert menganggapnya dua tagihan berbeda. Pakai nomor tagihan dari sistemmu sebagai external_id, jangan angka acak yang baru setiap kali dipanggil.

Muncul pesan terlalu banyak tagihan tertunda Ada batas jumlah tagihan belum dibayar per pelanggan. Selesaikan atau batalkan tagihan lama pelanggan itu sebelum membuat yang baru.

Key-nya hilang Coba dulu ikon mata di baris key itu; untuk key yang dibuat belakangan, kunci utuhnya masih bisa ditampilkan dan disalin. Kalau muncul pesan tidak bisa dipulihkan, key itu dibuat sebelum fitur salin ulang ada: cabut lewat ikon tempat sampah, buat key baru, perbarui di sistemmu.

Key toko lain tidak muncul / key-ku hilang dari daftar Daftar key mengikuti toko yang sedang aktif. Ganti toko aktif lewat pemilih toko di atas daftar menu Pengaturan.

Muncul pesan metode tidak tersedia Ada dua sebabnya. Pertama, kode metodenya belum dinyalakan untuk toko ini — cek lewat GET /api/v1/payment-methods. Kedua, tagihannya sendiri dibuat dengan payment_methods yang lebih sempit; pesan errornya menyebutkan metode mana saja yang diizinkan untuk tagihan itu. Untuk kasus kedua, GET /api/v1/payment-methods tetap menampilkan metodenya — batasannya ada di tagihan, bukan di toko.

Muncul pesan metode terkunci Tagihan ini sudah punya instrumen QRIS atau virtual account lain yang masih berlaku. Tunggu sampai switch_locked_until lewat, atau biarkan pelanggan menyelesaikan pembayaran dengan metode yang sudah terbit.

Muncul pesan tagihan tidak bisa dibayar Tagihan ini sudah tidak berstatus pending — sudah lunas, kedaluwarsa, atau dibatalkan. Instrumen pembayaran baru cuma bisa diminta untuk tagihan yang masih menunggu pembayaran.

Muncul pesan tagihan akan segera kedaluwarsa (INVOICE_EXPIRING_SOON) Sisa waktu tagihan sudah kurang dari 5 menit saat kamu memanggil /pay — tidak cukup untuk pelanggan sempat membayar. Buat tagihan baru dengan expires_in_hours yang lebih longgar.


1.8.11Selanjutnya

  • Kelola Orders — mengelola pesanan dan tagihan yang sudah masuk
  • Tarik Saldo — mencairkan dana yang sudah settle
  • Webhook — supaya sistemmu diberi tahu otomatis begitu tagihan dibayar

Siap buat landing page pertamamu?

Daftar Konvert gratis dan mulai jualan online hari ini.

Daftar Sekarang