The beginning of a long sentence, is also a personal record of me, the overall realization of the wechat scan code payment, but there are many details and points to improve, in order to introduce the clear, only the whole process to everyone string, if there are big guys see also want to give more guidance, do not understand can also be private letter to me. If you want to know about wechat login, there are articles related to wechat in the column, you can have a look

To apply wechat payment, you need to have a wechat merchant platform: pay.weixin.qq.com. To open wechat Pay, you need to apply for the authentication fee of 300 for your public account (service number). In wechat payment, there needs to be a public ID and key as well as a merchant ID and key. If you do not have an online application to understand the overall process, of course, it is better to have a friend company and so on.

First will use some of the link address put here, convenient for everyone to view

WeChat payment application process: pay.weixin.qq.com/guide/qrcod…

The commonly used method of payment document: pay.weixin.qq.com/wiki/doc/ap…

Case presentation: pay.weixin.qq.com/guide/webba…

Sweep code payment document: pay.weixin.qq.com/wiki/doc/ap…

WeChat payment sequence diagram: pay.weixin.qq.com/wiki/doc/ap…

Unified order document: pay.weixin.qq.com/wiki/doc/ap…

Signature algorithm specification: pay.weixin.qq.com/wiki/doc/ap…

Signature verification tools: pay.weixin.qq.com/wiki/doc/ap…

NatAPP Intranet penetration: natapp.cn/

You need to explain some of the necessary knowledge before you go to the code, otherwise you will still be confused, complete the function but do not understand the process is wasted

Step1: Introduction to payment by scanning code on wechat website

Stpe1.1: Noun understanding

Appid: unique identifier of the public number

Appsecret: public key

McH_id: merchant number assigned when applying for wechat Pay

Key: key to generate signature during payment transaction. Set path: wechat merchant platform (pay.wexin.qq.com) – > Account Center – > Account Settings – >API Security – > Key Settings

Step1.2: Wechat payment interaction mode

  • POST Submission

  • Protocol in XML format

  • Signature algorithm MD5

  • Interaction service rules first judge the return of protocol fields, then the return of services, and finally the transaction status. Interface transaction units are divided into transaction types: JSAPI– public account payment, NATIVE — NATIVE code scanning payment, and app-app payment

  • Merchant order number rule: merchant payment order number generated by merchants to custom, only support line in use letters, Numbers, -, _ _, the vertical bar |, asterisks * the combination of these English half Angle character, do not use Chinese characters or special characters, such as whole Angle keep uniqueness WeChat payment request merchant order number

Step1.3: Timing diagram explanation (key!)

There is a link to wechat’s official sequence chart at the top. It’s important to understand this diagram, because the code below tells you what step it is in the sequence, so it’s easy to understand the code

The sequence diagram is simply the process of an operation. Which object method will be passed in this process, and what operation will be returned

Sequence diagram: interactive flow diagram (loading elephant into refrigerator in several steps)

Object, Lifeline, Activation, Message

Objects: Objects in the sequence diagram play the role of objects in the interaction, using rectangles to enclose object names with underscores under them

Lifeline: A lifeline is a vertical dotted line that indicates the existence of an object. In a sequence diagram, each object has a lifeline

Active: Represents the period during which an object in the sequence diagram performs an operation, indicating that the object is occupied to complete a task. When the object is active, the lifeline can be widened to a rectangle

Messages: Objects interact by sending messages to each other, with message names indicated above the arrows. One object can request (request) another object to do an event. Messages are sent from the source object to the target object, and control is transferred from the source object to the target object as soon as the message is sent, in a strictly top-down reading order

Solid line in message interaction: request message

Dashed line in message interaction: response returns message

Call your own method: the reflexive message

Let me tell you in my vernacular:

1. Users place an order, enter the purchase page, and click “Buy” to enter the background

2. The background receives the request and generates the order. You must have used Taobao to buy something, right? When you buy something and find that the money is not enough, the order has been stored for payment for a period of time, but the order has been applied in the database, and the status of the order will be changed after you pay

3. The background adjusts wechat to place unified orders. Not only the background generates orders, but wechat also generates order numbers

4. Wechat returns to code_URL Payment transaction link and generates a QR code picture based on this value

5. Return the QR code to the front desk user, who will scan it for payment. Here is direct interaction with wechat

6. Verify the validity of wechat payment system, and return to the user whether to confirm payment after verification

7. Confirm and enter the password. Return authorization to wechat Payment system

