Java Spring MVC Implements the Complete Edition of Web Payment on PC

Posted by phpmady on Fri, 07 Jun 2019 23:37:02 +0200

First: Previous period WeChat Payment of literacy knowledge

The prerequisite is that we have already applied for the public number of Wechat payment function, and then we need to get the public number APPID and the Wechat business number, which can be found on the Wechat public number and the Wechat payment business platform respectively. In fact, after you apply for payment function successfully, Wechat will transfer Mail to you by mail. With this information, we can go to the support page of Wechat Payment Service: https://pay.weixin.qq.com/service_provider/index.shtml.

Open this page and click on the link at the top right [Development Document] to go to the API Document Description page, which looks like this


Choose red circle sweep code payment is that we have to do access mode, mouse move to the above will prompt you to view the development documents, if this do not know how to view, you can wash and sleep, you really do not fit to be a programmer, address as follows:

https://pay.weixin.qq.com/wiki/doc/api/native.PHP chapter=6_1 opens in the browser and you will see it


We should focus on and read the content I have marked with red ellipse, first read the protocol specifications in the [interface rules], joke that you want to do Wechat Payment without reading this, this is like you want to pick up a girl, you have to collect some basic background information, understand each other's characteristics, otherwise how to communicate below. It turns out that only programmers who are good at picking up girls are good sellers. Let's take a look at the cases and specifications in Scenario Introduction. Just look at the LOGO downloaded from Wechat Payment to put it on our own Scanning Payment Web page. It looks more professional. Then focus on [Mode 2]

We are here to adopt mode 2 to realize the function of page scanning and payment on PC side.

The official explanation for mode 2 is as follows: "The business background system first calls the unified single interface of Weichat payment, and the Weichat background system returns the link parameter code_url. The business background system generates the code_url value into a two-dimensional code picture, and the user uses the Weichat client to scan the code before paying. Note: code_url is valid for 2 hours and cannot initiate payment after expiration. You see, first we need to call Wechat to provide a unified single interface and get a key information code_url (as to what the code_url is, I don't know), then we use our own program to generate a two-dimensional code, which I use Google's zxing library. Then display the two-dimensional code on your PC page. In this way, the end user will pay as soon as he scans the code, and the payment will be completed. You must be very excited to see here. You will find that Weixin payment is so simple, and so on. There is another thing we don't know yet. The customer knows how to pay, but we don't know the server side. Wechat Development Personnel's IQ has been thinking about this problem for a long time, so when you call the unified single interface, one of the required parameters is the callback URL, that is, if the client payment succeeds, Wechat will submit some data to our own server through this URL, and then we will parse the data in the background to complete our own operation. Only then can we know whether the customer has actually paid through Wechat. This is the end of the whole process. This is Mode 2. Micro Credit is a time series diagram that represents this process.


The expression is more complicated and seems to be more laborious. To sum up what our server should do is as follows:

1. To get the corresponding data of code_url from the returned data, the correct parameters (including our callback URL, of course) and signature verification are passed through the unified single interface.

2. According to the data of code_url, we generate a two-dimensional code picture and display it on the browser's web page.

3. Add our own business logic processing to the callback URL.

 

Now that literacy is over, you finally know what kind of process the scanner pays for. Now let's work together to pick up its API usage and do a good job in every step.

II: Development process

Before you develop your code, prepare a few things.

1. Adding ZXing's maven dependency

2. Adding jdom's maven dependencies

3. Download Java Version SDK demo program, address here

https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=11_1

We need two files, MD5Util.java and XMLUtil.java.

4. We use HttpClient version 4.5.1, remember to add Maven dependencies

After the above preparations have been completed, continue to look down:

First of all, we need to call the unified order interface of Wechat. We click on the unified order in the API list to see this page:


