[e-commerce website 010] business function: order

The order system interface will be called
Realize order settlement function
Realize wechat payment function

1. Order system interface Leyou order

We don't do development, we just explain

1.1. Import order service (non interview focus)

Copy the Leyou order provided in the pre class materials to the D:\heima\code\leyou directory.

1.2. Swagger UI (code is the document, not the focus of the interview)


1.2.1. What is OpenAPI

With the development of Internet technology, the current website architecture has basically changed from * * the original back-end rendering to front-end rendering, * * that is, the front-end and back-end are completely separated, and the front-end technology and back-end technology are further and further on their respective paths. The only connection between the front end and the back end becomes the API interface; API documents have become a link between front and back-end developers and become more and more important.

Before there was no API documentation tool, everyone wrote API documents by hand, wherever they were written. Moreover, API documents were not standardized and formatted uniformly, and each company was different. This undoubtedly brought disaster to the development.

OpenAPI Specification (OAS) is a project of Linux foundation, which attempts to standardize the development process of RESTful services by defining a language used to describe API format or API definition. At present, v3 Version 0 of the OpenAPI Specification has been released and open source on github.

1.2.2. What is swagger?

OpenAPI is a specification for writing API documents. However, it is very troublesome to write OpenAPI specification documents manually. Swagger is a tool set that implements the OpenAPI specification.

Swagger contains toolsets:

  • Swagger Editor: Swagger Editor allows you to edit the OpenAPI specification in YAML in the browser and preview the document in real time.
  • Swagger UI: swagger UI is a collection of HTML, Javascript and CSS assets, which can dynamically generate beautiful documents from OAS compliant API s.
  • **Swagger Codegen: * * allows automatic generation of API client libraries (SDK generation), server stubs and documents according to OpenAPI specifications.
  • **Swagger Parser: * * independent library for parsing OpenAPI definitions from Java
  • **Swagger Core: * * Java related libraries for creating, using, and using OpenAPI definitions
  • Swagger Inspector (free): API testing tool that allows you to validate your API and generate OpenAPI definitions from existing APIs
  • SwaggerHub (free and commercial): API design and documentation, built for teams using OpenAPI.

1.2.3 practice: SpringBoot integrates Swagger

SpringBoot has integrated swagger and can generate swagger API documents with simple annotations.

1) Introduce dependency


2) Write configuration

public class SwaggerConfig {
    public Docket api() {
        return new Docket(DocumentationType.SWAGGER_2)

    private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                .title("Leyou mall order system")
                .description("Leyou mall order system interface document")

3) Interface declaration

Add the interface description annotation on each handler of the controller:

@Api("Order service interface")
public class OrderController {

    private OrderService orderService;

    private PayHelper payHelper;

