微信小程序支付java后台案例(公众号支付同适用)(及签名错误解决)
前言
1.微信小程序支付官方接口文档:[点击查看微信开放平台api开发文档]
2.遇到的坑:预支付统一下单签名结果返回[签名错误]失败,建议用官方[签名验证工具]检查签名是否存在问题.
3.遇到的坑:签名格式正确但统一下单接口依旧返回签名错误,解决方法=>去微信[商户平台]重新生成商户支付api密钥(文中提到的appKey),使用新的密钥进行调用接口。一般第一次生成的密钥是不可用的,需重新生成第二次后再试。
4.涉及的数据库表结构在最后面贴上
1.1 小程序支付说明
1.2 引入小程序轻量级支付jar包,里面封装了常用的接口方法(jar源码作者egan)
<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,请多多指教