微信h5支付,微信外浏览器支付实现

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兼容问题已经解决

我将基本会用到的jar贴一下

<dependency>
      <groupId>com.jfinal</groupId>
      <artifactId>enjoy</artifactId>
      <version>3.2</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-databind</artifactId>
      <version>2.6.0</version>
    </dependency>

    <dependency>
      <groupId>com.github.binarywang</groupId>
      <artifactId>weixin-java-mp</artifactId>
      <version>2.8.0</version>
    </dependency>

    <dependency>
      <groupId>com.jfinal</groupId>
      <artifactId>jfinal</artifactId>
      <version>3.2</version>
    </dependency>

    <dependency>
      <groupId>com.jfinal</groupId>
      <artifactId>jfinal-weixin</artifactId>
      <version>1.9</version>
    </dependency>

    <dependency>
      <groupId>com.github.javen205</groupId>
      <artifactId>IJPay</artifactId>
      <version>1.0</version>
    </dependency>

    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>fastjson</artifactId>
      <version>1.2.37</version>
    </dependency>


    <!-- okhttp2 -->
    <dependency>
      <groupId>com.squareup.okhttp</groupId>
      <artifactId>okhttp</artifactId>
      <version>2.7.5</version>
    </dependency>
    <dependency>
      <groupId>com.squareup.okhttp3</groupId>
      <artifactId>okhttp</artifactId>
      <version>3.8.0</version>
    </dependency>
    <dependency>
      <groupId>com.squareup.okio</groupId>
      <artifactId>okio</artifactId>
      <version>1.11.0</version>
    </dependency>

着手代码

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,感兴趣可以看他的更多相关支付

猜你喜欢

转载自blog.csdn.net/Goligory/article/details/78392505