zoukankan      html  css  js  c++  java
  • 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];
         }
    }

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

      

  • 相关阅读:
    Maven关于web.xml中Servlet和Servlet映射的问题
    intellij idea的Maven项目运行报程序包找不到的错误
    修改Maven项目默认JDK版本
    刷题15. 3Sum
    刷题11. Container With Most Water
    刷题10. Regular Expression Matching
    刷题5. Longest Palindromic Substring
    刷题4. Median of Two Sorted Arrays
    刷题3. Longest Substring Without Repeating Characters
    刷题2. Add Two Numbers
  • 原文地址:https://www.cnblogs.com/wangfajun/p/6520705.html
Copyright © 2011-2022 走看看