Chuyển tới nội dung chính

Bước 1 - Tạo đơn hàng ở phía hệ thống của bạn

Khi người dùng nhấn nút "Thanh toán", Mini App cần gọi đến backend để tạo đơn hàng.

Tạo đơn hàng

Backend

Backend cần cung cấp một API để lưu trữ thông tin đơn hàng của người dùng như: các món đồ trong giỏ hàng, tổng tiền, phương thức thanh toán, địa chỉ giao hàng,... API này cần trả về ID của đơn hàng được tạo trong hệ thống của bạn:

Hướng dẫn tạo file src/backend.ts ở bước Chuẩn bị backend

src/backend.ts
- import { Order as OrderInfo } from "./types";
+ import { CreateOrderRequest, Order as OrderInfo } from "./types";

express()
+ .use(express.json())
.use(cors({ origin: ["https://h5.zdn.vn", "http://localhost:3000"] }))
.get("/", async (req, res) => {
res.json({
message: "Đây là backend cho Checkout SDK Tutorial!",
});
})
.get("/products", async (req, res) => {
res.json((await import("./mock/products.json")).default);
})
.get("/categories", async (req, res) => {
res.json((await import("./mock/categories.json")).default);
})
.get("/banners", async (req, res) => {
res.json((await import("./mock/banners.json")).default);
})
.get("/stations", async (req, res) => {
res.json((await import("./mock/stations.json")).default);
})
.get("/orders", async (req, res) => {
const allOrders = db.data.orders;
const orderInfos = allOrders.map((order) => order.info).reverse();
res.json(orderInfos);
})
+ .post("/orders", async (req, res) => {
+ const { zaloUserId, items, total } = req.body as CreateOrderRequest;
+ const id = db.data.orders.length + 1;
+ const order: Order = {
+ id,
+ zaloUserId,
+ info: {
+ id,
+ items,
+ total,
+ delivery: {
+ type: "pickup",
+ stationId: 1,
+ },
+ note: "",
+ createdAt: new Date(),
+ receivedAt: new Date(),
+ status: "pending",
+ paymentStatus: "pending",
+ },
+ };
+ db.data.orders.push(order);
+ db.write();
+ res.json({
+ message: "Đã tạo đơn hàng thành công!",
+ orderId: order.id,
+ });
+ })
.listen(port, () => {
console.log(`Server running at http://localhost:${port}`);
});

Khai báo các interface để thuận tiện cho việc giao tiếp giữa frontend và backend:

src/types.d.ts
+ interface CreateOrderRequest {
+ zaloUserId: string;
+ items: CartItem[];
+ total: number;
+ }
+
+ interface CreateOrderReponse {
+ orderId: string;
+ }

Frontend

Sử dụng util requestWithPost để gọi đến backend với payload chứa thông tin đơn hàng và nhận về ID của đơn hàng được tạo:

src/hooks.ts
- import { Product } from "@/types";
+ import { CreateOrderReponse, CreateOrderRequest, Product } from "@/types";
+ import { requestWithPost } from "./utils/request";

export function useCheckout() {
const { totalAmount } = useAtomValue(cartTotalState);
const [cart, setCart] = useAtom(cartState);
const requestInfo = useRequestInformation();
const navigate = useNavigate();
const refreshNewOrders = useSetAtom(ordersState("pending"));

return async () => {
try {
- await requestInfo();
+ const userInfo = await requestInfo();

+ // 1. Tạo đơn hàng ở phía hệ thống của bạn
+ const { orderId: myOrderId } = await requestWithPost<
+ CreateOrderRequest,
+ CreateOrderReponse
+ >("/orders", {
+ zaloUserId: userInfo.id,
+ items: cart,
+ total: totalAmount,
+ });

setCart([]);
Tại sao lại là src/hooks.ts?

Bạn có thể viết code trực tiếp trong onClick handler của <Button>Thanh toán</Button>. Tuy nhiên, để tái sử dụng và tách các logic phức tạp ra khỏi UI component, template ZaUI Market đặt các logic này trong src/hooks.ts.

Logic thanh toán ở Mini App được đặt trong custom hook useCheckout. Logic này bao gồm: tạo đơn hàng bằng Checkout SDK, kích hoạt thanh toán, clear giỏ hàng, hiện thông báo "Thanh toán thành công", và cuối cùng, redirect người dùng đến trang "Lịch sử đơn hàng". Tutorial này sẽ hướng dẫn bạn tích hợp luồng thanh toán hoàn chỉnh hơn, do đó, chúng ta bỏ đoạn code tạo đơn hàng bằng Checkout SDK trong demo (đầu khối try và kết thúc trước setCart([]);), và bắt đầu ở dòng 94:

Giải thích logic khi nhấn nút Thanh toán

Kết thúc bước này, bạn sẽ nhận được myOrderId (định danh của đơn hàng được tạo trong hệ thống của bạn).