Thanh toán AI Agent với Idempotency & Webhook: Hướng dẫn AgentPay VN

2026-07-28 · AgentPay VN

aipythonvietqrmcpwebhook

Nỗi Đau Thực Tế: Khi AI Agent Không Biết "Tiền Đã Về Hay Chưa"

Bạn vừa xây dựng một bot bán khoá học trực tuyến chạy 24/7. Khách hàng thanh toán qua bot, bot gửi tài liệu PDF... nhưng đột nhiên mạng bị gián đoạn. Bot không biết tiền đã chuyển vào tài khoản ngân hàng hay chưa, nên nó gửi khoá học 2 lần. Hoặc tệ hơn: bot không nhận được xác nhận settlement từ đâu, khách kêu "tôi đã chuyển rồi mà sao chưa nhận?"

Đây là cuộc chiến thường ngày của những developer xây dựng hệ thống AI agent thanh toán tự động. May mắn là AgentPay VN đã giải quyết vấn đề này một cách sạch sẽ.

Vì Sao Idempotency Là Anh Hùng Thầm Lặng

Idempotency có nghĩa là: "Dù gọi API bao nhiêu lần, kết quả cũng giống nhau." Tưởng tượng bạn gọi hàm create_payment_request() ba lần do timeout — nếu không có idempotency, bạn sẽ có ba yêu cầu thanh toán khác nhau, khách hàng sẽ confused ("Sao có 3 QR code?").

AgentPay VN xử lý điều này tự động:

from agentpay_vn import AgentPayClient

client = AgentPayClient(api_key="your_api_key_here")

# Tạo yêu cầu thanh toán với idempotency key
# Dù gọi 10 lần, vẫn chỉ tạo 1 request
payment_req = client.create_payment_request(
    amount=299_000,  # VNĐ
    description="Khoá học Python Pro",
    merchant_id="shop_123",
    idempotency_key="order_khoa_hoc_2024_001"  # Chìa khoá bí mật!
)

print(f"QR Code URL: {payment_req['checkout_url']}")
print(f"Payment ID: {payment_req['payment_id']}")

Giải thích từng dòng: - amount=299_000: Giá bán (đơn vị đồng) - description: Mô tả sản phẩm, khách sẽ thấy trên app ngân hàng - idempotency_key: Mã duy nhất (thường dùng order_id hoặc email+timestamp). Nếu gọi lại với key này, hệ thống trả về request cũ, không tạo request mới

Đây là lý do tại sao bot của bạn sẽ không bao giờ gửi khoá học 2 lần — vì payment request đã tồn tại rồi.

Webhook: Bot Biết Tiền Về Khi Nào, Từ Đâu

Webhook là cách AgentPay gọi ngược lại bot của bạn để báo tin tốt: "Tiền đã vào!" Thay vì bot phải hỏi "Tiền về chưa? Tiền về chưa?" cứ mỗi 5 giây (gọi là polling, rất lãng phí), webhook chủ động push sự kiện tới bot.

Cấu hình webhook cho AgentPay MCP server (cho Claude hoặc agent khác):

{
  "tools": [
    {
      "name": "agentpay-mcp",
      "config": {
        "webhook_url": "https://your-bot-domain.com/webhooks/agentpay",
        "webhook_secret": "your_webhook_secret_key_12345",
        "events": ["payment.settled", "payment.failed", "payment.expired"]
      }
    }
  ]
}

Lưu ý quan trọng: AgentPay KHÔNG BAO GIỜ giữ tiền. QR code trỏ thẳng vào tài khoản ngân hàng thương nhân của bạn. Khi khách quét QR và chuyển khoản, tiền tức thì về bank account bạn. AgentPay chỉ theo dõi, xác nhận qua bank feed (OTP hoặc API ngân hàng), rồi gửi webhook tới bot.

Kịch Bản Đời Thực: Bot Bán Khóa Học Online

Phạm Minh Anh (30 tuổi) mở cửa hàng khóa học trực tuyến trên Telegram. Hàng ngày cô nhận 5-10 đơn hàng. Trước đây cô phải check bank app thủ công, sau đó copy-paste link Drive vào tin nhắn khách (rất kém efficiency).

