Fitur & Build Notes · · 3 menit baca
Fitur e-commerce: alur checkout Midtrans Snap yang benar
Alur pembayaran Midtrans dari ujung ke ujung: buat transaksi Snap di server (Server Key aman), popup snap.pay di browser, dan webhook dengan verifikasi signature SHA-512 sebagai satu-satunya sumber kebenaran.
Ringkasan pesanan
Metode pembayaran
Popup Snap terbuka di sini · status final ditentukan webhook
Midtrans Snap adalah cara tercepat menerima pembayaran lokal (VA semua bank, QRIS, GoPay, OVO, kartu) di toko online Indonesia — inilah gateway yang kupasang di NexaMart dan build klien. Alurnya tiga langkah: server membuat transaksi, browser membuka popup Snap, dan webhook mengonfirmasi pembayaran. Klik Lihat preview untuk melihat UI ringkasan checkout-nya.
Arsitektur (dan aturan emasnya)
Browser ──POST /checkout──▶ Server ──(SERVER KEY)──▶ Midtrans ──▶ snap token
Browser ◀── token ─────────┘
Browser ──snap.pay(token)──▶ popup pembayaran Midtrans
Midtrans ──webhook──▶ Server: verifikasi signature ──▶ tandai pesanan LUNAS
Aturan emas #1: Server Key tidak boleh menyentuh browser. Aturan emas #2: status “lunas” hanya boleh ditulis oleh webhook yang terverifikasi — bukan oleh callback JavaScript (user bisa menutup popup, koneksi bisa putus, dan callback bisa dipalsukan).
1. Server: buat transaksi Snap
// POST /checkout — berjalan di server (contoh: Cloudflare Workers / Node)
const AUTH = "Basic " + btoa(env.MIDTRANS_SERVER_KEY + ":"); // perhatikan ":" di akhir
const resp = await fetch("https://app.sandbox.midtrans.com/snap/v1/transactions", {
method: "POST",
headers: { Authorization: AUTH, "Content-Type": "application/json" },
body: JSON.stringify({
transaction_details: {
order_id: `ORDER-${crypto.randomUUID()}`, // unik — Midtrans menolak duplikat
gross_amount: 170000, // total dalam Rupiah, tanpa desimal
},
item_details: [
{ id: "kopi-gayo-250", name: "Kopi Gayo 250g", price: 85000, quantity: 2 },
],
customer_details: { first_name: "Budi", email: "budi@mail.com", phone: "0812..." },
}),
});
const { token } = await resp.json();
return c.json({ token }); // hanya token yang boleh ke browser
Ganti app.sandbox.midtrans.com → app.midtrans.com saat production.
2. Browser: buka popup Snap
<!-- sandbox; production: https://app.midtrans.com/snap/snap.js -->
<script src="https://app.sandbox.midtrans.com/snap/snap.js"
data-client-key="SB-Mid-client-xxx"></script>
<script>
async function bayar() {
const { token } = await fetch("/checkout", { method: "POST" }).then(r => r.json());
window.snap.pay(token, {
onSuccess: () => location.href = "/pesanan/terima-kasih",
onPending: () => location.href = "/pesanan/menunggu", // VA: user bayar belakangan
onClose: () => {/* user menutup popup — biarkan, jangan tandai gagal */},
});
}
</script>
Callback ini hanya untuk navigasi UX — bukan sumber kebenaran status.
3. Webhook: satu-satunya sumber kebenaran
Midtrans mengirim POST ke URL notifikasi-mu. Verifikasi tanda tangannya sebelum percaya:
// POST /webhook/midtrans
const n = await c.req.json();
// signature_key = SHA512(order_id + status_code + gross_amount + ServerKey)
const raw = n.order_id + n.status_code + n.gross_amount + env.MIDTRANS_SERVER_KEY;
const hash = [...new Uint8Array(
await crypto.subtle.digest("SHA-512", new TextEncoder().encode(raw)),
)].map((b) => b.toString(16).padStart(2, "0")).join("");
if (hash !== n.signature_key) return c.text("invalid signature", 403);
const lunas = n.transaction_status === "settlement" ||
(n.transaction_status === "capture" && n.fraud_status === "accept");
if (lunas) await tandaiLunas(n.order_id); // idempotent — webhook bisa terkirim ulang
return c.text("ok");
Buat handler-nya idempotent: Midtrans bisa mengirim notifikasi yang sama lebih dari sekali.
Pelajaran
- Uji penuh di sandbox (kartu & VA simulator Midtrans tersedia) sebelum menyentuh mode production.
order_idadalah kunci penghubung semua sistem — format konsisten, simpan di database sejak sebelum popup terbuka.- Status pesanan punya lebih dari dua nilai:
pending(VA belum dibayar) adalah status normal, bukan error — desain UI-mu untuk itu.
Mau fitur seperti ini dibangun untuk bisnismu?