1. 程式人生 > >微信APP支付Java後臺總結

微信APP支付Java後臺總結

出於興趣寫了一個純支付的模組,有興趣的同學可以去看看(戳我

———————————分隔線———————————–

微信APP支付大致的流程和支付寶APP支付有很大不同(想了解支付寶APP支付的同學點這裡),其中略坑的一點就是MD5加密的方法需要自己寫,好在微信官方給出了測試地址

前置準備

微信的東西比支付寶要複雜一點,要先去註冊開發者賬號,然後一系列巴拉巴拉……,然後得到下面列出的我們開發需要的引數,下面的引數中TOKEN和CERT_PATH是需要退款和設計到其他功能才需要的,如果只做APP支付功能的同學可以忽略。

  • APP_ID : 服務號的應用ID
  • APP_SECRET : 服務號的應用金鑰
  • TOKEN : 服務號的配置token
  • MCH_ID : 商戶號
  • API_KEY : API金鑰
  • SIGN_TYPE : 簽名加密方式
  • CERT_PATH : 微信支付證書名稱、

支付流程

具體的流程大概就是後臺拿到(或生成)自己的訂單號後,拿著一堆引數按ASCII碼(即abc…)排序後用MD5加密後呼叫微信的伺服器地址拿到一個prepayId,再將prepayId和當前的引數再次排序和加密(二次加密)生成sign後返回前端進行支付,支付完成後,微信的後臺會回撥我們的後臺介面。

Java程式碼

maven依賴

`<!-- 微信支付依賴 -->
<dependency>
    <groupId>org.xmlpull</groupId>
    <artifactId>xmlpull</artifactId>
    <version>1.1.3.1 </version>
</dependency>
<dependency>
    <groupId>net.sf.json-lib</groupId>
    <artifactId>json-lib</artifactId>
    <version>2.3</version>
    <classifier>jdk15</classifier>
</dependency>
<!-- https://mvnrepository.com/artifact/com.thoughtworks.xstream/xstream -->
<dependency>
    <groupId>com.thoughtworks.xstream</groupId>
    <artifactId>xstream</artifactId>
    <version>1.4.5</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.ning/async-http-client -->
<dependency>
    <groupId>com.ning</groupId>
    <artifactId>async-http-client</artifactId>
    <version>1.8.13</version>
</dependency>`

Java程式碼

@ResponseBody
@RequestMapping(value="/wxpaySign",method=RequestMethod.POST)
public JsonResult getWXPaySign(double totalAmount,String userId,String out_trade_no,String ip) 
            throws JDOMException, IOException{
        //預備引數
        WXPayDTO wxPayDTO = new WXPayDTO();
        String
subject = "******"; String body = "******"; String nonceStr = setNonceStr(); long timeStamp = (long)CommonUtil.getSecondTimestamp(new Date()); String packageStr = "Sign=WXPay"; String total_fee = String.valueOf(CommonUtil.double100ToInt(totalAmount)); SortedMap<String,String> payMap = genOrderData(PayConstants.WX_APP_ID,body,out_trade_no, nonceStr,packageStr,PayConstants.WX_PARTNER_ID,total_fee,subject,ip); //設定WXPayDTO wxPayDTO.setAppId(PayConstants.WX_APP_ID); //商家id wxPayDTO.setPartnerId(PayConstants.WX_PARTNER_ID); wxPayDTO.setPrepayId(out_trade_no); //訂單 wxPayDTO.setNonceStr(nonceStr); //設定隨機串 wxPayDTO.setTimeStamp(timeStamp); //設定時間戳 wxPayDTO.setPackageStr(packageStr); wxPayDTO.setSign(payMap.get("sign")); wxPayDTO.setPrepayId(payMap.get("prepayid")); return new JsonResult(Message.M2000,wxPayDTO); }
private SortedMap<String, String> genOrderData(String appId, String body, String out_trade_no,
            String nonceStr, String packageStr, String partnerId,String total_fee,String subject,String ip) 
                    throws JDOMException, IOException {
        SortedMap<String, String> paraMap = new TreeMap<String,String>();
    //按照官方文件的要求,這裡引數名ASCII碼從小到大排序(字典序);
        paraMap.put("appid", appId);
        paraMap.put("body",body);
        paraMap.put("mch_id",partnerId);        //微信商戶賬號
        paraMap.put("nonce_str",nonceStr);  //32位不重複的編號
        paraMap.put("notify_url", 
        PayConstants.WX_Notify_Url);    //設定一個回撥的地址
        paraMap.put("out_trade_no", out_trade_no);  //訂單號
        paraMap.put("spbill_create_ip", ip);//前端傳回來的ip
        paraMap.put("total_fee",total_fee);     //設定金額
        paraMap.put("trade_type","APP");    //方式為APP支付
        //用上面封裝的引數生成sign
        String sign = PayCommonUtil.createSign("UTF-8", paraMap,PayConstants.WX_API_KEY);
        //這裡的sign用二次簽名生成的sign
        paraMap.put("sign",sign);
        //統一下單地址
        //將上面集合內容轉成xml
        String xml = getRequestXML(paraMap);
        String xmlStr = HttpUtil.postData(PayConstants.WX_Notify_Url,xml);
        //將返回的xml轉換成map
        Map map = XMLUtil.doXMLParse(xmlStr);
        //預付商品id
        String prepay_id = (String) map.get("prepay_id");

        //這裡要對引數進行二次簽名
        SortedMap<String, String> secParaMap = new TreeMap<String,String>();
        secParaMap.put("appid", appId);
        secParaMap.put("partnerid",partnerId);
        secParaMap.put("prepayid", prepay_id);
        //這裡不確定是不是要重新生成一個隨機串,再改
        secParaMap.put("noncestr", nonceStr);
        secParaMap.put("timestamp", CommonUtil.getTimeStamp());
        secParaMap.put("package", "Sign=WXPay");
        String secSign = PayCommonUtil.createSign("UTF-8", secParaMap, PayConstants.WX_API_KEY);
        secParaMap.put("sign", secSign);
        secParaMap.put("prepayid", prepay_id);

        return secParaMap;
    }

支付回撥
微信的支付回撥是用流形式傳輸的,所以要先對流形式的資料進行解析再得到我們需要的東西。

    @RequestMapping(value = "payNotifyUrl", produces = "application/json;charset=UTF-8")
    @ResponseBody
    public String payNotifyUrl(HttpServletRequest request, HttpServletResponse response) throws Exception {
        //讀取引數 

        InputStream inputStream ;  
        StringBuffer sb = new StringBuffer();  
        inputStream = request.getInputStream();  
        String s ;  
        BufferedReader in = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));  
        while ((s = in.readLine()) != null){  
            sb.append(s);  
        }  
        in.close();  
        inputStream.close();  

        //解析xml成map  
        Map<String, String> m = new HashMap<String, String>();  
        m = XMLUtil.doXMLParse(sb.toString());  

        //過濾空 設定 TreeMap  
        SortedMap<Object,Object> packageParams = new TreeMap<Object,Object>();        
        Iterator it = m.keySet().iterator();  
        while (it.hasNext()) {  
            String parameter = (String) it.next();  
            String parameterValue = m.get(parameter);  

            String v = "";  
            if(null != parameterValue) {  
                v = parameterValue.trim();  
            }  
            packageParams.put(parameter, v);  
        }  

        //判斷簽名是否正確  
        if(PayCommonUtil.isTenpaySign("UTF-8", packageParams,PayConstants.WX_API_KEY)) {  
            //------------------------------  
            //處理業務開始  
            //------------------------------  
            String resXml = "";  
            if("SUCCESS".equals((String)packageParams.get("result_code"))){  
                // 這裡是支付成功  
                //////////執行自己的業務邏輯////////////////  
                String mch_id = (String)packageParams.get("mch_id");  
                String openid = (String)packageParams.get("openid");  
                String is_subscribe = (String)packageParams.get("is_subscribe");  
                String out_trade_no = (String)packageParams.get("out_trade_no");  

                String total_fee = (String)packageParams.get("total_fee");  

                System.out.println("mch_id:"+mch_id);  
                System.out.println("openid:"+openid);  
                logger.info("is_subscribe:"+is_subscribe);  
                logger.info("out_trade_no:"+out_trade_no);  
                logger.info("total_fee:"+total_fee);  

                //////////執行自己的業務邏輯////////////////  

                logger.info("支付成功");  
                //通知微信.非同步確認成功.必寫.不然會一直通知後臺.八次之後就認為交易失敗了.  
                resXml = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>"  
                        + "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> ";  

            } else {  
                logger.info("支付失敗,錯誤資訊:" + packageParams.get("err_code"));  
                resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>"  
                        + "<return_msg><![CDATA[報文為空]]></return_msg>" + "</xml> ";  
            }  
            //------------------------------  
            //處理業務完畢  
            //------------------------------  
            BufferedOutputStream out = new BufferedOutputStream(  
                    response.getOutputStream());  
            out.write(resXml.getBytes());  
            out.flush();  
            out.close();  
        } else{  
            logger.info("通知簽名驗證失敗");  
        }  
        return "SUCCESS";
    }

程式碼中包含一些常用的工具類,是從別人的資源來下載來的,覺得很好用。