java-微信公众号充值

实现功能 :公众号内H5页面调用微信支付实现充值,
1.开发流程
1) 申请公众号 并且认证        (这个是前提 自行百度有很多方法)
2)获取用户授权 获取openid (上一篇文章有介绍怎么获取)
3)调用统一下单接口获取预支付id  微信统一接口API  https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1
4)H5调起微信支付的内置JS  
5)支付完成后,微信回调URL的处理
第1).2)步就不在介绍了....
2.统一接口参数介绍
appid ==应用ID==登陆微信公众号后台-开发-基本配置
mch_id == 微信支付商户号==登陆微信支付后台,即可看到
body==商品描述==商品或支付单简要描述
spbill_create_ip == 获取发起电脑ip
代码如下
String spbill_create_ip = LLPayUtil.getIpAddr(request).replace("_", "."); 

    /**
     * 
     * 功能描述:获取真实的IP地址
     * @param request
     * @return
     * @author guoyx
     */
    public static String getIpAddr(HttpServletRequest request)
    {
        String ip = request.getHeader("x-forwarded-for");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip))
        {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip))
        {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip))
        {
            ip = request.getRemoteAddr();
        }
        if (!isnull(ip) && ip.contains(","))
        {
            String[] ips = ip.split(",");
            ip = ips[ips.length - 1];
        }
        //转换IP 格式
        if(!isnull(ip)){
            ip=ip.replace(".", "_");
        }
        return ip;
    }
trade_type==交易类型==取值如下:JSAPI,NATIVE,APP。我们这里使用的JSAPI。标题已经说了,是微信公众号支付。他们的区别,请参考https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=4_2
      ps:JSAPI--公众号支付、NATIVE--原生扫码支付、APP--app支付,统一下单接口trade_type的传参可参考这里。MICROPAY--刷卡支付,刷卡支付有单独的支付接口,不调用统一下单接口
nonce_str==随机字符串==随机字符串,不长于32位
    代码如下

    /**
     * 获取随机字符串 Nonce Str
     *
     * @return String 随机字符串
     */
    public static String generateNonceStr() {
        return UUID.randomUUID().toString().replaceAll("-", "").substring(0, 32);
    }
notify_url==通知地址==接收微信支付异步通知回调地址,通知url必须为直接可访问的url,不能携带参数。
out_trade_no==商户订单号==商户系统内部的订单号,32个字符内、可包含字母
代码如下
    /**
 * 生成订单号,格式:XX(前缀) + yyyyMMddHHmmss + 10位数
 * @param prefix
 * @return
 */
public static String generate(String prefix) {
synchronized(locker) {
if (sn == 999999999) {
sn = 0;
} else {
sn ++;
}
String str = String.format("%010d", sn);
return prefix + sdf.format(new Date()) + str;
}
}

total_fee==总金额==订单总金额
代码如下 money金额
String.valueOf((int)(money*100))
openid ==用户标识==trade_type=JSAPI,此参数必传,用户在商户appid下的唯一标识
sign ==签名==官方给的签名算法
代码如下


key ==key设置路径:微信商户平台(pay.weixin.qq.com)-->账户设置-->API安全-->密钥设置
String sign = PayCommonUtil.createSign("UTF-8", packageParams, key);
/**
 * @author
 * @date 2016-4-22
 * @Description:sign签名
 * @param characterEncoding
 *            编码格式
 * @param parameters
 *            请求参数
 * @return
 */
public static String createSign(String characterEncoding, SortedMap packageParams, String API_KEY) {
StringBuffer sb = new StringBuffer();
Set es = packageParams.entrySet();
Iterator it = es.iterator();
while (it.hasNext()) {
Map.Entry entry = (Map.Entry) it.next();
String k = (String) entry.getKey();
String v = (String) entry.getValue();
if (null != v && !"".equals(v) && !"sign".equals(k) && !"key".equals(k)) {
sb.append(k + "=" + v + "&");
}
}
sb.append("key=" + API_KEY);
String sign = MD5Util.MD5Encode(sb.toString(), characterEncoding).toUpperCase();
return sign;
}

