这篇文章主要给大家结合微信支付接口开发的实践,从获取用户授权到各主要接口的使用方法等方面介绍微信支付的关键点技术,有需要的小伙伴可以参考下

最近做了微信公众号支付的开发,由于是第一次做也摸索了几天的时间,也只是达到了实现功能的水平,并没有太多考虑到性能问题,所以这篇文章比较适合初学者。
微信公众号支付的总体其实很简单,大致就分为三步。
   第一步需要获取用户授权;
   第二步调用统一下单接口获取预支付id;
   第三步H5调起微信支付的内置的js。
下面介绍具体每一步的开发流程。



一、    首先要明确微信公众号支付属于网页版支付,所以相较于app的直接调取微信支付要多一步微信授权。也就是需要获取用户的openid。微信公众号使用的交易类型是JSAPI,所以统一下单接口的文档明确的写到


因此我们必须去获取openid,同时也可以处理一些我们需要的逻辑。获取用户授权有两种方式:1.scope=snsapi_base;2.scope=snsapi_userinfo.我使用的是snsapi_base  //具体可参照官方文档

Scope为snsapi_base

https://open.weixin.qq.com/connect/oauth2/authorize?appid=wxf4bd796d62cb1bb5&redirect_uri=http://www.muguaerp.com/mcc/mobile/shop/product/pay.jhtml&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect

注意:这是发起支付前的操作。这里的redirect_uri需要填写你要发起支付的页面

参数详解:


首先appid就不多说了就是你微信公众号的appid固定写死的,redirect_uri这个参数是最重要的,这个地址是访问你处理的接口地址。你可以在这个链接上拼接上你所需要的参数,一般你是要把订单的金额传到这个接口里的,访问这个链接的时候微信会给你code你需要用它去获取openid,记得要对其进行urlencode处理。state参数可以理解为扩展字段,其他的参数都是固定写法就不在多做介绍了。下面是获取openid的代码片段。

//第1步 获取openId
        HttpClientUtil util = HttpClientUtil.getInstance();
        Map map = new HashMap();
        map.put("appid", Configure.getAppid());
        map.put("secret",Configure.getAppsecret());
        map.put("code", code);
        map.put("grant_type", Configure.getGrant_type());
        String returnStr = util.getResponseBodyAsString("https://api.weixin.qq.com/sns/oauth2/access_token", map);
        AccessToken at = JSON.parseObject(returnStr, AccessToken.class);
        System.out.println(at);
//工具类
AccessToken.java


public class AccessToken {

  private String access_token;
  private String expires_in;
  private String refresh_token;
  private String openid;
  private String scope;
  private String unionid;

  public String getAccess_token() {
    return access_token;
  }
  public void setAccess_token(String access_token) {
    this.access_token = access_token;
  }
  public String getExpires_in() {
    return expires_in;
  }
  public void setExpires_in(String expires_in) {
    this.expires_in = expires_in;
  }
  public String getRefresh_token() {
    return refresh_token;
  }
  public void setRefresh_token(String refresh_token) {
    this.refresh_token = refresh_token;
  }
  public String getOpenid() {
    return openid;
  }
  public void setOpenid(String openid) {
    this.openid = openid;
  }
  public String getScope() {
    return scope;
  }
  public void setScope(String scope) {
    this.scope = scope;
  }
  public String getUnionid() {
    return unionid;
  }
  public void setUnionid(String unionid) {
    this.unionid = unionid;
  }
  @Override
  public String toString() {
    return "AccessToken [access_token=" + access_token + ", expires_in="
        + expires_in + ", refresh_token=" + refresh_token + ", openid="
        + openid + ", scope=" + scope + ", unionid=" + unionid + "]";
  }



}

二、    我们获取了openid后,就可以进行下一步的统一下单的开发了。微信上统一下单接口的文档写的比较详细了,具体的参数含义我就不多做介绍了。下面直接贴最直观的代码,特别提醒的是一定要注意签名的正确。签名所使用的key并不是AppSecret而是你申请时自己定义的商户key。