Sau khi dùng AgentPay VN, quy trình tự động hoàn toàn:

  1. Khách gõ: /mua khoa_python_pro
  2. Bot tạo payment request (với idempotency_key = telegram_user_id + timestamp)
  3. Bot gửi QR code cho khách (QR này trỏ thẳng vào tài khoản bank của cô)
  4. Khách quét & chuyển khoản qua app ngân hàng (Vietcombank, Techcombank, ...)
  5. Tiền vào account cô ngay lập tức
  6. AgentPay webhook báo "payment.settled" tới Telegram bot
  7. Bot tự động gửi link Drive cho khách (không cần cô can thiệp)

Công việc thủ công cô từ 2 giờ/ngày còn 5 phút/ngày.

Hướng Dẫn Từng Bước: Từ Cài Đặt Đến Đối Soát Tự Động

1. Cài đặt AgentPay SDK

pip install agentpay-vn

2. Code Workflow 3 Bước

import asyncio
from agentpay_vn import AgentPayClient
from datetime import datetime

client = AgentPayClient(
    api_key="sk_live_your_key_here",
    merchant_id="merchant_abc123"
)

async def payment_flow(customer_email: str, product_name: str, amount: int):
    """
    Quy trình thanh toán 3 bước: request → checkout → await
    """

    # Bước 1: Tạo yêu cầu thanh toán
    idempotency_key = f"{customer_email}_{product_name}_{int(datetime.now().timestamp())}"

    payment_req = client.create_payment_request(
        amount=amount,
        description=f"Purchase: {product_name}",
        idempotency_key=idempotency_key,
        metadata={"customer_email": customer_email}
    )

    checkout_url = payment_req['checkout_url']
    payment_id = payment_req['payment_id']

    print(f"✓ Tạo yêu cầu thành công!")
    print(f"  Mã thanh toán: {payment_id}")
    print(f"  Link QR: {checkout_url}")

    # Bước 2: Gửi QR cho khách (thực hiện bên ngoài, ví dụ gửi SMS/email)
    # send_qr_to_customer(checkout_url, customer_email)

    # Bước 3: Chờ xác nhận settlement từ bank feed
    # timeout=300 (5 phút), check mỗi 3 giây
    settlement = await client.await_settlement(
        payment_id=payment_id,
        timeout=300,
        check_interval=3
    )

    if settlement['status'] == 'settled':
        print(f"✓ THANH TOÁN THÀNH CÔNG!")
        print(f"  Số tiền: {settlement['amount']:,} VNĐ")
        print(f"  Thời gian: {settlement['settled_at']}")
        return True
    else:
        print(f"✗ Thanh toán thất bại: {settlement['reason']}")
        return False

# Chạy
async def main():
    success = await payment_flow(
        customer_email="customer@example.com",
        product_name="khoa_python_pro",
        amount=499_000
    )

if __name__ == "__main__":
    asyncio.run(main())

Chi tiết code: - idempotency_key: Tạo mã unique từ email + tên sản phẩm + timestamp - create_payment_request(): Trả về dict với checkout_url (QR) và payment_id - await_settlement(): Chờ xác nhận từ bank feed (async, không chặn thread) - metadata: Dữ liệu thêm để theo dõi (customer_email, order_id, ...)

3. Setup MCP Server Cho Claude

Nếu bạn dùng Claude hoặc agent khác, chạy MCP server:

agentpay-mcp --api-key sk_live_your_key --port 3000

Rồi config trong Claude:

{
  "mcpServers": {
    "agentpay": {
      "command": "agentpay-mcp",
      "args": ["--api-key", "sk_live_your_key", "--port", "3000"],
      "env": {
        "WEBHOOK_URL": "https://your-bot.com/webhooks/agentpay"
      }
    }
  }
}

Bây giờ Claude có thể gọi create_payment_requestawait_settlement như một tool thông thường.

Nên Làm & Không Nên Làm