8. Verify authorization and complete payment transaction

9. Return payment result, send SMS and wechat message prompt. This is parallel processing, one notifies the user, one notifies the background

10. Asynchronous notification of background payment results, which will carry some parameters, such as order number, etc. Inform the receiving status after receiving the result

11. If the background goes down, wechat will send a notification periodically. The background can do scheduled tasks

Some of the following processes depend on the business

Step2: Place unified orders

Merchant system first invokes this interface to generate pre-payment transaction orders in the background of wechat Payment service, returns the correct pre-payment transaction identification, and then generates transaction strings to initiate payment according to different scenarios such as scan code, JSAPI and APP. Here is step two of the sequence diagram

The links at the top of the document: pay.weixin.qq.com/wiki/doc/ap…

Here is the flow chart of the last unified order

1. Tell wechat Pay you want to place an order

2. The wechat Payment system database generates an order, but it is not paid. Your background also generates an unpaid order

3. After payment, wechat Pay will update the order as paid

4. The wechat background informed us that the payment had been made

5. The background returns a confirmation message

Time sequence diagram and unified ordering process are basically here, must understand, must be clear!

Step2.1: unified single request

To send an HTTP request to the wechat payment system, we need to compose a data message in XML format, which includes some necessary parameters

The official example

Most people are doing wechat payment is wrong here, the signature is not right, or some of the information transmitted does not meet the specifications, etc., here is just to explain to you first, the actual combat will be clear below.

Stpe2.2: Unified order return message

Returns some of the parameters we need, namely code_URL, and an error message if you send the XML incorrectly

Step3: prepare before the war

Step3.1: Database design

The video table can also be considered as some fields in the commodity table are according to the requirements of my project. You can not add some fields that you feel are not needed. CREATE TABLE 'video' (' id' int(11) NOT NULL AUTO_INCREMENT COMMENT '主键 iD ', 'title' varchar(255) DEFAULT NULL COMMENT '主 体 ',' summary 'varchar(255) DEFAULT NULL COMMENT' 主 体 ', 'cover_img' varchar(255) DEFAULT NULL COMMENT '主 体 ',' price 'int(11) DEFAULT NULL COMMENT' 主 体 ', 'c_id' int(10) DEFAULT NULL COMMENT 'iD ',' point 'double(1,2) DEFAULT NULL COMMENT' 'create_time' datetime DEFAULT NULL COMMENT 'create_time ',' view_num 'int(10) DEFAULT NULL COMMENT' view_num ', 'online' int(11) DEFAULT '1' COMMENT '0 InnoDB (' id ') ENGINE=InnoDB AUTO_INCREMENT= utF8;Copy the code
CREATE TABLE 'video_order' (' id 'int(11) NOT NULL CREATE TABLE' video_order '(' id' int(11) NOT NULL AUTO_INCREMENT COMMENT 'id',' out_trade_no 'varchar(64) DEFAULT NULL COMMENT' ID ', 'state' int(11) DEFAULT NULL COMMENT 'create_time' DEFAULT NULL COMMENT 'create_time' DEFAULT NULL COMMENT 'create_time' DEFAULT NULL COMMENT 'create_time ', 'total_fee' int(11) DEFAULT NULL COMMENT 'order amount ',' video_id 'int(11) DEFAULT NULL COMMENT' video_id ', 'video_title' varchar(128) DEFAULT NULL COMMENT '主 义 的 ',' video_img 'varchar(255) DEFAULT NULL COMMENT' 主 义 的 ', 'user_id' int(11) DEFAULT NULL COMMENT 'iD ',' IP 'varchar(64) DEFAULT NULL COMMENT' IP ', 'openID' varchar(64) DEFAULT NULL COMMENT 'user id ',' notify_time 'DEFAULT NULL COMMENT' pay callback time ', 'nickname' varchar(32) DEFAULT NULL COMMENT '微信 ', 'head_img' varchar(128) DEFAULT NULL COMMENT '微信 ', 'del' int(11) DEFAULT '0' COMMENT '0' PRIMARY KEY (' id '); UNIQUE KEY `out_trade_no` (`out_trade_no`) ) ENGINE=InnoDB AUTO_INCREMENT=35 DEFAULT CHARSET=utf8;Copy the code

Step3.2: Entity class

Add your own get set method

/** * public class Video implements Serializable {private Integer ID; /** * private String title; /** * description */ private String summary; / / @jsonProperty ("cover_img") private String cover_img; /** ** private Integer price; /** * @jsonProperty ("c_id") private Integer cId; /** * private Double point; @jsonformat (pattern = "YYYY-MM-DD hh: MM :ss", locale = "zh", timezone = "GMT+8") @JsonProperty("create_time") private Date createTime; /** * @jsonProperty ("view_num") private Integer viewNum; /** * 0 indicates no online, and 1 indicates online */ private Integer online; @JsonProperty("chapter_list") private List<Chapter> chapterList; }Copy the code
/** * Public Class VideoOrder implements Serializable {private Integer ID; /** * @jsonProperty ("out_trade_no") private String outTradeNo; /** * Order status */ private Integer state; @jsonFormat (pattern = "YYYY-MM-DD hh: MM :ss", locale = "zh", timezone = "GMT+8") private Date createTime; @jsonProperty ("total_fee") private Integer total_fee; /** * videoId */ @jsonproperty ("video_id") private Integer videoId; /** * @jsonProperty ("video_title") private String videoTitle; @jsonProperty ("video_img") private String videoImg; /** * userId */ @jsonproperty ("user_id") private Integer userId; /** * private String IP address; @JsonProperty("open_id") private String openId; @jsonProperty ("notify_time") @jsonFormat (pattern = "YYYY-MM-DD hh: MM :ss", locale = "zh", timezone = "GMT+8") private Date notifyTime; /** * redundant field: @jsonProperty ("nick_name") private String nickName; @jsonProperty ("head_img") private String headImg; /** * 0 indicates not deleted, 1 indicates deleted */ private Integer del; }Copy the code
Public class VideoOrderDto extends VideoOrder {} public class VideoOrderDto extends VideoOrder {}Copy the code