//第2步 统一下单
        String strAttach = "微信支付";
        //strAttach = new String(strAttach.getBytes("gbk"),"utf-8");
        String strBody = "微信公众号支付";
        //strBody = new String(strBody.getBytes("gbk"),"utf-8");
        Order order= orderService.findBySn(sn);
        String ip = request.getRemoteAddr();
        WxPaySendData data = new WxPaySendData();
        data.setAppid(Configure.getAppid());
        data.setAttach(URLEncoder.encode(strAttach, "UTF-8"));
        data.setBody(URLEncoder.encode(strBody, "UTF-8"));
        data.setMch_id(Configure.getMchid());
        data.setNonce_str(RandomStringGenerator.getRandomStringByLength(32));
        data.setNotify_url(Configure.getNotify_url());
        data.setOut_trade_no(UtilDate.getOrderNum());
        data.setTotal_fee((order.getAmount().multiply(new BigDecimal(100)).intValue()));//单位:分
        data.setTrade_type("JSAPI");
        data.setSpbill_create_ip(ip);
        data.setOpenid(at.getOpenid());
        String returnXml = UnifiedorderService.unifiedOrder(data,Configure.getKey());
        WxPayReturnData reData = new WxPayReturnData();
        XStream xs1 = new XStream(new DomDriver());
        xs1.alias("xml", WxPayReturnData.class);
        reData = (WxPayReturnData) xs1.fromXML(returnXml);

注意:这里腾讯用的是xml格式的参数因此需要把参数转成xml格式。这里用的是XStream,并且需要把sendData 和 returnData 封装成class

//工具类
UnifiedorderService.java
public class UnifiedorderService {
    
  private final static Logger logger = LoggerFactory.getLogger(UnifiedorderService.class);
    
  public static String unifiedOrder(WxPaySendData data,String key){
    //统一下单支付
    String returnXml = null;
    try {
      //生成sign签名
      SortedMap parameters = new TreeMap();
      parameters.put("appid", data.getAppid()); 
      parameters.put("attach", data.getAttach());
      parameters.put("body", data.getBody());
      parameters.put("mch_id", data.getMch_id());
      parameters.put("nonce_str", data.getNonce_str());
      parameters.put("notify_url", data.getNotify_url());
      parameters.put("out_trade_no", data.getOut_trade_no());
      parameters.put("total_fee", data.getTotal_fee());
      parameters.put("trade_type", data.getTrade_type());
      parameters.put("spbill_create_ip", data.getSpbill_create_ip());
      parameters.put("openid", data.getOpenid());
      parameters.put("device_info", data.getDevice_info());
      logger.info("SIGN:"+WxSign.createSign(parameters,key));
      data.setSign(WxSign.createSign(parameters,key));
      XStream xs = new XStream(new DomDriver("UTF-8",new XmlFriendlyNameCoder("-_", "_")));
      xs.alias("xml", WxPaySendData.class);
      String xml = xs.toXML(data);
      logger.info("统一下单xml为:\n" + xml);
      HttpClientUtil util = HttpClientUtil.getInstance();
      returnXml = util.doPostForString("https://api.mch.weixin.qq.com/pay/unifiedorder", null, xml);
      logger.info("返回结果:" + returnXml);
    } catch (Exception e) {
      e.printStackTrace();
    } 
    return returnXml;
  }
    
}
//工具类
WxSign.java
public class WxSign {
    
    
  private static String characterEncoding = "UTF-8";
  
  @SuppressWarnings("rawtypes")
  public static String createSign(SortedMap parameters,String key){ 
    StringBuffer sb = new StringBuffer(); 
    Set es = parameters.entrySet();//所有参与传参的参数按照accsii排序(升序) 
    Iterator it = es.iterator(); 
    while(it.hasNext()) { 
      Map.Entry entry = (Map.Entry)it.next(); 
      String k = (String)entry.getKey(); 
      Object v = entry.getValue(); 
      if(null != v && !"".equals(v)  
          && !"sign".equals(k) && !"key".equals(k)) { 
        sb.append(k + "=" + v + "&"); 
      } 
    } 
    sb.append("key=" + key);
    String sign = MD5.MD5Encode(sb.toString(), characterEncoding).toUpperCase(); 
    return sign; 
  }
    
  public static String getNonceStr() {
    Random random = new Random();
    return MD5Util.MD5Encode(String.valueOf(random.nextInt(10000)), "UTF-8");
  }
  
  public static String getTimeStamp() {
    return String.valueOf(System.currentTimeMillis() / 1000);
  }
  
}
//md5.java
package com.pawpaw.plugin.tencentpay;


import java.security.MessageDigest;


/**
* User: rizenguo
* Date: 2014/10/23
* Time: 15:43
*/
public class MD5 {
    private final static String[] hexDigits = {"0", "1", "2", "3", "4", "5", "6", "7",
            "8", "9", "a", "b", "c", "d", "e", "f"};


