前言
1.微信小程序支付官方接口文檔:[點擊查看微信開放平台api開發文檔]
2.遇到的坑:預支付統一下單簽名結果返回[簽名錯誤]失敗,建議用官方[簽名驗證工具]檢查簽名是否存在問題.
3.遇到的坑:簽名格式正確但統一下單接口依舊返回簽名錯誤,解決方法=>去微信[商戶平台]重新生成商戶支付api密鑰(文中提到的appKey),使用新的密鑰進行調用接口。一般第一次生成的密鑰是不可用的。
4.涉及的數據庫表結構在最后面貼上
1.1 小程序支付說明
1.2 小程序輕量級支付模板(引用碼雲作者egan)引入jar包
<dependency> <groupId>com.egzosn</groupId> <artifactId>pay-java-common</artifactId> <version>2.12.1</version> </dependency> <dependency> <groupId>com.egzosn</groupId> <artifactId>pay-java-wx</artifactId> <version>2.12.1</version> </dependency>
1.3 初始化微信支付配置存儲類
private WxPayService wxUserPayService;
/** * 微信支付必要信息初始化 * @param appId */
private void wxUserPayServiceInit(String appId) { if (wxUserPayService == null) { WxPayConfigStorage wxPayConfigStorage = new WxPayConfigStorage(); wxPayConfigStorage.setMchId(mchId);//支付商戶號 wxPayConfigStorage.setAppid(appId);//小程序appid // wxPayConfigStorage.setKeyPublic("轉賬公鑰,轉賬時必填"); wxPayConfigStorage.setSecretKey(appKey);// 商戶支付密鑰 wxPayConfigStorage.setNotifyUrl(domain + "/wx/userpaycallback");//支付結果回調接口地址,必須外網可訪問的完整url地址 wxPayConfigStorage.setSignType("MD5"); wxPayConfigStorage.setInputCharset("utf-8"); // https證書設置,退款必須 方式一 /*HttpConfigStorage httpConfigStorage = new HttpConfigStorage(); httpConfigStorage.setKeystorePath(keystorePath);//apiclient_cert.p12證書存放路徑,填寫絕對路徑 httpConfigStorage.setStorePassword(mchId); httpConfigStorage.setPath(true); httpConfigStorage.setMaxTotal(20); httpConfigStorage.setDefaultMaxPerRoute(10); wxUserPayService = new WxPayService(wxPayConfigStorage, httpConfigStorage);*/ wxUserPayService = new WxPayService(wxPayConfigStorage); } }
1.4 付款前生成微信預支付交易單,返回正確的預支付交易參數信息,供給微信小程序前端開發者調用付款API
String title = "開通會員";//支付下單標題 double total_fee = vipConfigInfoService.getInfo().getPrice();//實際支付金額 // 測試默認都是 // if (settingActive.equals("test")) { // total_fee = 0.01; // } // 添加支付記錄信息,生成商戶訂單號,這里用支付記錄id String out_trade_no = payService.representVip(type, appId, openId, userId, total_fee, title); wxUserPayServiceInit(appId);//初始化微信支付配置存儲類 PayOrder order = new PayOrder(title, type, new BigDecimal(total_fee), out_trade_no); order.setTransactionType(WxTransactionType.JSAPI); order.setOpenid(openId); order.setSpbillCreateIp(spbill_create_ip);//終端ip,調用微信支付API的機器IP order.setAddition(type); JSONObject result = wxUserPayService.unifiedOrder(order);//微信統一下單接口,此方法在引入的jar中,直接傳入必填的信息調用即可 System.out.println("請求微信支付統一下單結果:" + result.toString()); String timeStamp = System.currentTimeMillis() / 1000 + ""; JSONObject json = new JSONObject(); json.put("nonceStr", result.getString("nonce_str")); json.put("package", "prepay_id=" + result.getString("prepay_id")); json.put("timeStamp", timeStamp); String stringSignTemp = "appId=" + appId + "&nonceStr=" + result.getString("nonce_str") + "&package=prepay_id=" + result.getString("prepay_id") + "&signType=MD5&timeStamp=" + timeStamp; json.put("paySign", wxUserPayService.createSign(stringSignTemp, "utf-8")); return json;//接口方法返回預支付交易信息,小程序前端獲取返回參數,調用對應的付款接口
1.5 支付回調(付款后微信服務器后台回調支付結果信息)
/** * * 支付回調接口 */ @RequestMapping("/userpaycallback") public String wxUserPaycallback(HttpServletRequest request) { try { Map<String, Object> params = wxUserPayService.getParameter2Map(request.getParameterMap(), request.getInputStream()); if (null == params) { throw new Exception("回調參數為空"); } // 校驗 if (!wxUserPayService.verify(params)) { throw new Exception("校驗失敗"); } payDone(params);//支付處理 return wxUserPayService.getPayOutMessage("SUCCESS", "成功").toMessage(); } catch (Exception e) { e.printStackTrace(); return wxUserPayService.getPayOutMessage("FALL", "失敗").toMessage(); } } /** * 支付結果處理 */ private void payDone(Map<String, Object> dataMap) throws Exception { System.out.println("支付回調:" + JSON.toJSONString(dataMap)); // {"transaction_id":"4200000109201805293331420304","nonce_str":"402880e963a9764b0163a979a16e0002","bank_type":"CFT","openid":"oXI6G5Jc4D44y2wixgxE3OPwpDVg","sign":"262978D36A3093ACBE4B55707D6EA7B2","fee_type":"CNY","mch_id":"1491307962","cash_fee":"10","out_trade_no":"14913079622018052909183048768217","appid":"wxa177427bc0e60aab","total_fee":"10","trade_type":"JSAPI","result_code":"SUCCESS","time_end":"20180529091834","is_subscribe":"N","return_code":"SUCCESS"} String result_code = dataMap.get("result_code").toString();//支付結果code String out_trade_no = dataMap.get("out_trade_no").toString();//商戶交易訂單id,此為支付記錄id if (!result_code.equals("SUCCESS")) { throw new Exception("支付回調失敗:" + JSON.toJSONString(dataMap)); } Pay pay = payService.getById(out_trade_no);//獲取支付記錄 if (pay == null || pay.getStatus() != 10) {//10=可用/待支付 20=支付成功 30=支付失敗 throw new Exception("支付記錄已經處理"); } payService.pay(out_trade_no, result_code.equals("SUCCESS") ? 20 : 30);//根據回調結果更改支付結果狀態 // 升級成會員的數據處理 if(result_code.equals("SUCCESS")) {//支付結果SUCCESS,支付成功開通會員,進行下一步操作 Document doc=new Document(); doc.put("vipStatus", 20);//狀態20開通會員 doc.put("vipCreateTime", new Date()); doc.put("expireTime", StringUtil.getMonthNextOrBeforeDate(3));//設置過期時間為3個月后 userService.updateUserVip(pay.getUserId(),doc); } }
2.1數據庫表
支付記錄表Pay
字段 |
名稱 |
類型 |
備注 |
---|---|---|---|
_id | 唯一標識 | String | |
type | 支付訂單類型 | String | |
orderId | 關聯訂單Id | String | |
remark | 備注 | String | |
openId | 微信用戶openId | String | |
account | 金額 | double | |
status | 狀態 | int | 10=可用/待支付 20=支付成功 30=支付失敗 |
createTime | 創建時間 | Date | |
callBackTime | 回調時間 | Date |
3.1需要引用的類class
import com.egzosn.pay.common.bean.PayOrder; import com.egzosn.pay.common.util.XML; import com.egzosn.pay.wx.api.WxPayConfigStorage; import com.egzosn.pay.wx.api.WxPayService; import com.egzosn.pay.wx.bean.WxTransactionType;
結尾:此示例是用戶開通vip的接口方法,重要的代碼都有貼上。有疑問歡迎各位留言或聯系郵箱laifw@foxmail.com,請多多指教