java版微信公眾號支付(H5調微信內置API)


最近需要做微信公眾號支付,網上找了大堆的代碼,大多都只說了個原理,自己踩了太多坑,所有的坑,都會再下面的文章中標注,代碼我也貼上最全的(叫我雷鋒)!!!


第一步:配置支付授權目錄

你需要有將你公司的微信公眾號開通支付(審核要等個幾天),登錄后找到 微信支付-->開發配置,你會看到如下圖所示:

說明一下:配置支付授權目錄,就是當你再H5界面調起支付控件進行支付時,要對你支付的post請求進行校驗(再不懂看下面的調起微信支付控件代碼):

如果出現錯誤,支付界面一閃而過(有調起支付控件的跡象),你看不出報什么錯,你可以打印出微信返回的res:alert(JSON.stringify(res));

function onBridgeReady(){
        WeixinJSBridge.invoke(
            'getBrandWCPayRequest', {
                 "appId":appId,     //公眾號名稱,由商戶傳入
                 "paySign":sign,         //微信簽名
                 "timeStamp":timeStamp, //時間戳,自1970年以來的秒數
                 "nonceStr":nonceStr , //隨機串
                 "package":packageStr,  //預支付交易會話標識
                 "signType":signType     //微信簽名方式
             },
             function(res){
          alert(JSON.stringify(res));//出錯可以在這里看看.....
                 if(res.err_msg == "get_brand_wcpay_request:ok" ) {
                //window.location.replace("index.html");
                  alert('支付成功');
              }else if(res.err_msg == "get_brand_wcpay_request:cancel"){
               alert('支付取消');
               }else if(res.err_msg == "get_brand_wcpay_request:fail" ){
                alert('支付失敗');
              }
                 //使用以上方式判斷前端返回,微信團隊鄭重提示:res.err_msg將在用戶支付成功后返回    ok,但並不保證它絕對可靠。
             }
        );
    }

以下信息你都能從公眾好+商戶號中獲得:

public interface WeChatConst {
    //公眾號支付APPID
    //String APPID = "上圖中的appId";
    //公眾號支付AppSecret
    //String APP_SECRET = "上圖中的secret";
    //公眾號支付商戶號
    String MCH_ID = "xxxx";
    //商戶后台配置的一個32位的key,位置:微信商戶平台-賬戶中心-API安全
    String KEY = "xxxxx";
    //交易類型
    String TRADE_TYPE = "JSAPI";
}

 :每個微信公眾號對應一個商戶號,當用戶關注你的公眾號后,如果在你的公眾號里面要進行支付操作,那么他支付的軟妹幣就流入到了你這個商戶號里去了

上面代碼中的 公眾號支付商戶號 哪里獲得?進入微信商戶號,登錄后找到導航欄的 帳戶中心--->商戶信息【就是上面接口WeChatConst中說的 公眾號支付商戶號 !!!】

 KEY是啥? 同理,商戶號中 賬戶中心--API安全 ↓↓↓ 長度32位哦。。。。

 

做完上面公眾號配置跟商戶號配置,就開始寫代碼啦。。。。。。不知道上面這些步驟的,找起來真的累。。。。

 