For example, I call the actual situation, the following parameters are necessary, for your convenience, I have turned it into a POJO object, the code is as follows:

  1. public class UnifiedorderDto implements WeiXinConstants {  
  2.   
  3.     private String appid;  
  4.     private String body;  
  5.     private String device_info;  
  6.     private String mch_id;  
  7.     private String nonce_str;  
  8.     private String notify_url;  
  9.     private String openId;  
  10.     private String out_trade_no;  
  11.     private String spbill_create_ip;  
  12.     private int total_fee;  
  13.     private String trade_type;  
  14.     private String product_id;  
  15.     private String sign;  
  16.       
  17.     public UnifiedorderDto() {  
  18.         this.appid = APPID;  
  19.         this.mch_id = WXPAYMENTACCOUNT;  
  20.         this.device_info = DEVICE_INFO_WEB;  
  21.         this.notify_url = CALLBACK_URL;  
  22.         this.trade_type = TRADE_TYPE_NATIVE;  
  23.     }  
  24.   
  25.     public String getAppid() {  
  26.         return appid;  
  27.     }  
  28.   
  29.     public void setAppid(String appid) {  
  30.         this.appid = appid;  
  31.     }  
  32.   
  33.     public String getBody() {  
  34.         return body;  
  35.     }  
  36.   
  37.     public void setBody(String body) {  
  38.         this.body = body;  
  39.     }  
  40.   
  41.     public String getDevice_info() {  
  42.         return device_info;  
  43.     }  
  44.   
  45.     public void setDevice_info(String device_info) {  
  46.         this.device_info = device_info;  
  47.     }  
  48.   
  49.     public String getMch_id() {  
  50.         return mch_id;  
  51.     }  
  52.   
  53.     public void setMch_id(String mch_id) {  
  54.         this.mch_id = mch_id;  
  55.     }  
  56.   
  57.     public String getNonce_str() {  
  58.         return nonce_str;  
  59.     }  
  60.   
  61.     public void setNonce_str(String nonce_str) {  
  62.         this.nonce_str = nonce_str;  
  63.     }  
  64.   
  65.     public String getNotify_url() {  
  66.         return notify_url;  
  67.     }  
  68.   
  69.     public void setNotify_url(String notify_url) {  
  70.         this.notify_url = notify_url;  
  71.     }  
  72.   
  73.     public String getOpenId() {  
  74.         return openId;  
  75.     }  
  76.   
  77.     public void setOpenId(String openId) {  
  78.         this.openId = openId;  
  79.     }  
  80.   
  81.     public String getOut_trade_no() {  
  82.         return out_trade_no;  
  83.     }  
  84.   
  85.     public void setOut_trade_no(String out_trade_no) {  
  86.         this.out_trade_no = out_trade_no;  
  87.     }  
  88.   
  89.     public String getSpbill_create_ip() {  
  90.         return spbill_create_ip;  
  91.     }  
  92.   
  93.     public void setSpbill_create_ip(String spbill_create_ip) {  
  94.         this.spbill_create_ip = spbill_create_ip;  
  95.     }  
  96.   
  97.     public int getTotal_fee() {  
  98.         return total_fee;  
  99.     }  
  100.   
  101.     public void setTotal_fee(int total_fee) {  
  102.         this.total_fee = total_fee;  
  103.     }  
  104.   
  105.     public String getTrade_type() {  
  106.         return trade_type;  
  107.     }  
  108.   
  109.     public void setTrade_type(String trade_type) {  
  110.         this.trade_type = trade_type;  
  111.     }  
  112.   
  113.     public String getSign() {  
  114.         return sign;  
  115.     }  
  116.   
  117.     public void setSign(String sign) {  
  118.         this.sign = sign;  
  119.     }  
  120.   
  121.     public String getProduct_id() {  
  122.         return product_id;  
  123.     }  
  124.   
  125.     public void setProduct_id(String product_id) {  
  126.         this.product_id = product_id;  
  127.     }  
  128.     public String generateXMLContent() {  
  129.         String xml = "<xml>" +  
  130.            "<appid>" + this.appid + "</appid>" +   
  131.            "<body>" + this.body + "</body>" +   
  132.            "<device_info>WEB</device_info>" +   
  133.            "<mch_id>" + this.mch_id + "</mch_id>" +   
  134.            "<nonce_str>" + this.nonce_str + "</nonce_str>" +  
  135.            "<notify_url>" + this.notify_url + "</notify_url>" +   
  136.            "<out_trade_no>" + this.out_trade_no + "</out_trade_no>" +   
  137.            "<product_id>" + this.product_id + "</product_id>" +  
  138.            "<spbill_create_ip>" + this.spbill_create_ip+ "</spbill_create_ip>" +  
  139.            "<total_fee>" + String.valueOf(this.total_fee) + "</total_fee>" +   
  140.            "<trade_type>" + this.trade_type + "</trade_type>" +   
  141.            "<sign>" + this.sign + "</sign>" +   
  142.         "</xml>";  
  143.         return xml;  
  144.     }  
  145.       
  146.     public String makeSign() {  
  147.         String content ="appid=" + this.appid +   
  148.                    "&body=" + this.body +   
  149.                    "&device_info=WEB" +   
  150.                    "&mch_id=" + this.mch_id +   
  151.                    "&nonce_str=" + this.nonce_str +   
  152.                    "¬ify_url=" + this.notify_url +  
  153.                    "&out_trade_no=" + this.out_trade_no +   
  154.                    "&product_id=" + this.product_id +   
  155.                    "&spbill_create_ip=" + this.spbill_create_ip+  
  156.                    "&total_fee=" + String.valueOf(this.total_fee) +  
  157.                    "&trade_type=" + this.trade_type;  
  158.         content = content + "&key=" + WeiXinConstants.MD5_API_KEY;  
  159.         String esignature = WeiXinPaymentUtil.MD5Encode(content, "utf-8");  
  160.         return esignature.toUpperCase();  
  161.     }  
  162.       
  163. }  