    /**
     * 转换字节数组为16进制字串
     * @param b 字节数组
     * @return 16进制字串
     */
    public static String byteArrayToHexString(byte[] b) {
        StringBuilder resultSb = new StringBuilder();
        for (byte aB : b) {
            resultSb.append(byteToHexString(aB));
        }
        return resultSb.toString();
    }


    /**
     * 转换byte到16进制
     * @param b 要转换的byte
     * @return 16进制格式
     */
    private static String byteToHexString(byte b) {
        int n = b;
        if (n < 0) {
            n = 256 + n;
        }
        int d1 = n / 16;
        int d2 = n % 16;
        return hexDigits[d1] + hexDigits[d2];
    }


    /**
     * MD5编码
     * @param origin 原始字符串
     * @return 经过MD5加密之后的结果
     */
    public static String MD5Encode(String origin) {
        String resultString = null;
        try {
            resultString = origin;
            MessageDigest md = MessageDigest.getInstance("MD5");
            resultString = byteArrayToHexString(md.digest(resultString.getBytes()));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return resultString;
    }


}
//WxPaySendData.java
package com.pawpaw.entity;


public class WxPaySendData {
        //公众号ID
        private String appid;
        //附加参数
        private String attach;
        //商品名称
        private String body;
        //商户ID
        private String mch_id;
        //随机支付串
        private String nonce_str;
        //通知地址,不能携带参数,直接就能访问
        private String notify_url;
        //用户订单号
        private String out_trade_no;
        //总金额 以分为单位
        private int total_fee;
        //交易类型
        private String trade_type;
        //终端IP
        private String spbill_create_ip;
        //openID
        private String openid;
        //签名
        private String sign;
        public String getAppid() {
                return appid;
        }
        public void setAppid(String appid) {
                this.appid = appid;
        }
        public String getAttach() {
                return attach;
        }
        public void setAttach(String attach) {
                this.attach = attach;
        }
        public String getBody() {
                return body;
        }
        public void setBody(String body) {
                this.body = body;
        }
        public String getMch_id() {
                return mch_id;
        }
        public void setMch_id(String mch_id) {
                this.mch_id = mch_id;
        }
        public String getNonce_str() {
                return nonce_str;
        }
        public void setNonce_str(String nonce_str) {
                this.nonce_str = nonce_str;
        }
        public String getNotify_url() {
                return notify_url;
        }
        public void setNotify_url(String notify_url) {
                this.notify_url = notify_url;
        }
        public String getOut_trade_no() {
                return out_trade_no;
        }
        public void setOut_trade_no(String out_trade_no) {
                this.out_trade_no = out_trade_no;
        }
        public int getTotal_fee() {
                return total_fee;
        }
        public void setTotal_fee(int total_fee) {
                this.total_fee = total_fee;
        }
        public String getTrade_type() {
                return trade_type;
        }
        public void setTrade_type(String trade_type) {
                this.trade_type = trade_type;
        }
        public String getSpbill_create_ip() {
                return spbill_create_ip;
        }
        public void setSpbill_create_ip(String spbill_create_ip) {
                this.spbill_create_ip = spbill_create_ip;
        }
        public String getOpenid() {
                return openid;
        }
        public void setOpenid(String openid) {
                this.openid = openid;
        }
        public String getSign() {
                return sign;
        }
        public void setSign(String sign) {
                this.sign = sign;
        }
        
        
}
//WxPayReturnData.java
package com.pawpaw.entity;


public class WxPayReturnData {
        //业务结果
        private String return_code;
        //消息
        private String return_msg;
                //公众号ID
                private String appid;
                //商户ID
                private String mch_id;
                //随机支付串
                private String nonce_str;
                //签名
                private String sign;
                //业务结果
                private String result_code;
                //预支付ID
                private String prepay_id;
                //交易类型
                private String trade_type;
        
