Declaration: Reprint please attach the original link

🐕, a junior, is preparing for the internship interview next year. Recently, I have been sorting out JDK source code, design pattern and NOTES of JVM tutorials, which is really boring. So share an interesting article about SpringBoot integrating payments with scan login!

Extension: Springboot operation Ali cloud OSS file server upload and download files case Springboot integration alipay payment (sandbox test)

0. Prepare

Before using wechat Pay, the default partner has the following skills:

  • Familiar with SpringBoot (SSM) + Mybatis(plus)/JPA + HttpClient + mysql5.x
  • Understand JWT permission verification
  • Having read documents related to wechat payment and wechat login, I can simply understand the sequence diagram
  • Have wechat open platform developer qualification account, have wechat pay (if you don’t have it, you can borrow it from someone around you)

1. Log in by scanning code on wechat

1.1 Introduction to one-click Login of wechat Authorization

Introduction: The advantages and disadvantages of login methods and the one-click login function of wechat authorization are introduced

# 1. Register with your mobile phone number or email
Advantages: 1) The enterprise obtains the basic information of users, which is conducive to the subsequent business development and pushes marketing information. 2) Users can use a mobile phone number or email address to obtain the corresponding app welfare registration and send coupons. 3) It is convenient for feedback information
Disadvantages: 1) step more 2) if the site is not secure, such as the site is attacked, leaked personal information, such as mobile phone number, password, etc. 3) a small number of bad enterprises selling personal information, such as mobile phone number
# 2. OAuth2.0 One-click authorization login
Example: Douban: www.douban.com Advantages: quick to use, good user experience, relatively safe data disadvantages: Problem 1, feedback trouble, difficult to know that the only logo 2, if there are multiple application is under the enterprise, including the application does not support Auth2.0 login, can't do the user information through, integral can't reuse and so on Such as app WeChat access authorization login, but the site doesn't, then I couldn't get through, or only authorized party provides a terminal authorization, The message can't get through.
# 3. Selection method:
2) Make sure to distinguish between common passwords and core passwordsCopy the code

1.2 Preparation for the development of wechat Scan function

Introduction: Development process and document preparation related to wechat Scan function

# 1 introduction to wechat Open Platform (enterprise information is required for website application)
WeChat open platform website: https://open.weixin.qq.com/
# 2. What is appID, AppSecret, authorization code
Appid and AppSecret are an ID and secret key code assigned by the resource owner to the applicant. A->B initiates authorization, and in order to obtain the information of the authorized user, A must carry the authorization code to obtain the authorization information from B (you must bring your ID card if you want to take things out from me).
# 3. First carefully read the wechat login development guide given by the official wechat open platform:
		https://developers.weixin.qq.com/doc/oplatform/Website_App/WeChat_Login/Wechat_Login.html
Copy the code

Wechat Open Platform registration and login:

Since enterprise certification is required to create website applications and 300 yuan is required to pay Tencent for wechat verification, the cost is too high for individual developers, so they can only use others’ or their own money to apply.

For testing convenience, here is a database user table:

# Dump of table user
# ------------------------------------------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `openid` varchar(128) DEFAULT NULL COMMENT 'WeChat openid',
  `name` varchar(128) DEFAULT NULL COMMENT 'nickname',
  `head_img` varchar(524) DEFAULT NULL COMMENT 'avatar',
  `phone` varchar(64) DEFAULT ' ' COMMENT 'Mobile phone Number',
  `sign` varchar(524) DEFAULT 'Full Stack Engineer' COMMENT 'User signature',
  `sex` tinyint(2) DEFAULT '1' COMMENT '0 for female, 1 for male ',
  `city` varchar(64) DEFAULT NULL COMMENT 'city',
  `create_time` datetime DEFAULT NULL COMMENT 'Creation time'.PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Copy the code

1.3 Wechat Oauth2.0 interaction process

Introduction: Wechat Oauth2.0 interaction process

Reference article: open.weixin.qq.com/cgi-bin/sho…

The official document: developers.weixin.qq.com/doc/oplatfo…

The preparatory work

Website application wechat login is a wechat OAuth2.0 authorized login system based on OAuth2.0 protocol standard. Before wechat OAuth2.0 authorized login and access, register a developer account on wechat open platform, and have an approved website application, and obtain the corresponding AppID and AppSecret. After applying for wechat login and passing the approval, the access process can start.

Authorization Process description

Wechat OAuth2.0 Authorized Login Allows wechat users to securely log in to third-party applications or websites with wechat identity. After a wechat user authorizes to log in to a third-party application that has access to wechat OAuth2.0, the third party can obtain the user’s interface call certificate (Access_token). Access_token can be used to invoke the authorization relationship interface of wechat open platform, so as to obtain basic open information of wechat users and help users to realize basic open functions. Wechat OAuth2.0 authorization login currently supports authorization_code mode, which is suitable for application authorization with server side. The overall process of this mode is as follows:

  • After a third party initiates a wechat authorized login request and a wechat user authorizes a third-party application, wechat will pull up the application or redirect it to the third-party website, and bring the code parameter of the authorized temporary note.
  • Access_token is exchanged via API with code parameter plus AppID, AppSecret, etc.
  • The access_token interface is invoked to obtain basic data resources or help users to perform basic operations.
# 1. Differentiate role users, third application, wechat open platform

# 2. If you want to see the sequence diagram knowledge, please jump to the wechat Payment chapter for the sequence diagram knowledge explanation

# 3, scan the url instance:
https://open.weixin.qq.com/connect/qrconnect?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect
The parameters of this link are detailed in the official documentationCopy the code

Here is the sequence diagram for obtaining the access_token. Be sure to read it!

To further understand the process, draw a flow chart and compare it with the official sequence diagram:

Step 1: Request CODE

Third party use of the site application authorization login please pay attention to have access to the corresponding page before the authorization scope (scope = snsapi_login), will be in the PC to open the following links: open.weixin.qq.com/connect/qrc… If the link cannot be accessed is displayed, check whether the domain name of redirect_uri is different from the authorized domain name specified during audit or the scope is not snSAPi_login.

Parameters that

parameter Whether must instructions
appid is Application unique identifier
redirect_uri is Please use urlEncode to process the link
**response_type ** is Fill in the code
scope is Apply the authorization scope. Separate multiple scopes with commas (,). For web applications, only snSAPi_login is used
state no It is used to preserve the status of requests and callbacks, and to authorize requests to be returned to third parties as is. This parameter is used to prevent CSRF attacks (cross-site request forgery attacks). You are advised to set this parameter to a simple random number plus session for verification

Return instructions

After the user grants authorization, it is redirected to the redirect_URI url with code and state parameters

redirect_uri? code=CODE&state=STATECopy the code

If the user disables authorization, the redirection does not carry the code parameter but only the state parameter

redirect_uri? state=STATECopy the code

Step 2: Obtain the access_token with code

Obtain the access_token by code

https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code
Copy the code

Parameters that

parameter Whether must instructions
appid is The unique identifier of the application will be obtained after the application is approved on wechat open platform
secret is The application key AppSecret is obtained after the application is submitted and approved on wechat open platform
code is Fill in the code parameter obtained in the first step
grant_type is Fill authorization_code

Return instructions

Correct return:

{ 
    "access_token":"ACCESS_TOKEN"."expires_in":7200."refresh_token":"REFRESH_TOKEN"."openid":"OPENID"."scope":"SCOPE"."unionid": "o6_bmasdasdsad6_2sgVt7hMZOPfL"
}
Copy the code

Parameters that

parameter instructions
access_token Interface call credentials
expires_in Timeout time for access_token interface to call credentials, in seconds
refresh_token The access_token was refreshed. Procedure
openid Unique identifier of an authorized user
scope Scope of user authorization, separated by commas (,)
unionid This field appears if and only if the site application has been authorized by the user’s UserInfo.

An example error message is returned:

{"errcode":40029."errmsg":"invalid code"}
Copy the code

Obtaining user personal information (UnionID mechanism)

Interface specification

This interface is used to obtain user personal information. Developers can access basic user information through OpenID. In particular, it should be noted that if the developer has multiple mobile applications, website applications and public accounts, the uniqueness of the user can be distinguished by obtaining the unionID in the user’s basic information, because as long as it is the same wechat open platform account under the mobile application, website applications and public accounts, the user’s unionID is unique. In other words, the same user, different applications under the same wechat open platform, unionID is the same. Please note that the old wechat profile picture URL will become invalid after users modify the wechat profile picture. Therefore, the developer should save the profile picture after obtaining the user’s information to avoid the abnormal situation after the wechat profile picture URL becomes invalid.

