Java集成微信H5支付/支付宝手机网站支付

微信H5支付:

1,微信外部H5支付:

名词解释:就是在自己的H5网站页面里调用微信支付功能,注意,这里只能是在微信外部支付,在微信内打开网站是无法支付的,

要另外使用微信公众号支付

调用微信H5支付接口前提条件:

1,注册公众号并且通过认证

2,在公众号里申请微信支付,成为商户号

3,在商户平台里申请H5支付

4,在商户平台里的开发设置里设置好H5支付域名

以上4个条件都满足时,便可以调用微信H5支付接口

https://pay.weixin.qq.com/wiki/doc/api/H5.php?chapter=9_20&index=1

上面网址是微信支付API文档,先了解调用接口需要传递的参数,仅看必填项:

appid:微信分配的公众账号ID(企业号corpid即为此appId)

mch_id:微信支付分配的商户号

nonce_str:随机字符串,不长于32位

sign:签名

body:商品简单描述,该字段须严格按照规范传递

out_trade_no:商户系统内部的订单号,32个字符内、可包含字母

total_fee:订单总金额,单位为分

spbill_create_ip:必须传正确的用户端IP

notify_url:接收微信支付异步通知回调地址,通知url必须为直接可访问的url,不能携带参数

trade_type:H5支付的交易类型为MWEB

scene_info:场景信息,API文档上标注是必填项,但是我没用到,也成功了

以上就是我们要调用微信H5支付接口需要的参数,然后按照以下步骤处理参数:

先调用统一下单接口,获取微信跳转的支付页面URL地址

1,先生成一个随机的32位字符串

String nonceStr = UUID.randomUUID().toString().trim().replaceAll("-", "").toUpperCase();

2,生成签名sign

把参数封装成一个map,传递到以下方法中,得到一个排序后的字符串stringA:

Map<String, String> map = new HashMap<String, String>();
map.put("nonce_str", nonceStr);
map.put("appid", appId);
map.put("mch_id", mchId);
map.put("body", body);
map.put("out_trade_no", outTradeNo);
map.put("total_fee", totalFee);
map.put("spbill_create_ip", "113.87.162.45");
map.put("notify_url", notifyUrl);
map.put("trade_type", tradeType);
map.put("key", key);

注意:这个key是商户平台里面设置的一个秘钥key,要拼接到参数中

String stringA = UnicodeUtils.formatUrlMap(map, false, true);

/** 
     *  
     * 方法用途: 对所有传入参数按照字段名的Unicode码从小到大排序(字典序),并且生成url参数串<br> 
     * 实现步骤: <br> 
     *  
     * @param paraMap   要排序的Map对象 
     * @param urlEncode   是否需要URLENCODE 
     * @param keyToLower    是否需要将Key转换为全小写 
     *            true:key转化成小写,false:不转化 
     * @return 
     */  
    public static String formatUrlMap(Map<String, String> paraMap, boolean urlEncode, boolean keyToLower){  
        String buff = "";  
        Map<String, String> tmpMap = paraMap;  
        try{  
            List<Map.Entry<String, String>> infoIds = new ArrayList<Map.Entry<String, String>>(tmpMap.entrySet());  
            // 对所有传入参数按照字段名的 ASCII 码从小到大排序(字典序)  
            Collections.sort(infoIds, new Comparator<Map.Entry<String, String>>() {  
   
                @Override  
                public int compare(Map.Entry<String, String> o1, Map.Entry<String, String> o2) {  
                    return (o1.getKey()).toString().compareTo(o2.getKey());  
                }  
            });  
            // 构造URL 键值对的格式  
            StringBuilder buf = new StringBuilder();  
            for (Map.Entry<String, String> item : infoIds){  
                if (StringUtils.isNotBlank(item.getKey())){  
                    String key = item.getKey();  
                    String val = item.getValue();  
                    if (urlEncode){  
                        val = URLEncoder.encode(val, "utf-8");  
                    }  
                    if (keyToLower){  
                        buf.append(key.toLowerCase() + "=" + val);  
                    } else {  
                        buf.append(key + "=" + val);  
                    }  
                    buf.append("&");  
                }  
            }  
            buff = buf.toString();  
            if (buff.isEmpty() == false){  
                buff = buff.substring(0, buff.length() - 1);  
            }  
        } catch (Exception e){  
           return null;  
        }  
        return buff;  
    }  