                public String getReturn_code() {
                        return return_code;
                }
                public void setReturn_code(String return_code) {
                        this.return_code = return_code;
                }
                public String getReturn_msg() {
                        return return_msg;
                }
                public void setReturn_msg(String return_msg) {
                        this.return_msg = return_msg;
                }
                public String getAppid() {
                        return appid;
                }
                public void setAppid(String appid) {
                        this.appid = appid;
                }
                public String getMch_id() {
                        return mch_id;
                }
                public void setMch_id(String mch_id) {
                        this.mch_id = mch_id;
                }
                public String getNonce_str() {
                        return nonce_str;
                }
                public void setNonce_str(String nonce_str) {
                        this.nonce_str = nonce_str;
                }
                public String getSign() {
                        return sign;
                }
                public void setSign(String sign) {
                        this.sign = sign;
                }
                public String getResult_code() {
                        return result_code;
                }
                public void setResult_code(String result_code) {
                        this.result_code = result_code;
                }
                public String getPrepay_id() {
                        return prepay_id;
                }
                public void setPrepay_id(String prepay_id) {
                        this.prepay_id = prepay_id;
                }
                public String getTrade_type() {
                        return trade_type;
                }
                public void setTrade_type(String trade_type) {
                        this.trade_type = trade_type;
                }


                
                        
}

最后要提一下的是NOTIFY_URL回调地址,接收微信支付异步通知回调地址。


三    通过上面的操作我们获得了预支付交易会话标识prepay_id,这样我们就可以进行最后一步的操作了。使用H5调起支付api。

//第3步 数据传到前端,H5调起支付
        model.addAttribute("appId", reData.getAppid());
        model.addAttribute("timeStamp", Wxsign.getTimeStamp());
        model.addAttribute("nonceStr", reData.getNonce_str());
        model.addAttribute("ackage", "prepay_id="+reData.getPrepay_id());
        model.addAttribute("signType", "MD5");
        SortedMap signMap = new TreeMap();
        signMap.put("appId", reData.getAppid()); 
        signMap.put("timeStamp", Wxsign.getTimeStamp());
        signMap.put("nonceStr", reData.getNonce_str());
        signMap.put("package", "prepay_id="+ reData.getPrepay_id());
        signMap.put("signType", "MD5");
        model.addAttribute("paySign", Wxsign.createSign(signMap,Configure.getKey()));
        model.addAttribute("numb", order.getSn());
        model.addAttribute("order", order);

将需要的参数传给页面后,使用微信提供方法调起支付。


$(function(){
//这里是调用微信内置浏览器的微信支付
$("#anniu_z").click(function(){
callPay();
});


  
  function onBridgeReady() {

    WeixinJSBridge.invoke('getBrandWCPayRequest', {
      "appId" : "${appId}",//"wx2421b1c4370ec43b", //公众号名称,由商户传入   
      "timeStamp" : "${timeStamp}",//"1395712654", //时间戳,自1970年以来的秒数   
      "nonceStr" : "${nonceStr}",//"e61463f8efa94090b1f366cccfbbb444", //随机串   
      "package" : "${Package}",//"prepay_id=u802345jgfjsdfgsdg888",
      "signType" : "${signType}",//"MD5", //微信签名方式:   
      "paySign" : "${paySign}"//"70EA570631E4BB79628FBCA90534C63FF7FADD89" //微信签名 
    }, function(res) { // 使用以上方式判断前端返回,微信团队郑重提示:res.err_msg将在用户支付成功后返回  ok,但并不保证它绝对可靠。  
      //alert(res.err_msg);
      if (res.err_msg == "get_brand_wcpay_requestk") {
        //alert("支付成功");
       //这里是商品入库
                $.ajax({
                                   url:'${base}/mobile/shop/product/returnPay.jhtml',
                                   type:'post',
                                   dataType:'json',
                                   success:function(message){
                                                  $.message(message);
                                                  if("${productCategory.name}"=="教育文库"){
                                                  setTimeout('location.href="${base}/mobile/shop/product/searchWord.jhtml?id=${product.id}"',3000);
                                                  }else {
                                                  setTimeout('location.href="${base}/mobile/shop/product/search.jhtml?id=${product.id}"',3000);
                                                  }
                                           }
                                        });                
      }
      if (res.err_msg == "get_brand_wcpay_request:cancel") {
        alert("交易取消");
      }
      if (res.err_msg == "get_brand_wcpay_request:fail") {
        alert("支付失败");
      }
    });
  }
  
  function callPay() {
    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();
    }
  }

});

在返回结果的地方可以自定义一些自己的返回页面。

   总结:由于我也是第一次做,写这篇文章是想记录一下自己的工作成果,和分享给一下也是新手的朋友们可以有一些帮助,最后希望有好的见解朋友可以留言讨论,大家一起学习进步。

以上就是关于java开发微信公众支付的所有内容了,希望大家能够喜欢。


补充一下:微信公众号开发用到的所有工具类