How to ensure that orders submitted by the same user are not duplicated?

recently, I have been experiencing a very depressing thing. After the system developed by myself is put online, there will always be a problem that one or two payments have not changed the status of the order. And the order with the problem is that several orders are repeatedly generated in the same second and finally the money is not changed to the order status.

problem diagram:

122017092918226

9012

:

Special note: both yourself and the company"s people (more than 100 people) have tested the system at the same time (if the operation is not clear), all are normal. In addition, 25 people were asked to issue orders at the same time (testing without a clear explanation of the operation). More than 300 orders were issued within 10 minutes, and none of them went wrong. However, I do not know why this problem occurs when customers spend in the store, because they do not know how users operate, and then check the traffic monitoring. At that time, it was not the request point at the peak of the meal, and during the time period when the order was generated, a payment was successfully generated in a few minutes. Therefore, it is inferred that it is not the duplicate order generation caused by the concurrency at the same time during the meal peak, and all other tables can be paid successfully, but it is only the repeated order generation of individual tables that leads to the failure of payment, which really depresses me.

I hope God can diagnose this problem for me. Xiaosheng encountered this kind of thing for the first time. Thank you.


submit the order and save it to the system, and then find out the details of the order display. The confirmation button is shown below, and the page to be paid after confirmation is displayed
. This prevents duplicate submission directly from the design point of view. Even if the step of submitting the order is repeated, the confirmed order definitely exists

.

PS: does not recommend changing the button disable state directly, because sometimes it can be clicked for no reason.
it is best to directly display a layer to prevent event bubbling, and the layer covers the entire page. It is safer to use it with disabled status


I don't see the complete functions of front-end payOrder and payMoney, and I don't know if there's a problem.
looking at the description of the landlord, I didn't find anything wrong with the front end.

On the back-end side, the payment has completed the status of the order without modification, which must be a problem with the back-end code.
when you WeChat Pay, you definitely need to send a local order number to Wechat. It seems that there is a field like attach, which can send a local order. After the payment is successful, it will be returned as is.
after returning, you can modify the status of the corresponding order through this order number and the corresponding payment amount. Even if many orders are generated within a short period of time, the order number will be different; there should be no problem to modify the order status.

add:
the problem of repeatedly placing an order can be controlled by the back end before the front-end problem is found. If the item number and quantity contained in each order are the same, and within a short period of time, such as 2 minutes, it can be judged to be a repeat order.


my current platform also has this kind of problem. It is usually repeated when the user clicks, an instant thing, which is difficult to identify. What I use now is the most simple and rude. As soon as it is submitted, the button will be grayed out or redirected without waiting for the server to give feedback. I hope you can share it when you have a good plan.


one button is enough

<button class="payf" type="button" onclick="payOrder()" disabled="false">
    
</button>

add a disabled attribute to the button, and then set a flag bit.
when sending a request, set disabled to true, to disable click payment until the asynchronous result comes back.

there is also a situation where when the asynchronous result returns that the order number already exists, you can jump to a new page and tell the user not to submit any more and where to view the submitted order.


1. The front end forbids repeated clicks. 2. The backend processes the business and finds duplicate orders, which are prohibited from entering the table


this problem should not be solved through a pure front-end approach. Look at the records in the database are basically submitted at the same time, the subject also said that more than N people in the company failed to reproduce a test.

solution: make the request "idempotent". to put it simply, getting a token, that places an order in advance on the payment page can only submit an order once per token, and creating an order with the same token will not succeed .


payment operations should not only be checked at the front end, but also need to be intercepted at the backend


for testing compatibility. If there is a problem with the


callback, find out which sql statements were executed for that order, where the code execution suddenly broke, and Wechat initiated a total of several callbacks for you. For example, turn on
to enable the debug mode of tp. Or mysql writes slow logs, and you can write a method to save the content to be debugged in a file or database, such as the beginning of the callback, save the execution time of the xml, callback, and write these down for troubleshooting!


the final solution is

  1. Front end restrictions
The button at the front end needs to be changed a little bit, and I'm replacing it with another button that doesn't have a click event to prevent it from being clicked again.

2. Backend limit
whether there is a difference of 15 seconds between the previous query, if repeated submission of orders is prohibited within 15 seconds. Effectively prevent multiple orders from being submitted in a short period of time.


frontend: status settings to prevent repeated clicks on
backend: redis setnx

MySQL Query : SELECT * FROM `codeshelper`.`v9_news` WHERE status=99 AND catid='6' ORDER BY rand() LIMIT 5
MySQL Error : Disk full (/tmp/#sql-temptable-64f5-1bb7747-44612.MAI); waiting for someone to free some space... (errno: 28 "No space left on device")
MySQL Errno : 1021
Message : Disk full (/tmp/#sql-temptable-64f5-1bb7747-44612.MAI); waiting for someone to free some space... (errno: 28 "No space left on device")
Need Help?