第一步:授權(可以是用戶進你的界面就直接做,也可以用戶點擊界面中的某個按鈕再授權,主要是拿openId

一開始我覺得不需要這個openId,取了干啥?難道后面的下單一定要?開發文檔里說了,trade_type=JSAPI時(即公眾號支付),此參數必傳,此參數為微信用戶在商戶對應appid下的唯一標識,好吧,老實點。。。。。

/**
  跳轉支付界面,將code帶過去
**/

@RequestMapping("toPay.do")
public ModelAndView toPay(HttpServletRequest request, HttpServletResponse response) throws Exception {
        
    ModelAndView modelAndView = new ModelAndView();
    logger.debug("玩家准備填寫充值信息了:" + HttpUtil.buildOriginalURL(request));
        
    //重定向Url
    String redirecUri = URLEncoder.encode(GlobalThreadLocal.getSiteConfig().getBasePath() + "/wxOfficialAccountsPay/toInputAccountInfo.do");
    //用於獲取成員信息的微信返回碼
    String code = null;
    if( request.getParameter("code")!=null ){
        code =request.getParameter("code");
    }
    if( code == null) {
        //授權         
        return authorization(redirecUri);
    }
    code =request.getParameter("code");
    // 獲取用戶信息
    WeixinLoginUser weixinLoginUser = getWeixinLoginUser(code);
        
    modelAndView.addObject("openId",des.getEncString(weixinLoginUser.getOpenID()));
    // 跳轉到支付界面
    String viewName = "/wxOfficialAccountsPay/pay";
    modelAndView.setViewName(viewName);
    return modelAndView;
}
/**
* 授權方法
* @param redirecUri 重定向鏈接
* 
* */
private ModelAndView authorization(String redirecUri) {
    String siteURL="redirect:https://open.weixin.qq.com/connect/oauth2/authorize?appid="
    +GlobalThreadLocal.getSiteConfig().getWeixin_appId()
    +"&redirect_uri="+redirecUri+"&response_type=code&scope=snsapi_userinfo&state=1234#wechat_redirect";
    logger.debug("授權路徑:[ "+siteURL+" ]");
    return new ModelAndView(siteURL);
}
:GlobalThreadLocal.getSiteConfig().getWeixin_appId():是我配置的全局的appId
  (就是前面公眾號圖中的的appId,我放一個接口來存這些信息,是給你們一個事例。。。)   GlobalThreadLocal.getSiteConfig().getWeixin_appSecret() :公眾號的Secret
或許你會說為什么用scope=snsapi_userinfo 這個值(用戶界面操作有感知的授權)而不用 scope=snsapi_base ? 因為你用后者,在進行授權的時候,會報redirec_Uri出錯,反正我遇到了,到現在也沒搞懂。。。。

根據code,獲取用戶授權信息

/**
     * 獲取微信授權登陸用戶
     * @param code
     * @return
     * @throws Exception
     */
    private WeixinLoginUser getWeixinLoginUser(String code) throws Exception {
        logger.debug("由code獲取授權用戶信息");
        Oauth oauth = new Oauth();
        // 由code獲取access_token等信息
        String str = oauth.getToken(code, GlobalThreadLocal.getSiteConfig().getWeixin_appId(), GlobalThreadLocal.getSiteConfig().getWeixin_appSecret());
        // 解析返回的json數據,獲取所需的信息
        String openID = (String) JSON.parseObject(str, Map.class).get("openid");
        String accessToken = (String) JSON.parseObject(str, Map.class).get("access_token");
        String refreshToken = (String) JSON.parseObject(str, Map.class).get("refresh_token");
        // 用openid,access_token獲取用戶的信息,返回userinfo對象
        UserInfo userInfo = oauth.getSnsUserInfo(openID, accessToken);
        // 將用戶信息放入登錄session中
        WeixinLoginUser weixinLoginUser = new WeixinLoginUser();
        weixinLoginUser.setOpenID(openID);
        weixinLoginUser.setUnionID(userInfo.getUnionid());
        weixinLoginUser.setHeadImageUrl(userInfo.getHeadimgurl());
        weixinLoginUser.setNickName(userInfo.getNickname());
        weixinLoginUser.setRefreshToken(refreshToken);
        //
        int siteID = GlobalThreadLocal.getSiteConfig().getSiteId();
        weixinLoginUser.setSiteID(siteID);
        // 返回weixinLoginUser對象
        return weixinLoginUser;
    }

用戶信息封裝類:

public class WeixinLoginUser implements Serializable{
    
    private static final long serialVersionUID = -8449856597137213512L;

    private String openID;
    private String unionID;
    private String headImageUrl;
    private String nickName;
    private String refreshToken;
    private int siteID;
    
    public String getOpenID() {
        return openID;
    }
    public void setOpenID(String openID) {
        this.openID = openID;
    }
    public String getUnionID() {
        return unionID;
    }
    public void setUnionID(String unionID) {
        this.unionID = unionID;
    }
    public String getHeadImageUrl() {
        return headImageUrl;
    }
    public void setHeadImageUrl(String headImageUrl) {
        this.headImageUrl = headImageUrl;
    }
    public String getNickName() {
        return nickName;
    }
    public void setNickName(String nickName) {
        this.nickName = nickName;
    }
    public String getRefreshToken() {
        return refreshToken;
    }
    public void setRefreshToken(String refreshToken) {
        this.refreshToken = refreshToken;
    }
    public int getSiteID() {
        return siteID;
    }
    public void setSiteID(int siteID) {
        this.siteID = siteID;
    }
}

授權做完,openId拿到了,跳轉到支付界面,如下:

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>支付界面</title>
    <script type="text/javascript" src="../public/jquery/jquery-2.0.3.min.js"></script>
</head>
<body>
  <input type="button" value="pay" onclick="pay()"/>
    
<script>
  var prepay_id ;
    var sign ;
    var appId ;
    var timeStamp ;
    var nonceStr ;
    var packageStr ;
    var signType ;
    
    function pay(){
        var url = '${ctx}/wxOfficialAccountsPay/pay.do';
        $.ajax({
        type:"post",
        url:url,
        dataType:"json",
        data:{openId:'${openId}'},
        success:function(data) {
            
          if(data.result_code == 'SUCCESS'){
                      appId = data.appid;
             sign = data.sign;
                      timeStamp = data.timeStamp;
                      nonceStr = data.nonce_str;
                      packageStr = data.packageStr;
                      signType = data.signType;
              //調起微信支付控件
             callpay();
          }else{
            alert("統一下單失敗");
           }
        }
    }); 
    }

    function onBridgeReady(){
        WeixinJSBridge.invoke(
            'getBrandWCPayRequest', {
                 "appId":appId,     //公眾號名稱,由商戶傳入
                 "paySign":sign,         //微信簽名
                 "timeStamp":timeStamp, //時間戳,自1970年以來的秒數
                 "nonceStr":nonceStr , //隨機串
                 "package":packageStr,  //預支付交易會話標識
                 "signType":signType     //微信簽名方式
             },
             function(res){
                 if(res.err_msg == "get_brand_wcpay_request:ok" ) {
             //window.location.replace("index.html");
                     alert('支付成功');
         }else if(res.err_msg == "get_brand_wcpay_request:cancel"){
             alert('支付取消');
         }else if(res.err_msg == "get_brand_wcpay_request:fail" ){
            alert('支付失敗');
         }
                 //使用以上方式判斷前端返回,微信團隊鄭重提示:res.err_msg將在用戶支付成功后返回    ok,但並不保證它絕對可靠。
             }
        );
    }

    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>
    
</body>
</html>

點擊支付按鈕,進入下面一步,耐心點,繼續往下看,我寫到這里,感覺也弄了挺久了,心情好,繼續 2333 

 第三步:統一下單(支付前,需要獲取一系列支付參數,在這步做) 

/**
  * 點擊確認充值 統一下單,獲得預付id(prepay_id)
  * @param request
  * @param response
  * @return
   */
  @ResponseBody
  @RequestMapping({"pay"})
    public WxPaySendData prePay(HttpServletRequest request,HttpServletResponse response,String openId){
    WxPaySendData result = new WxPaySendData();
try {
      //商戶訂單號
      String out_trade_no = WeChatUtil.getOut_trade_no();
    //產品價格,單位:分
    Integer total_fee = 1;
      //客戶端ip
      String ip = HttpUtil.getIpAddr(request);
      //支付成功后回調的url地址
      String notify_url = "http://你的域名/odao-weixin-site/wxOfficialAccountsPay/callback.do";
            
      //統一下單
      String strResult = WeChatUtil.unifiedorder("testPay", out_trade_no, total_fee, ip, notify_url,openId);
            
      //解析xml
       XStream stream = new XStream(new DomDriver());
      stream.alias("xml", WxPaySendData.class);
      WxPaySendData wxReturnData = (WxPaySendData)stream.fromXML(strResult);
            
      //兩者都為SUCCESS才能獲取prepay_id
      if( wxReturnData.getResult_code().equals("SUCCESS") && wxReturnData.getReturn_code().equals("SUCCESS") ){
        //業務邏輯,寫入訂單日志(你自己的業務) .....
    
        String timeStamp = WeChatUtil.getTimeStamp();//時間戳
        String nonce_str = WeChatUtil.getNonceStr();//隨機字符串
        注:上面這兩個參數,一定要拿出來作為后續的value,不能每步都創建新的時間戳跟隨機字符串,不然H5調支付API,會報簽名參數錯誤
        result.setResult_code(wxReturnData.getResult_code());
        result.setAppid(GlobalThreadLocal.getSiteConfig().getWeixin_appId());
        result.setTimeStamp(timeStamp);
        result.setNonce_str(nonce_str);
        result.setPackageStr("prepay_id="+wxReturnData.getPrepay_id());
        result.setSignType("MD5");
                
          WeChatUtil.unifiedorder(.....) 下單操作中,也有簽名操作,那個只針對統一下單,要區別於下面的paySign
        //第二次簽名,將微信返回的數據再進行簽名
        SortedMap<Object,Object> signMap = new TreeMap<Object,Object>();
        signMap.put("appId", GlobalThreadLocal.getSiteConfig().getWeixin_appId());
        signMap.put("timeStamp", timeStamp);
        signMap.put("nonceStr", nonce_str);
        signMap.put("package", "prepay_id="+wxReturnData.getPrepay_id());  //注:看清楚,值為:prepay_id=xxx,別直接放成了wxReturnData.getPrepay_id()
        signMap.put("signType", "MD5");
        String paySign = WxSign.createSign(signMap,  WeChatConst.KEY);//支付簽名
                
        result.setSign(paySign);
    }else{
        result.setResult_code("fail");
    }    
    } catch (Exception e) {
      e.printStackTrace();
    }
    return result;
}

統一下單封裝方法: 

public class WeChatUtil {
    
    private static final Logger logger = Logger.getLogger(WeChatUtil.class);
    
    /**
     * 統一下單
     * 獲得PrePayId
     * @param body   商品或支付單簡要描述
     * @param out_trade_no 商戶系統內部的訂單號,32個字符內、可包含字母
     * @param total_fee  訂單總金額,單位為分
     * @param IP    APP和網頁支付提交用戶端ip
     * @param notify_url 接收微信支付異步通知回調地址
     * @param openid 用戶openId
     * @throws IOException
     */
    public static String unifiedorder(String body,String out_trade_no,Integer total_fee,String ip,String notify_url,String openId)throws IOException {
        /**
         * 設置訪問路徑
         */
        HttpPost httppost = new HttpPost("https://api.mch.weixin.qq.com/pay/unifiedorder");
        String nonce_str = getNonceStr();//隨機數據
        SortedMap<Object,Object> parameters = new TreeMap<Object,Object>();
        /**
         * 組裝請求參數
         * 按照ASCII排序
         */
        parameters.put("appid",GlobalThreadLocal.getSiteConfig().getWeixin_appId() );
        parameters.put("body", body);
        parameters.put("mch_id", WeChatConst.MCH_ID );
        parameters.put("nonce_str", nonce_str);
        parameters.put("out_trade_no", out_trade_no);
        parameters.put("notify_url", notify_url);
        parameters.put("spbill_create_ip", ip);
        parameters.put("total_fee",total_fee.toString() );
        parameters.put("trade_type",WeChatConst.TRADE_TYPE );
        parameters.put("openid", openId);
        
        String sign = WxSign.createSign(parameters, WeChatConst.KEY);

        /**
         * 組裝XML
         */
        StringBuilder sb = new StringBuilder("");
        sb.append("<xml>");
        setXmlKV(sb,"appid",GlobalThreadLocal.getSiteConfig().getWeixin_appId());
        setXmlKV(sb,"body",body);
        setXmlKV(sb,"mch_id",WeChatConst.MCH_ID);
        setXmlKV(sb,"nonce_str",nonce_str);
        setXmlKV(sb,"notify_url",notify_url);
        setXmlKV(sb,"out_trade_no",out_trade_no);
        setXmlKV(sb,"spbill_create_ip",ip);
        setXmlKV(sb,"total_fee",total_fee.toString());
        setXmlKV(sb,"trade_type",WeChatConst.TRADE_TYPE);
        setXmlKV(sb,"sign",sign);
        setXmlKV(sb,"openid",openId);
        sb.append("</xml>");

        StringEntity reqEntity = new StringEntity(new String (sb.toString().getBytes("UTF-8"),"ISO8859-1"));//這個處理是為了防止傳中文的時候出現簽名錯誤
        httppost.setEntity(reqEntity);
        DefaultHttpClient httpclient = new DefaultHttpClient();
        HttpResponse response = httpclient.execute(httppost);
        String strResult = EntityUtils.toString(response.getEntity(), Charset.forName("utf-8"));

        return strResult;

    }


    //獲得隨機字符串
    public static String getNonceStr(){
         Random random = new Random();
         return MD5Util.MD5Encode(String.valueOf(random.nextInt(10000)), "UTF-8");
    }

    //插入XML標簽
    public static StringBuilder setXmlKV(StringBuilder sb,String Key,String value){
        sb.append("<");
        sb.append(Key);
        sb.append(">");

        sb.append(value);

        sb.append("</");
        sb.append(Key);
        sb.append(">");

        return sb;
    }

    //解析XML  獲得 PrePayId
    public static String getPrePayId(String xml){
        int start = xml.indexOf("<prepay_id>");
        int end = xml.indexOf("</prepay_id>");
        if(start < 0 && end < 0){
            return null;
        }
        return xml.substring(start + "<prepay_id>".length(),end).replace("<![CDATA[","").replace("]]>","");
    }
    
    //商戶訂單號
    public static String getOut_trade_no(){
        DateFormat df = new SimpleDateFormat("yyyyMMddHHmmssSSS");
        return df.format(new Date()) + RandomChars.getRandomNumber(7);
    }
    
    //時間戳
    public static String getTimeStamp() {
        return String.valueOf(System.currentTimeMillis() / 1000);
    }
    
    //隨機4位數字
    public static int buildRandom(int length) {
        int num = 1;
        double random = Math.random();
        if (random < 0.1) {
            random = random + 0.1;
        }
        for (int i = 0; i < length; i++) {
            num = num * 10;
        }
        return (int) ((random * num));
    }
    
    public static String inputStream2String(InputStream inStream, String encoding){
         String result = null;
         try {
         if(inStream != null){
          ByteArrayOutputStream outStream = new ByteArrayOutputStream();
          byte[] tempBytes = new byte[1024];
          int count = -1;
          while((count = inStream.read(tempBytes, 0, 1024)) != -1){
            outStream.write(tempBytes, 0, count);
          }
          tempBytes = null;
          outStream.flush();
          result = new String(outStream.toByteArray(), encoding);
         }
         } catch (Exception e) {
         result = null;
         }
         return result;
        }
    
    public static void main(String[] args) {
        System.out.println(getOut_trade_no());
    }
    
}

簽名封裝類(網上很多,原理差不多): 

public class WxSign {
  
  /**
  * 創建簽名
  * @param parameters
  * @param key
  * @return
  */
    @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 = MD5Util.MD5Encode(sb.toString(), "UTF-8").toUpperCase();
         return sign;
    }
}