Request instructions

The HTTP request method: GET https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENIDCopy the code

Parameters that

parameter Whether must instructions
access_token is Call the credentials
openid is Common user id, unique to the current developer account
lang no Country/region language version: zh_CN simplified, zh_TW traditional, en English, default zh-CN

Return instructions

The correct Json result is returned:

{
    "openid":"OPENID"."nickname":"NICKNAME"."sex":1."province":"PROVINCE"."city":"CITY"."country":"COUNTRY"."headimgurl": "https://thirdwx.qlogo.cn/mmopen/g3MonUZtNHkdmzicIlibx6iaFqAc56vxLSUfpb6n5WKSYVY0ChQKkiaJSgQ1dZuTOgvLLrhJbERQQ4eMsv84eav HiaiceqxibJxCfHe/0"."privilege": ["PRIVILEGE1"."PRIVILEGE2"]."unionid": " o6_bmasdasdsad6_2sgVt7hMZOPfL"
}
Copy the code
parameter instructions
openid Common user id, unique to the current developer account
nickname Common User nickname
sex Common user gender: 1 is male, 2 is female
province The province in which the personal information of ordinary users is filled in
city The city where the personal information of ordinary users is filled in
country Countries, such as China, are CN
headimgurl The last value represents the size of square head (0, 46, 64, 96, 132 are optional, 0 represents 640 x 640 square head). This parameter is null if the user does not have an image
privilege User privilege information, JSON array, such as wechat User for (Chinaunicom)
unionid Unified user id. For an application under a wechat open platform account, the unionID of the same user is unique.

Advice:

It is best for developers to save the user’s unionID information so that user information can be exchanged between different applications later.

An example of an incorrect Json return:

{
	"errcode":40003."errmsg":"invalid openid"
}
Copy the code

Step 3: Invoke the interface via access_token

After obtaining the access_token, the interface invocation must meet the following requirements:

1.Access_token is valid and has not timed out.2.The wechat user has authorized the interface scope of the third-party application account.Copy the code

For an interface scope, the following interfaces can be called:

Authorization scope interface Interface specification
snsapi_base /sns/oauth2/access_token Exchange access_token, refresh_token, and authorized scope with code
snsapi_base /sns/oauth2/refresh_token Refresh or renew the access_token usage
snsapi_base /sns/auth Check the validity of access_Token
snsapi_userinfo /sns/userinfo Obtain user’s personal information

The snsapi_base interface belongs to the basic interface. If the application has the permission of other scopes, the application has the permission of snsapi_base by default. By using SNSAPi_base, mobile web page authorization can bypass the action of redirecting authorization request from user login page and directly redirect to third-party web pages with authorization temporary ticket (Code), but the scope of user authorization is only SNSAPi_base. As a result, data and basic functions that require user authorization cannot be obtained. For the method of interface invocation, please refer to “Wechat Authorization Relationship Interface Invocation Guide”.

1.4 wechat Authorization One-click login to obtain the authorization URL

Brief introduction: Obtain the url address of wechat open platform scan code link

# add result tool class, JsonData; Added the application.properties configuration
# wechat open platform configuration
Wxopen. Appid = wxopen. Appsecret = # url redirection wxopen. Redirect_url = http://test/pub/api/v1/wechat/user/callback1Copy the code

application.properties

# wechat related configuration:
# # the public
wxpay.appid=wx5beXXXXX7cdd40c
wxpay.appsecret=55480123XXXXXXXXb382fe548215e9
# wechat open platform configuration
wxopen.appid=wx025XXXXX9a2d5b
wxopen.appsecret=f5b6730c59XXXXXXX5aeb8948a9f3
Redirect the URL to the home page and get the token according to the code, so as to obtain the login information of the wechat scan user
# This domain name is authenticated by others. It can only be used for reference
wxopen.redirect_url=http://XXXX.cn/XXXX/wechat/user/callback
Copy the code

JsonData.java

package com.haust.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import lombok.experimental.Accessors;

import java.io.Serializable;

/ * * *@Auther: csp1999
 * @Date: 2020/08/27 / nothing *@Description: JSON result wrapper class */
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
@Accessors(chain = true)
public class JsonData implements Serializable {

    private static final long serialVersionUID = 1L;

    private Integer code; // Status code 0 indicates success, 1 indicates processing, and -1 indicates failure
    private Object data; / / data
    private String msg;/ / description

    // Success, passed data
    public static JsonData buildSuccess(a) {
        return new JsonData(0.null.null);
    }

    // Success, passed data
    public static JsonData buildSuccess(Object data) {
        return new JsonData(0, data, null);
    }

    // The description is passed in
    public static JsonData buildError(String msg) {
        return new JsonData(-1.null, msg);
    }

    // Failed, passed in the description, status code
    public static JsonData buildError(String msg, Integer code) {
        return new JsonData(code, null, msg);
    }

    // Success, incoming data, and description
    public static JsonData buildSuccess(Object data, String msg) {
        return new JsonData(0, data, msg);
    }

    // Success, incoming data, and status code
    public static JsonData buildSuccess(Object data, int code) {
        return new JsonData(code, data, null); }}Copy the code

Added attributes to wechatConfig. Java:

/* * @auther: cSP1999 * @date: 2020/08/26/10:27 * @description: wechat related configuration */
@Configuration
/* * @propertysource annotation specifies the configuration file location: (Property name specification: large module. Submodule. Attribute name) */
@PropertySource(value = "classpath:application.properties")// Read the configuration from application.properties under the classpath

@Data // Lombok has a built-in set/get method
@Accessors(chain = true) // chain call
public class WeChatConfig {

    // Wechat open platform qr code connection
    // Parameter to be filled: appID =%s redirect_URI =%s state=%s
    private final static String OPEN_QRCODE_URL = "https://open.weixin.qq.com/connect/qrconnect?appid=%s&redirect_uri=%s&response_type=code&scope=snsapi_login&state=%s#we chat_redirect";

    // wechat open platform obtains access_token address
    // Parameter to be filled: appId =%s secret=%s code=%s
    private final static String OPEN_ACCESS_TOKEN_URL="https://api.weixin.qq.com/sns/oauth2/access_token?appid=%s&secret=%s&code=%s&grant_type=authorization_code";

    // Get user information
    Access_token =%s openID =%s
    private final static String OPEN_USER_INFO_URL ="https://api.weixin.qq.com/sns/userinfo?access_token=%s&openid=%s&lang=zh_CN";

    @Value("${wxpay.appid}")
    private String appid;/ / WeChat appid

    @Value("${wxpay.appsecret}")
    private String appsecret;// wechat key

    @Value("${wxopen.appid}")
    private String openAppid;// Open platform appID

    @Value("${wxopen.appsecret}")
    private String openAppsecret;// Open platform key

    @Value("${wxopen.redirect_url}")
    private String openRedirectUrl;// Open platform callback address

    public static String getOpenUserInfoUrl(a) {
        return OPEN_USER_INFO_URL;
    }

    public static String getOpenAccessTokenUrl(a) {
        return OPEN_ACCESS_TOKEN_URL;
    }

    public static String getOpenQrcodeUrl(a) {
        returnOPEN_QRCODE_URL; }}Copy the code

Testing:

/ * * *@Auther: csp1999
 * @DateBetter: 2020/08/27 / *@Description: wechat related Controller */
@Controller
@RequestMapping("/wechat")
public class WeChatController {

    @Autowired
    private WeChatConfig weChatConfig;

