Chuyển đến nội dung chính

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.

Payment flow 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 callbacktruyề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. Payment flow

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. Payment flow

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:

  1. Khởi tạo liên kết với một thẻ.
  2. Thanh toán bằng token thẻ.
  3. 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

  1. 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": ""
}
  1. 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 đaMô tả
binding_idString64ID 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)
statusInt1: 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.

  1. 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 đaMô tả
1app_idIntAppID của đối tác, được hệ thống Zalopay tạo.
2app_trans_idString40Merchant unique ID để liên kết.
3binding_typeString20Phân loại liên kết. Trong trường hợp náy, giá trị là CARD
4binding_idString64ID 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)
5pay_tokenString128Token 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.
6server_timeIntServer timestamp (seconds).
7merchant_user_idString128Trườ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)
8statusInt1: Active, 3: Cancelled
9msg_typeIntLoạ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
10card_idString128Mã đị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ẻ
11masked_card_numberString20Thông tin số thẻ đã được che (Ví dụ: "520032********5097")
12issuing_bank_logoString256Url chứa thông tin logo của Ngân hàng phát hành thẻ
13issuing_bank_nameString64Thô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 theo yymmddxxx. 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ạngVí dụ
bindinginfoJSON 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ẻ

  1. 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ố.

  1. 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.

  1. 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": "..."
}