开发前准备工作:先设置微信公众号里的商户ID和微信商家里的商户ID保持一致,申请到公众平台的Appid,设置好回调域名
JAVA后台controller层的代码如下:
/**
* 微信PC和H5支付
*
* @return
*/
@PostMapping("/createNative")
@ApiOperation("微信支付,生成二维码,orderId订单ID,payType(1PC端,2是web端)")
public DzResult createNative(String orderId, String payType) throws Exception {
if (StringUtils.isBlank(orderId) || StringUtils.isBlank(payType)) {
System.out.println("空参数-------------------->");
return new DzResult().error(OrderCode.ERROR_PARAMS, "参数错误");
}
// 获取当前用户
Claims claims = (Claims) request.getAttribute("authInfo");
if (claims == null) {
return new DzResult().error(OrderCode.NOT_LEGAL, "非法操作");
}
// 调用业务层查询订单信息
CyOrder order = weixinPayService.findOrderByOrderId(orderId);
if (order == null) {
return new DzResult().error(OrderCode.ORDER_NOT_EXIST, "订单不存在");
}
if (!claims.getId().equals(order.getFrontUserId())) {
return new DzResult().error(OrderCode.NOT_BELONG, "订单不属于当前用户");
}
// 商户订单号(用下划线后面数字为标识1:支付定金 2:支付尾款)
BigDecimal totalAmount;
// 将数据库价格去分
BigDecimal mun = BigDecimal.valueOf(100);
// 名称
String subject;
//设置商品详情参数
HashMap<String, String> attach = new HashMap<>(16);
// 订单号
String outTradeNo = orderId;
//订单号
attach.put("outTradeNo", outTradeNo);
//自定义参数
String attachStr;
//全款
//微信是按分为单位,此处*100去分:
totalAmount = order.getPayMoney().multiply(mun);
subject = "呈衣全款支付";
attach.put("subject", subject);
//去掉小数点后的零
attach.put("totalAmount", totalAmount.stripTrailingZeros().toPlainString());
attachStr = JSONUtils.toJSONString(attach);
Map map = weixinPayService.createNative(outTradeNo, totalAmount.stripTrailingZeros().toPlainString(), attachStr, payType);
if (map == null) {
return new DzResult().error(OrderCode.ORDER_WECAT_ERROR, "微信支付异常");
}
if ("FAIL".equals(map.get("return_code"))) {
System.out.println("通讯异常");
Object errorMsg = map.get("return_msg");
return new DzResult().error(OrderCode.ORDER_WECAT_ERROR, errorMsg + "");
}
if ("FAIL".equals(map.get("result_code"))) {
System.out.println("支付错误");
Object errCode = map.get("err_code");
Object errCodeDes = map.get("err_code_des");
String errorMsg = errCode + " : " + errCodeDes;
return new DzResult().error(OrderCode.ORDER_WECAT_ERROR, errorMsg + "");
}
boolean haveMwebUrl = map.containsKey("mweb_url");
System.out.println("是否有拉起微信APP的路径:" + haveMwebUrl);
/* if (haveMwebUrl) {
String mwebUrl = map.get("mweb_url").toString();
System.out.println("支付跳转链接:" + mwebUrl);
//请求该url来拉起微信客户端
weixinPayService.requestMwebUrl(mwebUrl);
}*/
return new DzResult().success(map);
}
业务层调微信统一下单接口代码如下:
/**
* 生成二维码
*/
@Override
public Map createNative(String outTradeNo, String totalFee, String attachStr, String payType) {
System.out.println("进入生成二维码方法");
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
//设置失效时间5分钟
String expireTime = getOrderExpireTime(300 * 1000L);
//1.创建参数
HashMap<String, String> param = new HashMap<>(16);
//公账号
param.put("appid", WeChatPayConfig.app_id);
//自定义参数(attach)
param.put("attach", attachStr);
//商品描述:商品名字
param.put("body", "呈衣定制");
//商户号
param.put("mch_id", WeChatPayConfig.partner);
//随机字符串
param.put("nonce_str", WXPayUtil.generateNonceStr());
//回调地址
param.put("notify_url", WeChatPayConfig.notify_url);
//订单号
param.put("out_trade_no", outTradeNo);
//ip,
param.put("spbill_create_ip", getIpAddr(request));
//设置失效时间5分钟
param.put("time_expire", expireTime);
//总金额(分)
param.put("total_fee", totalFee);
//1是PC 2 是webApp
if ("1".equals(payType)) {
//交易类型
param.put("trade_type", "NATIVE");
}
if ("2".equals(payType)) {
//交易类型
param.put("trade_type", "MWEB");
//支付场景,H5必填
param.put("scene_info", "{\"h5_info\": {\"type\":\"Wap\",\"wap_url\": \"https://www.cyclothes.cn\",\"wap_name\": \"呈衣服装定制\"}}");
}
try {
//2.生成要发送的xml,方法中传入签名
String xmlParam = WXPayUtil.generateSignedXml(param, WeChatPayConfig.partner_key);
System.out.println("调用微信的请求方法,请求的参数" + xmlParam);
//请求的url地址
HttpClient httpclient = new HttpClient(WeChatPayConfig.request_url);
//是否是https协议
httpclient.setHttps(true);
//发送的xml数据
httpclient.setXmlParam(xmlParam);
//执行的请求方法
httpclient.post();
Map<String, String> returnMap = WXPayUtil.xmlToMap(httpclient.getContent());
returnMap.put("out_trade_no", outTradeNo);
returnMap.put("attach", attachStr);
System.out.println("调用统一下单接口正常");
return returnMap;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
微信异步通知代码如下:
/**
* 微信回调方法,被拦截器放行
* 注意该回调方法要和微信配置中心中的回调方法保持一致
*
* @throws Exception
*/
@PostMapping("/WeChatPayNotify")
@ApiOperation("微信回调方法")
public void weChatPayNotify() throws Exception {
System.out.println("进入回调方法,核对订单信息");
//读取参数
InputStream inputStream;
StringBuffer sbs = new StringBuffer();
inputStream = request.getInputStream();
String s;
BufferedReader in = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
while ((s = in.readLine()) != null) {
sbs.append(s);
}
in.close();
inputStream.close();
//解析xml成map
Map<String, String> map = WXPayUtil.xmlToMap(sbs.toString());
if (map == null) {
logger.info("支付异常");
System.out.println("支付异常");
}
System.out.println("支付数据正常");
String resXml = "";
if (!"SUCCESS".equals(map.get("return_code"))) {
Object errorMsg = map.get("return_msg");
System.out.println("微信通信异常: " + errorMsg);
logger.info("微信通信异常: " + errorMsg);
resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>"
+ "<return_msg><![CDATA[" + errorMsg + "]]></return_msg>" + "</xml>";
}
System.out.println("通讯正常");
//自定义参数
JSONObject attach = JSON.parseObject(map.get("attach"));
String outTradeNo = (String) attach.get("outTradeNo");
String subject = (String) attach.get("subject");
String totalAmount = (String) attach.get("totalAmount");
System.out.println("attach中获取的总金额" + totalAmount);
String appid = map.get("appid");
String tradeNo = map.get("out_trade_no");
// 获取订单号以及交易类型(如果有)
Map<String, String> checkMap = new HashMap<>(16);
checkMap.put("out_trade_no", outTradeNo);
// 创建参数校验载体Map
checkMap.put("total_amount", totalAmount);
checkMap.put("app_id", appid);
String orderId = checkMap.get("out_trade_no");
//获取验签参数
SortedMap<Object, Object> packageParams = WeixinPayCommonUtil.isTrue(map);
//验证签名是否成功
boolean tenpaySign = WeixinPayCommonUtil.isTenpaySign("UTF-8", packageParams, WeChatPayConfig.partner_key);
if (!tenpaySign) {
// 校验参数
System.out.println("验证签名失败");
logger.info("验证签名失败");
} else {
// 校验参数
weixinPayService.checkRequest(checkMap);
if ("FAIL".equals(map.get("result_code"))) {
Object errorMsg = map.get("return_msg");
System.out.println("微信支付异常: " + errorMsg);
logger.info("微信支付失败: " + errorMsg);
resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>"
+ "<return_msg><![CDATA[" + errorMsg + "]]></return_msg>" + "</xml> ";
} else {
System.out.println("通信成功");
String mch_id = (String) packageParams.get("mch_id");
String openid = (String) packageParams.get("openid");
String is_subscribe = (String) packageParams.get("is_subscribe");
String outTradeNo1 = (String) packageParams.get("out_trade_no");
String totalFee = (String) packageParams.get("total_fee");
//判断当前的订单是否已经处理
boolean isHandle = weixinPayService.isHandle(orderId);
String message;
if (!isHandle) {
// 未处理,处理业务,修改状态
weixinPayService.updateStatus(orderId, tradeNo, totalAmount);
System.out.println("已处理支付业务...");
message = subject + ":支付成功,处理业务";
logger.info("message" + message);
} else {
System.out.println("SUCCESS: 已处理支付业务,无需再次处理...");
message = subject + ":支付成功,已处理支付业务,无需再次处理";
// logger.info("mch_id:" + mch_id);//商户id
// logger.info("openid:" + openid);//用户标识
// logger.info("is_subscribe:" + is_subscribe);//是否关注公众号
logger.info("out_trade_no:" + outTradeNo1);
logger.info("total_fee:" + totalFee);
logger.info("message" + message);
}
logger.info("支付成功");
//通知微信.异步确认成功.必写.不然会一直通知后台.10次之后就认为交易失败了.
resXml = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>"
+ "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> ";
System.out.println("支付成功结束");
}
}
BufferedOutputStream out = new BufferedOutputStream(
response.getOutputStream());
out.write(resXml.getBytes());
out.flush();
out.close();
}
注意:微信调用异步通知签名验证失败很有可能是partner_key(商户key)在商户品台中没设置,或者和代码中的没有保持一致导致的