Cấu hình IPN URL
IPN URL được cấu hình tại trang quản lý merchant trên SePay:
- Đăng nhập vào SePay
- Vào Cổng thanh toán → Cấu hình → IPN
- Nhập URL endpoint của bạn để nhận IPN
- Lưu cấu hình
Lưu ý quan trọng
IPN URL phải là HTTPS và endpoint phải trả về HTTP status code 200 để xác nhận đã nhận thành công.
Request từ SePay đến Merchant
POST
https://your-url (url bạn cấu hình trong IPN)Headers:
X-Secret-Key: <secret_key>
Content-Type: application/json
Ghi chú
X-Secret-Key: Secret key để xác thực (chỉ có khi merchant cấu hình auth type = SECRET_KEY)
Danh sách tham số
timestampintegerrequired
Unix timestamp khi gửi thông báo
notification_typestringrequired
Loại thông báo: ORDER_PAID (thanh toán thành công), TRANSACTION_VOID (hủy giao dịch)
orderobjectrequired
Thông tin đơn hàng
transactionobjectrequired
Thông tin giao dịch
customerobjectrequired
Thông tin khách hàng
Ví dụ request body:
REQUEST
{
"timestamp": 1757058220,
"notification_type": "ORDER_PAID",
"order": {
"id": "e2c195be-c721-47eb-b323-99ab24e52d85",
"order_id": "NPSETVI00101000042R",
"order_status": "CAPTURED",
"order_currency": "VND",
"order_amount": "50000.00",
"order_invoice_number": "SUB_202509_001",
"custom_data": [],
"user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
"ip_address": "14.xxx.xxx.xxx",
"order_description": "Thanh toán định kỳ gói Premium tháng 9/2025"
},
"transaction": {
"id": "384c66dd-41e6-4316-a544-b4141682595c",
"payment_method": "CARD",
"transaction_id": "68ba94ac80123",
"transaction_type": "PAYMENT",
"transaction_date": "2025-09-01 00:00:15",
"transaction_status": "APPROVED",
"transaction_amount": "50000",
"transaction_currency": "VND",
"authentication_status": "AUTHENTICATION_SUCCESSFUL",
"card_number": "4111XXXXXXXX1111",
"card_holder_name": "NGUYEN VAN A",
"card_expiry": "12/26",
"card_funding_method": "CREDIT",
"card_brand": "VISA"
},
"customer": {
"id": "bae12d2f-0580-4669-8841-cc35cf671613",
"customer_id": "CUST_001"
}
}Xử lý IPN endpoint:
PHPPHP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Route::post('/payment/ipn', function(Request $request) {// Verify secret keyif ($request->header('X-Secret-Key') !== $secretKey) {return response()->json(['error' => 'Unauthorized'], 401);}$data = $request->json()->all();if ($data['notification_type'] === 'ORDER_PAID') {$order = Order::where('invoice_number', $data['order']['order_invoice_number'])->first();$order->status = 'paid';$order->save();}// Return 200 to acknowledge receiptreturn response()->json(['success' => true], 200);});