    Url * @ Parameter set: [accessPage] * @ Return type: com.haust. Pojo. JsonData * @ Author name: CSP1999 * @ Date & time: 2020/8/27 * / art
    @ResponseBody
    @GetMapping("/login_url")
    @CrossOrigin
    public JsonData weChatloginUrl(
        @RequestParam(value = "state", required = true) String state) throws UnsupportedEncodingException {
        /** * state: * Is used to preserve the status of the request and callback, and to authorize the request to be returned to the third party as is. This parameter can be used to prevent * check CSRF attacks, cross-site request forgery attacks), suggested that a third party with the parameters, can be set up as a simple machine number plus the session with * for calibration, such as: state = 3 d6be0a4035d839573b04816624a415e * /
        // Get the open platform redirect address
        String redirectUrl = weChatConfig.getOpenRedirectUrl();
        // the wechat open platform document stipulates that the url to be called back should be encoded using urlEncode to encode the link
        String callbackUrl = URLEncoder.encode(redirectUrl, "GBK");
        // Fill the qrcodeUrl parameter appID =%s redirect_URI =%s state=%s to OPEN_QRCODE_URL
        String qrcodeUrl = String.format(weChatConfig.getOpenQrcodeUrl(), 
                                         weChatConfig.getOpenAppid(), callbackUrl, state);
        // Build json object returns
        returnJsonData.buildSuccess(qrcodeUrl); }}Copy the code

Go to http://localhost:8081/xdclass/wechat/login? access_page=abcdef

[Img-Iediacle-1605166581351] (D: Note notes springboot Notes Project practice Springboot small D classroom online education practice (including wechat payment)] \ wechat Login and Payment Note. assets image-20200827165451131.png)

Data: open.weixin.qq.com/connect/qrc…

The link address in data is the address of the scanned page:

[imG-AMETU1FF-1605166581352] (D: Note notes springboot Notes Project practice Springboot small D classroom online education practice (including wechat payment)] \ wechat Login and Payment Note. assets image-20200827165639498.png)

Sweep the code after login will jump to: 16 webtest. Ngrok. Xiaomiqiu. Cn domain name address is configured in the configuration file

[imG-CTCICOPQ-1605166581354] [IMG-CTCICOPQ-1605166581354] [IMG-CTCICOPQ-1605166581354] [IMG-CTCICOPQ-1605166581354] [IMG-CTCICOPQ-1605166581354] \ wechat Login and Payment Note. assets image-20200827165757448.png)

Compared with wechat pay, wechat scanning code login is relatively simple. Because it is the domain name of others, so nothing is displayed, the blogger himself is also a student, individual developers can not apply for wechat open platform website application qualification, only in wechat open platform authorized callback domain name can scan code jump!

So far, we ask wechat for the code and we are done! The next thing we need to do is exchange access_token with wechat through code and the existing appID + Appsecret!

1.5 Obtaining and using the httpClient4. x tool

Overview: Explains httpClient4.x dependencies and encapsulates basic methods.

1. Add dependencies

<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
    <version>4.5.3</version>
</dependency>
<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>
<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpcore</artifactId>
</dependency>
<! -- Gson tool for encapsulating HTTP -->
<dependency>
    <groupId>com.google.code.gson</groupId>
    <artifactId>gson</artifactId>
    <version>2.8.0</version>
</dependency>
Copy the code

2. Encapsulate doGet and doPost

/ * * *@Auther: csp1999
 * @Date: 2020/08/27/18:01
 * @Description: a utility class that encapsulates HTTP get/ POST methods */
public class HTTPUtils {

    private static final Gson gson = new Gson();

    / * * * @ method description: encapsulation get * @ parameters collection: [url] * @ return type: Java. Util. Map < Java. Lang. String, Java. Lang. Object > * @ the author name: csp1999 * @ date/time: 2020/8/27 18:04 * /
    public static Map<String, Object> doGet(String url) {

        Map<String, Object> map = new HashMap<>();
        CloseableHttpClient httpClient = HttpClients.createDefault();

        RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(5000) // Connection timed out
                .setConnectionRequestTimeout(5000)// The request timed out
                .setSocketTimeout(5000)
                .setRedirectsEnabled(true)  // Allow automatic redirection
                .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()); map = gson.fromJson(jsonResult, map.getClass()); }}catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                httpClient.close();
            } catch(Exception e) { e.printStackTrace(); }}return map;
    }

    /** * @Encapsulate post * @ Parameter set: [URL, data, timeout] * @ Return type: java.lang.String * @ Author name: CSP1999 * @ Date time: 2020/8/27 18:04 */
    public static String doPost(String url, String data, int timeout) {
        CloseableHttpClient httpClient = HttpClients.createDefault();
        // Set timeout

        RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(timeout) // Connection timed out
                .setConnectionRequestTimeout(timeout)// The request timed out
                .setSocketTimeout(timeout)
                .setRedirectsEnabled(true)  // Allow automatic redirection
                .build();


        HttpPost httpPost = new HttpPost(url);
        httpPost.setConfig(requestConfig);
        httpPost.addHeader("Content-Type"."text/html; chartset=UTF-8");

        if(data ! =null && data instanceof String) { // Use a string to pass parameters
            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);
                returnresult; }}catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                httpClient.close();
            } catch(Exception e) { e.printStackTrace(); }}return null; }}Copy the code

1.6 wechat Scan Login Callback Local Domain name mapping tool Ngrock

Introduction: wechat scan code callback local domain name ngrock explanation

# 1. Why use this? Wechat scan code needs to configure callback and corresponding domain name
In the local computer development, wechat cannot call back, so we need to configure an address mapping, that is, wechat server can access the address of the current development computer through this address
# 2. Use documentation:
https://natapp.cn/article/natapp_newbie # 3, download address: https://natapp.cn/Copy the code

Enter natApp official website to register and log in to buy its free random domain name tunnel. After it is configured with its own host through the official document, it can access its own project through the tunnel domain name + project path (saving the time of domain name registration, but the free tunnel speed is very slow), the effect is shown in the following figure:

1.7 Authorized login to obtain personal information of wechat users

Introduction: Describes the interface for obtaining user personal information by using the authorization code

# Key point: look at the wechat document and try to copy the fields

# 1: Access with code_token document: https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1419316505&token=7e1296c8174816ac988643825ae16f25d8c7e781&lang=zh_CN

# 2. Access_token as micro credit my account and nicknames such as basic information document: https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1419316518&token=7e1296c8174816ac988643825ae16f25d8c7e781&lang=zh_CN
Copy the code

Matters needing attention:

Since individuals cannot apply for wechat open platform website application, they cannot get the authorized domain name and cannot jump to their own project page. Therefore, they can only jump by borrowing the authorized domain name of others. After the jump succeeds, they can replace the domain name with their own domain name or host IP address.

As shown in figure:

After scanning the code, the wechat user calls to this page, and then only needs to change its domain name to its own domain name or localhost:

So you can call the backend of your own project.

Now we continue to develop the wechat scan callback interface and save the wechat scan user information to the database:

1.8 User module development: Save wechat user information

Brief introduction: Develop User data access layer to save wechat User information

UserMapper.java

/ * * *@Auther: csp1999
 * @Date: 2020/08/28 / o *@Description: User Mapper
 */
@Repository
public interface UserMapper {

    // Save the basic information of wechat login user
    Integer saveUser(@Param("user") User user);

    // Query according to openID
    User findByUserOpenid(String openid);

    // Query by primary key ID
    User findByUserId(Integer id);

    // Update wechat user information
    void updateUser(@Param("user") User user);
}
Copy the code

UserMapper.xml

