Lesson 5 / 7 · 12 min
Pembayaran dalam chat dengan sandbox Midtrans
Buat invoice, kirim link, verifikasi signature webhook pembayaran, konfirmasi otomatis. Checkpoint: order lunas penuh, end to end.
Daftar akun Midtrans gratis dan alihkan dashboard ke mode Sandbox: API sungguhan, uang tes. Salin Server Key sandbox ke MIDTRANS_SERVER_KEY di .env. Kita pakai Snap, halaman pembayaran hosted mereka yang menerima QRIS, virtual account, e-wallet, dan kartu, jadi kita tidak pernah menyentuh data kartu sendiri.
Langkah 1: buat invoice (pay.js)
export async function createInvoice(orderId, amount) {
const auth = Buffer
.from(process.env.MIDTRANS_SERVER_KEY + ":")
.toString("base64");
const res = await fetch(
"https://app.sandbox.midtrans.com/snap/v1/transactions",
{
method: "POST",
headers: {
Authorization: "Basic " + auth,
"Content-Type": "application/json",
},
body: JSON.stringify({
transaction_details: {
order_id: orderId,
gross_amount: amount,
},
}),
},
);
const data = await res.json();
return data.redirect_url; // the hosted payment page
}Langkah 2: kirim link dari alur
// server.js: after step(), handle the action from CONFIRM
import { createInvoice } from "./pay.js";
const { reply, action } = step(session, msg.text.body);
saveSession(session);
if (reply) await sendText(msg.from, reply);
if (action === "create_invoice") {
const link = await createInvoice(
session.data.orderId,
session.data.total,
);
await sendText(
msg.from,
"Total Rp " + session.data.total.toLocaleString("id-ID") +
". Silakan bayar di sini ya: " + link,
);
}Langkah 3: webhook pembayaran, dengan verifikasi signature
// server.js
import crypto from "crypto";
import { db, addLabel } from "./db.js";
app.post("/payment/notify", async (req, res) => {
res.sendStatus(200);
const n = req.body;
// Midtrans signs: sha512(order_id + status_code + gross_amount + ServerKey)
const expected = crypto
.createHash("sha512")
.update(
n.order_id + n.status_code + n.gross_amount +
process.env.MIDTRANS_SERVER_KEY,
)
.digest("hex");
if (expected !== n.signature_key) {
return console.error("BAD SIGNATURE for", n.order_id);
}
if (n.transaction_status === "settlement" ||
n.transaction_status === "capture") {
// idempotent: only flips once, no matter how many callbacks arrive
const r = db.prepare(
"UPDATE orders SET status='paid', paid_at=unixepoch() " +
"WHERE id = ? AND status != 'paid'",
).run(n.order_id);
if (r.changes === 1) {
const o = db.prepare("SELECT phone FROM orders WHERE id = ?")
.get(n.order_id);
addLabel(o.phone, "customer");
await sendText(o.phone,
"Pembayaran diterima. Pesanan Anda sedang diproses ya, terima kasih!");
}
}
});Langkah 4: beri tahu Midtrans harus memanggil ke mana
Di dashboard sandbox Midtrans, isi Payment Notification URL dengan alamat ngrok Anda plus /payment/notify. Lalu buat pesanan dari ponsel, buka link-nya, dan bayar dengan salah satu metode tes sandbox yang tercantum di docs Midtrans (kartu tes dan simulasi QRIS atau virtual account).
Checkpoint: pesan dari ponsel, bayar di sandbox, dan lihat bot mengonfirmasi sendiri, dengan order berbalik jadi paid di database dan label customer tertempel. Loop itu, permintaan ke uang ke konfirmasi tanpa satu pun sentuhan manusia, adalah jantung otomasi perdagangan.
Yang Anda dapatkan
- Snap memberi halaman pembayaran hosted; Anda tidak pernah memegang data kartu
- Verifikasi signature sha512 di setiap notifikasi, tanpa kecuali
- Update lunas idempoten: WHERE status != 'paid' menjaga callback ganda
- Sandbox membuktikan loop penuh dengan uang tes sebelum rupiah sungguhan bergerak
Sumber terkurasi
Referensi pilihan untuk mendalami lebih jauh, direview oleh tim kami.