Java 后端微信小程序支付demo (网上说的坑里面基本上都有)
Java 后端微信小程序支付
一、遇到的问题
1. 商户号该产品权限未开通,请前往商户平台>产品中心检查后重试
2.签名错误
3.已经调起微信统一下单接口,可以拿到预支付ID,但是前端支付的时候出现 支付验证签名失败
网上的问题基本上就是这三类下面说解决方案。
第一个问题:商户号的问题,微信app支付与微信小程序支付的商户号不同,微信app内支付与微信开放平台内的商户号绑定,微信小程序支付是与微信公众平台内的商户号绑定。
第二个问题:签名错误, 第一次签名需要的appid mchid key 分别对应的是小程序的appid、微信公众平台绑定的商户号、以及商户平台的密钥(重要的事情说三遍:商户平台的!商户平台的!商户平台的!)。
这里重点说下商户平台的密钥(当时就是因为密钥的问题耽误了不少时间):具体请看图(商户平台--->账户中心--->API安全---->设置密钥)
第三个问题:支付验证签名失败 这个就得需要严格执行小程序支付文档中的生成签名的规则。参数字段大小写、顺序一定要对(切记,切记,切记)
如果能把以上都注意到了,那基本上没什么问题了。下面是我自己写的demo供大家参考,有什么问题可以留言。
二、demo 事例
1.下载商户的证书(在问题2的截图中已经展示下载的位置)放在resources下的cert目录下(cert目录需要自己手动建)
2.实现微信的 WXPayConfig接口
1 package com.ieou.franky.payCenter.common; 2 3 import com.github.wxpay.sdk.WXPayConfig; 4 import org.apache.commons.io.IOUtils; 5 6 import java.io.ByteArrayInputStream; 7 import java.io.InputStream; 8 9 public class MiniprogramConfig implements WXPayConfig{ 10 11 private byte[] certData; 12 13 public MiniprogramConfig() throws Exception { 14 InputStream certStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("cert/miniProgram/apiclient_cert.p12"); 15 this.certData = IOUtils.toByteArray(certStream); 16 certStream.close(); 17 } 18 19 @Override 20 public String getAppID() { 21 return "wx6db448a73f8a6666"; 22 } //小程序appid 23 24 @Override 25 public String getMchID() { 26 return "1500669999"; 27 } //商户号 28 29 /** 商户平台-账户中心-API安全中的密钥 */ 30 @Override 31 public String getKey() { 32 return "1add1a30ac87aa77yyy2db72f57a237577777"; 33 } 34 35 @Override 36 public InputStream getCertStream() { 37 return new ByteArrayInputStream(this.certData); 38 } 39 40 @Override 41 public int getHttpConnectTimeoutMs() { 42 return 0; 43 } 44 45 @Override 46 public int getHttpReadTimeoutMs() { 47 return 0; 48 } 49 }
3.调用微信统一下单接口并且实现签名
1 private Map<String, String> createMiniProgramTradePay(WxpayTradeAppPayModel model,String notifyUrl) throws Exception { 2 MiniprogramConfig config = new MiniprogramConfig(); 3 4 WXPay wxpay = new WXPay(config); 5 6 Map<String, String> data = new HashMap<>(); 7 data.put("appid",config.getAppID()); 8 data.put("mch_id",config.getMchID()); 9 data.put("body",model.getBody()); 10 data.put("out_trade_no", model.getOutTradeNo()); 11 data.put("device_info", ""); 12 data.put("fee_type", "CNY"); 13 data.put("total_fee", model.getTotalFee()); 14 data.put("spbill_create_ip", "127.0.0.1"); 15 data.put("notify_url", notifyUrl); 16 data.put("trade_type", "JSAPI"); 17 data.put("openid",model.getOpenId()); 18 // data.put("nonce_str","1add1a30ac87aa2db72f57a2375d8fec"); 19 String sign = WXPayUtil.generateSignature(data, config.getKey()); 20 data.put("sign",sign); 21 22 Map<String, String> resp = wxpay.unifiedOrder(data); 23 if ("SUCCESS".equals(resp.get("return_code"))) { 24 //再次签名 25 /** 重要的事情说三遍 小程序支付 所有的字段必须大写 驼峰模式 严格按照小程序支付文档 26 *https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=7_7&index=3# 27 * ******* 我当初就因为timeStamp中S没大写弄了3个小时 ********** 28 * */ 29 Map<String, String> reData = new HashMap<>(); 30 reData.put("appId", config.getAppID()); 31 reData.put("nonceStr", resp.get("nonce_str")); 32 String newPackage = "prepay_id=" + resp.get("prepay_id"); 33 reData.put("package", newPackage); 34 reData.put("signType","MD5"); 35 reData.put("timeStamp", String.valueOf(System.currentTimeMillis() / 1000)); 36 37 String newSign = WXPayUtil.generateSignature(reData, config.getKey()); 38 resp.put("paySign",newSign); 39 resp.put("timeStamp", reData.get("timeStamp")); 40 return resp; 41 } else { 42 throw new FrankyErrorException(resp.get("return_msg")); 43 } 44 45 }
说明:这里只是说明小程序支付与微信app内支付的不同和遇到的问题,有关里面用到微信的SDK,请看上一篇文章。
到这一步java 后端代码就差不多了,前端拿到返回的字段调起支付接口。
hello world!!!