API hóa đơn tự động Pay2S: Endpoint, HMAC, chống trùng và trạng thái

API hóa đơn tự động Pay2S kết nối máy chủ với hóa đơn nháp qua cổng xác thực, minh họa bảo mật và chống trùng.

API hóa đơn tự động Pay2S dành cho server của khách hàng cần tạo nháp, gửi nháp tới nhà cung cấp, phát hành và tra cứu hóa đơn qua kết nối đã cấu hình. API không tạo link thanh toán, không cộng tiền và không tự xác minh một đơn hàng bên ngoài đã thanh toán. Vì vậy, backend của doanh nghiệp phải chịu trách nhiệm xác định đơn nào đủ điều kiện trước khi gửi yêu cầu.

Bài viết này trên hoadontudong.com.vn tập trung vào những phần dễ sai nhất: giữ nguyên JSON đã ký, tạo chữ ký HMAC-SHA256, phân biệt khóa tài khoản chính với khóa Partner API, dùng external_ref để chống trùng, tra cứu khi timeout và tách Sandbox khỏi Production. Tài liệu chính thức vẫn là nguồn cần đối chiếu khi triển khai thực tế.

Endpoint và xác thực HMAC

Mọi thao tác dùng POST tới https://api.pay2s.vn/api/v1/einvoices với Content-Type application/json, X-Partner-Code, X-Access-Key, X-Timestamp và X-Signature. Timestamp là Unix 10 chữ số, thời gian lệch tối đa 300 giây. Chuỗi ký gồm đúng bốn dòng: timestamp, POST, đường dẫn /api/v1/einvoices và SHA-256 của nguyên body JSON; giữa các dòng dùng LF và không thêm xuống dòng ở cuối.

Secret Key phải được giữ ở backend và không đưa vào frontend, log hoặc request gửi cho bên thứ ba. Sau khi tạo body, hãy băm đúng chuỗi sẽ gửi, ký đúng canonical string rồi gửi chính body đó. Không serialize lại JSON sau khi ký vì thứ tự, cách biểu diễn số hoặc khoảng trắng có thể làm digest thay đổi. Giới hạn tài liệu nêu là 256 KiB cho JSON và 60 request mỗi phút trên mỗi tài khoản.

Các action và vòng đời hóa đơn

Action connections trả về các kết nối đang hoạt động thuộc tài khoản đã xác thực. Action create_draft lưu bản nháp tại Pay2S, chưa gọi nhà cung cấp và chưa phát hành nếu auto_submit và auto_publish đều tắt. Submit_draft gửi bản nháp sang nhà cung cấp nhưng chưa ký cấp số. Publish phát hành sau khi đã kiểm tra. Detail đọc dữ liệu Pay2S, sync yêu cầu đồng bộ từ nhà cung cấp, còn files lấy PDF hoặc XML dạng Base64 khi nhà cung cấp hỗ trợ.

Mỗi bản ghi nên có connection_id, environment, external_ref và danh sách items hợp lệ. API yêu cầu buyer_name hoặc buyer_contact_name; doanh nghiệp cần tên đơn vị, mã số thuế và địa chỉ, còn cá nhân cần họ tên và địa chỉ. Dòng hàng có name bắt buộc, quantity lớn hơn 0, unit_price không âm và tax_rate thuộc tập giá trị được tài liệu quy định. Tổng tiền do backend tính, không nhận tổng do client tự khai.

Chống trùng, timeout và Production

external_ref là mã đơn riêng và duy nhất theo tài khoản chính, xuyên các kết nối và môi trường. Nếu gửi lại cùng mã cùng dữ liệu, API trả lại cùng ID và đánh dấu duplicate thay vì tạo thêm hóa đơn. Nếu cùng mã nhưng khác dữ liệu hoặc kết nối, có thể nhận HTTP 409. Khi request create_draft timeout, hãy ký lại timestamp mới nhưng giữ nguyên external_ref và dữ liệu; đừng đổi mã chỉ vì chưa nhận được phản hồi.

