Blog
payment-gatewaypayment-apiintegrationdeveloper

Tích hợp Payment Gateway: Hướng dẫn từ đầu đến cuối cho developer

Hướng dẫn thực tế tích hợp Payment Gateway với TConnect API — từ xác thực, mã hóa AES, tạo giao dịch đến nhận IPN callback và xử lý edge cases.

TConnect Team 18 tháng 2, 2026 6 min read

Tổng quan

Payment Gateway là cầu nối giữa ứng dụng của bạn và hệ thống ngân hàng / ví điện tử. Khi khách hàng thanh toán, dữ liệu đi qua gateway để xác thực, định tuyến và xác nhận giao dịch.

TConnect Payment Gateway hỗ trợ:

  • Thẻ nội địa / quốc tế (Napas, Visa, Mastercard, JCB)
  • Ví điện tử (MoMo, ZaloPay, VNPay)
  • QR VietQR (chuyển khoản ngân hàng)
  • Trả góp qua các ngân hàng đối tác

Một giải pháp API, tất cả phương thức — không cần tích hợp từng cổng riêng lẻ.


Kiến trúc tổng thể

[Frontend]  →  POST /create-order  →  [Backend của bạn]

                                   POST /openapi/v1/payment/create

                                       [TConnect API]

                               ┌─────────────┴──────────────┐
                          [Ngân hàng]                  [Ví điện tử]

                    redirect_url / deeplink trả về

                       [Khách thanh toán]

                    IPN Callback → [Backend của bạn]

                    Cập nhật trạng thái đơn hàng

Bước 1: Xác thực và lấy Access Token

TConnect dùng OAuth2 với cặp client_id / client_secret. Token có hiệu lực 36.000 giây (10 giờ).

function login(base_url, username, password, client_id, client_secret):
    payload = {
        username:      username,
        password:      password,
        client_id:     client_id,
        client_secret: client_secret,
        grant_type:    "password"
    }
    POST {base_url}/openapi/v1/auth/login
        body: payload
 
    return response.access_token, response.refresh_token

Lưu ý: Cache access_token — đừng gọi login trước mỗi request. Dùng refresh_token để gia hạn khi hết hạn.


Bước 2: Mã hóa payload với AES-256-CBC

Tất cả request body gửi lên TConnect đều phải mã hóa AES-256-CBC. IV (16 bytes) được prepend vào ciphertext, toàn bộ hex-encode.

function encrypt_payload(data, aes_key):
    iv         = generate random 16 bytes
    plaintext  = json_encode(data) as UTF-8 bytes
    ciphertext = AES_256_CBC_encrypt(plaintext, key=aes_key, iv=iv)
    return hex_encode(iv + ciphertext)
 
function decrypt_response(hex_data, aes_key):
    raw        = hex_decode(hex_data)
    iv         = first 16 bytes of raw
    ciphertext = remaining bytes of raw
    plaintext  = AES_256_CBC_decrypt(ciphertext, key=aes_key, iv=iv)
    return json_decode(plaintext)

Bước 3: Tạo giao dịch thanh toán

function create_payment(base_url, partner_code, access_token, aes_key, order_data):
    payload = {
        order_id:       order_data.id,
        amount:         order_data.amount,   // VND, không có số thập phân
        currency:       "VND",
        description:    order_data.description,
        return_url:     "https://yoursite.com/payment/return",
        cancel_url:     "https://yoursite.com/payment/cancel",
        ipn_url:        "https://yoursite.com/webhook/ipn",
        payment_method: "all",               // hoặc "card", "wallet", "qr"
        buyer_info: {
            name:  order_data.customer_name,
            email: order_data.customer_email,
            phone: order_data.customer_phone
        }
    }
 
    encrypted = encrypt_payload(payload, aes_key)
 
    POST {base_url}/openapi/v1/payment/create
        body: { data: encrypted }
        headers:
            Authorization:  "Bearer {access_token}"
            Partner-Code:   partner_code
            Content-Type:   "application/json"
 
    result = decrypt_response(response.data, aes_key)
    return result.payment_url   // redirect khách đến đây

Sau khi có payment_url, redirect frontend của bạn đến URL này. Khách hàng sẽ chọn phương thức thanh toán và hoàn tất.


Bước 4: Nhận IPN Callback

Khi giao dịch hoàn tất (thành công hoặc thất bại), TConnect POST đến ipn_url bạn cung cấp.

// IPN endpoint: POST /webhook/ipn
function ipn_handler(request):
    body = parse_json(request)
 
    // 1. Decrypt
    txn = decrypt_response(body.data, AES_KEY)
 
    // 2. Idempotency — tránh xử lý 2 lần
    if cache.get("ipn:{txn.request_id}") exists:
        return { status: "ok" }
    cache.set("ipn:{txn.request_id}", ttl=86400)
 
    // 3. Xử lý theo trạng thái
    if txn.status == "SUCCESS":
        mark_order_paid(txn.order_id, txn.amount)
    else if txn.status == "FAILED":
        mark_order_failed(txn.order_id)
 
    // 4. Bắt buộc trả 200 — TConnect retry nếu không nhận được
    return { status: "ok" }

Quan trọng: Luôn trả HTTP 200 kể cả khi bạn đã xử lý rồi. Nếu trả lỗi, TConnect sẽ retry — dẫn đến xử lý trùng lặp.


Bước 5: Kiểm tra trạng thái giao dịch (polling)

Ngoài IPN, bạn có thể chủ động query trạng thái:

function check_payment_status(base_url, partner_code, access_token, aes_key, order_id):
    payload   = { order_id: order_id }
    encrypted = encrypt_payload(payload, aes_key)
 
    POST {base_url}/openapi/v1/payment/query
        body: { data: encrypted }
        headers:
            Authorization: "Bearer {access_token}"
            Partner-Code:  partner_code
 
    return decrypt_response(response.data, aes_key)

Dùng polling như fallback khi IPN bị delay — ví dụ: poll sau 5 phút nếu chưa nhận IPN.


Xử lý các edge cases thường gặp

Tình huốngCách xử lý
Khách đóng trình duyệt sau khi thanh toánIPN vẫn gửi — xử lý bình thường
Mạng lỗi, IPN không đếnTConnect retry tự động · Thêm cronjob poll trạng thái sau 15 phút
Khách thanh toán 2 lần cùng orderIdempotency check qua request_id
return_url redirect nhưng chưa có IPNKhông mark paid tại return_url — chờ IPN
Token hết hạn giữa chừngCatch 401 → refresh token → retry request

Checklist trước khi go-live

  • Test đầy đủ flow thanh toán trên Sandbox
  • Implement idempotency cho IPN handler
  • Log toàn bộ IPN payload (để debug sau này)
  • Cấu hình retry/fallback khi IPN delay
  • Không dùng return_url để mark paid — luôn dùng IPN
  • Rate limiting cho endpoint IPN (tránh DDoS)
  • Rotate aes_keyclient_secret định kỳ

Kết luận

Tích hợp Payment Gateway không phức tạp nếu bạn hiểu đúng luồng: tạo giao dịch → redirect → IPN callback. Phần quan trọng nhất là xử lý IPN an toàn với idempotency và không bao giờ tin vào return_url để xác nhận thanh toán thành công.

Sandbox TConnect miễn phí và không yêu cầu KYB — bắt đầu ngay hôm nay.

Bắt đầu tích hợp ngay

Sandbox miễn phí · Tài liệu API đầy đủ · Hỗ trợ kỹ thuật