Step3.3: configure files

****** means using your own wechat configuration

Server port 8089 # = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = database related configuration = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = spring.datasource.driver-class-name =com.mysql.cj.jdbc.Driver spring.datasource.url=jdbc:mysql://localhost:3306/educationapp? useUnicode=true&characterEncoding=utf-8&useSSL=false spring.datasource.username=root spring.datasource.password=root # Use Alibaba Druid data source Default built-in (com) zaxxer, hikari. HikariDataSource) if you use the default here. You don't have to write the spring datasource. Type = com. Alibaba. The druid. Pool. DruidDataSource # = = = = = = = = = = = = = = = = = = = = = = = = = = = = = # open console print SQL MyBatis related configuration Mybatis. Configuration. The log - impl = org. Apache. Ibatis. Logging. Stdout. # StdOutImpl mybatis underline hump configuration, both can # mybatis. Configuration. MapUnderscoreToCamelCase = true. Mybatis configuration. The map - the underscore - to - camel case = true # mapper configuration scanning Mybatis. Mapper-locations =classpath:mapper/*. XML Mybatis. Type - aliases - package = net. Jhclass. Online_jhclass. Model. # pojo = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = # # WeChat related public Wxpay. Appid = * * * * * * * * * wxpay appsecret = * * * * * * * * * * * # # WeChat merchant platform merchants wxpay mer_id = * * * * * * * * # key wxpay. Key = * * * * * * * * * * * * * * # callback address wxpay.callback=http://sj6e6c.natappfree.cc/api/v2/wechat/order/callbackCopy the code

Step3.4: wechat configuration class

Add your own get and set methods

