• 微信支付开发


    微信支付真心最坑爹的支付,总结一下

    一:前台页面

    <include file="Public/mobile_head"/>
    <html>
    <head>
        <meta http-equiv="content-type" content="text/html;charset=utf-8"/>
        <meta name="viewport" content="width=device-width, initial-scale=1"/> 
        <title>微信支付样例-支付</title>
        <script type="text/javascript">
        //调用微信JS api 支付
        function jsApiCall()
        {
            WeixinJSBridge.invoke(
                'getBrandWCPayRequest',
                {$jsApiParameters},
                function(res){
                    WeixinJSBridge.log(res.err_msg);
                    //alert(res.err_code+res.err_desc+res.err_msg);
                    var html="您已成功购买{$month_time}个月月卡,立刻获赠游戏币{$coin_award}。游戏UID:{$uid}<br>月卡到期时间: {$month_card_overtime}<br><button class='blue_btn mb_10 '>确定</button>";
                    $.Dialog.sure(html);//成功调用 提示一秒后自动关闭
                    $(".dialog_sure").click(function(){
                        WeixinJSBridge.call('closeWindow');
                    });
                    
                }
            );
        }
    
        function callpay()
        {
            if (typeof WeixinJSBridge == "undefined"){
                if( document.addEventListener ){
                    document.addEventListener('WeixinJSBridgeReady', jsApiCall, false);
                }else if (document.attachEvent){
                    document.attachEvent('WeixinJSBridgeReady', jsApiCall); 
                    document.attachEvent('onWeixinJSBridgeReady', jsApiCall);
                }
            }else{
                jsApiCall();
            }
        }
    
    
        
        </script>
    </head>
    <body>
        <br/>
        <font color="#9ACD32"><b>该笔订单支付金额为<span style="color:#f00;font-size:50px">{$mustpay/100}元</span></b></font><br/><br/>
        <div align="center">
            <button style="210px; height:50px; border-radius: 15px;background-color:#FE6714; border:0px #FE6714 solid; cursor: pointer;  color:white;  font-size:16px;" type="button" onclick="callpay()" >立即支付</button>
        </div>
    </body>
    </html>

    两个问题

    (1)$jsApiParameters参数要对,包含noticestr,stamp,prepayid等,不能丢失,否则付款按钮不能点击

    (2)WeixinJSBridge.call('closeWindow');这是微信的关闭当前页面,不然支付完成还会返回到这里

    二.后台php

        function index() {
            if(IS_POST){
          /**前台参数post处理部分 略***/
            }
            //用的是weiphp 主要是要获取openid
            $param ['token'] = get_token ();
            $param ['openid'] = get_openid ();
            $config = getAddonConfig ( 'Jssdk' );
            $appid = $config['APPID'];
            $secret = $config['APPSECRET'];
            //$id = 1;                                            //如有插件中数据id,分享url中应加入id参数
            //$url = addons_url ( 'WxPay://WxPay/payorder', $param );  //分享的url需要和WeixinAddonModel中的组装回复url保持相同
            //$this->assign ( 'share_url', $url );
            
            $jssdk = new JSSDK($appid, $secret);
            $jssdk->debug = false;    //启用本地调试模式,将官方的两个json文件放到入口文件index.php同级目录即可!
            $signPackage = $jssdk->GetSignPackage();
            
            $this->assign ( 'signPackage', $signPackage );
            
            //微信支付部分
            //此处可以动态获取数据库中的MCHID和KEY
           /***************************/ 
        //这部分自行配置 /***************************/ $jssdk->MCHID =$config['MCHID']; // 动态MCHID;微信支付分配的商户号 $jssdk->KEY = $config['KEY']; // 动态KEY; $jssdk->SSLCERT_PATH = $config['SSLCERT_PATH']; // 动态KEY; $jssdk->SSLKEY_PATH = $config['SSLKEY_PATH']; // 动态KEY; $jssdk->APPID = $config['APPID']; // 动态KEY; $jssdk->APPSECRET = $config['APPSECRET']; // 动态KEY; //=========步骤2:使用统一支付接口,获取prepay_id============ //使用统一支付接口 $jssdk->parameters['openid'] = $param ['openid']; //trade_type=JSAPI,此参数必传,用户在商户appid下的唯一标识。 $jssdk->parameters['body']="订单支付"; //商品或支付单简要描述 $jssdk->parameters['out_trade_no'] = "outtradeno".time(); //商户系统内部的订单号,32个字符内、可包含字母,不可重复 $jssdk->parameters['total_fee'] = 1; //此处单位为分 出现小数点接口报错必须是整数 $jssdk->parameters['notify_url'] = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxx 带http的地址 就下面那个alarmnotify方法'; //接收微信支付异步通知回调地址 $jssdk->parameters['trade_type'] = "JSAPI"; //取值如下:JSAPI,NATIVE,APP $jssdk->parameters['spbill_create_ip'] = get_client_ip(); //APP和网页支付提交用户端ip,Native支付填调用微信支付API的机器IP。 //以下非必填参数根据需要添加 //$jssdk->parameters['device_info'] = "013467007045764"; //微信支付分配的终端设备号,商户自定义 $jssdk->parameters['detail'] = $desc; //商品名称明细列表 //$jssdk->parameters['attach'] = "说明"; //附加数据,在查询API和支付通知中原样返回,该字段主要用于商户携带订单的自定义数据 //$jssdk->parameters['fee_type'] = "CNY"; //符合ISO 4217标准的三位字母代码,默认人民币:CNY $jssdk->parameters['time_start'] = time(); //订单生成时间,格式为yyyyMMddHHmmss //$jssdk->parameters['time_expire'] = "20091227091010"; //订单失效时间,格式为yyyyMMddHHmmss //$jssdk->parameters['goods_tag'] = "WXG"; //商品标记,代金券或立减优惠功能的参数 $jssdk->parameters['product_id'] = $product_id; //trade_type=NATIVE,此参数必传。此id为二维码中包含的商品ID,商户自行定义。 $prepay_id = $jssdk->getPrepayId(); //微信生成的预支付回话标识,用于后续接口调用中使用,该值有效期为2小时 $jssdk->prepay_id = $prepay_id ; //微信生成的预支付回话标识,用于后续接口调用中使用,该值有效期为2小时 //=========步骤3:使用jsapi调起支付============ $jsApiParameters = $jssdk->getParameters(); //=========步骤N:当有prepayid再生成订单用户自己生成订单============ if($prepay_id){
        //这是自己的业务逻辑,理论上有prepayid之后,自己数据库里面就应该有一张订单,未支付的订单
    $data['uid']=$uid; $data['mustpay']=$mustpay; $data['prepay_id']=$prepay_id; $data['goods']=$sel;//选择的商品,目前是select的值 $data['desc']=$desc; $data['product_id'] = $product_id; $data['openid']= $param ['openid']; $data['trade_type']="JSAPI"; $data['time']=time(); $data['body']= $jssdk->parameters['body']; $data['out_trade_no']=$jssdk->parameters['out_trade_no']; M ( "mall_order" )->add($data); } //JSSDK 用户支付完成后的一些系统操作 /* $param1 ['dcnum'] = $jssdk->parameters['out_trade_no']; $param1 ['openid'] = $jssdk->parameters['openid']; $ajaxurl = addons_url ( 'Jssdk://Jssdk/orderpaid', $param1 ); //用户支付完成后,在微信支付返回alarmnotify之前(不保证时序),可以通过ajax调用,进行一些预处理操作 $jsApiParameters = substr($jsApiParameters,1,-1).",success: function (res) { // 支付成功后的js回调函数 }"; */ $param1 ['openid'] = $jssdk->parameters['openid']; $ajaxurl= addons_url ( 'Member://Member/show_info' ,$param); $this->assign("ajaxurl",$ajaxurl); //向页面传整理好的调起支付参数 $this->assign("jsApiParameters",$jsApiParameters); //向页面传整理好的调起支付参数 $this->display (); }

    三,通知处理

     //支付完成接收支付服务器返回通知
        public function alarmnotify(){
            $post_data = $GLOBALS ['HTTP_RAW_POST_DATA'];
            ThinkLog::record ( "alarmnotify". $post_data  );
                    //log的内容大体如下
            /*
              public 'appid' => string 'w345345345a83a9b9b9' (length=18)
              public 'bank_type' => string 'CFT' (length=3)
              public 'cash_fee' => string '1' (length=1)
              public 'fee_type' => string 'CNY' (length=3)
              public 'is_subscribe' => string 'Y' (length=1)
              public 'mch_id' => string '1233453902' (length=10)
              public 'nonce_str' => string 'ma4l5345345rzZN' (length=16)
              public 'openid' => string 'oV345345345calllGlBdwRU1BjY' (length=28)
              public 'out_trade_no' => string 'outtradeno14234324' (length=20)
              public 'result_code' => string 'SUCCESS' (length=7)
              public 'return_code' => string 'SUCCESS' (length=7)
              public 'sign' => string '735C329E5234234796E723728969' (length=32)
              public 'time_end' => string '2034534504523' (length=14)
              public 'total_fee' => string '1' (length=1)
              public 'trade_type' => string 'JSAPI' (length=5)
              public 'transaction_id' => string '13453453453422835' (length=28)
             */
           
            $result = xmlToArray($post_data);
            $resp = true;
            $respdata["return_code"] = "SUCCESS";
            $respdata["return_msg"] = "";
        
            $map["appid"] = $result["appid"];
            $map["mch_id"] = $result["mch_id"];
            $map["openid"] = $result["openid"];
            $map["out_trade_no"] = $result["out_trade_no"];
            
     
            if($result['result_code']=='SUCCESS'  &&      $result['return_code']=='SUCCESS'){
                //支付成功了
                //进行业务处理
                
                echo "success";
            }
            
    
    
        }    

    四,微信支付类

    <?php
    
    /**
    
    
    类是uctoo写的,貌似现在的类更好,我反正能用就行,
    
    
     * 官方文档:http://mp.weixin.qq.com/wiki/7/aaa137b55fb2e0456bf8dd9148dd613f.html
     * 微信支付:http://pay.weixin.qq.com/wiki/doc/api/index.php?chapter=9_1#
     * 官方示例:http://demo.open.weixin.qq.com/jssdk/sample.zip
     * 
     * 微信JSSDK类,主要修改了保存会话信息机制,示例中使用的是文件,这里使用了ThinkPHP的缓存机制,参考官方提供的示例文档 
     * 新增了调试模式,调用示例如下:
     *         $jssdk = new JSSDK(C('WX_APPID'), C('WX_SECRET'));
     *      $jssdk->debug = true;    //启用本地调试模式,将官方的两个json文件放到入口文件index.php同级目录即可!
     *       $signPackage = $jssdk->GetSignPackage();
     * @命名空间版本
     * @author uctoo (www.uctoo.com) & 阿甘 (QQ:33808 624)
     * @date 2015-1-10 14:10
     */
    namespace Com;
    
    class JsSdk {
      private $appId;
      private $appSecret;
      public $debug = false;
      public $parameters;//获取prepay_id时的请求参数
      //受理商ID,身份标识
      public $MCHID = '';
      //商户支付密钥Key。审核通过后,在微信发送的邮件中查看
      public $KEY = '';
    
      //=======【JSAPI路径设置】===================================
      //获取access_token过程中的跳转uri,通过跳转将code传入jsapi支付页面
      public $JS_API_CALL_URL = '';
    
      //=======【证书路径设置】=====================================
      //证书路径,注意应该填写绝对路径
      public $SSLCERT_PATH = '/xxx/xxx/xxxx/WxPayPubHelper/cacert/apiclient_cert.pem';
      public $SSLKEY_PATH = '/xxx/xxx/xxxx/WxPayPubHelper/cacert/apiclient_key.pem';
    
      //=======【异步通知url设置】===================================
      //异步通知url,商户根据实际开发过程设定
      //C('url')."admin.php/order/notify_url.html";
      public $NOTIFY_URL = '';
    
      //=======【curl超时设置】===================================
      //本例程通过curl使用HTTP POST方法,此处可修改其超时时间,默认为30秒
      public  $CURL_TIMEOUT = 30;
    
      public  $prepay_id;
    
    
      public function __construct($appId, $appSecret) {
        $this->appId = $appId;
        $this->appSecret = $appSecret;
      }
    
      public function getSignPackage() {
        $jsapiTicket = $this->getJsApiTicket();
        $url = "http://$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI]";
        $timestamp = time();
        $nonceStr = $this->createNonceStr();
    
        // 这里参数的顺序要按照 key 值 ASCII 码升序排序
        $string = "jsapi_ticket=$jsapiTicket&noncestr=$nonceStr&timestamp=$timestamp&url=$url";
    
        $signature = sha1($string);
    
        $signPackage = array(
          "appId"     => $this->appId,
          "nonceStr"  => $nonceStr,
          "timestamp" => $timestamp,
          "url"       => $url,
          "signature" => $signature,
          "rawString" => $string
        );
        return $signPackage; 
      }
    
      private function createNonceStr($length = 16) {
        $chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
        $str = "";
        for ($i = 0; $i < $length; $i++) {
          $str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
        }
        return $str;
      }
    
      private function getJsApiTicket() {
          //debug模式
          if ($this->debug) {
            // jsapi_ticket 应该全局存储与更新,以下代码以写入到文件中做示例
            $data = json_decode(file_get_contents("jsapi_ticket.json"));
          } else {
              //从cache中读取,基于ThinkPHP的缓存机制
              $data = (object)(S('jsapi_ticket_json'));
          }
    
        if ($data->expire_time < time()) {       
          $accessToken = $this->getAccessToken();
          $url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?type=1&access_token=$accessToken";
          $res = json_decode($this->httpGet($url));
          $ticket = $res->ticket;
          
          if ($ticket) {
            $data->expire_time = time() + 7200;
            $data->jsapi_ticket = $ticket;
                   
            //debug模式
            if ($this->debug) {
                $fp = fopen("jsapi_ticket.json", "w");
                fwrite($fp, json_encode($data));
                fclose($fp);
            } else {
                //将对象以数组的形式进行缓存
                S('jsapi_ticket_json', (array)$data);
            }
    
          }
        } else {
          $ticket = $data->jsapi_ticket;
        }
    
        return $ticket;
      }
    
      private function getAccessToken() {
    
          //debug模式
          if ($this->debug) {
            // access_token 应该全局存储与更新,以下代码以写入到文件中做示例
              $data = json_decode(file_get_contents("access_token.json"));
              dump($data);
              die();
          } else {
              //从缓存中读取数组并转成对象
            $data = (Object)(S('access_token.json'));
          }
        
        if ($data->expire_time < time()) { 
          $url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=$this->appId&secret=$this->appSecret";
          $res = json_decode($this->httpGet($url));
          $access_token = $res->access_token;
    
          if ($access_token) {
            $data->expire_time = time() + 7000;
            $data->access_token = $access_token;
    
            //debug模式
            if ($this->debug) {
                $fp = fopen("access_token.json", "w");
                fwrite($fp, json_encode($data));
                fclose($fp);
            } else {
                //缓存数组
                S('access_token.json', (array)$data);            
            }
            
          }
        } else {
          $access_token = $data->access_token;
        }
        return $access_token;
      }
    
      private function httpGet($url) {
          
        $curl = curl_init();
        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($curl, CURLOPT_TIMEOUT, 500);
        curl_setopt($curl, CURLOPT_URL, $url);
    
        $res = curl_exec($curl);
        
        //错误检测
        $error = curl_error($curl);
        
        curl_close($curl);
    
        //发生错误,抛出异常
        if($error) throw new Exception('请求发生错误(表检查是否在授权域名下访问):' . $error);
        
        return $res;
      }
    
      //微信支付相关方法
      /**
       *     作用:格式化参数,签名过程需要使用
       */
      function formatBizQueryParaMap($paraMap, $urlencode)
      {
        $buff = "";
        ksort($paraMap);
        foreach ($paraMap as $k => $v)
        {
          if($urlencode)
          {
            $v = urlencode($v);
          }
          //$buff .= strtolower($k) . "=" . $v . "&";
          $buff .= $k . "=" . $v . "&";
        }
        $reqPar = "";
        if (strlen($buff) > 0)
        {
          $reqPar = substr($buff, 0, strlen($buff)-1);
        }
        return $reqPar;
      }
      /**
       *     作用:设置jsapi的参数
       */
      public function getParameters()
      {
        $jsApiObj["appId"] = $this->appId;           //请求生成支付签名时需要,js调起支付参数中不需要
        $timeStamp = time();
        $jsApiObj["timeStamp"] = "$timeStamp";      //用大写的timeStamp参数请求生成支付签名
        $jsParamObj["appId"] =$jsApiObj["appId"]; //加入一行appid
        $jsParamObj["timeStamp"] = $jsApiObj["timeStamp"];      //用小写的timestamp参数生成js支付参数,还要注意数据类型,坑!
        $jsParamObj["nonceStr"] = $jsApiObj["nonceStr"] = $this->createNoncestr();
        $jsParamObj["package"] = $jsApiObj["package"] = "prepay_id=$this->prepay_id";
        $jsParamObj["signType"] = $jsApiObj["signType"] = "MD5";
        $jsParamObj["paySign"] = $jsApiObj["paySign"] = $this->getSign($jsApiObj);
    
        $jsParam = json_encode($jsParamObj);
    
        return $jsParam;
      }
    
      /**
       * 获取prepay_id
       */
      function getPrepayId()
      {
        $result = $this->xmlToArray($this->postXml());
        $prepay_id = $result["prepay_id"];
        return $prepay_id;
      }
      /**
       *     作用:将xml转为array
       */
      public function xmlToArray($xml)
      {
        //将XML转为array
        $array_data = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
        return $array_data;
      }
      /**
       *     作用:post请求xml
       */
      function postXml()
      {
        $xml = $this->createXml();
    
        return  $this->postXmlCurl($xml,"https://api.mch.weixin.qq.com/pay/unifiedorder",$this->CURL_TIMEOUT);
    
      }
      /**
       *     作用:以post方式提交xml到对应的接口url
       */
      public function postXmlCurl($xml,$url,$second=30)
      {
        //初始化curl
        $ch = curl_init();
        //设置超时
        curl_setopt($ch,CURLOP_TIMEOUT, $this->CURL_TIMEOUT);
        //这里设置代理,如果有的话
        //curl_setopt($ch,CURLOPT_PROXY, '8.8.8.8');
        //curl_setopt($ch,CURLOPT_PROXYPORT, 8080);
        curl_setopt($ch,CURLOPT_URL, $url);
        curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,FALSE);
        curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,FALSE);
        //设置header
        curl_setopt($ch, CURLOPT_HEADER, FALSE);
        //要求结果为字符串且输出到屏幕上
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
        //post提交方式
        curl_setopt($ch, CURLOPT_POST, TRUE);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
        //运行curl
        $data = curl_exec($ch);
        curl_close($ch);
        //返回结果
        if($data)
        {
          curl_close($ch);
          return $data;
        }
        else
        {
          $error = curl_errno($ch);
          echo "curl出错,错误码:$error"."<br>";
          echo "<a href='http://curl.haxx.se/libcurl/c/libcurl-errors.html'>错误原因查询</a></br>";
          curl_close($ch);
          return false;
        }
      }
      /**
       *     作用:设置标配的请求参数,生成签名,生成接口参数xml
       */
      function createXml()
      {
        $this->parameters["appid"] = $this->appId;//公众账号ID
        $this->parameters["mch_id"] = $this->MCHID;//商户号
        $this->parameters["nonce_str"] = $this->createNoncestr();//随机字符串
        $this->parameters["sign"] = $this->getSign($this->parameters);//签名
        return  $this->arrayToXml($this->parameters);
      }
       /**
       *     作用:array转xml
       */
      function arrayToXml($arr)
      {
        $xml = "<xml>";
        foreach ($arr as $key=>$val)
        {
          if (is_numeric($val))
          {
            $xml.="<".$key.">".$val."</".$key.">";
    
          }
          else
            $xml.="<".$key."><![CDATA[".$val."]]></".$key.">";
        }
        $xml.="</xml>";
        return $xml;
      }
      /**
       *     作用:生成签名
       */
      public function getSign($Obj)
      {
        foreach ($Obj as $k => $v)
        {
          $Parameters[$k] = $v;
        }
        //签名步骤一:按字典序排序参数
        ksort($Parameters);
        $String = $this->formatBizQueryParaMap($Parameters, false);
        //echo '【string1】'.$String.'</br>';
        //签名步骤二:在string后加入KEY
        $String = $String."&key=".$this->KEY;
        //echo "【string2】".$String."</br>";
        //签名步骤三:MD5加密
        $String = md5($String);
        //echo "【string3】 ".$String."</br>";
        //签名步骤四:所有字符转为大写
        $result_ = strtoupper($String);
        //echo "【result】 ".$result_."</br>";
        return $result_;
      }
        
    }

    有问题再单独开日志吧...感觉不是很顺畅..比支付宝差远了

  • 相关阅读:
    YII2 Gridview 批量删除
    YII2 的复杂查询的一个例子
    Service.properties参数详解
    Kafka安装
    Zookeeper集群安装
    Kafka partition 副本迁移与broker上下线
    副本和分区状态机
    Controller机制
    replica副本同步机制
    Server端处理fetchRequest请求
  • 原文地址:https://www.cnblogs.com/dk1988/p/4590822.html
Copyright © 2020-2023  润新知