Thanh toán đơn hàng

Tbox Mini App cho phép Nhà Phát Triển có thể sử dụng Hosted App như một cổng thanh toán.

Nhà Phát Triển tạo các ứng dụng Mini App, và cho phép người sử dụng mua các vật phẩm / đơn hàng trên ứng dụng của Nhà Phát Triển.

Tuy nhiên, khi người sử dụng muốn thanh toán các đơn hàng này, Nhà Phát Triển sẽ không trực tiếp thực hiện chức năng thanh toán trên Mini App của họ, mà dựa vào chức năng thanh toán trên Hosted App.

Một luồng thông thường cho phần Thanh Toán đơn hàng sẽ bao gồm các bước sau:

  • Người dùng đi vào Mini App, lựa chọn sản phẩm / dịch vụ muốn mua
  • Mini App gọi vào Mini App Backend để tạo đơn hàng
  • Mini App Backend gọi vào Hosted Backend để tạo đơn hàng
  • Hosted Backend trả về mã đơn hàng cho Mini App Backend
  • Mini App Backend trả về mã đơn hàng cho Mini App
  • Mini App gọi tới Hosted App để mở màn hình thanh toán đơn hàng
  • Hosted App mở màn hình thanh toán đơn hàng, và chờ người dùng thực hiện chức năng thanh toán
  • Tuỳ vào kết quả thanh toán của người dùng, Hosted App sẽ mở một page tương ứng trên Mini App

image

Từ đây trở về sau, khi chúng ta gọi bước thứ X, tức là chúng ta đang trỏ tới bước thứ X trong hình vẽ ở trên

Trong luồng này, Nhà Phát Triển sẽ phải thực hiện các việc

  • Đối với Mini App Frontend, Nhà Phát Triển thực hiện các việc:

    • Bước 2: Gọi API của Mini App Backend để tạo đơn hàng
    • Bước 6: Gọi Hosted App để mở màn hình thanh toán
    • Bước 10: Cung cấp các page để xử lý đơn hàng sau khi thanh toán thành công
  • Đối với Mini App Backend, Nhà Phát Triển cần làm các bước:

    • Bước 2: Cung cấp API để Mini App có thể tạo đơn hàng
    • Bước 3: Gọi API tới Host để tạo đơn hàng trên Hosted
    • Bước 9: Cung cấp API để nhận sự kiện đơn hàng thanh toán thành công / thất bại từ Host

Để thực hiện các bước trên Mini App Frontend, Nhà Phát Triển có thể sử dụng API của checkoutOrder.

Mở màn hình thanh toán

API checkoutOrder nhận vào tham số là một dictionary có những trường sau

tên trườngkiểu dữ liệubắt buộc phải cóý nghĩa
orderIdstringid của order được trả về từ bước 1.1
widgetobjectthông tin cấu hình widget để render trên màn hình Thanh Toán Đơn Hàng
handlePagestringtên của page sẽ được gọi tới ở bước 1.3

Tại màn hình Thanh Toán Đơn Hàng, Hosted App sẽ render Widget được cấu hình từ thuộc tính widget ở trên.

image

Trong màn hình Thanh Toán như ở hình trên, phần được tô màu hồng là vùng được sử dụng để thể hiện Widget. Trường widget truyền vào hàm checkoutOrder là một object chứa các tham số

tên trườngkiểu dữ liệubắt buộc phải cóý nghĩa
namestringtên của widget
paramsobjectcác parameters truyền vào widget

Bằng cách sử dụng API checkoutOrder, Nhà Phát Triển có thể cài đặt luồng thanh toán như sau

image

Ở file cài đặt màn hình trước khi thanh toán. Chúng ta tạm gọi file này được lưu tại địa chỉ /pages/BillingInfo/index.js

import React, { useState, useEffect } from "react";
import tbox from "tbox";
import api from "./api";
import { Button } from "tbox/ui";
function CheckoutButton() {
const [loading, setLoading] = useState(false);
const handlerCheckout = async () => {
setLoading(true);
const order = await api.createOrder();
tbox.checkoutOrder({
id: order.id,
widget: {
name: "checkout",
params: {
id: order.id,
},
},
handlePage: "OrderDetail",
});
};
return (
<Containter>
{isLoading ? <Loading /> : <Button onClick={handleCheckout} />}
</Container>
);
}

Thông qua việc gọi hàm

tbox.checkoutOrder({
id: order.id,
widget: {
name: "checkout",
params: {
id: order.id,
},
},
handlePage: "OrderDetail",
});