The explanation of each member variable can be referred to the explanation of Unified Single Interface.

After that, we have to fill in the content we want to set, call the interface to get the return data, get the code_url data from it, and then generate a two-dimensional picture, return the address of the picture to the PC-side web page, and then it will be displayed. Here is a special note, our own PC-side web page will be invoked by ajax when we click on Wechat Payment. Our own backstage Spring MVC Controller then uses HTTP Client to complete the parsing of the returned XML data from a single interface under the unification of Wechat to get the value of code_url, generate two-dimensional code and return it to the front page through the corresponding method of Controller. The code implemented in Controller is as follows:

  1. Map<String,Object> result=new HashMap<String,Object>();  
  2.         UnifiedorderDto dto = new UnifiedorderDto();  
  3.         if(cash == null || "".equals(cash)) {  
  4.             result.put("error""cash could not be zero");  
  5.             return result;  
  6.         }  
  7.         int totalfee = 100*Integer.parseInt(cash);  
  8.         logger.info("total recharge cash : " + totalfee);  
  9.         dto.setProduct_id(String.valueOf(System.currentTimeMillis()));  
  10.         dto.setBody("repair");  
  11.         dto.setNonce_str(String.valueOf(System.nanoTime()));  
  12.         LoginInfo loginInfo = LoginInfoUtil.getLoginInfo();  
  13.         //Identify by our backend order number + UUID  
  14.         dto.setOut_trade_no("Your order number+Key information, which you can verify, is returned after Wechat callback.");  
  15.         dto.setTotal_fee(totalfee);  
  16.         dto.setSpbill_create_ip("127.0.0.1");  
  17.         // generate signature  
  18.         dto.setSign(dto.makeSign());  
  19.         logger.info("sign : " + dto.makeSign());  
  20.         logger.info("xml content : " + dto.generateXMLContent());  
  21.         try {  
  22.             HttpClient httpClient = HttpClientBuilder.create().build();   
  23.             HttpPost post = new HttpPost(WeiXinConstants.UNIFIEDORDER_URL);  
  24.             post.addHeader("Content-Type""text/xml; charset=UTF-8");  
  25.             StringEntity xmlEntity = new StringEntity(dto.generateXMLContent(), ContentType.TEXT_XML);  
  26.             post.setEntity(xmlEntity);  
  27.             HttpResponse httpResponse = httpClient.execute(post);  
  28.             String responseXML = EntityUtils.toString(httpResponse.getEntity(), "UTF-8");  
  29.             logger.info("response xml content : " + responseXML);  
  30.             // parse CODE_URL CONTENT  
  31.             Map<String, String> resultMap = (Map<String, String>)XMLUtil.doXMLParse(responseXML);  
  32.             logger.info("response code_url : " + resultMap.get("code_url"));  
  33.             String codeurl = resultMap.get("code_url");  
  34.             if(codeurl != null && !"".equals(codeurl)) {  
  35.                 String imageurl = generateQrcode(codeurl);  
  36.                 result.put("QRIMAGE", imageurl);  
  37.             }  
  38.             post.releaseConnection();  
  39.         } catch(Exception e) {  
  40.             e.printStackTrace();  
  41.         }  
  42.         result.put("success""1");  
  43.         return result;  

The code to generate the two-dimensional code is as follows:

  1. private String generateQrcode(String codeurl) {  
  2.     File foldler = new File(basePath + "qrcode");  
  3.     if(!foldler.exists()) {  
  4.         foldler.mkdirs();  
  5.     }  
  6.       
  7.     String f_name = UUIDUtil.uuid() + ".png";  
  8.        try {  
  9.         File f = new File(basePath + "qrcode", f_name);  
  10.         FileOutputStream fio = new FileOutputStream(f);  
  11.         MultiFormatWriter multiFormatWriter = new MultiFormatWriter();  
  12.         Map hints = new HashMap();  
  13.         hints.put(EncodeHintType.CHARACTER_SET, "UTF-8"); //Set the character set encoding type  
  14.         BitMatrix bitMatrix = null;  
  15.            bitMatrix = multiFormatWriter.encode(codeurl, BarcodeFormat.QR_CODE, 300300,hints);  
  16.            BufferedImage image = toBufferedImage(bitMatrix);  
  17.            //Output Two-Dimensional Code Picture Stream  
  18.            ImageIO.write(image, "png", fio);  
  19.            return ("qrcode/" + f_name);  
  20.        } catch (Exception e1) {  
  21.            e1.printStackTrace();  
  22.            return null;  
  23.        }       
  24. }  
