XPay

API v1 · Webhook 2026-09-01

Từ giao dịch đến
luồng xử lý của bạn.

Đọc lịch sử bằng API, nhận sự kiện qua webhook. Bắt đầu với môi trường Test trước khi tích hợp dữ liệu thực tế.

  1. Xác minh email và có gói Donate đang hiệu lực do Admin cấp.
  2. Cấu hình kết nối phù hợp với gateway và giới hạn tài khoản của gói.
  3. Mở API & webhook trên từng dòng kết nối để tạo key/endpoint riêng. Test và Live tách riêng.

Bank hỗ trợ, quyền sử dụng và quota lấy từ cấu hình Admin; tài liệu này không cam kết một danh sách bank hoặc số lượng tài khoản cố định.

01 / API key

Tại Dashboard → Kết nối → API & webhook, chủ workspace hoặc quản trị viên đã xác minh có thể chọn quyền transactions:read, payment_intents:read, payment_intents:write. Chỉ cấp quyền cần thiết; quyền ghi không bao gồm quyền đọc. Môi trường cố định theo kết nối. Chọn IP hoặc CIDR được phép kết nối và thời hạn 1–365 ngày.

Secret chỉ hiển thị một lần. Lưu ở backend của bạn, không đặt vào URL, frontend, log hay kho mã nguồn. Nếu mất secret, thu hồi key cũ rồi tạo key mới.

Authorization: Bearer <API_KEY>
Accept: application/json

Key tự xác định workspace, kết nối và môi trường: không gửi workspace_id, không đổi mode bằng query. IP nguồn, hạn dùng, quyền người tạo, gói Donate và quota đều được kiểm tra mỗi request. Production yêu cầu HTTPS.

02 / Lịch sử giao dịch

GET /api/v1/transactions — tối đa 50 bản ghi/trang, theo ID giảm dần. Dùng origin của hệ thống bạn được cấp; ví dụ dưới đây dành cho localhost.

curl "http://127.0.0.1:3000/api/v1/transactions?direction=credit" \
  -H "Authorization: Bearer <TEST_API_KEY>" \
  -H "Accept: application/json"
Bộ lọc tùy chọn
Tham sốÝ nghĩa
connection_idChỉ được gửi đúng ID kết nối đã gắn với key; bỏ trống sẽ tự dùng kết nối đó.
directioncredit tiền vào, debit tiền ra.
external_idMã giao dịch chính xác từ nguồn.
from / toNgày YYYY-MM-DD, tính theo UTC, gồm cả ngày kết thúc.
cursorGiữ nguyên chuỗi cursor trả về; URL-encode khi gửi.

Response gồm transactions, next_cursor, previous_cursor, mode và date_timezone (UTC). Đọc tiếp bằng next_cursor đến khi null; giữ nguyên bộ lọc khi chuyển trang. Thời gian trên dashboard có thể hiển thị UTC+7.

Mỗi giao dịch có ID, connection_id, external_id, occurred_at, amount, currency, direction và description. Xử lý số tiền bằng kiểu decimal chính xác, không dùng số thực để đối soát. Đây là API đọc lịch sử, không phải API chuyển tiền.

03 / Yêu cầu thanh toán

API hiện chỉ nhận MOCK_BANK Test/VND. Yêu cầu tạo một mã đối soát để khớp tiền vào, không đăng nhập bank hay thực hiện chuyển tiền. Key chỉ được đọc/tạo/hủy yêu cầu của kết nối đã gắn, kể cả phát lại UUID. Lấy ID kết nối từ Dashboard → Kết nối; kết nối phải đang hoạt động và phiên còn hạn.

Endpoint và quyền cần cấp · cuộn ngang nếu cần
EndpointScope
POST /api/v1/payment-intentspayment_intents:write
GET /api/v1/payment-intentspayment_intents:read
GET /api/v1/payment-intents/{id}payment_intents:read
POST /api/v1/payment-intents/{id}/cancelpayment_intents:write
{
  "request_id": "0cf9654c-e798-4215-85da-5c3f9d1e6f96",
  "connection_id": 2,
  "external_order_id": "ORDER-12345",
  "amount": "125000",
  "currency": "VND",
  "expires_in": 900
}

Gửi JSON với Content-Type: application/json và Bearer key. Tạo UUID mới cho mỗi yêu cầu; lưu UUID cùng đơn hàng trước khi gửi. Nếu timeout, gửi lại đúng UUID và toàn bộ nội dung cũ: tạo mới trả 201, phát lại trả 200, đổi nội dung cùng UUID trả 409. Không tạo UUID mới chỉ vì chưa nhận được response.

Mã đơn dài tối đa 100 ký tự, dùng A–Z, 0–9, dấu gạch ngang/gạch dưới và bắt đầu bằng chữ hoặc số. Amount là chuỗi số nguyên dương VND, tối đa 999999999999; expires_in từ 60 đến 86400 giây. Không gửi workspace_id, mode hoặc status. Mã đơn không được lặp trong cùng workspace/môi trường.

