Skip to Content
View as Markdown

Lỗi

Mọi response 4xx/5xx đều mang cùng envelope JSON:

{ "code": 400, "message": "invalid_input", "details": [ { "field": "items[0].unit_price", "message": "must be a positive decimal string" } ] }
  • code — HTTP status dạng số (400, 401, 404, …). Hữu ích cho xử lý lớp HTTP chung, nhưng cho logic phân nhánh hãy switch trên message — code sẽ không phân biệt được giữa, ví dụ, invalid_input và payment_method_not_supported (cả hai đều 400).
  • message — tên sentinel lower-snake-case (ví dụ INVALID_INPUT trở thành "invalid_input"). Ổn định qua các bản phát hành — switch trên cái này.
  • details — được điền cho lỗi validation. Mảng các object { field, message } để client có thể pin lỗi vào input. Bỏ qua trong các trường hợp khác.

Body lỗi không bao gồm trace ID hay timestamp. Để báo cáo sự cố, hãy gửi cho support header Date của response, các header X-RateLimit-*, merchant ID, endpoint, và thời gian request xấp xỉ.

Lỗi xác thực dùng cấu trúc (structure) khác. Envelope ở trên là cái API trả về cho hầu hết các lỗi. Request bị reject trước khi được xử lý (X-Signature thiếu hoặc không hợp lệ, X-Client-ID không được nhận diện, hoặc X-Timestamp cũ trên lệnh gọi /b2b/v1/*) trả về dưới dạng { "error": "...", "message": "..." }, trong đó error là slug thô (unauthorized / bad_request / service_unavailable) và message mang chi tiết. Không có code dạng số và không có details. Hãy phân nhánh trên HTTP status trước, rồi đọc message. Ví dụ (401):

{ "error": "unauthorized", "message": "invalid signature" }

HTTP status → mã điển hình

HTTPCác giá trị code điển hìnhÝ nghĩa
400INVALID_INPUT, MISSING_REQUIRED, INVALID_FORMAT, INVALID_LENGTH, INVALID_VALUE, PAYMENT_METHOD_NOT_SUPPORTED, AMOUNT_BELOW_MINIMUMBad request — xem details
401INVALID_CREDENTIALS, INVALID_TOKEN, TOKEN_EXPIRED, INVALID_SIGNATURE (B2B); SESSION_EXPIRED (chỉ dashboard)Auth fail — key sai, timestamp hết hạn, chữ ký sai. Các mã đăng nhập dashboard không liên quan đến tích hợp B2B.
402INSUFFICIENT_CREDITSố dư prepaid của merchant đã hết — top up trước khi retry
403FORBIDDEN, IP_BLOCKEDKey hợp lệ nhưng thiếu scope/cấp phép IP cho lệnh gọi này
404NOT_FOUND, RECORD_NOT_FOUNDResource không tồn tại (hoặc không tồn tại cho merchant này)
409ALREADY_EXISTSReplay idempotency với body khác, hoặc state machine từ chối transition
429TOO_MANY_REQUESTS, TOO_MANY_ATTEMPTSĐạt rate limit; back off và retry
500INTERNAL_SERVER_ERROR, EXTERNAL_SERVICE_ERRORLỗi của chúng tôi; an toàn để retry với backoff.
503SERVICE_UNAVAILABLEMột dependency downstream đang down. Retry với backoff

Tham chiếu mã đầy đủ

Các giá trị code bạn có thể thấy:

Xác thực (401)

  • INVALID_CREDENTIALS — combo API key bị reject
  • INVALID_TOKEN — token không parse được hoặc bị tamper
  • TOKEN_EXPIRED — token đã hết hạn
  • SESSION_EXPIRED — session dashboard đã hết hạn
  • INVALID_SIGNATURE — chữ ký HMAC không khớp trên lệnh gọi B2B / webhook

Authorization (403)

  • FORBIDDEN — đã xác thực, nhưng bạn không được phép thực hiện hành động này
  • IP_BLOCKED — request từ địa chỉ IP này bị chặn

Not found (404)

  • NOT_FOUND — chung
  • RECORD_NOT_FOUND — row thiếu cho ID đã cho

Conflict (409)

  • ALREADY_EXISTS — chung

Validation (400)

  • INVALID_INPUT — chung; xem details
  • MISSING_REQUIRED — một trường bắt buộc bị thiếu
  • INVALID_FORMAT — giá trị không khớp định dạng mong đợi (ví dụ UUID, URL, email)
  • INVALID_LENGTH — giá trị quá ngắn hoặc quá dài
  • INVALID_VALUE — giá trị nằm ngoài enum/range cho phép
  • PAYMENT_METHOD_NOT_SUPPORTED — combo provider/asset chưa được enable cho merchant
  • AMOUNT_BELOW_MINIMUM — amount của order dưới sàn theo từng network hoặc theo cấp env. details.floor_usd trong response mang sàn cấu hình (USD) để bạn có thể surface trực tiếp; text message cũng nêu rõ.
  • INSUFFICIENT_BALANCE — ví của người mua không đủ pay asset để cover giao dịch chuyển.
  • INSUFFICIENT_GAS — ví của người mua thiếu phí mạng (gas) bằng đồng native để broadcast giao dịch chuyển.

Payment / billing (402)

  • INSUFFICIENT_CREDIT — số dư prepaid của merchant không thể bù phí mạng (gas) và phí nền tảng. Top up qua dashboard, rồi retry

Rate limiting (429)

  • TOO_MANY_REQUESTS — vượt quá rate limit theo IP
  • TOO_MANY_ATTEMPTS — các attempt fail lặp lại trên cùng resource (ví dụ OTP) chạm throttle

Lỗi server (500)

  • EXTERNAL_SERVICE_ERROR — một provider bên thứ ba bị lỗi
  • INTERNAL_SERVER_ERROR — lỗi bất ngờ; liên hệ support kèm header Date của response và các header X-RateLimit-*

Availability (503)

  • SERVICE_UNAVAILABLE — dịch vụ tạm thời không khả dụng; retry với backoff

Lỗi validation (400)

Khi vấn đề là body request bị lỗi định dạng, details là một mảng để bạn có thể map lỗi về các trường:

{ "code": 400, "message": "invalid_input", "details": [ { "field": "items[0].unit_price", "message": "must be a positive decimal string" }, { "field": "success_url", "message": "must be a valid https URL" } ] }

Lỗi auth (401)

INVALID_SIGNATURE có ba nguyên nhân thường gặp:

  • Sai secret key (kiểm tra lại env var)
  • Drift timestamp > 5 phút (đồng bộ NTP)
  • Sai chuỗi canonical (thường gặp nhất: quên separator \n, hoặc ký một body đã được parse rồi stringify lại khác với cái bạn đã gửi)

Xem Xác thực cho thuật toán ký chính xác.

Rate limit (429)

SurfaceGiới hạn
Mọi route API (kể cả /b2b/v1/*)Theo địa chỉ IP — 500 req/min, bucket chung. Không theo merchant.
Public checkout (/checkout/*)Sub-bucket chặt hơn — 20 req/min theo IP

X-RateLimit-Limit và X-RateLimit-Remaining được include trên mọi response bị rate-limit, không chỉ response thành công. Coi chúng là budget hiện tại cho IP của bạn.

Response bị rate-limit có include header Retry-After (số giây đến khi cửa sổ reset) — hãy tôn trọng nó. Như một fallback, back off với jitter — 1s base + exponential lên 30s.

Rate limit có thể thay đổi. Nếu bạn chạm chúng với traffic hợp lệ (ví dụ đối soát một khoảng lịch sử lớn), hãy liên hệ support.

Thiếu credit (402)

INSUFFICIENT_CREDIT (HTTP 402 Payment Required) nghĩa là số dư prepaid của bạn không thể bù phí mạng (gas) và phí nền tảng cho thao tác bạn cố thực hiện, thường là settle một thanh toán crypto hoặc một hành động on-chain được tài trợ phí mạng. Top up từ merchant dashboard (Billing → Add credit), rồi retry thao tác. Các thao tác đang chạy sẽ tiếp tục tự động khi số dư được top up.

Lỗi server (5xx)

500 nghĩa là chúng tôi không thể xử lý request. Retry với backoff — idempotency_key của bạn đảm bảo bạn không charge gấp đôi nếu request ban đầu đã thành công một phần.

Nếu retry không recover trong một phút, surface một “thanh toán tạm thời không khả dụng” chung cho người mua và liên hệ support với endpoint fail, merchant ID của bạn, header Date của response và các giá trị X-RateLimit-*, và thời gian request xấp xỉ — đó là đủ cho chúng tôi tìm ra request.

Lỗi delivery webhook

Delivery webhook là một kênh failure riêng — chúng không surface dưới dạng lỗi API vì server của bạn không phải là bên đang gọi. Khi một delivery trả về non-2xx (hoặc timeout), nó được retry với exponential backoff tại 0s, 1min, 5min, 15min, 1h, 6h (tổng sáu lần, cùng schedule như Webhooks → Tổng quan). Lịch sử đầy đủ theo từng delivery hiển thị dưới Developers → Webhooks → [endpoint] → Delivery log trong dashboard. Sau lần thứ sáu, delivery được đánh dấu “Failed” trong dashboard, và bạn có thể replay thủ công khi server của bạn khỏe trở lại.