1. 程式人生 > 實用技巧 >微信小程式支付+java後臺程式碼

微信小程式支付+java後臺程式碼

一、java後臺

 1、maven引入

<!--微信支付-->
<dependency>
<groupId>com.github.wxpay</groupId>
<artifactId>wxpay-sdk</artifactId>
<version>0.0.3</version>
</dependency>

<dependency>
<groupId>org.jdom</groupId>
<artifactId>jdom</artifactId>
<version>2.0.2</version>
</dependency>

 2、java程式碼

    WxUtils類

import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.HttpClients;

import org.jdom2.Document;
import org.jdom2.Element;
import
org.jdom2.JDOMException; import org.jdom2.input.SAXBuilder; import org.springframework.stereotype.Component; import org.springframework.stereotype.Service; import javax.servlet.http.HttpServletRequest; import java.io.*; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException;
import java.util.*; @Component public class WxUtils { /** * 執行 POST 方法的 HTTP 請求 * * @param url * @param parameters * @return * @throws IOException */ public String executeHttpPost(String url, SortedMap<String, Object> parameters) throws IOException { HttpClient client = HttpClients.createDefault(); HttpPost request = new HttpPost(url); request.setHeader("Content-type", "application/xml"); request.setHeader("Accept", "application/xml"); request.setEntity(new StringEntity(transferMapToXml(parameters), "UTF-8")); HttpResponse response = client.execute(request); return readResponse(response); } /** * 第一次簽名 * * @param parameters 資料為伺服器生成,下單時必須的欄位排序簽名 * @param key * @return */ public String createSign(SortedMap<String, Object> parameters, String key) { StringBuffer sb = new StringBuffer(); Set es = parameters.entrySet();//所有參與傳參的引數按照accsii排序(升序) Iterator it = es.iterator(); while (it.hasNext()) { Map.Entry entry = (Map.Entry) it.next(); String k = (String) entry.getKey(); Object v = entry.getValue(); if (null != v && !"".equals(v) && !"sign".equals(k) && !"key".equals(k)) { sb.append(k + "=" + v + "&"); } } sb.append("key=" + key); return encodeMD5(sb.toString()); } /** * 第二次簽名 * * @param result 資料為微信返回給伺服器的資料(XML 的 String),再次簽名後傳回給客戶端(APP)使用 * @param key 金鑰 * @return * @throws IOException */ public Map createSign2(String result, String key) throws IOException { SortedMap<String, Object> map = new TreeMap<>(transferXmlToMap(result)); Map app = new HashMap(); System.out.println(map); app.put("appId", map.get("appid")); app.put("nonceStr", map.get("nonce_str")); app.put("package", "prepay_id="+map.get("prepay_id")); app.put("signType", "MD5"); app.put("timeStamp",String.valueOf(new Date().getTime() / 1000) ); // 時間為秒,JDK 生成的是毫秒,故除以 1000 app.put("sign", createSign(new TreeMap<>(app), key)); return app; } /** * 驗證簽名是否正確 * * @return boolean * @throws Exception */ public boolean checkSign(SortedMap<String, Object> parameters, String key) throws Exception { String signWx = parameters.get("sign").toString(); if (signWx == null) return false; parameters.remove("sign"); // 需要去掉原 map 中包含的 sign 欄位再進行簽名 String signMe = createSign(parameters, key); return signWx.equals(signMe); } /** * 讀取 request body 內容作為字串 * * @param request * @return * @throws IOException */ public String readRequest(HttpServletRequest request) throws IOException { InputStream inputStream; StringBuffer sb = new StringBuffer(); inputStream = request.getInputStream(); String str; BufferedReader in = new BufferedReader(new InputStreamReader(inputStream, "UTF-8")); while ((str = in.readLine()) != null) { sb.append(str); } in.close(); inputStream.close(); return sb.toString(); } /** * 讀取 response body 內容為字串 */ public String readResponse(HttpResponse response) throws IOException { BufferedReader in = new BufferedReader( new InputStreamReader(response.getEntity().getContent())); String result = new String(); String line; while ((line = in.readLine()) != null) { result += line; } return result; } /** * 將 Map 轉化為 XML * * @param map * @return */ public String transferMapToXml(SortedMap<String, Object> map) { StringBuffer sb = new StringBuffer(); sb.append("<xml>"); for (String key : map.keySet()) { sb.append("<").append(key).append(">") .append(map.get(key)) .append("</").append(key).append(">"); } return sb.append("</xml>").toString(); } /** * 將 XML 轉化為 map * * @param strxml * @return * @throws org.jdom2.JDOMException * @throws IOException */ public Map transferXmlToMap(String strxml) throws IOException { strxml = strxml.replaceFirst("encoding=\".*\"", "encoding=\"UTF-8\""); if (null == strxml || "".equals(strxml)) { return null; } Map m = new HashMap(); InputStream in = new ByteArrayInputStream(strxml.getBytes("UTF-8")); SAXBuilder builder = new SAXBuilder(); Document doc = null; try { doc = builder.build(in); } catch (JDOMException e) { throw new IOException(e.getMessage()); // 統一轉化為 IO 異常輸出 } // 解析 DOM Element root = doc.getRootElement(); List list = root.getChildren(); Iterator it = list.iterator(); while (it.hasNext()) { Element e = (Element) it.next(); String k = e.getName(); String v = ""; List children = e.getChildren(); if (children.isEmpty()) { v = e.getTextNormalize(); } else { v = getChildrenText(children); } m.put(k, v); } //關閉流 in.close(); return m; } // 輔助 transferXmlToMap 方法遞迴提取子節點資料 private String getChildrenText(List<Element> children) { StringBuffer sb = new StringBuffer(); if (!children.isEmpty()) { Iterator<Element> it = children.iterator(); while (it.hasNext()) { Element e = (Element) it.next(); String name = e.getName(); String value = e.getTextNormalize(); List<Element> list = e.getChildren(); sb.append("<" + name + ">"); if (!list.isEmpty()) { sb.append(getChildrenText(list)); } sb.append(value); sb.append("</" + name + ">"); } } return sb.toString(); } /** * 生成 32 位隨機字串,包含:數字、字母大小寫 * * @return */ public String gen32RandomString() { char[] dict = {'1', '2', '3', '4', '5', '6', '7', '8', '9', '0', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'}; StringBuffer sb = new StringBuffer(); Random random = new Random(); for (int i = 0; i < 32; i++) { sb.append(String.valueOf(dict[(int) (Math.random() * 36)])); } return sb.toString(); } /** * MD5 簽名 * * @param str * @return 簽名後的字串資訊 */ public String encodeMD5(String str) { try { MessageDigest messageDigest = MessageDigest.getInstance("MD5"); byte[] inputByteArray = (str).getBytes(); messageDigest.update(inputByteArray); byte[] resultByteArray = messageDigest.digest(); return byteArrayToHex(resultByteArray); } catch (NoSuchAlgorithmException e) { return null; } } // 輔助 encodeMD5 方法實現 private String byteArrayToHex(byte[] byteArray) { char[] hexDigits = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; char[] resultCharArray = new char[byteArray.length * 2]; int index = 0; for (byte b : byteArray) { resultCharArray[index++] = hexDigits[b >>> 4 & 0xf]; resultCharArray[index++] = hexDigits[b & 0xf]; } // 字元陣列組合成字串返回 return new String(resultCharArray); } }

統一下單介面

  //統一下單介面
    public Map createOrder(@PathVariable String ids, @CurrentPerson CurPersonInfo currentPerson) throws IOException {
       
        SortedMap<String, Object> parameters = new TreeMap<String, Object>();
        parameters.put("appid", APPID);
        parameters.put("mch_id", MCH_ID);
        parameters.put("device_info", "WEB"); // 預設 "WEB"
        parameters.put("body", body);
        parameters.put("nonce_str", wxUtils.gen32RandomString()); // 32 位隨機字串
        parameters.put("notify_url", notifyUrl);
        parameters.put("out_trade_no", ids);
        parameters.put("sign_type", "MD5");
        parameters.put("total_fee", 1); // 測試時,將支付金額設定為 1 分錢
        parameters.put("spbill_create_ip", "127.0.0.1");
        parameters.put("trade_type", "JSAPI");
        System.out.println(currentPerson.getOpenId());
        parameters.put("openid", currentPerson.getOpenId());
        parameters.put("sign", wxUtils.createSign(parameters, KEY)); // sign 必須在最後

        System.out.println(parameters);
        String result = wxUtils.executeHttpPost(placeUrl, parameters); // 執行 HTTP 請求,獲取接收的字串(一段 XML)
        Map resultInfo=wxUtils.createSign2(result, KEY);
        System.out.println(resultInfo);
        return resultInfo;
  
    }

回撥介面

 @RequestMapping(value = "/callBack")
    public String callBack(HttpServletRequest request, HttpServletResponse response) throws Exception {
        // 預先設定返回的 response 型別為 xml
        response.setHeader("Content-type", "application/xml");
        // 讀取引數,解析Xml為map
        Map<String, String> map = wxUtils.transferXmlToMap(wxUtils.readRequest(request));
        // 轉換為有序 map,判斷簽名是否正確
        boolean isSignSuccess = wxUtils.checkSign(new TreeMap<String, Object>(map), KEY);
        if (isSignSuccess) {
            // 簽名校驗成功,說明是微信伺服器發出的資料
               業務程式碼
            return success();
        } else {
            // 簽名校驗失敗(可能不是微信伺服器發出的資料)
            return fail();
        }
    }

    String fail() {
        return "<xml>\n" +
                "  <return_code><![CDATA[FAIL]]></return_code>\n" +
                "  <return_msg><![CDATA[]]></return_msg>\n" +
                "</xml>";
    }

    String success() {
        return "<xml>\n" +
                "  <return_code><![CDATA[SUCCESS]]></return_code>\n" +
                "  <return_msg><![CDATA[OK]]></return_msg>\n" +
                "</xml>";
    }

二、小程式調起支付程式碼

返回的引數為統一下單介面返回的

 wx.requestPayment(
            {
            'timeStamp': data.timeStamp,
            'nonceStr': data.nonceStr,
            'package': data.package,
            'signType': 'MD5',
            'paySign': data.sign,
            'success':function(res){
               console.log(res)
            },
            'fail':function(res){
              console.log(res)
             
            }
          })