<? 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="com.haust.mapper.UserMapper">
    <insert id="saveUser" parameterType="com.haust.entity.User" useGeneratedKeys="true" keyProperty="id"
            keyColumn="id">
        INSERT INTO `xdclass`.`user`(`openid`, `name`, `head_img`, `phone`, `sign`, `sex`, `city`, `create_time`)
        VALUES (#{user.openid}, #{user.name}, #{user.headImg}, #{user.phone}, #{user.sign}, #{user.sex}, #{user.city}, #{user.createTime});
    </insert>

    <select id="findByUserOpenid" parameterType="string" resultType="com.haust.entity.User">
        SELECT * FROM `xdclass`.`user` WHERE `openid` = #{openid}
    </select>

    <select id="findByUserId" parameterType="integer" resultType="com.haust.entity.User">
        SELECT * FROM `xdclass`.`user` WHERE `id` = #{id}
    </select>

    <update id="updateUser" parameterType="com.haust.entity.User">
        UPDATE `xdclass`.`user` SET
        `name` = #{user.name},
        `head_img` = #{user.headImg},
        `phone` =  #{user.phone},
        `sign` = #{user.sign},
        `sex` = #{user.sex},
        `city` = #{user.city}
        WHERE `openid` = #{user.openid};
    </update>
</mapper>
Copy the code

UserServiceImpl.java

/ * * *@Auther: csp1999
 * @Date: 2020/08/27 / *@Description: User Service implementation class */
@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private WeChatConfig weChatConfig;

    @Autowired
    private UserMapper userMapper;

    /** * Obtain access_token * from wechat via code and appId appSecret and obtain user basic information (nickname, address, avatar, etc.) by access_token to save data to database *@param code
     * @return* /
    @Override
    public User saveWeChatUser(String code) {
        // Get access_tokenURL with code
        String accessTokenUrl = String.format(
                WeChatConfig.getOpenAccessTokenUrl(),
                weChatConfig.getOpenAppid(),
                weChatConfig.getOpenAppsecret(),
                code);

        // Send a request to wechat open platform via access_tokenURL to obtain an access_token
        Map<String, Object> baseMap = HTTPUtils.doGet(accessTokenUrl);

        if (baseMap == null || baseMap.isEmpty()) {
            return null;
        }

        / / get the accessToken
        String accessToken = (String) baseMap.get("access_token");
        String openId = (String) baseMap.get("openid");

        // Get the URL of the request sent to wechat open platform to get the basic information of the user through accessToken
        String userInfoUrl = String.format(WeChatConfig.getOpenUserInfoUrl(), accessToken, openId);

        / / get access_token
        Map<String, Object> baseUserMap = HTTPUtils.doGet(userInfoUrl);

        if (baseUserMap == null || baseUserMap.isEmpty()) {
            return null;
        }
        // Get the user's basic information
        String nickname = (String) baseUserMap.get("nickname");// Wechat account name
        System.out.println(baseUserMap.get("sex"));
        Double sexTemp = (Double) baseUserMap.get("sex");// Gender of wechat user
        System.out.println(sexTemp);
        int sex = sexTemp.intValue();// Double => Integer
        String province = (String) baseUserMap.get("province");// Wechat user's province
        String city = (String) baseUserMap.get("city");// Wechat user's city
        String country = (String) baseUserMap.get("country");// Wechat user's country
        String headimgurl = (String) baseUserMap.get("headimgurl");// wechat account like

        StringBuilder builder = new 
            StringBuilder(country).append("| |").append(province).append("| |").append(city);
        String finalAddress = builder.toString();

        try {
            // Resolve Chinese garbled characters
            nickname = new String(nickname.getBytes("ISO-8859-1"), "UTF-8");
            finalAddress = new String(finalAddress.getBytes("ISO-8859-1"), "UTF-8");

        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        User user = new User();
        user.setName(nickname).setHeadImg(headimgurl).setCity(finalAddress)
            .setOpenid(openId).setSex(sex).setCreateTime(new Date());

        User findUser = userMapper.findByUserOpenid(openId);
        if(findUser ! =null) { // If the wechat user information is already in the database, update the latest basic information of the wechat user and return it directly
            userMapper.updateUser(user);
            return user;
        }// If not, proceed

        userMapper.saveUser(user);// Save the user information
        returnuser; }}Copy the code

weChatController.java

/** * @ Method description: Obtain token encapsulating user information by scanning the parameter code carried on the skipping page * @ Parameter set: [code, state, response] * @ Return type: Com.haust. Pojo. JsonData * @ Author name: CSP1999 * @ Date time: 2020/8/27 19:17 */
 @GetMapping("/user/callback")
 public String weChatUserCallback(@RequestParam(value = "code", required = true)// Can be used to save the current page address RedirectAttributes RedirectAttributes){
     User user = userService.saveWeChatUser(code);
     System.out.println("user:"+user);
     redirectAttributes.addFlashAttribute("user",user);
     String token = null;
     if(user ! =null) {// JWT generates tokens
         token = JWTUtils.createJsonWebToken(user);
         redirectAttributes.addFlashAttribute("token",token);
         redirectAttributes.addFlashAttribute("state",token);
         return "redirect:/test/test03? token="+token;// Concatenate tokens to urls for interceptor filtering
     }else{
         return "redirect:/error/error"; }}Copy the code

testController.java

@GetMapping("/test03")
public String test03(Model model, @ModelAttribute("user") User user,
                     @RequestParam("token")String token,// Get the token in the URL@ModelAttribute("state") String state) {/ / test videoMapper
    if (token==null) {return "/error/error";
    }
    System.out.println("= = = = = = = = = = = = = >"+token);
    System.out.println("= = = = = = = = = = = = = >"+state);
    model.addAttribute("user", user);
    model.addAttribute("token", token);
    return "test";
}
Copy the code

The result obtained on the Test page is as shown in the figure below:

The database saves the user information as shown in the following figure (the name field is different because I changed it later) :

Matters needing attention:

Since I didn’t introduce HttpServletResponse, forwarding and redirection are done using the ThymLeaf template engine, which is relatively simple to configure, just follow the code.

1.9 SpringBoot2.x User login interceptor development

LoginInterceptor Is the interceptor for user login

Create the interceptor class loginInterceptor.java

/ * * *@Auther: csp1999
 * @Date: 2020/08/28/17:54
 * @Description: Login interceptor */
public class LoginIntercepter implements HandlerInterceptor {

    [request, response, handler] * @ Return type: Boolean * @ Author name: CSP1999 * @ Date time: 2020/8/28 17:57 * /
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // Get the token from the URL argument
        String token = request.getParameter("token");

        // If the token exists, decrypt it:
        if(token ! =null&& token ! ="") {// If there is no token in the header
            Claims claims = JWTUtils.paraseJsonWebToken(token);

            if(claims ! =null) {
                String openid = (String) claims.get("openid");
                String name = (String) claims.get("name");
                String imgUrl = (String) claims.get("img");

                request.setAttribute("openid", openid);
                request.setAttribute("name", name);
                request.setAttribute("imgUrl", imgUrl);

                return true;/ / release
            }
        }
        response.sendRedirect("/xdclass/user/login");
        return false;/ / intercept
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {}@Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {}}Copy the code

Configure the interceptor interceptorconfig.java

/ * * *@Auther: csp1999
 * @Date: 2020/08/28 / *@DescriptionInterceptor configuration */
@Configuration
public class IntercepterConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // Register interceptors
        registry.addInterceptor(new LoginIntercepter())
                .addPathPatterns("/video/**")
                .addPathPatterns("/user/**")
                .excludePathPatterns("/test/**")
                .excludePathPatterns("/user/login")
                .excludePathPatterns("/wechat/**"); }}Copy the code

Test indexController.java

If the token cannot resolve the wechat user information or the token does not exist, the user is redirected to the login page

/ * * *@Auther: csp1999
 * @Date: 2020/08/28 / it *@Description: home Controller */
@Controller
@RequestMapping("/user")
public class IndexController {

    @GetMapping("/index")
    public String test03(Model model,
                         @RequestParam("token")String token,// Get the token in the URL@ModelAttribute("state") String state) {/ / test videoMapper
        if (token==null) {return "/error/error";
        }
        System.out.println("= = = = = = = = = = = = = >"+token);
        model.addAttribute("token", token);
        return "test";
    }

    @GetMapping("/login")
    public String login(a){
        return "login"; }}Copy the code

Test results: visit j47im5. Natappfree. Cc/xdclass/use… If the token is empty, the login page is redirected

Test scan login complete!

2. Wechat scan code payment

Note: Both wechat Pay and Alipay need the merchant number, key, and callback domain name. If you are a student, you are advised to borrow it from someone else’s senior who works for you, or use the sandbox test of Alipay (you can find out by yourself).

2.1 Introduction to scanning code payment on wechat website

Brief introduction: Wechat web scan code payment brief introduction

# 1, sweep the yard payment document: https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=2Appid: public number unique identifier APPSecret: public number secret key MCH_Id: merchant number assigned when applying for wechat Pay
Key: The payment transaction generates the secret key for the signature, Setting path wechat merchant platform (pay.weixin.qq.com)--> Account Center --> Account Setting -->API Security --> Key setting # 3, interaction mode with wechat Payment 1, post submission 2, XML format protocol 3, signature algorithm MD5 4, interactive business rules 5. The interface transaction unit is divided. 6. Transaction type: JSAPI-- public account payment, NATIVE-- NATIVE code scanning payment, APP-- APP paymentCopy the code
# 7 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
# 8. Safety Specifications:
Signature algorithm: https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=4_3 WeChat payment request parameters calibration tools: https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=20_1
# 9. Adopt the scanning mode ii of wechat Pay (not dependent on merchant platform to set callback URL)
Copy the code

2.2 Introduction to sequence diagram knowledge

Introduction: What is a sequence diagram? Why look at sequence diagrams?

# wechat Payment sequence chart
	https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=6_5

