• 微信小程序支付


    <?php
    class Index{
        // 首先获取小程序如下配置
        private $wxpayconf = [
                'appid'           => 'wx0ad699db1a9ff6d3', // 小程序appid
                'mch_id'          => 1513841901, // 小程序商户号
                'key'             => 'qwer122asdauiyfue1nm65qa12dds1r1', //  key 获取设置路径:微信商户平台(pay.weixin.qq.com)-->账户设置-->API安全-->密钥设置,支付API秘钥
                //'appsecret'       => 'd624aca96d0349eec924cde1baec36cb', // 小程序 APPSecret,这里暂时用不到
                    
                'order_url'       => 'https://api.mch.weixin.qq.com/pay/unifiedorder', //统一下单接口地址
                'orderquery_url'  => 'https://api.mch.weixin.qq.com/pay/orderquery',//查询订单接口地址
                'closeorder_url'  => 'https://api.mch.weixin.qq.com/pay/closeorder',//关闭订单接口地址
                'refund_url'      => 'https://api.mch.weixin.qq.com/secapi/pay/refund',//申请退款接口地址
                'refundquery_url' => 'https://api.mch.weixin.qq.com/pay/refundquery'//查询退款接口地址
                    
        ];
        
        
        
        /*
         * 1.预支付 通过微信 https://api.mch.weixin.qq.com/pay/unifiedorder 接口进行预支付,详见 https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=9_1&index=1
         * 2.通过预支付返回的prepay_id 进行重新组建数据,组建成功后返回给前端,前端进行弹框支付 ,重新组建数据详见 https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=7_7&index=3
         * 3.notify_url 地址就是回调地址,在前端支付成功后,微信会往这个地址发送支付成功订单的相关信息,小程序这个地址不允许有参数,在这个地址中处理微信发送的支付成功订单信息
         */
        // 统一下单
        function index() {
            //这里是封装支付接口给服务商,所以一些商品信息需要获取
            $openid       = $_POST ['openid'];
            $notify_url   = $_POST ['notify_url']; // 支付成功后回调地址,类似 http://www.gaoqingsong.com/index.php 这样后面不能有参数,这里因为封装接口给别人用,所以对方需要提供一个回调接口,将微信回调的xml信息返回给对方
            $body         = $_POST ['body']; //商品描述
            $out_trade_no = $_POST ['out_trade_no']; // 订单号
            $total_fee    = $_POST ['total_fee']; // 订单总金额,单位为分
            $userIP       = $_POST ['userIP'];
            
            // 远程终端ip
            $userIP    = !empty($userIP) ? $userIP : $_SERVER ['REMOTE_ADDR'];
            //获取随机字符串
            $nonce_str = $this->getNonceStr ();
            
            $sign ['appid']            = $this->wxpayconf ['appid'];
            $sign ['mch_id']           = $this->wxpayconf ['mch_id'];
            $sign ['nonce_str']        = $nonce_str;
            $sign ['body']             = !empty($body) ? $body : '集妆箱商品';
            $sign ['out_trade_no']     = $out_trade_no;
            $sign ['total_fee']        = $total_fee;
            $sign ['spbill_create_ip'] = $userIP; // 终端客户端ip
            $sign ['trade_type']       = 'JSAPI'; // 小程序支付值固定:JSAPI
            $sign ['openid']           = $openid;
            $sign ['notify_url']       = 'https://xcxoauth.beauty-box.cn/index/index/payres';//$notify_url;//先将客户的$notify_url保存在订单表数据中(代码中未写),微信回调先调用内部的payres方法,payres记录相关信息后,再找到对应订单数据中的$notify_url将微信的xml信息回调给客户;
            $sign ['sign']             = $this->getSign ( $sign, $this->wxpayconf ['key'] );
            // 微信统一下单接口
            // 先将数组换成XML格式
            $xmlData = $this->arrayToXml ( $sign );
            // 提交订单数据,预支付
            $xml_result = $this->postXmlCurl ( $xmlData, $this->wxpayconf ['order_url'] );
            // 将返回信息从XML还原回数组
            $wx_result = $this->xmlToArray ( $xml_result );
    
            //如果预支付提交订单没问题,则组织相关信息返回给前端,进行支付方,支付成功后,微信会往回调页 $notify_url 发送订单信息
            if ($wx_result ['return_code'] == 'SUCCESS' && $wx_result ['result_code'] == 'SUCCESS') {
                $prepay_id = $wx_result ['prepay_id'];
                
                $return ['appId']     = $this->wxpayconf ['appid'];
                $return ['timeStamp'] = ( string ) time ();
                $return ['nonceStr']  = $nonce_str;
                $return ['package']   = 'prepay_id=' . $prepay_id;
                $return ['signType']  = 'MD5';
                $return ['paySign']   = $this->getSign ( $return, $this->wxpayconf ['key'] );
                echo json_encode ( $return );
            } else {
                if ($wx_result ['return_code'] == 'FAIL') {
                    $info ['return_msg'] = $wx_result ['return_msg'];
                }
                if ($wx_result ['return_code'] == 'SUCCESS' && $wx_result ['result_code'] == 'FAIL') {
                    $info ['err_code']     = $wx_result ['err_code'];
                    $info ['err_code_des'] = $wx_result ['err_code_des'];
                }
                echo json_encode ( $info );
            }
        }
        
        
        
        
        //支付结果回调方法
        public function payres(){
            //记录日志
            $log_filename = "log/payres.txt";
            $log_content = ' ================    访问接口 payres  ================= 时间 : ' . date("Y-m-d H:i:s");
            file_put_contents ( $log_filename, $log_content, FILE_APPEND );
        
            //获取微信回调post传回的xml数据
            $xml_result = file_get_contents('php://input');
        
            $log_content = "
    
    
    =====接收到的xml数据 : " . $xml_result . " ======================================================
    
    ";
            file_put_contents ( $log_filename, $log_content, FILE_APPEND );
        
        
            // 将返回信息从XML还原回数组
            $wx_result = $this->xmlToArray ( $xml_result );
            
            //订单表数据对象,这里假设本地有订单表,字段名和微信订单参数名一致,保存下单时的订单数据
            //这里模型框架用 thinkPHP5
            $this->order_model = new OrderModel ();
        
            if ($wx_result ['return_code'] == 'SUCCESS') {
                //先获取下订单数据
                $where = array();
                $where['out_trade_no'] = $wx_result['out_trade_no'];
                $where['nonce_str']    = $wx_result['nonce_str'];
                //$where['sign']         = $wx_result['sign'];
                $where['total_fee']    = $wx_result['total_fee'];
                $order = $this->order_model->where($where)->find();
                    
                //订单信息
                $log_content = "
    
    
    =====查询本地的对应订单信息 : " . json_encode($order) . " ======================================================
    
    ";
                file_put_contents ( $log_filename, $log_content, FILE_APPEND );
                    
                //如果订单存在并且为未支付状态,订单表字段 pay_status 支付状态,1成功,默认0待支付
                if(!empty($order['id']) && $order['pay_status'] != 1){
        
                    //支付成功后保存数据,并往供应商提供的回调页面传微信回调信息
                    $data = array();
                    $data['id']             = $order['id'];
                    $data['time_end']       = !empty($wx_result['time_end'])?$wx_result['time_end']:null;
                    $data['openid']         = !empty($wx_result['openid'])?$wx_result['openid']:null;
                    $data['bank_type']      = !empty($wx_result['bank_type'])?$wx_result['bank_type']:null;
                    $data['cash_fee']       = !empty($wx_result['cash_fee'])?$wx_result['cash_fee']:null;
                    $data['transaction_id'] = !empty($wx_result['transaction_id'])?$wx_result['transaction_id']:null;
                    $data['return_code']    = !empty($wx_result['return_code'])?$wx_result['return_code']:null;
                    $data['result_code']    = !empty($wx_result['result_code'])?$wx_result['result_code']:null;
                    $data['notify_str']     = $xml_result;
                    $data['notify_time']    = date("Y-m-d H:i:s");
        
                    if($wx_result ['result_code'] == 'SUCCESS'){
                        $data['pay_status'] = 1;
                    }
        
                    //支付成功后保存的数据
                    $log_content = "
    
    
    =====支付成功后保存的数据 : " . json_encode($data) . " ======================================================
    
    ";
                    file_put_contents ( $log_filename, $log_content, FILE_APPEND );
        
        
                    //保存支付数据
                    if($this->order_model->save($data, $data['id']) !== false){
                        /*
                        //如果是封装支付接口,则还需把微信返回的xml数据传给接口使用者
                        //给第三方返回信息
                        $this->postXmlCurl ( $xml_result, $order ['notify_url'] );
                            
                        //保存成功后回传客户端的url
                        $log_content = "
    
    
    =====保存成功后回传客户端的url : " . $order ['notify_url'] . " ======================================================
    
    ";
                        $log_content .= "
    
    
    =====保存成功后回传客户端的xml : " . $xml_result . " ======================================================
    
    ";
                        file_put_contents ( $log_filename, $log_content, FILE_APPEND );
                        */
                        //给微信返回信息
                        $sign ['return_code'] = 'SUCCESS';
                        $sign ['return_msg']  = 'OK';
                        // 先将数组换成XML格式
                        $xmlData = $this->arrayToXml ( $sign );
                            
                        $log_content = "
    
    
    =====返回给微信端 xml : " . $xmlData . " ======================================================
    
    ";
                        $log_content .= "
    
    
    ===== payres 接口结束======================================================
    
    ";
                        file_put_contents ( $log_filename, $log_content, FILE_APPEND );
                        echo $xmlData;exit;
                    }
                }
            }
        
            $log_content = "
    
    
    ===== payres 接口结束======================================================
    
    ";
            file_put_contents ( $log_filename, $log_content, FILE_APPEND );
        }
    
        
        
        
        //查询订单,详见 https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=9_2
        public function orderquery(){
            $out_trade_no = $_POST ['out_trade_no']; // 商户订单号
        
            //获取随机字符串
            $nonce_str = $this->getNonceStr ();
        
            $sign ['appid']            = $this->wxpayconf ['appid'];
            $sign ['mch_id']           = $this->wxpayconf ['mch_id'];
            $sign ['out_trade_no']     = $out_trade_no;
            $sign ['nonce_str']        = $nonce_str;
            $sign ['sign']             = $this->getSign ( $sign, $this->wxpayconf ['key'] );
        
            // 先将数组换成XML格式
            $xmlData = $this->arrayToXml ( $sign );
            // 提交查询订单数据
            $xml_result = $this->postXmlCurl ( $xmlData, $this->wxpayconf ['orderquery_url'] );
            // 将返回信息从XML还原回数组
            $wx_result = $this->xmlToArray ( $xml_result );
            //从查询订单起,返回结果不需要再签名,直接将结果返回,判断逻辑由调用端操作
            //返回结果详见 https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=9_2 返回结果
            echo json_encode ($wx_result);
            exit;
        }
        
        
        
        
        //关闭订单,详见 https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=9_3
        public function closeorder(){
            $out_trade_no = $_POST ['out_trade_no']; // 商户订单号
    
            //获取随机字符串
            $nonce_str = $this->getNonceStr ();
        
            $sign ['appid']            = $this->wxpayconf ['appid'];
            $sign ['mch_id']           = $this->wxpayconf ['mch_id'];
            $sign ['out_trade_no']     = $out_trade_no;
            $sign ['nonce_str']        = $nonce_str;
            $sign ['sign']             = $this->getSign ( $sign, $this->wxpayconf ['key'] );
        
            // 先将数组换成XML格式
            $xmlData = $this->arrayToXml ( $sign );
            // 提交查询订单数据
            $xml_result = $this->postXmlCurl ( $xmlData, $this->wxpayconf ['closeorder_url'] );
            // 将返回信息从XML还原回数组
            $wx_result = $this->xmlToArray ( $xml_result );
            //返回结果详见 https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=9_3 返回结果
            echo json_encode ($wx_result);
            exit;
        }
        
        
        
        //申请退款,详见 https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=9_4
        //退款需用到证书,在 postXmlCurl 方法中用到的证书在,微信商户平台(pay.weixin.qq.com)-->账户设置-->API安全-->证书下载 。证书文件有四个,这里用三个,详见 postXmlCurl 方法中证书调用
        function refund(){
            $out_trade_no  = $_POST ['out_trade_no']; // 订单号
            $out_refund_no = $_POST ['out_refund_no']; // 商户退款单号
            $total_fee     = $_POST ['total_fee']; // 订单总金额,单位为分
            $refund_fee    = $_POST ['refund_fee']; // 退款金额,单位为分
            $notify_url    = $_POST ['notify_url'];//退款结果通知url
        
            //获取随机字符串
            $nonce_str = $this->getNonceStr ();
        
            $sign ['appid']            = $this->wxpayconf ['appid'];
            $sign ['mch_id']           = $this->wxpayconf ['mch_id'];
            $sign ['nonce_str']        = $nonce_str;
            $sign ['out_trade_no']     = $out_trade_no;
            $sign ['out_refund_no']    = $out_refund_no;
            $sign ['total_fee']        = $total_fee;
            $sign ['refund_fee']       = $refund_fee;
            $sign ['notify_url']       = $notify_url;
            $sign ['sign']             = $this->getSign ( $sign, $this->wxpayconf ['key'] );
    
            // 先将数组换成XML格式
            $xmlData = $this->arrayToXml ( $sign );
            // 提交退款申请
            $xml_result = $this->postXmlCurl ( $xmlData, $this->wxpayconf ['refund_url'], true );
            // 将返回信息从XML还原回数组
            $wx_result = $this->xmlToArray ( $xml_result );
            //返回结果详见 https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=9_4 返回结果
            echo json_encode ($wx_result);
            exit;
        }
        
        
        
        
        //退款查询,详见 https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=9_5
        function refundquery(){
            $out_refund_no = $_POST ['out_refund_no']; // 商户退款单号
    
            //获取随机字符串
            $nonce_str = $this->getNonceStr ();
        
            $sign ['appid']            = $this->wxpayconf ['appid'];
            $sign ['mch_id']           = $this->wxpayconf ['mch_id'];
            $sign ['nonce_str']        = $nonce_str;
            $sign ['out_refund_no']    = $out_refund_no;
            $sign ['sign']             = $this->getSign ( $sign, $this->wxpayconf ['key'] );
    
            // 先将数组换成XML格式
            $xmlData = $this->arrayToXml ( $sign );
            // 提交退款申请
            $xml_result = $this->postXmlCurl ( $xmlData, $this->wxpayconf ['refundquery_url']);
            // 将返回信息从XML还原回数组
            $wx_result = $this->xmlToArray ( $xml_result );
            //返回结果详见 https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=9_5 返回结果
            echo json_encode ($wx_result);
            exit;
        }
        
        
        
        
        // 签名方法
        function getSign($params, $key1) {
            // 签名步骤一:按字典序排序数组参数
            ksort ( $params );
            $singstring = '';
            foreach ( $params as $key => $value ) {
                $singstring .= '&' . $key . '=' . $value;
            }
            $string = $singstring . "&key=" . $key1;
            // 签名步骤三:MD5加密
            $string = ltrim ( $string, '&' );
            $string = md5 ( $string );
            // 签名步骤四:所有字符转为大写
            $result = strtoupper ( $string );
            return $result;
        }
        
        // 数组转xml
        function arrayToXml($arr, $is_array = false) {
            if (! $is_array) {
                $xml = '<xml>';
            }
            foreach ( $arr as $key => $val ) {
                if (is_array ( $val )) {
                    $xml .= "<" . $key . ">" . $this->arrayToXml ( $val, true ) . "</" . $key . ">";
                } else {
                    $xml .= "<" . $key . ">" . $val . "</" . $key . ">";
                }
            }
            if (! $is_array) {
                $xml .= "</xml>";
            }
            return $xml;
        }
        
        
        // 数组转xml,备用,第一个不好用,用这个
        protected function arrayToXml_bak($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;
        }
        
        // XML 转数组
        protected function xmlToArray($xml) {
            $array_data = json_decode ( json_encode ( simplexml_load_string ( $xml, 'SimpleXMLElement', LIBXML_NOCDATA ) ), true );
            return $array_data;
        }
        
        // 发送xml请求方法,$verifyhost是否验证主机证书,退款时需要验证
        private static function postXmlCurl($xml, $url, $verifyhost = false, $second = 30) {
            $ch = curl_init ();
            curl_setopt ( $ch, CURLOPT_URL, $url );
            curl_setopt ( $ch, CURLOPT_HEADER, FALSE );
            curl_setopt ( $ch, CURLOPT_RETURNTRANSFER, TRUE );
            // 是否需要证书验证,支付时不需要,退款时需要验证证书
            if($verifyhost == true){
                curl_setopt( $ch, CURLOPT_SSL_VERIFYPEER, TRUE);//证书检查
                curl_setopt( $ch, CURLOPT_SSLCERTTYPE, 'pem');
                curl_setopt( $ch, CURLOPT_SSLCERT, '/var/www/html/xcxoauth.beauty-box.cn/public/cert/apiclient_cert.pem');
                curl_setopt( $ch, CURLOPT_SSLCERTTYPE, 'pem');
                curl_setopt( $ch, CURLOPT_SSLKEY, '/var/www/html/xcxoauth.beauty-box.cn/public/cert/apiclient_key.pem');
                //curl_setopt( $ch, CURLOPT_SSLCERTTYPE, 'pem');
                curl_setopt( $ch, CURLOPT_CAINFO, '/var/www/html/xcxoauth.beauty-box.cn/public/cert/rootca.pem');
            }else{
                curl_setopt ( $ch, CURLOPT_SSL_VERIFYPEER, FALSE );
                curl_setopt ( $ch, CURLOPT_SSL_VERIFYHOST, FALSE );
            }
            // post提交方式
            curl_setopt ( $ch, CURLOPT_POST, TRUE );
            curl_setopt ( $ch, CURLOPT_POSTFIELDS, $xml );
            curl_setopt ( $ch, CURLOPT_CONNECTTIMEOUT, 20 );
            // 设置超时
            curl_setopt ( $ch, CURLOPT_TIMEOUT, $second );
            set_time_limit ( 0 );
            // 运行curl
            $data = curl_exec ( $ch );
            // 返回结果
            if ($data) {
                curl_close ( $ch );
                return $data;
            } else {
                $error = curl_errno ( $ch );
                curl_close ( $ch );
                throw new WxPayException ( "curl出错,错误码:$error" );
            }
        }
        
        /*
         * 生成随机字符串方法
         */
        protected function getNonceStr($length = 32) {
            $chars = "abcdefghijklmnopqrstuvwxyz0123456789";
            $str = "";
            for($i = 0; $i < $length; $i ++) {
                $str .= substr ( $chars, mt_rand ( 0, strlen ( $chars ) - 1 ), 1 );
            }
            return $str;
        }
    }
  • 相关阅读:
    239. [LeetCode ]Sliding Window Maximum
    152.[LeetCode] Maximum Product Subarray
    53. [LeetCode] Maximum Subarray
    90 [LeetCode] Subsets2
    78[LeetCode] Subsets
    练习7.52
    练习7.47、7.48、7.49、7.51
    关于类类型的隐式类型转换
    练习7.44、7.45、7.46
    练习7.36、7.37、7.39、7.40
  • 原文地址:https://www.cnblogs.com/dreamhome/p/9586193.html
Copyright © 2020-2023  润新知