← Semua tutorial

TUTORIAL 4 / 12

Buat checkout. Verifikasi pembayarannya.

Buat invoice dari backend-mu dan proses notifikasi pembayaran terverifikasi.

Yang akan kamu miliki

Alur pembayaran sisi server dengan perlindungan percobaan ulang dan duplikat.

1. Siapkan ID dan akses

Mulai dengan toko yang diaktifkan dan metode pembayaran yang sudah diuji. Di Pengaturan → Akses API, buat kredensial baca/tulis yang dibatasi ke proyek yang diperlukan. Simpan token di backend-mu, jangan pernah di kode browser atau repositori publik.

Salin ID API Proyek dan ID API Toko dari kotak Dasar → ID API toko. Ini adalah UUID, bukan pengenal proyek yang mudah dibaca atau nomor pesananmu. Gunakan nama host API-mu sendiri.

Di Toko → IPN, buat rahasia penandatanganan sebelum memberikan ipn_url. Penerima HTTPS-mu harus dapat dijangkau dari VPS merchant.

2. Buat invoice

Ganti placeholder dan kirim permintaan ini dari backend-mu. Jumlah berupa string desimal, bukan perhitungan floating-point.

curl --fail-with-body --request POST \
  'https://api.example.com/v1/projects/YOUR_PROJECT_ID/stores/YOUR_STORE_ID/invoices' \
  --header 'Authorization: Bearer YOUR_MERCHANT_API_TOKEN' \
  --header 'Content-Type: application/json' \
  --header 'Idempotency-Key: order-1042-attempt-1' \
  --data '{
  "amount": "10.00",
  "currency": "EUR",
  "order_id": "order-1042",
  "description": "Example order",
  "ipn_url": "https://shop.example.com/payments/wholly-ipn",
  "metadata": {
    "cart_id": "cart-1042"
  }
}'

Simpan data.invoice_id bersama pesananmu, lalu arahkan pelanggan ke links.checkout. Gunakan kunci idempotensi unik untuk percobaan pembayaran baru. Jika terjadi timeout, coba ulang dengan kredensial, kunci, dan byte badan yang persis sama.

Tidak menyertakan payment_methods memakai metode yang diterima toko. Kamu dapat mempersempitnya per invoice dengan slug chain dan ticker; ini tidak pernah mengaktifkan aset yang tidak diterima. Semua kolom permintaan dan contoh respons →

3. Pilih IPN, webhook, atau keduanya

IPN mengikuti siklus hidup invoice. Atur URL IPN bawaan toko, atau ganti dengan ipn_url untuk suatu invoice. Webhook mendaftarkan endpoint untuk event tertentu, misalnya invoice.settled.

Keduanya memakai format tanda tangan yang sama, tetapi rahasia berbeda: IPN memakai rahasia IPN toko; setiap endpoint webhook memiliki rahasianya sendiri. Keduanya tidak memakai token bearer API untuk penandatanganan.

Jika keduanya mengirim ke aplikasimu, perkirakan adanya notifikasi yang tumpang tindih. Jangan mengkreditkan pesanan dua kali.

4. Verifikasi dan simpan notifikasi

  1. Baca badan permintaan mentah yang persis sebelum parsing JSON. Verifikasi Wholly-Signature dengan rahasia yang cocok serta pemeriksaan stempel waktu/replay. SDK resmi menyediakan verifier.
  2. Validasi identitas proyek, toko, invoice, dan event pada badan bertanda tangan. Header pengiriman tanpa tanda tangan bukan sumber autentikasi.
  3. Simpan event secara persisten dengan event_idyang unik, lalu segera kembalikan HTTP 2xx. Proses pesanan dalam pekerja latar belakang.
  4. Ambil invoice saat ini dari host API yang kamu konfigurasi, bukan host sembarang yang diberikan dalam permintaan. Bandingkan proyek, toko, jumlah, mata uang, dan referensi pesanan yang kamu simpan.

PHP · Python · JavaScript / TypeScript · Spesifikasi tanda tangan dan contoh penerima

5. Penuhi sekali, saat penyelesaian

Untuk penerima berbasis event , tangani event_type = invoice.settled, lalu verifikasi status = settled saat ini dan kebijakan pengecualianmu. Penuhi sekali dalam transaksi database/batasan pesanan unik.

Jenis event dan status berbeda

Chain yang mencapai finalitas dengan cepat dapat mengirim keduanya payment.received dan invoice.settled dengan status = settled. Pada chain lain, payment.received mungkin masih menyatakan processing. Kedua alur tersebut bukan kesalahan.

Hilangkan event duplikat berdasarkan event_id, bukan hanya sequence: jenis event berbeda dapat berbagi sequence. Handler SDK berbasis status justru menggabungkan revisi invoice dan memeriksa status tanpa memandang jenis event. Jangan campurkan penggabungan tersebut dengan filter jenis event. Kedua pendekatan tetap membutuhkan perlindungan duplikat tingkat pesanan.

Periksa requires_review dan penyelesaian manual sebelum memenuhi pesanan. amount_status = paid saja tidak membuktikan konfirmasi. Kolom aset dibayar tingkat teratas merangkum penyelesaian; payment_info menyimpan detail penerimaan dan data penawaran. Semua status, event, dan aturan pengecualian →

6. Uji percobaan ulang dan pemulihan

Uji pembayaran kecil, pengiriman duplikat, invoice kedaluwarsa, dan penerima yang sementara tidak tersedia. Memutar ulang event tidak boleh membuat kredit pesanan kedua. Tangani event yang datang tidak berurutan tanpa menimpa status yang lebih baru.

Periksa Toko → IPN / Webhook → Riwayat → Detail, atau bagian pengiriman pada Detail invoice. Kirim ulang memakai kembali event yang tercatat, bukan penyelesaian baru.

Kredit pemrosesan rendah menjeda IPN/webhook sementara pembayaran tetap berjalan. Rekonsiliasi pesanan tertunda melalui API dan tangani pengiriman yang masih tersimpan setelah pemulihan. Jangan pernah memenuhi pesanan berdasarkan pengalihan browser atau tangkapan layar pelanggan.