Thanh toán khi nhận hàng
Tổng quan
Thanh toán khi nhận hàng (Zalopay On Delivery - ZOD) là giải pháp thanh toán cho phép người dùng thanh toán hàng hóa khi nhận hàng bằng cách quét mã QR bằng Zalopay.
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 cung cấp trong 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:
Khái niệm callback và truyền dữ liệu an toàn.
Cách hoạt động
Luồng thanh toán được mô tả như sau:
Tích hợp
Phần này tập trung vào việc tích hợp luồng ZOD từ Zalopay. Quy trình công việc điển hình mà người bán cần xử lý bao gồm các bước sau:
- Khởi tạo đơn hàng ZOD
- Xử lý kết quả thanh toán
Bước 1. Khởi tạo đơn hàng ZOD
Tại trang thanh toán của người bán, sau khi khách hàng chọn thanh toán khi nhận hàng bằng Zalopay, người bán cần gọi CreateZODInvoice API
để tạo đơn hàng ZOD.
Để xem chi tiết các API khác, 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 bạn tạo một đơn đặt hàng ZOD:
// For a working example, please navigate to:
// https://github.com/zalopay-samples/quickstart-nextjs-zod
[...]
export default async function handler(req, res) {
if (req.method === 'POST') {
try {
const mcExtInfo = {};
const zodInvoice = {
appId: configZLP.app_id,
mcRefId: req.body.mcRefId,
hubId: "",
driverId: "",
amount: req.body.amount,
receiver: req.body.receiver,
orderInfo: req.body.orderInfo,
mcExtInfo: JSON.stringify(mcExtInfo)
};
// appId|mcRefId|amount|mcExtInfo
const data = zodInvoice.appId + "|" + zodInvoice.mcRefId + "|" + zodInvoice.amount + "|" + zodInvoice.mcExtInfo;
zodInvoice.mac = CryptoJS.HmacSHA256(data, configZLP.key1).toString();
const config = {
method: 'post',
url: configZLP.zlp_endpoint + ZLP_API_PATH.ZOD_CREATE_INVOICE,
headers: {
'Content-Type': 'application/json'
},
data: JSON.stringify(zodInvoice)
};
axios.request(config)
.then(result => {
res.status(200).json({
orderUrl: result.data.orderUrl,
});
})
.catch(err => {
res.status(err.response.status).json({
error: true,
message: err.response.data.message
})
});
} 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ư sau:
{
"orderUrl": "https://sbqrpay.zalopay.vn/zod/{token}"
}
Url này được sử dụng để tạo mã QR sẽ được hiển thị trên ứng dụng shipper, sẽ hết hạn và được đánh dấu là KHÔNG HỢP LỆ sau 1 tháng kể từ khi được tạo.
Ngoài ra, người bán cũng có thể truy xuất orderUrl
này bằng API /v2/zod/invoice
.
Người bán sẽ chịu trách nhiệm mã hóa orderUrl
thành mã QR. Điều này có thể đạt được bằng cách sử dụng bất kỳ thư viện tạo mã QR nào mà bạn thấy thuận tiện hoặc thoải mái khi làm việc.
Sau đây là cách Zalopay gợi ý bạn tạo mã QR:
// For a working example, please navigate to:
// https://github.com/zalopay-samples/quickstart-nextjs-zod
import { QRCode } from 'antd';
<div id="qr-code">
<QRCode value={result.url}/>
</div>
Bước 2. Xử lý kết quả thanh toán
Khi người mua của bạn hoàn tất thanh toán bằng cách quét mã QR và xác nhận thanh toán, Zalopay sẽ thông báo kết quả thanh toán qua callback với yêu cầu POST với Content-Type: application/json
.
Dữ liệu callback bao gồm thông tin giao dịch được chỉ định như sau:
No | Tham số | Kiểu dữ liệu | Mô tả |
---|---|---|---|
1 | appId | String | Định danh ứng dụng được cung cấp bởi Zalopay. |
2 | mcRefId | String | Merchant order's reference ID. |
3 | amount | Int | Số tiền đơn hàng. |
4 | zpTransId | Int | Zalopay transaction id. |
5 | serverTime | Int | Server timestamp (seconds). |
6 | channel | Int | Payment channel. |
7 | zpUserId | String | Định danh người dùng Zalopay với mỗi người bán. |
8 | userFeeAmount | Int | Số tiền phí. |
9 | discountAmount | Int | Số tiền giảm giá. |
10 | userChargeAmount | Int | Số tiền người dùng phải trả. |
- Điều quan trọng là phải xác minh rằng yêu cầu thực sự đến từ Zalopay bằng cách sử dụng callback key để xác thực dữ liệu của lệnh callback.
- Vì lý do bảo mật, callback url của bạn phải có cùng domain với máy chủ của bạn.
- Callback end-point của bạn phải được truy cập công khai.
- Do sự cố kỹ thuật như mạng hết thời gian chờ, service của bạn không khả dụng để phục vụ yêu cầu gọi lại... Zalopay có thể không thông báo được cho bạn khi callback.
Dưới đây là một số ví dụ:
// For a working example, please navigate to:
// https://github.com/zalopay-samples/quickstart-nextjs-zod
[...]
export default async function handler(req, res) {
if (req.method === 'POST') {
try {
let result = {};
try {
let dataStr = req.body.data;
let reqMac = req.body.mac;
let mac = CryptoJS.HmacSHA256(dataStr, configZLP.key2).toString();
console.log("mac =", mac);
// validate valid callback from Zalopay server
if (reqMac !== mac) {
// invalid callback
result.return_code = -1;
result.return_message = "mac not equal";
} else {
// payment successfully
// merchant update the order's status
let dataJson = JSON.parse(dataStr, configZLP.key2);
console.log(`💰 Payment callback received!`);
console.log("✅ Update order's status = success where mcRefId =", dataJson["mcRefId"]);
result.return_code = 1;
result.return_message = "success";
}
} catch (ex) {
result.return_code = 0; // Zalopay server will send callback again (max is 3)
result.return_message = ex.message;
}
// response to Zalopay server
res.json(result);
} catch (err) {
res.status(500).json({statusCode: 500, message: err.message});
}
} else {
res.setHeader('Allow', 'POST');
res.status(405).end('Method Not Allowed');
}
}
Chúng tôi khuyên bạn nên chủ động truy vấn trạng thái đơn hàng. Điều này có thể được thực hiện bằng cách gọi POST tới QueryZODOrder API
.
Code mẫu của chúng tôi để truy vấn trạng thái của đơn đặt hàng:
// For a working example, please navigate to:
// https://github.com/zalopay-samples/quickstart-nextjs-zod
[...]
export default async function handler(req, res) {
if (req.method === 'POST') {
try {
let params = {
appId: configZLP.app_id,
mcRefId: req.body.mcRefId,
}
let data = params.appId + "|" + params.mcRefId; // appId|mcRefId
params.mac = CryptoJS.HmacSHA256(data, configZLP.key1).toString();
let config = {
method: 'get',
url: configZLP.zlp_endpoint + ZLP_API_PATH.ZOD_QUERY_STATUS,
headers: {
'Content-Type': 'application/json'
},
params
};
axios(config)
.then(function (response) {
res.status(200).json(response.data);
})
.catch(function (error) {
console.log(error.response.data);
});
} catch (err) {
res.status(500).json({statusCode: 500, message: err.message});
}
} else {
res.setHeader('Allow', 'POST');
res.status(405).end('Method Not Allowed');
}
}
Như đã đề cập trước đó, một đơn đặt hàng đã được tạo sẽ tự động hết hạn và được đánh dấu là KHÔNG HỢP LỆ sau 1 tháng kể từ khi tạo.
- Bạn có thể đặt khoảng thời gian để truy vấn trạng thái đơn hàng ZOD dựa trên thời gian này và coi trạng thái mới nhất là trạng thái cuối cùng.
Sau khi nhận được trạng thái đơn hàng cuối cùng từ Zalopay, bây giờ bạn có thể hiển thị cho người mua trạng thái thanh toán mới nhất.
Xem thêm
Tiếp theo
- Đã cài đặt xong? Hãy kiểm thử kết quả tích hợp.