# 1. What is a sequence diagram
Is a UML interaction diagram, which describes the chronological order of message transfer between objects. It is used to represent the sequence of behavior in use cases. It is an interaction diagram that emphasizes the chronological order of message.
Common explanation: it is the flow chart of interaction (put the elephant into the refrigerator in several steps)Copy the code
# 2. The sequence diagram includes four elements: Object, Lifeline, Activation, and 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
News: the interaction between objects are implemented by sending messages to each other, the arrow mark message name above, an object can request (request) to another object to do something event messages from the source object point to the target object, once message is sent to transfer control from the source object to a target object, the reading order of interest is strictly from top to bottom
Solid line in message interaction: request message Dotted line in message interaction: response return message
Call your own method: the reflexive message
# reference: https://www.cnblogs.com/langtianya/p/3825764.html
Copy the code

2.3 Wechat webpage scanning code payment sequence diagram explanation and unified ordering interface

Introduction: Explain wechat web page scanning code payment sequence diagram and unified ordering interface

Timing diagram # 1, address: https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=6Introduction to unified ordering interface: merchant system first invokes this interface to generate pre-payment transaction order in the background of wechat Payment service, and then returns the correct pre-payment transaction session id, and then generates transaction string to initiate payment according to different scenarios such as sweep code, JSAPI and APP.Copy the code

Sequence diagram of wechat Payment (carefully analyze each process to facilitate the understanding of subsequent codes) :

Wechat Pay business process description:

(1) The merchant background system generates orders according to the commodities purchased by users.

(2) After confirming the payment, users call wechat Pay [Unified Order API] to generate pre-payment transactions;

(3) After receiving the request, the wechat payment system generates the pre-payment transaction order and returns the CODE_URL of the TWO-DIMENSIONAL code link of the transaction session.

(4) The merchant background system generates the QR code according to the returned Code_URL.

(5) The user opens the wechat “Scan” to scan the QR code, and the wechat client sends the scan content to the wechat payment system.

(6) The wechat payment system receives the client’s request, verifies the validity of the link and initiates user payment, requiring user authorization.

(7) The user enters the password on the wechat client, and after confirming the payment, the wechat client submits authorization.

(8) Wechat Payment system completes payment transactions according to user authorization.

(9) The wechat payment system returns the transaction result to the wechat client after completing the payment transaction, and prompts the user with the transaction result through SMS or wechat message. The wechat client displays the payment transaction result page.

(10) Wechat Payment system notifies merchant background system of payment results by sending asynchronous messages. The merchant background system shall reply to the receipt and inform the wechat background system not to send the payment notice of the order any more.

(11) If no payment notice is received, the merchant background system calls [Order Query API].

(12) The merchant confirms that the order has been paid and delivers the goods to the user.

2.4 wechat Pay order interface (add, delete, modify and check order)

Introduction: Add, delete, change and check the order of the unified order interface development of wechat scan code payment

Unified order WeChat official documentation: pay.weixin.qq.com/wiki/doc/ap…

Unified order official wechat sequence diagram:

Provide a database order table

# Dump of table video_order
# ------------------------------------------------------------

DROP TABLE IF EXISTS `video_order`;

CREATE TABLE `video_order` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `openid` varchar(32) DEFAULT NULL COMMENT 'User identification',
  `out_trade_no` varchar(64) DEFAULT NULL COMMENT 'Unique Order Identifier',
  `state` int(11) DEFAULT NULL COMMENT '0 means not paid, 1 means paid ',
  `create_time` datetime DEFAULT NULL COMMENT 'Order Generation time',
  `notify_time` datetime DEFAULT NULL COMMENT 'Payback time',
  `total_fee` int(11) DEFAULT NULL COMMENT 'Amount paid, unit of minutes',
  `nickname` varchar(32) DEFAULT NULL COMMENT 'wechat nickname',
  `head_img` varchar(128) DEFAULT NULL COMMENT 'wechat Profile picture',
  `video_id` int(11) DEFAULT NULL COMMENT 'Video primary key',
  `video_title` varchar(128) DEFAULT NULL COMMENT 'Video Name',
  `video_img` varchar(256) DEFAULT NULL COMMENT 'Video picture',
  `user_id` int(11) DEFAULT NULL COMMENT 'user id',
  `ip` varchar(64) DEFAULT NULL COMMENT 'User IP address',
  `del` int(5) DEFAULT '0' COMMENT '0 means not deleted, 1 means deleted '.PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

LOCK TABLES `video_order` WRITE;
/ *! 40000 ALTER TABLE `video_order` DISABLE KEYS */;
Copy the code

VideoOrderMapper.java

/ * * *@Auther: csp1999
 * @Date: * 2020/08/29 / will@Description: order Mapper */
@Repository
public interface VideoOrderMapper {

    // New order
    int insertVideoOrder(VideoOrder videoOrder);

    // Find order information based on id
    VideoOrder findVideoOrderById(int id);

    // Search by order unique identifier
    VideoOrder findVideoOrderByOutTradeNo(String outTradeNo);

    // Delete by id
    int deleteVideoOrderByIdAndUserId(@Param("id") int id, @Param("userId") int userId);

    // Find all user orders according to userID
    List<VideoOrder> findUserVideoOrderList(int userId);

    // Update according to order serial number
    int updateVideoOrderByOutTradeNo(VideoOrder videoOrder);
}
Copy the code

VideoOrderMapper.xml


      
<! DOCTYPEmapper PUBLIC "- / / mybatis.org//DTD Mapper / 3.0 / EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.haust.mapper.VideoOrderMapper">
    <resultMap id="userOrderList" type="com.haust.entity.VideoOrder">
        <id column="id" property="id"/>
        <result column="openid" property="openid"/>
        <result column="out_trade_no" property="outTradeNo"/>
        <result column="state" property="state"/>
        <result column="create_time" property="createTime"/>
        <result column="notify_time" property="notifyTime"/>
        <result column="total_fee" property="totalFee"/>
        <result column="nickname" property="nickname"/>
        <result column="head_img" property="headImg"/>
        <result column="video_id" property="videoId"/>
        <result column="video_title" property="videoTitle"/>
        <result column="video_img" property="videoImg"/>
        <result column="user_id" property="userId"/>
        <result column="ip" property="ip"/>
        <result column="del" property="del"/>
    </resultMap>

    <insert id="insertVideoOrder" parameterType="com.haust.entity.VideoOrder" useGeneratedKeys="true" keyColumn="id" keyProperty="id">
        INSERT INTO `xdclass`.`video_order`(
        `openid`,
        `out_trade_no`,
        `state`,
        `create_time`,
        `notify_time`,
        `total_fee`,
        `nickname`,
        `head_img`,
        `video_id`,
        `video_title`,
        `video_img`,
        `user_id`,
        `ip`,
        `del`)
        VALUES (
        #{videoOrder.openid},
        #{videoOrder.outTradeNo},
        #{videoOrder.state},
        #{videoOrder.createTime},
        #{videoOrder.notifyTime},
        #{videoOrder.totalFee},
        #{videoOrder.nickname},
        #{videoOrder.headImg},
        #{videoOrder.videoId},
        #{videoOrder.videoTitle},
        #{videoOrder.videoImg},
        #{videoOrder.userId},
        #{videoOrder.ip},
        #{videoOrder.del});
    </insert>

    <select id="findVideoOrderById" parameterType="integer" resultType="com.haust.entity.VideoOrder">
        SELECT * FROM `xdclass`.`video_order` WHERE id = #{id} AND del=0
    </select>

    <select id="findVideoOrderByOutTradeNo" parameterType="string" resultType="com.haust.entity.VideoOrder">
        SELECT * FROM `xdclass`.`video_order` WHERE out_trade_no = #{outTradeNo} AND del=0
    </select>

    <update id="deleteVideoOrderByIdAndUserId" useGeneratedKeys="true" keyProperty="id" keyColumn="id">
        UPDATE `xdclass`.`video_order` SET del = 1 where id = #{id} and user_id = #{userId}
    </update>

    <select id="findUserVideoOrderList" parameterType="integer" resultMap="userOrderList">
        SELECT * FROM `xdclass`.`video_order` WHERE user_id = #{userId}
    </select>

    <update id="updateVideoOrderByOutTradeNo" parameterType="com.haust.entity.VideoOrder" useGeneratedKeys="true" keyProperty="id" keyColumn="id">
        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

The test test:

@SpringBootTest
class VideoOrderMapperTest {

    @Autowired
    private VideoOrderMapper videoOrderMapper;
    
    @Test
	void insertVideoOrder(a) {
    VideoOrder order = new VideoOrder();
    order.setOpenid("uvwxyz").setNotifyTime(new Date()).setState(0).setCreateTime(new Date())
            .setHeadImg("http://xxxxx.jpg").setDel(0).setIp("127.0.0.1").setNickname(One Piece);
    videoOrderMapper.insertVideoOrder(order);
    System.out.println("Data inserted successfully!");
	}

    @Test
    void findVideoOrderById(a) {... }@Test
    void findVideoOrderByOutTradeNo(a) {... }@Test
    void deleteVideoOrderByIdAndUserId(a) {... }@Test
    void findUserVideoOrderList(a) {... }@Test
    void updateVideoOrderByOutTradeNo(a) {... }}Copy the code

Once the test is complete, if you can add records to the database normally, you can continue reading the article!

2.5 Development of CommonUtils and WXpayUtils for unified single interface development of wechat

Summary: Encapsulates common utility classes CommonUtils and WXpayUtils

Can from WeChat developer documentation for part of the code pay.weixin.qq.com/wiki/doc/ap…

[img-DorogqS3-1605166581358] (D: Note notes Springboot Notes Project practice Springboot small D classroom online education practice (including wechat payment) \ wechat Login and Payment Note. assets\image-20200829134912351.png)

[imG-iINpDEK4-1605166581359] [IMG-iINpDEK4-1605166581359] [IINpDEK4-1605166581359] [IMG-iINpDEK4-1605166581359] \ wechat Login and Payment Note. assets image-20200829140855873.png)

