The development of preparation

Reference documentation JSAPI Payment Development documentation

Method of payment

At present, there are 6 mainstream payment methods in wechat

way instructions
Payment code Payment code payment is a mode in which the user displays the “card barcode/QR code” in the wechat wallet to the merchant system for scanning and directly completes the payment. Mainly used in offline face to face cashier scene.
Native to pay Native payment is a mode in which the merchant system generates a TWO-DIMENSIONAL code for payment according to the wechat payment protocol, and the user completes the payment by “scanning” with wechat. This model is applicable to PC website payment, entity store single product or order payment, media advertising payment and other scenarios.
The JSAPI pay JSAPI payment means that the user opens the H5 page of the merchant in wechat, and the merchant invoks the JSAPI interface provided by wechat Pay to call the wechat Pay module to complete the payment on the H5 page.
APP to pay APP payment, also known as mobile payment, is a mode for merchants to complete the payment by integrating the open SDK into the mobile application APP and activating the wechat payment module.
H5 pay H5 Payment is mainly a payment product that evokes wechat Payment through the browser in mobile devices such as mobile phones and ipads.
Small program payment Applets are payment products that are specifically defined for use in applets. At present, small programs can and can only be used to invoke wechat Pay.

Because of the previous article about the public number, so here is the main introduction of JSAPI payment, the development of the back is also around this.

The application scenarios of JSAPI are as follows:

  • Users enter the merchant’s official account in the wechat public account, open a home page, and complete the payment
  • Users’ friends share the connection of the merchant page in the circle of friends, chat window, etc. Users click the link to open the merchant page and complete the payment
  • The merchant page is converted into a TWO-DIMENSIONAL code. After scanning the two-dimensional code, the user opens the page in the wechat browser and completes the payment

The core of nouns

Different from wechat public account test development, Intranet penetration, and ordinary test account can be used. Wechat Pay requires developers to have a real merchant number that has passed verification, and the merchant number has the payment function, and the merchant has a real public number, etc.

  1. [wechat Merchant platform] Wechat Merchant platform is a collection of functions related to wechat Pay, including parameter configuration, payment data query and statistics, online refund, voucher or discount operation, and other functional platform entrance: Pay.weixin.qq.com

  2. [wechat Public Platform] Wechat public platform is the background of wechat public account application entrance and management. Merchants can submit basic information, business information and financial information on the public platform to apply for opening the wechat pay function. Platform entry: mp.weixin.qq.com

  3. [wechat Pay System] wechat Pay system refers to the general name of the API interface, background business processing system, accounting system, callback notification and other systems involved in the process of wechat pay.

  4. 【 Merchant Certificate 】 The Merchant certificate is a binary file provided by wechat. When the merchant system initiates a communication request with the backend server of wechat Pay, it serves as the credential for the backend to identify the real identity of the merchant.

  5. [Merchant background System] The merchant background system is the general name of the merchant background processing business system, such as: merchant website, cashier system, purchase-sales-inventory system, delivery system, customer service system, etc., generally associated with the developer’s own database.

  6. [Signature] Merchant background and wechat Pay background generate a result based on the same key and algorithm, which is used to verify the legitimacy of the identities of both parties. The signature algorithm is formulated and disclosed by wechat Pay. Common signature methods include MD5, SHA1, SHA256, and HMAC.

  7. [Payment password] Payment password is the password set separately when users open wechat pay, which is used to confirm the completion of payment transaction authorization. The password is different from the wechat login password.

  8. [Openid] User’s id in the official account. Different official accounts have different OpenIDS. The merchant background system can obtain the user’s OpenID through apis such as login authorization, payment notification and order query. It is mainly used to identify the same user and send customer service messages and template messages to users.

Core account parameters for application:

Account Parameters

Parameters in the mail API parameter name Detailed instructions
APPID appid Appid is the unique identifier of wechat public account or open platform APP. After applying for public account on public platform or APP account on open platform, wechat will automatically assign the corresponding APPID to identify the application. It can be viewed in wechat public platform –> Development –> Basic configuration, and the value of this field will also be included in the wechat payment approval email of the merchant.
Wechat Pay merchant number mch_id After a merchant applies for wechatPay, the merchant’s account number assigned by wechatPay.
API keys key The key that generates the signature during the transaction process is only kept in the merchant system and the background of wechat Payment, and will not be spread in the network. The merchant should keep the Key properly, do not transmit it in the network or store it in other clients, and ensure that the Key will not be leaked. Merchants can log in to the wechat merchant platform to set up according to the email prompt. You can also set the following path: wechat merchant platform (pay.weixin.qq.com)–> Account center –> Account Settings –>API security –> key Settings
Appsecret secret AppSecret is the interface password corresponding to the APPID, used forGets the interface invocation credentialsAccess_token is used. In wechat Pay, pass firstOAuth2.0 interfaceObtain the user openID, which is used in the single interface of wechat web payment mode. You can log in to the public platform –> wechat Pay to get AppSecret (you need to be a developer and your account is not abnormal).

