WeChat payment for H5 app development

Foreword, WeChat payment can be divided into several payment methods as shown in the following figure according to the actual situation:

        However, if you use an app developed with H5, for login or payment, whether it belongs to H5 login/payment or app login/payment, and whether you need to apply for the mobile terminal or the web terminal when applying, I have not yet figured out, but it is currently practiced. As a result, on the WeChat open platform, applying for a mobile application and then enabling WeChat login and payment permissions can realize WeChat login and payment in apps developed with H5 technology.

Closer to home, the current implementation of WeChat payment uses a page made of H5+vue and packaged into an app with HBuilder. The effect is that when paying at the midpoint of the app, call up the WeChat client and enter the password to realize the payment function. In this process, if you want to use the app made by H5 to activate WeChat, you can’t do without a thing called HTML5 plus. Specifically, what is this connection: https://www.cnblogs.com/gzhjj/p/11903773.html , to find this is the best choice to implement the current function, in which the plus.payment method is used for payment, which will be discussed in detail later.

1. First of all, let's talk about the overall process of WeChat payment with plus.payment:

(1), the app initiates a payment request to its own server

(2) Encapsulate the corresponding data content on your own server and request the WeChat server to place an order (that is, to obtain the order data)

(3), the own server returns the order data obtained from the WeChat server to the app

(4) The app gets the order data and initiates a request to the WeChat server through the method in plus.payment. After the request is successful, the WeChat client will be activated to enter the password to pay.

2. Front-end and back-end code implementation process and explanation:

        (1) The front-end mainly uses the vue framework, and the payment function is mainly implemented based on the two methods of plus.payment.getChannels and plus.payment.request of plus.payment in html5+. The former mainly obtains the payment channel, and the latter sends to the WeChat client Initiate a payment request. For the specific introduction of plus.payment, please refer to the official website: http://www.html5plus.org/doc/zh_cn/payment.html

The specific code is as follows:

a. Click the payment button and select WeChat payment:

<div class="zyy_payBox tab">
    <div class="WeChat opt" @click="wxPay">
        <input class="magic-radio" type="radio" name="radio" id="r1" value="option1" checked="">
        <label for="r1"><img src="../../assets/images/WeChat.png" >WeChat Pay</label>
    </div>
    <div class="Alipay opt" @click="aliPay">
        <input class="magic-radio" type="radio" name="radio" id="r2" value="option2">
        <label for="r2"><img src="../../assets/images/Alipay.png" >Pay with Ali-Pay</label>
    </div>
</div>

b. Click the WeChat payment button to call the following method

wxPay(){
    var _self = this;
    //After the device information is loaded, first obtain the WeChat payment channel
    var payChanel;
	  plus.payment.getChannels(function(channels) {
	   for (var i in channels) {
	    if (channels[i].id == "wxpay") {
          payChanel = channels[i];
	    }
	   }
	  }, function(e) {
	   alert("Failed to get payment channel:" + e.message);
    });
    //Request the backend server, and request the order data from the WeChat client through the backend server
    toWxpay({ "orderId": this.orderId}).then(res => {
      //_self.$toast("Background return data:"+JSON.stringify(res.data.resp));
	  //The background obtains the order data, and the front desk encapsulates it in json format and sends it to the request method to the WeChat server to initiate a payment request. After the request is successful, the WeChat client will be activated to enter the password to perform the payment operation.
	   plus.payment.request(payChanel, JSON.stringify(res.data.resp), function(result) {
	   //The payment is successful, the processing part of its own business logic
        _self.$toast(JSON.stringify("Paid successfully"));
        updateOrderStatus({ "orderId": this.orderId, "orderStatus": 'order_paid_noShipped' }).then(res => {//Payment successful, to be shipped
          _self.$toast(res.data.errmsg);
          this.$router.push({
            path: "/mall/myorder",
            query: {
              orderType: "order_paid_noShipped",
              index:2
            }
          });
        });
	   }, function(e) {
	    _self.$toast("Payment failed");
      });
    })
},

(2) The backend service is requested through the toWxpay({ "orderId": this.orderId}). method on the front end, and the backend service calls the interface method in the sdk provided by WeChat to obtain the order data. The main backend code is as follows:

a. Introduce the official sdk jar package, which provides an interface to request the WeChat server to obtain order data

<!--WeChat Pay -->
		<dependency>
			<groupId>com.github.wxpay</groupId>
			<artifactId>wxpay-sdk</artifactId>
			<version>0.0.3</version>
		</dependency>