At this point, after the client's Wechat scanner, Wechat will return the data to us through the callback URL. To complete our own processing in the callback method, we should pay special attention to that your callback interface must be implemented through HTTP POST method, otherwise we can not accept XML data. The code for callback processing is as follows:

  1. @RequestMapping(value = "/your_callback_url", method = RequestMethod.POST)  
  2. @ResponseBody  
  3. public void finishPayment(HttpServletRequest request, HttpServletResponse response) {  
  4.     try {  
  5.         logger.info("start to callback from weixin server: " + request.getRemoteHost());  
  6.         Map<String, String> resultMap = new HashMap<String, String>();  
  7.         InputStream inputStream = request.getInputStream();  
  8.         //Read the input stream  
  9.         SAXBuilder saxBuilder= new SAXBuilder();  
  10.         Document document = saxBuilder.build(inputStream);  
  11.         //Get the xml root element  
  12.         Element root = document.getRootElement();  
  13.         //Get all the child nodes of the root element  
  14.         List list = root.getChildren();  
  15.         Iterator it = list.iterator();  
  16.         while(it.hasNext()) {  
  17.             Element e = (Element) it.next();  
  18.             String k = e.getName();  
  19.             String v = "";  
  20.             List children = e.getChildren();  
  21.             if(children.isEmpty()) {  
  22.                 v = e.getTextNormalize();  
  23.             } else {  
  24.                 v = XMLUtil.getChildrenText(children);  
  25.             }  
  26.             resultMap.put(k, v);  
  27.         }  
  28.           
  29.         //Verify signature!!!  
  30.         /* 
  31.         String[] keys = resultMap.keySet().toArray(new String[0]); 
  32.         Arrays.sort(keys); 
  33.         String kvparams = ""; 
  34.         for(int i=0; i<keys.length; i++) { 
  35.             if(keys[i].equals("esign")) { 
  36.                 continue; 
  37.             } 
  38.             // signature algorithm 
  39.             if(i == 0) { 
  40.                 kvparams += (keys[i] + "=" + resultMap.get(keys[i])); 
  41.             } else { 
  42.                 kvparams += ("&" + keys[i] + "=" + resultMap.get(keys[i])); 
  43.             } 
  44.         } 
  45.         String esign = kvparams + "&key=" + WeiXinConstants.MD5_API_KEY; 
  46.         String md5esign = WeiXinPaymentUtil.MD5Encode(esign, "UTF-8"); 
  47.         if(!md5esign.equals(resultMap.get("sign"))) { 
  48.             return; 
  49.         }*/  
  50.           
  51.         //Closing flow  
  52.         //Releasing resources  
  53.         inputStream.close();  
  54.         inputStream = null;  
  55.         String returnCode = resultMap.get("return_code");  
  56.         String outtradeno = resultMap.get("out_trade_no");  
  57.         //Divided into units  
  58.         int nfee = Integer.parseInt(resultMap.get("total_fee"));  
  59.         logger.info("out trade no : " + outtradeno);  
  60.         logger.info("total_fee : " + nfee);  
  61.         //Business process  
  62.         if("SUCCESS".equals(returnCode)) {                
  63.             // TODO: your business process add here  
  64.             response.getWriter().print(XMLUtil.getRetResultXML(resultMap.get("return_code"), resultMap.get("return_code")));  
  65.         } else {  
  66.             response.getWriter().print(XMLUtil.getRetResultXML(resultMap.get("return_code"), resultMap.get("return_msg")));  
  67.         }  
  68.     }  
  69.     catch(IOException ioe) {  
  70.         ioe.printStackTrace();  
  71.     } catch (JDOMException e1) {  
  72.         e1.printStackTrace();  
  73.     }  
  74. }  

The two classes of XMLUtil and MD5Util used by Wechat's official Java version of Demo remember to take them and change them. The demo code can be found on its official demo page. The maven dependencies are as follows:

  1. <dependency>  
  2.     <groupId>jdom</groupId>  
  3.     <artifactId>jdom</artifactId>  
  4.     <version>1.1</version>  
  5. </dependency>  
  6. <dependency>  
  7.     <groupId>com.google.zxing</groupId>  
  8.     <artifactId>core</artifactId>  
  9.     <version>3.3.0</version>  
  10. </dependency>  

Finally, we should pay special attention to the signature. I got the class of MD5 generated by signature from the Java Demo program downloaded directly from the official website of Wechat. I suggest you do the same, because this is the best choice to ensure that the MD5 signature is consistent. Specifically generated signature algorithm You can check the official documents of Wechat, and I strongly recommend that you make sure that the official API explains that 90% of the problems you encounter in your development are due to trusting someone's blog instead of looking at the official documents. This is the real purpose and purpose of this article. According to the official documents, using my Java code, Wechat PC-side web page scanning payment must fly in your WEB application.

Topics: xml Java Maven QRCode