Chuyển tới nội dung chính

Bắt đầu nhanh

Nhận khoản thanh toán đầu tiên ở môi trường development: liên kết ngân hàng giả lập, tạo payment, giả lập người mua chuyển tiền, rồi nhận lời gọi có chữ ký trên server của bạn. Bạn không cần tài khoản ngân hàng thật.

Trước khi bắt đầu​

  • Tài khoản Hạ Tầng đã bật module Thanh toán (Cài đặt doanh nghiệp → Module). Khi module tắt, mọi request Thanh toán trả về 403 product_disabled.
  • Một server nhận được POST HTTPS từ internet, dùng ở bước 6. Khi chạy trên máy cá nhân, dùng tunnel như ngrok hoặc Cloudflare Tunnel.

Mọi request trên trang này đều gửi Hatang-Environment: development. Không có header này, request sẽ đi vào production. Xem Môi trường và sandbox.

Bước 1: Tạo API key​

Trên dashboard, mở API Keys và tạo key có scope payments:read và payments:write. Chỉ giữ key trên server.

export HATANG_API_KEY=ht_123...

Với Node.js, cài SDK:

npm install @hatang/sdk
lib/hatang.ts
import { Hatang } from "@hatang/sdk";

export const hatang = new Hatang(process.env.HATANG_API_KEY, {
baseUrl: "https://api.hatang.dev",
environment: "development", // gửi dưới dạng header Hatang-Environment
});

Bước 2: Liên kết ngân hàng giả lập​

Bắt đầu liên kết, rồi xác nhận bằng mã 000000. Ngân hàng giả lập luôn dùng mã này.

curl https://api.hatang.dev/payments/bank-connections \
-H "Authorization: Bearer $HATANG_API_KEY" \
-H "Hatang-Environment: development" \
-H "Content-Type: application/json" \
-d '{ "provider": "simulator", "label": "Tài khoản thử" }'

curl https://api.hatang.dev/payments/bank-connections/bcn_123/confirm \
-H "Authorization: Bearer $HATANG_API_KEY" \
-H "Hatang-Environment: development" \
-H "Content-Type: application/json" \
-d '{ "otp": "000000" }'

Lời gọi đầu trả về 201 với kết nối ở trạng thái pending_otp kèm challenge; sau khi xác nhận, kết nối chuyển sang active:

{
"object": "bank_connection",
"id": "bcn_01a0b90428fd7c499145a12c133abe41",
"environment": "development",
"provider": "simulator",
"bank": { "bin": "970422", "name": "MBBank" },
"account_number": "SIM4821937506",
"account_name": "TAI KHOAN GIA LAP",
"account_type": "personal",
"status": "active",
"label": "Tài khoản thử",
"link_mode": "otp",
"activated_at": "2026-09-19T09:30:02.114Z",
"created_at": "2026-09-19T09:29:51.402Z"
}

Bạn cũng làm được việc này trên dashboard: Cài đặt thanh toán → Tài khoản ngân hàng → Thêm tài khoản ngân hàng.

Bước 3: Tạo payment​

Gửi Idempotency-Key (mã đơn là lựa chọn tốt) để request thử lại trả về cùng một payment thay vì tạo cái thứ hai.

curl https://api.hatang.dev/payments \
-H "Authorization: Bearer $HATANG_API_KEY" \
-H "Hatang-Environment: development" \
-H "Idempotency-Key: order-1001" \
-H "Content-Type: application/json" \
-d '{
"amount": 150000,
"description": "Đơn hàng #1001",
"ipn_url": "https://shop.example.vn/hatang/ipn",
"metadata": { "order_id": "1001" }
}'

Phản hồi là 201 với payment ở trạng thái pending, kèm code, qr và checkout_url. Object đầy đủ được mô tả ở Tạo thanh toán.

Bước 4: Mở trang checkout​