CommonUtils.java

/ * * *@Auther: csp1999
 * @Date: 2020/08/29/13:30
 * @Description: Common tool class encapsulation, MD5, UUID, etc. */
public class CommonUtils {

    // Generate a uUID that identifies an order and is also used as nonce_str
    public static String generateUUID(a) {
        return UUID.randomUUID().toString().replaceAll("-"."")// Remove the default - delimiter
                .substring(0.32);// Capture 32 bits
    }

    MD5 encryption tool class
    public static String getMD5String(String data) {
        try {
            // Obtain the MD5 encryption instance
            MessageDigest md = MessageDigest.getInstance("MD5");
            // Get an array object
            byte[] array = md.digest(data.getBytes("UTF-8"));
            // Concatenate encrypted strings
            StringBuilder builder = new StringBuilder();
            for (byte item : array) {
                builder.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1.3));
            }
            return builder.toString().toUpperCase();// All letters in uppercase
        } catch (Exception exception) {
            System.out.println("MD5 encryption algorithm is abnormal...");
        }
        return null; }}Copy the code

Download and import WXPayUtils from the official website. UUID and MD5 encryption tools are also included in the official tool class, as shown in the figure below:

[imG-HG75R7BP-1605166581360] [IMG-HG75R7BP-1605166581360] [IMG-HG75R7BP-1605166581360] [IMG-HG75R7BP-1605166581360] Assets \image-20200829140835980. PNG)

We can mainly use the tools provided by wechat official.

2.6 wechat Pay ordering API interface

Introduction: explain the development of single interface, development skills and payment profile Settings

#	1The unified ordering parameters require wechat signature, and the signature rules are as follows - Document address: HTTPS://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=4_3- The general procedure for generating signatures is as follows: First, set all the sent or received data as set M, sort the non-empty parameter values in set M in alphabetical order according to the ASCII characters of parameter names, and use the FORMAT of URL key-value pairs (key1= Value1&key2 =value2...). Concatenated to the string stringA. The second step is to obtain the stringSignTemp string from the key on the final concatenation of stringA, perform MD5 operation on stringSignTemp, and convert all characters of the string to uppercase to obtain the sign value signValue. Key setting path: wechat business platform (pay.weixin.qq.com)--> Account setting -->API security --> key setting <-- parameter: SortedMap<String, String> params =new TreeMap<>();  
	        params.put("appid", wxPayConfig.getAppId());  // Public ID
	        params.put("mch_id", wxPayConfig.getMchId());  / / merchants
	        params.put("nonce_str", CommonUtil.generateNonceStr());  // Random string
	        params.put("body", videoOrder.getVideoTitle());  // Product description
	        / / merchant order number, merchants system internal order number, request within 32 characters, only Numbers, upper and lower case letters _ - | * and only under the same merchant number
	        params.put("out_trade_no", videoOrder.getOutTradeNo()); 
	        params.put("total_fee", videoOrder.getTotalFee().toString());  // Price points
	        params.put("spbill_create_ip", videoOrder.getIp());  
	        // Notify the address
	        params.put("notify_url", wxPayConfig.getDomain()+wxPayConfig.getCallbackUrl()); 
	        // Transaction type JSAPI public account Payment NATIVE scan code payment APP APP payment
	        params.put("trade_type"."NATIVE");  
        	// Generate a signature
	        String sign = WXPayUtil.createSign(params, wxPayConfig.getKey());
	        params.put("sign", sign); 
	        // Convert the parameter to XMLString requestXMl = WXPayUtil.mapToXml(params); After the signature is generated, use the tool to verify HTTPS://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=20_1-- - > #2Test address: localhost:8081/api/v1/order/add? video_id=2

#    3, course sign test results: sign: 85118 c91dfcb052fb02ac183bf3d57d2Copy the code
# wechat related configuration:
# # the public
wxpay.appid=wx252XXXXX1xs9h
wxpay.appsecret=qm4i2u43oXXXXXXXX7055s8c99a8
# wechat open platform configuration
wxopen.appid=wx025XXXXXXa2d5b
wxopen.appsecret=f5b6730c59XXXXXXXXeb8948a9f3
Redirect the URL to the home page and get the token according to the code, so as to obtain the login information of the wechat scan user
# This domain name is authenticated by others. It can only be used for reference
wxopen.redirect_url=http://XXXXXXXXXXXXXX.cn/xdclass/wechat/user/callback
# wechat merchant platform Merchant ID Order secret key callback address
wxpay.mer_id=8XXXXXX068
wxpay.key=MbZL0DiXXXXXXXXX5S51MK2
wxpay.callback=http://XXXXXXXXXXXXXXX.cn/xdclass/
Copy the code

Example of signature verification:


      
<xml>
<nonce_str>6a8ee5f42xxxxxxxxxxad31522bb</nonce_str>
<out_trade_no>6ba6270f0xxxxxxxxxx97dd7532c</out_trade_no>
<appid>wx5beXXXXXXXXXXXd40c</appid>
<total_fee>500</total_fee>
<sign>624D0FEXXXXXXXXXXXXX7857F95</sign>
<trade_type>NATIVE</trade_type>
<mch_id>15xxxxxx832</mch_id>
<body>New version ELK ElasticSearch for 6.2 in 2020</body>
<notify_url>http://XXXXXXXXXXXXXXXXX/wechat/order/callback1</notify_url>
<spbill_create_ip>0:0:0:0:0:0:1-0</spbill_create_ip>
</xml>Merchant id: xxxxxxxxxxxxxxxx018dCopy the code

WeChat official signature check address: pay.weixin.qq.com/wiki/doc/ap…

Signature verification (signature generation must be tested by ourselves, many people’s signature generation format is not correct, resulting in wechat payment failure) :

[img-BCFRK2ZJ-1605166581361] [IMG-BCFRk2ZJ-1605166581361] [IMG-BCFRk2ZJ-1605166581361] [IMG-BCFRk2ZJ-1605166581361] \ wechat Login and Payment Note. assets\image-20200829171420175. PNG)

2.7 Call wechat unified single interface practice

Brief introduction: Call wechat unified single interface combat, send POST request, and obtain the response into map, obtain the code_url of the TWO-DIMENSIONAL code link of the transaction session

# 1. Configure unified single interface
# 2. Send request validation
< XML >
      
       
       
      
       
       
      
       
       
      
       
       
      
       
       
      
       
       
      
       
       
      
       
       
      
       
       
      
        According to the error code https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=9_1
      Copy the code

VideoOrderServiceImpl.java

/ * * *@Auther: csp1999
 * @Date: 2020/08/29 / insisting *@Description: Video order Service implementation class */
@Service
public class VideoOrderServiceImpl implements VideoOrderService {

    @Autowired
    private WeChatConfig weChatConfig;

    @Autowired
    private VideoMapper videoMapper;

    @Autowired
    private VideoOrderMapper videoOrderMapper;

    @Autowired
    private UserMapper userMapper;