Nên Làm ✓ Không Nên Làm ✗
Dùng idempotency_key duy nhất per order Gọi create_payment_request mà không idempotency_key
Lưu payment_id để sau có thể query lại Tin tưởng ngay mà không chờ webhook/settlement
Dùng webhook để nhận sự kiện real-time Polling API mỗi 2 giây (tốn tài nguyên)
Kiểm tra webhook_secret trước xử lý Bỏ qua secret, dễ bị giả mạo
Set timeout hợp lý (300-600s) cho await_settlement Timeout quá ngắn (< 10s) khiến false negative
Log payment_id & idempotency_key vào database Mất vết thanh toán, khó debug sau
Test với sandbox trước production Deploy thẳng vào production

Nâng Cao: Xử Lý Lỗi & Retry

Thực tế, mạng không bao giờ hoàn hảo. Webhook có thể tới muộn, network timeout, server down tạm thời. Code production cần handle những trường hợp này:

import asyncio
from agentpay_vn.exceptions import AgentPayError

async def robust_payment_flow(payment_id: str, max_retries: int = 3):
    """
    Chờ settlement với retry logic
    """
    for attempt in range(max_retries):
        try:
            settlement = await client.await_settlement(
                payment_id=payment_id,
                timeout=120,
                check_interval=5
            )

            if settlement['status'] == 'settled':
                return settlement
            elif settlement['status'] == 'failed':
                raise Exception("Khách hàng hủy thanh toán")

        except asyncio.TimeoutError:
            print(f"Lần {attempt+1}: Timeout. Thử lại trong 10s...")
            await asyncio.sleep(10)

        except AgentPayError as e:
            if "rate_limit" in str(e) and attempt < max_retries - 1:
                print(f"Rate limit. Chờ 30s...")
                await asyncio.sleep(30)
            else:
                raise

    # Nếu vẫn chưa xong, lưu vào queue để check sau
    print(f"Không xác nhận được {payment_id}. Sẽ check lại sau 1 giờ.")
    schedule_retry(payment_id, delay=3600)

FAQ: Những Câu Hỏi Hay Gặp

Q1: Tiền thanh toán qua QR có đi vào tài khoản AgentPay không? Không! Tiền trực tiếp vào tài khoản ngân hàng của bạn. AgentPay chỉ track transaction qua bank feed API. Bạn luôn là chủ nhân tiền.

Q2: Webhook có độ trễ không? Nếu webhook mất (down server), thanh toán có bị mất không? Webhook có thể trễ 5-30s (phụ thuộc mạng), nhưng nó sẽ tới cuối cùng. Nếu bot của bạn down, AgentPay sẽ retry webhook 3 lần trong 1 giờ. Và dù webhook mất, bạn vẫn có thể query payment status sau này bằng client.get_payment(payment_id). Tiền không bao giờ bị mất.

Q3: Có phí gì không? AgentPay VN là open-source MIT, miễn phí. Nhưng ngân hàng có thể tính phí API/bank feed (thường 0-2%). Bạn nên hỏi ngân hàng của mình.

Q4: Dùng idempotency_key dài bao nhiêu là đủ? Nên dùng ≥ 32 ký tự (ví dụ UUID hoặc hash). AgentPay chấp nhận max 255 ký tự. Ví dụ: order_20240115_john_doe_8ba31562.

Tóm Tắt Nhanh

Kết & Hành Động Tiếp Theo

Nếu bạn đang chạy bot AI (Telegram, Discord, web app) cần nhận tiền từ người dùng, AgentPay VN sẽ giảm 90% pain point của bạn: không cần tích hợp API ngân hàng phức tạp, không cần xử lý idempotency thủ công, không cần poll API hàng giây.

Hành động ngay hôm nay:

  1. Cài đặt: pip install agentpay-vn
  2. Đọc docs: https://agentpay.servicesai.vn/v1/docs
  3. Sao chép code ví dụ trên, test ở sandbox
  4. Deploy lên production (nếu bạn không có sandbox, hãy contact team qua GitHub)
  5. Theo dõi webhook để xác nhận settlement

Repo GitHub (star ⭐ nếu thích): https://github.com/phuocdu/agentpay-vn

Chúc bạn xây dựng bot thanh toán tuyệt vời! 🎉

Bắt đầu →

← Tất cả bài