Thanh toán AI agent: Idempotency, Webhook & Đối soát tự động
Bài Toán Thực Tế: Khi AI Agent Phải Xử Lý Thanh Toán
Hãy tưởng tượng bạn có một chatbot bán khoá học trực tuyến. Khách hàng nhắn: "Mình muốn mua khoá Python nâng cao." Bot của bạn tạo link thanh toán VietQR và gửi cho khách. Nhưng rồi điều tồi tệ xảy ra:
- Khách quét QR, nhưng do mạng yếu, anh ta quét 2 lần → thanh toán bị tạo 2 lần.
- Bot không biết khách đã chuyển tiền hay chưa, vẫn cứ "chờ" mà không xử lý tiếp.
- Ngân hàng xác nhận thanh toán, nhưng webhook không tới → tài khoản không được cấp quyền học.
- Cuối cùng khách giận, để lại review 1 sao.
Đây chính là lý do AgentPay VN ra đời — một SDK Python mã mở (MIT License) giúp AI agent xử lý thanh toán an toàn, tự động, không bao giờ giữ tiền (toàn bộ chuyển thẳng vào tài khoản ngân hàng của bạn). Bài viết này sẽ hướng dẫn bạn tránh những tình huống trên.
Tại Sao Idempotency Quan Trọng Với AI Agent?
Khi một AI agent thực hiện hành động (tạo yêu cầu thanh toán), nó có thể bị gọi lại nhiều lần do: - Timeout hoặc kết nối mạng không ổn định. - Retry logic trong framework AI (Claude, GPT, Agent framework). - Người dùng bấm nút "Thanh toán" lại do chưa thấy kết quả.
Idempotency (tính đơn điệu) đảm bảo rằng dù gọi API 10 lần với cùng dữ liệu, bạn cũng chỉ nhận được 1 yêu cầu thanh toán duy nhất. AgentPay VN xử lý điều này qua idempotency_key — một ID độc nhất do bạn cung cấp:
from agentpay_vn import AgentPayClient
# Khởi tạo client
client = AgentPayClient(api_key="your_api_key_here")
# Tạo yêu cầu thanh toán với idempotency_key
# Nếu gọi lại với cùng key, sẽ nhận lại cùng kết quả thay vì tạo mới
payment_request = client.create_payment_request(
amount=299000, # 299k VND cho khoá học
description="Khoá học Python Nâng Cao",
merchant_id="shop_python_vn",
idempotency_key="order_user123_20250116" # Duy nhất per user/order
)
print(f"Payment ID: {payment_request.payment_id}")
print(f"Checkout URL: {payment_request.checkout_url}")
print(f"QR mã VietQR: {payment_request.vietqr_string}")
Giải thích từng dòng:
- idempotency_key: Xây dựng từ user ID + timestamp/order ID đảm bảo tính duy nhất.
- amount: Tiền tính bằng VND; không có phí ẩn.
- checkout_url: Link mà bạn gửi cho khách hàng (hoặc hiển thị QR đã mã hóa).
Lợi ích: Nếu agent retry 5 lần vì timeout, chỉ 1 thanh toán được tạo. Bạn không sợ khách bị "double charge".
Webhook: Vòng Lặp Xác Nhận Thanh Toán
Không chỉ chờ trong vòng lặp (polling) là không hiệu quả. AgentPay VN gửi webhook tới server của bạn ngay khi thanh toán được xác nhận từ ngân hàng:
from flask import Flask, request, jsonify
import hmac
import hashlib
import json
app = Flask(__name__)
WEBHOOK_SECRET = "your_webhook_secret_key"
@app.route("/webhook/agentpay", methods=["POST"])
def handle_webhook():
# 1. Xác thực chữ ký webhook để tránh fake request
signature = request.headers.get("X-Signature")
body = request.get_data(as_text=True)
expected_sig = hmac.new(
WEBHOOK_SECRET.encode(),
body.encode(),
hashlib.sha256
).hexdigest()
if not hmac.compare_digest(signature, expected_sig):
return jsonify({"error": "Invalid signature"}), 401
# 2. Parse payload từ webhook
payload = request.json
payment_id = payload["payment_id"]
status = payload["status"] # "settled", "pending", "failed"
amount = payload["amount"]
merchant_account = payload["merchant_account"] # Tài khoản ngân hàng nhận tiền
# 3. Cập nhật database của bạn
if status == "settled":
# Thanh toán thành công → cấp quyền truy cập khoá học
user_id = payload["metadata"].get("user_id")
course_id = payload["metadata"].get("course_id")
grant_course_access(user_id, course_id)
send_confirmation_email(user_id, course_id, amount)
print(f"✓ Thanh toán {payment_id} thành công. Tiền vào: {merchant_account}")
elif status == "failed":
# Xử lý thanh toán thất bại
refund_or_notify_user(payment_id)
# 4. Trả về 200 OK để AgentPay biết bạn đã nhận webhook
return jsonify({"status": "received"}), 200
if __name__ == "__main__":
app.run(port=5000)
Điểm quan trọng:
- Xác thực chữ ký (X-Signature): Đảm bảo webhook thực sự từ AgentPay, không phải hacker.
- Idempotent processing: Lưu payment_id vào DB; nếu webhook đến 2 lần (hiếm nhưng có thể), bạn chỉ cấp quyền 1 lần.
- Status = "settled": Tiền đã tới tài khoản ngân hàng của bạn an toàn.
- Metadata: Gắn user_id, order_id hay thông tin custom để xử lý sau.
Đối Soát Tự Động: So Khớp Bank Feed Với Thanh Toán
AgentPay VN cung cấp API để lấy bank feed — danh sách các giao dịch thực tế từ ngân hàng — giúp bạn tự động so khớp với thanh toán đã tạo:
from agentpay_vn import AgentPayClient
from datetime import datetime, timedelta
client = AgentPayClient(api_key="your_api_key_here")
# Lấy danh sách thanh toán từ 7 ngày trước đến hôm nay
start_date = (datetime.now() - timedelta(days=7)).isoformat()
end_date = datetime.now().isoformat()
payments = client.get_payments(
start_date=start_date,
end_date=end_date,
status="settled" # Chỉ lấy những cái đã xác nhận
)
bank_feed = client.get_bank_feed(
start_date=start_date,
end_date=end_date
)
# So khớp: Mỗi payment phải có tương ứng trong bank_feed
matched = 0
unmatched = []
for payment in payments:
found = False
for transaction in bank_feed:
if (
payment.amount == transaction.amount and
payment.settlement_date.date() == transaction.date
):
found = True
matched += 1
break
if not found:
unmatched.append({
"payment_id": payment.payment_id,
"amount": payment.amount,
"reason": "Không tìm thấy trong bank feed"
})
print(f"Đối soát thành công: {matched}/{len(payments)}")
if unmatched:
print(f"Cần kiểm tra: {unmatched}")
# Gửi alert tới team kế toán
alert_accounting_team(unmatched)
Lợi ích: - Tự động phát hiện lỗi: Nếu thanh toán được tạo nhưng tiền không tới, bạn sẽ biết ngay. - Tuân thủ kiểm toán: Tất cả giao dịch được ghi chép đầy đủ. - Giảm công thủ công: Không cần nhân viên kế toán phải so sánh bằng tay.
Cấu Hình MCP Server Cho Claude & AI Agent
Nếu bạn dùng Claude hoặc các agent framework khác, bạn có thể cấu hình AgentPay VN như một MCP (Model Context Protocol) server để agent gọi trực tiếp:
{
"mcpServers": {
"agentpay": {
"command": "agentpay-mcp",
"args": [],
"env": {
"AGENTPAY_API_KEY": "your_api_key_here",
"AGENTPAY_WEBHOOK_SECRET": "your_webhook_secret",
"AGENTPAY_MERCHANT_ID": "your_merchant_id"
}
}
}
}
Trong Claude (hoặc Agent): Bạn không cần viết code gọi API. Claude sẽ tự gọi hàm qua MCP:
User: "Tôi muốn mua khoá học giá 599k"
Claude: "Tôi sẽ tạo yêu cầu thanh toán cho bạn."
[Gọi MCP: agentpay.create_payment_request(amount=599000, ...)]
Claude: "Vui lòng quét mã QR này: [QR]. Thanh toán sẽ được xác nhận trong vòng 30 giây."
Kịch Bản Thực Tế: Bot Bán Khóa Học Online
Hãy tưởng tượng một chatbot Telegram bán khoá học Python:
Luồng xử lý:
- User gõ: "Mua khoá Python Nâng Cao"
- Bot tạo payment request với
idempotency_key = f"user_{user_id}_course_python_nang_cao" - Bot gửi QR + checkout link cho user
- User quét, chuyển tiền (hoặc bấm link thanh toán online)
- Webhook từ AgentPay tới server →
status = settled - Bot tự động: - Cấp quyền truy cập khoá học (lưu vào DB) - Gửi email xác nhận + link học tập - Gửi tin nhắn Telegram: "✓ Thanh toán thành công! Bạn có thể bắt đầu học ngay."
- Hàng ngày, script đối soát so khớp các payment với bank feed
Lợi ích: - Toàn bộ tự động, không cần can thiệp. - User nhận quyền truy cập ngay (UX tuyệt vời). - Không bao giờ bị "double charge" nhờ idempotency. - Tài chính rõ ràng, dễ kiểm toán.
Nên & Không Nên Khi Dùng AgentPay VN
| Nên Làm | Không Nên Làm |
|---|---|
✓ Luôn dùng idempotency_key khi tạo payment |
✗ Bỏ qua idempotency; tạo payment mà không key |
| ✓ Xác thực webhook bằng signature | ✗ Tin tưởng mọi webhook mà không verify |
✓ Lưu payment_id vào DB ngay sau khi tạo |
✗ Chỉ lưu khi webhook tới (có thể mất dữ liệu) |
| ✓ Xử lý idempotent trong webhook (check DB trước) | ✗ Cấp quyền/gửi email ngay mà không check trùng |
| ✓ Đối soát hàng ngày với bank feed | ✗ Chỉ tin webhook; không so khớp với thực tế |
| ✓ Gắn metadata (user_id, order_id) vào payment | ✗ Tạo payment trống, không thể track sau |
Hướng Dẫn Cài Đặt Từng Bước
1. Cài SDK
pip install agentpay-vn
2. Lấy API Key
Truy cập https://agentpay.servicesai.vn/v1/docs → đăng ký tài khoản merchant → copy API key.
3. Khởi Tạo Client
from agentpay_vn import AgentPayClient
client = AgentPayClient(
api_key="sk_live_xxxxx", # Hoặc sk_test_ cho sandbox
base_url="https://agentpay.servicesai.vn/api" # Tuỳ chọn
)
4. Tạo Payment Request
payment = client.create_payment_request(
amount=100000,
description="Sản phẩm test",
merchant_id="your_merchant_id",
idempotency_key="test_key_001",
metadata={"user_id": "123", "order_id": "ord_456"}
)
print(payment.checkout_url) # Gửi cho khách
5. Setup Webhook
- Cấu hình URL webhook trong dashboard:
https://your-domain.com/webhook/agentpay - Lấy webhook secret từ dashboard
- Code xử lý webhook (xem phần trên)
6. Đợi & Xác Nhận
import time
payment_id = payment.payment_id
# Nếu không dùng webhook, có thể polling (không khuyến khích)
for i in range(30): # Chờ tối đa 30 giây
status = client.get_payment_status(payment_id)
if status == "settled":
print("✓ Thanh toán thành công!")
break
time.sleep(1)
Câu Hỏi Thường Gặp (FAQ)
Q1: AgentPay VN có giữ tiền của tôi không?
Không bao giờ. Toàn bộ tiền từ QR code VietQR được chuyển trực tiếp vào tài khoản ngân hàng của bạn. AgentPay chỉ là một cầu nối — xử lý logic, webhook, và đối soát.
Q2: Nếu webhook bị mất/không tới, tôi sẽ biết khi nào?
Bạn có thể (1) polling get_payment_status() sau một khoảng thời gian, hoặc (2) đối soát hàng ngày với bank feed. Nếu payment tồn tại trong payment list nhưng webhook không tới, bạn sẽ phát hiện trong bước đối soát.
Q3: Có phí ẩn nào không?
Không. Giá được hiển thị rõ ràng trên tài liệu. Chỉ có phí service (khoảng 1-2%) được tính khi tạo payment, không có phí webhook hoặc API call thêm.
Q4: Tôi có thể test trước khi production không?
Có. Dùng API key sk_test_ và merchant sandbox. Mọi payment sẽ được giả lập — không tiêu tiền thật.
Mẹo Nâng Cao
- Rate limiting: AgentPay cho phép ~1000 API call/phút. Nếu bot của bạn có lưu lượng cao, hãy cache payment request.
- Retry strategy: Nếu webhook timeout, AgentPay sẽ retry 5 lần với backoff exponential. Đảm bảo endpoint webhook của bạn không "crash".
- Bulk settlement: Nếu bạn có nhiều payment, dùng
client.get_payments()với filter để lấy danh sách hàng loạt thay vì từng cái một.
Tóm Tắt Nhanh
- 💡 Idempotency giải quyết vấn đề thanh toán trùng lặp; luôn dùng
idempotency_key. - 🔐 Webhook an toàn khi được ký bằng signature; xác thực trước xử lý.
- 📊 Đối soát tự động giữa payment request và bank feed giúp phát hiện lỗi sớm.
- 🤖 MCP integration cho phép AI agent (Claude, GPT) gọi thanh toán trực tiếp mà không cần code.
- ✅ Toàn bộ mã mở (MIT License) — bạn kiểm soát hoàn toàn, không lock-in.
Bước Tiếp Theo
- Cài đặt:
pip install agentpay-vn - Đọc docs: https://agentpay.servicesai.vn/v1/docs
- Fork repository: https://github.com/phuocdu/agentpay-vn (nếu muốn contribute)
- Setup sandbox và thử tạo payment đầu tiên.
- Deploy webhook và test xác nhận thanh toán.
Nếu gặp vấn đề, hãy mở issue trên GitHub — team AgentPay VN sẽ hỗ trợ bạn. Chúc bạn triển khai thành công! 🚀