    / * * * @ method description: generation, save the order information and call the unified order, * @ parameters collection: [videoOrderPojo] * @ return type: com. Haust. Entity. @ VideoOrder * author's name: csp1999 * @ date/time: 2020/8/29 beginning * /
    @Override
    public String save(VideoOrderPojo videoOrderPojo) throws Exception {
        // Find the video information based on the ID
        Video video = videoMapper.findVideoById(videoOrderPojo.getVideoId());

        // Find user information
        User user = userMapper.findByUserId(videoOrderPojo.getUserId());

        // Construct the order object
        VideoOrder videoOrder = new VideoOrder();
        videoOrder.setTotalFee(video.getPrice());
        videoOrder.setVideoImg(video.getCoverImg());
        videoOrder.setVideoTitle(video.getTitle());
        videoOrder.setCreateTime(new Date());
        videoOrder.setVideoId(video.getId());
        videoOrder.setState(0);
        videoOrder.setUserId(user.getId());
        videoOrder.setHeadImg(user.getHeadImg());
        videoOrder.setNickname(user.getName());
        videoOrder.setDel(0);
        videoOrder.setIp(videoOrderPojo.getIp());
        videoOrder.setOutTradeNo(CommonUtils.getUUID());
        videoOrderMapper.insertVideoOrder(videoOrder);

        // Get the codeURL
        String codeUrl = unifiedOrder(videoOrder);

        return codeUrl;
    }

    /** * @ Method description: unified order method request wechat unified order interface, and finally obtain the url of wechat pay TWO-DIMENSIONAL code picture * @ parameter set: [videoOrder] * @ Return type: java.lang.String * @ Csp1999 * @ Date time: 2020/8/29 16:33 */
    public String unifiedOrder(VideoOrder videoOrder) throws Exception {
        WXPay wxPay = new WXPay();

        // Encapsulate order parameters and wechat pay parameters with map
        SortedMap<String, String> data = new TreeMap<>();
        data.put("appid", weChatConfig.getAppid());// Public account ID: public account ID assigned by wechat Pay (enterprise ID corpid is appId)
        data.put("mch_id", weChatConfig.getMchId());// Merchant number: merchant number assigned by wechat Pay
        data.put("nonce_str", CommonUtils.getUUID());// Random string: user-defined parameter, can be terminal device number (store number or cashier device ID), PC WEB page or public account can send "WEB"
        data.put("body", videoOrder.getVideoTitle());// Product description
        data.put("out_trade_no", videoOrder.getOutTradeNo());/ / merchant order no. : 32 characters, only Numbers, upper and lower case letters _ - | * and only under the same merchant number.
        data.put("total_fee", videoOrder.getTotalFee().toString());// Price: in minutes
        data.put("spbill_create_ip", videoOrder.getIp());// The client IP address of the ordering user
        data.put("notify_url", weChatConfig.getPayCallbackUrl());// Notification address: callback address for receiving notification of wechat payment result asynchronously. The notification URL must be accessible to the external network and cannot carry parameters.
        data.put("trade_type"."NATIVE");// Transaction type: this is specified as scan payment

        // Generate the sign signature
        String sign = WXPayUtil.generateSignature(data, weChatConfig.getKey());
        data.put("sign", sign);// Signature: signature value returned by wechat

        System.out.println("-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- the XML data is as follows: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --");
        / / map XML
        String payXmlData = WXPayUtil.mapToXml(data);
        System.out.println(payXmlData);

        / / order, send a POST request WeChat background unified order interface: https://api.mch.weixin.qq.com/pay/unifiedorder for the orderStr XML format string
        String orderStr = HTTPUtils.doPost(WeChatConfig.getUnifiedOrderUrl(), payXmlData, 4000);
        System.out.println("-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- unity request order interface returned orderStr data is as follows: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --");
        System.out.println(orderStr);

        if (null == orderStr) {
            return null;
        }

        // Convert the xmL-formatted string orderStr returned by the unified single interface to map
        Map<String, String> unifiedOrderMap = WXPayUtil.xmlToMap(orderStr);
        System.out.println("-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- into the map orderStr data is as follows: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --");
        // The purpose of this method is to solve the problem that the printed object Chinese characters are garbled and cannot read the error message
        String string = new String(unifiedOrderMap.toString().getBytes("ISO-8859-1"), "UTF-8");
        System.out.println(string);

        if(unifiedOrderMap ! =null) {
            System.out.println("Payment QR code URL :" + unifiedOrderMap.get("code_url"));
            return unifiedOrderMap.get("code_url");// Get the code_URL (URL of the payment QR code picture) data returned by the unified order interface
        }

        // Otherwise null is returned
        return null; }}Copy the code

WeChatConfig.java

/ * * *@Auther: csp1999
 * @Date: 2020/08/26/10:27
 * @Description: wechat related configuration */
@Configuration
/ * * *@PropertySourceAnnotations specify the location of the configuration file: (Attribute name specification: large module. Submodule. Attribute name) */
@PropertySource(value = "classpath:application.properties")// Read the configuration from application.properties under the classpath

@Data // Lombok has a built-in set/get method
@Accessors(chain = true) // chain call
public class WeChatConfig {

    /** * tO be filled: appID =%s redirect_URI =%s state=%s */
    private final static String OPEN_QRCODE_URL = "https://open.weixin.qq.com/connect/qrconnect?appid=%s&redirect_uri=%s&response_type=code&scope=snsapi_login&state=%s#we chat_redirect";

    /** * obtain access_token address * Parameter to be filled: appID =%s secret=%s code=%s */
    private final static String OPEN_ACCESS_TOKEN_URL="https://api.weixin.qq.com/sns/oauth2/access_token?appid=%s&secret=%s&code=%s&grant_type=authorization_code";

    /** * User information address * Parameter to be filled: access_token=%s openID =%s */
    private final static String OPEN_USER_INFO_URL ="https://api.weixin.qq.com/sns/userinfo?access_token=%s&openid=%s&lang=zh_CN";

    /** * wechat pay unified single order URL */
    private final static String UNIFIED_ORDER_URL = "https://api.xdclass.net/pay/unifiedorder";

    /** * merchant id */
    @Value("${wxpay.mer_id}")
    private String mchId;

    /** * Pay key */
    @Value("${wxpay.key}")
    private String key;

    /** * wechat pay callback url */
    @Value("${wxpay.callback}")
    private String payCallbackUrl;

    /** * wechat appid */
    @Value("${wxpay.appid}")
    private String appid;

    /** * wechat key */
    @Value("${wxpay.appsecret}")
    private String appsecret;

    /**
     * 开放平台appid
     */
    @Value("${wxopen.appid}")
    private String openAppid;

    /** * open platform key */
    @Value("${wxopen.appsecret}")
    private String openAppsecret;

    /** * Open platform callback address */
    @Value("${wxopen.redirect_url}")
    private String openRedirectUrl;

    public static String getUnifiedOrderUrl(a) {
        return UNIFIED_ORDER_URL;
    }

    public static String getOpenUserInfoUrl(a) {
        return OPEN_USER_INFO_URL;
    }

    public static String getOpenAccessTokenUrl(a) {
        return OPEN_ACCESS_TOKEN_URL;
    }

    public static String getOpenQrcodeUrl(a) {
        returnOPEN_QRCODE_URL; }}Copy the code

2.8 Google QR code tool generates scan payment QR code

Summary: Use Google QR code tool to generate scan payment QR code according to Code_URL

1. Generate a TWO-DIMENSIONAL code to return to the page end and add dependencies

<! -- Google QR code generation package -->
<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

2. Use wechat scan code to complete payment

# References:
https://blog.csdn.net/shenfuli/article/details/68923393 https://www.cnblogs.com/lanxiamo/p/6293580.html
# qr code knowledge: https://coolshell.cn/articles/10590.html
Copy the code

OrderController.java

/ * * *@Auther: csp1999
 * @Date: 2020/08/28/18:30
 * @Description: Order Controller */
@Controller
@RequestMapping("/order")
public class OrderController {

    @Autowired
    private VideoOrderService videoOrderService;