Mở checkout_url trên trình duyệt. Trang hiển thị VietQR, thông tin ngân hàng và đồng hồ đếm ngược. Ở development, trang có thêm nút giả lập chuyển khoản.

Bước 5: Giả lập chuyển khoản​

curl https://api.hatang.dev/payments/simulate/transfer \
-H "Authorization: Bearer $HATANG_API_KEY" \
-H "Hatang-Environment: development" \
-H "Content-Type: application/json" \
-d '{ "payment_id": "pay_123" }'

Lời gọi trả về 202. Trong vài giây, payment chuyển sang paid, trang checkout chuyển sang trạng thái đã thanh toán, và ipn_url của bạn nhận lời gọi payment.succeeded.

Bước 6: Xử lý IPN​

Endpoint của bạn phải kiểm tra header Hatang-Signature với body gốc trước khi tin bất cứ điều gì, rồi trả 2xx trong 10 giây. Secret key nằm ở Cài đặt thanh toán → Khoá tích hợp (key development bắt đầu bằng sk_test_).

app/api/hatang/ipn/route.ts (Next.js)
import { constructIpnEvent, HatangSignatureError } from "@hatang/sdk";

export async function POST(req: Request) {
const raw = await req.text(); // body gốc, trước khi parse JSON
try {
const ipn = await constructIpnEvent(raw, req.headers, process.env.HATANG_SECRET_KEY!);
if (ipn.data.status === "paid" || ipn.data.status === "overpaid") {
await markOrderPaid(ipn.data.metadata.order_id as string, ipn.data.paid_amount);
}
return new Response(null, { status: 200 });
} catch (err) {
if (err instanceof HatangSignatureError) return new Response(null, { status: 401 });
throw err; // trả 5xx để Hatang thử lại sau
}
}

Xác minh IPN có handler tương tự bằng PHP, Python và C#.

Bước 7: Xem những gì đã gửi​

Mọi lời gọi IPN và kết quả của nó được liệt kê theo payment, mới nhất trước:

curl https://api.hatang.dev/payments/pay_123/ipn-deliveries \
-H "Authorization: Bearer $HATANG_API_KEY" \
-H "Hatang-Environment: development"
{
"object": "list",
"data": [
{
"object": "ipn_delivery",
"id": "ipn_01a0b90429d87cf89e37fc2f66040ec5",
"payment_id": "pay_01a0b904297873e8b3524a26fdca0ab5",
"event_type": "payment.succeeded",
"url": "https://shop.example.vn/hatang/ipn",
"status": "success",
"attempt": 1,
"response_status": 200,
"response_ms": 84,
"last_error": null,
"next_attempt_at": null,
"delivered_at": "2026-09-19T09:34:13.041Z",
"created_at": "2026-09-19T09:34:12.952Z"
}
]
}

Trang chi tiết payment trên dashboard hiển thị cùng danh sách này.

Lỗi thường gặp​

LỗiNguyên nhânCách xử lý
422 no_bank_connectionChưa có tài khoản ngân hàng nào đang hoạt động ở môi trường nàyLàm bước 2, hoặc kiểm tra đã gửi Hatang-Environment: development chưa
422 validation_error kèm "Several bank accounts are linked; pass connection_id"Có nhiều hơn một tài khoản đang hoạt độngGửi connection_id khi tạo payment
422 validation_error ở ipn_urlURL là địa chỉ nội bộ (localhost, 10.x, 192.168.x…) hoặc không phải httpsDùng URL HTTPS công khai, ví dụ qua tunnel
401 restricted_api_keyKey thiếu scope Thanh toánTạo key có payments:read và payments:write
403 environment_mismatchGiả lập chuyển khoản ngoài môi trường developmentGửi Hatang-Environment: development
Không nhận được IPNEndpoint không trả 2xxXem last_error ở bước 7; Hạ Tầng thử lại trong khoảng một ngày

Bước tiếp theo​

Xem trang này dạng Markdown