Xử lý lỗi khi tích hợp webhook

Lịch retry tự động, cách chẩn đoán khi webhook không gửi, và các câu hỏi thường gặp về thứ tự sự kiện, IP, phát lại và giới hạn.

||

Khi endpoint của bạn lỗi, SePay tự gửi lại theo lịch riêng. Bài này tổng hợp lịch retry, cách tự chẩn đoán khi webhook không gửi, và các câu hỏi hay gặp.

Lịch retry

Khi webhook bật "Tự động gửi lại khi server trả lỗi", SePay thử lại nếu:

  • Không kết nối được endpoint (DNS, connection refused, timeout)
  • Server trả HTTP status ngoài 200-299

Khoảng cách giữa các lần tăng dần theo Fibonacci:

LầnChờTổng
1 (ban đầu)ngay0 phút
21 phút1 phút
31 phút2 phút
42 phút4 phút
53 phút7 phút
65 phút12 phút
78 phút20 phút
8 (cuối)13 phút33 phút

Tổng cộng 8 lần gửi (1 lần đầu cộng 7 lần retry), kéo dài khoảng 33 phút nếu tất cả đều thất bại. Webhook khi đó được đánh dấu Failed và SePay gửi cảnh báo (nếu bạn đã bật). Webhook cũ hơn 5 giờ sẽ không được cron quét retry tiếp, đây là vùng an toàn dự phòng, bình thường không tới mức này vì 33 phút đã kết thúc trước rồi.

Mẹo endpoint nhanh

Trả 200 ngay khi nhận được, rồi đẩy payload vào queue xử lý sau. Đừng chặn response khi đang xử lý nặng.

Phân loại lỗi

Lỗi kết nối

LỗiLà gìSửa thế nào
DNS ErrorKhông tìm thấy domainKiểm tra URL, DNS
Connection RefusedServer từ chốiKiểm tra server chạy chưa, port, firewall
TimeoutQuá 30 giây không phản hồiTối ưu endpoint, trả 200 trước rồi xử lý sau
SSL ErrorChứng chỉ hoặc chuỗi CA có vấn đềKiểm tra chứng chỉ còn hạn, CA chain đầy đủ, không dùng self-signed

Lỗi HTTP

StatusLà gìSửa thế nào
401Sai xác thựcKiểm tra API Key, Secret Key
403Bị chặnThêm IP SePay vào whitelist
404Sai URLKiểm tra đường dẫn (chú ý hoa thường)
405Sai methodEndpoint phải nhận POST
500Lỗi serverKiểm tra logs
502/503Server không sẵn sàngBảo trì hoặc quá tải

Tốc độ phản hồi

Thời gianNhận xét
< 1 giâyTốt
1-5 giâyNên cải thiện
> 5 giâyDễ bị timeout

Mã cURL

Lịch sử gửi ghi error_code (mã cURL) + response_time_ms mỗi lần gửi.

LỗiGhi chú
6DNSSai domain hoặc DNS chưa trỏ
7Connection RefusedServer tắt, sai port, firewall chặn
28TimeoutPhản hồi quá 30 giây
35SSLLỗi bắt tay TLS
51SSL CertChứng chỉ không hợp lệ
55Send ErrorMất kết nối khi gửi
56Receive ErrorMất kết nối khi nhận
60CA CertKhông xác minh được chuỗi chứng chỉ (CA)

Chẩn đoán webhook không gửi

Đã bật webhook nhưng server không nhận request? Đi từ trên xuống, lỗi thường ở mấy bước đầu.

1. Webhook còn bật

Dashboard → Webhooks, cột Trạng thái phải là Bật. Lưu webhook không tự bật lại nếu bạn đã tắt, kiểm tra lại công tắc sau mỗi lần sửa.

2. URL có gọi được không

Bấm Gửi thử. SePay gửi payload mẫu và kết quả hiện ra ngay.

Kết quảNguyên nhân thường gặp
Thành côngURL ổn, qua bước 3
DNS ErrorDomain chưa trỏ, hoặc sai chính tả URL
Connection RefusedServer chưa lắng nghe port đó
TimeoutFirewall chặn, hoặc server quá chậm
SSL ErrorChứng chỉ hết hạn hoặc không hợp lệ
HTTP 4xx / 5xxServer nhận được nhưng trả lỗi, mở chi tiết log

3. Loại sự kiện có khớp

Webhook có 3 loại: Tất cả (cả vào cả ra), Chỉ tiền vào, Chỉ tiền ra.

Webhook Chỉ tiền vào mà test rút tiền → không gửi.