Agreement rules

When a merchant accesses wechat Pay, the API call must follow the following rules:

transport To ensure transaction security, HTTPS is used for transmission
submission The POST method is used
The data format Both the submitted and returned data are in XML format, with a root node named XML
A character encoding Utf-8 character encoding is adopted
Signature algorithm MD5/HMAC-SHA256
Signature required Signature verification is required for requesting and receiving data. For details, seeSecurity specification – Signature algorithm
The certificate request The application for refund, order cancellation, red envelope interfaces, etc., require merchant API certificates, which are explained in the API interface documents.
Judgment logic Judge the return of the protocol field, then judge the return of the business, and finally judge the status of the transaction

Parameters configured in the development code (in actual development, it is recommended to configure directly in the properties file to facilitate environment switching)

// Public account, applet appID
public static String APP_ID = "xxxxxxxxx"; 
// AppSecret
public static String SECRET = "xxxxxxxxx";
/ / merchants
public static final String MCH_ID = "xxxxxxxxx";
/ / API keys
public static final String API_KEY = "xxxxxxxxx";
// Web authorization domain name, JSAPI payment authorization directory, JS interface security domain name
public static final String AUTH_URL = "xxxxxxxxx";
Copy the code

The above parameters cannot be disclosed. If the company has a ready-made payment account, it is better, if not, I am afraid it can only be rented in a certain treasure, but without these will not affect the early business development.

The business card

Business process timing diagram

For developers, in the process of initiating payments,

Back end: it mainly calls three interfaces in JSAPI payment: [unified order API], [payment result notification API], [Query order API].

Front end:

The front-end H5 in wechat starts payment, providing button and JSON data transmission for users to trigger wechat payment.

To start developing

Project structures,

I. Adopt SpringBoot+Thymeleaf structure, refer to wechat Public Account rapid development (II) Project construction and passive response

Second, the introduction of the official SDK toolkit

After reading the document, I found that for XML parsing, encryption algorithm and other methods are often used, wechat directly provides us with a common tool class method of semi-finished products, attention, these can only be semi-finished products, need to make appropriate changes when using.

Link: DOWNLOAD SDK and DEMO. Select the JAVA version and decompress it

Code development

Configuration of Public Account

First, inject the information of the public account and the merchant into the Bean

@Component
public class WXPayConfigExtend extends WXPayConfig {

    private byte[] certData;

    private WXPayConfigExtend(a) throws Exception {
// String certPath = WXPayConstants.APICLIENT_CERT;
// File file = new File(certPath);
// InputStream certStream = new FileInputStream(file);
// this.certData = new byte[(int) file.length()];
// certStream.read(this.certData);
// certStream.close();
    }

    @Override
    public String getAppID(a) {
        return WXPayConstants.APP_ID;
    }
    @Override
    public String getMchID(a) {
        return WXPayConstants.MCH_ID;
    }
    @Override
    public String getKey(a) {
        return WXPayConstants.API_KEY;
    }
    @Override
    public InputStream getCertStream(a) {
        ByteArrayInputStream certBis = new ByteArrayInputStream(this.certData);
        return certBis;
    }
    @Override
    public int getHttpConnectTimeoutMs(a) {
        return 2000;
    }
    @Override
    public int getHttpReadTimeoutMs(a) {
        return 10000;
    }
    @Override
    public IWXPayDomain getWXPayDomain(a) {
        return WXPayDomainSimpleImpl.instance();
    }
    public String getPrimaryDomain(a) {
        return "api.mch.weixin.qq.com";
    }
    public String getAlternateDomain(a) {
        return "api2.mch.weixin.qq.com";
    }
    @Override
    public int getReportWorkerNum(a) {
        return 1;
    }
    @Override
    public int getReportBatchSize(a) {
        return 2; }}Copy the code

To obtain the openid

Page to provide web page authorization, in order to obtain OpenID, about wechat web page authorization can refer to: wechat public account rapid development (4) wechat web page authorization

Page:

Templates/preorder.html is a static page designed directly to initiate prepayment buttons

It contains a form to jump to the back-end payment interface:

