h5支付的资料还真叫个少,不过找到一个好的方式,按着大神的步骤去实现还真就ok了,话不多说,开始准备吧
看一下官方文档还是很必要的,知道必不可少的参数是什么:https://pay.weixin.qq.com/wiki/doc/api/H5.php?chapter=9_20&index=1
微信支付的坑很多,特别在平台的设置上
首先需要APPID,微信支付商户号mch_id,API密钥key,Appsecret(secret),说明在这里https://pay.weixin.qq.com/wiki/doc/api/H5.php?chapter=3_1
然后设置授权域名,在接口设置中就能找到,包括js接口安全域名和网页授权域名:
这个点进去之后会看到最下面两个:js接口安全域名,这个可以设置三个,就是填写你访问页面的域名即可
下面这个是网页授权回调域名,用于你支付完毕后回调的域名,将下载的文件放到服务器的根路径,确保可以访问,我是放在tomcat的webapp中
设置的域名要备案
然后设置支付域名,设置路径:商户平台-->产品中心-->开发配置中设置域名,
如果是公众号支付就设置对应的,要注意的是公众号支付授权域名为请求的前一级,比如你要请求http://xxx/wx/abc,那么你就设置http://xxx/wx即可
h5支付设置h5域名就行,不用后缀,直接写你要设置的域名
partnerkey需要在API中设置,需要安装证书,这个根据提示安装即可,自行设置32位partnerkey
我用的是一个大神的IJPay的springboot版,写成自己的SpringMVC版,后续都会给链接
主要用的的是封装的jar,现在maven库中已经有了,用0.8版本以上的吧,这样jdk兼容问题已经解决
着手代码
public static WxPayApiConfig getApiConfig() {
return WxPayApiConfig.New()
.setAppId(appID)
.setMchId(mchID)
.setPaternerKey(partnerKey)
.setPayModel(WxPayApiConfig.PayModel.BUSINESSMODEL);
}
/**
* 微信H5 支付--------------------好使
* 注意:必须再web页面中发起支付且域名已添加到开发配置中
*/
@RequestMapping(value ="/pay.do",method = {RequestMethod.POST,RequestMethod.GET})
public void wapPay(HttpServletRequest request,HttpServletResponse response){
System.out.println("--pay start--");
String notify_url = "https://你的域名/pay_notify.do";//这是回调地址,方法在下面
//获取ip
String ip = IpKit.getRealIp(request);
if (com.jpay.ext.kit.StrKit.isBlank(ip)) {
ip = "127.0.0.1";
}
H5ScencInfo sceneInfo = new H5ScencInfo();
H5 h5_info = new H5();
h5_info.setType("Wap");
//此域名必须在商户平台--"产品中心"--"开发配置"中添加
h5_info.setWap_url("http://www.xxx.com");
h5_info.setWap_name("公司官网");
sceneInfo.setH5_info(h5_info);
WxPayApiConfig wxPayApiConfig=getApiConfig();
Map<String, String> params=WxPayApiConfig.New()
.setAppId(appID)
.setMchId(mchID)
.setBody("H5支付测试")
.setSpbillCreateIp(ip)
.setTotalFee("520")
.setTradeType(WxPayApi.TradeType.MWEB)
.setNotifyUrl(notify_url)
.setPaternerKey(partnerKey)
.setOutTradeNo(String.valueOf(System.currentTimeMillis()))
.setSceneInfo("{\"h5_info\": {\"type\":\"IOS\",\"app_name\": \"mtgg\",\"package_name\": \"com.tencent.tmgp.sgame\"}}")
.setAttach("H5支付测试")
.build();
String xmlResult = WxPayApi.pushOrder(false,params);
Map<String, String> result = PaymentKit.xmlToMap(xmlResult);
//返回结果
String return_code = result.get("return_code");
String return_msg = result.get("return_msg");
if (!PaymentKit.codeIsOK(return_code)) {
log.error("return_code>"+return_code+" return_msg>"+return_msg);
throw new RuntimeException(return_msg);
}
String result_code = result.get("result_code");
if (!PaymentKit.codeIsOK(result_code)) {
log.error("result_code>"+result_code+" return_msg>"+return_msg);
throw new RuntimeException(return_msg);
}
// 以下字段在return_code 和result_code都为SUCCESS的时候有返回
String prepay_id = result.get("prepay_id");
String mweb_url = result.get("mweb_url");
log.info("prepay_id:"+prepay_id+" mweb_url:"+mweb_url);
try {
response.sendRedirect(mweb_url);
} catch (IOException e) {
e.printStackTrace();
}
}
先给出H5ScencInfo
package com.mtgg.entity;
import com.jfinal.kit.JsonKit;
/**
* @author Javen
*/
public class H5ScencInfo {
private H5 h5_info;
public H5 getH5_info() {
return h5_info;
}
public void setH5_info(H5 h5_info) {
this.h5_info = h5_info;
}
@Override
public String toString() {
return JsonKit.toJson(h5_info);
}
public static class H5{
private String type;
private String app_name;
private String bundle_id;
private String package_name;
private String wap_url;
private String wap_name;
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getApp_name() {
return app_name;
}
public void setApp_name(String app_name) {
this.app_name = app_name;
}
public String getBundle_id() {
return bundle_id;
}
public void setBundle_id(String bundle_id) {
this.bundle_id = bundle_id;
}
public String getPackage_name() {
return package_name;
}
public void setPackage_name(String package_name) {
this.package_name = package_name;
}
public String getWap_url() {
return wap_url;
}
public void setWap_url(String wap_url) {
this.wap_url = wap_url;
}
public String getWap_name() {
return wap_name;
}
public void setWap_name(String wap_name) {
this.wap_name = wap_name;
}
}
}
注意notify_url要保证能够访问,用域名访问
最后发送mweb_url就可以打开微信进行支付了
给出支付成功的返回
@RequestMapping(value = "/pay_notify.do",method={RequestMethod.POST,RequestMethod.GET})
@ResponseBody
public void pay_notify(HttpServletRequest request,HttpServletResponse response) {
// 支付结果通用通知文档: https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_7
System.out.println("---------------支付回调----------------");
String xmlMsg = HttpKit.readData(request);
System.out.println("pay notice---------"+xmlMsg);
Map<String, String> params = PaymentKit.xmlToMap(xmlMsg);
// String appid = params.get("appid");
// //商户号
// String mch_id = params.get("mch_id");
String result_code = params.get("result_code");
// String openId = params.get("openid");
// //交易类型
// String trade_type = params.get("trade_type");
// //付款银行
// String bank_type = params.get("bank_type");
// // 总金额
String total_fee = params.get("total_fee");
total_fee = total_fee.substring(0,total_fee.length()-2);
// //现金支付金额
// String cash_fee = params.get("cash_fee");
// // 微信支付订单号
// String transaction_id = params.get("transaction_id");
// // 商户订单号
// String out_trade_no = params.get("out_trade_no");
// // 支付完成时间,格式为yyyyMMddHHmmss
// String time_end = params.get("time_end");
/////////////////////////////以下是附加参数///////////////////////////////////
String account = params.get("attach");
System.out.println("回调total_fee-->"+total_fee);
System.out.println("回调account-->"+account);
// String fee_type = params.get("fee_type");
// String is_subscribe = params.get("is_subscribe");
// String err_code = params.get("err_code");
// String err_code_des = params.get("err_code_des");
// 注意重复通知的情况,同一订单号可能收到多次通知,请注意一定先判断订单状态
// 避免已经成功、关闭、退款的订单被再次更新
// Order order = Order.dao.getOrderByTransactionId(transaction_id);
// if (order==null) {
String resXml = "";
WxPayApiConfigKit.setThreadLocalWxPayApiConfig(getApiConfig());
if(PaymentKit.verifyNotify(params, WxPayApiConfigKit.getWxPayApiConfig().getPaternerKey())){
if (("SUCCESS").equals(result_code)) {
// TODO 根据商户订单号更改押金状态
System.out.println("成功,存储");
resXml = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>"
+ "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> ";
} else {
//TODO FAIL支付失败
log.debug("支付失败的回调消息");
resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>"
+ "<return_msg><![CDATA[报文为空]]></return_msg>" + "</xml> ";
}
BufferedOutputStream out = null;
try {
out = new BufferedOutputStream(
response.getOutputStream());
out.write(resXml.getBytes());
out.flush();
out.close();
} catch (Exception e) {
e.printStackTrace();
}
}
// }
}
这里需要注意最后的xml.put(),return PaymentKit.toXml(xml)一定要返回给微信,SUCCESS表示商户接收通知成功并校验成功,这样微信才会知道商户支付成功,否则会不断通知,这样就会重复处理数据,这个错误是致命的
上面回调我改了一下,可以做到闭嘴,不会重复通知
下面就是测试调起微信支付
常见错误:
1、网络环境未能通过安全验证,请稍后再试(IP改变导致的)
2、商家参数格式有误,请联系商家解决(H5支付的referer为空导致)
3、商家存在未配置的参数,请联系商家解决(H5支付的域名问题)
4、支付请求已失效,请重新发起支付(有效期为5分钟)
5、请在微信外打开订单,进行支付(H5支付不能直接在微信客户端内调起)
我遇到过一次获取code时回调了两次错误,因为code只能用一次,第二次就失效了网上说什么的都有,谁知道怎么完全解决可以留言,感谢
我的demo地址:注意回调不用demo中的,用这篇文档的回调方式,可以闭嘴(包括公众号支付):http://download.csdn.net/download/goligory/10044575
借鉴:http://blog.csdn.net/zyw_java/article/details/77507835,感兴趣可以看他的更多相关支付