java-微信公众号充值

来源:互联网 发布:企业网络机房建设标准 编辑:程序博客网 时间:2024/05/16 16:21
实现功能:公众号内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<Object, Object> 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<Object, Object> packageParams = new TreeMap<Object, Object>();
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>wxb1427ebebexxxxxx</appid>    <body>XXX费用</body>    <device_info>WEB</device_info>    <mch_id>132186xxxx</mch_id>    <nonce_str>6AED000AF86A084F9CB0264161E29DD3</nonce_str>    <notify_url>https://一个域名/api/wechatPay/jsapiPayNotify</notify_url>    <openid>oo8WUt0taCqjt552htW1vw-xxxxx</openid>    <out_trade_no>1</out_trade_no>    <sign>各种排序+key生成的那个sign</sign>    <total_fee>1</total_fee>    <trade_type>JSAPI</trade_type></xml>

代码如下
String requestXML = PayCommonUtil.getRequestXml(packageParams);
/** * @author * @date 2016-4-22 * @Description:将请求参数转换为xml格式的string * @param parameters *            请求参数 * @return */public static String getRequestXml(SortedMap<Object, Object> parameters) {StringBuffer sb = new StringBuffer();sb.append("<xml>");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 + ">" + "<![CDATA[" + v + "]]></" + k + ">");} else {sb.append("<" + k + ">" + v + "</" + k + ">");}}sb.append("</xml>");return sb.toString();}


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

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

<xml>
  <return_code><![CDATA[SUCCESS]]></return_code>
  <return_msg><![CDATA[OK]]></return_msg>
  <appid><![CDATA[wxb1427ebebexxxxxx]]></appid>
  <mch_id><![CDATA[132186xxxx]]></mch_id>
  <device_info><![CDATA[WEB]]></device_info>
  <nonce_str><![CDATA[Hh4LFHUUvtDYtNdp]]></nonce_str>
  <sign><![CDATA[079F8A915FD3044F4A17D75F4945E955]]></sign>
  <result_code><![CDATA[SUCCESS]]></result_code>
  <prepay_id><![CDATA[wx20160617155030d9e6a0e48b0533061255]]></prepay_id>
  <trade_type><![CDATA[JSAPI]]></trade_type>
</xml>

我们需要的,就是这货

prepay_id

拿到prepay_id后 进行下一步 生成paySign 签名 代码如下
SortedMap<Object, Object> packagePara = new TreeMap<Object, Object>();//自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_idpackagePara.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<Object, Object> 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;}
注意:记得添加授权目录
 

前台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;      }      });    }  
效果图如下
原创粉丝点击