版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_30641447/article/details/85128781
思路:
第一次做H5支付,在网上看到了很多文章,大多说怎么调用openId什么的。
其实H5支付不需要获取openId,而是根据用户的ip地址作为识别。
大体思路如下:
前端采用vue:
生成订单:
// 生成订单
submitOrder: function () {
let param = {userId: this.userId }
this.$http
.post('/api/xxxx', param, { emulateJSON: true })
.then(res => {
headerId = []
var arr = []
if (res.data.success == true) {
for (var i = 0; i < res.data.orders.length; i++) {
arr.push(res.data.orders[i].id)
}
headerId = arr//这里保存订单id
this.pay()//这里调用支付
} else {
Indicator.open({
text: '网络出错...',
spinnerType: 'fading-circle'
})
}
})
},
支付--调用统一订单
pay: function () {
var arr = headerId.join(',')
let param = {
total_fee: this.total,
attach: 'H5支付',
openId: '',
body: 'H5支付',
userId: this.userId,
requestNum: '',
payWays: this.payWay,//支付方式:11 微信 10 支付宝
headerIds: arr}
this.$http
.post('/api/xxxx', param, {emulateJSON: true})
.then(res => {
// eslint-disable-next-line eqeqeq
if (this.payWay == '11') {
window.location.href = res.body.data.payUrl + '&redirect_url=http%3a%2f%2fxxx.com' //这里的redirect_url 需要进行urlencode处理
} else {
var bhtml = res.body.data.payUrl
document.getElementById('alipay').innerHTML = bhtml
document.forms[0].submit()
}
})
},
调用统一订单接口:
@Override
public String orderPay(String outTradeNo) {
List<Map<String, Object>> jsonArray = new ArrayList<Map<String, Object>>();
TradeInfo tradeInfo = tradeInfoService.selectByOutTradeNo(outTradeNo);
String totle = String.valueOf(tradeInfo.getAmount());
DecimalFormat decimalFormat = new DecimalFormat("###################.###########");
totle = String.valueOf(decimalFormat.format(Double.parseDouble(totle)));
String appid = WECHAT_APPID;// 小程序ID-->替换为自己的ID
String mch_id = WECHAT_MCH_ID;// 商户号-->替换为自己的商户号
String nonce_str = UUIDHexGenerator.generate();// 随机字符串
String out_trade_no = outTradeNo;// 商户订单号mch_id + today + code
String notify_url = RETURN_URL + "/api/wx/payBack/notifyOrder";// 通知地址
// 交易类型JSAPI---NATIVE---MWEB
String trade_type = null;
String body = "xxxx";
String openId = null;
String spbillCreateIp = null;
if (tradeInfo.getTradeWay() == TradeWay.WECHAT_H5){
trade_type = "MWEB";
notify_url = RETURN_URL + "/api/xxx";// 通知地址--自己controler接口
Gson gson = new Gson();
@SuppressWarnings("unchecked")
Map<String, String> resultMap = gson.fromJson(tradeInfo.getExtraParams(), Map.class);
spbillCreateIp = resultMap.get("spbill_create_ip");
}
PaymentPo paymentPo = new PaymentPo();
paymentPo.setAppid(appid);
paymentPo.setMch_id(mch_id);
paymentPo.setNonce_str(nonce_str);
String newbody = new String(body);// 以utf-8编码放入paymentPo,微信支付要求字符编码统一采用UTF-8字符编码
paymentPo.setBody(newbody);
paymentPo.setOut_trade_no(out_trade_no);
paymentPo.setTotal_fee(String.valueOf(decimalFormat.format(Double.parseDouble(totle) * 100)));
paymentPo.setNotify_url(notify_url);
paymentPo.setTrade_type(trade_type);
paymentPo.setOpenid(openId);
paymentPo.setSpbill_create_ip(spbillCreateIp);
// 把请求参数打包成数组
Map<String, String> sParaTemp = new HashMap<String, String>();
sParaTemp.put("appid", paymentPo.getAppid());
sParaTemp.put("mch_id", paymentPo.getMch_id());
sParaTemp.put("nonce_str", paymentPo.getNonce_str());
sParaTemp.put("body", paymentPo.getBody());
sParaTemp.put("out_trade_no", paymentPo.getOut_trade_no());
sParaTemp.put("total_fee", paymentPo.getTotal_fee());
sParaTemp.put("notify_url", paymentPo.getNotify_url());
sParaTemp.put("trade_type", paymentPo.getTrade_type());
sParaTemp.put("openid", paymentPo.getOpenid());
sParaTemp.put("spbill_create_ip", paymentPo.getSpbill_create_ip());
// 除去数组中的空值和签名参数
Map<String, String> sPara = PayUtil.paraFilter(sParaTemp);
String prestr = PayUtil.createLinkString(sPara); // 把数组所有元素,按照“参数=参数值”的模式用“&”字符拼接成字符串
String key = "&key=" + WECHAT_MERC_KEY; // 商户支付密钥
// MD5运算生成签名
String mysign = PayUtil.sign(prestr, key, "utf-8").toUpperCase();
paymentPo.setSign(mysign);
// 打包要发送的xml
String respXml = MessageUtil.messageToXML(paymentPo);
// 打印respXml发现,得到的xml中有“__”不对,应该替换成“_”
respXml = respXml.replace("__", "_");
// 统一下单API接口链接
String result = PayUtil.httpRequest("https://api.mch.weixin.qq.com/pay/unifiedorder", "POST", respXml);
// 保存请求参数(忽略)
// 返回预支付单信息prepay_id
// 将解析结果存储在HashMap中
Map<String, String> map = new HashMap<String, String>();
InputStream in = new ByteArrayInputStream(result.getBytes());
// 读取输入流
SAXReader reader = new SAXReader();
Document document = null;
try {
document = reader.read(in);
} catch (DocumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// 得到xml根元素
if(null != document){
Element root = document.getRootElement();
// 得到根元素的所有子节点
@SuppressWarnings("unchecked")
List<Element> elementList = root.elements();
for (Element element : elementList) {
map.put(element.getName(), element.getText());
}
}
// 返回信息
String return_code = map.get("return_code");// 返回状态码
Map<String, Object> jsonObject = new HashMap<String, Object>();
if (return_code == "SUCCESS" || return_code.equals("SUCCESS")) {
// 业务结果
jsonObject.put("out_trade_no", out_trade_no);
jsonObject.put("return_code", return_code);
String prepay_id = map.get("prepay_id");// 返回的预付单信息
String nonceStr = UUIDHexGenerator.generate();
jsonObject.put("nonceStr", nonceStr);
jsonObject.put("package", "prepay_id=" + prepay_id);
Long timeStamp = System.currentTimeMillis() / 1000;
jsonObject.put("timeStamp", timeStamp + "");
String stringSignTemp =
"appId=" + appid + "&nonceStr=" + nonceStr + "&package=prepay_id=" + prepay_id + "&signType=MD5&timeStamp=" + timeStamp;
jsonObject.put("signType", "MD5");
jsonObject.put("codeUrl", map.get("code_url"));
// H5跳转URL
jsonObject.put("mweb_url", map.get("mweb_url"));
// 再次签名
String paySign = PayUtil.sign(stringSignTemp, "&key=" + WECHAT_MERC_KEY, "utf-8").toUpperCase(); // 替换为自己的密钥
jsonObject.put("paySign", paySign);
jsonArray.add(jsonObject);
} else {
jsonObject.put("return_code", return_code);
}
Gson gson = new Gson();
//这里操作订单 (略)
return gson.toJson(jsonObject);
}
其他工具类可以参考小程序支付的文章:
https://blog.csdn.net/qq_30641447/article/details/73222648
H5支付所遇到的坑:
第一:ip地址,本地公网ip,本地用request获取的都是127.0.0.1,所以在测试的时候最好先写死公网ip
第二:域名配置,需要在公众号配置而且前端(前后端分离的)的映射访问域名要一致,不然微信会检测referer与配置的域名是否一致
第三:微信、支付宝支付成功跳转到本系统页面(回调页面)配置:
微信
需要在MWEB_URL后拼接上redirect_url参数: 就是在返回的payUrl上拼接redirect_url(redirect_url进行urlencode处理)
支付宝
支付宝需要在支付宝回调的时候 在后端重定向到对应页面
由于返回后 前端的数据可能会丢失,所以第一可以一开始可以把前端的数据保存到本地缓存,第二在跳转的时候传参,返回页面的时候再获取
常见问题:https://pay.weixin.qq.com/wiki/doc/api/H5.php?chapter=15_4