Skip to Content
Tham chiếu APIXác thực

Xác thực

InfraIO Pay có hai surface API với mô hình auth khác nhau. Chọn cái khớp với người gọi:

SurfacePath prefixĐối tượngAuth
B2B Merchant/b2b/v1/*Server của bạnKý request HMAC-SHA256
DashboardTheo từng service: /auth/*, /payment/*, /merchant/*, /event/*, /user/*, …Session trình duyệt cho dashboard merchantBearer JWT

Trang này cover surface B2B — cái bạn gọi từ server với một cặp API key. Nếu bạn nhúng dashboard InfraIO hoặc xây tool nội bộ, dùng surface dashboard (tài liệu riêng, chưa public).

Gateway định tuyến mỗi surface theo prefix đầu tiên mà nó strip trước khi forward: /b2b/v1/checkout-sessions/quick đến payment-service dưới dạng /v1/checkout-sessions/quick, và /payment/v1/orders của dashboard đến nó dưới dạng /v1/orders. Nên nếu bạn thấy các path /v1/* trần ở nơi khác, đó là path backend internal sau khi prefix public đã được loại bỏ — client của bạn luôn gửi dạng có prefix. (Một hệ quả cho việc ký: chuỗi canonical B2B ký path kèm prefix /b2b vẫn được gắn — xem dưới.)

Endpoints

Môi trườngBase URL
Testhttps://api-dev.infraio.xyz
Livehttps://api.infraio.xyz

Cùng một pattern URL — môi trường được điều khiển bởi prefix của key (pk_test_… vs pk_live_…), không phải URL.

Cặp key

Bạn nhận được hai giá trị từ merchant dashboard (Developers → API keys → + Add key):

  • Publishable key (pk_test_… hoặc pk_live_…) — nhận diện tài khoản của bạn. Gửi dưới dạng X-Client-ID. An toàn để nhúng trong bundle trình duyệt của bạn (SDK đã làm điều này).
  • Secret key (sk_test_… hoặc sk_live_…) — khóa ký HMAC. Chỉ phía server. Đối xử với nó như password database.

Nếu một secret key bao giờ lọt vào bundle trình duyệt, git repo, dòng log, hay chat chia sẻ — revoke ngay lập tức từ dashboard. Không có cửa sổ overlap; revoke có hiệu lực tức thời. Phát hành key mới và redeploy.

Ký một request

Mọi lệnh gọi đến /b2b/v1/* mang ba header:

X-Client-ID: pk_live_70d0becbd7a53047b0dfe88a960d10ad X-Timestamp: 1715990400 X-Signature: 9a8b7c6d… (hex HMAC-SHA256)

Chữ ký được tính trên một chuỗi canonical:

METHOD + "\n" + PATH + "\n" + TIMESTAMP + "\n" + BODY
  • METHOD — HTTP verb viết hoa (POST, GET, …).
  • PATH — path của request kèm prefix /b2b, không có host và không có query string (ví dụ /b2b/v1/checkout-sessions/quick). Gateway xác thực chữ ký trên raw path đến trước khi strip /b2b, nên prefix phải có mặt. Query parameter không được ký — với GET …?cursor=…&limit=20, chỉ ký path, không phải phần ?….
  • TIMESTAMP — unix seconds, dưới dạng chuỗi decimal (ví dụ "1715990400"), khớp chính xác với X-Timestamp.
  • BODY — raw bytes của body request. Chuỗi rỗng cho GET/DELETE.

Ký bằng HMAC-SHA256 với khóa secret, xuất hex:

import { createHmac } from "node:crypto"; function sign({ method, path, body, secret }: { method: string; path: string; body: string; secret: string; }) { const timestamp = Math.floor(Date.now() / 1000).toString(); const input = [method.toUpperCase(), path, timestamp, body].join("\n"); const signature = createHmac("sha256", secret).update(input).digest("hex"); return { timestamp, signature }; }

Vì sao HMAC, không Bearer?

Một API Bearer-token thuần ship secret duy nhất qua dây trên mọi request. Bất kỳ ai bắt được một log của proxy TLS-terminated đều có chìa khóa tài khoản của bạn. Ký HMAC nghĩa là secret không bao giờ di chuyển — chỉ chữ ký được dẫn xuất, là single-use (gắn vào đúng request đó + đúng phút đó).

Đánh đổi: bạn phải tính chữ ký cho mỗi lệnh gọi. Một SDK server sẽ ẩn điều này; cho đến khi chúng tôi public một SDK, helper ở trên là ~15 dòng theo từng ngôn ngữ.

Tolerance timestamp

Tolerance ràng buộc là ±5 phút (300 giây), được enforce bởi merchant-service khi nó xác thực chữ ký. Bản thân gateway lỏng hơn một chút (310s) như là defence-in-depth, nhưng một request đi qua gateway và fail kiểm tra bên trong vẫn kết thúc với 401 invalid_signature — coi 300s là contract. Hai hệ quả:

  1. Đồng bộ đồng hồ server với NTP. Một cron chạy dài hạn với đồng hồ trôi sẽ fail không đều.
  2. Đừng pre-compute và xếp hàng đợi chữ ký. Nếu một request nằm trong queue retry >5 phút, chữ ký của nó hết hạn.

Scope của key

Secret key mang một hoặc nhiều bundle scope sau:

ScopeUse case dự kiến
readList/đọc orders, sessions, refunds
write_orderTạo checkout sessions, orders
write_refundPhát hành refund, mint token refund-request
webhook_manageTạo/cập nhật/xóa webhook endpoint

Dashboard phát hành key “full access” mặc định (cả bốn scope). Bạn có thể phát hành key scope hạn chế từ Developers → API keys → + Add key và chỉ tick các scope mà tích hợp cần.

Enforce scope hiện tại là advisory, không gate. Scope được ghi trên key và hiển thị lại cho bạn trong dashboard, nhưng middleware của gateway hiện chưa reject các lệnh gọi out-of-scope — bất kỳ key sk_… hợp lệ nào đều hoạt động như full-access hôm nay. Scope gate theo từng endpoint nằm trong bản phát hành tiếp theo. Đừng dựa vào scope như là ranh giới bảo mật chưa; coi chúng như nhãn và rotate / revoke key để hạn chế truy cập trong thời gian chờ.

Chữ ký được xác thực ở đâu

Xác thực HMAC xảy ra một lần, tại gateway. Gateway:

  1. Đọc X-Client-ID, X-Timestamp, X-Signature.
  2. Tra cứu merchant + secret qua pk_…, chạy kiểm tra cửa sổ timestamp, tính lại chữ ký, so sánh constant-time.
  3. Khi thành công, strip auth header, đóng dấu request với header nội bộ (X-B2B-Auth: 1, X-Merchant-ID, X-Merchant-Domain) và forward đến downstream service (payment-service, merchant-service, v.v.). Môi trường + scope đã resolve KHÔNG được inject hôm nay — code downstream cần môi trường dẫn xuất từ body request / cấu hình theo merchant, không phải từ header.
  4. Khi thất bại, trả về 401 INVALID_SIGNATURE mà không bao giờ chạm vào backend.

Downstream service không chạy lại HMAC — chúng tin tưởng header gateway đã inject và hành động trên merchant mà gateway đã resolve. Chúng cũng không scope-gate theo từng endpoint: như đã lưu ý ở trên, scope của key không được inject, nên bất kỳ sk_… đã xác thực nào cũng đến được bất kỳ endpoint nào cho merchant của nó (enforce scope là advisory hôm nay — xem callout dưới Scope của key). Điều này quan trọng theo hai cách:

  • Nếu bạn vận hành reverse proxy của riêng mình trước InfraIO Pay, đừng strip X-B2B-Auth / X-Merchant-ID (và cũng đừng forge chúng — gateway reject các inbound request mang chúng ở public edge).
  • Path public-network (/b2b/v1/*) là surface duy nhất chạy bước HMAC. gRPC nội bộ giữa các service của chúng tôi dùng mTLS — một mô hình tin cậy khác không chấp nhận X-Client-ID.

Tiếp theo

  • Lỗi — hình dáng response trên 4xx/5xx.
  • Bảo mật → API keys — rotate, revoke, làm gì nếu secret bị lộ.
  • Webhooks → Xác thực chữ ký — dùng một scheme HMAC khác (header X-Signature: sha256=…, ký X-Timestamp + "." + raw_body, cộng thêm X-Signature-Prev tùy chọn trong cửa sổ grace 24 giờ khi rotate). Đừng nhầm hai scheme — chúng dùng chung thuật toán hash nhưng bytes được ký và họ secret (whsec_… vs sk_…) là khác nhau.