Token Thẻ
Tổng quan
Thanh toán thẻ ATM/CC bằng token là một tính năng cho phép khách hàng đồng ý lưu trữ thông tin token thẻ thanh toán của họ để sử dụng trong tương lai, cho phép thanh toán nhanh hơn và dễ sử dụng để thanh toán trực tuyến:
- Hỗ trợ thẻ tín dụng
- Hỗ trợ thẻ ATM với + 40 ngân hàng qua NAPAS
Thanh toán bằng token thẻ là lựa chọn phổ biến ở nhiều ngành khác nhau:
- Subscription: Khách hàng đồng ý cho doanh nghiệp thu tiền hóa đơn định kỳ dựa trên token thẻ của họ
- Ecommerce: Thông tin token thanh toán của chủ thẻ được lưu trong hồ sơ để tránh việc họ phải nhập lại thông tin thẻ của mình cho mỗi đơn hàng
- Travel: Thông tin token thẻ được lưu trữ thường được sử dụng để dễ dàng hoàn tiền và phạt tiền đối với các sự cố, chẳng hạn như phí không sử dụng dịch vụ, v.v.
Trong các phần tiếp theo, chúng tôi sẽ hướng dẫn bạn từng bước để tích hợp Zalopay. Bạn có thể dùng thử ứng dụng demo của chúng tôi để có cái nhìn tổng quan. Một triển khai ví dụ hoàn chỉnh (với NextJs) cũng được cung cấp tại github repository.
Tiền điều kiện
Trước khi bạn bắt đầu, hãy đảm bảo các công việc sau được thực hiện để tích hợp suôn sẻ:
Đã đăng ký tài khoản merchant thành công và có được
app_id
,mac_key
từ Merchant Portal.Hiểu cách sử dụng và đặc điểm kỹ thuật của các API sau:
- CreateOrder API
- QueryOrder API
- CreateBinding API
- Unbind API
- PayByToken API
- QueryPaymentToken API
Khái niệm callback và truyền dữ liệu an toàn.
Cách thực hiện
Zalopay cung cấp cho người bán khời tạo 2 luồng liên kết thẻ ATM/CC của người dùng cho việc thực hiện thanh toán bằng token thẻ ATM/CC, bao gồm:
Luồng 1. Chỉ liên kết thẻ ATM/CC
Người bán khởi tạo liên kết thẻ ATM/CC cho người dùng → Người dùng thực hiện việc xác nhận liên kết thẻ → Người bán lưu trử thông tin token thẻ của người dùng để xử lý thanh toán những lần sau.
Luồng 2. Thực hiện thanh toán bằng thẻ ATM/CC và liên kết thẻ
Người bán khởi tạo đơn hàng có chứa thông tin người dùng đồng ý liên kết thẻ ATM/CC → Người dùng thực hiện thanh toán đơn hàng bằng thẻ ATM/CC → Sau khi thanh toán thành công, người bán lưu trử thông tin token thẻ ATM/CC của người dùng để xử lý thanh toán những lần sau.
Tích hợp
Phần này tập trung vào cách tích hợp luồng thanh toán bằng token thẻ từ Zalopay. Một quy trình công việc điển hình bao gồm các bước sau:
- Khởi tạo liên kết với một thẻ.
- Thanh toán bằng token thẻ.
- Hủy liên kết một thẻ.
Bước 1. Khởi tạo liên kết với một thẻ ATM/CC
Để người dùng có thể thanh toán bằng tính năng token thẻ, trước tiên người bán phải khởi tạo yêu cầu tạo một liên kết mới cho hợp đồng liên kết thẻ CC/ATM của người dùng với ứng dụng của bạn.
Luồng 1. Chỉ liên kết thẻ ATM/CC
- Từ máy chủ của bạn, hãy gọi CreateBinding API. Để cụ thể hơn, vui lòng tham khảo API Explorer để biết các tham số quan trọng khác.
Đây là một ví dụ về cách tạo liên kết cho một hợp đồng liên kết thẻ:
/ For a working example, please navigate to:
// https://github.com/zalopay-samples/quickstart-nextjs-tokenized-payment
import axios from "axios";
import CryptoJS from "crypto-js";
import {
API_ROUTES,
configZLP,
ZLP_API_PATH
} from "../../config";
export default async function handler(req, res) {
if (req.method === 'POST') {
try {
const binding_data = "";
const bind = {
app_id: configZLP.app_id,
app_trans_id: req.body.appTransID,
req_date: Date.now(), // milliseconds
identifier: "ZLP User",
max_amount: 0,
binding_type: 'CARD',
binding_data: JSON.stringify(binding_data),
callback_url: configZLP.host + API_ROUTES.AGREEMENT_CALLBACK,
redirect_url: "http://localhost:3000/cart" // testing purpose
};
// app_id|app_trans_id|binding_data|binding_type|identifier|max_amout|req_date
const data = configZLP.app_id + "|" + bind.app_trans_id + "|" + bind.binding_data + "|" + bind.binding_type + "|" + bind.identifier + "|" + bind.max_amount + "|" + bind.req_date;
bind.mac = CryptoJS.HmacSHA256(data, configZLP.key1).toString();
axios.post(configZLP.zlp_endpoint + ZLP_API_PATH.AGREEMENT_BINDING, null, {
params: bind
})
.then(result => {
if (result.data.return_code === 1) {
res.status(200).json({
binding_token: result.data.binding_token,
binding_qr_link: result.data.binding_qr_link // web-based scenario
});
} else {
res.status(200).json({
error: true,
error_code: result.data.sub_return_code,
message: result.data.sub_return_message
});
}
})
.catch(err => console.log(err));
} catch (err) {
res.status(500).json({
statusCode: 500,
message: err.message
});
}
} else {
res.setHeader('Allow', 'POST');
res.status(405).end('Method Not Allowed');
}
}
Sau đó bạn sẽ nhận được phản hồi như thế này:
{
"return_code": 1,
"return_message": "Operation success.",
"sub_return_code": 1,
"sub_return_message": "Operation success.",
"binding_token": "",
"deep_link": "",
"binding_qr_link": "https://qcgateway.zalopay.vn/binding?binding_id=231120EVqxdkzxWxQQuVyqDpZ9dR0DW",
"short_link": ""
}
- Hiển thị trang thông tin để người dùng thực hiện liên kết thẻ
Trong phần dữ liệu response có chứa giá trị binding_qr_link
. Người bán cần điều hướng mở trang binding_qr_link
cho phép người dùng nhập thông tin thẻ ATM/CC để xác nhận sự liên kết thẻ trên nền tảng của người bán.
Sau khi người dùng xác nhận thành công, Zalopay sẽ chuyển hướng đến hệ thống bán hàng (web, app) với các thông số:
Tham số | Kiểu dữ liệu | Độ dài tối đa | Mô tả |
---|---|---|---|
binding_id | String | 64 | ID của liên kết đã được xác nhận trong hệ thống Zalopay. Người bán cần lưu trữ giá trị binding_id cho việc sử dụng API hủy liên kết (Unbinding API) |
status | Int | 1: Thành công. Các mã lỗi khác là thất bại |
Hãy sử dụng binding_id
này để truy vấn trạng thái liên kết trong trường hợp bạn không nhận được callback thông báo kết quả liên kết thẻ CC/ATM thành công từ Zalopay.
- Nhận kết quả liên kết
Khi người dùng hoàn tất việc liên kết bằng cách nhập thông tin thẻ trên website và xác nhận, Zalopay sẽ thông báo kết quả liên kết thẻ CC/ATM thông qua callback bằng request POST với Content-Type: application/json.
Dữ liệu callback bao gồm trường pay_token mà người bán sẽ lưu trữ trong hệ thống cho các thanh toán sau này sử dụng token này. Cụ thể hơn:
Số thứ tự | Tham số | Kiểu dữ liệu | Độ dài tối đa | Mô tả |
---|---|---|---|---|
1 | app_id | Int | AppID của đối tác, được hệ thống Zalopay tạo. | |
2 | app_trans_id | String | 40 | Merchant unique ID để liên kết. |
3 | binding_type | String | 20 | Phân loại liên kết. Trong trường hợp náy, giá trị là CARD |
4 | binding_id | String | 64 | ID của liên kết đã được xác nhận trong hệ thống Zalopay. Người bán cần lưu trữ giá trị binding_id cho việc sử dụng API hủy liên kết thẻ ATM/CC (Unbinding API) |
5 | pay_token | String | 128 | Token công khai sử dụng khi thanh toán token thẻ. Người bán cần lưu trữ giá trị pay_token cho việc sử dụng các lần thanh toán tiếp theo. |
6 | server_time | Int | Server timestamp (seconds). | |
7 | merchant_user_id | String | 128 | Trường định danh người dùng của hệ thống người bán khi liên kết. Đây là trường identifier trong API khởi tạo tạo liên kết (Create Binding API) |
8 | status | Int | 1: Active, 3: Cancelled | |
9 | msg_type | Int | Loại hành động: 1: Người dùng xác nhận liên kết 2: Người dùng cập nhật liên kết | |
10 | card_id | String | 128 | Mã định danh duy nhất cho thẻ thanh toán, được sử dụng để kiểm tra thông tin trùng lặp thẻ |
11 | masked_card_number | String | 20 | Thông tin số thẻ đã được che (Ví dụ: "520032********5097 ") |
12 | issuing_bank_logo | String | 256 | Url chứa thông tin logo của Ngân hàng phát hành thẻ |
13 | issuing_bank_name | String | 64 | Thông tin tên Ngân hàng phát hành thẻ (Ví dụ: "Vietinbank ") |
Luồng 2. Thực hiện thanh toán bằng thẻ ATM/CC và liên kết thẻ
Trong quy trình thực hiện thanh toán bằng thẻ ATM/CC và liên kết thẻ, khi tạo một đơn hàng thông qua API Create Order
, người bán cần phải truyền thêm thông tin "bindinginfo
" trong tham số "embed_data
". Thông tin "bindinginfo
" phải bao gồm các chi tiết sau:
- agree_to_link: Thông tin người dùng đồng ý liên kết thẻ, Người bán phải truyền thông tin này với giá trị là: true.
- app_trans_id: Thông tin uniqued ID để khởi tạo yêu cầu liên kết được tạo bởi hệ thống người bán, Người bán cần lưu trữ thông tin này, để sử dụng cho việc truy vấn token thẻ đã liên kết của người dùng (dùng
API Query Payment Token
). Định dạng phải theoyymmddxxx
. Ví dụ:230313_123456
. - identifier: Trường định danh người dùng của hệ thống người bán khi liên kết, có thể là user's ID, số điện thoại, email, vv. Ví dụ: 84903863801.
- binding_type: Phân loại liên kết. trong trường hợp này, giá trị phải là: CARD.
- callback_url: Thông tin url để nhận thông báo kết quả liên kết từ hệ thống Zalopay . Ví dụ: https://merchant.com/binding-result/
Định dạng dữ liệu bắt buộc của tham số embed_data's trong luồng này, tham khảo như bên dưới:
Tham số | Kiểu dữ liệu | Định dạng | Ví dụ |
---|---|---|---|
bindinginfo | JSON String | {"field_name":"value"} | {"bindinginfo":"{\"agree_to_link\":true,\"app_trans_id\":\"230313_123456\",\"identifier\":\"84903863801\",\"binding_type\":\"CARD\",\"callback_url\":\"https://merchant.com/binding-result/\"}"} |
Bước 2. Thanh toán sử dụng token thẻ
- Từ máy chủ của bạn, hãy gọi
Create Order API
để tạo đơn hàng.
Dưới đây là một số ví dụ về cách bạn sẽ xử lý nó dưới backend:
// For a working example, please navigate to:
// https://github.com/zalopay-samples/quickstart-nextjs-dynamic-qrcode
import axios from "axios";
import CryptoJS from "crypto-js";
import moment from "moment";
import {
configZLP
} from "../config";
const embed_data = {};
const items = [{}]; // todo: collect items from Cart page
const transID = Math.floor(Math.random() * 1000000);
const appTransID = `${moment().format('YYMMDD')}_${transID}`;
const order = {
app_id: configZLP.app_id,
app_trans_id: appTransID,
app_user: "user123",
app_time: Date.now(), // miliseconds
item: JSON.stringify(items),
embed_data: JSON.stringify(embed_data),
amount: 50000,
description: `Payment for the order #${transID}`,
bank_code: "zalopayapp",
callback_url: configZLP.callback_url
};
const data = [configZLP.app_id, order.app_trans_id, order.app_user, order.amount, order.app_time, order.embed_data, order.item].join("|");
order.mac = CryptoJS.HmacSHA256(data, configZLP.key1).toString();
axios.post(configZLP.endpoint + 'create', null, {
params: order
})
.then(result => {
res.status(200).json({
appTransID: appTransID,
url: result.data.order_url
});
})
.catch(err => console.log(err));
Sau khi Zalopay chấp nhận lệnh tạo đơn hàng của bạn, bạn sẽ nhận được phản hồi như sau:
{
"return_code": 1,
"return_message": "...",
"sub_return_code": 1,
"sub_return_message": "...",
"zp_trans_token": "20090400000112548Y3z18",
"order_url": "https://sbgateway.zalopay.vn/openinapp?order=eyJ6cHRyYW5zdG9rZW4iOiIyMDA5MDQwMDAwMDExMjU0OFkzejE4IiwiYXBwaWQiOjI1NTN9",
"order_token": "ptazBLb128DJ6MSe4d-I2okWQpO7FdUwK9VA4MNqFliUIO1SM3E8ElOsum-iie61rou4A1lblWwddvCwKObzFpo0xJRX4AgP6moSsVqKsxM8K-Duix5ZdH3HFNN07fk7"
}
Nếu bạn nhận được phản hồi với return_code khác 1, hãy tham khảo tài liệu tham khảo về mã trạng thái để khắc phục sự cố.
- Sau khi người bán tạo đơn hàng thanh toán thành công, bạn cần gọi
Pay By Token API
để thông báo cho Zalopay tiến hành thanh toán.
Đây là một ví dụ về cách bạn thực hiện thanh toán đơn hàng theo token:
// For a working example, please navigate to:
// https://github.com/zalopay-samples/quickstart-nextjs-dynamic-qrcode
import axios from "axios";
import CryptoJS from "crypto-js";
import {
configZLP,
ZLP_API_PATH
} from "../../config";
export default async function handler(req, res) {
if (req.method === 'POST') {
try {
const postData = {
app_id: configZLP.app_id,
identifier: "ZLP User",
binding_type: "CARD"
pay_token: req.body.payToken,
zp_trans_token: req.body.zpTransToken,
req_date: Date.now(), // milliseconds
};
// ap_pid|identifier|zp_trans_token|pay_token|req_date
const data = configZLP.app_id + "|" + postData.identifier + "|" + postData.zp_trans_token + "|" + postData.pay_token + "|" + postData.req_date;
postData.mac = CryptoJS.HmacSHA256(data, configZLP.key1).toString();
axios.post(configZLP.zlp_endpoint + ZLP_API_PATH.AGREEMENT_PAY, null, {
params: postData
})
.then(result => {
if (result.data.return_code === 1) {
res.status(200).json(result.data);
} else {
res.status(200).json({
error: true,
error_code: result.data.sub_return_code,
message: result.data.sub_return_message
});
}
})
.catch(err => console.log(err));
} catch (err) {
console.log(err)
res.status(500).json({
statusCode: 500,
message: err.message
});
}
} else {
res.setHeader('Allow', 'POST');
res.status(405).end('Method Not Allowed');
}
}
Sau khi Zalopay chấp nhận lệnh thanh toán của bạn, bạn sẽ nhận được phản hồi như sau
{
"return_code": 3,
"return_message": "...",
"sub_return_code": 3,
"sub_return_message": "...",
"verification_url": "https://qcgateway.zalopay.vn/querypaybytokenstatus?order=eyJhcHBpZCI6MTAwMzMsInpwdHJhbnN0b2tlbiI6IkFDTG0xNkI4TFVsY1IxWEZldXBDTDQ5dyJ9"
}
Trong phần dữ liệu response có chứa giá trị verification_url . Người bán cần điều hướng mở trang verification_url cho phép người dùng nhập thông tin OTP xác nhận thanh toán.
- Callback đơn hàng đã thanh toán thành công
Sau khi thanh toán đã được xử lý thành công, một callback sẽ được gửi đến người bán để biết chi tiết về khoản thanh toán.
Logic xử lý giống như Dynamic QR, bạn có thể tham khảo tại đây.
Bước 3. Hủy liên kết thẻ ATM/CC
Trong trường hợp người dùng của bạn không muốn sử dụng tính năng thanh toán token thẻ ATM/CC, hãy gọi Unbind API
với binding_id
được trả về khi liên kết được xác nhận với Zalopay.
{
"return_code": 1,
"return_message": "...",
"sub_return_code": 1,
"sub_return_message": "..."
}