Those two routes are only gated by requireAdminAuth (Basic Auth), independent of LOG_EXPOSE_API, which controls /api/logs* and the dev/test echo endpoints. Previous wording implied all of /api/logs*, /openapi.json, and /docs shared the same LOG_EXPOSE_API gate. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|---|---|---|
| .. | ||
| README.md | ||
| api-docs.html | ||
| index.cjs | ||
| logviewer.html | ||
| openapi.json | ||
README.md
Simaya Midtrans Payment Server
Backend Express.js server untuk integrasi pembayaran Midtrans dengan sistem ERP.
Dokumentasi endpoint yang selalu sinkron dengan kode: buka
/docs(Scalar, HTTP Basic Auth) atau/openapi.json. Untuk testing manual, importpostman/Midtrans-Middleware.postman_collection.json— mencakup seluruh route di bawah ini.
Daftar Isi
Fitur Utama
1. Dual Mode Payment
- CORE API: Bank Transfer, Credit Card, GoPay/QRIS, Convenience Store
- SNAP: Hosted payment interface dengan UI Midtrans
2. Payment Link Generation
- Dua bentuk payload: langsung (
order_id/nominal/customer/expire_at) atau ERP (mercant_id/item[]) - Signature HMAC-SHA256 pada token, TTL configurable
order_idyang mengandung karakter selainA-Za-z0-9-_~.(mis.:) otomatis disanitasi jadi.sebelum dikirim ke Midtrans
3. ERP Integration
- Notifikasi otomatis ke sistem ERP setelah pembayaran sukses
- Multi-endpoint support (comma-separated URLs)
- Signature verification untuk keamanan
4. Webhook Handler
- Unified webhook untuk CORE dan SNAP
- Signature verification
- Idempotent notification handling
5. State Persisten
activeOrders,notifiedOrders,orderRetryCount,orderMerchantIddisimpan keserver/data/state.json(auto-save), jadi tidak hilang saat server restart
6. Advanced Logging
- Level-based logging (debug, info, warn, error), file harian (
LOGS_DDMMYYYY.log) - Auto-delete log lebih tua dari
LOG_RETENTION_DAYS - Log viewer interaktif di
/api/logs/view(search, filter level, click-to-trace) - Payload masking untuk sensitive data, Jakarta timezone (WIB/UTC+7)
Konfigurasi Environment
Referensi lengkap ada di .env.example di root project. Ringkasan per kategori:
Midtrans Configuration
MIDTRANS_SERVER_KEY=your-server-key
MIDTRANS_CLIENT_KEY=your-client-key
MIDTRANS_IS_PRODUCTION=false
ENABLE_BANK_TRANSFER=true
ENABLE_CREDIT_CARD=true
ENABLE_GOPAY=true
ENABLE_CSTORE=true
Payment Link & External API
EXTERNAL_API_KEY=your-api-key # X-API-KEY untuk /createtransaksi & POST /api/payment-links
PAYMENT_LINK_SECRET=your-signing-secret # HMAC secret untuk token link
PAYMENT_LINK_TTL_MINUTES=1440
PAYMENT_LINK_BASE=http://localhost:5174/pay
ERP Integration
ERP_NOTIFICATION_URL=https://your-erp.com/api/payment-notification
ERP_NOTIFICATION_URLS=https://erp1.com/api/notif,https://erp2.com/api/notif # multi-URL, opsional
ERP_CLIENT_ID=your-erp-client-id # juga dipakai sebagai secret HMAC (fallback ERP_CLIENT_SECRET)
ERP_ENABLE_NOTIF=true
Logging & Admin Access
LOG_LEVEL=info
LOG_EXPOSE_API=false # default sudah tertutup (false) kalau tidak di-set
LOG_BASIC_AUTH_USER= # wajib diisi di production untuk lindungi /api/logs* dan /docs
LOG_BASIC_AUTH_PASS=
LOG_RETENTION_DAYS=30
LOG_BUFFER_SIZE=1000
CORS & Rate Limiting
CORS_ALLOWED_ORIGINS=https://your-frontend.example.com # kosong = izinkan semua origin (dev default)
RATE_LIMIT_WINDOW_MS=60000
RATE_LIMIT_MAX=20
Server
PORT=8000
NODE_ENV=production # HARUS "production" di server production — banyak gerbang keamanan bergantung pada ini
API Endpoints
Health & Config
GET /api/health
Health check endpoint.
GET /api/config
Ambil payment toggles & Midtrans client key saat ini.
POST /api/config (non-production only)
Update payment toggles saat runtime. Return 403 kalau NODE_ENV=production.
Payment Operations
POST /api/payments/charge
Buat transaksi Core API (bank transfer, card, GoPay/QRIS, cstore). Dipanggil langsung dari browser checkout, tanpa perlu pra-registrasi order. Rate-limited.
POST /api/payments/snap/token
Generate Snap token untuk hosted payment popup. Dipanggil langsung dari browser (PayPage/CheckoutPage). Rate-limited.
GET /api/payments/:orderId/status
Cek status transaksi (passthrough Midtrans). Juga memicu fallback notifikasi ERP kalau status sudah sukses tapi belum ter-notify.
Payment Link
POST /api/payment-links — payload langsung
Requires X-API-KEY. Rate-limited.
{
"order_id": "DEMO-MERCHANT-0001:ITEM-0001",
"nominal": 179000,
"customer": { "name": "Budi Santoso", "phone": "081234567890", "email": "budi.santoso@example.com" },
"expire_at": 1785852063058
}
order_id dipakai apa adanya untuk tracking internal; versi yang dikirim ke Midtrans disanitasi (: dan karakter lain → .). expire_at dipakai kalau valid & di masa depan, kalau tidak fallback ke PAYMENT_LINK_TTL_MINUTES.
Response:
{ "status": "200", "messages": "SUCCESS", "data": { "url": "...", "order_id": "...", "midtrans_order_id": "...", "expire_at": 1785852063058 } }
POST /createtransaksi — payload ERP (mercant_id/item)
Requires X-API-KEY. Rate-limited.
{
"mercant_id": "merchant-001",
"nominal": 150000,
"nama": "John Doe",
"email": "john@example.com",
"no_telepon": "081234567890",
"item": [{ "item_id": "product-123", "nama": "Product Name", "harga": 150000, "qty": 1 }],
"allowed_methods": ["bank_transfer", "gopay"]
}
order_id diturunkan sebagai mercant_id.item[0].item_id (dot-joined). expire_at TIDAK bisa di-supply client — dihitung server dari PAYMENT_LINK_TTL_MINUTES.
Response:
{ "status": "200", "messages": "SUCCESS", "data": { "url": "http://localhost:5174/pay/eyJ2Ijox..." } }
GET /api/payment-links/:token
Resolve token jadi detail order: { order_id, nominal, customer, expire_at, allowed_methods }.
Webhook
POST /api/payments/notification
Handler webhook Midtrans (unified CORE & SNAP). Verifikasi signature_key (SHA512(order_id+status_code+gross_amount+MIDTRANS_SERVER_KEY)), lalu meneruskan notifikasi ke ERP. Return non-2xx kalau notifikasi ERP gagal supaya Midtrans retry.
Logs & Docs (internal — HTTP Basic Auth di production)
| Endpoint | Keterangan |
|---|---|
GET /api/logs |
Log in-memory terbaru (?limit, ?level, ?q) |
GET /api/logs/files |
Daftar file log harian |
GET /api/logs/files/:filename |
Isi satu file log (JSON), auto-redirect ke viewer HTML kalau diakses dari browser |
GET /api/logs/view?file=... |
Viewer interaktif: filter level, cari teks, klik nilai apa pun untuk trace |
GET /openapi.json |
Spec OpenAPI mentah |
GET /docs |
Dokumentasi API interaktif (Scalar) |
Dev/Test (aktif hanya kalau LOG_EXPOSE_API=true)
POST /api/echo,POST /api/echo2— mock endpoint untuk testing callback ERPPOST /api/test/notify-erp— triggernotifyERP()langsung, bypass webhook
Payment Flow
1. Payment Link Creation Flow
ERP System → POST /createtransaksi atau POST /api/payment-links → Server generate token → Payment URL
2. Payment Execution Flow
Customer → Payment URL → Frontend resolve token →
Pilih metode → POST /api/payments/charge atau /api/payments/snap/token → Midtrans
3. Payment Completion Flow
Midtrans → Webhook → Verifikasi signature → Update ledger →
Notify ERP → Tandai order selesai
4. Format Notifikasi ke ERP
{
"mercant_id": "merchant-001",
"status_code": "200",
"nominal": "150000",
"signature": "sha512-hash"
}
Perhitungan Signature: SHA512(mercant_id + status_code + nominal + ERP_CLIENT_ID)
Catatan: payload ke ERP tidak membawa
order_id— pencocokan transaksi murni lewatmercant_id. Kalau satumercant_idbisa punya lebih dari satu order aktif bersamaan, ERP perlu strategi lain untuk membedakannya.
Testing
Lihat folder tests/ untuk file-file testing:
node tests/test-create-payment-link.cjs
node tests/test-frontend-payload.cjs
node tests/test-snap-token.cjs
Atau pakai postman/Midtrans-Middleware.postman_collection.json — mencakup seluruh endpoint di atas, dikelompokkan per folder, termasuk request untuk mensimulasikan webhook Midtrans dari payload asli.
Logging
Log Levels
- debug: Detail untuk diagnosa masalah
- info: Informasi umum
- warn: Peringatan
- error: Error yang masih memungkinkan aplikasi jalan
Format Log (file, satu baris per event)
[2026-08-03T21:35:54.746+07:00] [INFO ] webhook.notifying_erp | {"order_id":"DEMO-MERCHANT-0002.ITEM-0002","mercant_id":"DEMO-MERCHANT-0002"}
Paling nyaman dibaca lewat GET /api/logs/view (filter level, cari teks, klik nilai untuk trace) daripada membaca file mentah.
Event Penting
Payment Lifecycle: charge.request, charge.success, charge.error, status.request, webhook.received
Payment Link: payment-links.create.issued, createtransaksi.issued, paymentlink.resolve.success
ERP Integration: erp.notify.start, erp.notify.success, erp.notify.error, erp.notify.skip
Security: webhook.signature.invalid, createtransaksi.unauthorized, payment-links.create.unauthorized, admin.auth.failed, rate_limit.blocked
Keamanan
- Signature Verification — webhook, token payment link, notifikasi ERP semuanya ditandatangani/diverifikasi.
- Idempotency — cegah duplikasi order/notifikasi ERP; state disimpan konsisten pakai
order_idyang sudah disanitasi (Midtrans-safe) di semua map internal (activeOrders,notifiedOrders,orderRetryCount,orderMerchantId), sehingga tidak ada mismatch walauorder_idasli mengandung karakter seperti:. - API Key Authentication —
X-API-KEYuntuk/createtransaksidanPOST /api/payment-links. Dev mode fallback (bypass) hanya aktif kalauNODE_ENV !== 'production'dan key belum di-set. - HTTP Basic Auth untuk admin tooling —
/api/logs*,/openapi.json,/docssemuanya dilindungirequireAdminAuth(LOG_BASIC_AUTH_USER/PASS). Wajib di-set di production — gerbang ini bergantung padaNODE_ENV=productionuntuk benar-benar dipaksa (lihat poin 8). Terpisah dari itu,LOG_EXPOSE_API=false(default) mematikan/api/logs*dengan 403 sekalipun auth-nya lolos, dan membuat endpoint dev/test (/api/echo,/api/echo2,/api/test/notify-erp) tidak terdaftar sama sekali — flag ini tidak memengaruhi/openapi.jsonatau/docs. - Rate Limiting —
/createtransaksi,/api/payment-links,/api/payments/charge,/api/payments/snap/tokendibatasi per-IP (RATE_LIMIT_MAXrequest perRATE_LIMIT_WINDOW_MS) untuk mencegah bot abuse / card-testing pada endpoint yang memang harus tetap bisa diakses langsung dari browser tanpa API key. - CORS — dibatasi lewat
CORS_ALLOWED_ORIGINS(comma-separated). Kosong = izinkan semua origin (default dev). - Payload Masking — field sensitif (card number, CVV, token, server key) otomatis disamarkan di log.
NODE_ENVproduction — banyak gerbang keamanan (API key, Basic Auth) bergantung padaisDevEnv(). Pastikan proses production (ecosystem.config.cjs) benar-benar setNODE_ENV=production, lalupm2 restart <app> --update-envsetelah mengubahnya.
Running the Server
npm install
npm run server # atau: node server/index.cjs
# Server jalan di http://localhost:8000
Common Issues
"Transaction already pending"
Penyebab: Order ID sudah punya transaksi pending di Midtrans. Solusi: Pakai instruksi pembayaran yang ada, atau buat order baru dengan ID berbeda.
"ERP notification failed"
Penyebab: Endpoint ERP tidak bisa diakses, atau signature mismatch (ERP_CLIENT_ID beda dengan yang dipakai ERP).
Solusi: Cek ERP_NOTIFICATION_URL(S) dan ERP_CLIENT_ID, replay payload webhook asli lewat request "Simulate Midtrans Notification (Local)" di Postman collection untuk debug.
"Invalid signature on webhook"
Penyebab: MIDTRANS_SERVER_KEY salah, atau webhook dari sumber tak sah.
Solusi: Pastikan MIDTRANS_SERVER_KEY sama dengan akun Midtrans yang memproses transaksi tersebut (sandbox vs production beda key).
Log//docs mengembalikan 401 atau 503
Penyebab: LOG_BASIC_AUTH_USER/PASS belum di-set di production (NODE_ENV=production).
Solusi: Set kedua env var tersebut, restart server.