支付參數封裝類(還算全,夠用了,這個appid,是小寫的 i): 

/package com.odao.weixin.site.cases2017.wxpay.entity;

/**
 * 微信支付參數
 * @author wangfj
 *
 */
public class WxPaySendData {
    //公眾賬號ID
    private String appid;
    //附加數據
    private String attach;
    //商品描述
    private String body;
    //商戶號
    private String mch_id;
    //隨機字符串
    private String nonce_str;
    //通知地址
    private String notify_url;
    //商戶訂單號
    private String out_trade_no;
    //標價金額
    private String total_fee;
    //交易類型
    private String trade_type;
    //終端IP
    private String spbill_create_ip;
    //用戶標識
    private String openid;
    //簽名
    private String sign;
    //預支付id
    private String prepay_id;
    //簽名類型:MD5
    private String signType;
    //時間戳
    private String timeStamp;
    //微信支付時用到的prepay_id
    private String packageStr;
    
    private String return_code;
    private String return_msg;
    private String result_code;
    
    private String bank_type;
    private Integer cash_fee;
    private String fee_type;
    private String is_subscribe;
    private String time_end;
    //微信支付訂單號
    private String transaction_id;
    private String ip;
    private Integer coupon_count;
    private Integer coupon_fee;
    private Integer coupon_fee_0;
    private String coupon_type_0;
    private String coupon_id_0;
    