Trạng thái processing hoặc unknown yêu cầu tra cứu trước khi thử lại. Khi gửi nháp hoặc phát hành bị timeout, gọi detail hoặc sync; không tạo hóa đơn mới để tránh trùng. Với Production, các bước phải dùng environment production, đúng kết nối Production và confirm_production true theo quy định API. Một response status true chưa đủ để kết luận hóa đơn đã phát hành; cần kiểm tra HTTP, data.status và mã hóa đơn.

Checklist vận hành nhanh

  • Secret Key và Partner Code của tài khoản chính được lưu ở secret manager hoặc biến môi trường backend.
  • Canonical string dùng LF, không có dòng trống cuối và digest lấy từ đúng body sẽ gửi.
  • external_ref được tạo ổn định, không đổi khi retry sau timeout.
  • Mọi response đều kiểm tra cả HTTP status, trường status và data.
  • processing, unknown hoặc lỗi 500/502/503 đều được tra cứu trước khi tạo lại.
  • Production có confirm_production, quyền phát hành và kết nối đúng môi trường.

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

API có tự kiểm tra đơn đã thanh toán không?

Không. API hóa đơn điện tử không tự xác minh đơn hàng bên ngoài đã thanh toán. Backend phải kiểm tra đơn, số tiền và giao dịch trước khi tạo hoặc phát hành.

Có thể dùng khóa Partner API độc lập không?

Tài liệu yêu cầu Partner Code, Access Key và Secret Key của tài khoản chính Pay2S; không dùng khóa Partner API độc lập, khóa nhân viên hoặc token Dashboard.

Khi timeout ở publish có nên gọi lại ngay không?

Không nên gọi lại mù quáng. Hãy detail hoặc sync để xác định trạng thái processing, issued, failed hay unknown trước.

Đọc tiếp trong cụm hóa đơn tự động Pay2S

API hóa đơn điện tử Pay2S cho phép backend tạo nháp, gửi nháp, phát hành và đọc hóa đơn qua kết nối đã cấu hình. Hướng dẫn của hoadontudong.com.vn pay2s tập trung vào giao thức và vòng đời yêu cầu; xem riêng thiết kế chống trùng và xử lý timeout khi xây dựng luồng vận hành.

Điều kiện trước khi tích hợp

Tài khoản chính cần gói Basic trở lên, được bật HĐĐT, có kết nối đã kiểm tra và mẫu mặc định. Khi gửi store_id, cửa hàng phải được gán mẫu thuộc kết nối đó. Production cần quyền phát hành thật.

Dùng Partner Code, Access Key và Secret Key của tài khoản chính, cùng bộ khóa tích hợp thanh toán. Không dùng khóa Partner API độc lập, khóa nhân viên/cửa hàng hoặc token Dashboard. Secret Key chỉ được giữ ở backend. API này không tạo link thanh toán và không tự xác minh đơn hàng bên ngoài đã trả tiền.

Endpoint và chữ ký HMAC

POST https://api.pay2s.vn/api/v1/einvoices
Content-Type: application/json
X-Partner-Code: YOUR_PARTNER_CODE
X-Access-Key: YOUR_ACCESS_KEY
X-Timestamp: UNIX_SECONDS
X-Signature: LOWERCASE_HMAC_SHA256_HEX

Timestamp dùng Unix giây 10 chữ số; sai lệch tối đa 300 giây. Body tối đa 256 KiB. Chuỗi ký có đúng bốn dòng, phân tách LF và không thêm xuống dòng cuối:

{timestamp}
POST
/api/v1/einvoices
{sha256_hex_cua_nguyen_body_JSON}

Tính HMAC-SHA256 với Secret Key, xuất hex chữ thường. Body gửi đi phải chính là chuỗi JSON đã băm; không serialize lại sau khi ký.

Ví dụ Node.js: đọc danh sách kết nối

Mẫu dưới đây chỉ gọi hành động đọc kết nối. Cấu hình ba biến môi trường trên backend; không dán khóa vào frontend hoặc bài viết.

