491 lines
9.7 KiB
Markdown
491 lines
9.7 KiB
Markdown
# Simaya Midtrans Payment Server
|
|
|
|
Backend Express.js server untuk integrasi pembayaran Midtrans dengan sistem ERP.
|
|
|
|
## 📋 Daftar Isi
|
|
|
|
- [Fitur Utama](#fitur-utama)
|
|
- [Konfigurasi Environment](#konfigurasi-environment)
|
|
- [API Endpoints](#api-endpoints)
|
|
- [Payment Flow](#payment-flow)
|
|
- [Testing](#testing)
|
|
- [Logging](#logging)
|
|
|
|
## 🚀 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**
|
|
- Generate secure payment link dengan signature validation
|
|
- Configurable TTL (Time To Live)
|
|
- Token-based authentication
|
|
|
|
### 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. **Advanced Logging**
|
|
- Level-based logging (debug, info, warn, error)
|
|
- In-memory log buffer
|
|
- Payload masking untuk sensitive data
|
|
- Jakarta timezone (WIB/UTC+7)
|
|
|
|
## ⚙️ Konfigurasi Environment
|
|
|
|
### Midtrans Configuration
|
|
```env
|
|
# Required
|
|
MIDTRANS_SERVER_KEY=your-server-key
|
|
MIDTRANS_CLIENT_KEY=your-client-key
|
|
MIDTRANS_IS_PRODUCTION=false
|
|
|
|
# Payment Method Toggles
|
|
ENABLE_BANK_TRANSFER=true
|
|
ENABLE_CREDIT_CARD=true
|
|
ENABLE_GOPAY=true
|
|
ENABLE_CSTORE=true
|
|
```
|
|
|
|
### Payment Link Configuration
|
|
```env
|
|
# External API Access
|
|
EXTERNAL_API_KEY=your-api-key
|
|
|
|
# Payment Link Settings
|
|
PAYMENT_LINK_SECRET=your-secret-for-signing
|
|
PAYMENT_LINK_TTL_MINUTES=1440
|
|
PAYMENT_LINK_BASE=http://localhost:5174/pay
|
|
```
|
|
|
|
### ERP Integration
|
|
```env
|
|
# Single URL (legacy)
|
|
ERP_NOTIFICATION_URL=https://your-erp.com/api/payment-notification
|
|
ERP_CLIENT_SECRET=your-erp-client-secret
|
|
|
|
# Multi-URL (recommended)
|
|
ERP_NOTIFICATION_URLS=https://erp1.com/api/notif,https://erp2.com/api/notif
|
|
|
|
# Toggle
|
|
ERP_ENABLE_NOTIF=true
|
|
```
|
|
|
|
### Logging Configuration
|
|
```env
|
|
# Logging Level: debug, info, warn, error
|
|
LOG_LEVEL=info
|
|
|
|
# Expose /api/logs endpoint (dev only)
|
|
LOG_EXPOSE_API=true
|
|
|
|
# In-memory buffer size
|
|
LOG_BUFFER_SIZE=1000
|
|
```
|
|
|
|
### Server Configuration
|
|
```env
|
|
PORT=8000
|
|
NODE_ENV=development
|
|
```
|
|
|
|
## 📡 API Endpoints
|
|
|
|
### Health & Config
|
|
|
|
#### `GET /api/health`
|
|
Health check endpoint.
|
|
|
|
**Response:**
|
|
```json
|
|
{
|
|
"ok": true,
|
|
"env": {
|
|
"isProduction": false,
|
|
"hasServerKey": true,
|
|
"hasClientKey": true
|
|
}
|
|
}
|
|
```
|
|
|
|
#### `GET /api/config`
|
|
Get current payment configuration.
|
|
|
|
**Response:**
|
|
```json
|
|
{
|
|
"paymentToggles": {
|
|
"bank_transfer": true,
|
|
"credit_card": true,
|
|
"gopay": true,
|
|
"cstore": true
|
|
},
|
|
"midtransEnv": "sandbox",
|
|
"clientKey": "SB-Mid-client-xxx"
|
|
}
|
|
```
|
|
|
|
#### `POST /api/config` (Dev Only)
|
|
Update payment toggles at runtime.
|
|
|
|
**Request:**
|
|
```json
|
|
{
|
|
"paymentToggles": {
|
|
"bank_transfer": false
|
|
}
|
|
}
|
|
```
|
|
|
|
### Payment Operations
|
|
|
|
#### `POST /api/payments/charge`
|
|
Create payment transaction via Midtrans Core API.
|
|
|
|
**Headers:**
|
|
```
|
|
Content-Type: application/json
|
|
```
|
|
|
|
**Request Body:**
|
|
```json
|
|
{
|
|
"payment_type": "bank_transfer",
|
|
"transaction_details": {
|
|
"order_id": "order-123",
|
|
"gross_amount": 150000
|
|
},
|
|
"bank_transfer": {
|
|
"bank": "bca"
|
|
}
|
|
}
|
|
```
|
|
|
|
**Response:**
|
|
```json
|
|
{
|
|
"status_code": "201",
|
|
"status_message": "Success",
|
|
"transaction_id": "xxx",
|
|
"order_id": "order-123",
|
|
"va_numbers": [
|
|
{
|
|
"bank": "bca",
|
|
"va_number": "12345678901"
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
#### `POST /api/payments/snap/token`
|
|
Generate Snap token for hosted payment.
|
|
|
|
**Request:**
|
|
```json
|
|
{
|
|
"transaction_details": {
|
|
"order_id": "order-123",
|
|
"gross_amount": 150000
|
|
},
|
|
"customer_details": {
|
|
"first_name": "John",
|
|
"email": "john@example.com"
|
|
}
|
|
}
|
|
```
|
|
|
|
**Response:**
|
|
```json
|
|
{
|
|
"token": "snap-token-xxx"
|
|
}
|
|
```
|
|
|
|
#### `GET /api/payments/:orderId/status`
|
|
Check payment status.
|
|
|
|
**Response:**
|
|
```json
|
|
{
|
|
"status_code": "200",
|
|
"transaction_status": "settlement",
|
|
"order_id": "order-123",
|
|
"gross_amount": "150000.00"
|
|
}
|
|
```
|
|
|
|
### Payment Link
|
|
|
|
#### `POST /createtransaksi`
|
|
Generate payment link (external ERP endpoint).
|
|
|
|
**Headers:**
|
|
```
|
|
X-API-KEY: your-external-api-key
|
|
Content-Type: application/json
|
|
```
|
|
|
|
**Request:**
|
|
```json
|
|
{
|
|
"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"]
|
|
}
|
|
```
|
|
|
|
**Response:**
|
|
```json
|
|
{
|
|
"status": "200",
|
|
"messages": "SUCCESS",
|
|
"data": {
|
|
"url": "http://localhost:5174/pay/eyJ2Ijox..."
|
|
}
|
|
}
|
|
```
|
|
|
|
#### `GET /api/payment-links/:token`
|
|
Resolve payment link token.
|
|
|
|
**Response:**
|
|
```json
|
|
{
|
|
"order_id": "merchant-001:product-123",
|
|
"nominal": 150000,
|
|
"customer": {
|
|
"name": "John Doe",
|
|
"email": "john@example.com",
|
|
"phone": "081234567890"
|
|
},
|
|
"expire_at": 1733280000000,
|
|
"allowed_methods": ["bank_transfer", "gopay"]
|
|
}
|
|
```
|
|
|
|
### Webhook
|
|
|
|
#### `POST /api/payments/notification`
|
|
Midtrans webhook handler (unified for CORE & SNAP).
|
|
|
|
**Request (from Midtrans):**
|
|
```json
|
|
{
|
|
"order_id": "order-123",
|
|
"transaction_status": "settlement",
|
|
"gross_amount": "150000.00",
|
|
"signature_key": "xxx"
|
|
}
|
|
```
|
|
|
|
**Response:**
|
|
```json
|
|
{
|
|
"ok": true
|
|
}
|
|
```
|
|
|
|
### Logging (Dev Only)
|
|
|
|
#### `GET /api/logs?limit=100&level=info&q=payment`
|
|
Get recent logs.
|
|
|
|
**Query Parameters:**
|
|
- `limit`: Max entries (1-1000, default: 100)
|
|
- `level`: Filter by level (debug|info|warn|error)
|
|
- `q`: Search keyword
|
|
|
|
**Response:**
|
|
```json
|
|
{
|
|
"count": 50,
|
|
"items": [
|
|
{
|
|
"ts": "2024-12-04T12:00:00.000+07:00",
|
|
"level": "info",
|
|
"msg": "charge.request",
|
|
"meta": {
|
|
"id": "abc123",
|
|
"payment_type": "bank_transfer"
|
|
}
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
### Testing (Dev Only)
|
|
|
|
#### `POST /api/echo`
|
|
Echo endpoint untuk testing ERP notification.
|
|
|
|
#### `POST /api/test/notify-erp`
|
|
Manual trigger ERP notification.
|
|
|
|
**Request:**
|
|
```json
|
|
{
|
|
"orderId": "order-123",
|
|
"nominal": "150000",
|
|
"mercant_id": "merchant-001"
|
|
}
|
|
```
|
|
|
|
## 🔄 Payment Flow
|
|
|
|
### 1. Payment Link Creation Flow
|
|
```
|
|
ERP System → POST /createtransaksi → Server generates token → Payment URL
|
|
```
|
|
|
|
### 2. Payment Execution Flow
|
|
```
|
|
Customer → Payment URL → Frontend resolves token →
|
|
Choose method → POST /api/payments/charge or SNAP → Midtrans
|
|
```
|
|
|
|
### 3. Payment Completion Flow
|
|
```
|
|
Midtrans → Webhook → Verify signature → Update ledger →
|
|
Notify ERP → Mark order complete
|
|
```
|
|
|
|
### 4. ERP Notification Format
|
|
```json
|
|
{
|
|
"mercant_id": "merchant-001",
|
|
"status_code": "200",
|
|
"nominal": "150000",
|
|
"signature": "sha512-hash"
|
|
}
|
|
```
|
|
|
|
**Signature Calculation:**
|
|
```
|
|
SHA512(mercant_id + status_code + nominal + ERP_CLIENT_SECRET)
|
|
```
|
|
|
|
## 🧪 Testing
|
|
|
|
Lihat folder `tests/` untuk file-file testing:
|
|
|
|
```bash
|
|
# Test create payment link
|
|
node tests/test-create-payment-link.cjs
|
|
|
|
# Test frontend payload
|
|
node tests/test-frontend-payload.cjs
|
|
|
|
# Test snap token
|
|
node tests/test-snap-token.cjs
|
|
```
|
|
|
|
Lihat `tests/README.md` untuk detail lengkap.
|
|
|
|
## 📝 Logging
|
|
|
|
### Log Levels
|
|
- **debug**: Detailed information, typically of interest only when diagnosing problems
|
|
- **info**: General informational messages
|
|
- **warn**: Warning messages for potentially harmful situations
|
|
- **error**: Error events that might still allow the application to continue running
|
|
|
|
### Log Format
|
|
```
|
|
[2024-12-04T12:00:00.000+07:00] [info] charge.request {"id": "abc123", "payment_type": "bank_transfer"}
|
|
```
|
|
|
|
### Important Log Events
|
|
|
|
#### Payment Lifecycle
|
|
- `charge.request`: Payment charge initiated
|
|
- `charge.success`: Charge successful
|
|
- `charge.error`: Charge failed
|
|
- `status.request`: Status check requested
|
|
- `webhook.received`: Webhook notification received
|
|
|
|
#### ERP Integration
|
|
- `erp.notify.start`: ERP notification started
|
|
- `erp.notify.success`: ERP notified successfully
|
|
- `erp.notify.error`: ERP notification failed
|
|
- `erp.notify.skip`: Notification skipped (already sent or disabled)
|
|
|
|
#### Security
|
|
- `webhook.signature.invalid`: Invalid webhook signature
|
|
- `createtransaksi.unauthorized`: Unauthorized API key
|
|
|
|
## 🔒 Security Features
|
|
|
|
1. **Signature Verification**
|
|
- Webhook signature validation
|
|
- Payment link token signing
|
|
- ERP notification signing
|
|
|
|
2. **Idempotency**
|
|
- Prevent duplicate order creation
|
|
- Prevent duplicate ERP notifications
|
|
- Block re-charge for pending orders
|
|
|
|
3. **API Key Authentication**
|
|
- External API key for `/createtransaksi`
|
|
- Dev mode fallback for easier local testing
|
|
|
|
4. **Payload Masking**
|
|
- Sensitive fields masked in logs
|
|
- Card numbers, CVV, tokens automatically hidden
|
|
|
|
## 🚀 Running the Server
|
|
|
|
```bash
|
|
# Install dependencies
|
|
npm install
|
|
|
|
# Start server
|
|
node server/index.cjs
|
|
|
|
# Server runs on http://localhost:8000
|
|
```
|
|
|
|
## 📚 Related Documentation
|
|
|
|
- [Tests README](../tests/README.md) - Testing documentation
|
|
- [Temp Files README](../temp/README.md) - Temporary files info
|
|
- [Frontend README](../README.md) - Main project README
|
|
|
|
## 🐛 Common Issues
|
|
|
|
### Issue: "Transaction already pending"
|
|
**Cause**: Order ID already has pending transaction in Midtrans
|
|
**Solution**: Use existing payment instructions or create new order with different ID
|
|
|
|
### Issue: "ERP notification failed"
|
|
**Cause**: ERP endpoint unreachable or signature mismatch
|
|
**Solution**: Check `ERP_NOTIFICATION_URLS` and `ERP_CLIENT_SECRET` configuration
|
|
|
|
### Issue: "Invalid signature on webhook"
|
|
**Cause**: Incorrect server key or webhook from unauthorized source
|
|
**Solution**: Verify `MIDTRANS_SERVER_KEY` matches your Midtrans account
|
|
|
|
## 📞 Support
|
|
|
|
Untuk bantuan lebih lanjut, hubungi tim development atau lihat dokumentasi Midtrans:
|
|
- [Midtrans API Documentation](https://docs.midtrans.com/)
|
|
- [Midtrans Node.js Library](https://github.com/Midtrans/midtrans-nodejs-client)
|