Sign in
Giới thiệu/Xác thực

Xác thực và ký yêu cầu

Ký các yêu cầu API bằng HMAC-SHA256 với project UUID và API key của bạn.

Mọi yêu cầu API (ngoại trừ webhook đến) đều phải kèm theo project UUID và chữ ký yêu cầu. Chữ ký chứng minh rằng yêu cầu xuất phát từ bạn và không bị ai thay đổi trên đường truyền.

API keys

2328.io sử dụng hai key dùng chung một thuật toán ký nhưng áp dụng cho các endpoint khác nhau:

KeyDùng cho
API keyThanh toán, ví tĩnh, số dư, tỷ giá và xác minh webhook thanh toán / ví tĩnh
Payout API keyTất cả endpoint /v1/payout/* và xác minh webhook rút tiền

Cả hai key đều nằm trong phần cài đặt project tại 2328.io. Các ví dụ bên dưới ghi chung chung là "API key" — hãy thay bằng key phù hợp cho endpoint mà bạn đang gọi.

Tuyệt đối không lẫn lộn hai key: ký một yêu cầu rút tiền bằng API key thông thường (hoặc ký yêu cầu thanh toán bằng Payout key) sẽ trả về lỗi chữ ký.

Headers bắt buộc

HeaderTypeRequiredDescription
Content-TypestringyesLuôn là application/json
projectstringyesProject UUID của bạn
signstringyesChữ ký HMAC-SHA256 của yêu cầu, được tính bằng API key của bạn
User-AgentstringĐịnh danh ứng dụng của bạn (ví dụ MyShop/1.4 (+https://myshop.example)). Yêu cầu không có User-Agent có thể bị chặn.

Cách hoạt động của chữ ký

Hãy hình dung chữ ký như là dấu vân tay của thân yêu cầu. Nó được tạo ra bằng cách:

  1. Serialize thân yêu cầu sang JSON (compact — không có khoảng trắng dư).
  2. Mã hóa Base64 chuỗi JSON đó. Bước này chuẩn hóa đầu vào giữa các ngôn ngữ — khi đã là ASCII thuần, mọi ngôn ngữ đều cho ra cùng các byte để HMAC.
  3. Tính HMAC-SHA256 của chuỗi Base64 bằng API key của bạn, sau đó chuyển kết quả sang hex chữ thường.

Đối với GET và các loại yêu cầu khác không có thân, hãy ký chuỗi rỗng thay vì JSON.

Chữ ký của chuỗi rỗng là cố định với một API key cho trước. Bạn có thể cache lại nếu thực hiện nhiều cuộc gọi GET.

Triển khai

PHP
<?php
function apiSign(array $data, string $apiKey): string {
    $json = json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
    $base64 = base64_encode($json);
    return hash_hmac('sha256', $base64, $apiKey);
}

Yêu cầu không có thân (GET)

Đối với các yêu cầu không có thân (ví dụ GET /v1/payout/status/{uuid}), hãy ký một chuỗi rỗng:

Shell
SIGN=$(printf '' | openssl dgst -sha256 -hmac "$API_KEY" -hex | awk '{print $NF}')

Ví dụ yêu cầu đầy đủ

Shell
curl -X POST https://api.2328.io/api/v1/payment \
  -H "Content-Type: application/json" \
  -H "User-Agent: MyShop/1.0 (+https://myshop.example)" \
  -H "project: YOUR_PROJECT_UUID" \
  -H "sign: YOUR_HMAC_SIGNATURE" \
  -d '{"amount":"100.00","currency":"USD","order_id":"ORDER-123"}'

Tuyệt đối không để lộ API key trong mã chạy phía client. Hãy ký yêu cầu trên backend của bạn. Một API key bị rò rỉ sẽ cho bất kỳ ai toàn quyền truy cập tài khoản merchant của bạn.

Xác minh chữ ký webhook

Khi 2328.io gửi webhook cho bạn, cùng thuật toán đó được chạy ngược lại:

  1. Lấy trường sign ra khỏi payload.
  2. Mã hóa JSON các trường còn lại (compact, không khoảng trắng).
  3. Mã hóa Base64 chuỗi đó.
  4. Tính HMAC-SHA256 bằng key phù hợp.
  5. So sánh với sign nhận được bằng cách so sánh thời gian không đổi (hash_equals, crypto.timingSafeEqual, hmac.compare_digest, subtle.ConstantTimeCompare, OpenSSL.fixed_length_secure_compare).

Key dùng để ký phụ thuộc vào nguồn webhook:

WebhookKey để xác minh
Webhook thanh toán / ví tĩnh (/v1/payment, /v1/static-wallet)API key
Webhook rút tiền (/v1/payout)Payout API key

Lỗi xác minh thường gặp. Bộ mã hóa JSON của bạn phải tạo ra đúng những byte giống hệt như bên gửi đã tạo — nếu không, Base64 sẽ khác đi và chữ ký sẽ không khớp.

  • Go: dùng json.NewEncoder với SetEscapeHTML(false). json.Marshal mặc định sẽ escape <, >, & thành < và làm hỏng chữ ký.
  • Python: truyền ensure_ascii=False vào json.dumps. Nếu không, các ký tự không thuộc ASCII (Kirin, Trung Quốc, …) sẽ bị escape thành \uXXXX.
  • JSON rút gọn: không có khoảng trắng giữa các trường (separators=(",", ":") trong Python).
  • Thứ tự trường (Go): một map[string]any thường sẽ ngẫu nhiên hóa thứ tự khóa khi mã hóa lại. Hãy dùng json.RawMessage, một struct có thứ tự, hoặc loại bỏ sign khỏi byte gốc.

Nếu việc xác minh vẫn thất bại, hãy tự chạy apiSign trên payload — nó phải tạo ra cùng chuỗi hex với sign đã nhận.

Một chữ ký hợp lệ không ngăn được replay. Nó chỉ chứng minh rằng webhook đến từ 2328.io — không ngăn được kẻ tấn công gửi lại một webhook đã bị bắt được sau đó. Luôn kiểm tra idempotency bằng uuid (hoặc txid cho ví tĩnh) trước khi ghi có tiền. Từ chối với HTTP 401 nếu chữ ký bị thiếu hoặc sai.

Các ví dụ mã đầy đủ có trên Webhook Notifications. Xử lý retry và quy tắc idempotency có trong Best practices.