import { createHash, createHmac } from 'node:crypto';
const path = '/api/v1/einvoices';
const body = JSON.stringify({ action: 'connections' });
const timestamp = String(Math.floor(Date.now() / 1000));
const hash = createHash('sha256').update(body, 'utf8').digest('hex');
const canonical = [timestamp, 'POST', path, hash].join('\n');
const signature = createHmac('sha256', process.env.PAY2S_SECRET_KEY)
  .update(canonical, 'utf8').digest('hex');
const response = await fetch('https://api.pay2s.vn' + path, {
  method: 'POST', body,
  headers: {
    'Content-Type': 'application/json',
    'X-Partner-Code': process.env.PAY2S_PARTNER_CODE,
    'X-Access-Key': process.env.PAY2S_ACCESS_KEY,
    'X-Timestamp': timestamp, 'X-Signature': signature
  }, signal: AbortSignal.timeout(90000)
});
const result = await response.json();
if (!response.ok || result.status !== true)
  throw new Error(result.message || 'Yêu cầu thất bại');
console.log(result.data);

Các hành động theo vòng đời hóa đơn

action Kết quả cần phân biệt
connections Lấy ID kết nối thực tế thuộc tài khoản.
create_draft Tạo nháp tại Pay2S; mặc định chưa gọi nhà cung cấp.
submit_draft Gửi nháp sang nhà cung cấp; chưa ký/phát hành.
publish Ký/cấp số theo môi trường và quyền.
detail Đọc dữ liệu đã lưu tại Pay2S.
sync Đồng bộ trạng thái từ nhà cung cấp, sau đó đọc detail.
files Lấy PDF/XML Base64 khi nhà cung cấp hỗ trợ.

Khi tạo nháp, gửi connection_id, environment, external_ref, người mua và items. Mỗi dòng có name, quantity, unit_price, tax_rate; mã và đơn vị dùng code, unit. Số lượng phải dương, đơn giá không âm. Tổng do backend tính.

Lưu data.id làm invoice_id cho các bước sau. external_ref duy nhất theo tài khoản chính, xuyên môi trường/kết nối; nên dùng tiền tố Sandbox và Production khác nhau.

Production, tự gửi và tự phát hành

Production cần đúng kết nối, environment: "production", confirm_production: true ở các bước tạo/gửi/phát hành và quyền thực tế của tài khoản. Không tự chuyển môi trường bằng cách đổi một trường khi kết nối vẫn là Sandbox.

Với create_draft, chọn auto_submit: true để gửi nháp, hoặc auto_publish: true để tạo và phát hành. Không bật cả hai; dùng boolean JSON. Nếu gửi lỗi sau khi đã lưu nháp, phản hồi có thể có status: false nhưng vẫn có ID và local_draft_saved: true; giữ ID để kiểm tra.

Đọc trạng thái và giới hạn

draft là nháp; kiểm tra thêm has_provider_reference. processing cần đợi; issued là đã phát hành. Với failed hoặc unknown, xem nhật ký, đồng bộ và đối soát trước khi gửi lại. Phiên bản được rà soát dùng polling, chưa có webhook riêng cho trạng thái HĐĐT.

Giới hạn 60 request/phút/tài khoản tính chung hành động; gặp 429 thì đợi phút kế tiếp. Với files, chọn file_type là all, pdf hoặc xml và giải mã Base64 thành tệp. Không mặc định API luôn trả URL tải công khai.

Đối chiếu đặc tả phiên bản: API HĐĐT chính thức Pay2S, rà soát 13/10/2026.

Biên soạn theo tài liệu Pay2S, rà soát ngày 13/10/2026. Xem cẩm nang hóa đơn điện tử Pay2S để đi theo toàn bộ lộ trình.

Logo Pay2S by Web2M trên hoadontudong.com.vn

hoadontudong.com.vn pay2s · Giải pháp hóa đơn tự động · Tư vấn triển khai.

Leave your comment