    public String getCoupon_type_0() {
        return coupon_type_0;
    }
    public void setCoupon_type_0(String coupon_type_0) {
        this.coupon_type_0 = coupon_type_0;
    }
    public String getCoupon_id_0() {
        return coupon_id_0;
    }
    public void setCoupon_id_0(String coupon_id_0) {
        this.coupon_id_0 = coupon_id_0;
    }
    public Integer getCoupon_fee_0() {
        return coupon_fee_0;
    }
    public void setCoupon_fee_0(Integer coupon_fee_0) {
        this.coupon_fee_0 = coupon_fee_0;
    }
    public Integer getCoupon_fee() {
        return coupon_fee;
    }
    public void setCoupon_fee(Integer coupon_fee) {
        this.coupon_fee = coupon_fee;
    }
    public Integer getCoupon_count() {
        return coupon_count;
    }
    public void setCoupon_count(Integer coupon_count) {
        this.coupon_count = coupon_count;
    }
    public String getIp() {
        return ip;
    }
    public void setIp(String ip) {
        this.ip = ip;
    }
    public String getBank_type() {
        return bank_type;
    }
    public void setBank_type(String bank_type) {
        this.bank_type = bank_type;
    }
    public Integer getCash_fee() {
        return cash_fee;
    }
    public void setCash_fee(Integer cash_fee) {
        this.cash_fee = cash_fee;
    }
    public String getFee_type() {
        return fee_type;
    }
    public void setFee_type(String fee_type) {
        this.fee_type = fee_type;
    }
    public String getIs_subscribe() {
        return is_subscribe;
    }
    public void setIs_subscribe(String is_subscribe) {
        this.is_subscribe = is_subscribe;
    }
    public String getTime_end() {
        return time_end;
    }
    public void setTime_end(String time_end) {
        this.time_end = time_end;
    }
    public String getTransaction_id() {
        return transaction_id;
    }
    public void setTransaction_id(String transaction_id) {
        this.transaction_id = transaction_id;
    }
    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 String getTotal_fee() {
        return total_fee;
    }
    public void setTotal_fee(String 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 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 getResult_code() {
        return result_code;
    }
    public void setResult_code(String result_code) {
        this.result_code = result_code;
    }
    public String getSign() {
        return sign;
    }
    public void setSign(String sign) {
        this.sign = sign;
    }
    public String getPrepay_id() {
        return prepay_id;
    }
    public void setPrepay_id(String prepay_id) {
        this.prepay_id = prepay_id;
    }
    public String getSignType() {
        return signType;
    }
    public void setSignType(String signType) {
        this.signType = signType;
    }
    public String getTimeStamp() {
        return timeStamp;
    }
    public void setTimeStamp(String timeStamp) {
        this.timeStamp = timeStamp;
    }
    
    public String getPackageStr() {
        return packageStr;
    }
    public void setPackageStr(String packageStr) {
        this.packageStr = packageStr;
    }
    @Override
    public String toString() {
        return "WxPaySendData [appid=" + appid + ", attach=" + attach
                + ", body=" + body + ", mch_id=" + mch_id + ", nonce_str="
                + nonce_str + ", notify_url=" + notify_url + ", out_trade_no="
                + out_trade_no + ", total_fee=" + total_fee + ", trade_type="
                + trade_type + ", spbill_create_ip=" + spbill_create_ip
                + ", openid=" + openid + ", sign=" + sign + ", prepay_id="
                + prepay_id + ", signType=" + signType + ", timeStamp="
                + timeStamp + ", packageStr=" + packageStr + ", return_code="
                + return_code + ", return_msg=" + return_msg + ", result_code="
                + result_code + ", bank_type=" + bank_type + ", cash_fee="
                + cash_fee + ", fee_type=" + fee_type + ", is_subscribe="
                + is_subscribe + ", time_end=" + time_end + ", transaction_id="
                + transaction_id + ", ip=" + ip + ", coupon_count="
                + coupon_count + ", coupon_fee=" + coupon_fee
                + ", coupon_fee_0=" + coupon_fee_0 + ", coupon_type_0="
                + coupon_type_0 + ", coupon_id_0=" + coupon_id_0 + "]";
    }
}

好了,做完上面,你已經在微信支付控件里輸入了密碼,支付完了,跟着我的步伐,別亂。。。。

第四步:微信支付回調(還記得上面第三部中配置的notify_url個字段嗎,忘記了翻上去看看,為什么要做這一步?你需要告訴微信,你支付成功了,然后你可以在回調函數中寫你的業務邏輯。。。)

/**
 * 支付回調接口
* @param request
* @return
 */
@RequestMapping("/callback")
public void callBack(HttpServletRequest request, HttpServletResponse response){
     response.setContentType("text/xml;charset=UTF-8");
     try {
      InputStream is = request.getInputStream();
      String result = IOUtils.toString(is, "UTF-8");
            if("".equals(result)){
                response.getWriter().write("<xm><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[參數錯誤!]]></return_msg></xml>");
                return ;
            }
      //解析xml
      XStream stream = new XStream(new DomDriver());
      stream.alias("xml", WxPaySendData.class);
      WxPaySendData wxPaySendData = (WxPaySendData)stream.fromXML(result);
      System.out.println(wxPaySendData.toString());
            
          String appid = wxPaySendData.getAppid();
          String mch_id =wxPaySendData.getMch_id();
          String nonce_str = wxPaySendData.getNonce_str();
            String out_trade_no = wxPaySendData.getOut_trade_no();
            String total_fee = wxPaySendData.getTotal_fee();
            //double money = DBUtil.getDBDouble(DBUtil.getDBInt(wxPaySendData.getTotal_fee())/100.0);
            String trade_type = wxPaySendData.getTrade_type();
            String openid =wxPaySendData.getOpenid();
            String return_code = wxPaySendData.getReturn_code();
            String result_code = wxPaySendData.getResult_code();
            String bank_type = wxPaySendData.getBank_type();
            Integer cash_fee = wxPaySendData.getCash_fee();
            String fee_type = wxPaySendData.getFee_type();
            String is_subscribe = wxPaySendData.getIs_subscribe();
            String time_end = wxPaySendData.getTime_end();
            String transaction_id = wxPaySendData.getTransaction_id();
            String sign = wxPaySendData.getSign();
            
            //簽名驗證
            SortedMap<Object,Object> parameters = new TreeMap<Object,Object>();
            parameters.put("appid",appid);
            parameters.put("mch_id",mch_id);
            parameters.put("nonce_str",nonce_str);
            parameters.put("out_trade_no",out_trade_no);
            parameters.put("total_fee",total_fee);
            parameters.put("trade_type",trade_type);
            parameters.put("openid",openid);
            parameters.put("return_code",return_code);
            parameters.put("result_code",result_code);
            parameters.put("bank_type",bank_type);
            parameters.put("cash_fee",cash_fee);
            parameters.put("fee_type",fee_type);
            parameters.put("is_subscribe",is_subscribe);
            parameters.put("time_end",time_end);
            parameters.put("transaction_id",transaction_id);
        //以下4個參數針對優惠券(鼓勵金之類的)這個坑真的弄了好久
       parameters.put("coupon_count",wxPaySendData.getCoupon_count());
            parameters.put("coupon_fee",wxPaySendData.getCoupon_fee());
            parameters.put("coupon_id_0",wxPaySendData.getCoupon_id_0());
            parameters.put("coupon_fee_0",wxPaySendData.getCoupon_fee_0()); String sign2
= WxSign.createSign(parameters, WeChatConst.KEY); if(sign.equals(sign2)){//校驗簽名,兩者需要一致,防止別人繞過支付操作,不付錢直接調用你的業務,不然,哈哈,你老板會很開心的 233333.。。。 if(return_code.equals("SUCCESS") && result_code.equals("SUCCESS")){ //業務邏輯(先判斷數據庫中訂單號是否存在,並且訂單狀態為未支付狀態)          //do something ...    //request.setAttribute("out_trade_no", out_trade_no); //通知微信.異步確認成功.必寫.不然會一直通知后台.八次之后就認為交易失敗了. response.getWriter().write("<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>"); }else{ response.getWriter().write("<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[交易失敗]]></return_msg></xml>"); } }else{ //通知微信.異步確認成功.必寫.不然會一直通知后台.八次之后就認為交易失敗了. response.getWriter().write("<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[簽名校驗失敗]]></return_msg></xml>"); } response.getWriter().flush(); response.getWriter().close(); return ; } catch (IOException e) { e.printStackTrace(); } }
如果上面的簽名校驗,你兩個簽名怎么都對不上,那么點這里:https://pay.weixin.qq.com/wiki/tools/signverify/ 如圖配置你上面的參數,再在控制台看你打印的簽名跟這個是不是一直,不一致肯定是上面的參數值有問題(在這里尤其注意那個金額 total_fee )。。。
 
          
         

 好了,到了這里,我想整個流程你應該理解了,把上面的代碼COPY到你的項目里,我再補一下具體的工具類。。。如下:

/**
 * md5加密算法實現
 */
public class MD5Util {
    
    private static final String hexDigits[] = { "0", "1", "2", "3", "4", "5","6", "7", "8", "9", "a", "b", "c", "d", "e", "f" };
    
    /**
     * md5加密
     * 
     * @param text 需要加密的文本
     * @return
     */
    public static String encode(String text) {
        try {// aes rsa
            MessageDigest md = MessageDigest.getInstance("MD5");
            byte[] result = md.digest(text.getBytes()); // 對文本進行加密
            // b
            // 000000..0000011111111
            StringBuilder sb = new StringBuilder();
            for (byte b : result) {
                int i = b & 0xff ; // 取字節的后八位有效值
                String s = Integer.toHexString(i);
                if (s.length() < 2) {
                    s = "0" + s;
                }
                sb.append(s);
            }
            
            // 加密的結果
            return sb.toString();
        } catch (NoSuchAlgorithmException e) {
            // 找不到該算法,一般不會進入這里
            e.printStackTrace();
        }
        
        return "";
    }

     public static String MD5Encode(String origin, String charsetname) {
         String resultString = null;
         try {
            resultString = new String(origin);
            MessageDigest md = MessageDigest.getInstance("MD5");
            if (charsetname == null || "".equals(charsetname))
                resultString = byteArrayToHexString(md.digest(resultString.getBytes()));
            else
                resultString = byteArrayToHexString(md.digest(resultString.getBytes(charsetname)));
        } catch (Exception exception) {

        }
        return resultString;
    }
     
     private static String byteArrayToHexString(byte b[]) {
         StringBuffer resultSb = new StringBuffer();
         for (int i = 0; i < b.length; i++)
             resultSb.append(byteToHexString(b[i]));

         return resultSb.toString();
     }

     private static String byteToHexString(byte b) {
        int n = b;
        if (n < 0)
            n += 256;
        int d1 = n / 16;
        int d2 = n % 16;
        return hexDigits[d1] + hexDigits[d2];
     }
}

好了,看到這里,我相信你也差不多大功告成了,出去溜達溜達。。。。然后。。。。

  

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM