- #idempotency
- #api
- #payment-system
- #spring-boot
- #go
Tránh trừ tiền hai lần: Idempotency trong API thanh toánPrevent Double-Charge: Idempotency in Payment APIs
Giải thích lỗi trừ tiền hai lần và cách thiết kế API idempotent để tránh mất tiền khách hàng.Explain the double‑charge bug and how to design an idempotent payment API to protect users.
Mục lụcContents· 7
Bạn có bao giờ nhận được báo cáo "khách hàng bị trừ tiền hai lần" trong log ticket? Đó là một trong những lỗi khó chịu nhất vì nó vừa ảnh hưởng tới uy tín công ty, vừa làm khách hàng mất niềm tin. Thông thường lỗi này xuất hiện khi client gọi lại endpoint /pay sau một timeout, hoặc khi service phía sau thực hiện cùng một giao dịch hai lần vì retry mà không có cơ chế bảo vệ.
Nguyên nhân phổ biến#
- Timeout + retry – Client không nhận được response trong thời gian mong đợi, nên tự động retry. Nếu transaction đã được commit ở lần đầu, lần retry sẽ tạo một bản ghi mới và trừ tiền lại.
- Message duplication – Khi dùng Kafka hoặc RabbitMQ, một tin nhắn có thể được tiêu thụ lại nếu consumer không ack đúng cách.
- Không có request ID – API không nhận được một định danh duy nhất để nhận biết yêu cầu đã được xử lý.
Nguyên tắc idempotency#
- Mỗi request phải có một
Idempotency-Key– một chuỗi ngẫu nhiên do client sinh ra (UUID v4 hoặc hash của payload). Key này phải được gửi trong header, ví dụIdempotency-Key: 123e4567‑e89b‑12d3‑a456‑426614174000. - Lưu trạng thái request – Khi nhận được một key mới, service kiểm tra bảng
idempotency_keys. Nếu key chưa tồn tại, tiến hành giao dịch và ghi lạikey → result. Nếu đã tồn tại, trả về kết quả đã lưu mà không thực hiện lại transaction.