     * Create order
     * @param order Order object
     * @return Order number
    @ApiOperation(value = "Create an order interface and return the order number", notes = "Create order")
    @ApiImplicitParam(name = "order", required = true, value = "Order json object,Contains order entries and logistics information")
    public ResponseEntity<Long> createOrder(@RequestBody @Valid Order order) {
        Long id = this.orderService.createOrder(order);
        return new ResponseEntity<>(id, HttpStatus.CREATED);

     * Query current user's order by page
     * @param status Order status
     * @return Paging order data
    @ApiOperation(value = "The current user's order can be queried in pages, and can be filtered according to the order status", 
                  notes = "Query current user's order by page")
        @ApiImplicitParam(name = "page", value = "Current page", 
                          defaultValue = "1", type = "Integer"),
        @ApiImplicitParam(name = "rows", value = "Size per page", 
                          defaultValue = "5", type = "Integer"),
            name = "status", 
            value = "Order status: 1 unpaid, 2 paid but not delivered, 3 delivered but not confirmed, 4 confirmed but not evaluated, 5 transaction closed, 6 transaction successful and evaluated", type = "Integer"),
    public ResponseEntity<PageResult<Order>> queryUserOrderList(
        @RequestParam(value = "page", defaultValue = "1") Integer page,
        @RequestParam(value = "rows", defaultValue = "5") Integer rows,
        @RequestParam(value = "status", required = false) Integer status) {
        PageResult<Order> result = this.orderService.queryUserOrderList(page, rows, status);
        if (result == null) {
            return new ResponseEntity<>(HttpStatus.NOT_FOUND);
        return ResponseEntity.ok(result);

Common notes:

 @Api: Modify the whole class to describe the function of Controller
 @ApiOperation: Describe a method or interface of a class
 @ApiParam: Single parameter description
 @ApiModel: Receive parameters with objects
 @ApiProperty: When receiving parameters with an object, a field describing the object
 @ApiResponse: HTTP Respond to one of the descriptions
 @ApiResponses: HTTP Overall response description
 @ApiIgnore: Use this annotation to ignore this API
 @ApiError : Information returned when an error occurs
 @ApiImplicitParam: A request parameter
 @ApiImplicitParams: Multiple request parameters

4) Start test

Start the service and access: http://localhost:8089/swagger-ui.html

Click order controller to view the interface information:

Click any interface to see the details:

1.3. Test interface

1.3.1 create create an order interface (the order interface must bring a token to indicate login) POST

You can see the interface information through the page:

  • Request method: POST
  • Request path: / order
  • Request parameter: json object containing order, order details and other data.
  • Return result: order number

Click Try It Out to test:

[the external chain picture transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the picture and upload it directly (img-nlBlyZwd-1602687868055)(assets/1528726383029.png)]

input data:

  "totalPay": 236800,
  "postFee": 0,
  "paymentType": 2,
  "actualPay": 236800,
  "buyerMessage": null,
  "buyerNick": "huge",
  "orderDetails": [
      "skuId": 3893493,
      "num": 1,
      "title": "Apples( Apple)iPhone 6 (A1586) 16GB Golden Mobile Unicom Telecom 4 G Mobile 3",
      "price": 236800,
      "ownSpec": "{\"body color \":\"Diamond carving blue\",\"Memory\":\"4GB\",\"Fuselage storage\":\"64GB\"}",
      "image": "http://image.leyou.com/images/9/4/1524297342728.jpg"
  "receiver": "Brother Feng",
  "receiverMobile": "15800000000",
  "receiverState": "Shanghai",
  "receiverCity": "Shanghai",
  "receiverDistrict": "Pudong New Signature",
  "receiverAddress": "Building 3, Chuanzhi podcast, No. 18, hangtou Road, hangtou town",
  "receiverZip": "210000",
  "invoiceType": 0,

Then click execute:

[the external chain picture transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the picture and upload it directly (img-FuIwGgss-1602687868056)(assets/1534050960735.png)]


[the external chain picture transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the picture and upload it directly (img-Z1KOYhbf-1602687868058)(assets/1534056625226.png)]

To place an order, you need to log in and generate a token through login:

[the external chain picture transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the picture and upload it directly (img-8EiByqiy-1602687868060)(assets/1534056963545.png)]

Manually add the value of token to the browser's cookie:

[the external chain picture transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the picture and upload it directly (img-w7XjPnFz-1602687868062)(assets/1534057111628.png)]

[the external chain picture transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the picture and upload it directly (img-nwrda5Ed-1602687868065)(assets/1534057197865.png)]

Added successfully, respond to the order number. However, it is different from the order number saved in the database (the last few digits are different, and the accuracy loss will occur when the browser displays long integer)

1.3.2 if the order table is huge, the distributed database must be used to generate ID (the ID of sub database and sub table is repeated, and the self increasing ID and snowflake algorithm are not used)

Particularity of order id
The order data is very huge, and we will make sub warehouse and sub table in the future. In this case, to ensure the uniqueness of the id, you can't rely on the self increment of the database (because different database IDS will be repeated), but implement the algorithm to generate the unique id.

Snowflake algorithm
The order id here is generated through a tool class:

The id generation algorithm used by the tool class is the snowflake algorithm open source by Twitter.

Snowflake algorithm
Snowflake algorithm will generate a 64 bit binary data, which is a Long type. (up to 19 bits in length after being converted into a string), its basic structure:

First: not used
Part II (milliseconds): 41 bits are milliseconds (the length of 41 bits can be used for 69 years)
The third part (nodes): 5-bit datacenter ID and 5-bit workerid (the length of 10 bits can support the deployment of 1024 nodes at most)
Part IV: the last 12 bits are counts within milliseconds (the 12 bit counting sequence number supports 4096 ID sequence numbers per millisecond for each node)
The IDs generated by snowflake are sorted by time increment as a whole, and there is no ID collision in the whole distributed system (distinguished by datacenter and workerId), and the efficiency is high. After testing, snowflake can generate 260000 IDS per second.
This ensures the uniqueness of the id of the distributed database. Previously, a single database can only ensure its own id, but not between databases.

to configure

To ensure no duplication, we configure the machine id for each deployed node:

    workerId: 1
    datacenterId: 1

Load properties:

@ConfigurationProperties(prefix = "leyou.worker")
public class IdWorkerProperties {

    private long workerId;// Current machine id

    private long datacenterId;// serial number

    public long getWorkerId() {
        return workerId;

    public void setWorkerId(long workerId) {
        this.workerId = workerId;

    public long getDatacenterId() {
        return datacenterId;

    public void setDatacenterId(long datacenterId) {
        this.datacenterId = datacenterId;

Write configuration class:

public class IdWorkerConfig {

    public IdWorker idWorker(IdWorkerProperties prop) {
        return new IdWorker(prop.getWorkerId(), prop.getDatacenterId());


1.3.2 query order interface GET

Interface Description:

  • Request method: GET
  • Request path: / order/{id}
  • Request parameter: id, order number
  • Return result: Order, the json object of the Order



1.3.3 update update order status PUT

Interface Description:

  • Request parameter: PUT
  • Request path: / order/{id}/{status}
  • Request parameters:
    • id: order number; String type; cannot be blank
    • Order status: cannot be empty
  • Return result: null



Changes have also taken place in the database:

1.3.4.query paging query order GET

Interface Description:

  • Request method: Get
  • Request path: / order/list
  • Request parameters:
    • Page: current page, Integer type, defaults to 1
    • rows: size of each page, Integer type, 5 by default
    • Status: order status, String type; all orders are queried by default
  • Return result: the PageResult object contains the following properties:
    • Total: total number
    • items: order array of current page
      • Order object



1.3.5. Generate wechat payment link

Interface Description:

  • Request method: Get
  • Request path: / order/url/{id}
  • Request parameter: id, order number
  • Return result: String type, generated wechat payment link



Wechat payment tool


public class PayHelper {

    private WXPay wxPay;

    private static final Logger logger = LoggerFactory.getLogger(PayHelper.class);

    private StringRedisTemplate redisTemplate;

    private OrderService orderService;

    public PayHelper(PayConfig payConfig) {
        // Real development time
        wxPay = new WXPay(payConfig);
        // When testing
        // wxPay = new WXPay(payConfig, WXPayConstants.SignType.MD5, true);

    public String createPayUrl(Long orderId) {
        String key = "ly.pay.url." + orderId;
        try {
            String url = this.redisTemplate.opsForValue().get(key);
            if (StringUtils.isNotBlank(url)) {
                return url;
        } catch (Exception e) {
            logger.error("Query cache payment link exception,Order No.:{}", orderId, e);

        try {
            Map<String, String> data = new HashMap<>();
            // Product description
            data.put("body", "Leyou mall test");
            // order number
            data.put("out_trade_no", orderId.toString());
            data.put("fee_type", "CNY");
            //Amount in cents
            data.put("total_fee", "1");
            //Call the terminal IP of wechat payment (the IP of estore mall)
            data.put("spbill_create_ip", "");
            //token url 
            data.put("notify_url", "http://test.leyou.com/wxpay/notify");
            // The transaction type is code scanning payment
            data.put("trade_type", "NATIVE");
            //Commodity id, using false data
            data.put("product_id", "1234567");

            Map<String, String> result = this.wxPay.unifiedOrder(data);

            if ("SUCCESS".equals(result.get("return_code"))) {
                String url = result.get("code_url");
                // Cache the payment address for 10 minutes
                try {
                    this.redisTemplate.opsForValue().set(key, url, 10, TimeUnit.MINUTES);
                } catch (Exception e) {
                    logger.error("Cache payment link exception,Order No.:{}", orderId, e);
                return url;
            } else {
                logger.error("Failed to create pre transaction order, error message:{}", result.get("return_msg"));
                return null;
        } catch (Exception e) {
            logger.error("Exception in creating pre transaction order", e);
            return null;

     * Query order status
     * @param orderId
     * @return
    public PayState queryOrder(Long orderId) {
        Map<String, String> data = new HashMap<>();
        // order number
        data.put("out_trade_no", orderId.toString());
        try {
            Map<String, String> result = this.wxPay.orderQuery(data);
            if (result == null) {
                // If no result is found, it is considered as unpaid
                return PayState.NOT_PAY;
            String state = result.get("trade_state");
            if ("SUCCESS".equals(state)) {
                // success, the payment is considered successful

                // Modify order status
                this.orderService.updateStatus(orderId, 2);
                return PayState.SUCCESS;
            } else if (StringUtils.equals("USERPAYING", state)
                       || StringUtils.equals("NOTPAY", state)) {
                // Unpaid or being paid is considered unpaid
                return PayState.NOT_PAY;
            } else {
                // Other statuses are considered payment failed
                return PayState.FAIL;
        } catch (Exception e) {
            logger.error("Abnormal query order status", e);
            return PayState.NOT_PAY;

Other categories related to payment:

1.3.6. Query payment status GET

Interface Description:

  • Request method: Get
  • Request path: / state/{id}
  • Request parameter: id, order number
  • Return result: 0, no payment information found, 1, payment succeeded, 2, payment failed (query failed, or order expired) Unpaid

Query when unpaid, test:


The query returned 0 because the payment has not yet been made. payment

Turn the link into QR code through JS.

Find the JS page provided in the pre class materials:

Enter and enter the address just generated: Paid

Scan the code for payment, and then query again:

If the status code is 1, the payment is successful!

2, Order settlement page

2.1 step 1: jump to the order settlement page

At the bottom of the shopping cart page, there is a button to settle:

When clicking settlement, we should jump to the order settlement page, that is: getorderinfo html

View the settlement button of shopping cart:

As you can see, the address is correct. However, only the login user can settle the payment, so we can't jump directly, but check the login status of the user before jumping. If it is found that the user is not logged in, we should redirect to the login page!

We bind the click event to this button:

Judge the login status in the event and jump the page:

toOrderInfo() {
    // Determine whether to log in
    ly.verifyUser().then(() => {
        // Logged in
        window.location.href = "/getOrderInfo.html"
    }).catch(() => {
        // Not logged in
        window.location.href = "/login.html?returnUrl=" + window.location.href;

Post login test:

The content to be rendered on this page mainly includes three parts:

  • Consignee information
  • Payment method
  • Commodity information

2.2 the second step is the harvest address of the logged in user

The consignee information here must be the receiving address of the current login user. Therefore, we need to query according to the current login user. At present, we write false data on the page:

You can provide an interface for adding, deleting, changing and querying addresses in the background, and then query according to the current login user when the page is loaded, and then assign a value to addresses.

2.3 payment method of logged in users

There are two payment methods:

  • Wechat payment
  • Cash on Delivery

Associated with paymentType in our order data:

Therefore, we can define an attribute in the Vue instance to record the payment method:

Then associate with this variable when the page is rendered:

2.4 list of goods in the shopping cart of the logged in user

design sketch:

The delivery list here is actually the goods to be paid selected by the user in the shopping cart

Therefore, we need to carry the information of the selected shopping cart when the shopping cart jumps over

2.4.1. Shopping cart information acquisition

We modify cart The page Jump logic in HTML transmits the shopping cart information selected by the user:

Then obtain the shopping cart data in the created hook function and save it to the local attribute. Note that we should verify the user login status before obtaining the data. If we find that the user is not logged in, we will directly redirect to the login page:

Then reload the page and view the console:

2.4.2. Page rendering

Page position to be modified: each li is a commodity

We amend it to read:

<ul class="send-detail">
    <li v-for="(cart,index) in carts" :key="index">
        <div class="sendGoods">
            <ul class="yui3-g">
                <li class="yui3-u-1-6">
                    <span><img width="70px" height="70px" :src="cart.image"/></span>
                <li class="yui3-u-7-12">
                    <div class="desc">{{cart.title}}</div>
                    <div class="seven">
                        <span v-for="(v) in JSON.parse(cart.ownSpec)">{{v + "  "}} </span>
                <li class="yui3-u-1-12">
                    <div class="price">¥{{ly.formatPrice(cart.price * cart.num)}}</div>
                <li class="yui3-u-1-12">
                    <div class="num">{{cart.num}}</div>
                <li class="yui3-u-1-12">
                    <div class="exit">in stock</div>

2.5. Total amount

In addition, there is a calculation of the total amount under the commodity list:

It can be seen that there are four main data on the page:

  • Total amount: totalPay
  • Discount cash back: discount 0
  • Freight: postFee 0
  • Paid in amount: actualPay

However, we don't offer any preferential activities. In addition, the freight rate needs to be calculated in combination with the logistics system. For the time being, we set it to 0 and write it in the order attribute:

We calculate the attributes to get the totalPay and actualPay values:

computed: {
        return this.carts.reduce((c1, c2) => c1 + c2.num, 0)
        return this.carts.reduce((c1, c2) => c1 + c2.price * c2.num, 0);
        return this.totalPay + this.order.postFee - this.order.discount;

Then render on the page:


2.6. User order submission page

2.6.1. Page submission

Let's look at the data required for the order interface:

[the external chain picture transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the picture and upload it directly (img-EUcqIlnI-1602687868108)(assets/1534074122199.png)]

It is divided into three parts

  • Basic information of the order itself

    • Total amount
    • Paid in amount
    • Payment type
    • Buyer information is the current user
  • Order details

    • It is the goods in the shopping cart, but the shopping cart data will have an additional userId, which we can remove:

    [the external chain picture transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the picture and upload it directly (img-rYdAu3ej-1602687868108)(assets/1534074293296.png)]

  • Logistics information

    • Logistics address information selected by the current user

Bind event to submit button:

[the external chain picture transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the picture and upload it directly (img-RryXwDA3-1602687868109)(assets/1534074374101.png)]

Then write the method, organize the data and submit:

methods: {
    submit() {
        // Process shopping cart data into order details
        const orderDetails = this.carts.map(({userId, ...rest}) => rest);
        // Processing logistics information
        const addr = this.addresses[this.selectedAddress];
        const obj = {
            receiver: addr.name,
            receiverState: addr.state,
            receiverCity: addr.city,
            receiverAddress: addr.address,
            receiverDistrict: addr.district,
            receiverMobile: addr.phone,
            receiverZip: addr.zipCode
        // Copy to order object
        Object.assign(this.order, obj, {
            totalPay: this.totalPay,
            actualPay: this.actualPay,
        // place order
        ly.http.post("/order", this.order).then(({data}) => {
            // Online payment, need to go to the payment page
            window.location = "pay.html?orderId=" + data;
        }).catch((resp) => {
            alert("Order submission failed. It may be out of stock!")

2.6.2. Accuracy loss problem

Click submit test on the page:

Order generated successfully!

Then look at the page Jump:

What seems to be wrong? The last two digits of the order number are incorrect!

This is actually because JS has limited Long integer precision, and java's Long type data is out of range, so there is a loss of precision.

We return the string of JSON in the background, and JSON will be automatically called inside axios If the parse () method converts the JSON string into JS data, there will be a loss of progress. If you do not convert and still use it as a string, there will be no problem.

Therefore, we rewrite the processing callback function of axios for response:

Test again and it's OK.

Then it's your turn to pay.

3, Wechat payment

3.1. introduce

Official documents of wechat payment: https://pay.weixin.qq.com/index.php/core/home/login?return_url=%2F

We select the development document, and then enter the selection page:

Select code scanning payment:

Here we use mode 2 to develop:

3.2. Development process

Compared with mode 1, mode 2 has a simpler process and does not rely on the set callback payment URL.

The merchant background system first calls the unified ordering interface of wechat payment, and the wechat background system returns the link parameter code_url;

Merchant background system will_ The URL value generates a QR code image, and the user initiates payment after scanning the code with the wechat client.

Note: Code_ The validity period of the URL is 2 hours. After expiration, scanning the code can no longer initiate payment.

flow chart:

Here we summarize what the merchant (we) should do:

  • 1. Merchant generates order
  • 2. The merchant calls the wechat order interface to obtain the link of pre transaction
  • 3. The merchant will link to generate a QR code picture and display it to the user;
  • 4. User pays and confirms
  • 5. Payment result notice:
    • Wechat asynchronously informs the merchant of the payment result, and the merchant informs the wechat payment reception
    • If the merchant does not receive the notification, it can call the interface to query the payment status
  • 6. If the payment is successful, ship the goods and modify the order status

In the previous business, we have completed:

  • 1. Generate order

Next, what we need to do is:

  • 2. Call wechat interface to generate links.
  • 3. And generate QR code pictures

3.3. Generate QR code

3.3.1. Generate pre transaction link

We first call the background service according to the order number to generate the transaction link, and then we can generate the QR code according to the link.

Initiate a request on the page:

var payVm = new Vue({
        orderId:0,// Order number
        // Judge login status
        ly.http.get("/auth/verify").then(() => {
            // Get order number
            this.orderId = ly.getUrlParam("orderId");
            // Get request link
            ly.http.get("/order/url/" + this.orderId)
                .then(resp => {
        }.catch(() => {
			// Not logged in, jump to the login page
             location.href = "/login.html?returnUrl=" + location.href;
    components: {
        shortcut: () => import("./js/pages/shortcut.js")

The interface for generating payment address has been defined in the background.

Refresh page view:

3.3.2. Generate QR code

Here we use a JS plug-in to generate QR Code: qrcode, official website: https://github.com/davidshimjs/qrcodejs

We introduce this js script into the project:

Official use cases:

Then reference on the page:

A div is defined on the page to display the QR Code:

Then, after obtaining the payment link, generate a QR code according to the link:

// Judge login status
ly.http.get("/auth/verify").then(() => {
    // Get order number
    this.orderId = ly.getUrlParam("orderId");
    // Get request link
    ly.http.get("/order/url/" + this.orderId)
        .then(resp => {
            new QRCode(document.getElementById("qrImage"), {
                text: resp.data,
                width: 250,
                height: 250,
                colorDark: "#000000",
                colorLight: "#ffffff",
                correctLevel: QRCode.CorrectLevel.H
}).catch(() => {
    // Not logged in, jump to the login page
    location.href = "/login.html?returnUrl=" + location.href;

Refresh the page to see the effect:

At this time, the customer scans the QR code with his mobile phone and can see the payment page.

3.4. Payment status query

After jumping to the payment page, we wait for the user to pay. When the payment is completed, we jump to the payment success page.

3.4.1. Page cycle query payment status

However, because it is not clear when the user will pay, a circular method is adopted here to constantly request to judge whether the payment is successful.

// Open the scheduled task and query the payment status
const taskId = setInterval(() => {
    ly.http.get("/order/state/" + this.orderId)
        .then(resp => {
        let i = resp.data;
        if (i === 1) {
            // Payment succeeded
            // Jump to payment success page
            location.href = "/paysuccess.html?orderId=" + this.orderId;
        } else if (i === 2) {
            // Payment failed
            // Jump to payment failure page
            location.href = "/payfail.html";
}, 3000);

3.4.2. Payment success page

When the payment is successful, it will automatically jump to the payment success page:

Posted by dcjones on Wed, 11 May 2022 22:55:38 +0300