封装成XML参数
SortedMap packageParams = new TreeMap();
packageParams.put("appid", appid);
packageParams.put("mch_id", mch_id);
packageParams.put("nonce_str",nonce_str );
packageParams.put("body", body);
packageParams.put("out_trade_no", out_trade_no);
packageParams.put("total_fee", String.valueOf((int)(money*100)));// 价格(注意:价格的单位是分)
packageParams.put("spbill_create_ip", spbill_create_ip);
packageParams.put("notify_url", notify_url);
packageParams.put("trade_type", "JSAPI"); //交易类型 JSAPI--公众号支付
packageParams.put("openid", openid); //微信ID
packageParams.put("sign", sign)


准备好以上参数之后,封装成XML
格式如下:
xml version="1.0" encoding="UTF-8" standalone="yes"?>
<xml>
    <appid>wxb1427ebebexxxxxxappid>
    <body>XXX费用body>
    <device_info>WEBdevice_info>
    <mch_id>132186xxxxmch_id>
    <nonce_str>6AED000AF86A084F9CB0264161E29DD3nonce_str>
    <notify_url>https://一个域名/api/wechatPay/jsapiPayNotifynotify_url>
    <openid>oo8WUt0taCqjt552htW1vw-xxxxxopenid>
    <out_trade_no>1out_trade_no>
    <sign>各种排序+key生成的那个signsign>
    <total_fee>1total_fee>
    <trade_type>JSAPItrade_type>
xml>

代码如下
String requestXML = PayCommonUtil.getRequestXml(packageParams);
	/**
	 * @author
	 * @date 2016-4-22
	 * @Description:将请求参数转换为xml格式的string
	 * @param parameters
	 *            请求参数
	 * @return
	 */
	public static String getRequestXml(SortedMap parameters) {
		StringBuffer sb = new StringBuffer();
		sb.append("");
		Set es = parameters.entrySet();
		Iterator it = es.iterator();
		while (it.hasNext()) {
			Map.Entry entry = (Map.Entry) it.next();
			String k = (String) entry.getKey();
			String v = (String) entry.getValue();
			if ("attach".equalsIgnoreCase(k) || "body".equalsIgnoreCase(k) || "sign".equalsIgnoreCase(k)) {
				sb.append("<" + k + ">" + "");
			} else {
				sb.append("<" + k + ">" + v + "");
			}
		}
		sb.append("");
		return sb.toString();
	}


调用微信的统一下单地址:https://api.mch.weixin.qq.com/pay/unifiedorder
调用代码如下
String resXml = HttpUtil.postData(PropertiesHandler.getConfigValue("UFDODER_URL").toString(), requestXML);

 见证奇迹的时刻。如果以上参数都神奇的对了,那么会收到微信返回的XML字符串,格式如下

 
 
 
 
 
 
 
 
 
 

我们需要的,就是这货

prepay_id

拿到prepay_id后 进行下一步 生成paySign 签名 代码如下
	SortedMap packagePara = new TreeMap();
			//自1970年1月1日 0点0分0秒以来的秒数。注意:部分系统取到的值为毫秒级,需要转换成秒(10位数字)。
			long timeSt = (LLPayUtil.getPhoneCurrentDateTimeStr()); 
			String timeStr = Long.toString(timeSt);
			packagePara.put("appId", appid); //公众号名称,由商户传入    
			packagePara.put("timeStamp",timeStr); //时间戳,自1970年以来的秒数     
			packagePara.put("nonceStr", nonce_str); // 随机串  
			packagePara.put("package", "prepay_id="+urlCode); //prepay_id
			packagePara.put("signType", "MD5");	//微信签名方式	
			String paySign = PayCommonUtil.createSign("UTF-8", packagePara, key); // 微信签名
	/**
	 * @author
	 * @date 2016-4-22
	 * @Description:paySign 签名
	 * @param characterEncoding
	 *            编码格式
	 * @param parameters
	 *            请求参数
	 * @return
	 */
	public static String createSign(String characterEncoding, SortedMap packageParams, String API_KEY) {
		StringBuffer sb = new StringBuffer();
		Set es = packageParams.entrySet();
		Iterator it = es.iterator();
		while (it.hasNext()) {
			Map.Entry entry = (Map.Entry) it.next();
			String k = (String) entry.getKey();
			String v = (String) entry.getValue();
			if (null != v && !"".equals(v) && !"sign".equals(k) && !"key".equals(k)) {
				sb.append(k + "=" + v + "&");
			}
		}
		sb.append("key=" + API_KEY);
		String sign = MD5Util.MD5Encode(sb.toString(), characterEncoding).toUpperCase();
		return sign;
	}

