Files
sys-analysis-design/sa-output/e-commerce/02-architecture/adr/ADR-004_idempotency-doi-soat-webhook-thanh-toan.md
Canhchimlac 343ad8bbbc save
2026-09-15 16:02:30 +07:00

11 KiB

ADR-004 — Cơ chế idempotency cho webhook thanh toán + job đối soát bù định kỳ

Tên file: adr/ADR-004_idempotency-doi-soat-webhook-thanh-toan.md

Status Proposed
Date 2026-09-15
Người quyết SA + Tech Lead (tích hợp/idempotency — decision-radar.md §5)
Người đề xuất SA (qua skill sa-2-architecture, hoạt động adr)
Điểm radar ~6/10
Supersedes —
Superseded by —
Liên quan ASR-004 · QAS-014 · BR-CART-06 (BA) · DRV-08 · ARISK-03 · CMP-11

⚠️ ADR bất biến sau khi Accepted. Muốn đổi quyết định thì viết ADR mới có Supersedes: ADR-004 và đổi trạng thái bản cũ thành Superseded by.


1. Bối cảnh

Xác nhận thanh toán qua webhook VNPay/Momo (bên thứ ba) chưa được xác minh khả thi gần thời gian thực (DRV-08, ASM-01 của BA chưa xác minh). Nếu webhook trễ/lỗi/gọi lặp, đơn hàng có thể kẹt "chờ thanh toán" dù tiền đã thu, hoặc bị cập nhật trạng thái sai nếu xử lý webhook không idempotent. BR-CART-06 (BA) đã đặt quy tắc nghiệp vụ: chỉ cập nhật Payment.status = success khi webhook có chữ ký hợp lệ và trong thời hạn — nhưng con số thời hạn cụ thể chưa chốt (thuộc phạm vi kỹ thuật của ADR này, không phải quyết định BA).

Ràng buộc đang chi phối:

Nguồn Nội dung
ASR-004 Webhook phải idempotent + có job đối soát bù
BR-CART-06 (BA) Chỉ cập nhật Payment.status=success khi chữ ký hợp lệ + trong hạn
QAS-014 Job đối soát phát hiện lệch trong ≤15 phút (đề xuất SA, OQ-021 chưa xác nhận)
ARISK-03 VNPay/Momo chưa có sandbox thật — thiết kế chưa kiểm chứng

Cái đã biết chắc / cái còn là giả định:

Điều 🟢 Đã kiểm chứng / 🔴 Giả định Bằng chứng
Webhook có thể bị gọi lặp lại (retry của VNPay/Momo theo tài liệu công khai) 🟡 Ước lượng có cơ sở Thực hành phổ biến của cổng thanh toán, chưa xác nhận với sandbox thật
gateway_transaction_ref là khoá duy nhất, ổn định qua các lần gọi lặp 🔴 Giả định Chưa có tài liệu API/sandbox thật (CON-04)
Tần suất đối soát ≤15 phút đủ nhanh để tránh khiếu nại CSKH 🔴 Giả định (ASM-18) OQ-021 — Tech Lead chưa xác nhận

2. Phương án đã cân nhắc

PA-1 — Chỉ dựa vào webhook, không có job đối soát bù

Mô tả Cập nhật Payment.status hoàn toàn dựa vào webhook đến; không có cơ chế nào chủ động kiểm tra lại
Ưu Đơn giản nhất, không cần job chạy định kỳ
Nhược Nếu webhook mất/trễ vĩnh viễn (lỗi mạng, đối tác không gửi lại), đơn hàng kẹt "chờ thanh toán" vô thời hạn dù tiền đã thu — vi phạm trực tiếp DRV-08/ASR-004
Chi phí đảo ngược Cao — phải xây job đối soát sau khi đã có dữ liệu thật bị kẹt, kèm backfill thủ công cho các đơn đã kẹt

PA-2 — Webhook idempotent (khoá theo gateway_transaction_ref) + job đối soát bù định kỳ (chọn)