Chỉ tiền ra thêm ràng buộc: chỉ hỗ trợ Sacombank, TPBank, VietinBank, và phải dùng TKP (VA nội dung, không dùng VA chính thức). Xem Lưu ý khi chọn Chỉ tiền ra.

4. Tài khoản ngân hàng có trong danh sách

Mở webhook → tab Tài khoản.

Chế độ Tất cả tài khoản: mọi tài khoản đều kích hoạt webhook.

Chế độ Chọn cụ thể: tài khoản phát sinh giao dịch phải có trong cột Đã chọn.

Tài khoản mới không tự thêm

Tài khoản liên kết SAU KHI tạo webhook không tự có trong danh sách tuỳ chọn. Mở webhook, thêm tay rồi lưu.

5. Cấu hình VA

Mỗi tài khoản trong danh sách có 3 chế độ VA:

  • Tất cả VA: nhận mọi VA, kể cả VA tạo sau.
  • Chỉ VA được chọn: chỉ khi giao dịch qua VA trong danh sách tick.
  • Không nhận VA: bỏ qua mọi giao dịch qua VA.

Giao dịch đi qua VA? Kiểm tra chế độ:

  • Không nhận VA: webhook bỏ qua, đổi chế độ.
  • Chỉ VA được chọn: VA đó đã tick chưa?

Ngân hàng chỉ hỗ trợ VA (BIDV, MSB, KienlongBank, OCB) không có tài khoản chính, bắt buộc phải có VA.

6. Mã thanh toán

Nếu Chỉ gửi khi có mã thanh toán bật, giao dịch phải có code nhận diện được. Xem Cấu hình mã thanh toán cho mẫu công ty bạn.

Có bộ lọc Lọc theo mã thanh toán (tiền tố) thì mã phải bắt đầu bằng một trong các tiền tố đó.

Test nhanh: tắt công tắc này, chuyển lại khoản test.

7. Server trả về gì

Mở Lịch sử gửi → dòng mới nhất → tab Response.

Trả HTTP 200/201 với body {"success": true} mới tính thành công. Khác đi bị đánh dấu thất bại dù URL nhận được request. Chi tiết: Phản hồi hợp lệ.

8. Đang bị retry ngầm

Nếu server thỉnh thoảng sập hoặc chậm, SePay sẽ retry. Mỗi lần retry tạo một dòng riêng trong Lịch sử gửi.

Nếu thấy 8 dòng liên tiếp cùng id và đều Failed, nghĩa là giao dịch đó đã mất. Dùng Sự cố để tìm tất cả giao dịch mất và gửi lại.

9. OAuth 2.0 lỗi xác thực

Với OAuth 2.0, SePay phải gọi token endpoint trước rồi mới gọi đến webhook URL kèm Bearer token. Nếu token endpoint hỏng, webhook URL sẽ không nhận được request nào. Lúc debug bạn cần nhìn cả log token, không chỉ log webhook.

Chẩn đoán OAuth 2.0

OAuth 2.0 có thể hỏng ở 5 chỗ. Mở Lịch sử gửi xem log để xác định:

Chỗ hỏngBiểu hiện trong logHướng kiểm tra
Không gọi được token endpointDNS Error / Timeout / Connection Refused ở bước lấy tokenURL token online? Firewall chặn IP SePay?
Token endpoint trả sai statusHTTP 4xx/5xx từ URL tokenEndpoint phải trả 200
Token endpoint body sai format"Phản hồi OAuth không đúng định dạng"Phải có access_token (dạng chuẩn) hoặc data.accessToken (dạng tùy chỉnh)
Token OK, webhook URL trả 401Đã có token rồi mà webhook từ chốiServer chưa xác minh Bearer đúng, hoặc coi token đã hết hạn
Webhook URL trả 200Thành côngKhông cần làm gì
Test token endpoint bằng curl trước

Trước khi cấu hình webhook, gọi thử token endpoint bằng curl:

Bash
1
2
3
curl -X POST https://your-auth-server/oauth/token \
-d "grant_type=client_credentials" \
-u "your_client_id:your_client_secret"

Response phải là JSON status 200 có access_token. OK rồi mới cấu hình webhook.

Cấu hình + flow OAuth xem ở Xác thực OAuth 2.0.

Câu hỏi thường gặp

Khi nào cần đối soát

Retry tự động chỉ trong khoảng 33 phút. Endpoint sập lâu hơn thì webhook mất. Lúc đó dùng đối soát giao dịch hoặc Sự cố để xem danh sách bị ảnh hưởng và gửi lại.

Tiếp theo