<form name=wexinpayment action='http://chety.mynatapp.cc/api/v1/wechat1/placeOrder' method=post target="_blank">.Copy the code

Thymeleaf page forwarding controller:

@Controller
@RequestMapping("/api/v1/wechat1")
public class IndexController {

    // For the thymeleaf environment, jump to the HTML page corresponding to the string
    @RequestMapping("/{path}")
    public String webPath(@PathVariable String path) {
        returnpath; }}Copy the code

Page authorization entry controller:

@Controller
@RequestMapping("/api/v1/wechat1")
public class IndexController {...@RequestMapping("/index")
    public void index(String code, Model model, HttpServletRequest request, HttpServletResponse response) throws IOException {
        // Give explicit authorization to get code
        if(code ! =null) {
            JSONObject json = WeChatUtil.getWebAccessToken(code);
            WXPayUtil.getLogger().info("code: ",json.toJSONString());
            String openid = json.getString(("openid"));
            request.getSession().setAttribute("openid", openid);
            WXPayUtil.getLogger().info("index openid={}",openid);
            // Redirect to the pre-order page
            response.sendRedirect("preOrder"); // Redirect to the pre-paid page
        } else {
            StringBuffer url = RequestUtil.getRequestURL(request);
            WXPayUtil.getLogger().info("Index request path: {}"+url);
            String path = WeChatUtil.WEB_REDIRECT_URL.replace("APPID", WeChatConstants.APP_ID).replace("REDIRECT_URI", url).replace("SCOPE"."snsapi_userinfo");
            WXPayUtil.getLogger().info("Index redirection: {}",path);
            // Redirect to the page where you are authorized to get the coderesponse.sendRedirect(path); }}}Copy the code

To start the project, request the interface:

1. In the address bar of wechat developer tool, enter: {webpage authorized domain name}// API /v1/wechat1/index

2. Confirm [agree] authorization (the purpose here is to obtain OpenID, and the base silent authorization mode can also be used, without the displayed prompt authorization), jump to the pre-payment page, as shown in the figure:

Initiate payment

When the user confirms the order on the pre-paid page, it will request the [/placeOrder] interface, which will call the [Unified Order] interface of wechat:

First, wechat unified order entity class

@Setter
@Getter
@ToString
@XmlRootElement(name = "xml")
@XmlAccessorType(XmlAccessType.FIELD)
public class WxOrderEntity {
    private String appid;
    private String mchId;
    private String deviceInfo;
    private String nonceStr;
    private String sign;
    private String body;
    private String outTradeNo;
    private int totalFee;
    private String spbillCreateIp;
    private String notifyUrl;
    private String tradeType;
    private String openid;
}
Copy the code

Second, the business layer of wechat Pay

@Service
public class WxBackendServiceImpl {

    @Autowired
    WXPayConfigExtend wxPayConfigExtend;

    // Unified order
    public Map<String, Object> unifiedorder(Model model, HttpServletRequest request) throws Exception {
        WXPayUtil.getLogger().info("Enter the single controller...");
        Map<String,Object> data = null;
        try {
            // Generate the order number
            WXPay wxpay = new WXPay(wxPayConfigExtend);
            WxOrderEntity order = new WxOrderEntity();

            double price = 0.01;
            String orderName = "XXX -- wechat Pay";
            int number = (int)((Math.random()*9) *1000);/ / random number
            DateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmss");/ / time
            String orderNumber = dateFormat.format(new Date()) + number;
            String nonceStr = WXPayUtil.generateNonceStr();
            String openId = (String) request.getSession().getAttribute("openid");
            openId = openId == null ? "o4036jqo2PN9isV6N2FHGRsGRVqg" : openId; // openid = openID

            order.setBody(orderName);
            order.setOutTradeNo(orderNumber);
            order.setTotalFee(MoneyUtil.Yuan2Fen(price));
            order.setSpbillCreateIp(IpUtils.getIpAddr(request));
            order.setOpenid(openId);
            order.setNotifyUrl(WXPayConstants.NOTIFY_URL);
            order.setTradeType(WXPayConstants.TRADE_TYPE_JSAPI);
            order.setNonceStr(nonceStr);

            WXPayUtil.getLogger().info("Save unified single interface call,order:{}",order);
            // Wxpay.fillRequestData (data) is automatically called;
            Map<String, String> response = wxpay.doWxPayApi(order,WXPayConstants.UNIFIEDORDER);
            WXPayUtil.getLogger().info("Save order result,response:{}",response);

            if(response.get(WXPayConstants.RETURN_CODE).equals("SUCCESS")&&response.get(WXPayConstants.RESULT_CODE).equals("SUCCESS")){
                String url = request.getQueryString() == null? request.getRequestURL().toString():request.getRequestURL()+"?"+request.getQueryString();
                String prepayId = response.get(WXPayConstants.PREPAY_ID);
                data = wxpay.permissionValidate(nonceStr,url,prepayId,wxPayConfigExtend.getKey());
                returndata; }}catch (Exception e) {
            WXPayUtil.getLogger().error("DoUnifiedOrder -- order failed: {}" , e.getMessage());
        }
        return null; }}Copy the code

wxpay.doWxPayApi(…) Encapsulates calls to a single interface:

public Map<String, String> doWxPayApi(WxOrderEntity order,String apiType) {
    Map<String, String> resp = null;
    try {
        Map<String,String> map = new HashMap<>();
        map.put("out_trade_no", order.getOutTradeNo());
        map.put("nonce_str", order.getNonceStr());
        map.put("trade_type", order.getTradeType());

        if ("unifiedorder".equalsIgnoreCase(apiType)) {
            map.put("spbill_create_ip", order.getSpbillCreateIp());
            map.put("openid", order.getOpenid());
            map.put("notify_url", order.getNotifyUrl());
            map.put("total_fee", String.valueOf(order.getTotalFee()));
            map.put("body", order.getBody());

            resp = unifiedOrder(map);
        } else if ("orderquery".equalsIgnoreCase(apiType)) {
            resp = orderQuery(map);
        } else if ("closeorder".equalsIgnoreCase(apiType)) { resp = orderQuery(map); }}catch (Exception e) {
        WXPayUtil.getLogger().error(order.getOutTradeNo()+"-- failed to call interface {}",e.getMessage());
    }
    return resp;
}
Copy the code

wxPay.doWxPayApi(…) Encapsulates the quadratic verification of the signature:

public Map<String, Object> permissionValidate(String nonceStr, String url, String prepayId, String key) throws Exception {
    // JSSDK permission authentication parameters
    TreeMap<Object, Object> param = new TreeMap<>();
    Map<String, Object> data = new HashMap<>();
    param.put("appId", WeChatConstants.APP_ID);
    String timestamp = String.valueOf(WXPayUtil.getCurrentTimestamp());
    param.put("timestamp", timestamp);/ / all lowercase
    param.put("nonceStr", nonceStr);
    //map.put("signature",WeChatUtil.getSignature(timestamp,uuid,RequestUtil.getUrl(request)));
    param.put("signature", WeChatUtil.getSignature(timestamp, nonceStr, url));
    data.put("configMap", param);

    // Wechat Pay permission authentication parameters
    Map<String, String> payMap = new HashMap<>();
    payMap.put("appId", WeChatConstants.APP_ID);
    payMap.put("timeStamp", timestamp);/ / hump
    payMap.put("nonceStr", nonceStr);
    payMap.put("package"."prepay_id=" + prepayId);
    payMap.put("signType"."MD5");
    payMap.put("paySign", WXPayUtil.generateSignature(payMap, key));
    payMap.put("packageStr"."prepay_id=" + prepayId);
    data.put("payMap", payMap);

    return data;
}
Copy the code

Payment result notification and callback

Configure the controller of the callback interface:

@Controller
@RequestMapping("/api/v1/wechat1")
public class NotifyController {

    WxBackendServiceImpl wxBackendService;

    /** * When calling the single interface, we will pass in the callback address for asynchronously receiving wechat payment result notification, as the name implies, this address is used to receive payment result notification, * when the user pays successfully in the front end, the wechat server will automatically call this address, and then the merchant will process *@param request
     * @param response
     * @return* /
    @RequestMapping("/wxnotify")
    public String wxNotify(HttpServletRequest request, HttpServletResponse response) {
        String respXml = "";
        try (InputStream in = request.getInputStream();
             ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
            byte[] buffer = new byte[1024];
            int len = 0;
            while((len = in.read(buffer)) ! = -1) {
                baos.write(buffer, 0, len);
            }
            // Get the return message when wechat calls our notify_URL
            String notifyData = new String(baos.toByteArray(), "utf-8");
            // Callback processing
            respXml = wxBackendService.payCallBack(notifyData);
        } catch (Exception e) {
            WXPayUtil.getLogger().error("Wxnotify: Payment callback issue exception:", e.getMessage());
        } finally {
            try (BufferedOutputStream bos = new BufferedOutputStream(response.getOutputStream())){
                // The processing is complete
                bos.write(respXml.getBytes());
            } catch (IOException e) {
                WXPayUtil.getLogger().error("Wxnotify: Pay callback issue exception :out:", e.getMessage()); }}returnrespXml; }}Copy the code

Callback service:

public String payCallBack(String notifyData) throws Exception{
    // String respXml = WXPayConstants.RESP_FAIL_XML;
    Map<String, String> notifyMap = WXPayUtil.xmlToMap(notifyData);

    if (WXPayConstants.SUCCESS.equalsIgnoreCase(notifyMap.get(WXPayConstants.RESULT_CODE))) {
        WXPayUtil.getLogger().info("PayCallBack: wechat pay ---- return successful");
        if (WXPayUtil.isSignatureValid(notifyMap, WXPayConstants.API_KEY)) {
            // TODO database operation, payment record modification & record payment log
            WXPayUtil.getLogger().info("PayCallBack: wechat Pay ---- Verify signature success and update database");
            /*String outTradeNo = notifyMap.get("out_trade_no"); OrderTrading dbOrder = transactionService.findByOutTradeNo(outTradeNo); // Change the unpaid state to paid if (dbOrder! = null && dborder.getState () == 1) {OrderTrading order = new OrderTrading(); order.setOutTradeNo(outTradeNo); order.setNotifyTime(new Date()); order.setState(1); transactionService.updateTransOrderByWxnotify(order); // TODO database update exception, compensation measures}*/
            // Notify wechat. Asynchronous confirmation succeeded. Mandatory. Otherwise, they'll keep telling the background. Eight times before they call the deal dead.
            return WXPayConstants.RESP_SUCCESS_XML;
        } else {
            WXPayUtil.getLogger().error("PayCallBack: wechat pay ---- judge signature error"); }}else {
        WXPayUtil.getLogger().error("PayCallBack: payment failed, error message:" + notifyMap.get(WXPayConstants.ERR_CODE_DES));
    }
    return WXPayConstants.RESP_FAIL_XML;
}
Copy the code

A static page

PreOrder page: templates/ preorder.html

Confirm the order page: templates/ toorder.html

This page is used for signature verification and parameter passing. To facilitate observation, enable debugging mode

<! DOCTYPE html><html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>Immediate payment: 123</h1>

<button type="submit" id="payBtn">pay</button>

<script th:src="@ {/ static/js/jquery - 1.8.3. Min. Js}" type="text/javascript" charset="utf-8" rel="stylesheet"></script>
<script type="text/javascript" th:src="@{/static/js/jquery.rotate.min.js}" rel="stylesheet"></script>
<! -- wechat JSSDK-->
<script th:src="@ {http://res.wx.qq.com/open/js/jweixin-1.2.0.js}"></script>
<script>
    $(function() {<! Alert (alert(alert(alert(alert(alert(alert))))'[[${configMap}]]');
        alert('[[${payMap}]]');
        wx.config({
            debug: true.// Enable the debug mode
            appId: '[[${configMap.appId}]]'.// The unique identifier of the official account
            timestamp: '[[${configMap.timestamp}]]'.// Generate a timestamp for the signature
            nonceStr: '[[${configMap.nonceStr}]]'.// Generate a random string of signatures
            signature: '[[${configMap.signature}]]'./ / signature
            jsApiList: ['chooseWXPay'] // Fill in the list of JS interfaces that need to be used
        });

        <! -- config will call the code in ready when validation is successful -->Wx. ready(function(){$("#payBtn").click(function(){wx.choosewxpay ({timestamp: '[[${payMap. TimeStamp}]]', / / pay signature timeStamp, nonceStr: '[[${payMap. NonceStr}]]', / / pay signature random string, not longer than 32 bit package: '[[${paymap. packageStr}]]', // The value of prepay_id returned by the payment interface is in the format of: prepay_id= XXXX) signType: '[[${paymap.signtype}]]', 'MD5' paySign: '[[${paymap.signtype}]]', '${paymap.signtype}]]', 'MD5' paySign: ',' ${paymap.signtype}]]', '${payMap. Function (res) {alert(" Payment was successful! "); ); }}); })}); });</script>
</body>
</html>
Copy the code

Results demonstrate

After the project starts, click “Confirm payment” to see the display of parameters in debug mode. The final payment effect is as follows:

Note:

  1. The port for the payment callback must be 80, presumably for security reasons
  2. Web development tools can only be used for debugging, testing the payment function, need to be opened with mobile phone.
  3. Careful friends may see that the time of the order is more than a month early. This is a screenshot I developed with my company account and domain name.

The code in this article was intended to show the process of placing a single order, but it was not intended to be portable and has been refactored.

Please see the source code: github.com/chetwhy/clo…