近期,公司内部移动办公系统要实现微信支付模块,用于员工缴费,微信支付页面用h5开发,嵌入移动办公系统,后台是java。
大致流程如下:
1.前端页面生成订单(确定支付金额、订单详情,此步骤根据业务而定)
2.调用后台预支付接口,该接口需要请求微信统一支付接口,拿到返回链接,直接将该链接返回给前端
3.前端拿到连接后,通过iframe请求该链接。
4.该请求会响应一个请求页面,这个页面自动加载,最终会调用“weixin://”协议打开微信
5.如果支付成功,微信会回调后台提供的支付通知接口,该接口接收微信回调和修改本地订单状态。
目前已发现和解决的问题如下:
1. 不能从微信中打开本地app
由于设置了回调页面,而回调页面也是h5开发的,与app无关,所以redirect_url这个参数必须要传。我们的思路是修改app的schemal,如果任意修改,微信会报错参数错误,经过测试只能修改成支付平台中配置的域名,如支付域名是www.abc.com,则对应的schemal也是这个。app拦截schemal,对后面拼接的连接直接进行转发即可。
2. 唤醒微信空白页
这个问题是原因在于前端接收到预支付后返回的url,但是没有成功发送响应请求。代码中再次发送。(这个问题i只有ios13中出现,不是很理解,不过采用这种方法即可)
代码如下
/** 预支付
* 发起支付
*/
@SuppressWarnings("unchecked")
@PostMapping(value = "/pay")
@Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
public Map<String, String> pay(HttpServletRequest request,@RequestBody Map<String,Object> param) {
String userCode = String.valueOf(String.valueOf(param.get("userCode")));
String ym = String.valueOf(String.valueOf(param.get("ym")));
Order order = orderMapper.selectOrderByThisYear(ym,userCode);
Map<String,String> map = new HashMap<>();
map.put("result_code", "fail");
if(Objects.isNull(order)) {
map.put("msg", "未查询到可支付的订单,无需支付");
return map;
}
if(order.getStatus() == 3 || order.getStatus() == 2) { //支付成功
map.put("msg", "订单已经完成,无需重复支付");
return map;
}
EntityWrapper<Order> wrapper = new EntityWrapper<>();
wrapper.eq("user_id", userCode).eq("period", ym);
Order entity = new Order();
entity.setUserId(userCode);
entity.setOrderNo("");
entity.setStatus(1);
int flag = 0;
if(order.getStatus() == 0 || order.getStatus() == 4) {//交易关闭、取消
flag = 1;
}
if(!Objects.isNull(order.getCreateTime()) && order.getStatus() == 1 && System.currentTimeMillis() - order.getCreateTime().getTime() >= 5 * 60 * 1000 ) {//超时
flag = 2;
}
if(flag != 0) {
orderMapper.update(entity, wrapper);
orderMapper.updateDateTime(ym,userCode);
if(flag == 1) {
map.put("msg", "订单异常,请重新发起支付");
}else if(flag == 2) {
map.put("msg", "订单超时,请重新发起支付");
}
return map;
}
//更新订单创建时间和生成订单号
entity.setCreateTime(new Date());
entity.setOrderNo(System.currentTimeMillis() + "");
orderMapper.update(entity, wrapper);
SortedMap<Object, Object> packageParams = new TreeMap<Object, Object>();
String currTime = PayToolUtil.getCurrTime();
String strTime = currTime.substring(8, currTime.length());
String strRandom = PayToolUtil.buildRandom(4) + "";
String nonce_str = strTime + strRandom;
packageParams.put("appid", WXConfig.appid);
packageParams.put("mch_id", WXConfig.mch_id);
packageParams.put("nonce_str", nonce_str);
packageParams.put("body", order.getDesc());
packageParams.put("out_trade_no", entity.getOrderNo());
Double totalFee = order.getPayment().doubleValue() * 100;
packageParams.put("total_fee", totalFee.intValue() + ""); // 价格的单位为分
packageParams.put("spbill_create_ip",getIP(request));
packageParams.put("notify_url", WXConfig.notify_url);
packageParams.put("trade_type", WXConfig.tradeType);
String sign = PayToolUtil.createSign("UTF-8", packageParams, WXConfig.key);
packageParams.put("sign", sign);
String requestXML = PayToolUtil.getRequestXml(packageParams);
String resXml = HttpUtil.postData(WXConfig.unifiedorder_url,
requestXML);
try {
map = XMLUtil.doXMLParse(resXml);
/*System.out.println(resXml);*/
if(!map.get("return_code").equals("FAIL")) {
//确认支付过后跳的地址,需要经过urlencode处理,必须为h5支付绑定的域名
String urlString = URLEncoder.encode("http:\\xxxxx.com/index.html#/success?userCode=" + userCode, "GBK");//该地址为支付成功后回调的页面,该页面需要轮训订单详情接口;;判断订单状态
String mweb_url = map.get("mweb_url") + "&redirect_url=" + urlString;
map.clear();
map.put("result_code", "success");
//mweb_url就是唤醒微信的深度连
map.put("mweb_url", mweb_url);
}else {
map.put("result_code", "fail");
}
} catch (JDOMException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return map;
}
回调方法
/**
* 异步回调
*/
@PostMapping(value = "/notify")
public Boolean notify(@RequestBody String notifyData) throws Exception {
try {
Map<String,String> doXMLParse = XMLUtil.doXMLParse(notifyData);
String string = doXMLParse.get("out_trade_no");
EntityWrapper<Order> wrapper = new EntityWrapper<>();
wrapper.eq("order_no", string);
Order order = orderService.selectOne(wrapper);
order.setStatus(3);
orderMapper.update(order, wrapper);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
效果展示