美文网首页PHP编程王国程序员
php开发微信APP支付接口

php开发微信APP支付接口

作者: 代码汇 | 来源:发表于2017-12-26 01:55 被阅读94次

    最近在开发APP中用到了微信支付,因为是第一次用,花了三个晚上才搞定,中途也遇到了好多问题,通过查看文档,终于完成了该功能的实现。在这里简单分享一下后台php接口的开发实例。

    开发流程

    1:用户在商户APP中选择商品,提交订单,选择微信支付。
    2:商户后台收到用户支付单,调用微信支付统一下单接口。参见【统一下单API】。
    3:统一下单接口返回正常的prepay_id,再按签名规范重新生成签名后,将数据传输给APP。参与签名的字段名为appid,partnerid,prepayid,noncestr,timestamp,package。注意:package的值格式为Sign=WXPay
    4:商户APP调起微信支付。api参见本章节【app端开发步骤说明
    5:商户后台接收支付通知。api参见【支付结果通知API
    6:商户后台查询支付结果。,api参见【查询订单API

    开发中

    首先呢我们需要拿到三个参数(appid,mch_id,key),这三个参数分别对应的是 在微信开发平台中创建的移动应用appid,微信支付商户号商户支付秘钥,详情看参考【支付结果通知API
    然后我们先把统一下单所需要的参数列出来

    $request_data = array(
          'appid' => C('WX_APPID'),                         #应用APPID
           'mch_id' => C('WX_MCHID'),                        #商户号
           'trade_type' => 'APP',                            #支付类型
           'nonce_str' => \Org\Util\String::randString(30),  #随机字符串 不长于32位
           'body' => '商品名称',                              #商品名称
           'out_trade_no' => '12345678912456',               #商户后台订单号
           'total_fee' => '1',                               #商品价格
           'spbill_create_ip' => get_client_ip(),            #用户端实际ip
           'notify_url' => 'http://***/app/index.php/Home/Wxpay/wx_notify', #异步通知回调地址
      );
    

    这些都是请求参数必填项,其他参数请查看文档
    下来我们就要使用这些参数生成签名了

    $request_data['sign'] = $this -> get_sign($request_data);
    

    我们下来需要把微信请求的数据拼装成 xml格式,注意:xml数据要使用<![CDATA[]]>包括

    $xml_data = $this -> set_xmldata($request_data);
    打印$xml_data结果如下
    <xml>
        <appid><![CDATA[wx7ad3cc6c6111111]]></appid>
        <mch_id><![CDATA[1494741111]]></mch_id>
        <trade_type><![CDATA[APP]]></trade_type>
        <nonce_str><![CDATA[WXXWkMDOgLIqhUnITfNrBbJEVGQdRO]]></nonce_str>
        <body><![CDATA[u5546u54c1u540du79f0]]></body>
        <out_trade_no><![CDATA[12345678912456]]></out_trade_no>
        <total_fee><![CDATA[1]]></total_fee>
        <spbill_create_ip><![CDATA[1.86.242.193]]></spbill_create_ip>
        <notify_url><![CDATA[http://***/app/index.php/Home/Wxpay/wx_notify]]></notify_url>
        <sign><![CDATA[EC0BFB3434A72F20C2CA3378BF07264C]]></sign>
    </xml>
    

    现在就可以向微信发送请求了

    $res = $this -> send_prePaycurl($xml_data);
    这是请求的返回值
    {
      return_code: "SUCCESS",       #业务结果 只有这里返回SUCCESS才会有prepay_id
      return_msg: "OK",             #返回结果描述
      appid: "wx7ad3cc6c6111111",  #应用APPID
      mch_id: "1494741111",         #商户号
      nonce_str: "jkh9mmRlmSHBJxO0",   #随机字符串
      sign: "AF3B26B1E58591D6565E61DDFBB7837B",  #签名
      result_code: "SUCCESS",    #也是业务结果
      prepay_id: "wx20171226005556c5c65b325a0132782836", #预支付交易会话标识,用于APP请求微信支付调用,有效期两小时
      trade_type: "APP"  #支付类型
    }
    

    到这里拿到prepay_id还没完我们还需要对返回的数据进行二次签名

    if($res['return_code'] == 'SUCCESS' && $res['result_code'] == 'SUCCESS'){
           $two_data['appid'] = C('WX_APPID');  #APPID
           $two_data['partnerid'] = C('WX_MCHID');  #商户号
           $two_data['prepayid'] = $res['prepay_id'];  //预支付交易会话标识
           $two_data['noncestr'] = \Org\Util\String::randString(30);  
           $two_data['timestamp'] = time();   #时间戳
           $two_data['package'] = "Sign=WXPay";   #固定值
           $two_data['sign'] = $this -> get_twosign($two_data);  #二次签名
           $this->ajaxReturn(array('code'=>200,'info'=>$two_data));
     }else{
           $this->ajaxReturn(array('code'=>201,'info'=>$res['err_code_des']));
     }
    

    然后就可以在商户APP端通过prepayid进行支付了
    下面我们来列出上面调用的几个公共方法

        //一次签名的函数
        private function get_sign($data){
            ksort($data);
            $str = '';
            foreach ($data as $key => $value) {
                $str .= !$str ? $key . '=' . $value : '&' . $key . '=' . $value;
            }
            $str.='&key='.C('WX_KEY');
            $sign = strtoupper(md5($str));
            return $sign;
        }
        //二次签名的函数
        private function get_twosign($data){
            $sign_data = array(
                "appid"=>$data['appid'],
                "partnerid"=>$data['partnerid'],
                "prepayid"=>$data['prepayid'],
                "noncestr"=>$data['noncestr'],
                "timestamp"=>$data['timestamp'],
                "package"=>$data['package'],
            );
            return $this -> get_sign($sign_data);
        }
        //生成xml格式的函数
        private function set_xmldata($data) {
            $xmlData = "<xml>";
              foreach ($data as $key => $value) {
               $xmlData.="<".$key."><![CDATA[".$value."]]></".$key.">";
              }
              $xmlData = $xmlData."</xml>";
              return $xmlData;
        }
        //通过curl发送数据给微信接口的函数
        private function send_prePaycurl($xmlData) {
            $url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
            $header[] = "Content-type: text/xml";
            $curl = curl_init();
            curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
            curl_setopt($curl, CURLOPT_URL, $url);
            curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
            curl_setopt($curl, CURLOPT_POST, 1);
            curl_setopt($curl, CURLOPT_POSTFIELDS, $xmlData);
            $data = curl_exec($curl);
            if (curl_errno($curl)) {
                print curl_error($curl);
            }
            curl_close($curl);
            return $this -> _xmldataparse($data);
        }
        //xml数据解析函数
        private function _xmldataparse($data){
            $msg = array();
            $msg = (array)simplexml_load_string($data, 'SimpleXMLElement', LIBXML_NOCDATA);
            return $msg;
        }
    

    微信回调

    支付有了,肯定还得有回调

        //微信回调
        public function wx_notify(){  
           //允许从外部加载XML实体(防止XML注入攻击)
            libxml_disable_entity_loader(true);  
            $postStr = $this -> post_data();  #接收微信返回数据xml格式
            $postObj = simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA);   #xml格式数据转换成对象
            $arr = $this -> object_toarray($postObj); #对象转成数组  
            ksort($arr);   # 对数据进行排序  
            $str = $this -> params_tourl($arr);  #对数据拼接成字符串 
            $user_sign = strtoupper(md5($str));   //把微信返回的数据进行再次签名
           //验证签名
            if($user_sign == $arr['sign']){
                //验证签名成功  处理商户订单逻辑
                //给微信返回接收到数据通知
                return '<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>';
            }else{
                //签名验证失败   微信会再次访问回调方法
                return '<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>';
            }
        } 
    

    回调用到的方法如下

    // 接收post数据  
        /*  
        *  微信是用$GLOBALS['HTTP_RAW_POST_DATA'];这个函数接收post数据的  
        */  
        public function post_data(){  
            $receipt = $_REQUEST;  
            if($receipt==null){  
                $receipt = file_get_contents("php://input");  
                if($receipt == null){  
                    $receipt = $GLOBALS['HTTP_RAW_POST_DATA'];  
                }  
            }  
            return $receipt;  
        }  
          
        //把对象转成数组  
        public function object_toarray($arr) {  
            if(is_object($arr)) {  
                $arr = (array)$arr;  
            } if(is_array($arr)) {  
                foreach($arr as $key=>$value) {  
                    $arr[$key] = $this->object_toarray($value);  
                }  
            }  
            return $arr;  
        }  
          
          
         /**  
         * 格式化参数格式化成url参数  
         */  
        private function params_tourl($arr)  
        {  
            $weipay_key = C('WX_KEY');//微信的key,这个是微信支付给你的key,不要瞎填。  
            $buff = "";  
            foreach ($arr as $k => $v)  
            {  
                if($k != "sign" && $v != "" && !is_array($v)){  
                    $buff .= $k . "=" . $v . "&";  
                }  
            }  
            $buff = trim($buff, "&");  
            return $buff.'&key='.$weipay_key;  
        }      
    

    总结:首先微信支付的流程比较多,公众号开放平台微信商户,配置参数的时候要看仔细,不要后面的坑特别多,因为是第一次写微信支付,可能会存在部分问题,欢迎大家可以在下面留言反馈。

    这是最后完成的功能


    效果演示_1225.gif

    下面分享一下全部的代码

    <?php
    namespace Home\Controller;
    use Think\Controller;
    /**
     * php开发微信app支付接口
     * @global  WX_APPID  开放平台->移动应用appid
     * @global  WX_MCHID  微信支付商户号            
     * @global  WX_KEY    商户支付秘钥              
     * @author cnavaslh <admin@lihui521.cn> 2017-12-23
     */
    class WxpayController extends Controller
    {
        /**
         * 微信支付统一下单 >>> 生成预支付交易单
         */
        public function wx_pay(){
    
           $request_data = array(
                 'appid' => C('WX_APPID'),                         #应用APPID
                 'mch_id' => C('WX_MCHID'),                        #商户号
                 'trade_type' => 'APP',                            #支付类型
                 'nonce_str' => \Org\Util\String::randString(30),  #随机字符串 不长于32位
                 'body' => '商品名称',                             #商品名称
                 'out_trade_no' => '12345678912456',                    #商户后台订单号
                 'total_fee' => '1',                             #商品价格
                 'spbill_create_ip' => get_client_ip(),            #用户端实际ip
                 'notify_url' => 'http://shop.lsmrsd.com/app/index.php/Home/Wxpay/wx_notify', #异步通知回调地址
            );        
            // 获取签名
            $request_data['sign'] = $this -> get_sign($request_data);
            // 拼装数据
            $xml_data = $this -> set_xmldata($request_data);
            
    
            // 发送请求
            $res = $this -> send_prePaycurl($xml_data);
            $this->ajaxReturn($res);
            if($res['return_code'] == 'SUCCESS' && $res['result_code'] == 'SUCCESS'){
                $two_data['appid'] = C('WX_APPID');  #APPID
                $two_data['partnerid'] = C('WX_MCHID');  #商户号
                $two_data['prepayid'] = $res['prepay_id'];  //预支付交易会话标识
                $two_data['noncestr'] = \Org\Util\String::randString(30);  
                $two_data['timestamp'] = time();
                $two_data['package'] = "Sign=WXPay";
                $two_data['sign'] = $this->get_twosign($two_data);
                $this->ajaxReturn(array('code'=>200,'info'=>$two_data));
            }else{
                $this->ajaxReturn(array('code'=>201,'info'=>$res['err_code_des']));
            }
        }
     
        //通过curl发送数据给微信接口的函数
        private function send_prePaycurl($xmlData) {
            $url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
            $header[] = "Content-type: text/xml";
            $curl = curl_init();
            curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
            curl_setopt($curl, CURLOPT_URL, $url);
            curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
            curl_setopt($curl, CURLOPT_POST, 1);
            curl_setopt($curl, CURLOPT_POSTFIELDS, $xmlData);
            $data = curl_exec($curl);
            if (curl_errno($curl)) {
                print curl_error($curl);
            }
            curl_close($curl);
            return $this->_xmldataparse($data);
        }
     
        //xml数据解析函数
        private function _xmldataparse($data){
            $msg = array();
            $msg = (array)simplexml_load_string($data, 'SimpleXMLElement', LIBXML_NOCDATA);
            return $msg;
        }
     
        //生成xml格式的函数
        private function set_xmldata($data) {
            $xmlData = "<xml>";
              foreach ($data as $key => $value) {
               $xmlData.="<".$key."><![CDATA[".$value."]]></".$key.">";
              }
              $xmlData = $xmlData."</xml>";
              return $xmlData;
        }
     
        //一次签名的函数
        private function get_sign($data){
            ksort($data);
            $str = '';
            foreach ($data as $key => $value) {
                $str .= !$str ? $key . '=' . $value : '&' . $key . '=' . $value;
            }
            $str.='&key='.C('WX_KEY');
            $sign = strtoupper(md5($str));
            return $sign;
        }
     
        //二次签名的函数
        private function get_twosign($data){
            $sign_data = array(
                "appid"=>$data['appid'],
                "partnerid"=>$data['partnerid'],
                "prepayid"=>$data['prepayid'],
                "noncestr"=>$data['noncestr'],
                "timestamp"=>$data['timestamp'],
                "package"=>$data['package'],
            );
            return $this->get_sign($sign_data);
        }
    
        //微信回调
        public function wx_notify(){  
           //允许从外部加载XML实体(防止XML注入攻击)
            libxml_disable_entity_loader(true);  
            $postStr = $this -> post_data();//接收post数据  
            $postObj = simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA);  
            $arr = $this -> object_toarray($postObj);//对象转成数组  
            ksort($arr);// 对数据进行排序  
            $str = $this -> params_tourl($arr);//对数据拼接成字符串 
            $user_sign = strtoupper(md5($str));  
            if($user_sign == $arr['sign']){//验证签名 
                return '<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>';
            }else{
                return '<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>';
            }
        }  
          
        // 接收post数据  
        /*  
        *  微信是用$GLOBALS['HTTP_RAW_POST_DATA'];这个函数接收post数据的  
        */  
        public function post_data(){  
            $receipt = $_REQUEST;  
            if($receipt==null){  
                $receipt = file_get_contents("php://input");  
                if($receipt == null){  
                    $receipt = $GLOBALS['HTTP_RAW_POST_DATA'];  
                }  
            }  
            return $receipt;  
        }  
          
        //把对象转成数组  
        public function object_toarray($arr) {  
            if(is_object($arr)) {  
                $arr = (array)$arr;  
            } if(is_array($arr)) {  
                foreach($arr as $key=>$value) {  
                    $arr[$key] = $this->object_toarray($value);  
                }  
            }  
            return $arr;  
        }  
          
          
         /**  
         * 格式化参数格式化成url参数  
         */  
        private function params_tourl($arr)  
        {  
            $weipay_key = C('WX_KEY');//微信的key,这个是微信支付给你的key,不要瞎填。  
            $buff = "";  
            foreach ($arr as $k => $v)  
            {  
                if($k != "sign" && $v != "" && !is_array($v)){  
                    $buff .= $k . "=" . $v . "&";  
                }  
            }  
            $buff = trim($buff, "&");  
            return $buff.'&key='.$weipay_key;  
        }      
    
    }
    

    相关文章

      网友评论

      本文标题:php开发微信APP支付接口

      本文链接:https://www.haomeiwen.com/subject/mpflgxtx.html