拼接好参数之后 传回给前台
	if(null != paySign){
				model.put("res", true);
				model.put("appId", appid);
				model.put("timeStamp", timeStr);
				model.put("nonceStr", nonce_str);
				model.put("packag","prepay_id="+urlCode);
				model.put("signType", "MD5");
				model.put("paySign", paySign);
				return model;
			}
注意:记得添加授权目录
  java-微信公众号充值_第1张图片

前台JSP代码
//微信充值
	function wechat(){
		var phone = '${userInfo.phone}';
		var money = $("#money").val();
		if(money == "" || money == 0){
			 layer.msg("充值的金额不能为空");
			return false;
		}
		var moneyCheck = /^[0-9]+(.[0-9]{1,2})?$/;
		if(!moneyCheck.test(money)){
			 layer.msg("请保留两位小数!");
			return;
		}
		$.ajax({
			url: '<%=WEBPATH%>/fund/phoenWechatpay',
			type: "post",
			async: false, 
			data: {"money" : money},
			success: function(data){
				if(data.res){
					callPay(data.appId,data.timeStamp,data.nonceStr,
							data.packag,data.signType,data.paySign);
				}else{
					alert("充值失败!");
				}
			}		
		})
	}

		  //调用微信内置浏览器的微信支付  
	  function callPay(appId,timeStamp,nonceStr,
				packag,signType,paySign) {  
		    if (typeof WeixinJSBridge == "undefined") {  
		      if (document.addEventListener) {  
		        document.addEventListener('WeixinJSBridgeReady', onBridgeReady,  
		            false);  
		      } else if (document.attachEvent) {  
		        document.attachEvent('WeixinJSBridgeReady', onBridgeReady);  
		        document.attachEvent('onWeixinJSBridgeReady', onBridgeReady);  
		      }  
		    } else {  
		      onBridgeReady(appId,timeStamp,nonceStr,
						packag,signType,paySign);  
		    }  
		  }  
			//提交微信充值
		 function onBridgeReady(appId,timeStamp,nonceStr,
					packag,signType,paySign) {  	
			// alert("appId:"+appId+",timeStamp:"+timeStamp+",nonceStr:"+nonceStr+",packag:"+packag+",signType:"+signType+",paySign:"+paySign);								
			    WeixinJSBridge.invoke('getBrandWCPayRequest', {  
			      "appId" : appId, //公众号名称,由商户传入     
			      "timeStamp" : timeStamp, //时间戳,自1970年以来的秒数     
			      "nonceStr" : nonceStr, //随机串     
			      "package" : packag,	//"prepay_id=u802345jgfjsdfgsdg888",  
			      "signType" : signType, //微信签名方式:     
			      "paySign" : paySign //微信签名   
			    }, function(res) { // 使用以上方式判断前端返回,微信团队郑重提示:res.err_msg将在用户支付成功后返回  ok,但并不保证它绝对可靠。    
			    	if (res.err_msg == "get_brand_wcpay_request:ok") {  	
			    	  //充值成功后返回页面
			        window.location.replace("<%=WEBPATH %>/fund/phone_return_url");
			      }  
			      if (res.err_msg == "get_brand_wcpay_request:cancel") {  
			    	  layer.msg("交易取消");  
			    	  return false;
			      }  
			      if (res.err_msg == "get_brand_wcpay_request:fail") {  
			    	  layer.msg("支付失败");  
			    	  return false;
			      }  
			    });  
			  }  

效果图如下

你可能感兴趣的:(微信)