Mô tả Webhook endpoint kiểm tra idempotency key = gateway_transaction_ref trước khi ghi; nếu đã xử lý, trả 200 mà không ghi lại. Job đối soát chạy định kỳ (đề xuất ≤15 phút, OQ-021), tra cứu API đối tác để so khớp Payment.status
Ưu Chống hiệu ứng phụ khi webhook gọi lặp; phát hiện được các trường hợp webhook mất hoàn toàn (job chủ động tra cứu, không phụ thuộc webhook đến)
Nhược Thêm một job chạy định kỳ cần giám sát riêng (alerting nếu job tự nó lỗi); cần bảng log webhook đã nhận để so khớp
Chi phí đảo ngược Trung bình — đổi tần suất/cơ chế đối soát sau khi có dữ liệu thật tốn công viết lại job + backfill, nhưng không ảnh hưởng mô hình dữ liệu cốt lõi

PA-3 — Chỉ dùng job polling, không nhận webhook

Mô tả Bỏ hẳn endpoint webhook, chỉ dựa vào job định kỳ tra cứu API đối tác để cập nhật trạng thái
Ưu Không cần lo idempotency cho webhook đến (không có webhook)
Nhược Độ trễ xác nhận thanh toán phụ thuộc hoàn toàn chu kỳ polling (tối thiểu bằng tần suất job) — tệ hơn nhiều so với gần thời gian thực; tăng số lượng API call tới đối tác (chi phí + rủi ro rate limit)
Chi phí đảo ngược Trung bình — thêm lại webhook sau này không khó, nhưng đã đánh đổi trải nghiệm người dùng trong thời gian dùng PA-3

Bảng so sánh

Tiêu chí PA-1 PA-2 PA-3
Đáp ứng ASR-004 (idempotent + đối soát bù) ❌ ✅ 🔶 Có đối soát, không idempotent webhook (không có webhook)
Độ trễ xác nhận thanh toán (QAS-002) Nhanh nhất khi webhook tới đúng hạn, nhưng vô hạn khi mất Nhanh (webhook) + có lưới an toàn (job) Chậm nhất (phụ thuộc chu kỳ polling)
Chi phí vận hành thêm Thấp nhất Trung bình (1 job định kỳ) Trung bình-cao (nhiều API call hơn)

3. Quyết định

Chọn PA-2 — Webhook idempotent (khoá gateway_transaction_ref) + job đối soát bù định kỳ.

Vì sao: Đây là phương án duy nhất vừa giữ được độ trễ thấp cho trường hợp bình thường (webhook tới đúng hạn) vừa có lưới an toàn khi webhook trễ/mất — đúng yêu cầu ASR-004.

Phạm vi áp dụng: Toàn bộ luồng xác nhận thanh toán VNPay/Momo (không áp dụng cho COD — không có webhook).

Điều kiện chuyển Proposed → Accepted

Điều kiện Ai xác nhận Trạng thái hiện tại
Có sandbox VNPay/Momo thật để kiểm chứng hành vi retry webhook Tech Lead Chưa có (ARISK-03, CON-04)
OQ-021 — Tech Lead xác nhận tần suất job đối soát cụ thể Tech Lead Mở (đề xuất tạm ≤15 phút)
Không bắt buộc POC theo decision-radar.md §6 (không phải con số hiệu năng lớn, nhưng cần sandbox thật để xác minh hành vi đối tác) — Chờ sandbox

4. Phương án bị loại và lý do

Phương án Loại vì Gắn với Điều kiện nào thì xét lại
PA-1 — Chỉ dựa vào webhook, không đối soát Không phát hiện được trường hợp webhook mất hoàn toàn — vi phạm ASR-004 ASR-004, DRV-08 Không xét lại trừ khi VNPay/Momo đảm bảo bằng SLA hợp đồng webhook không bao giờ mất — chưa có tiền lệ ngành
PA-3 — Chỉ polling, không webhook Độ trễ xác nhận kém hơn hẳn, tăng API call — vi phạm gián tiếp QAS-002 (trải nghiệm checkout) QAS-002, QAS-014 Nếu VNPay/Momo không hỗ trợ webhook (một số cổng thanh toán khác trong tương lai) — xét lại cho riêng đối tác đó