通过上面方法得到排序后的字符串stringA后,拼接key并用MD5加密:

String stringSignTemp = stringA + "&key=" + key;

String sign = MD5Utils.getMD5(stringSignTemp);

签名sign就得到了

3,在把包括签名sign的所有参数转成XML格式:

Map<String, String> map = new HashMap<String, String>();
map.put("nonce_str", nonceStr);
map.put("appid", appId);
map.put("mch_id", mchId);
map.put("body", body);
map.put("out_trade_no", outTradeNo);
map.put("total_fee", totalFee);
map.put("spbill_create_ip", "113.87.162.45");
map.put("notify_url", notifyUrl);
map.put("trade_type", tradeType);
map.put("key", key);
		
String sign = WeChatUtil.buildSign(map);
log.info("生成的支付签名:" + sign);
map.put("sign", sign);
		
String xmlBody = XmlUtils.map2Xml(map);
log.info("微信支付XML参数:" + xmlBody);

4,把XML作为参数调用微信的支付接口:

String payUrl = wechatConfig.getPayUrl();
String result = HttpUtils.getInstance().postXml(payUrl, xmlBody);
log.info("微信支付结果:" + result);
Map<String, Object> resultMap = XmlUtils.xml2Map(result);
//微信返回的微信支付页面URL
mwebUrl = (String)resultMap.get("mweb_url");
mwebUrl = mwebUrl + "|" + outTradeNo;
			
String prepayId = (String)resultMap.get("prepay_id");
String returnCode = (String)resultMap.get("return_code");
if(returnCode.equals("SUCCESS")){
    //开始业务操作....
}

如果参数都没有问题调用接口返回就会包含mweb_url这样一个参数

这是微信支付的页面跳转链接,当返回结果为SECCUSS的时候处理完业务,把mweb_url返回到前端页面进行跳转

注意:如果trade_type设置的不是mweb_url的话是没有这个参数返回的

5,验证支付结果

当跳转到微信支付页面的时候,支付的过程其实是在微信APP里面操作的了,跟我们没什么关系

当支付完成后,默认会跳转回我们发起支付的页面,2种方式验证:

1,被动验证:

通过前面设置的notify_url地址,微信回给这个地址发送支付结果,在这个地址处理微信发送的信息再做业务处理

public Object receiveWechatPayResult(HttpServletResponse response, HttpServletRequest request) {
	    String resXml = "";  
        InputStream inStream;  
        try {  
            inStream = request.getInputStream();  
  
            ByteArrayOutputStream outSteam = new ByteArrayOutputStream();  
            byte[] buffer = new byte[1024];  
            int len = 0;  
            while ((len = inStream.read(buffer)) != -1) {  
                outSteam.write(buffer, 0, len);  
            }  
            outSteam.close();  
            inStream.close();  
            String result = new String(outSteam.toByteArray(), "utf-8");// 获取微信调用我们notify_url的返回信息  
            log.error("微信支付----result----=" + result);  
            Map<String, Object> map = XmlUtils.xml2Map(result);
            //业务操作.....
            resXml = "<xml>"
            		+ "<return_code><![CDATA[SUCCESS]]></return_code>"  
                    + "<return_msg><![CDATA[OK]]></return_msg>"
                    + "</xml> ";  
            
        } catch (IOException e) {  
            // TODO Auto-generated catch block  
        	log.error("支付回调发布异常:" + e);  
            e.printStackTrace();  
        }  
        return resXml;
	}

这里要注意的是返回的内容

<xml>
     <return_code><![CDATA[SUCCESS]]></return_code>
     <return_msg><![CDATA[OK]]></return_msg>
</xml> 

如果没有正确返回,会连续收到微信服务器推送的消息

如果正确返回了,只会收到3次微信推送的消息

这是本人的猜测,如果有大神清楚的请留言告知....

这里会有个延时的问题,就是微信有可能没有及时的往你这个地址发送,所以为了不影响业务,我们还可以主动验证

2,主动验证:

调用微信的订单查询接口查询刚刚的微信支付是否成功

                Map<String, String> map = new HashMap<String, String>();
		String nonceStr = UUID.randomUUID().toString().trim().replaceAll("-", "").toUpperCase(); 
		String appId = wechatConfig.getAppId();
		String mchId = wechatConfig.getMchId();
		String key = wechatConfig.getKey();
		map.put("nonce_str", nonceStr);
		map.put("appid", appId);
		map.put("mch_id", mchId);
		map.put("out_trade_no", tradeNo);
		map.put("key", key);
		
		String sign = WeChatUtil.buildSign(map);
		map.put("sign", sign);
		log.info("生成的支付签名:" + sign);
		
		String xmlBody = XmlUtils.map2Xml(map);
		log.info("微信查询订单状态XML参数:" + xmlBody);
		
		try {
			String queryPayStatusUrl = wechatConfig.getQueryPayStatusUrl();
			String result = HttpUtils.getInstance().postXml(queryPayStatusUrl, xmlBody);
			log.info("微信查询订单状态结果:" + result);
			Map<String, Object> resultMap = XmlUtils.xml2Map(result);
			tradeState = (String)resultMap.get("trade_state");
			if(tradeState.equals("SUCCESS")){
				//处理业务....
			}
		} catch (WrongHttpParameterException | IOException
				| InvalidHttpResponseException | EmptyDestinationException e) {
			e.printStackTrace();
		}

测试过程中有个问题,就是spbill_create_ip这个参数的问题,说是要用发起支付的用户端IP,但是根据文档获取了之后发现一直提示这个IP不对,然后写死了113.87.162.45这个IP居然还能支付成功,搞不懂

2,微信内部H5支付

前面几个条件参照上面写的外部H5支付,

然后在商户平台里的开发设置里配置好公众号支付的支付授权目录

也是要先调用统一下单接口,这个步骤参照上面写的,只有2个区别

1,trade_type这个参数的取值改为JSAPI

2,新增openId参数

openId的获取自己看一下API,很简单

微信内部H5支付与外部H5支付的最大区别在于微信外部H5支付是通过统一下单返回的mweb_url这个参数跳转到微信支付页面

而微信内部H5支付是把统一下单接口返回的prepay_id参数跟其他参数一起返回到前端页面,再由前端页面调用微信支付JS的API来跳转

Map<String, String> premap = new HashMap<String, String>();
premap.put("appId", appId);
premap.put("timeStamp", timeStamp.toString());
premap.put("nonceStr", nonceStr);
premap.put("package", "prepay_id=" + prepayId);
premap.put("signType", "MD5");
premap.put("key", key);
				
sign = WeChatUtil.buildSign(premap, false, false);
log.info("生成的支付签名:" + sign);
				
jsonObject.put("appId", appId);
jsonObject.put("timeStamp", timeStamp.toString());
jsonObject.put("nonceStr", nonceStr);
jsonObject.put("package", "prepay_id=" + prepayId);
jsonObject.put("signType", "MD5");
jsonObject.put("paySign", sign);
				

这里要注意的是以下几点:

1,又要重新获取一次签名sign,而且这里的appId,这个i是大写的,之前是小写的

2,timeStamp参数要用字符串,不然后面转成JSON的时候会没有双引号,导致微信服务器解析不到这个字段

3,加密方式,有些人会出现支付签名验证失败,是由于后台的加密跟前端的加密不一致导致的,统一加密方式即可

前端代码:

1,先引入微信支付JS API

<script type="text/javascript" src="http://res.wx.qq.com/open/js/jweixin-1.0.0.js"></script>

2,调用JS触发微信APP支付

WeixinJSBridge.invoke(
     'getBrandWCPayRequest', data, function(res){
          if(res.err_msg == "get_brand_wcpay_request:ok" ) {
               //doit 这里处理支付成功后的逻辑,通常为页面跳转
               $("#returnpay").show();
          }else{
                alert('支付失败,请联系管理员!res.err_msg:' + res.err_msg);
          }
     }
);

这里的data是后台封装好的JSON对象,作为参数代入,res是微信返回的数据

验证支付状态参照上面写的即可



支付宝手机网站支付:

还在研究中 ....

猜你喜欢

转载自blog.csdn.net/wsbgmofo/article/details/80404942
今日推荐