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

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.

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 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ẻ:

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:

  1. Khởi tạo đơn hàng ZOD
  2. 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:

src/api/zod/create/index.js
// 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}"
}
thông tin

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:

pages/index.js
// 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:

NoTham sốKiểu dữ liệuMô tả
1appIdStringĐịnh danh ứng dụng được cung cấp bởi Zalopay.
2mcRefIdStringMerchant order's reference ID.
3amountIntSố tiền đơn hàng.
4zpTransIdIntZalopay transaction id.
5serverTimeIntServer timestamp (seconds).
6channelIntPayment channel.
7zpUserIdStringĐịnh danh người dùng Zalopay với mỗi người bán.
8userFeeAmountIntSố tiền phí.
9discountAmountIntSố tiền giảm giá.
10userChargeAmountIntSố tiền người dùng phải trả.
cẩn thận
  • Đ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ụ:

src/api/callback/index.js
// 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:

src/api/zod/query_status/index.js
// 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.