java開發微信公衆號支付

這篇文章主要給大家結合微信支付接口開發的實踐,從獲取用戶授權到各主要接口的使用方法等方面介紹微信支付的關鍵點技術,有需要的小夥伴可以參考下

最近做了微信公衆號支付的開發,由於是第一次做也摸索了幾天的時間,也只是達到了實現功能的水平,並沒有太多考慮到性能問題,所以這篇文章比較適合初學者。
微信公衆號支付的總體其實很簡單,大致就分爲三步。
   第一步需要獲取用戶授權;
   第二步調用統一下單接口獲取預支付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<String, String> map = new HashMap<String, String>();
        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<Object,Object> parameters = new TreeMap<Object,Object>();
      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<Object,Object> 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("Package", "prepay_id="+reData.getPrepay_id());
        model.addAttribute("signType", "MD5");
        SortedMap<Object,Object> signMap = new TreeMap<Object,Object>();
        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);

將需要的參數傳給頁面後,使用微信提供方法調起支付。

<script type="text/javascript">
$(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_request:ok") {
        //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();
    }
  }

});
</script>



在返回結果的地方可以自定義一些自己的返回頁面。
   總結:由於我也是第一次做,寫這篇文章是想記錄一下自己的工作成果,和分享給一下也是新手的朋友們可以有一些幫助,最後希望有好的見解朋友可以留言討論,大家一起學習進步。
以上就是關於java開發微信公衆支付的所有內容了,希望大家能夠喜歡。


補充一下:微信公衆號開發用到的所有工具類



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章