Recently, I have been exposed to some things about WeChat payment in my work. I saw that the DEMOs I gave were all PHP versions, and I was really not satisfied with the WeChat payment documents. After experiencing many pitfalls, I was idle to make a summary.
1. Preparation
To develop WeChat, you must first apply for a public account. After the application is successful, you will be sent to you by email. The public account contains development documents, necessary information during development, and data query for testing.
2. Tools
1.MD5 encryption tool class
package com.pay.utils.weixin;import java.security.MessageDigest;public class MD5Util { public final static String MD5(String s) { char hexDigits[]={'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'}; try { byte[] btInput = s.getBytes(); // Get the MessageDigest object of the MD5 digest algorithm MessageDigest mdInst = MessageDigest.getInstance("MD5"); // Update the digest mdInst.update(btInput); // Get the ciphertext byte[] md = mdInst.digest(); // Convert the ciphertext into a hex string form int j = md.length; char str[] = new char[j * 2]; int k = 0; for (int i = 0; i < j; i++) { byte byte0 = md[i]; str[k++] = hexDigits[byte0 >>> 4 & 0xf]; str[k++] = hexDigits[byte0 & 0xf]; } return new String(str); } catch (Exception e) { e.printStackTrace(); return null; } }}2. CommonUtil tool class , used to replace the XML required for WeChat. The following return new String(xml.toString().getBytes(),"ISO8859-1"); Change utf-8 in the tool class to iso8859-1, otherwise the Chinese text in the WeChat order will appear garbled, and it can be displayed correctly after the change.
package com.pay.utils.weixin;import java.io.UnsupportedEncodingException;import java.net.URLEncoder;import java.util.*;import java.util.Map.Entry;public class CommonUtil { public static String CreateNoncestr(int length) { String chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; String res = ""; for (int i = 0; i < length; i++) { Random rd = new Random(); res += chars.indexOf(rd.nextInt(chars.length() - 1)); } return res; } public static String CreateNoncestr() { String chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; String res = ""; for (int i = 0; i < 16; i++) { Random rd = new Random(); res += chars.charAt(rd.nextInt(chars.length() - 1)); } return res; } public static String FormatQueryParaMap(HashMap<String, String> parameters) throws SDKRuntimeException { String buff = ""; try { List<Map.Entry<String, String>> infoIds = new ArrayList<Map.Entry<String, String>>( parameters.entrySet()); Collections.sort(infoIds, new Comparator<Map.Entry<String, String>>() { public int compare(Map.Entry<String, String> o1, Map.Entry<String, String> o2) { return (o1.getKey()).toString().compareTo( o2.getKey()); } }); for (int i = 0; i < infoIds.size(); i++) { Map.Entry<String, String> item = infoIds.get(i); if (item.getKey() != "") { buff += item.getKey() + "=" + URLEncoder.encode(item.getValue(), "utf-8") + "&"; } } if (buff.isEmpty() == false) { buff = buff.substring(0, buff.length() - 1); } } catch (Exception e) { throw new SDKRuntimeException(e.getMessage()); } return buff; } public static String FormatBizQueryParaMap(HashMap<String, String> paraMap, boolean urlencode) throws SDKRuntimeException { String buff = ""; try { List<Map.Entry<String, String>> infoIds = new ArrayList<Map.Entry<String, String>>( paraMap.entrySet()); Collections.sort(infoIds, new Comparator<Map.Entry<String, String>>() { public int compare(Map.Entry<String, String> o1, Map.Entry<String, String> o2) { return (o1.getKey()).toString().compareTo( o2.getKey()); } }); for (int i = 0; i < infoIds.size(); i++) { Map.Entry<String, String> item = infoIds.get(i); //System.out.println(item.getKey()); if (item.getKey() != "") { String key = item.getKey(); String val = item.getValue(); if (urlencode) { val = URLEncoder.encode(val, "utf-8"); } buff += key.toLowerCase() + "=" + val + "&"; } } if (buff.isEmpty() == false) { buff = buff.substring(0, buff.length() - 1); } } catch (Exception e) { throw new SDKRuntimeException(e.getMessage()); } return buff; } public static boolean IsNumeric(String str) { if (str.matches("//d *")) { return true; } else { return false; } } public static String ArrayToXml(HashMap<String, String> arr) { String xml = "<xml>"; Iterator<Entry<String, String>> iter = arr.entrySet().iterator(); while (iter.hasNext()) { Entry<String, String> entry = iter.next(); String key = entry.getKey(); String val = entry.getValue(); if (IsNumeric(val)) { xml += "<" + key + ">" + val + "</" + key + ">"; } else xml += "<" + key + "><![CDATA[" + val + "]]></" + key + ">"; } xml += "</xml>"; try { return new String(xml.toString().getBytes(),"ISO8859-1"); } catch (UnsupportedEncodingException e) { // TODO Auto-generated catch block e.printStackTrace(); } return ""; }}3.ClientCustomSSL tool class , used to generate signs and create WeChat order package com.pay.utils.weixin;
import java.util.ArrayList;import java.util.Collections;import java.util.Comparator;import java.util.HashMap;import java.util.List;import java.util.Map;import org.springframework.util.StringUtils;/** * This example demonstrates how to create secure connections with a custom SSL * context. */public class ClientCustomSSL { public static String GetBizSign(HashMap<String, String> bizObj) throws SDKRuntimeException { HashMap<String, String> bizParameters = new HashMap<String, String>(); List<Map.Entry<String, String>> infoIds = new ArrayList<Map.Entry<String, String>>( bizObj.entrySet()); System.out.println(infoIds); Collections.sort(infoIds, new Comparator<Map.Entry<String, String>>() { public int compare(Map.Entry<String, String> o1, Map.Entry<String, String> o2) { return (o1.getKey()).toString().compareTo(o2.getKey()); } });System.out.println("--------------------"); System.out.println(infoIds); for (int i = 0; i < infoIds.size(); i++) { Map.Entry<String, String> item = infoIds.get(i); if (item.getKey() != "") { bizParameters.put(item.getKey().toLowerCase(), item.getValue()); } } //bizParameters.put("key", "1234567812345678123456781234567812345671"); String bizString = CommonUtil.FormatBizQueryParaMap(bizParameters, false); bizString += "&key=123456781234567812345671"; System.out.println("***************"); System.out.println(bizString);// return SHA1Util.Sha1(bizString); return MD5Util.MD5(bizString); } /** * WeChat create order* @param nonceStr * @param orderDescribe * @param orderNo * @param price * @param timeStart * @param timeExpire * @return * @throws SDKRuntimeException */ public static String CreateNativePackage(String nonceStr,String orderDescribe,String orderNo,String price,String timeStart,String timeExpire) throws SDKRuntimeException { HashMap<String, String> nativeObj = new HashMap<String, String>(); nativeObj.put("appid", "see public account"); //Public account Id nativeObj.put("mch_id", "see email"); //Merchant number nativeObj.put("nonce_str", nonceStr); //Random string nativeObj.put("body", orderDescribe); //Product description nativeObj.put("attach", "tradeno"); //Attached data nativeObj.put("out_trade_no", orderNo); //Merchant order number (global unique) nativeObj.put("total_fee", price); //Total amount (unit is cent, cannot be taken with decimal points) nativeObj.put("spbill_create_ip","192.168.0.144"); //Terminal Ip nativeObj.put("time_start", timeStart); //Transaction start time nativeObj.put("time_expire", timeExpire); //Transaction end time nativeObj.put("notify_url", CustomizedPropertyPlaceholderConfigurer.getContextProperty("wxurl")+"/weixin_callback/weixinCallback/init.action"); //Callback notification address nativeObj.put("trade_type", "NATIVE"); //Transaction type String sign = GetBizSign(nativeObj); nativeObj.put("sign", sign.toUpperCase()); return CommonUtil.ArrayToXml(nativeObj); } /*** WeChat order payment query* @param nonceStr* @param orderDescribe* @param orderNo* @param price* @param timeStart* @param timeExpire* @return* @throws SDKRuntimeException*/public static String SearchNativePackage(String transactionId,String outTradeNo,String nonceStr) throws SDKRuntimeException {HashMap<String, String> nativeObj = new HashMap<String, String>();nativeObj.put("appid", "see public account"); //Public account IdnativeObj.put("mch_id", "see email"); // Merchant number nativeObj.put("nonce_str", nonceStr); // Random string if(!StringUtils.isEmpty(transactionId)){nativeObj.put("transaction_id", transactionId); }if(!StringUtils.isEmpty(outTradeNo)){nativeObj.put("out_trade_no", outTradeNo); // Random string}String sign = GetBizSign(nativeObj);nativeObj.put("sign", sign.toUpperCase()); return CommonUtil.ArrayToXml(nativeObj); /*** WeChat refund* @param outTradeNo* @param outRefundNo * @param totalFee* @param totalFee* @param refundFee* @return* @throws SDKRuntimeException*/public static String RefundNativePackage(String outTradeNo,String outRefundNo,String totalFee,String refundFee,String nonceStr) throws SDKRuntimeException {HashMap<String, String> nativeObj = new HashMap<String, String>();nativeObj.put("appid", "see public account");//Public account IdnativeObj.put("mch_id", "see email");//Merchant number nativeObj.put("nonce_str", nonceStr);//Random string nativeObj.put("out_trade_no", outTradeNo);//Merchant order number (global unique) nativeObj.put("out_refund_no", outRefundNo);//Merchant refund order number (global unique) nativeObj.put("total_fee", totalFee);//Total amount (unit in cents, cannot take decimal points) nativeObj.put("refund_fee", refundFee);//Refund amount (unit in cents, cannot take decimal points) nativeObj.put("op_user_id", "mail"); String sign = GetBizSign(nativeObj); nativeObj.put("sign", sign.toUpperCase());return CommonUtil.ArrayToXml(nativeObj);}/*** WeChat to be paid* @param nonceStr* @param orderDescribe* @param orderNo* @param price* @param timeStart* @param timeExpire* @return* @throws SDKRuntimeException*/public static String CreateJsApiPackage(String nonceStr,String orderDescribe,String orderNo,String price,String timeStart,String timeExpire,String openId) throws SDKRuntimeException {HashMap<String, String> nativeObj = new HashMap<String, String>();nativeObj.put("appid", "see public account");//Public account IdnativeObj.put("openid", openId);//Public account IdnativeObj.put("mch_id", "see email")//Merchant number nativeObj.put("nonce_str", nonceStr);//Random string nativeObj.put("body", orderDescribe);//Product description nativeObj.put("attach", "tradeno");//Attach data nativeObj.put("out_trade_no", orderNo);//Merchant order number (global unique) nativeObj.put("total_fee", price);//Total amount (unit is cent, cannot take decimal points) nativeObj.put("spbill_create_ip","192.168.0.144");//Terminal IpnativeObj.put("time_start", timeStart);//Transaction start time nativeObj.put("time_expire", timeExpire)//Transaction end time nativeObj.put("notify_url",CustomizedPropertyPlaceholderConfigurer.getContextProperty("wxurl")+"/weixin_callback/weixinCallback/init.action");//Notification address nativeObj.put("trade_type", "JSAPI");//Transaction type String sign = GetBizSign(nativeObj);nativeObj.put("sign", sign.toUpperCase()); return CommonUtil.ArrayToXml(nativeObj);}/*** Close order on WeChat* @param nonceStr* @param orderDescribe* @param orderNo* @param price* @param timeStart* @param timeExpire* @param openId* @return* @throws SDKRuntimeException*/public static String CreateCloseOrder(String outTradeNo,String nonceStr) throws SDKRuntimeException {HashMap<String, String> nativeObj = new HashMap<String, String>();nativeObj.put("appid", "See the public account");//Public account IdnativeObj.put("mch_id", "See the email");//Merchant number nativeObj.put("out_trade_no", outTradeNo);//Merchant order number (globally unique) nativeObj.put("nonce_str", nonceStr);//Random string String sign = GetBizSign(nativeObj);nativeObj.put("sign", sign.toUpperCase()); return CommonUtil.ArrayToXml(nativeObj);}} 4. Call the WeChat payment interface
package com.pay.controller.weixin;import java.io.File;import java.io.FileInputStream;import java.security.KeyStore;import java.text.SimpleDateFormat;import java.util.Date;import java.util.List;import javax.net.ssl.SSLContext;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import net.sf.json.JSONArray;import net.sf.json.JSONObject;import org.apache.http.HttpEntity;import org.apache.http.client.methods.CloseableHttpResponse;import org.apache.http.client.methods.HttpPost;import org.apache.http.conn.ssl.SSLConnectionSocketFactory;import org.apache.http.conn.ssl.SSLContexts;import org.apache.http.entity.StringEntity;import org.apache.http.impl.client.CloseableHttpClient;import org.apache.http.impl.client.HttpClients;import org.apache.http.util.EntityUtils;import org.dom4j.Document;import org.dom4j.DocumentHelper;import org.dom4j.Element;import org.dom4j.io.SAXReader;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.http.HttpStatus;import org.springframework.web.bind.annotation.RequestBody;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestMethod;import org.springframework.web.bind.annotation.ResponseStatus;import org.springframework.web.bind.annotation.RestController;import com.pay.bo.PayHistants.import com.pay.constants.PayHistoryPayStatus;import com.pay.constants.PayHistoryPayType;import com.pay.service.WeiXinPayService;import com.pay.utils.weixin.ClientCustomSSL;import com.pay.utils.weixin.CloseWeiXinOrderUtils;import com.pay.utils.weixin.CustomizedPropertyPlaceholderConfigurer;@RestController@RequestMapping("/Pay")public class WeiXinPayController { @Autowired WeiXinPayService weiXinPayService; private static long standardTime = 1662652800000L; /** * Return the url that generates the QR code * @param request * @param response * @return */ @RequestMapping(value="/getUrl",method=RequestMethod.POST) @ResponseStatus(HttpStatus.OK) public Object getUrl(HttpServletResponse response,@RequestBody String body){ try{ JSONObject jsonO = JSONObject.fromObject(body); PayHist ph = null;// List<Map<String,Object>> td = weiXinPayService.getTrade(orderNo); Date dt = new Date(); SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss"); String nonceStr = sdf.format(dt).toString(); Date now = new Date(); String tradePayNo = jsonO.get("orderNo").toString()+String.format("%10d",standardTime - now.getTime()).substring(0, 10); System.out.println("订单标号orderNo======="+jsonO.get("orderNo").toString()); System.out.println("10位随机数======="+String.format("%10d",standardTime - now.getTime()).substring(0, 10)); String price = Math.round(Float.valueOf(jsonO.get("price").toString())*100)+""; Long timeExpireStrOld = dt.getTime(); Long timeNew = Long.parseLong(CustomizedPropertyPlaceholderConfigurer.getContextProperty("weixin.send2finish.overtime").toString()); Long timeExpireNew = timeExpireStrOld+timeNew; Date dtTimeExpire = new Date(timeExpireNew); SimpleDateFormat dtSdf = new SimpleDateFormat("yyyyMMddHHmmss"); String timeExpire = dtSdf.format(dtTimeExpire).toString(); System.out.println("nonceStr=="+nonceStr); System.out.println("orderNo=="+jsonO.get("orderNo").toString()); System.out.println("price=="+price); System.out.println("timeStart=="+nonceStr); System.out.println("timeExpire=="+timeExpire); JSONObject result = (JSONObject) setUrl(nonceStr,"Order",tradePayNo,price,nonceStr,timeExpire); if(result.get("status").toString().equals("success")){ ph = new PayHist(); ph.setTradePayUrl(result.getString("weixinPayUrl"));//This field is a payment link. You can generate a QR code to scan the code to pay ph.setPayTradeNo(jsonO.get("orderNo").toString()); ph.setTradePayNo(tradePayNo); ph.setPayStatus(PayHistoryPayStatus.WECHAT_PAY_STATUS_WAIT); ph.setPayType(PayHistoryPayType.WECHAT); ph.setAppKey(jsonO.getString("appKey").toString()); ph.setPayAmount(price); result.put("payTradeNo", ph.getPayTradeNo()); result.put("tradePayNo", ph.getTradePayNo()); result.put("payStatus", ph.getPayStatus()); result.put("payType", ph.getPayType()); } return result; }catch(Exception e){ e.printStackTrace(); JSONObject result = new JSONObject(); result.put("status","error"); result.put("msg",e.getMessage());// return result.toString(); } return null; } public Object setUrl(String nonceStr,String orderDescribe,String orderNo,String price,String timeStart,String timeExpire) { try{ KeyStore keyStore = KeyStore.getInstance("PKCS12"); FileInputStream Instream = new FileInputStream(new File(absolute path of WeChat certificate)); try { keyStore.load(instream, "Merchant ID".toCharArray()); } finally { enterstream.close(); } // Trust own CA and all self-signed certs SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore,<span style="font-family: Arial, Helvetica, sans-serif;">Merchant ID</span>.toCharArray()).build(); // Allow TLSv1 protocol only SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext, new String[] { "TLSv1" }, null, SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER); CloseableHttpClient httpclient = HttpClients.custom() .setSSLSocketFactory(sslsf).build(); // HttpGet httpget = new // HttpGet("https://api.mch.weixin.qq.com/secapi/pay/refund"); HttpPost httppost = new HttpPost( "https://api.mch.weixin.qq.com/pay/unifiedorder"); String xml = ClientCustomSSL.CreateNativePackage(nonceStr,orderDescribe,orderNo,price,timeStart,timeExpire); try { StringEntity se = new StringEntity(xml); httppost.setEntity(se); System.out.println("executing request" + httppost.getRequestLine()); CloseableHttpResponse responseEntry = httpclient.execute(httppost); try { HttpEntity entity = responseEntry.getEntity(); System.out.println("----------------------------------------"); System.out.println(responseEntry.getStatusLine()); if (entity != null) { System.out.println("Response content length: " + entity.getContentLength()); /* BufferedReader bufferedReader = new BufferedReader( new InputStreamReader(entity.getContent())); String text; while ((text = bufferedReader.readLine())) != null) { System.out.println("=================="+text); }*/ SAXReader saxReader = new SAXReader(); Document document = saxReader.read(entity.getContent()); Element rootElt = document.getRootElement(); System.out.println("root node:" + rootElt.getName()); System.out.println("==="+rootElt.elementText("result_code")); System.out.println("==="+rootElt.elementText("return_msg")); String resultCode = rootElt.elementText("result_code"); JSONObject result = new JSONObject(); Document documentXml =DocumentHelper.parseText(xml); Element rootEltXml = documentXml.getRootElement(); if(resultCode.equals("SUCCESS")){ System.out.println("=================prepay_id===================="+ rootElt.elementText("prepay_id")); System.out.println("=================sign===================="+ rootEltXml.elementText("sign")); result.put("weixinPayUrl", rootElt.elementText("code_url")); result.put("prepayId", rootElt.elementText("prepay_id")); result.put("status","success"); result.put("msg","success"); }else{ result.put("status","false"); result.put("msg",rootElt.elementText("err_code_des")); } return result; } EntityUtils.consume(entity); } finally { responseEntry.close(); } } finally { httpclient.close(); } return null; } catch(Exception e){ e.printStackTrace(); JSONObject result = new JSONObject(); result.put("status","error"); result.put("msg",e.getMessage()); return result; } } } httpclient jar package and json jar package: download address.
The above is all the content of this article. I hope it will be helpful to everyone's learning and I hope everyone will support Wulin.com more.