1. 程式人生 > >微信支付(APP支付)-服務端開發(一)

微信支付(APP支付)-服務端開發(一)

微信支付,首先需要註冊一個商戶平臺公眾賬號,(網址:https://pay.weixin.qq.com/index.php/home/d_login)

目前微信支付的接入方式有四種方式:公眾號支付,APP支付,掃描支付,刷卡支付。本文中我將詳細講解一下APP支付。

微信支付→APP支付官方文件:https://pay.weixin.qq.com/wiki/doc/api/app.php?chapter=8_1

主要流程如下:(https://pay.weixin.qq.com/wiki/doc/api/app.php?chapter=8_3)

商戶系統和微信支付系統主要互動說明:

步驟1:使用者在商戶APP中選擇商品,提交訂單,選擇微信支付。

步驟2:商戶後臺收到使用者支付單,呼叫微信支付統一下單介面。參見【統一下單API】。

步驟3:統一下單介面返回正常的prepay_id,再按簽名規範重新生成簽名後,將資料傳輸給APP。參與簽名的欄位名為appId,partnerId,prepayId,nonceStr,timeStamp,package。注意:package的值格式為Sign=WXPay

步驟4:商戶APP調起微信支付。api參見本章節【app端開發步驟說明

步驟5:商戶後臺接收支付通知。api參見【支付結果通知API

步驟6:商戶後臺查詢支付結果。,api參見【查詢訂單API

其中與後臺相關的主要為步驟2和步驟6。雖然都有官方文件的說明,但是開發過程中,難免還會遇到不少的坑,比如簽名問題,大小寫問題等。

我首先講解一下步驟2(統一下單API:https://pay.weixin.qq.com/wiki/doc/api/app.php?chapter=9_1):

統一下單介面地址:URL地址:https://api.mch.weixin.qq.com/pay/unifiedorder,請求引數,返回引數等文件中都有詳細描述,就不再一一說明,直接上程式碼:

@RequestMapping(value = "/createOrder", method = {RequestMethod.GET, RequestMethod.POST})
public String createOrder(Map<String, String> model) throws Exception{
log.debug("WeChatPayController.createOrder,parameter[{trade_no,subject,total_fee},{"
+ request.getParameter("trade_no")
+ ","
+ request.getParameter("subject")
+ ","
+ request.getParameter("total_fee") + "}]");

WeChatRsp response = new WeChatRsp();
WeChat weChat = new WeChat();

String orderNo = request.getParameter("trade_no"); //訂單號
String money = request.getParameter("total_fee"); //訂單金額
String body = request.getParameter("subject"); //商品描述根據情況修改

//金額轉化為分為單位
float sessionmoney = Float.parseFloat(money);
String finalmoney = String.format("%.2f", sessionmoney);
finalmoney = finalmoney.replace(".", "");

//商戶相關資料 
String appid = CommonUtils.getPropertiesValue("config", "appid");
String appsecret = CommonUtils.getPropertiesValue("config", "appsecret");
String partner = CommonUtils.getPropertiesValue("config", "partnerId");
String partnerkey = CommonUtils.getPropertiesValue("config", "partnerkey");

//商戶號
String mch_id = partner;
//隨機數 
Random random = new Random();
String nonce_str = cn.emagsoftware.utils.MD5Util.getMD5String(String.valueOf(random.nextInt(10000)));

//商戶訂單號
String out_trade_no = orderNo;
int intMoney = Integer.parseInt(finalmoney);

//總金額以分為單位,不帶小數點
int total_fee = intMoney;
//訂單生成的機器 IP
String spbill_create_ip = request.getRemoteAddr();
System.out.println("訂單生成的機器IP:"+spbill_create_ip);

//這裡notify_url是 支付完成後微信發給該連結資訊,可以判斷會員是否支付成功,改變訂單狀態等。
String notify_url = CommonUtils.getPropertiesValue("config", "weChat_notify_url");
//交易型別
String trade_type = CommonUtils.getPropertiesValue("config", "trade_type");

SortedMap<String, String> packageParams = new TreeMap<String, String>();
packageParams.put("appid", appid); 
packageParams.put("mch_id", mch_id); 
packageParams.put("nonce_str", nonce_str); 
packageParams.put("body", body); 
packageParams.put("out_trade_no", out_trade_no); 

packageParams.put("total_fee", total_fee+""); 
packageParams.put("spbill_create_ip", spbill_create_ip); 
packageParams.put("notify_url", notify_url); 

packageParams.put("trade_type", trade_type);

HttpServletResponse httpServletResponse = null;
RequestHandler reqHandler = new RequestHandler(request, httpServletResponse);
reqHandler.init(appid, appsecret, partnerkey);

String sign = reqHandler.createSign(packageParams);
String xml="<xml>"+
"<appid>"+appid+"</appid>"+
"<body><![CDATA["+body+"]]></body>"+
"<mch_id>"+mch_id+"</mch_id>"+
"<nonce_str>"+nonce_str+"</nonce_str>"+
"<notify_url>"+notify_url+"</notify_url>"+
"<out_trade_no>"+out_trade_no+"</out_trade_no>"+
"<sign>"+sign+"</sign>"+
"<spbill_create_ip>"+spbill_create_ip+"</spbill_create_ip>"+
"<total_fee>"+total_fee+"</total_fee>"+
"<trade_type>"+trade_type+"</trade_type>"+
"</xml>";
log.debug("xml = "+xml);
String createOrderURL = WECHAT_CREATE_ORDER_URL;
String prepay_id="";
//獲取預支付交易號
try {
prepay_id = new GetWxOrderno().getPayNo(createOrderURL, xml, "prepay_id");
} catch (Exception e1) {
e1.printStackTrace();
response.setResultCode(Constant.ERROR_CODE_9998);
response.setResultMessage(Constant.ERROR_MESSAGE.get(Constant.ERROR_CODE_9998));
}

if (prepay_id!=null& !prepay_id.equals("")) {
response.setResultCode(Constant.SUCCESS_CODE);
response.setResultMessage(Constant.ERROR_MESSAGE.get(Constant.SUCCESS_CODE));
weChat.setAppid(appid);
weChat.setPrepayid(prepay_id);
String timestamp = String.valueOf(System.currentTimeMillis() / 1000);
String nonce_string = cn.emagsoftware.utils.MD5Util.getMD5String(timestamp);
weChat.setTimestamp(timestamp);
weChat.setNoncestr(nonce_string);
weChat.setPackages("Sign=WXPay");
weChat.setPartnerid(partner);

//二次簽名
SortedMap<String, String> finalpackage = new TreeMap<String, String>();
finalpackage.put("appid", appid); 
finalpackage.put("timestamp", timestamp); 
finalpackage.put("noncestr", nonce_string); 
finalpackage.put("package", "Sign=WXPay"); 
finalpackage.put("partnerid", mch_id);
finalpackage.put("prepayid", prepay_id);
String finalsign = reqHandler.createSign(finalpackage);
weChat.setSign(finalsign);
response.setData(weChat);
}else {
log.debug("預支付交易號生成失敗。。。");
response.setResultCode(Constant.ERROR_CODE_9998);
response.setResultMessage(Constant.ERROR_MESSAGE.get(Constant.ERROR_CODE_9998));
}

try{
model.put(Constant.RETURN_MESSAGE, JsonUtils.getJSONString(response));
log.debug("WeChatPayController.createOrder.response=="+JsonUtils.getJSONString(response));
} catch (Exception ex) {
log.error("VersionController.getVersion", ex);
}
return RET_JSP;
}

APP端發起支付請求之後,會發送訂單號給服務端程式,服務端拿到訂單號之後,根據統一下單地址,傳送xml檔案給微信,微信接受處理後,如果返回成功,則同時會返回一個預支付交易會話標識(prepay_id),拿到這個標識之後,服務端進行二次簽名,生成sign,簽名引數如下:

//二次簽名
SortedMap<String, String> finalpackage = new TreeMap<String, String>();
finalpackage.put("appid", appid);   //appid
finalpackage.put("timestamp", timestamp);  //時間戳  十位  
finalpackage.put("noncestr", nonce_string); //隨機字串
finalpackage.put("package", "Sign=WXPay");   //固定值
finalpackage.put("partnerid", mch_id);  //商戶id(微信商戶平臺獲取)
finalpackage.put("prepayid", prepay_id);  //第一次請求微信,成功後,返回的引數
String finalsign = reqHandler.createSign(finalpackage);  //生成簽名
weChat.setSign(finalsign);  //生成簽名後,放入物件中

response.setData(weChat);  //引數返回給前端

第一次傳送統一下單的時候,官方文件中都有說明,哪些引數是必須的,哪些引數不是必須的,以及引數型別,此處不再一一解釋,不理解的可以參考官方文件。重點說一下二次簽名:

二次簽名的時候涉及到的引數有appid,timestamp,noncestr,partnerid,prepayid,package,這6個引數全部是小寫(大小寫不同,MD5加密結果不一致,二次簽名官方沒有文件,比較坑)

APP端獲取到服務端傳遞的引數後,調起支付介面(https://pay.weixin.qq.com/wiki/doc/api/app.php?chapter=9_12&index=2),這一點是APP端的操作,不在詳解。

APP端支付成功後,會再次傳送請求到伺服器端,確定訂單是否付款成功,服務端需要再次向微信發起請求,查詢訂單,具體操作檢視下一章節。

註釋:開發中遇到任務問題(服務端),歡迎諮詢,我也是初次開發微信支付,希望可以幫到你。