/ * * * WeChat Configuration class * / @ Configuration @ PropertySource (value = "classpath: application. The properties") public class WeChatConfig { /** * appId */ @value ("${wxpay.appId}") private String AppId; /** * @value ("${wxpay.appSecret}") private String AppSecret; /** * @value ("${wxpay.mer_id}") private String McHId; @value ("${wxpay.key}") private String key; /** * @value ("${wxpay.callback}") private String payCallbackUrl; / * * * WeChat unified order url address * / private final static String UNIFIED_ORDER_URL = "https://api.mch.weixin.qq.com/pay/unifiedorder";Copy the code

Step3.5: Encapsulate HTTP, GET, and POST methods

Related,

<! --HttpClient--> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> < version > 4.5.3 < / version > < / dependency > < the dependency > < groupId >. Org. Apache httpcomponents < / groupId > <artifactId>httpmime</artifactId> <version>4.5.2</version> </dependency> <dependency> <groupId> Commons -codec</groupId> <artifactId>commons-codec</artifactId> </dependency> <dependency> <groupId>commons-logging</groupId> < artifactId > Commons - logging < / artifactId > < version > 1.1.1 < / version > < / dependency > < the dependency > <groupId>org.apache.httpcomponents</groupId> <artifactId>httpcore</artifactId> </dependency>Copy the code
/** * public class HttpUtils {private static final Gson Gson = new Gson(); Public static Map<String,Object> doGet(String url){Map<String,Object> Map = new HashMap<>(); CloseableHttpClient httpClient = HttpClients.createDefault(); Custom ().setConnectTimeout(5000)// Connection timeout . SetConnectionRequestTimeout (5000). / / request connection timeout setSocketTimeout (5000). SetRedirectsEnabled (true) / / allowed to redirect the build (); HttpGet httpGet = new HttpGet(url); httpGet.setConfig(requestConfig); try { HttpResponse httpResponse = httpClient.execute(httpGet); if(httpResponse.getStatusLine().getStatusCode() == 200){ String jsonResult = EntityUtils.toString(httpResponse.getEntity()); // Convert key value form map = gson.fromjson (jsonResult,map.getClass()); } }catch (Exception e){ e.printStackTrace(); }finally {// Close the request try {httpClient.close(); }catch (Exception e){ e.printStackTrace(); } } return map; } public static String doPost(String url,String data,int timeout){ CloseableHttpClient httpClient = HttpClients.createDefault(); RequestConfig RequestConfig = requestconfig.custom ().setConnectTimeout(timeout)// Connection timeout . SetConnectionRequestTimeout (timeout). / / request connection timeout setSocketTimeout (timeout). SetRedirectsEnabled (true) / / allowed to redirect the build (); HttpPost httpPost = new HttpPost(url); httpPost.setConfig(requestConfig); // addHeader information httppost. addHeader(" content-type ","text/ HTML; chartset=UTF-8"); if(data ! StringEntity StringEntity = new StringEntity(data," utF-8 "); httpPost.setEntity(stringEntity); } try { CloseableHttpResponse httpResponse = httpClient.execute(httpPost); HttpEntity httpEntity = httpResponse.getEntity(); if(httpResponse.getStatusLine().getStatusCode() == 200){ String result = EntityUtils.toString(httpEntity); return result; } }catch (Exception e){ e.printStackTrace(); }finally { try { httpClient.close(); }catch (Exception e){ e.printStackTrace(); } } return null; }}Copy the code

Step3.6: Transfer wechat Payment tool class XML to Map MAO and XML to generate signatures

Wechat official also has Java related tool classes, basically give you the same, here I will directly give you the code to use

Public class WXPayUtil {/** * convert XML format string to Map ** @param strXML XML string * @return Map after XML data conversion * @throws Exception */ public static Map<String, String> xmlToMap(String strXML) throws Exception { try { Map<String, String> data = new HashMap<String, String>(); DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); InputStream stream = new ByteArrayInputStream(strXML.getBytes("UTF-8")); org.w3c.dom.Document doc = documentBuilder.parse(stream); doc.getDocumentElement().normalize(); NodeList nodeList = doc.getDocumentElement().getChildNodes(); for (int idx = 0; idx < nodeList.getLength(); ++idx) { Node node = nodeList.item(idx); if (node.getNodeType() == Node.ELEMENT_NODE) { org.w3c.dom.Element element = (org.w3c.dom.Element) node; data.put(element.getNodeName(), element.getTextContent()); } } try { stream.close(); } catch (Exception ex) { // do nothing } return data; } catch (Exception ex) { throw ex; }} /** * Converts a Map to an XML String ** @param data Map data * @return XML String * @throws Exception */ public static String mapToXml(Map<String, String> data) throws Exception { DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); DocumentBuilder documentBuilder= documentBuilderFactory.newDocumentBuilder(); org.w3c.dom.Document document = documentBuilder.newDocument(); org.w3c.dom.Element root = document.createElement("xml"); document.appendChild(root); for (String key: data.keySet()) { String value = data.get(key); if (value == null) { value = ""; } value = value.trim(); org.w3c.dom.Element filed = document.createElement(key); filed.appendChild(document.createTextNode(value)); root.appendChild(filed); } TransformerFactory tf = TransformerFactory.newInstance(); Transformer transformer = tf.newTransformer(); DOMSource source = new DOMSource(document); transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8"); transformer.setOutputProperty(OutputKeys.INDENT, "yes"); StringWriter writer = new StringWriter(); StreamResult result = new StreamResult(writer); transformer.transform(source, result); String output = writer.getBuffer().toString(); //.replaceAll("\n|\r", ""); try { writer.close(); } catch (Exception ex) { } return output; } sign * @return */ public static String createSign(SortedMap<String, String> params, String key){ StringBuilder sb = new StringBuilder(); Set<Map.Entry<String, String>> es = params.entrySet(); Iterator<Map.Entry<String,String>> it = es.iterator(); / / generated stringA = "appid = wxd930ea5d5a258f4f & body = test&device _info = 1000 & 10000100 & nonce_str McH_id = = ibuaiVcKdpRxkhJA"; while (it.hasNext()){ Map.Entry<String,String> entry = (Map.Entry<String,String>)it.next(); String k = (String)entry.getKey(); String v = (String)entry.getValue(); if(null ! = v && !" ".equals(v) && !" sign".equals(k) && !" key".equals(k)){ sb.append(k+"="+v+"&"); } } sb.append("key=").append(key); String sign = CommonUtils.MD5(sb.toString()).toUpperCase(); return sign; } public static Boolean isCorrectSign(SortedMap<String, String> params, String key){ String sign = createSign(params,key); String weixinPaySign = params.get("sign").toUpperCase(); return weixinPaySign.equals(sign); } /** * public static SortedMap<String,String> getSortedMap(Map<String,String> map){ SortedMap<String,String> sortedMap = new TreeMap<>(); Iterator<String> it = map.keySet().iterator(); while (it.hasNext()){ String key = (String)it.next(); String value = map.get(key); // Define the temporary variable String temp=""; if(null! =value){ temp = value.trim(); } sortedMap.put(key,temp); } return sortedMap; }}Copy the code

Step3.7: return to the tool class

* @param <T> * Created by hanlu on 2017/5/7. */ public class Dto<T>{private String success; // Check whether the system is wrong and return the corresponding true or false. // The error code is self-defined. Generally, 0 indicates no error. Private T data; // Return data content (POJO, custom VO, others) private int count; Public int getCount() {return count; } public void setCount(int count) { this.count = count; } public T getData() { return data; } public void setData(T data) { this.data = data; } public String getSuccess() { return success; } public void setSuccess(String success) { this.success = success; } public String getErrorCode() { return errorCode; } public void setErrorCode(String errorCode) { this.errorCode = errorCode; } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; }}Copy the code
/** * Created by XX on 17-5-8. */ public class DtoUtil {public static String success="true"; public static String fail="false"; public static String errorCode="0"; /*** * public static DTO returnSuccess(){DTO DTO =new DTO (); dto.setSuccess(success); return dto; } /*** * public static DTO returnSuccess(String message,Object data){DTO DTO =new DTO (); dto.setSuccess(success); dto.setMsg(message); dto.setErrorCode(errorCode); dto.setData(data); return dto; } /*** * public static DTO returnSuccess(String message){DTO DTO =new DTO (); dto.setSuccess(success); dto.setMsg(message); dto.setErrorCode(errorCode); return dto; } public static DTO returnDataSuccess(Object data){DTO DTO =new DTO ();} public static DTO returnDataSuccess(Object data){DTO DTO =new DTO (); dto.setSuccess(success); dto.setErrorCode(errorCode); dto.setData(data); return dto; } /** * Request failed, Return error statement and errorCode * @param message * @param errorCode * @return */ public static Dto returnFail(String message,String) errorCode){ Dto dto=new Dto(); dto.setSuccess(fail); dto.setMsg(message); dto.setErrorCode(errorCode); return dto; } @param data * @param count * @return */ public static Dto returnPage(Object data,int count){Dto dto=new Dto(); dto.setSuccess(success); dto.setErrorCode(errorCode); dto.setData(data); dto.setCount(count); return dto; }}Copy the code

Step4: generate an order

Some other operations like query user information and query video information will not give you on, is a simple query, here is mainly to do the order, to avoid you do not understand the following code of some methods

Step4.1: the Service layer

Public interface VideoOrderService {/** * order operation you can ask: shouldn't int return why String? If you write int service, you write String, String * @return */ String saveVideoOrder(VideoOrderDto VideoOrderDto) throws Exception; /** * query user order List * @param userId userId * @return */ List<VideoOrder> listOrderByUserId(Integer userId); @param outTradeNo * @return */ VideoOrder findByOutTradeNo(String outTradeNo); / * * * according to the serial number to update the order status * @ param videoOrder * @ return * / int updateVideoOrderByOutTradeNo (videoOrder videoOrder); }Copy the code

Step4.2: Mapper file

<? The XML version = "1.0" encoding = "utf-8"? > <! DOCTYPE mapper PUBLIC "- / / mybatis.org//DTD mapper / 3.0 / EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" > < mapper namespace="net.jhclass.online_jhclass.mapper.VideoOrderMapper"> <! - check the user order status - > < select id = "findByUserIdAndVideoIdAndState resultType" = "VideoOrder" > select * from ` video_order ` where user_id=#{user_id} and video_id=#{video_id} and state=#{state} </select> <! <insert id="saveVideoOrder" useGeneratedKeys="true" keyColumn="id" keyProperty="id" parameterType="VideoOrder"> insert into `video_order` (out_trade_no,state,create_time,total_fee,video_id,video_title,video_img,user_id,ip,openid,notify_time,nickname,head_img ,del) values (#{outTradeNo},#{state},#{createTime},#{totalFee},#{videoId},#{videoTitle},#{videoImg},#{userId},#{ip},#{openId},#{notif yTime},#{nickName},#{headImg},#{del}) </insert> <! ResultType ="VideoOrder"> select * from 'video_order' where user_id=#{user_id}  </select> <! <select id="findById" resultType="VideoOrder"> select * from 'video_order' where id=#{order_id} and del=0 </select> <! <select id="findByOutTradeNo" resultType="VideoOrder"> select * from 'video_order' where select * from 'video_order out_trade_no=#{out_trade_no} and del=0 </select> <! <update id="del"> update video_order set del=0 where id=#{id} and user_id=#{userId} </update> <! Update order status - WeChat callback According to update order serial number - > < the update id = "updateVideoOrderByOutTradeNo parameterType" = "VideoOrder" > update video_order set  state=#{state},notify_time=#{notifyTime},openid=#{openId} where out_trade_no=#{outTradeNo} and state=0 and del=0 </update> </mapper>Copy the code

Step5: control layer

With all the preparation done, it’s time to focus!

The control layer will be updated later

@restController @requestMapping (" API /v1/pri/order") public class VideoOrderController {@autoWired private VideoOrderService videoOrderService; @param videoId videoId * @param request user information * @return * @throws Exception */ @getmapping ("saveOrder") public void saveOrder(@RequestParam(value = "video_id",required = true) int videoId, HttpServletRequest request, HttpServletResponse Response) throws Exception {// Record the user's ordered IP address. //String IP = iputils.getipaddr (request); iputils.getipaddr (request); String IP = "120.25.1.43"; UserId = (Integer)request.getAttribute("user_id"); userId = (Integer) request.getattribute ("user_id"); VideoOrderDto videoOrderDto = new VideoOrderDto(); videoOrderDto.setVideoId(videoId); videoOrderDto.setUserId(userId); videoOrderDto.setIp(ip); videoOrderService.saveVideoOrder(videoOrderDto); Return dtoutil. returnSuccess(" order successful "); }}Copy the code

Step6: Implement ServiceImpl

The operation here is to ensure the success of the order, the database can generate data

All the comments need to be updated

To complete this step, you can start and adjust the interface to see if the data can be added successfully

@Service public class VideoOrderServiceImpl implements VideoOrderService { @Autowired private VideoOrderMapper videoOrderMapper; @Autowired private UserMapper userMapper; @Autowired private VideoMapper videoMapper; @Autowired private WeChatConfig weChatConfig; /** * Future version discount volume function, wechat payment, risk control user check, generate order basic information, generate payment information * @return */ @override @Transactional(Propagation = Propagation.REQUIRED)// Default isolation level Public String saveVideoOrder(VideoOrderDto VideoOrderDto) throws Exception {int videoId =  videoOrderDto.getVideoId(); int userId = videoOrderDto.getUserId(); String ip = videoOrderDto.getIp(); / / determine whether already purchase order status code 1 VideoOrder VideoOrder = videoOrderMapper. FindByUserIdAndVideoIdAndState (userId, videoId, 1); if(videoOrder! =null){return null if the order has been paid; } // Query Video information. Video Video = videomapper.findByid (videoId); User User = usermapper.findByUserId (userId); VideoOrder newvideoOrder = new VideoOrder(); newvideoOrder.setCreateTime(new Date()); / / order creation time newvideoOrder. SetOutTradeNo (CommonUtils. GenerateUUID ()); . / / the only serial number newvideoOrder setTotalFee (video. GetPrice ()); / / price newvideoOrder setState (0); // Payment status newVideoOrder.setUserId (userId); // User ID newVideoOrder.setVideoid (video.getid ()); // Video id newVideoOrder.setheadimg (user.getheadimg ()); / / WeChat avatar newvideoOrder. SetNickName (user. The getUsername ()); . / / WeChat nickname newvideoOrder setVideoImg (video) getCoverImg ()); . / / redundancy field newvideoOrder setVideoTitle (video) getTitle ()); // Redundant field newVideoOrder.setdel (0); newvideoOrder.setIp(ip); / / save order int num = videoOrderMapper. SaveVideoOrder (newvideoOrder); String codeUrl = unifiedOrder(newvideoOrder); // get the code_URL // generate the TWO-DIMENSIONAL code return codeUrl; }}Copy the code

Step6.1: Signature development

Write another unified order method under orderServiceImpl to generate the signature

@return */ private String unifiedOrder(VideoOrder VideoOrder) throws Exception {// Generate a signature SortedMap<String,String> params = new TreeMap<>(); params.put("appid",weChatConfig.getAppId()); // public id AppId params.put(" McH_id ", wechatConfig.getmchid ()); // Merchant ID params.put("nonce_str", commonUtils.generateuuid ()); params.put("body",videoOrder.getVideoTitle()); / / commodity description params. Put (" out_trade_no ", videoOrder getOutTradeNo ()); Params.put ("total_fee", videoOrder.getTotalfee ().tostring ()); Params. Put ("spbill_create_ip", videoOrder.getip ()); / / terminal IP params. Put (" notify_url ", weChatConfig getPayCallbackUrl ()); // Notify the address params.put("trade_type","NATIVE"); / / transaction type Sweep yards pay / / sign signature call tool String sign = WXPayUtil. CreateSign (params, weChatConfig getKey ()); params.put("sign",sign); Map > XML String payXml = wxpayUtil.maptoxml (params); System.out.println(payXml); System.out.println(sign); / / unified order / / send a post request String orderStr = HttpUtils. DoPost (WeChatConfig. GetUnifiedOrderUrl (), payXml, 4000); if(orderStr == null){ return null; Map<String,String> unifiedOrderMap = wxPayUtil.xmlTomap (orderStr); if(unifiedOrderMap ! = null){ return unifiedOrderMap.get("code_url"); } return null; }Copy the code

Debug at the break point

This is important because if you don’t generate your signature correctly, you can’t do this

After you get the payXml value, copy it to verify the wechat Pay document signature. If you can pass it, congratulations, the important first step is completed. Link at the top!

Step6.2: Send the request

Send a request to wechat after the signature is verified. This is all step two of the sequence diagram

OrderStr is the information returned to us by wechat. If “SUCCESS” is displayed, the order is successfully placed

Step6.3: take code_url

Mainly to illustrate, in the third step of the sequence diagram, wechat returns such a value after generating the order, which contains code_URL, which is the link to generate the TWO-DIMENSIONAL code. We need this value to generate the two-dimensional code

Step7: Update the control layer to generate two-dimensional code

Step7.1: Add Google QR code dependency

		<dependency>
			<groupId>com.google.zxing</groupId>
			<artifactId>javase</artifactId>
			<version>3.3.0</version>
		</dependency>

		<dependency>
			<groupId>com.google.zxing</groupId>
			<artifactId>core</artifactId>
			<version>2.0</version>
		</dependency>
Copy the code

Step7.2: Update the control layer

The code_URL value should be returned by the service

@GetMapping("saveOrder") public void saveOrder(@RequestParam(value = "video_id",required = true) int videoId, HttpServletRequest Request, HttpServletResponse Response) throws Exception {// Record the user order IP address // If using Reques is not strict, it is easy to get the IP address. // Get IP //String IP = iputils.getipaddr (request); String IP = "120.25.1.43"; // obtain userId Integer userId = (Integer)request.getAttribute("user_id"); // obtain userId Integer userId = (Integer)request.getAttribute("user_id"); VideoOrderDto videoOrderDto = new VideoOrderDto(); videoOrderDto.setVideoId(videoId); videoOrderDto.setUserId(userId); videoOrderDto.setIp(ip); / / unified order with payment transaction link codeUrl String codeUrl = videoOrderService. SaveVideoOrder (videoOrderDto); if(codeUrl == null){ throw new NullPointerException(); <EncodeHintType,Object> Hints = new HashMap<>(); ERROR_CORRECTION (encodeHintType. ERROR_CORRECTION, errorcorrectionlevel.l); Put (EncodeHintType.CHARACTER_SET,"UTF-8"); BitMatrix = new MultiFormatWriter().encode(codeUrl, barcodeformat.qr_code,400,400,hints); OutputStream out = response.getOutputStream(); MatrixToImageWriter.writeToStream(bitMatrix,"png",out); }catch (Exception e){ e.printStackTrace(); }}Copy the code

Step7.3: Generate two-dimensional code

Restart the project, use postman test, the old version does not display two-dimensional code, generated is garbled, need to go to the browser to access, the new version can display two-dimensional code

Appear below two-dimensional code you can open mobile phone scan code!

After the steps we all understand, is the user and wechat interaction, confirm the payment input password and so on. Go straight to step 8 of the sequence diagram

Step8: The Intranet receives messages through penetration

After wechat completes the pre-payment information, it sends a message to the user and also sends a message to our background, telling us that the payment is successful. After we get the information, we modify the order status and then we are done. But the problem is that we are local development, how to receive the information sent?

Use the tool NatApp, there is a link at the top, it is very simple to use, use free tunnel, but every time you start it is random tunnel, so you need to change the configuration file every time

The previous domain name: rrgdbr.natappfree.cc represents local projects like mine

RRGDBR. Natappfree. Cc/API/v1 / pri…

Note that the configuration file also needs to be modified, which may be confusing. When did I tell the wechat payment system this value? It was recorded when we generated the signature and sent the unified order to wechat for the first time

Step9: Receive wechat confirmation messages

The path must be right ah, don’t you write the callback address and your control layer to receive the message path is inconsistent, otherwise you can not receive the message, the first time I cerebral palsy, path write wrong, interrupt point tried for a long time did not enter the control layer, waste several cents…

The following code is simple to say a few words, there are annotations, the process is to verify the signature after receiving the request, there is no error information, and then update the order status, then tell wechat, I am OK here! Will do. If you don’t tell wechat, it will keep sending you messages until you tell it.

@Controller @RequestMapping("api/v2/wechat") public class WechatController { @Autowired private WeChatConfig weChatConfig; @Autowired private VideoOrderService videoOrderService; @requestMapping ("/order/callback") public void orderCallback(HttpServletResponse Response) HttpServletRequest Request) throws Exception{// Get the stream information. InputStream InputStream = request.getinputStream (); BufferedReader in = new BufferedReader(new InputStreamReader(inputStream,"UTF-8")); StringBuffer sb = new StringBuffer(); String line; while ((line = in.readLine())! =null){ sb.append(line); } in.close(); inputStream.close(); Map<String,String> callbackMap = WXPayUtil.xmlToMap(sb.toString()); //map to sortedMap sortedMap <String,String> sortedMap = wxpayUtil.getSortedMap (callbackMap); / / whether the signature right if (WXPayUtil isCorrectSign (sortedMap, weChatConfig getKey ())) {System. Out. Println (" OK "); If ("SUCCESS".equals(sortedmap. get("result_code"))){String outTradeNo = sortedmap. get("out_trade_no"); / / use the queue performance VideoOrder dbVideoOrder = videoOrderService. FindByOutTradeNo (outTradeNo); // Update order status if(dbVideoOrder! =null &&dbVideoOrder.getState ()==0){// Determine the logic to look at the business scene VideoOrder VideoOrder = new VideoOrder(); videoOrder.setOpenId(sortedMap.get("openid")); videoOrder.setOutTradeNo(outTradeNo); videoOrder.setNotifyTime(new Date()); videoOrder.setState(1); int rows = videoOrderService.updateVideoOrderByOutTradeNo(videoOrder); System.out.println(" affected rows: "+rows); If (rows==1){response.setContentType("text/ XML "); response.getWriter().println("success"); return; }}}} // both processes fail response.setContentType("text/ XML "); response.getWriter().println("fail"); }}Copy the code

Preface: The whole wechat payment is like this, but there are details, verification of orders, or problems with a transaction have not been done. Some friends will ask. How did THE front desk show the user that the payment was OK after I paid successfully? You didn’t tell the front desk here. In fact, this operation is the front desk to send requests, circular to the background to send requests, query the user’s order status, become 1 does not send requests, and then to the user to show the payment OK. If you need a small partner source can be private letter, thank you!

Feel good big guy point a thumbs up bai ~ hand knock screenshot demo is not easy