Integrasi API Payment Gateway Crypto: Invoice, Signature, dan Webhook

Panduan praktis untuk developer: satu request bertanda tangan untuk membuat invoice, satu endpoint webhook untuk menerima status, dan detail keamanan yang membuatnya bisa dipercaya.

Diperbarui: 5 menit baca
Integrasi API pembayaran crypto: request POST bertanda tangan membuat invoice dan respons webhook

Integrasi API pembayaran crypto membutuhkan dua bagian: request sisi server yang membuat invoice untuk setiap pesanan dan mengembalikan URL checkout, serta endpoint webhook yang menerima pembaruan status bertanda tangan ketika pembayaran terdeteksi dan terkonfirmasi di blockchain. Di mistKET keduanya adalah HTTPS dan JSON biasa, ditandatangani dengan HMAC-SHA256 — tanpa perlu SDK.

Panduan ini membahas alur lengkap beserta kodenya, lalu detail keamanan dan keandalan yang membedakan demo dari sistem produksi.

Arsitektur API payment gateway crypto dalam satu gambar

Setiap integrasi API pembayaran crypto mengikuti siklus lima langkah yang sama:

  1. Pesanan dibuat — backend Anda memanggil POST /api/v1/invoices dengan nominal, mata uang, ID pesanan, dan callback URL.
  2. Redirect — Anda mengarahkan pelanggan ke checkout_url yang dikembalikan.
  3. Pembayaran — pelanggan memilih jaringan dan membayar di halaman hosted; kurs dikunci selama masa berlaku invoice.
  4. Webhook — mistKET mengirim POST invoice.paid, invoice.confirmed, atau event pengecualian ke callback URL Anda.
  5. Pemenuhan — Anda memverifikasi signature, menandai pesanan lunas, lalu mengirimkannya.

API key, API secret, dan webhook secret diterbitkan di panel merchant setelah akun Anda disetujui. Referensi lengkapnya ada di panel pada menu API.

Langkah 1: tandatangani dan kirim request invoice

Setiap request API membawa tiga header. Signature-nya adalah hex HMAC-SHA256 dari string {timestamp}.{raw body} menggunakan API secret Anda:

POST /api/v1/invoices
X-Api-Key: mp_live_…
X-Api-Timestamp: 1760000000
X-Api-Signature: hex(hmac_sha256("{ts}.{body}", secret))

{
  "amount": "184.50",
  "currency": "USD",
  "order_id": "2041",
  "callback_url": "https://yourstore.com/webhooks/mistket"
}

Versi PHP minimalnya seperti ini:

$body = json_encode([
  'amount' => '184.50', 'currency' => 'USD',
  'order_id' => '2041',
  'callback_url' => 'https://yourstore.com/webhooks/mistket',
]);
$ts  = (string) time();
$sig = hash_hmac('sha256', $ts . '.' . $body, $apiSecret);
// send $body with X-Api-Key, X-Api-Timestamp, X-Api-Signature

Tandatangani byte yang persis sama dengan yang Anda kirim. Meng-encode ulang JSON setelah ditandatangani (spasi atau urutan key berbeda) adalah penyebab nomor satu kesalahan signature.

Langkah 2: redirect ke hosted checkout

Panggilan yang berhasil mengembalikan 201 Created dengan UUID invoice, statusnya (new), dan checkout_url. Simpan UUID bersama pesanan Anda lalu arahkan pelanggan. Halaman hosted menampilkan nominal pasti, kode QR, hitung mundur, dan status langsung, serta berfungsi di ponsel. Pelanggan memilih jaringan di sana — USDT di TRON, BNB Smart Chain, Ethereum atau Arbitrum, plus TRX, BNB dan ETH — kecuali Anda menetapkan jaringan di panggilan API.

Selain membuat invoice, API juga memungkinkan Anda menampilkan daftar, mengambil, dan membatalkan invoice, yang berguna saat isi keranjang berubah atau pesanan ditinggalkan.

Langkah 3: terima dan verifikasi webhook pembayaran crypto

Saat status berubah, mistKET mengirim JSON lewat POST ke callback URL Anda dengan header berikut:

HeaderFungsi
X-Mistket-EventNama event, mis. invoice.confirmed
X-Mistket-TimestampWaktu Unix yang dipakai dalam signature
X-Mistket-Signaturesha256= + HMAC-SHA256 dari {timestamp}.{raw body} dengan webhook secret Anda
X-Mistket-DeliveryID pengiriman, berguna untuk logging

Verifikasi di PHP:

$raw = file_get_contents('php://input');
$ts  = $_SERVER['HTTP_X_MISTKET_TIMESTAMP'] ?? '';
$got = $_SERVER['HTTP_X_MISTKET_SIGNATURE'] ?? '';
$exp = 'sha256=' . hash_hmac('sha256', $ts . '.' . $raw, $webhookSecret);
if (!hash_equals($exp, $got) || abs(time() - (int) $ts) > 300) {
  http_response_code(401); exit;
}
$event = json_decode($raw, true);

Gunakan perbandingan constant-time (hash_equals di PHP, hmac.compare_digest di Python, crypto.timingSafeEqual di Node.js) dan tolak timestamp yang sudah basi untuk mencegah replay. Untuk latar belakang primitifnya, lihat RFC 2104 (HMAC).

Poin penting: sumber kebenaran adalah webhook — bukan browser pelanggan yang kembali ke situs Anda. Jangan pernah menandai pesanan lunas tanpa signature yang valid.

Contoh payload webhook

Pembayaran terkonfirmasi tiba sebagai dokumen JSON yang ringkas. Field yang paling sering Anda pakai adalah event, ID pesanan, status, serta nominal yang diharapkan dan yang diterima:

POST https://yourstore.com/webhooks/mistket
X-Mistket-Event: invoice.confirmed
X-Mistket-Signature: sha256=…

{ "event": "invoice.confirmed", "data": {
  "order_id": "2041", "status": "confirmed",
  "pay_amount": "184.5", "paid_amount": "184.5",
  "pay_currency": "USDT", "network": "TRC20" } }

Simpan pay_currency dan network bersama pesanan. Keduanya berguna untuk akuntansi dan percakapan dukungan di kemudian hari.

Verifikasi di Node.js dan Python

Pemeriksaan yang sama dalam dua bahasa populer lainnya:

// Node.js (Express with express.raw for this route)
const exp = 'sha256=' + crypto.createHmac('sha256', secret)
  .update(ts + '.' + rawBody).digest('hex');
const ok = crypto.timingSafeEqual(Buffer.from(exp), Buffer.from(sig));

# Python
exp = 'sha256=' + hmac.new(secret.encode(), f'{ts}.{raw}'.encode(),
                           hashlib.sha256).hexdigest()
ok = hmac.compare_digest(exp, sig)

Di kedua kasus, pastikan Anda membaca raw request body. Banyak framework mem-parse JSON secara otomatis; setelah di-parse dan diserialisasi ulang, byte-nya tidak lagi cocok dengan signature.

Pengujian lokal

Webhook membutuhkan URL HTTPS publik, jadi laptop lokal tidak bisa menerimanya secara langsung. Dua opsi praktis: deploy handler ke server staging sejak awal, atau buka port lokal untuk sementara melalui tools tunneling. Buat invoice kecil yang mengarah ke callback staging, lalu periksa log pengiriman di dashboard merchant untuk melihat persis apa yang dikirim dan apa jawaban server Anda.

Usahakan memicu setiap jenis event setidaknya sekali selama pengujian: paid, confirmed, underpaid, dan expired. Jika setiap cabang handler sudah dijalankan dengan webhook sungguhan, hampir tidak ada lagi yang bisa mengejutkan Anda di produksi, dan Anda tahu persis seperti apa log pengiriman ketika ada masalah.

Langkah 4: tangani setiap status

Payload webhook berisi ID pesanan, status, nominal yang diharapkan (pay_amount), nominal yang diterima (paid_amount), mata uang, dan jaringan. Petakan setiap event ke sebuah aksi:

