美文网首页
【PHP】一个类完成支付宝当面付及WAP支付(手机网站支付)

【PHP】一个类完成支付宝当面付及WAP支付(手机网站支付)

作者: 复秋 | 来源:发表于2020-03-11 14:02 被阅读0次

    废话不多说,直接上代码,框架是TP5.把类保存放到extend/payment下面。代码如下:

    <?php
    namespace payment;
    
    use \think\Db;
    
    /**
     * @author
     *
     * 支付宝支付类
     */
    class Alipay {
    
        //是否沙盒环境
        public $is_sandbox = false;
        //沙盒地址
        private $sandurl = 'https://openapi.alipaydev.com/gateway.do';
        //正式地址
        private $url = 'https://openapi.alipay.com/gateway.do';
        //appid
        private $appid ;
        //商户应用私钥
        private $rsaPrivateKey = '商户设置的私钥';
    
        //支付宝公钥 验签使用
        private $alipayPublicKey= '支付宝自动生成的公钥';
    
    
        //回调地址
        private $notify_url;
    
        //订单号
        private $order_id;
    
        //订单金额
        private $order_money;
    
        //订单描述
        private $order_desc;
    
        //扩展字段
        private $extends;
    
        private $charset = 'gbk';
    
        public function __construct($alipay, $order){
    
            $this->appid = $alipay['appID'];
            $this->rsaPrivateKey = $alipay['privateKey'];
            $this->alipayPublicKey = $alipay['publicKey'];
    
            $this->order_id = $order['order_id'];
            $this->order_money = $order['order_money'];
            $this->order_desc = $order['order_desc'];
            $this->notify_url = $order['notify_url'];
            $this->extends = $order['extends'];
    
        }
    
        /**
         * @author
         *
         * 发起支付
         */
        public function pay(){
    
            $time = time();
            $url = $this->url;
            //异步回调
            $notify_url = $this->notify_url;
    
            //请求参数的集合 json_encode
            $biz_content = [
                'out_trade_no' => $this->order_id,
                //'seller_id' => ''
                'total_amount' => $this->order_money,
                //'discountable_amount' => '',
                'subject' => $this->order_desc,
                //'goods_detail' => '',
                //'body' => $order['body'],
                //'operator_id' => '',
                //'store_id' => '',
                //'disable_pay_channels' => '',
                //'enable_pay_channels' => '',
                //'terminal_id'=> '',
                'extend_params' => ['industry_reflux_info' => $this->extends],
                //'timeout_express' => '',
                //'settle_info' => '',
                //'business_params' => '',
                //'qr_code_timeout_express' => '',
            ];
    
            //参数
            $param = [
                'app_id' => $this->appid,                       //支付宝分配给开发者的应用ID
                'method' => 'alipay.trade.precreate',                       //接口名称
                //'format' => 'JSON',                       //紧支持JSON
                'charset' => 'gbk',                 //请求使用的编码格式
                'sign_type' => 'RSA2',                      //商户生成签名字符串所使用的签名算法类型,目前支持RSA2和RSA,推荐使用RSA2
                'sign' => '',                               //商户请求参数的签名串
                'timestamp' => date('Y-m-d H:i:s', $time),                          //发送请求的时间,格式"yyyy-MM-dd HH:mm:ss"
                'version' => '1.0',                             //调用的接口版本,固定为1.0
                'notify_url' => $notify_url,                                //支付宝服务器主动通知商户服务器里指定的页面http/https路径
                //'app_auth_token' => '',                       //app_auth_token
                'biz_content' => json_encode($biz_content),                         //请求参数的集合,最大长度不限,除公共参数外所有请求参数都必须放在这个参数中传递,具体参照各产品快速接入文档
            ];
    
            //组合生成签名参数
            $signdata = [];
            $signdata['app_id'] = $param['app_id'];
            $signdata['method'] = $param['method'];
            $signdata['charset'] = $param['charset'];
            $signdata['sign_type'] = $param['sign_type'];
            $signdata['timestamp'] = $param['timestamp'];
            $signdata['version'] = $param['version'];
            $signdata['notify_url'] = $param['notify_url'];
            $signdata['biz_content'] = $param['biz_content'];
            //生成签名
            $sign = $this->generateSign($signdata, 'RSA2');
            $param['sign'] = $sign;
            $content = $this->curlPost($url,$param);
            $return  = json_decode($content, true);
    
            $return = $return['alipay_trade_precreate_response'];
            if($return['code'] == 10000){
                $out_trade_no = $return['out_trade_no'];
                if($out_trade_no != $this->order_id){
                    return ['code' => -1, 'msg' => '返回订单号错误'];
                }
    
                //生成成功
                $qr_code = $return['qr_code'];
                $data = [];
                $data['order_id'] = $this->order_id;
                $data['code_url'] = $qr_code;
                return ['code' => 1 , 'msg' => '成功' , 'data' => $data];
    
            }else {
                file_put_contents('alipay.log', 'err code:' . $return['code'] . ', err msg:' . $return['msg'] . '\r\n', FILE_APPEND);
                return ['code' => -1 , 'msg' => 'err_code:' . $return['code'] . ',err_msg:' . $return['msg'].',sub_msg:'.$return['sub_msg']];
            }
    
        }
    
    
        /**
         * @author
         *
         * @DateTime
         * 支付宝支付通知
         *
         * @param      <type>  $data   The data
         */
        public function notify($data){
    
            //验签
            //组合验签数据
            $param = $data;
            unset($param['sign']);
            unset($param['sign_type']);
    
            $rst = $this->rsaCheck($param, $data['sign'] , $data['sign_type']);
            if($rst){
                //查询订单状态
                $order = Db::name("order")->where('order_unique', $data['out_trade_no'])->where('status', -1)->find();
                if(!empty($order)){
    
                    $rst = true;
                    if($rst){
                        $time = time();
                        Db::startTrans();
    
                        try {
                            //修改订单状态
                            $order_data = [];
                            $order_data['status'] =1;
                            $order_data['pay_time'] = $time;
                            $order_data['trade_no'] = $data['trade_no'];
                            $rst = Db::name('order')->where('id', $order['id'])->update($order_data);
    
                            Db::commit();
                        }catch(\Exception $e){
                            Db::rollback();
                            file_put_contents('alipaynotify.log', '支付通知数据库操作错误:' . $e->getMessage() . '\r\n', FILE_APPEND);
                            exit;
                        }
                        echo  "success";exit;
                    }else {
                        file_put_contents('alipaynotify.log', '查询订单状态错误\r\n', FILE_APPEND);
                    }
    
                }else {
                    file_put_contents('alipaynotify.log', '未找到相应订单\r\n' , FILE_APPEND );
                    exit;
                }
            }else {
                file_put_contents('alipaynotify.log', '验签失败\r\n' , FILE_APPEND );
                exit;
            }
        }
    
    
        /**
         * @author
         *
         * @DateTime
         * 支付查询接口
         *
         * @param  $order
         * @param  $status  要验证的状态  WAIT_BUYER_PAY-交易创建等待买家付款 TRADE_CLOSED-未付款交易超时关闭或支付完成后全额退款  TRADE_SUCCESS-交易支付成功 TRADE_FINISHED-交易结束不可退款
         * @return false
         */
        public function orderquery($order , $status){
    
            $time = time();
            $url = '';
            $biz_content = [
                'out_trade_no' => $order['order_unique'],
                'trade_no' => $order['trade_no'],
                //'org_pid' => '',
            ];
    
            $param = [
                'app_id' => $this->appid,
                'method' => 'alipay.trade.query',
                'charset' => 'utf-8',
                'sign_type' => 'RSA2',
                'sign' => '',
                'timestamp' => date('Y-m-d H:i:s', $time),
                'version' => '1.0',
                'biz_content' => json_encode($biz_content),
            ];
    
            //组合签名数组
            $signdata = [];
            $signdata['app_id'] = $param['app_id'];
            $signdata['method'] = $param['method'];
            $signdata['charset'] = $param['charset'];
            $signdata['sign_type'] = $param['sign_type'];
            $signdata['timestamp'] = $param['timestamp'];
            $signdata['version'] = $param['version'];
            $signdata['biz_content'] = $param['biz_content'];
    
            //生成签名
            $sign = $this->generateSign($signdata, 'RSA2');
            $param['sign'] = $sign;
    
            $content = $this->curlPost($url,$param);
            $return  = json_decode($content, true);
    
            if($return['code'] == 10000){
    
                if($return['trade_status'] == $status){
                    return true;
                }else {
                    return false;
                }
            }else {
                return false;
            }
        }
    
        public function generateSign($params, $signType = "RSA") {
            return $this->sign($this->getSignContent($params), $signType);
        }
    
        public function getSignContent($params) {
            ksort($params);
            $stringToBeSigned = "";
            $i = 0;
            foreach ($params as $k => $v) {
                if (false === $this->checkEmpty($v) && "@" != substr($v, 0, 1)) {
                    // 转换成目标字符集
                    $v = $this->characet($v, $this->charset);
                    if ($i == 0) {
                        $stringToBeSigned .= "$k" . "=" . "$v";
                    } else {
                        $stringToBeSigned .= "&" . "$k" . "=" . "$v";
                    }
                    $i++;
                }
            }
            unset ($k, $v);
            file_put_contents('111.log', $stringToBeSigned );
            return $stringToBeSigned;
        }
    
        /**
         * 转换字符集编码
         * @param $data
         * @param $targetCharset
         * @return string
         */
        function characet($data, $targetCharset) {
            if (!empty($data)) {
                $fileType = $this->charset;
                if (strcasecmp($fileType, $targetCharset) != 0) {
                    $data = mb_convert_encoding($data, $targetCharset, $fileType);
                    //$data = iconv($fileType, $targetCharset.'//IGNORE', $data);
                }
            }
            return $data;
        }
    
    
        /**
         * 校验$value是否非空
         *  if not set ,return true;
         *    if is null , return true;
         **/
        protected function checkEmpty($value) {
            if (!isset($value))
                return true;
            if ($value === null)
                return true;
            if (trim($value) === "")
                return true;
            return false;
        }
    
    
        /**
         * @author
         *
         * 签名函数
         *
         * @param      <type>  $data      The data
         * @param      string  $signType  The sign type
         *
         * @return     <type>  ( description_of_the_return_value )
         */
        protected function sign($data, $signType = "RSA") {
            $priKey=$this->rsaPrivateKey;
            $res = "-----BEGIN RSA PRIVATE KEY-----\n" .
                wordwrap($priKey, 64, "\n", true) .
                "\n-----END RSA PRIVATE KEY-----";
            ($res) or die('您使用的私钥格式错误,请检查RSA私钥配置');
            if ("RSA2" == $signType) {
                openssl_sign($data, $sign, $res, version_compare(PHP_VERSION,'5.4.0', '<') ? SHA256 : OPENSSL_ALGO_SHA256); //OPENSSL_ALGO_SHA256是php5.4.8以上版本才支持
            } else {
                openssl_sign($data, $sign, $res);
            }
            $sign = base64_encode($sign);
            return $sign;
        }
    
        /**
         * @author
         *
         * 验签函数
         *
         * @param      <type>  $data        The data    带签名数据
         * @param      <type>  $sign        The sign    要校对的签名结果
         * @param      string  $type        The type
         *
         * @return     <type>  ( description_of_the_return_value )
         */
        public function rsaCheck($data, $sign,$type = 'RSA'){
            $public_key = $this->alipayPublicKey;
            $search = [
                "-----BEGIN PUBLIC KEY-----",
                "-----END PUBLIC KEY-----",
                "\n",
                "\r",
                "\r\n"
            ];
            $public_key=str_replace($search,"",$public_key);
            $public_key=$search[0] . PHP_EOL . wordwrap($public_key, 64, "\n", true) . PHP_EOL . $search[1];
            $res=openssl_get_publickey($public_key);
            if($res)
            {
                if($type == 'RSA'){
                    $result = (bool)openssl_verify($this->getSignContent($data), base64_decode($sign), $res);
                }elseif($type == 'RSA2'){
                    $result = (bool)openssl_verify($this->getSignContent($data), base64_decode($sign), $res,OPENSSL_ALGO_SHA256);
                }
                openssl_free_key($res);
            }else{
                return false;
            }
            return true;
        }
    
        public function curlPost($url = '', $postData = '', $options = array())
        {
            if (is_array($postData)) {
                $postData = http_build_query($postData);
            }
            $ch = curl_init();
            curl_setopt($ch, CURLOPT_URL, $url);
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
            curl_setopt($ch, CURLOPT_POST, 1);
            curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
            curl_setopt($ch, CURLOPT_TIMEOUT, 30); //设置cURL允许执行的最长秒数
            if (!empty($options)) {
                curl_setopt_array($ch, $options);
            }
            //https请求 不验证证书和host
            curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
            curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
            $data = curl_exec($ch);
            curl_close($ch);
            return mb_convert_encoding($data,'UTF-8','GBk');;
        }
    
        public function wapPay(){
            $config = [
                //应用ID,您的APPID。
                'app_id' => $this->appid,
                //商户私钥,您的原始格式RSA私钥
                'merchant_private_key' => $this->rsaPrivateKey,
                //异步通知地址
                'notify_url' => $this->notify_url,
                //同步跳转
                'return_url' => "",
                //编码格式
                'charset' => "UTF-8",
                //签名方式
                'sign_type'=>"RSA2",
                //支付宝网关
                'gatewayUrl' => $this->url,
                //支付宝公钥
                'alipay_public_key' => $this->alipayPublicKey,
            ];
            //超时时间
            $timeout_express="1m";
    
            vendor('Alipay.wappay.buildermodel.AlipayTradeWapPayContentBuilder');
            $payRequestBuilder = new \AlipayTradeWapPayContentBuilder();
            $payRequestBuilder->setSubject($this->order_desc);
            $payRequestBuilder->setOutTradeNo($this->order_id);
            $payRequestBuilder->setTotalAmount($this->order_money);
            $payRequestBuilder->setTimeExpress($timeout_express);
            vendor('Alipay.wappay.service.AlipayTradeService');
    
            $payResponse = new \AlipayTradeService($config);
    
            return $payResponse->wapPay($payRequestBuilder,$config['return_url'],$config['notify_url']);
        }
    
    }
    

    特别说明,当面付返回二维码,直接打开手机客户单。
    wap支付,引用了支付宝提供的sdk,请自行下载:https://docs.open.alipay.com/203/105910/

            vendor('Alipay.wappay.buildermodel.AlipayTradeWapPayContentBuilder');
            $payRequestBuilder = new \AlipayTradeWapPayContentBuilder();
            $payRequestBuilder->setSubject($this->order_desc);
            $payRequestBuilder->setOutTradeNo($this->order_id);
            $payRequestBuilder->setTotalAmount($this->order_money);
            $payRequestBuilder->setTimeExpress($timeout_express);
            vendor('Alipay.wappay.service.AlipayTradeService');
    
            $payResponse = new \AlipayTradeService($config);
    
            return $payResponse->wapPay($payRequestBuilder,$config['return_url'],$config['notify_url']);
    

    下载的sdk,放到vendor下面即可使用,另需要修改sdk/AopSdk.php 16行。修改代码如下:

    if (!defined("AOP_SDK_WORK_DIR"))
    {
        define("AOP_SDK_WORK_DIR", dirname(__FILE__) . "/tmp/");
    }
    

    不然会报错的哦!

    完成上述步骤就可以直接使用了,直接代码里面引用(当面付):

    $pay = new \payment\Alipay($alipay, $param);
            $data = $pay->pay();
    
            if($data['code'] == 1){
                header("Location:".$data['data']['code_url']);
                exit;
            }else{
                echo $data['msg'];
            }
    

    大功告成!

    相关文章

      网友评论

          本文标题:【PHP】一个类完成支付宝当面付及WAP支付(手机网站支付)

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