参考文档:
https://mp.weixin.qq.com/mp/homepage?__biz=MzI3OTIwNDU0MA==&hid=2&sn=efa76e36c5b580e4119030b68278c05b
https://pay.weixin.qq.com/wiki/doc/apiv3/index.shtml
文章目录
前言
随着互联网技术的不断发展,移动支付逐渐成为了一种新型的支付方式,越来越多的项目也开始加入移动支付功能,其中以微信支付最具代表。前段时间接手了一个项目,其中就有用到微信支付中的Native支付、H5支付和JSAPI支付,接下来总结一下吧。
一、开发前准备
设置支付目录
请确保实际支付时的请求目录与后台配置的目录一致(现在已经支持配置根目录,配置后有一定的生效时间,一般5分钟内生效),否则将无法成功唤起微信支付。
在微信商户平台(pay.weixin.qq.com)设置您的公众号支付支付目录,设置路径:商户平台–>产品中心–>开发配置。公众号支付在请求支付的时候会校验请求来源是否有在商户平台做了配置,所以必须确保支付目录已经正确的被配置,否则将验证失败,请求支付不成功。
设置授权域名
开发JSAPI支付时,在统一下单接口中要求必传用户openid,而获取openid则需要您在公众平台设置获取openid的域名,只有被设置过的域名才是一个有效的获取openid的域名,否则将获取失败。
登录微信公众平台(https://mp.weixin.qq.com/)–>公众号设置–>功能设置–>网页授权域名。
二、Native支付
官方解释:
Native支付是指商户系统按微信支付协议生成支付二维码,用户再用微信“扫一扫”完成支付的模式。
统一下单接口
官方解释:
商户系统先调用该接口在微信支付服务后台生成预支付交易单,返回正确的预支付交易会话标识后再按Native、JSAPI、APP等不同场景生成交易串调起支付。
什么意思?简单来说就是我们每次调起微信支付窗口之前,需要先生成一个预支付交易单,这个单子相当于和我们自身系统的交易订单一一对应,也就是我们每次支付需要记录的订单支付交易单。
PS:调用统一下单接口时,需要注意的是必须传入异步接收微信支付结果通知的回调地址,通知url必须为外网可访问的url,且不能携带参数。
代码如下:
public HzRestResult weChatPay(HttpServletRequest request,String body, double cost,String serviceType,String orderNo,String flag){
try {
RestTemplate restTemplate = new RestTemplate();
restTemplate.getMessageConverters().set(1, new StringHttpMessageConverter(Charset.forName("UTF-8")));
//组装请求参数
String data = this.payString(request, body, orderNo, cost, flag);
logger.info("请求参数:"+data);
//请求统一下单接口
String result = restTemplate.postForObject(PAYURL, data, String.class);
Map map = XMLUtil.doXMLParse(result);
logger.info("微信支付返回:"+map);
//下单失败
if(RETURN_CODE_FAIL.equals((String) map.get(RETURN_CODE))){
return HzRestResult.getFailed(String.valueOf(map.get("err_code_des")));
}
QRCodeVo qrCodeVo = new QRCodeVo();
qrCodeVo.setOrderNo(insert);
String code_url = null;
//H5支付,拼接前端支付页面地址
if (StringUtils.isNotBlank(flag) && flag.equals(HealthConst.YES)){
code_url = (String) map.get("mweb_url");
String redirectUrl = h5Url+"h5-result";
code_url = code_url+"&redirect_url="+URLEncoder.encode(redirectUrl,"utf-8");
}else {
code_url = (String) map.get(CODE_URL);
}
qrCodeVo.setUrl(code_url);
return HzRestResult.getSuccess(qrCodeVo);
} catch (Exception e) {
logger.error("支付异常",e);
return HzRestResult.getFailed("支付异常",e.getMessage());
}
}
组装请求参数
private String payString(HttpServletRequest request, String body,String orderNo,double cost,String flag) throws Exception {
StringBuilder sb = new StringBuilder("");
String randomString = getRandomString(9);
String ip = getIp(request);
String tradeType = "NATIVE";
if (StringUtils.isNotBlank(flag) && HealthConst.YES.equals(flag)){
//H5支付参数
tradeType = "MWEB";
}
int co = BigDecimal.valueOf(cost).multiply(BigDecimal.valueOf(100)).intValue();
sb.append("<xml>");
sb.append("<appid>" + APPID + "</appid>");
sb.append("<body>" + body + "</body>");//商品描述
sb.append("<mch_id>" + MCHID + "</mch_id>"); //商户号
sb.append("<nonce_str>" + randomString + "</nonce_str>");
sb.append("<notify_url>" + NOTIFYURL + "</notify_url>");
sb.append("<out_trade_no>" + orderNo + "</out_trade_no>");//商户订单号
sb.append("<spbill_create_ip>" + ip + "</spbill_create_ip>");
sb.append("<total_fee>" + co + "</total_fee>");//标价金额
sb.append("<trade_type>" + tradeType+ "</trade_type>"); //交易类型
String sign = getSign(body, orderNo, co, randomString, ip, tradeType);
sb.append("<sign>" + sign + "</sign>");
sb.append("</xml>");
return sb.toString();
}
生成签名
public String getSign(String body,String orderNo,int cost,String nonceStr,String ip,String tradeType){
TreeMap