EventArtiAksi umum
invoice.paidTransfer terdeteksi, konfirmasi berjalanTampilkan "pembayaran diterima, sedang dikonfirmasi"
invoice.confirmedJumlah konfirmasi yang dibutuhkan tercapaiTandai lunas dan penuhi pesanan
invoice.underpaidDi bawah toleransi AndaTunggu top-up atau hubungi pelanggan
invoice.overpaidLebih dari yang diharapkanPenuhi pesanan; putuskan soal selisihnya
invoice.expiredTidak ada pembayaran valid tepat waktuLepaskan stok, biarkan pesanan belum dibayar

Toleransi kekurangan bayar, pembayaran sebagian yang mencapai total, dan pembayaran terlambat dalam masa tenggang ditangani oleh mistKET; kode Anda hanya bereaksi pada hasil akhirnya.

Keandalan: retry dan idempotensi

Jika endpoint Anda down atau mengembalikan error, webhook dikirim ulang dengan back-off selama berjam-jam. Artinya, kadang Anda akan menerima event yang sama dua kali. Buat handler Anda idempoten berdasarkan ID pesanan:

  • Balas dengan 2xx secepatnya, lalu proses pekerjaan berat di antrean.
  • Sebelum memenuhi pesanan, cek apakah pesanan sudah ditandai lunas.
  • Catat ID pengiriman dan event agar masalah bisa ditelusuri; dashboard juga menampilkan log pengiriman webhook.

Checklist keamanan

Integrasi API pembayaran crypto menangani uang, jadi perlakukan seperti kode pembayaran lainnya:

  • Simpan API secret dan webhook secret hanya di sisi server — jangan pernah di kode front-end atau aplikasi mobile.
  • Batasi akses API dengan IP allow-list di panel.
  • Tolak request di luar jendela timestamp yang pendek.
  • Atur masa berlaku invoice sesuai logika reservasi stok Anda.
  • Perlakukan nominal sebagai string atau decimal, jangan pernah float.
Tips: sebelum live, bayar sendiri satu invoice kecil, sengaja kirim nominal kurang untuk melihat alur underpaid, dan biarkan satu invoice kedaluwarsa. Anda akan menguji setiap cabang dalam waktu kurang dari satu jam.

Langkah selanjutnya

Itulah integrasi API pembayaran crypto secara lengkap: satu request bertanda tangan, satu webhook terverifikasi, dan peta status. Untuk sudut pandang non-developer atas alur yang sama, baca cara menerima pembayaran crypto di website. Membangun bot, bukan website? Lihat menerima pembayaran crypto di bot Telegram. Minta API key dengan mendaftar lewat Telegram @mistnetwork, lihat contohnya di bagian developer, dan kelola key Anda di login merchant.

Pertanyaan umum

Apakah saya perlu SDK untuk integrasi API pembayaran crypto?
Tidak dengan mistKET. mistKET memakai HTTPS dan JSON biasa; bahasa apa pun yang bisa mengirim request HTTP dan menghitung HMAC-SHA256 bisa digunakan.
Bagaimana request API mistKET diautentikasi?
Setiap request mengirim X-Api-Key, X-Api-Timestamp, dan X-Api-Signature, dengan signature berupa hex HMAC-SHA256 dari '{timestamp}.{raw body}' memakai API secret Anda.
Bagaimana cara memverifikasi webhook pembayaran crypto?
Hitung ulang HMAC-SHA256 dari '{timestamp}.{raw body}' dengan webhook secret, bandingkan dengan X-Mistket-Signature secara constant-time, dan tolak timestamp lama.
Bagaimana jika server saya melewatkan webhook?
Webhook dikirim ulang dengan back-off selama berjam-jam, dan Anda juga bisa mengambil status invoice lewat API. Buat handler Anda idempoten berdasarkan ID pesanan.
Bisakah saya memaksa jaringan tertentu lewat API?
Bisa. Anda dapat menetapkan jaringan di panggilan API; jika tidak, pelanggan memilihnya di halaman pembayaran hosted.
Event mana yang harus memicu pemenuhan pesanan?
Penuhi pesanan pada invoice.confirmed. Gunakan invoice.paid hanya untuk menampilkan progres, dan tangani underpaid, overpaid, serta expired sesuai kebijakan Anda.

Artikel terkait