Tích hợp trong 5 phút
Làm đúng bốn bước này để đọc được giao dịch đầu tiên.
Token và nguyên tắc bảo mật
Token nằm trong path vì đây là contract API v3 hiện tại; hãy coi nó như mật khẩu.
- Mỗi token chỉ hợp lệ với đúng tài khoản và đúng mã ngân hàng.
- Đặt token trong biến môi trường hoặc secret manager; không commit vào Git.
- Thu hồi/đổi token ngay khi nghi ngờ lộ và khi ngừng tích hợp.
- API tự chặn nếu user, membership hoặc đại lý đang bị khóa.
API lịch sử giao dịch
Trả giao dịch mới nhất của tài khoản, sắp xếp giảm dần theo thời gian rồi ID.
GET https://bank.zflux.cloud/v3/{bank}/transhistory/{token}?limit=20Ví dụ cURL
curl --request GET \
--url 'https://bank.zflux.cloud/v3/vietcombank/YOUR_ACCOUNT_TOKEN?limit=20' \
--header 'Accept: application/json' \
--max-time 15Tham số
| Tên | Vị trí | Bắt buộc | Mô tả |
|---|---|---|---|
bank | path | Có | Mã ngân hàng, ví dụ acb, vietcombank, vpbank. |
token | path | Có | Token của đúng tài khoản cần đọc. |
limit | query | Không | Từ 1 đến 500; mặc định 20. |
Response thành công
Các trường dưới đây phản ánh đúng response của API hiện tại.
{
"ok": true,
"status": 200,
"source": "apibank_v3",
"provider": "bankhub",
"bank": "Vietcombank",
"account_number": "2038888888",
"transactions": [{
"id": "tx_01JABC...",
"transaction_date": "2026-08-20 19:30:00",
"account_number": "2038888888",
"transfer_type": "in",
"amount_in": 500000,
"amount_out": 0,
"amount": 500000,
"accumulated": 12500000,
"description": "THANH TOAN DON DH1024",
"reference_number": "FT26081234567890",
"code": null,
"bank_brand_name": "Vietcombank"
}]
}amount là số dương của phía có giá trị; dùng amount_in và amount_out để xác định dòng tiền. Không dùng mô tả làm khóa chống trùng.Mã lỗi cần xử lý
Không retry vô hạn với lỗi 403/404; cần sửa cấu hình hoặc trạng thái tài khoản.
| HTTP | Code | Ý nghĩa và cách xử lý |
|---|---|---|
| 403 | AGENCY / USER DENIAL | User, membership hoặc đại lý không còn quyền hoạt động. Dừng job và yêu cầu quản trị viên kiểm tra. |
| 404 | TOKEN_NOT_FOUND | Token không tồn tại hoặc không khớp bank. Kiểm tra lại tài khoản/token; không retry tự động. |
| 5xx | SERVER ERROR | Retry có backoff và jitter, giới hạn số lần; log request ID nhưng không log token. |
Webhook: HTTP contract
Nhận giao dịch mới qua HTTPS POST và xác minh trước khi cập nhật đơn hàng.
| Thành phần | Giá trị / cách dùng |
|---|---|
Method | POST, Content-Type application/json. |
X-APIBank-Timestamp | Unix timestamp lúc gửi; từ chối nếu lệch quá 5 phút. |
X-APIBank-Signature | Hex HMAC-SHA256 của timestamp.raw_body. |
X-APIBank-Event-Id | Khóa duy nhất dạng evt_...; dùng để chống xử lý trùng. |
| Timeout | 12 giây. Lưu event, đưa tác vụ nặng vào queue rồi trả 2xx. |
Payload giao dịch
{
"event": "transaction.created",
"created_at": "2026-08-20T12:30:00.000000Z",
"event_id": "evt_550e8400-e29b-41d4-a716-446655440000",
"data": {
"account": {
"source": "apibank_v3",
"provider": "tingee",
"bank_code": "acb",
"account_no": "2038888888",
"account_name": "NGUYEN VAN A",
"apibank_account_id": 123
},
"transaction": {
"uid": "tx_01JABC...",
"bank_code": "acb",
"account_no": "2038888888",
"direction": "in",
"amount": 500000,
"signed_amount": 500000,
"ref_id": "FT26081234567890",
"description": "THANH TOAN DON DH1024",
"happened_at": "2026-08-20 19:30:00",
"raw": {}
}
}
}Xác minh chữ ký HMAC
Bắt buộc dùng raw body chưa parse/chưa format lại và so sánh constant-time.
X-APIBank-Timestamp + "." + raw_body. Secret có dạng whsec_... và chỉ được lưu ở server.<?php
$secret = $_ENV['APIBANK_WEBHOOK_SECRET'];
$timestamp = $_SERVER['HTTP_X_APIBANK_TIMESTAMP'] ?? '';
$received = strtolower($_SERVER['HTTP_X_APIBANK_SIGNATURE'] ?? '');
$rawBody = file_get_contents('php://input');
if (!ctype_digit($timestamp) || abs(time() - (int) $timestamp) > 300) {
http_response_code(401); exit('Timestamp invalid');
}
$expected = hash_hmac('sha256', $timestamp.'.'.$rawBody, $secret);
if (!hash_equals($expected, $received)) {
http_response_code(401); exit('Signature invalid');
}
$event = json_decode($rawBody, true, 512, JSON_THROW_ON_ERROR);
// INSERT event_id với UNIQUE INDEX trước khi cập nhật đơn hàng.
http_response_code(204);import crypto from 'node:crypto';
import express from 'express';
const app = express();
app.post('/webhooks/apibank', express.raw({ type: 'application/json' }), (req, res) => {
const timestamp = req.get('X-APIBank-Timestamp') || '';
const received = req.get('X-APIBank-Signature') || '';
if (!/^\d+$/.test(timestamp) || Math.abs(Date.now()/1000 - Number(timestamp)) > 300)
return res.status(401).send('Timestamp invalid');
const expected = crypto.createHmac('sha256', process.env.APIBANK_WEBHOOK_SECRET)
.update(`${timestamp}.${req.body.toString('utf8')}`).digest('hex');
const valid = received.length === expected.length &&
crypto.timingSafeEqual(Buffer.from(received), Buffer.from(expected));
if (!valid) return res.status(401).send('Signature invalid');
const event = JSON.parse(req.body.toString('utf8'));
// Lưu event.event_id bằng UNIQUE INDEX rồi xử lý trong queue.
return res.sendStatus(204);
});Retry và chống xử lý trùng
Webhook có cơ chế at-least-once: một event có thể đến nhiều lần.
- Nếu timeout, lỗi mạng hoặc response ngoài 2xx, hệ thống gửi lại cùng
event_id. - Lần retry đầu sau khoảng 60 giây, sau đó 2, 4, 8… phút và tối đa mỗi 60 phút.
- Tối đa 100 lần. Nếu event đã xử lý, trả 2xx ngay và không cộng tiền lần nữa.
event_id với UNIQUE INDEX → commit → queue cập nhật đơn hàng → trả 204.Checklist trước khi chạy production
Dùng danh sách này trong code review và kiểm thử staging.
- HTTPS hợp lệ; backend timeout rõ ràng; retry có backoff + jitter.
- Token và secret chỉ ở secret manager; log che toàn bộ credential.
- Webhook kiểm tra timestamp, HMAC raw body và unique
event_id. - Không gọi API ngoài trước khi trả 2xx; tác vụ nặng chạy qua queue.
- Có cảnh báo cho 401/403/404, tỷ lệ 5xx, timeout và queue lag.
- Test lại bằng một giao dịch nhỏ trước khi bật tự động xác nhận đơn hàng.