Hosted Link & SDK
✧ Mở bằng AI
Hosted Link là trang do Pay2S cung cấp để khách hàng nhập thông tin, đồng ý liên kết và xác thực OTP. Backend đối tác tạo phiên; frontend mở URL trả về bằng SDK hoặc chuyển trang. Danh sách lựa chọn được cấu hình theo từng ứng dụng Partner.
Môi trường và chuẩn bị
| Môi trường | API base URL | SDK 1.1.0 |
|---|---|---|
| Production | https://api-partner.pay2s.vn |
https://merchant.pay2s.vn/sdk/pay2s-bankhub-1.1.0.js |
| UAT | https://api-partner-uat.pay2s.vn |
https://merchants-uat.pay2s.vn/sdk/pay2s-bankhub-1.1.0.js |
UAT dùng thông tin xác thực riêng, kết quả giả lập và OTP thử nghiệm 123456. Không dùng kết quả UAT để xác nhận liên kết ngân hàng thật. Kiểm tra environment trong kết quả API và webhook.
Trong Merchant → BankHub → Thiết lập, bật Cho phép tạo liên kết ngân hàng, lưu các địa chỉ HTTPS quay lại và cấu hình Webhook event — trạng thái liên kết. UAT quản lý địa chỉ tại URL & Webhook của ứng dụng UAT. Hạn mức production theo gói đang còn hiệu lực; UAT giới hạn 5 tài khoản.
Lấy Bearer Token theo Authentication. Access Key, Secret Key và API token chỉ lưu tại backend.
Ngân hàng được hỗ trợ
| Ngân hàng | Loại tài khoản | Kết nối | Thông tin bổ sung |
|---|---|---|---|
| ACB | Cá nhân | OpenAPI | Tên chủ tài khoản, số điện thoại |
| ACB | Doanh nghiệp | OpenAPI | Tên chủ tài khoản, số điện thoại, username ACB Onebiz |
| MBBank | Cá nhân, doanh nghiệp | OpenAPI | Tên chủ tài khoản, số điện thoại, email, CCCD/mã số thuế |
| BIDV | Cá nhân, hộ kinh doanh | OpenAPI | Tên chủ tài khoản, số điện thoại, email, CCCD/mã số thuế, hậu tố tài khoản ảo |
| Vietcombank | Cá nhân, doanh nghiệp | RPA | Tên đăng nhập, mật khẩu |
| Vietinbank | Cá nhân, doanh nghiệp | RPA | Tên đăng nhập, mật khẩu |
| Techcombank | Doanh nghiệp | RPA | Tên đăng nhập, mật khẩu |
BIDV không có lựa chọn doanh nghiệp trong Hosted Link. Tài khoản ảo được xác nhận theo kết quả ngân hàng; giữ merchantId và vaNumber để định tuyến giao dịch. Luồng RPA có thể hoàn tất ngay sau đăng nhập hoặc yêu cầu OTP (Vietcombank cá nhân); không giả định mọi ngân hàng đều có cùng bước OTP. Tên chủ tài khoản Techcombank doanh nghiệp có thể chưa có ngay khi kết nối thành công.
Chọn ngân hàng hiển thị
Partner vào BankHub → Thiết lập → Ngân hàng hiển thị trên Hosted Link, bật/tắt từng ngân hàng và loại tài khoản rồi lưu. UAT có lựa chọn riêng tại URL & Webhook. Mặc định khi nâng cấp chỉ bật ACB cá nhân để giữ cấu hình cũ; Partner tự bật thêm loại cần dùng.
Hosted Link chỉ cho bắt đầu với lựa chọn đã bật, backend kiểm tra lại trước khi gọi ngân hàng. Tắt một lựa chọn không ngắt tài khoản đã liên kết và không chặn hoàn tất phiên đã gửi yêu cầu/đang chờ OTP. Nếu tắt tất cả, không thể tạo phiên liên kết mới. Thiết lập này áp dụng riêng cho Hosted Link, không tắt ngân hàng trong các API thêm ngân hàng khác.
Khách nhập thông tin ngân hàng trực tiếp trên trang Pay2S. Không thu hộ mật khẩu/OTP ở frontend của đối tác và không gửi những trường này qua API tạo phiên. Các trường bổ sung được form Hosted Link hiển thị theo lựa chọn. Thông tin đăng nhập không nằm trong callback SDK hoặc webhook đối tác.
1. Backend tạo phiên
POST /v1/bankhub/link-sessions
Authorization: Bearer API_TOKEN
Content-Type: application/json
{
"customer_reference": "DEV_d33eb1382b55f87313b6840bff80f880_179165997315205323a",
"purpose": "LINK_BANK_ACCOUNT",
"parent_origin": "https://developer.example.com"
}
| Trường | Quy định |
|---|---|
customer_reference |
Bắt buộc, 1–128 ký tự: chữ, số, _ . : @ -. Backend lấy từ khách đã đăng nhập, không tin mã khách tùy ý gửi từ trình duyệt. |
purpose |
Mặc định LINK_BANK_ACCOUNT; các tác vụ khác xem bên dưới. |
parent_origin |
Dùng khi nhúng SDK; origin HTTPS phải khớp origin của một địa chỉ quay lại đã lưu. Không chứa đường dẫn. |
return_url |
Không bắt buộc với modal; nếu truyền phải khớp đầy đủ một URL đã lưu và có state. |
state |
Giá trị ngẫu nhiên gắn với phiên đăng nhập; chữ, số, _, -, tối đa 128 ký tự. Đối tác kiểm tra khi quay lại. |
application_id |
Chọn ứng dụng nếu thông tin xác thực của chủ tài khoản truy cập được nhiều ứng dụng. |
Nếu đối tác dùng nhiều website, đăng ký các URL HTTPS tương ứng và truyền đúng parent_origin của website mở SDK cho từng phiên. URL quay lại điều hướng trình duyệt; không phải URL nhận webhook.
Ví dụ phản hồi (URL và mã phiên chỉ minh họa):
{
"success": true,
"data": {
"session_id": "2f78eb564c44b9f3edd3a10d7807426c",
"environment": "uat",
"expires_in": 900,
"hosted_link_url": "https://merchants-uat.pay2s.vn/bankhub/connect#token=YOUR_KEY"
}
}
Dùng nguyên hosted_link_url nhận được, không tự ghép token hoặc đổi host. Link có hiệu lực 15 phút và chỉ trả về cho đúng khách hàng; không ghi vào log hoặc analytics. Tạo phiên chưa thực hiện thao tác ngân hàng. Giới hạn tạo phiên hiện tại: 10 phiên/phút/ứng dụng.
2. Frontend mở form
Ví dụ dưới đây giả định backend của bạn trả nguyên cấu trúc success/data từ API tạo phiên. Endpoint /my-api/bankhub/link thuộc hệ thống đối tác, cần kiểm tra đăng nhập và CSRF.
<script src="https://merchant.pay2s.vn/sdk/pay2s-bankhub-1.1.0.js" charset="utf-8"></script>
<button id="link-bank">Liên kết ngân hàng</button>
<script>
let hub;
document.querySelector('#link-bank').onclick = async () => {
const response = await fetch('/my-api/bankhub/link', { method: 'POST' });
const result = await response.json();
if (!response.ok || !result.success) throw new Error('Không tạo được phiên');
if (hub) hub.destroy();
hub = Pay2SBankHub.create({
mode: 'modal',
onSuccess: async () => {
// Hàm do đối tác triển khai: gọi backend để tra kết quả Pay2S.
await refreshLinkedAccountsFromYourBackend();
},
onEvent: event => { /* Cập nhật tiến trình giao diện */ },
onExit: event => { /* Chỉ đóng giao diện, không kết luận hủy phiên */ },
onExpired: event => { /* Cho khách tạo phiên mới */ },
onError: event => { /* Hiển thị lỗi và cho kiểm tra trạng thái */ }
});
hub.initLink(result.data.hosted_link_url, result.data.session_id);
hub.open();
};
</script>
Các chế độ: modal, newTab, newWindow, redirect; popup là tên tương thích cũ. Với newTab/newWindow, chuẩn bị link trước rồi gọi open() từ thao tác bấm để tránh bị chặn popup. redirect rời trang nên không duy trì callback trên trang cũ; dùng return_url, kiểm tra state và tra kết quả tại backend.
hub.close() đóng giao diện; hub.destroy() dọn instance/listeners. Đóng cửa sổ không hủy yêu cầu ngân hàng. Nút Hủy yêu cầu trong Hosted Link chỉ hủy khi trạng thái cho phép.
Các callback thành công của SDK: FINISHED_BANK_ACCOUNT_LINK, FINISHED_BANK_ACCOUNT_UNLINK, FINISHED_BANK_ACCOUNT_REACTIVATE. Callback chỉ phục vụ giao diện; backend xác minh kết quả bằng API hoặc webhook đã kiểm tra chữ ký.
3. Backend lấy kết quả
GET /v1/bankhub/link-sessions/{session_id}
Authorization: Bearer API_TOKEN
Phản hồi success/data có session_id, customer_reference, purpose, unlink_mode, status, environment, account, account_status, sequence. account có thể là null khi chưa có kết quả tài khoản. Khi cần chọn ứng dụng, thêm query application_id.
GET /v1/bankhub/customer-accounts?customer_reference=CUSTOMER-123
Authorization: Bearer API_TOKEN
API danh sách trả data là mảng các ánh xạ tài khoản của khách trong ứng dụng. Đây có thể bao gồm lịch sử liên kết; không suy ra mọi bản ghi đều đang hoạt động. account.id là ID ánh xạ BankHub; user_bank_id là ID ngân hàng Pay2S dùng cho các API giao dịch.
| Trạng thái phiên | Ý nghĩa |
|---|---|
created |
Chờ khách mở/nhập thông tin |
sending |
Đang gửi yêu cầu ngân hàng |
awaiting_otp |
Cần khách nhập OTP trên Hosted Link |
verifying |
Đang xác minh |
connected |
Luồng đã hoàn tất; đọc purpose để biết tác vụ |
cancelled, failed, expired, revoked |
Phiên bị hủy, lỗi, hết hạn hoặc thu hồi |
uncertain |
Chưa xác định kết quả ngân hàng; cần đối soát, không tự tạo yêu cầu ngân hàng lặp lại |
4. Hủy, vô hiệu hóa và bật lại
Cùng endpoint tạo phiên, thêm account_id lấy từ account.id hoặc API danh sách khách hàng. Không thay bằng user_bank_id. Backend kiểm tra khách đang đăng nhập sở hữu tài khoản cần thao tác.
purpose |
unlink_mode |
Hành vi |
|---|---|---|
UNLINK_BANK_ACCOUNT |
bank |
Hủy liên kết ACB OpenAPI qua OTP ngân hàng; chưa mở hủy phía ngân hàng cho các loại mới |
UNLINK_BANK_ACCOUNT |
deactivate |
Vô hiệu hóa tại Pay2S, không xác nhận đã hủy phía ngân hàng |
REACTIVATE_BANK_ACCOUNT |
Không cần truyền | Bật lại tài khoản còn tồn tại đã vô hiệu hóa tại Pay2S |
{
"customer_reference": "CUSTOMER-123",
"purpose": "UNLINK_BANK_ACCOUNT",
"account_id": 12,
"unlink_mode": "bank",
"parent_origin": "https://app.example.com"
}
Tài khoản đã hủy phía ngân hàng cần liên kết mới; bật lại tại Pay2S không khôi phục liên kết ngân hàng đã hủy. Đọc purpose và unlink_mode khi xử lý kết quả, không chỉ đọc status=connected.
5. Nhận thông báo và xử lý lỗi
Xem Webhook sự kiện để nhận tiến trình, kết quả tài khoản, xác thực HMAC và retry. Webhook biến động số dư dùng cấu hình webhook giao dịch riêng.
Nếu không tạo được phiên, kiểm tra quyền ứng dụng, bật liên kết, hạn gói, URL đã đăng ký và đúng môi trường. Khi tài khoản đang có phiên xử lý, tra phiên hiện tại trước khi tạo lại. Nếu khách quay lại sau khi link hết hạn, backend tạo phiên mới sau khi kiểm tra kết quả phiên cũ.
Tự đóng form sau khi hoàn tất
SDK 1.1.0 mặc định tự đóng modal/popup/tab do SDK mở sau khi nhận sự kiện hoàn tất với trạng thái connected. Form hiển thị đếm ngược 3 giây và nút Đóng ngay.
const handle = Pay2SBankHub.open({
url: response.data.hosted_link_url,
sessionId: response.data.session_id,
mode: 'modal',
autoClose: true,
autoCloseDelayMs: 3000,
onSuccess: event => { /* Backend kiểm tra kết quả qua API/webhook. */ },
onExit: event => { /* reason: completed, cancelled hoặc closed */ }
});
- autoClose: false giữ form mở, không hiển thị đếm ngược.
- autoCloseDelayMs: số mili giây từ 0 đến 60000; mặc định 3000. Giá trị 0 đóng ngay.
- Thành công: onEvent → onSuccess → đếm ngược → onExit với reason: completed, completed: true. SDK không chờ Promise của callback.
- Hủy yêu cầu được xác nhận bằng CANCELLED / cancelled: onEvent → đóng ngay → onExit với reason: cancelled, completed: false.
- Lỗi, sai OTP, hết hạn hoặc chưa rõ kết quả không tự đóng.
- Đóng thủ công không đồng nghĩa hủy phiên phía server. destroy() dọn giao diện và không phát onExit.
- redirect không hỗ trợ tự đóng hay callback trên trang cũ.
- Cần cập nhật cả SDK trên website nhúng và giao diện Hosted Link để thấy đếm ngược trong form. Backend vẫn xác nhận kết quả bằng API/webhook.
Thay đổi lựa chọn và lỗi liên kết
SDK phát BANK_CHANGED qua onEvent khi người dùng đổi ngân hàng hoặc loại tài khoản. Metadata gồm bank_code, bank_profile, account_type, previous_bank_code, previous_bank_profile và status: created. Đây là sự kiện giao diện trong trình duyệt, không phải webhook và chưa xác nhận ngân hàng đã liên kết. Chọn lại cùng profile không phát thêm sự kiện.
Khi phiên thất bại, Hosted Session API, API kết quả phiên và webhook LINK_SESSION_FAILED có thông tin lỗi an toàn:
{"error":{"code":"BANK_LOGIN_REJECTED","message":"Ngân hàng không chấp nhận thông tin đăng nhập. Kiểm tra tên đăng nhập, mật khẩu và trạng thái tài khoản trước khi tạo phiên mới."}}
Trong webhook và callback SDK, đọc metadata.error; trong kết quả API đọc data.error. SDK onError nhận sự kiện FAILED/UNCERTAIN; form giữ mở để người dùng đọc thông báo.
Mã lỗi: BANK_REJECTED (ngân hàng từ chối), BANK_LOGIN_REJECTED (thông tin đăng nhập bị từ chối), OTP_ATTEMPTS_EXCEEDED (hết lượt OTP), BANK_RESULT_UNKNOWN (chưa rõ kết quả). LINK_FAILED dành cho phiên cũ không lưu chi tiết. Không suy luận mọi lỗi đều do mật khẩu và không gửi phản hồi thô chứa thông tin nhạy cảm của ngân hàng. Lỗi HTTP/validation vẫn trả theo error envelope của API.
Request và response đối chiếu ngày 11/10/2026
Các mẫu dưới đây giữ cấu trúc trường và kiểu dữ liệu. Khóa, chữ ký, URL phiên đã được thay bằng placeholder; danh sách chỉ giữ tối đa hai phần tử và dữ liệu ảnh/PDF/XML dài được rút gọn. Không sao chép placeholder để gọi API thật.
Tạo phiên Hosted Link
Đã gọi API demo/UAT.
POST /v1/bankhub/link-sessions
Body:
{
"customer_reference": "DEV_d33eb1382b55f87313b6840bff80f880_179165997315205323a",
"purpose": "LINK_BANK_ACCOUNT",
"parent_origin": "https://developer.example.com"
}
HTTP 201. Response:
{
"success": true,
"data": {
"session_id": "2f78eb564c44b9f3edd3a10d7807426c",
"environment": "uat",
"expires_in": 900,
"hosted_link_url": "https://merchants-uat.pay2s.vn/bankhub/connect#token=<REDACTED>"
}
}
Tra cứu phiên liên kết
Đã gọi API demo/UAT.
GET /v1/bankhub/link-sessions/{session_id}
Tham số path/query (không gửi JSON body):
{
"session_id": "LAST_SESSION"
}
HTTP 200. Response:
{
"success": true,
"data": {
"session_id": "2f78eb564c44b9f3edd3a10d7807426c",
"purpose": "LINK_BANK_ACCOUNT",
"unlink_mode": "bank",
"customer_reference": "DEV_d33eb1382b55f87313b6840bff80f880_179165997315205323a",
"status": "created",
"error": null,
"environment": "uat",
"account": null,
"account_status": null,
"sequence": 344
}
}
Tài khoản của khách hàng
Đã gọi API demo/UAT.
GET /v1/bankhub/customer-accounts
Tham số path/query (không gửi JSON body):
{
"customer_reference": "CUSTOMER-DEMO"
}
HTTP 200. Response:
{
"success": true,
"data": []
}