5. Hệ quả

Hệ quả tích cực

  • Chống được hiệu ứng phụ khi webhook gọi lặp (idempotency key)
  • Có lưới an toàn phát hiện đơn kẹt trong thời gian giới hạn (đề xuất ≤15 phút), giảm rủi ro khiếu nại CSKH (DRV-08)

Hệ quả tiêu cực phải sống chung

  • Cần bảng log webhook đã nhận (ReconciliationLog) — tăng dung lượng lưu trữ, cần retention policy (thuộc DAT, hoạt động 5)
  • Job đối soát tự nó có thể lỗi — cần alerting riêng cho chính job này (không chỉ alerting cho luồng chính)

Cái quyết định này khoá lại

Muốn đổi về sau thì Tốn
Đổi khoá idempotency từ gateway_transaction_ref sang cơ chế khác Cần migrate log webhook đã có + rà lại toàn bộ giao dịch lịch sử

Việc phát sinh

Việc Chủ Hạn Ghi ở đâu
Đàm phán sandbox VNPay/Momo Tech Lead + PO Trước GĐ2 ICD hoàn tất ARISK-03
Thiết kế bảng ReconciliationLog + retention SA (hoạt động dat) GĐ2 tiếp theo DAT_e-commerce
Thêm alerting cho chính job đối soát (job lỗi/không chạy) SA (hoạt động inf) GĐ2 tiếp theo INF_e-commerce

6. Cách kiểm chứng quyết định này được tuân thủ

Cách kiểm Công cụ Chạy ở đâu FIT
Gọi lặp cùng một webhook payload không tạo hiệu ứng phụ (không nhân đôi trạng thái/log) Integration test CI + staging trước mỗi release FIT-07 (ứng viên)
Job đối soát phát hiện lệch giả lập trong ≤15 phút Test giả lập webhook trễ/mất trên staging Trước go-live — (cần sandbox thật, ARISK-03)

7. Điều kiện xét lại

Dấu hiệu Ngưỡng Ai theo dõi
Tần suất job đối soát thật khác đề xuất (OQ-021 trả lời khác ≤15 phút) Khi Tech Lead xác nhận số khác Tech Lead
Sandbox thật cho thấy webhook có hành vi khác giả định (VD không gọi lặp, hoặc gọi lặp với gateway_transaction_ref khác nhau) Khi có sandbox Tech Lead

8. Tham chiếu

  • POC: Không bắt buộc — nhưng cần sandbox thật (ARISK-03) trước khi kiểm chứng đầy đủ
  • Bài đo: FIT-07 (ứng viên GĐ3)
  • Tài liệu ngoài: BR_CartCheckout_v1.0.md §1 BR-CART-06 (BA)
  • Thảo luận: ASR_e-commerce_v1.0.md §B2 ASR-004

9. Review log (không đổi Status)

Ngày Người review Vai trò Quyết định Lý do chưa chuyển Accepted
2026-09-15 Điều phối dự án (thay mặt Tech Lead, chế độ chạy thử) Tech Lead (ký thay, ngoại lệ DEC-01) Reviewed (Proposed giữ nguyên) — nội dung đủ làm cơ sở thiết kế tiếp (icd/dat/sec/inf/fail) Chưa có chữ ký thật Tech Lead; cần chạy thử trên sandbox đối tác thanh toán trước khi chốt

Đây là ghi nhận review nội dung, không phải "sign" theo nghĩa gate: Status giữ nguyên Proposed. Xem 00-index/ADL_e-commerce.md (Change Log — Review 2026-09-15) và ADL §8 "Việc phải làm" cho điều kiện chuyển Accepted. Confidence tổng thể của lượt review: 🔴 — AG2 chưa ký.