# Idempotency key: cách chống trừ tiền hai lần khi client bấm lại hoặc retry

- URL: https://dramacongty.com/p/22-idempotency-key-cach-chong-tru-tien
- Chủ đề: Tech
- Tác giả: Drama Công Ty (@dramacongty, tài khoản chính thức của Drama Công Ty)
- Ngày đăng: 2026-10-09
- Thẻ: backend, api, thanh-toan, system-design
- Lượt thích: 0

Người dùng bấm "Thanh toán", mạng chập chờn, app không nhận được phản hồi nên gửi lại. Server nhận **hai** request giống hệt nhau. Nếu không xử lý, khách bị trừ tiền hai lần. Đây cũng là câu hỏi rất hay gặp ở vòng system design.

## Ý tưởng

Mỗi **thao tác** (không phải mỗi request) có một khoá duy nhất do client tạo, ví dụ một UUID, gửi kèm trong header `Idempotency-Key`. Server đảm bảo: cùng một khoá thì thao tác chỉ chạy **một lần**, các lần gửi lại nhận về đúng kết quả của lần đầu.

## Cách làm phổ biến

1. Tạo bảng lưu khoá: khoá, người dùng, mã băm nội dung request, trạng thái (đang xử lý, xong), kết quả trả về, thời điểm tạo. Đặt **ràng buộc unique** trên (người dùng, khoá).
2. Khi nhận request, **insert khoá trước** trong cùng transaction với thao tác chính, hoặc insert với trạng thái "đang xử lý".
   - Insert được: đây là lần đầu, xử lý bình thường, lưu kết quả.
   - Bị trùng khoá và đã xong: trả lại kết quả đã lưu.
   - Bị trùng khoá và đang xử lý: trả lỗi kiểu 409 để client chờ rồi hỏi lại.
3. Cùng khoá nhưng **nội dung request khác** (số tiền khác): từ chối, vì đó là lỗi phía client.
4. Xoá khoá cũ sau một thời gian (vài giờ đến vài ngày), đủ dài hơn thời gian client có thể retry.

Ràng buộc unique của database là phần quan trọng nhất: kiểm tra "đã có khoá chưa" bằng một câu SELECT rồi mới INSERT vẫn để lọt khi hai request tới cùng lúc.

## Khi gọi sang bên thứ ba (ngân hàng, cổng thanh toán)

- Gửi kèm mã giao dịch của chính bạn; nhiều cổng thanh toán hỗ trợ idempotency hoặc tra cứu theo mã đó.
- Gặp **timeout**, đừng vội coi là thất bại: trạng thái thật có thể là đã trừ tiền. Đánh dấu "chưa rõ", rồi tra cứu lại hoặc chờ đối soát.
- Có quy trình **đối soát** định kỳ giữa dữ liệu của bạn và của đối tác. Idempotency giảm lỗi, đối soát bắt nốt phần còn lại.

## Không chỉ cho thanh toán

Gửi email, tạo đơn hàng, cộng điểm thưởng, xử lý message từ hàng đợi (vốn có thể bị giao lại nhiều lần): ở đâu có retry, ở đó cần nghĩ tới idempotency.

Team bạn xử lý chuyện trùng request thế nào? Từng có sự cố trừ tiền hai lần chưa? Kể ở dưới nhé, nhớ bỏ tên công ty và thông tin nội bộ.

---

Nguồn: Drama Công Ty (https://dramacongty.com), nơi người đi làm Việt Nam kể ẩn danh chuyện công ty. Nội dung là trải nghiệm cá nhân của thành viên, chưa được kiểm chứng; khi trích dẫn, ghi rõ nguồn và link bài.