b. Encapsulate the request data that requests the WeChat server to place an order, and then initiate a request to the WeChat server to obtain the order data. The code is as follows:

    @RequestMapping(value = "/toWxpay")
	@ResponseBody
	public Object toWxpay(@RequestParam(value = "orderId") String orderId, HttpServletRequest request) {
		Orders order = ordersService.selectById(orderId);
		BigDecimal totalFee = new BigDecimal(order.getOrderTotalPrice()).multiply(new BigDecimal(100)).setScale(0, BigDecimal.ROUND_DOWN);
		try {
            //Load certificate file
            //The certificate file is the security certificate file provided by the WeChat Payer when applying for WeChat Pay, namely the API certificate
			WxPaymentUtils config = new WxPaymentUtils();
			WXPay wxpay = new WXPay(config);
            //-------Start to encapsulate the interface parameters for calling the WeChat ordering interface------
			Map<String, String> data = new HashMap<String, String>();
			// The format of the commodity description transaction field is as follows according to different application scenarios:
			// APP——The name of the APP on the application market needs to be passed in - the actual product name, every day love elimination - game recharge.
			data.put("body", SystemProperties.getProperty(SystemProperties.APP_NAME)+"-"+order.getProductBaseInfoModelList().get(0).getProductName());
			// The internal order number of the merchant system, within 32 characters, can only be numbers, uppercase and lowercase letters _-|* and is unique under the same merchant number. See Merchant Order Number for details
			data.put("out_trade_no", orderId);
			// The total amount of the order, in cents, see payment amount for details
			data.put("total_fee", totalFee.toString());
			// Supports IP addresses in both IPV4 and IPV6 formats. The IP of the machine that calls the WeChat payment API
			data.put("spbill_create_ip", IpUtil.getIp(request));//"123.12.12.123"
			// Receive the WeChat payment asynchronous notification callback address. The notification url must be a directly accessible url and cannot carry parameters.
			data.put("notify_url", "http://221.207.19.231:8080");
			// Payment type, here designated as scan code payment NATIVE APP
			data.put("trade_type", "APP");
            //--------End Encapsulate the interface parameters for calling the WeChat ordering interface------

            //Call the order interface to request the WeChat server
			Map<String, String> resp = wxpay.unifiedOrder(data);
			String returnCode = resp.get("return_code");
			String resultCode = resp.get("result_code");
			System.out.println(resp);
			// When the field return_code and result_code are both SUCCESS, the order is successful
			if (StringUtils.equals(returnCode, "SUCCESS") && StringUtils.equals(resultCode, "SUCCESS")) {
                
				resp.put("timestamp", (System.currentTimeMillis() / 1000) + "");
				resp.put("nonce_str", WXPayUtil.generateNonceStr());
                //------Start Get and encapsulate WeChat order data-------
				HashMap<String, String> map = new HashMap<>();
				map.put("appid", resp.get("appid"));
				// business number
				map.put("partnerid", resp.get("mch_id"));
				map.put("prepayid", resp.get("prepay_id"));
				map.put("package", "Sign=WXPay");
				map.put("noncestr", resp.get("nonce_str"));
				map.put("timestamp", resp.get("timestamp"));
                //SystemProperties.getProperty(SystemProperties.WX_KEY) is the API key in applying for WeChat Pay
				String sign = WXPayUtil.generateSignature(map, SystemProperties.getProperty(SystemProperties.WX_KEY),
						WXPayConstants.SignType.MD5);
				resp.put("sign", sign);
				resp.put("accountType", "WeChat");
				
				map.put("sign", sign);
				logger.info("[unified order API Generate order information]:{}=", map);
				//--------End Wechat order data obtained by encapsulation------
				Map<String, Object> obj = new HashedMap<String, Object>();
				obj.put("resp", map);
				obj.put("errno", 0);
				obj.put("errmsg", "success");
				return obj;
			} else {
				return null;
			}
		} catch (Exception e) {
			e.printStackTrace();
			logger.error("[Request WeChat unified order API fail]{}=", e.getMessage());
			return null;
		}
	}

The WxPaymentUtils tool method code in the code above is as follows:

public class WxPaymentUtils implements WXPayConfig {
	private static Logger log = LoggerFactory.getLogger(WxPaymentUtils.class);
	private static final String NOTIFY_URL = "";

	private byte[] certData;

	/**
	 * Load certificate file
	 */
	public WxPaymentUtils() throws Exception {
		InputStream certStream1 = this.getClass().getResourceAsStream("apiclient_cert.p12");
		log.info("certStream1=="+certStream1);
		File file = new File("apiclient_cert.p12");
		InputStream certStream = new FileInputStream(file);
		log.info("certStream=="+certStream);
		this.certData = new byte[certStream.available()];
		certStream.read(this.certData);
		certStream.close();
	}

	/**
	 * WeChat application appid
	 */
	@Override
	public String getAppID() {
		return SystemProperties.getProperty(SystemProperties.WX_APPID);
	}

	/**
	 * WeChat merchant id
	 */
	@Override
	public String getMchID() {
		return SystemProperties.getProperty(SystemProperties.WX_MCHID);
	}

	/**
	 * WeChat background configuration key
	 */
	@Override
	public String getKey() {
		return SystemProperties.getProperty(SystemProperties.WX_KEY);
	}

	@Override
	public InputStream getCertStream() {
		ByteArrayInputStream certBis = new ByteArrayInputStream(this.certData);
		return certBis;
	}

	@Override
	public int getHttpConnectTimeoutMs() {
		return 8000;
	}

	@Override
	public int getHttpReadTimeoutMs() {
		return 10000;
	}
}

 

Posted by Mieke23 on Wed, 04 May 2022 04:17:01 +0300