首先,强调下其实不管是什么支付。第一步基本上都是调用统一下单接口,然后二次签名。调起支付。
下面就一一说下我踩的坑
1、App支付、小程序支付、H5支付统一支付签名参数问题。
这是我做完小程序支付 ,做App支付时。想重用代码发生的坑。话不多说,直接上图
小程序参数‘
’App参数

发现问题了吗?没错,同样的接口,居然参数大小写不一致。。。
2、金额单位问题

金额单位为分,我们系统一般是元,这需要转换。并且在回调接口中也需要注意。特别注意:金额是整数,没有小数,没有小数,没有小数。重要事情说三遍。
3、appid、mch_id(商户id)、秘钥、商户key区别以及签名问题
首先小程序的appid和App的appid不一致。
商户id没啥说的,商户key是在商户平台设置的。这就是调起支付中二次签名的key。具体设置路径:

最后,签名时是有顺序要求的。具体看文档

具体代码,可以下载官方支付sdk。里面基本以及把所用的的工具类都写好了。只需要修改下就可以。大致结构:qizhong

其中WXPay是核心类,WXPayConfig是配置类。如果想封装统一下单和二次签名以及支付回调,需要对配置类做出修改。我贴下我的代码
import org.springframework.beans.factory.annotation.Value;import org.springframework.context.annotation.Configuration;import java.io.ByteArrayInputStream;import java.io.InputStream;@Configurationpublic class WXPayConfigImpl extends WXPayConfig {private byte[] certData; private static WXPayConfigImpl INSTANCE; @Value("${wx.appId}") private String appId; @Value("${wx.mchId}") private String mchId; @Value("${wx.key}") private String key; @Value("${wx.notifyUrl}") private String notifyUrl; @Value("${wx.timeOut}") private Integer timeOut; @Value("${wx.appTimeOut}") private Integer appTimeOut; @Value("${wx.bodyName}") private String bodyName; @Value("${wx.tradeType}") private String tradeType; public String getAppId() { return appId; } public void setAppId(String appId) { this.appId = appId; } public String getMchId() { return mchId; } public void setMchId(String mchId) { this.mchId = mchId; } public void setKey(String key) { this.key = key; } public String getNotifyUrl() { return notifyUrl; } public void setNotifyUrl(String notifyUrl) { this.notifyUrl = notifyUrl; } public Integer getTimeOut() { return timeOut; } public void setTimeOut(Integer timeOut) { this.timeOut = timeOut; } public Integer getAppTimeOut() { return appTimeOut; } public void setAppTimeOut(Integer appTimeOut) { this.appTimeOut = appTimeOut; } public String getBodyName() { return bodyName; } public void setBodyName(String bodyName) { this.bodyName = bodyName; } public void setTradeType(String tradeType) { this.tradeType = tradeType; } public static WXPayConfigImpl getInstance() throws Exception{ if (INSTANCE == null) { synchronized (WXPayConfigImpl.class) { if (INSTANCE == null) { INSTANCE = new WXPayConfigImpl(); } } } return INSTANCE; } @Override public String getAppID() { return appId; } @Override public String getMchID() { return mchId; } @Override public String getKey() { return key; } public String getNotifUrl() { return notifyUrl; } /** * @Description 交易类型 NATIVE: 适用于实体店 JSAPI 移动端支付 * @param * @date 2019/9/11 */ public String getTradeType() { return tradeType; } @Override public InputStream getCertStream() { ByteArrayInputStream certBis; certBis = new ByteArrayInputStream(this.certData); return certBis; } @Override public int getHttpConnectTimeoutMs() { return 2000; } @Override public int getHttpReadTimeoutMs() { return 10000; } public String getPrimaryDomain() { return "api.mch.weixin.qq.com"; } public String getAlternateDomain() { return "api2.mch.weixin.qq.com"; } @Override public int getReportWorkerNum() { return 1; } @Override public int getReportBatchSize() { return 2; } public String getSpbillCreateIp() { return "1.192.218.185"; }@Overridepublic IWXPayDomain getWXPayDomain() {return WXPayDomainSimpleImpl.instance();}}二次签名
//统一下单public BoyMap createOrder(HttpServletRequest request, Order order,WXPayConfigImpl config) { log.info("【用户下单模块】--开始"); try { MapString,String paramMap = new HashMap(); WXPay wxPay = new WXPay(config); //拼装参数 MapString,String data = generateParam(order,request,config); log.info("【用户下单模块】 -- 调用微信统一下参数为:" + JSONObject.toJSONString(data)); MapString, String resp = wxPay.unifiedOrder(data); log.info("【用户下单模块】 -- 调用微信统一下单返回结果为:" + JSONObject.toJSONString(resp)); if(resp != null){ String returnCode = resp.get("return_code"); //成功 if(WXPayConstants.SUCCESS.equalsIgnoreCase(returnCode)){ //保存订单,自己的业务 saveWXPayInfo(order,data.get("out_trade_no"),config); paramMap.put("prepayId",resp.get("prepay_id")); } }else { log.info("【用户下单模块】--调用微信支付接口异常"); return BoyMap .error("微信支付异常"); } log.info("【用户下单模块】-- 调用微信统一下单成功,返回前台数据为: " + JSONObject.toJSONString(paramMap)); return BoyMap .ok().put("paramMap",paramMap); } catch (Exception e) { log.error("【用户下单模块】--异常:" + e.getMessage()); e.printStackTrace(); return BoyMap .error("系统异常"); } }//封装参数@Override public MapString, String generateParam(Order order,HttpServletRequest request,WXPayConfigImpl config){ MapString, String data = new HashMapString, String(); data.put("nonce_str", WXPayUtil.generateNonceStr()); data.put("body", config.getBodyName()); //商户订单号 //生成订单编号 if(StringUtils.isNotBlank(order.getOrderNo())){ data.put("out_trade_no", order.getOrderNo()); }else { data.put("out_trade_no", SgenerateOrderNo(config)); } //签名类型 data.put("sign_type", "MD5"); //标价币种 if(ycejWxPayOrder.getCurrencyType() == null){ data.put("fee_type", "CNY"); } //终端IP data.put("spbill_create_ip", IPUtils.getIpaddr(request)); //通知地址 异步接收微信支付结果通知的回调地址,通知url必须为外网可访问的url,不能携带参数。 data.put("notify_url",config.getNotifUrl()); // 交易类型 data.put("trade_type", config.getTradeType()); BigDecimal amount = new BigDecimal(0.0); //订单金额 amount = order.getAmout().multiply(new BigDecimal(100)); String amountMoney = String.valueOf(amount); //金额必须为整数 if(amountMoney.contains(".")){ amountMoney = amountMoney.substring(0,amountMoney.indexOf(".")); } //标价金额 data.put("total_fee", (amount.compareTo(BigDecimal.ZERO) == 0)? "0":amountMoney); return data; }//二次签名@Override public BoyMap generateSignature(String prepayId,WXPayConfigImpl config) { log.info("【微信支付二次签名模块】--开始"); MapString,String paramMap = null; MapString,String signMap =null; try { paramMap = new HashMap(); signMap = new HashMap(); String nonceStr = UUIDUtil.getUUID(); String timeStamp = StringTools.stringOf(System.currentTimeMillis()/1000); //H5 支付跳转地址 if("APP".equalsIgnoreCase(config.getTradeType())){ //再次签名 signMap.put("appid",config.getAppID()); signMap.put("noncestr",nonceStr); signMap.put("package","Sign=WXPay"); signMap.put("partnerid",config.getMchID()); signMap.put("prepayid",prepayId); signMap.put("timestamp",timeStamp); }else if("JSAPI".equalsIgnoreCase(config.getTradeType())){ signMap.put("appId",config.getAppID()); signMap.put("timeStamp",timeStamp); signMap.put("nonceStr",nonceStr); signMap.put("package","prepay_id="+prepayId); signMap.put("signType","MD5"); } //二次组装签名 String sign = WXPayUtil.generateSignature(signMap,config.getKey(), "MD5"); if(sign != null){ log.info("【微信支付二次签名模块】--成功,签名为==:" + sign); if("APP".equalsIgnoreCase(config.getTradeType())){ paramMap.put("appid",config.getAppID()); paramMap.put("noncestr",nonceStr); paramMap.put("package","Sign=WXPay"); paramMap.put("partnerid",config.getMchID()); paramMap.put("timestamp",timeStamp); paramMap.put("sign",sign); paramMap.put("prepayid",prepayId); }else if("JSAPI".equalsIgnoreCase(config.getTradeType())){ paramMap.put("nonceStr",nonceStr); paramMap.put("sign",sign); paramMap.put("timeStamp",timeStamp); } return R.ok().put("paramMap",paramMap); }else { log.info("【微信支付二次签名模块】--失败"); return BoyMap .error("签名失败!"); } } catch (Exception e) { log.info("【微信支付二次签名模块】--异常"); e.printStackTrace(); } return BoyMap .error("签名失败!"); }