Chúng ta báo với Hosted App mở màn hình Thanh Toán Đơn Hàng với id là order.id. Màn hình này sẽ render thêm Widget tên là checkout, và Widget này sẽ nhận một object với giá trị là {id: order.id}.

Chúng ta có thể cài đặt Widget này tại đường dẫn /widgets/checkout/index.js

import React, { useState, useEffect } from "react";
import tbox from "tbox";
import api from "./api";
import { Button } from "tbox/ui";
export default function Checkout({ id }) {
const [order, setLoading] = useState({});
useEffect(async () => {
const serverOrder = await api.getOrderInfo(id);
setOrder(order);
});
return (
<Containter>
<OrderInfo {...order}>
</Container>
);
}

Trong đoạn code trên, khi render Widget checkout, chúng ta gọi tới API của Mini App Backend, để lấy thông tin của order, sau đó render thông tin của order ngay sau đó.

Trong một số trường hợp, nếu các thông tin để render Widget có thể lấy trực tiếp từ màn hình trước đó, chúng ta cũng có thể sửa lại để khi gọi hàm tbox.checkoutOrder, chúng ta truyền đủ các thông tin của Widget trong đó luôn

image

tbox.checkoutOrder({
id: order.id,
widget: {
name: "checkout",
params: {
provider: "EVN Hồ Chí Minh",
customerCode: "PE12345678901",
customerName: "Bùi Phạm Thanh Tú",
address: "A.03.02 chung cư Fuji Flora, P.Phước Long B, Q.9",
},
},
handlePage: "OrderDetail",
});

Với cách này, lúc render widget, chúng ta không cần phải gọi API tới Mini App Backend để lấy thông tin nữa

import React, { useState, useEffect } from "react";
import tbox from "tbox";
import { Button } from "tbox/ui";
export default function Checkout({
provider,
customerCode,
customerName,
address,
}) {
const [order, setLoading] = useState({});
return (
<Containter>
<Row value={provider} />
<Row value={customerCode} />
<Row value={customerName} />
<Row value={address} />
</Container>
);
}

Xử lý trạng thái thanh toán của Đơn Hàng

Tại bước 10, page của Mini App sẽ nhận được một object chứa các thông tin

tên trườngkiểu dữ liệubắt buộc phải cóý nghĩa
idstringid của order được trả về từ bước 1.1
statusstringtrạng thái thanh toán của đơn hàng

Trạng thái thanh toán của đơn hàng có thể nhận một trong 3 giá trị

tên trườngý nghĩa
successthanh toán thành công
failurethanh toán thất bại
timeouthết thời gian thanh toán

Trong ví dụ ở trên, Nhà Phát Triển gọi API checkoutOrder với các tham số

tbox.checkoutOrder({
id: order.id,
widget: {
name: "checkout",
params: {
id: order.id,
},
},
handlePage: "OrderDetail",
});

Điều này có nghĩa là, sau khi người dùng thực hiện việc thanh toán, Hosted App sẽ đi tới page OrderDetail.

Chúng ta sẽ cài đặt page OrderDetail tại địa chỉ /pages/OrderDetail/index.js như sau

import React, { useState, useEffect } from "react";
import tbox from "tbox";
import api from "./api";
import { Button } from "tbox/ui";
export default function OrderDetail({ id, status }) {
const [loading, setLoading] = useState(false);
const [order, setOrder] = useState({});
useEffect(async () => {
const serverOrder = await api.getOrderInfo(id);
setOrder(order);
});
return (
<Containter>{isLoading ? <Loading /> : <Order order={order} />}</Container>
);
}

Trong đoạn code ở trên, chúng ta bỏ qua trạng thái được trả về từ Hosted App, tuy nhiên, Nhà Phát Triển cũng có thể sử dụng thông tin này để đưa ra những các render khác nhau

import React, { useState, useEffect } from "react";
import tbox from "tbox";
import api from "./api";
import { Button } from "tbox/ui";
function Order({ order, status }) {
if (status == "success") {
return <OrderSuccess order={order} />;
}
if (status == "failure") {
return <OrderFailure order={order} />;
}
if (status == "timeout") {
return <OrderTimeout order={order} />;
}
}
export default function OrderDetail({ id, status }) {
const [loading, setLoading] = useState(false);
const [order, setOrder] = useState({});
useEffect(async () => {
const serverOrder = await api.getOrderInfo(id);
setOrder(order);
});
return (
<Containter>
{isLoading ? <Loading /> : <Order order={order} status={status} />}
</Container>
);
}
Last updated on by Kien Nguyen