    @ResponseBody
    @GetMapping("/add")
    public void saveOrder(@RequestParam(value = "video_id", required = true) int videoId,
                          HttpServletRequest request, HttpServletResponse response) throws Exception {

        //String ip = IPUtils.getIpAddr(request);
        String ip = "120.25.1.43"; // Temporary write dead, easy to test
        //int userId = request.getAttribute("user_id");
        int userId = 1;// Temporary write dead, easy to test
        VideoOrderPojo videoOrderPojo = new VideoOrderPojo();
        videoOrderPojo.setUserId(userId);// User order ID
        videoOrderPojo.setVideoId(videoId);/ / video id
        videoOrderPojo.setIp(ip);// The user orders the IP address

        // Save the order information and send a unified order request to wechat to obtain the CODE :codeUrl
        String codeURL = videoOrderService.save(videoOrderPojo);
        if (codeURL == null) {
            throw new NullPointerException();
        }

        try {
            // Generate a QR code:
            Map<EncodeHintType, Object> hints = new HashMap<>();
            // Set the error correction level
            hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.L);
            // Set the encoding
            hints.put(EncodeHintType.CHARACTER_SET, "UTF-8");

            // Generate qr code Google QR code generation package
            BitMatrix bitMatrix = new MultiFormatWriter().encode(codeURL, BarcodeFormat.QR_CODE, 400.400, hints);

            // Get the output stream by response
            ServletOutputStream out = response.getOutputStream();

            // The two-dimensional code output page Google two-dimensional code generation package
            MatrixToImageWriter.writeToStream(bitMatrix, "png", out);
        } catch (Exception e) {
            System.out.println("Abnormal generation of two-dimensional code..."); }}}Copy the code

2.9 Wechat Pay code sweep callback

Introduction: Use Ngrock to receive wechat callback locally and develop callback interface

The callback interface official documentation: pay.weixin.qq.com/wiki/doc/ap…

weChatController.java

Set of parameters: [request, response] * @ Return type: void * @ Author name: CSP1999 * @ Date time: 2020/8/29 20:57 */
@RequestMapping("/order/callback")// Note that GetMapper can not be written in the wechat payment development documentation
public void orderCallBack(HttpServletRequest request, HttpServletResponse response) throws Exception {
    // Get the input stream from request
    InputStream in = request.getInputStream();
    // Get the BufferedReader from the byte input stream :BufferedReader is a wrapper design pattern with higher performance
    BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(in,"UTF-8"));
    // Read data
    StringBuffer stringBuffer = new StringBuffer();// It is used to concatenate and get the data in XML format in the request sent by wechat platform
    String line;
    while((line = bufferedReader.readLine())! =null){
        stringBuffer.append(line);
    }
    // Close all streams
    bufferedReader.close();
    in.close();
    Map<String,String> callbackMap = WXPayUtil.xmlToMap(stringBuffer.toString());
    System.out.println("-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- get WeChat platform to send in the request XML format data: -- -- -- -- -- -- -- -- -- -- -- -- --");
    System.out.println(callbackMap.toString());
}
Copy the code
Callback data: < XML >< appID ><! [CDATA[wx5beac15ca207c40c]]></appid><bank_type><! [CDATA[CFT]]></bank_type><cash_fee><! [CDATA[10]]></cash_fee><fee_type><! [CDATA[CNY]]></fee_type><is_subscribe><! [CDATA[Y]]></is_subscribe><mch_id><! [CDATA[1503809911]]></mch_id><nonce_str><! [CDATA[de019d5f1e5d40649cd76de33f18b13e]]></nonce_str><openid><! [CDATA[oiNKG03vVY4PHlGUEwT-ztFo8K8Y]]></openid><out_trade_no><! [CDATA[4d8cea4a916440368583edaf82488624]]></out_trade_no><result_code><! [CDATA[SUCCESS]]></result_code><return_code><! [CDATA[SUCCESS]]></return_code><sign><! [CDATA[FA799B7DF70C2BAC558E839E01EF341A]]></sign><time_end><! [CDATA[20180626230347]]></time_end><total_fee>10</total_fee><trade_type><! [CDATA[NATIVE]]></trade_type><transaction_id><! [CDATA[4200000142201806264038572903]] > < / transaction_id > > < / XML into the map: {transaction_id=4200000142201806264038572903, nonce_str=de019d5f1e5d40649cd76de33f18b13e, bank_type=CFT, openid=oiNKG03vVY4PHlGUEwT-ztFo8K8Y, sign=FA799B7DF70C2BAC558E839E01EF341A, fee_type=CNY, mch_id=1503809911, cash_fee=10, out_trade_no=4d8cea4a916440368583edaf82488624, appid=wx5beac15ca207c40c, total_fee=10, trade_type=NATIVE, result_code=SUCCESS, time_end=20180626230347, is_subscribe=Y, return_code=SUCCESS}Copy the code

Matters needing attention:

  • Post is used for callback. There is no notification method for writing callback in wechat documents
  • You can use this annotation @requestMapping
  • Question: Be sure to read the log

2.10 Update order status and idempotency of wechat callback processing

Brief introduction: Wechat Pay callback processing update order status and explain what is idempotent interface, wechat callback notification rules:

(notice frequency for 30/180/1800/1800/1800/1800/3600/15/15, unit: second)

# idempotency: No matter how many times you call your interface with the same parameters and values, the response will be the same as if you called it once
Check whether the signature is correct and prevent forgery callback # 2 check whether the order has been updated
# 3. Update order status if not updated

# 4. Respond to wechat, SUCCESS or FAIL
response.setContentType("text/xml"); response.getWriter().println("success");Copy the code

Payment callback method is improved:

Set of parameters: [request, response] * @ Return type: void * @ Author name: CSP1999 * @ Date time: 2020/8/29 20:57 */
 @RequestMapping("/order/callback")// Note: can not write GetMapper wechat payment development documentation has been declared, you can read the documentation for details
 public void orderCallBack(HttpServletRequest request, HttpServletResponse response) 
     throws Exception {
     // Get the input stream from request
     InputStream in = request.getInputStream();
     // Get the BufferedReader from the byte input stream :BufferedReader is a wrapper design pattern with higher performance
     BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(in,"UTF-8"));
     // Read data
     StringBuffer stringBuffer = new StringBuffer();// It is used to concatenate and get the data in XML format in the request sent by wechat platform
     String line;
     while((line = bufferedReader.readLine())! =null){
         stringBuffer.append(line);
     }
     // Close all streams
     bufferedReader.close();
     in.close();
     Map<String,String> callbackMap = WXPayUtil.xmlToMap(stringBuffer.toString());
     System.out.println("-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- get WeChat platform to send in the request XML format data: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -");
     System.out.println(callbackMap.toString());
     // Check whether the signature is correct (the same way as the official website check, XML string and merchant key)
     if (WXPayUtil.isSignatureValid(callbackMap,weChatConfig.getKey())){
         System.out.println("Signature verified...");
         if ("SUCCESS".equals(callbackMap.get("result_code"))) {// result_code: Indicates the service result SUCCESS/FAIL
             // Find the order according to the serial number
             VideoOrder dbVideoOrder = videoOrderService.
                 findByVideoOrderOutTradeNo(callbackMap.get("out_trade_no"));
             if(dbVideoOrder.getState() == 0) {// Determine the service scenario: The payment status is 0, that is, the next step can be performed only when no payment is made
                 VideoOrder videoOrder = new VideoOrder();
                 videoOrder.setOpenid(callbackMap.get("openid"))// User id
                         .setOutTradeNo(callbackMap.get("out_trade_no"))// Wechat pay serial number
                         .setNotifyTime(new Date())// Pay the callback time
                         .setTotalFee(Integer.parseInt(callbackMap.get("total_fee")))// Pay the total amount
                         .setState(1);// The payment status is changed to paid
                 // Update the order according to the serial number
                 int row = videoOrderService.updateVideoOderByOutTradeNo(videoOrder);
                 Row == 1/row == 0 Indicates whether the order update succeeds or fails
                 if (row == 1) {// Success: inform wechat background that order processing is successful
                     response.setContentType("text/xml");
                     response.getWriter().println("success");
                     // SUCCESS: indicates that the wechat background is informed that the website platform has successfully received the notification and verified successfully in its own background}}}}// Failed: inform wechat background that order processing failed
     response.setContentType("text/xml");
     response.getWriter().println("fail");// FAIL: indicates that the wechat background is told that the web page verification fails
 }
Copy the code

2.11 Single transaction processing under wechat Pay

Introduction: explains the single interface to add transactions and common transaction options

  • Springboot open transaction, start the class add @ EnableTransactionManagement inside
  • Add @transactional (Propagation = Propagation.REQUIRED) to methods requiring transactions
  • Aop has the benefits of managing transactions and options to add, delete, and enable transactions

3. Demo demonstration and source code acquisition:

Test scan login:

Pay after successful login

Call wechat Pay to place an order

Code get address: Gitee warehouse address

  • If it is helpful to everyone, please support sanlian!
  • Have a question welcome to leave a message in the comment area, help you solve in time!