Response tạo/chi tiết nằm trong data, gồm ID, status, payment_reference, amount (chuỗi decimal), expires_at và version. Danh sách trả tối đa 20 dòng theo ID giảm dần, có next_before_id và mode. Dùng before_id để đọc tiếp hoặc external_order_id để tìm đúng đơn; giữ nguyên bộ lọc khi phân trang.

Hủy bằng JSON {"version":1} theo version hiện tại. Chỉ hủy yêu cầu đang chờ, chưa hết hạn; hủy lặp trả cùng bản ghi. Không hủy yêu cầu đã khớp. Trạng thái gồm awaiting_payment, cancelled, expired và paid. Khớp yêu cầu đúng mã, đúng số tiền, kết nối và thời hạn; chưa hỗ trợ thiếu/dư tiền, gộp giao dịch hoặc điều chỉnh thủ công.

04 / Nhận webhook

Tạo endpoint tại Dashboard → Kết nối → API & webhook. Endpoint mới được tạm dừng; URL phải là HTTPS với tên miền, port 443, không chứa query, fragment hoặc thông tin đăng nhập. Địa chỉ mạng nội bộ không được phép.

Có thể đăng ký transaction.detected (giao dịch mới), payment_intent.paid (yêu cầu đã khớp), hoặc cả hai. Endpoint chỉ nhận sự kiện của đúng kết nối đã chọn. Endpoint chỉ nhận sự kiện mới khi đủ điều kiện tại thời điểm ghi nhận; bật endpoint không phát lại lịch sử cũ. Secret whsec_… khác với API key và chỉ được trả một lần.

X-NDT-Event-Id: <event UUID>
X-NDT-Timestamp: <Unix timestamp giây>
X-NDT-Signature: v1=<hex HMAC SHA-256>

signed_message = timestamp + "." + raw_body
expected = HMAC_SHA256(full_whsec_secret, signed_message)
  1. Đọc nguyên byte body trước khi parse JSON; không serialize lại để kiểm tra chữ ký.
  2. Kiểm tra timestamp nằm trong khoảng chấp nhận của receiver, ví dụ ±5 phút; đồng bộ đồng hồ server.
  3. Tính HMAC bằng toàn bộ secret, gồm tiền tố whsec_; so sánh chữ ký bằng hàm constant-time.
  4. Sau khi xác thực, đối chiếu ID ở header với body. Ghi nhận event ID duy nhất và công việc cần xử lý trong cùng transaction bền vững.
  5. Trả 2xx sau khi đã lưu an toàn. Event trùng đã lưu cũng trả 2xx; xử lý nghiệp vụ qua worker của bạn.

Payload có id, type, api_version, created_at, mode và data.object. Với transaction.detected, object là giao dịch. Với payment_intent.paid, object có ID yêu cầu, connection_id, external_order_id, payment_reference, amount, currency, status, matched_transaction_id và paid_at. ID và số tiền là chuỗi. Mỗi lần thử giữ nguyên event ID/body nhưng có timestamp và chữ ký mới.

Hai loại sự kiện có ID độc lập và không đảm bảo thứ tự đến. Không dùng transaction.detected để suy ra đơn đã thanh toán. Matching hiện chỉ hỗ trợ MOCK_BANK Test/VND: sự kiện paid ở Test không xác nhận tiền thật. Receiver cần kiểm tra mode và schema theo type trước khi xử lý.

Retry, timeout và kết quả chưa rõ

Mặc định tối đa 8 lần thử tự động. Timeout, lỗi truyền tải, HTTP 408/429/5xx có thể được thử lại; các lỗi khác có thể kết thúc ngay. Timeout không chứng minh receiver chưa nhận.

Delivery thất bại có thể yêu cầu retry thủ công tối đa 3 lượt, mỗi lượt thêm đúng một lần thử nếu quyền và cấu hình còn hợp lệ. Xem payload/lịch sử trong dashboard. Pause hoặc thu hồi quyền không thể thu hồi request đã bắt đầu gửi.

05 / Xử lý lỗi & giới hạn

Phản hồi cần xử lý
HTTPHành động
401Kiểm tra key, thời hạn và trạng thái thu hồi.
403Kiểm tra IP, quyền, mode, gói Donate và quota.
404Kiểm tra ID tài nguyên và workspace.
409Dữ liệu thao tác đã cũ; tải lại trước khi thử lại.
422Sửa trường đầu vào theo lỗi validation.
429 / 503Tôn trọng Retry-After nếu có; backoff có jitter. Không lặp request liên tục.

Quota request/phút dùng chung cho mọi key và mode trong workspace. Đọc X-RateLimit-Limit, X-RateLimit-Remaining và Retry-After. Bộ lọc sai sau khi xác thực vẫn có thể tính quota.

Cần hỗ trợ? Mở trang hỗ trợ. Không gửi API key, signing secret hoặc mật khẩu bank trong yêu cầu hỗ trợ.