# Integrasi Pembayaran Midtrans Snap ke Aplikasi Next.js

> Panduan praktis menyambungkan Midtrans Snap ke Next.js: pembuatan token di server, popup Snap, dan webhook yang memverifikasi signature key SHA512.

URL: https://www.matthewswong.com/id/blog/midtrans-payment-gateway-integration-nextjs/

- Author: Matthews Wong (https://www.matthewswong.com/id/about/)
- Published: 2026-07-14
- Updated: 2026-10-17
- Category: Backend

> **Key takeaway:** Midtrans Snap butuh tiga bagian utama: route server yang menukar Server Key jadi Snap token, popup snap.js di sisi klien yang terbuka dengan token itu, dan webhook yang memverifikasi setiap pembayaran dengan menghitung ulang SHA512 dari order_id, status_code, gross_amount, dan Server Key Anda. Jangan pernah percaya redirect browser saja.

Midtrans adalah payment gateway asal Indonesia, dan Snap adalah checkout siap-pakainya: satu popup yang mencakup Virtual Account transfer bank (BCA, BNI, BRI, Permata, dan lainnya) serta Mandiri Bill Payment, QRIS, e-wallet besar (GoPay, OVO, DANA, ShopeePay), dan kartu. Di sebuah proyek untuk klien Indonesia, saya menyambungkan Snap ke build Next.js 15 App Router, dan alurnya lebih rapi daripada kesan dokumentasi lamanya, asalkan Anda pegang satu aturan: uang baru dianggap masuk kalau webhook yang bilang.

Panduan ini membahas seluruh perjalanan bolak-balik: membuat transaction token dari route server, membuka popup Snap di klien, menerima HTTP notification, memverifikasi tanda tangannya, dan membaca status settlement dengan benar. Contohnya memakai App Router dan fetch biasa sehingga bisa Anda pasang di codebase Next.js mana pun tanpa SDK resmi kalau lebih suka begitu.

## Tiga kunci dan dua environment

Midtrans memberi Anda Server Key dan Client Key, masing-masing tersedia dalam pasangan sandbox dan pasangan production. Server Key wajib hanya ada di server; itu rahasia yang sekaligus mengotorisasi pembuatan token dan menandatangani notifikasi webhook. Client Key aman diekspos di browser karena hanya dipakai snap.js untuk merender popup. Salah membagi dua kunci ini adalah kesalahan keamanan paling umum yang saya lihat di tutorial Midtrans berbahasa Indonesia.

> Sandbox dan production adalah environment terpisah dengan Server Key dan Client Key masing-masing: pilih environment lewat dropdown di kiri atas dashboard, lalu ambil pasangan kuncinya di Settings, Access Keys. Contoh sandbox di dokumentasi Midtrans memakai kunci berawalan SB-Mid-, tetapi patokannya tetap dashboard, bukan awalan kunci. Host snap.js dan API juga berbeda (app.sandbox.midtrans.com dan app.midtrans.com), jadi environment yang tidak cocok adalah hal pertama yang harus dicek saat popup gagal tanpa pesan.

## Langkah 1 — Buat Snap token di sisi server

Tugas backend adalah mem-POST detail pesanan ke endpoint transactions Snap memakai HTTP Basic Auth, dengan username berupa Server Key dan password kosong. Midtrans mengembalikan token yang dipakai klien untuk membuka popup. Buat sendiri order_id-nya dan pastikan unik serta idempoten per pesanan, karena nanti Anda mencocokkannya di webhook.

> gross_amount bertipe integer dalam Rupiah utuh, dan Midtrans juga meminta harga item ditulis tanpa desimal. Jika Anda mengirim item_details, total harga dikali jumlah untuk semua item harus sama persis dengan gross_amount. Di notifikasi nilainya kembali sebagai string seperti 10000.00, dan string itulah yang di-hash apa adanya.

## Langkah 2 — Buka popup Snap di klien

Di sisi klien Anda memuat snap.js sekali dengan Client Key, lalu memanggil window.snap.pay(token) dengan token yang dikembalikan route Anda. Di Next.js cara rapi menyisipkan skripnya adalah lewat next/script dengan URL sandbox atau production yang tepat. Popup-nya menangani semua metode pembayaran yang Anda aktifkan di akun Midtrans, jadi Anda tidak perlu membangun UI kartu atau Virtual Account sendiri.

> Callback onSuccess berjalan di browser dan bagus untuk UX, tetapi itu bukan bukti pembayaran. Pengguna bisa menutup tab, kehilangan sinyal, atau Virtual Account baru dibayar berjam-jam kemudian. Pakai callback ini hanya untuk memperbarui tampilan. Sumber kebenaran selalu webhook di sisi server pada Langkah 3.

## Langkah 3 — Tangani dan verifikasi webhook

Midtrans mengirim HTTP notification ke URL yang Anda daftarkan di dashboard setiap kali transaksi berubah status. Ini satu-satunya sinyal andal bahwa uang benar-benar bergerak. Setiap notifikasi membawa signature_key, dan Anda wajib menghitungnya ulang: SHA512 dari gabungan order_id, status_code, gross_amount, dan Server Key. Kalau hash Anda tidak cocok, buang permintaannya — itu palsu atau rusak. Baru setelah tanda tangan valid, baca statusnya.

> Kembalikan respons 2xx dengan cepat dan kerjakan proses berat secara asinkron. Midtrans menunggu paling lama 15 detik, meminta respons dalam sekitar 5 detik, dan mengulang pengiriman yang gagal hingga lima kali selama beberapa jam (lebih sedikit untuk 500, 503, 400, dan 404). Midtrans juga mengingatkan bahwa notifikasi yang sama sesekali bisa datang dua kali, jadi kunci logika update pesanan pada order_id agar settlement yang diulang tidak pernah mengirim pesanan dua kali atau menambah saldo dompet dua kali.

## Membaca status transaksi dengan benar

Field yang penting adalah transaction_status, dipasangkan dengan fraud_status untuk pembayaran kartu. Bug umum adalah menganggap pending sebagai sukses — untuk Virtual Account atau QRIS, pending hanya berarti instruksi pembayaran sudah dibuat dan pelanggan belum membayar. Hanya settlement (atau capture dengan fraud status accept untuk kartu) yang berarti dana sudah terkonfirmasi. Ada satu pengecualian terdokumentasi: untuk Permata VA, Mandiri Bill Payment, dan Indomaret, settlement sesekali bisa berbalik menjadi deny, biasanya dalam hitungan menit, dan Midtrans meminta transaksi itu dianggap belum dibayar. Kalau notifikasi terlambat atau tidak berurutan, tanyakan ke GET Status API.

| transaction_status | Artinya | Tindakan |
| --- | --- | --- |
| pending | VA/QRIS dibuat, pelanggan belum bayar | Tunggu notifikasi berikutnya |
| settlement | Dana dikonfirmasi bank/penyedia | Penuhi pesanan |
| capture | Kartu terotorisasi; cek fraud_status | Penuhi hanya jika fraud_status accept |
| deny | Ditolak penyedia atau sistem deteksi fraud Midtrans | Tandai pesanan gagal |
| expire | Tidak dibayar dalam batas waktu | Lepas stok, tandai gagal |
| cancel / refund | Dibatalkan atau uang dikembalikan | Batalkan pemenuhan pesanan |

## Menguji di sandbox sebelum go-live

- Pakai simulator pembayaran berbasis web milik Midtrans (simulator.sandbox.midtrans.com) untuk membayar Virtual Account atau kode QRIS uji tanpa uang sungguhan, pakai kartu uji yang terdokumentasi untuk skenario diterima, challenge, dan ditolak, lalu atur expiry kustom yang singkat untuk melihat notifikasi expire datang.
- Ekspos webhook lokal Anda dengan tunnel (misalnya ngrok) dan daftarkan URL itu di Settings, Configuration, Payment Notification URL.
- Uji pengecekan tanda tangan dengan mengirim notifikasi palsu ber-signature_key salah dan pastikan route Anda mengembalikan 403.
- Saat pindah ke production, ganti kedua kunci, ubah flag environment, dan daftarkan ulang URL notifikasi production — semuanya tidak terbawa dari sandbox.

## Sources

- Midtrans Docs — Backend Integration (Snap token)
- Midtrans Docs — HTTP(S) Notification / Webhooks
- Midtrans Docs — Transaction Status Cycle

## Source links

- https://docs.midtrans.com/reference/backend-integration
- https://docs.midtrans.com/docs/https-notification-webhooks
- https://docs.midtrans.com/docs/transaction-status-cycle

## FAQ

### Apakah Client Key Midtrans aman diekspos di browser?

Ya. Client Key memang dirancang untuk publik dan hanya dipakai snap.js untuk merender popup pembayaran. Server Key adalah yang rahasia — ia mengotorisasi pembuatan token dan menandatangani webhook, jadi harus tetap di sisi server dan tidak boleh diawali NEXT_PUBLIC_.

### Kenapa saya tidak boleh percaya callback onSuccess Snap sebagai bukti pembayaran?

Callback onSuccess berjalan di browser pengguna dan bisa terlewat kalau mereka menutup tab atau kehilangan koneksi. Untuk Virtual Account dan QRIS, pembayaran bisa selesai berjam-jam kemudian. Konfirmasi yang andal hanya HTTP notification di sisi server, yang tanda tangannya Anda verifikasi sebelum menandai pesanan lunas.

### Bagaimana cara memverifikasi signature_key webhook Midtrans?

Gabungkan order_id, status_code, gross_amount, dan Server Key jadi satu string, lalu hitung hash SHA512-nya dalam bentuk hex. Bandingkan dengan signature_key pada body notifikasi. Kalau tidak cocok, tolak permintaan dengan 403 karena berarti palsu atau rusak.

### Apa beda pending dan settlement di Midtrans?

Pending berarti instruksi pembayaran (nomor Virtual Account atau kode QRIS) sudah dibuat tetapi pelanggan belum membayar. Settlement berarti bank atau penyedia sudah mengonfirmasi dananya. Penuhi pesanan hanya saat settlement, atau saat capture dengan fraud_status accept untuk pembayaran kartu.

### Apa endpoint sandbox dan production untuk Midtrans Snap?

Sandbox membuat token di app.sandbox.midtrans.com/snap/v1/transactions dan memuat app.sandbox.midtrans.com/snap/snap.js. Production memakai app.midtrans.com untuk keduanya. Kedua environment punya kunci dan dashboard terpisah, dan token sandbox tidak akan terbuka di popup production.

---

- HTML version: https://www.matthewswong.com/id/blog/midtrans-payment-gateway-integration-nextjs/
- English: https://www.matthewswong.com/en/blog/midtrans-payment-gateway-integration-nextjs/
- Site index for agents: https://www.matthewswong.com/llms.txt
- Sitemap: https://www.matthewswong.com/sitemap.xml
- API description (OpenAPI): https://www.matthewswong.com/openapi.json
