PHP TP5 支付宝部分接口整合

<?php

namespace fast;

class Alipay
{
    //应用公钥证书
    private $appCertSN=ROOT_PATH."cert".DS.'appCertPublicKey_2021004...crt';
    //应用公钥sn
    private $app_cert_sn='3298923171e2';
    //支付宝公钥证书地址
    private $alipayCertSN=ROOT_PATH."cert".DS.'alipayCertPublicKey_RSA2.crt';
    //支付宝根证书地址
    private $alipayRootCertSN=ROOT_PATH."cert".DS.'alipayRootCert.crt';
    //支付宝根证书sn
    private $alipay_root_cert_sn='687b59193f3f462dd53';
    //应用ID
    private $appId='';
    //应用私钥值
    private $rsaPrivateKey='';
    //支付宝公钥
    private $alipayPublicKey='';
    //aes秘钥
    private $aeskey='';
    //网关
    private $gatewayUrl = "https://openapi.alipay.com/gateway.do";
    //模式 true=证书模式,false=公钥模式
    private $mode=true;

    public function __construct()
    {
        if($this->mode){
        	if(!$this->alipayCertSN){
                $this->alipayCertSN=realpath('.'.config('site.alipayCertSN'));
            }
            if(!$this->appCertSN){
                $this->appCertSN=realpath('.'.config('site.appCertSN'));
            }
            if(!$this->alipayPublicKey){
                $this->alipayPublicKey=$this->getPublicKey($this->alipayCertSN);
            }
            if(!$this->app_cert_sn){
                $this->app_cert_sn=$this->getCertSN($this->appCertSN);
            }
            if(!$this->alipay_root_cert_sn){
                $this->alipay_root_cert_sn=$this->getRootCertSN($this->alipayRootCertSN);
            }
        }else{
            if(!$this->alipayPublicKey){
                $this->alipayPublicKey=config('site.alipay_public_key');
            }
        }
        if(!$this->appId){
            $this->appId=config('site.appid_zfb');
        }
        if(!$this->rsaPrivateKey){
            $this->rsaPrivateKey=config('site.merchant_private_key');
        }
        if(!$this->aeskey){
            $this->aeskey=config('site.aeskey');
        }
    }

    /**
     * 支付宝授权访问令牌
     * https://opendocs.alipay.com/open/02xtla
     * @param $code String 授权码
     * @return array data=用户支付宝UID
     */
    public function aliToken($code){
        $params=$this->getPublicParams('alipay.system.oauth.token');
        $params['grant_type']='authorization_code';
        $params['code']=$code;
        $params['sign']=$this->getSign($params);
        $data=$this->http_request($this->gatewayUrl, $params);
        $result=str_replace(".", "_", $params['method']) . "_response";
        if(!empty($data[$result])){
            return ['code'=>1,'data'=>$data[$result]['user_id']??$data[$result]['open_id'],'token'=>$data[$result]['access_token']];
        }else{
            $res=$data['error_response'];
            return ['code'=>0,'msg'=>$res['msg'].$res['sub_msg']];
        }
    }

    //解密手机号 https://opendocs.alipay.com/mini/api/getphonenumber
    public function decodePhone($content){
        $aesKey=$this->aeskey;
        if(!$aesKey)return ['code'=>0,'msg'=>'未配置aes秘钥'];
        $result=openssl_decrypt(base64_decode($content), 'AES-128-CBC', base64_decode($aesKey),OPENSSL_RAW_DATA);
        if(!$result) return ['code'=>0,'msg'=>'参数有误'];
        $ret=json_decode($result, true);
        if($ret['code']!='10000')return ['code'=>0,'msg'=>$ret['msg'].($ret['subMsg']??'')];
        return ['code'=>1,'data'=>$ret['mobile']];
    }

    /**
     * 生成支付宝小程序推广二维码
     * https://opendocs.alipay.com/mini/03l9b8
     * @param $path string 地址
     * @param $param string 参数,门店id,管理员id
     * @param $desc string 描叙
     * @return array
     */
    public function qrcode($param,$path='pages/index/index',$desc='打开支付宝[扫一扫]'){
        trace('小程序二维码');
//        return ['code'=>1,
//            'qr_code_url'=>'',
//            'qr_code_url_circle_white'=>'',
//            'qr_code_url_circle_blue'=>'',
//        ];
        $order = [
            'url_param' => $path,
            'query_param' => (string)$param,
            'describe' => $desc,
        ];
        $params =$this->getPublicParams('alipay.open.app.qrcode.create');
        $params['biz_content']=json_encode($order,JSON_UNESCAPED_UNICODE);
        $params['sign']=$this->getSign($params);
        $data=$this->http_request($this->gatewayUrl, $params);
        $res=$data[str_replace(".", "_", $params['method']) . "_response"];
        if(!empty($res['code']) && $res['code']==10000){
            return ['code'=>1,
                'qr_code_url'=>$res['qr_code_url'],
                'qr_code_url_circle_white'=>$res['qr_code_url_circle_white'],
                'qr_code_url_circle_blue'=>$res['qr_code_url_circle_blue'],
            ];
        }else{
            return ['code'=>0,'msg'=>$res['msg'].'_'.$res['sub_code'].'_'.($res['sub_msg']??'')];
        }
    }

    /**
     * 统一收单交易创建接口
     * https://opendocs.alipay.com/open/02ekfj
     * @param $order_sn string 订单号
     * @param $money float 支付金额,单位:元
     * @param $subject string 备注
     * @param $buyer_id string 支付宝用户ID
     * @param $type int 异步通知地址0=pay_notify(order_pay表),1=item_notify(order_item表),2=reserve_notify(reserve表)
     * @return array
     */
    public function create($order_sn,$money,$subject,$buyer_id,$type=0){
        trace('统一收单交易创建接口');
        $order = [
            'out_trade_no' => $order_sn,
            'total_amount' => floatval($money),
            'subject' => $subject,
            'buyer_id'=>$buyer_id,
        ];
        if(substr($buyer_id,0,4)!='2088'){
            unset($order['buyer_id']);
            $order['buyer_open_id']=$buyer_id;
        }
        $params =$this->getPublicParams('alipay.trade.create');
        $params['biz_content']=json_encode($order,JSON_UNESCAPED_UNICODE);
        $notify= 'pay_notify';
        switch ($type){
            case 1:$notify= 'item_notify';break;
            case 2:$notify= 'reserve_notify';break;
        }
        $params['notify_url']=request()->domain().'/api/index/'.$notify;//异步通知地址
        $params['sign']=$this->getSign($params);
        $data=$this->http_request($this->gatewayUrl, $params);
        $res=$data['alipay_trade_create_response'];
        if(!empty($res['code']) && $res['code']==10000){
            return ['code'=>1,'data'=>$res['trade_no']];
        }else{
            return ['code'=>0,'msg'=>$res['msg'].$res['sub_msg']];
        }
    }

    //app支付
    public function appPay($order_sn,$money,$subject){
        $params=$this->getPublicParams('alipay.trade.app.pay');
        $order = [
            'out_trade_no' => $order_sn,
            'total_amount' => floatval($money), //支付金额,单位:元
            'subject' => $subject,
        ];
        $params['notify_url']=request()->domain().'/api/index/notify';
        $params['biz_content']=json_encode($order,JSON_UNESCAPED_UNICODE);
        $params['sign']=$this->getSign($params);
        ksort($params);
        return http_build_query($params);
    }

    /**
     * 统一收单交易支付
     * @param $auth_no      string  支付宝资金授权订单号
     * @param $total_amount float   订单总金额
     * @param $out_trade_no string  商户订单号
     * @param $subject      string  订单标题
     * @param $type         int     0=预授权主动扣,1=预授权自动扣,2=周期扣自动扣
     * @return array
     */
    public function pay($auth_no,$total_amount,$out_trade_no,$subject='租金',$type=1){
        trace('交易支付');
        $order = [
            'auth_no' => $auth_no,          //支付宝资金授权订单号
            'total_amount' => $total_amount,//订单总金额。
            'out_trade_no' => $out_trade_no,//商户订单号。
            'subject' => $subject,          //订单标题。
            'product_code' => 'PREAUTH_PAY',
            // 'enable_pay_channels' => 'balance',//支付方式限制只能余额
        ];
        $params =$this->getPublicParams('alipay.trade.pay');
        $params['notify_url']=request()->domain().'/api/index/pay_notify';//item表回调地址
        if($type==0){
            $params['notify_url']=request()->domain().'/api/index/payNotify';//pay表回调地址
        }
        if($type==2){
            $order['product_code']='GENERAL_WITHHOLDING';
            unset($order['auth_no']);
            $order['agreement_params']=[
                'agreement_no'=>$auth_no
            ];
        }
        $params['biz_content']=json_encode($order,JSON_UNESCAPED_UNICODE);
        $params['sign']=$this->getSign($params);
        $data=$this->http_request($this->gatewayUrl, $params);
        $res=$data['alipay_trade_pay_response'];
        if(!empty($res['code']) && $res['code']==10000){
            return ['code'=>1,'msg'=>'ok','data'=>$res];
        }else{
            return ['code'=>0,'msg'=>$res['msg'].$res['sub_msg'],'data'=>$res];
        }
    }

    //统一收单交易关闭接口
    public function close($order_sn){
        trace('交易关闭');
        $params =$this->getPublicParams('alipay.trade.close');
        $params['biz_content']=json_encode(['out_trade_no' => $order_sn,],JSON_UNESCAPED_UNICODE);
        $params['sign']=$this->getSign($params);
        $ret= $this->http_request($this->gatewayUrl, $params);
        $result=$ret[str_replace(".", "_", $params['method']) . "_response"];
        if(!empty($result['code']) && $result['code']==10000){
            return ['code'=>1,'data'=>$result['trade_no']];
        }else{
            return ['code'=>0,'msg'=>$result['msg'].$result['sub_msg']];
        }
    }

    //支付宝当面付    https://opendocs.alipay.com/open/f540afd8_alipay.trade.precreate
    public function precreate($order_sn,$money,$subject){
        trace('支付宝当面付');
        $order = [
            'out_trade_no' => $order_sn,
            'total_amount' => floatval($money), //支付金额,单位:元
            'subject' => $subject,
        ];
        $params =$this->getPublicParams('alipay.trade.precreate');
        $params['notify_url']=request()->domain().'/api/index/zfbNotify';
        $params['biz_content'] = json_encode($order,JSON_UNESCAPED_UNICODE);
        $params['sign']=$this->getSign($params);
        $data=$this->http_request($this->gatewayUrl, $params);
        $res=$data['alipay_trade_precreate_response'];
        if(!empty($res['code'])&&$res['code'] == 10000){
            return ['code'=>1,'data'=>$res['qr_code']];
        }else{
            return ['code'=>0,'msg'=>$res['msg'].$res['sub_msg']];
        }
    }

    /**
     * 支付宝退款
     * @param $order_sn string 商户订单号
     * @param $amount float 退款金额单位为元,支持两位小数。
     * @param $out_request_no string 退款请求号
     * @return array data=Y为退款成功=N或无此字段值返回时需通过退款查询接口进一步确认退款状态
     */
    public function refund($order_sn, $amount, $out_request_no){
        trace('退款');
        $params =$this->getPublicParams('alipay.trade.refund');
        $api_params = [
            'out_trade_no'  => $order_sn,
            'refund_amount'  => $amount,
            'out_request_no'  => $out_request_no,
        ];
        $params['biz_content'] = json_encode($api_params,JSON_UNESCAPED_UNICODE);
        $params['sign']=$this->getSign($params);
        $data=$this->http_request($this->gatewayUrl, $params);
        $result=$data['alipay_trade_refund_response'];
        if(!empty($result['code'])&&$result['code'] == 10000){
            return ['code'=>1,'data'=>$result['fund_change']];
        } else {
            return ['code'=>0,'msg'=>$result['msg'].$result['sub_msg']];
        }
    }

    /**
     * 退款查询
     * @param $order_sn string 商户订单号
     * @param $out_request_no string 退款请求号
     * @return array data=REFUND_SUCCESS时表示退款成功,否则表示退款没有执行成功。
     */
    public function query($order_sn,$out_request_no){
        $order = [
            'out_trade_no' => $order_sn,
            'out_request_no' => $out_request_no,
            'query_options'=>'gmt_refund_pay',
        ];
        $params =$this->getPublicParams('alipay.trade.fastpay.refund.query');
        $params['biz_content']=json_encode($order,JSON_UNESCAPED_UNICODE);
        $params['sign']=$this->getSign($params);
        $data=$this->http_request($this->gatewayUrl, $params);
        $res=$data['alipay_trade_fastpay_refund_query_response'];
        if(!empty($res['code']) && $res['code']==10000){
            return ['code'=>1,'data'=>$res['refund_status']??null,'gmt_refund_pay'=>$res['gmt_refund_pay']??null];
        }else{
            return ['code'=>0,'msg'=>$res['msg'].$res['sub_msg']];
        }
    }

    /**
     * 支付宝单笔转账
     * https://opendocs.alipay.com/open/02byuo?scene=ca56bca529e64125a2786703c6192d41
     * @param $out_biz_no string 转账订单号
     * @param $amount   float 金额
     * @param $type int =0支付宝的会员ID =1支付宝登录号
     * @param $identity int 支付宝用户 UID或账号
     * @param $name string 支付宝用户姓名
     * 调用示例
    $Alipay=new \fast\Alipay();
    $ret= $Alipay->transfer(time(),100,0,'2088722004869251');
     * @return array
     */
    public function transfer($out_biz_no,$amount,$type,$identity,$name=''){
        trace('单笔转账');
        $params=$this->getPublicParams('alipay.fund.trans.uni.transfer');
        //请求参数
        $api_params = [
            'out_biz_no'  => $out_biz_no,
            'trans_amount'  => $amount,
            'biz_scene'=>'DIRECT_TRANSFER',
            'product_code'=>'TRANS_ACCOUNT_NO_PWD',
            'order_title'=>'提现到账',
        ];
        if($type==0){
            $api_params['payee_info']=[
                'identity_type'=>'ALIPAY_USER_ID',
//                'identity_type'=>'ALIPAY_OPEN_ID',
                'identity'=>$identity
            ];
        }else{
            $api_params['payee_info']=[
                'identity_type'=>'ALIPAY_LOGON_ID',
                'identity'=>$identity,
                'name'=>$name
            ];
        }
        $params['biz_content'] = json_encode($api_params,JSON_UNESCAPED_UNICODE);
        $params['sign']=$this->getSign($params);
        $ret= $this->http_request($this->gatewayUrl, $params);
        $result=$ret[str_replace(".", "_", $params['method']) . "_response"];
        if(!empty($result['code'])&&$result['code'] == 10000){
            return ['code'=>1,'data'=>$result['order_id'],'paydate'=>$result['trans_date']];
        } else {
            return ['code'=>0,'msg'=>$result['msg'].':'.$result['sub_msg']];
        }
    }

    //---------芝麻免押,https://opendocs.alipay.com/open/03w0ab -------------------
    //线上资金授权冻结接口
    public function freeze($info){
        $params=$this->getPublicParams('alipay.fund.auth.order.app.freeze');
        $order = [
            'out_order_no' => $info['order_sn'],
            'out_request_no' => $info['order_sn'],
            'amount' => floatval($info['auth_money']),//需要冻结的金额
            'order_title' => '预授权冻结',
            'product_code'=>'PRE_AUTH_ONLINE',
            'timeout_express'=>'3d',
            'deposit_product_mode'=>'DEPOSIT_ONLY',
            'extra_param'=>[
                'category'=>$info['category'],//信用类目
                'serviceId'=>$info['serviceId'],//信用服务id
                //以下是申请租押分离参数
//                "outStoreCode"=> "test_0001",
//                "outStoreAlias"=> config('site.name')."免押服务",
//                "creditExtInfo"=> [
//                    "assessmentAmount"=> $info['price'],
//                    "carrierDesc"=> $info['title'],//商品标题
//                    "rentPeriod"=> $info['day'],//订单租期以天为单位
//                    "rentAmount"=> $info['pay_price'],//订单金额
//                    "rentGoodsId"=> '',//商品ID
//                    "deliveryAddress"=>$info['address'],//收货地址
//                    "deliveryMobile"=>$info['phone'],
//                    "deliveryName"=>$info['name']
//                ]
            ]
        ];
        $params['notify_url']=request()->domain().'/api/index/freezeNotify';
        $params['biz_content']=json_encode($order,JSON_UNESCAPED_UNICODE);
        $params['sign']=$this->getSign($params);
        ksort($params);
        return http_build_query($params);
    }
    //资金授权解冻
    public function unfreeze($auth_no,$out_request_no,$amount,$remark='解冻资金'){
        trace('资金授权解冻');
        $order = [
            'auth_no' => $auth_no,              //支付宝资金授权订单号
            'out_request_no' => $out_request_no,//解冻请求流水号
            'amount' => $amount,                //解冻的金额
            'remark' => $remark,
        ];
        $params =$this->getPublicParams('alipay.fund.auth.order.unfreeze');
        $params['notify_url']=request()->domain().'/api/index/freezeNotify';
        $params['biz_content']=json_encode($order,JSON_UNESCAPED_UNICODE);
        $params['sign']=$this->getSign($params);
        $data=$this->http_request($this->gatewayUrl, $params);
        $res=$data[str_replace(".", "_", $params['method']) . "_response"];
        if(!empty($res['code']) && $res['code']==10000){
            return ['code'=>1,'msg'=>'ok','data'=>$res];
        }else{
            return ['code'=>0,'msg'=>$res['msg'].$res['sub_msg']];
        }
    }
    //资金授权操作查询
    public function freezeQuery($auth_no,$out_order_no,$out_request_no){
        trace('资金授权操作查询');
        $order = [
            'auth_no' => $auth_no,              //支付宝资金授权订单号
            'out_order_no' => $out_order_no,    //商户的授权资金订单号
            'out_request_no' => $out_request_no,//请求流水号
            'operation_type' => 'FREEZE',
        ];
        $params =$this->getPublicParams('alipay.fund.auth.operation.detail.query');
        $params['biz_content']=json_encode($order,JSON_UNESCAPED_UNICODE);
        $params['sign']=$this->getSign($params);
        $data=$this->http_request($this->gatewayUrl, $params);
        $res=$data[str_replace(".", "_", $params['method']) . "_response"];
        if(!empty($res['code']) && $res['code']==10000){
            return ['code'=>1,'data'=>$res];
        }else{
            return ['code'=>0,'msg'=>$res['msg'].$res['sub_msg']];
        }
    }

    //----------------------------支付宝人脸认证服务接口---------------------------------------
    /**
     * 初始化
     * https://opendocs.alipay.com/open/02ahjy?pathHash=ed72e8ea
     * @param $outer_order_no string 商户请求的唯一标识
     * @param $cert_name string 真实姓名
     * @param $cert_no string 证件号码
     * @param $return_url string 需要回跳的目标地址
     * @return array
     */
    public function faceInitialize($outer_order_no,$cert_name,$cert_no,$return_url){
        $params =$this->getPublicParams('alipay.user.certify.open.initialize');
        //请求参数
        $api_params = [
            'outer_order_no'  => $outer_order_no,
            'biz_code'  => 'FACE',
            'identity_param'  => [
                'identity_type'=>'CERT_INFO',
                'cert_type'=>'IDENTITY_CARD',
                'cert_name'=>$cert_name,
                'cert_no'=>$cert_no,
            ], //需要验证的身份信息
            'merchant_config' => [
                'return_url'=>$return_url
            ]//商户个性化配置
        ];
        $params['biz_content'] = json_encode($api_params,JSON_UNESCAPED_UNICODE);
        $params['sign']=$this->getSign($params);
        $ret= $this->http_request($this->gatewayUrl, $params);
        $result=$ret[str_replace(".", "_", $params['method']) . "_response"];
        if(!empty($result['code'])&&$result['code'] == 10000){
            return ['code'=>1,'data'=>$result['certify_id']];
        } else {
            return ['code'=>0,'msg'=>$result['sub_code'].':'.$result['sub_msg']];
        }
    }
    //开始 $certify_id本次申请操作的唯一标识,由开放认证初始化接口调用后生成
    public function faceVerify($certify_id){
        $params =$this->getPublicParams('alipay.user.certify.open.certify');
        $params['biz_content'] = json_encode(['certify_id' => $certify_id],JSON_UNESCAPED_UNICODE);
        $params['sign']=$this->getSign($params);
        ksort($params);
        return $this->gatewayUrl.'?'.http_build_query($params);
    }
    //查询 $certify_id本次申请操作的唯一标识,由开放认证初始化接口调用后生成
    public function faceQuery($certify_id){
        $params =$this->getPublicParams('alipay.user.certify.open.query');
        $params['biz_content'] = json_encode(['certify_id' => $certify_id],JSON_UNESCAPED_UNICODE);
        $params['sign']=$this->getSign($params);
        $ret= $this->http_request($this->gatewayUrl, $params);
        $result=$ret[str_replace(".", "_", $params['method']) . "_response"];
        if(!empty($result['code'])&&$result['code'] == 10000){
            return ['code'=>1,'data'=>$result['passed']];//data是否通过,通过为T,不通过为F
        } else{
            return ['code'=>0,'msg'=>$result['msg'].':'.$result['sub_msg']];
        }
    }

    //----------------------------周期扣接口---------------------------
    /**
     * 签约
     * https://opendocs.alipay.com/open/08bntw?pathHash=725a0634&scene=common&ref=api
     * @param $order_sn string  商户签约号
     * @param $money    float   单次扣款最大金额
     * @param $day      string  首次执行时间
     * @return string
     */
    public function agreement($order_sn,$day,$money=100){
        $order = [
            'product_code' => 'GENERAL_WITHHOLDING',
            'personal_product_code' => 'CYCLE_PAY_AUTH_P',
            'sign_scene' => 'INDUSTRY|CARRENTAL',
            'external_agreement_no' => $order_sn,
            'access_params'=>[
                'channel'=>'ALIPAYAPP'
            ],
            'period_rule_params'=>[
//            'period_type'=>'MONTH',//按月
                'period_type'=>'DAY',//按天
                'period'=>1,
                'execute_time'=>$day,
                'single_amount'=>$money
            ]
        ];
        $params =$this->getPublicParams('alipay.user.agreement.page.sign');
        $params['notify_url']=request()->domain().'/api/index/sign_notify';
        $params['biz_content']=json_encode($order,JSON_UNESCAPED_UNICODE);
        $params['sign']=$this->getSign($params);
        ksort($params);
        return http_build_query($params);
    }
    /**
     * 解约
     * @param $agreement_no string 支付宝签约号
     * @return array
     */
    public function unsign($agreement_no){
        trace('周期扣解约');
        $params =$this->getPublicParams('alipay.user.agreement.unsign');
        $params['notify_url']=request()->domain().'/api/index/sign_notify';
        $params['biz_content']=json_encode(['agreement_no' => $agreement_no],JSON_UNESCAPED_UNICODE);
        $params['sign']=$this->getSign($params);
        $ret= $this->http_request($this->gatewayUrl, $params);
        $result=$ret[str_replace(".", "_", $params['method']) . "_response"];
        if(!empty($result['code']) && $result['code']==10000){
            return ['code'=>1,'msg'=>'ok'];
        }else{
            return ['code'=>0,'msg'=>$result['msg'].$result['sub_msg']];
        }
    }
    /**
     * 计划修改
     * @param $agreement_no string 支付宝签约号
     * @param $day          string 商户下一次扣款时间
     * @return array
     */
    public function modify($agreement_no,$day){
        trace('周期扣计划修改');
        $order = [
            'agreement_no' => $agreement_no,
            'deduct_time'=>$day
        ];
        $params =$this->getPublicParams('alipay.user.agreement.executionplan.modify');
        $params['biz_content']=json_encode($order,JSON_UNESCAPED_UNICODE);
        $params['sign']=$this->getSign($params);
        $ret= $this->http_request($this->gatewayUrl, $params);
        $result=$ret[str_replace(".", "_", $params['method']) . "_response"];
        if(!empty($result['code']) && $result['code']==10000){
            return ['code'=>1,'msg'=>'ok','data'=>$result['deduct_time']];
        }else{
            return ['code'=>0,'msg'=>$result['msg'].$result['sub_msg']];
        }
    }


    //实名证件信息比对验证预咨询 https://opendocs.alipay.com/open/02qq4q?pathHash=1e8d6efd
    public function preconsult($name,$idcard){
        trace('实名证件信息比对验证预咨询:');
        $params =$this->getPublicParams('alipay.user.certdoc.certverify.preconsult');
        $api_params = [
            'user_name' => $name,
            'cert_type' => 'IDENTITY_CARD',
            'cert_no' => $idcard,
        ];
        $params['biz_content'] = json_encode($api_params,JSON_UNESCAPED_UNICODE);
        $params['sign']=$this->getSign($params);
        $ret= $this->http_request($this->gatewayUrl, $params);
        $result=$ret[str_replace(".", "_", $params['method']) . "_response"];
        if(!empty($result['code'])&&$result['code'] == 10000){
            return ['code'=>1,'data'=>$result['verify_id']];
        }else{
            return ['code'=>0,'msg'=>$result['msg'].$result['sub_msg']];
        }
    }
    //实名证件信息比对验证咨询 https://opendocs.alipay.com/open/02qq4q?pathHash=1e8d6efd
    public function consult($verify_id,$auth_token){
        trace('实名证件信息比对验证咨询:');
        $params =$this->getPublicParams('alipay.user.certdoc.certverify.consult');
        $params['auth_token']=$auth_token;
        $params['biz_content'] = json_encode(['verify_id' => $verify_id,],JSON_UNESCAPED_UNICODE);
        $params['sign']=$this->getSign($params);
        $ret= $this->http_request($this->gatewayUrl, $params);
        $result=$ret[str_replace(".", "_", $params['method']) . "_response"];
        if(!empty($result['code'])&&$result['code'] == 10000){
            return ['code'=>1,'passed'=>$result['passed'],'msg'=>$result['passed']=='F'?$result['fail_reason']:''];
        }else{
            return ['code'=>0,'msg'=>$result['msg'].$result['sub_msg']];
        }
    }


    //----------------------------------分账----------------------------
    //分账关系绑定  https://opendocs.alipay.com/open/02c7hq
    public function bind(){
        $order = [
            'out_request_no' => time(),
            'receiver_list' => [[
                "type"=>"loginName",//用户号: userId支付宝登录号: loginName支付宝openId: openId
                "account"=>"",//账号。
                "name"=>"",//真实姓名。
                "memo"=>"技术服务费",
            ]],
        ];
        $params =$this->getPublicParams('alipay.trade.royalty.relation.bind');
        $params['biz_content'] = json_encode($order,JSON_UNESCAPED_UNICODE);
        $params['sign']=$this->getSign($params);
        $ret= $this->http_request($this->gatewayUrl, $params);
        dump($ret);
    }
    //分账结算
    public function settle($out_request_no,$trade_no,$money){
        $order = [
            'out_request_no' => $out_request_no,//请求流水号
            'trade_no' => $trade_no,//支付宝订单号
            'royalty_parameters'=>[[
                'trans_in_type'=>'loginName',//收入方账户
                'trans_in'=>'',//收入方账户
                'amount'=>$money,//分账的金额,单位为元
                'royalty_scene'=>'技术服务费',
            ]],//分账明细信息
            'extend_params' => [
                "royalty_finish"=>"true",// 代表该交易分账是否完结,可选值:true/false,不传默认为false。 true:代表分账完结,则本次分账处理完成后会把该笔交易的剩余冻结金额全额解冻。 false:代表分账未完结。
            ],
        ];
        $params =$this->getPublicParams('alipay.trade.order.settle');
        $params['biz_content'] = json_encode($order,JSON_UNESCAPED_UNICODE);
        $params['sign']=$this->getSign($params);
        $ret= $this->http_request($this->gatewayUrl, $params);
        $result=$ret[str_replace(".", "_", $params['method']) . "_response"];
        if(!empty($result['code'])&&$result['code'] == 10000){
            return ['code'=>1,'data'=>$result['settle_no']];
        }else{
            return ['code'=>0,'msg'=>$result['msg'].$result['sub_msg']];
        }
    }
    //分账查询
    function settle_query($settle_no){
        $params =$this->getPublicParams('alipay.trade.order.settle.query');
        $params['biz_content'] = json_encode([
            'settle_no' => $settle_no,//分账请求单号
        ],JSON_UNESCAPED_UNICODE);
        $params['sign']=$this->getSign($params);
        $ret= $this->http_request($this->gatewayUrl, $params);
        $result=$ret[str_replace(".", "_", $params['method']) . "_response"];
        if(!empty($result['code'])&&$result['code'] == 10000){
            return ['code'=>1,'data'=>$result['royalty_detail_list']];
        }else{
            return ['code'=>0,'msg'=>$result['msg'].$result['sub_msg']];
        }
    }

    //商品文件上传接口 https://opendocs.alipay.com/mini/03l4lq
    public function fileUpload($file){
        trace('商品文件上传接口');
        $params =$this->getPublicParams('alipay.merchant.item.file.upload');
        $params['scene']='SYNC_ORDER';
        $params['sign']=$this->getSign($params);
        //文件内容不参与签名,本地文件
        $params['file_content']=realpath('.'.$file);
        //开启oss了需要先下载下来,上传完了再删除
//        $url=cdnurl($file,true);
//        $name=basename($url);
//        file_put_contents('./'.$name,file_get_contents($url));
//        $params['file_content']=realpath('./'.$name);
        $ret= $this->http_request($params);
//        unlink('./'.$name);
        $result=$ret[str_replace(".", "_", $params['method']) . "_response"];
        if(!empty($result['code'])&&$result['code'] == 10000){
            return ['code'=>1,'data'=>$result['material_id']];
        }
        return ['code'=>0,'msg'=>$result['msg'].$result['sub_msg']];
    }

    //订单数据同步 https://opendocs.alipay.com/mini/043zb5   https://opendocs.alipay.com/mini/024hj6
    public function sync($order,$status){
        trace('订单数据同步:');
        $params =$this->getPublicParams('alipay.merchant.order.sync');
        $api_params = [
            'out_biz_no'  => $order['order_sn'],
            'buyer_id'  => $order['user']['ali_user_id'],
            'order_type'  => 'SERVICE_ORDER',
            'order_create_time'  => date('Y-m-d H:i:s',$order['createtime']),
            'order_modified_time'  => date('Y-m-d H:i:s'),
            'amount'=>$order['price'],
            'ext_info'=>[
                //待支付 WAIT_PAY(签完合同) 已发货 IN_DELIVERY  已逾期 OVERDUE    履约完成 EXERCISED
                ['ext_key'=>'merchant_order_status','ext_value'=>$status],
                ['ext_key'=>'merchant_biz_type','ext_value'=>'3C_RENT'],
                ['ext_key'=>'merchant_order_link_page','ext_value'=>'pages/order/orderdetail/orderdetail?id='.$order['id']],
                ['ext_key'=>'tiny_app_id','ext_value'=>config('site.appid_zfb')],
                ['ext_key'=>'business_info','ext_value'=>json_encode([
                    "first_rent"=> $order['orderitem'][0]['price'],
                    "total_rent"=> $order['price'],
                    "lease"=> count($order['orderitem'])."期",
                    "cash_pledge"=> 0,
                    "thaw_deposit"=> 0,
                    "delivery_time"=> $status!='WAIT_PAY' ? $order['delivery_time'] : '',//发货时间。
                    "lease_period"=> $order['orderitem'][0]['day'].'--'.$order['orderitem'][count($order['orderitem'])-1]['day'],
                    "courier_number"=> $status!='WAIT_PAY' ? $order['express_sn'] : '',//快递递送时的单号。
                    "overdue_amount"=> $status=='OVERDUE' ? $order['overdue_amount'] : '',//逾期的金额。
                    "receiving_time"=> ($status=='EXERCISED' || $status=='OVERDUE') ? $order['receiving_time'] : '',//收货时间。
                    "rent_due_date"=> ($status=='EXERCISED' || $status=='OVERDUE') ? $order['orderitem'][count($order['orderitem'])-1]['day'] : '',//租赁到期的日期。
                    "give_back_date"=> ($status=='EXERCISED' || $status=='OVERDUE') ? $order['orderitem'][count($order['orderitem'])-1]['day'] : '',//应该还机的日期。
                ],JSON_UNESCAPED_UNICODE)],
            ],
            'item_order_list'=>[[
                "unit_price"=>$order['price'],
                "quantity"=>1,
                "item_name"=> $order['goods_name'].$order['sku_attr'],
                "ext_info"=> [[
                    "ext_key"=> "image_material_id",
                    "ext_value"=> $order['material_id']
                ]]
            ]],
        ];
        $params['biz_content'] = json_encode($api_params,JSON_UNESCAPED_UNICODE);
        $params['sign']=$this->getSign($params);
        $ret= $this->http_request($params);
        $result=$ret[str_replace(".", "_", $params['method']) . "_response"];
        if(!empty($result['code'])&&$result['code'] == 10000){
            return ['code'=>1,'data'=>$result];
        }
        return ['code'=>0,'msg'=>$result['msg'].$result['sub_msg']];
    }


    //公共参数
    private function getPublicParams($method){
        $param=[
            'app_id'                => $this->appId,
            'method'                => $method,
            'format'                => 'JSON',
            'charset'               => 'UTF-8',
            'sign_type'             => 'RSA2',
            'timestamp'             => date('Y-m-d H:i:s'),
            'version'               => '1.0',
        ];
        if($this->mode){
            $param['alipay_root_cert_sn']=$this->alipay_root_cert_sn;
            $param['app_cert_sn']=$this->app_cert_sn;
        }
        return $param;
    }

    //支付宝签名
    private function getSign($params){
        ksort($params);
        $stringToBeSigned = "";
        $i = 0;
        foreach ($params as $k => $v) {
            if ($i == 0) {
                $stringToBeSigned .= "$k" . "=" . "$v";
            } else {
                $stringToBeSigned .= "&" . "$k" . "=" . "$v";
            }
            $i++;
        }
        $res = "-----BEGIN RSA PRIVATE KEY-----\n" .
            wordwrap($this->rsaPrivateKey, 64, "\n", true) .
            "\n-----END RSA PRIVATE KEY-----";
        openssl_sign($stringToBeSigned, $sign, $res, OPENSSL_ALGO_SHA256);
        return base64_encode($sign);
    }

    //支付宝验签
    public function verify($params) {
        trace($params);
        if(!isset($params['sign']))return false;
        $sign = $params['sign'];
        $params['sign_type'] = null;
        $params['sign'] = null;
        ksort($params);
        $stringToBeSigned = "";
        $i = 0;
        foreach ($params as $k => $v) {
            if($v!=null){
                if ($i == 0) {
                    $stringToBeSigned .= "$k" . "=" . "$v";
                } else {
                    $stringToBeSigned .= "&" . "$k" . "=" . "$v";
                }
                $i++;
            }
        }

        $res = "-----BEGIN PUBLIC KEY-----\n" .
            wordwrap($this->alipayPublicKey, 64, "\n", true) .
            "\n-----END PUBLIC KEY-----";

        //调用openssl内置方法验签,返回bool值
        return (bool)openssl_verify($stringToBeSigned, base64_decode($sign), $res, OPENSSL_ALGO_SHA256);
    }

    //发起curl请求
    private function http_request($data = [],$url = null) {
        if(!$url)$url=$this->gatewayUrl;
        $post=[];
        if(isset($data['biz_content'])){
            $post=http_build_query(['biz_content'=>$data['biz_content']]);
            unset($data['biz_content']);
        }
        $postMultipart = false;
        if(isset($data['file_content'])){
            $postMultipart = true;
            $post['scene']=$data['scene'];
            $post['file_content'] = new \CURLFile($data['file_content']);
            unset($data['scene'],$data['file_content']);
        }
        $curl = curl_init();
        curl_setopt($curl, CURLOPT_URL, $url.'?'.http_build_query($data));
        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);
        curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE);
        if($post){
            curl_setopt($curl, CURLOPT_POST, 1);
            curl_setopt($curl, CURLOPT_POSTFIELDS, $post);
        }
        if (!$postMultipart) {
            curl_setopt($curl, CURLOPT_HTTPHEADER, ['content-type: application/x-www-form-urlencoded;charset=utf-8']);
        }
//        curl_setopt($curl, CURLOPT_TIMEOUT, $time);
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
        $output = curl_exec($curl);
        curl_close($curl);
        $encode = mb_detect_encoding($output, array("ASCII",'UTF-8',"GB2312","GBK",'BIG5'));
        $output=mb_convert_encoding($output, 'UTF-8', $encode);//转为utf-8编码
//        trace($data);
        trace($output);
        return json_decode($output,true);
    }

    /**
     * 从证书中提取序列号
     * @param $certPath
     * @return string
     */
    function getCertSN($certPath)
    {
        $cert = file_get_contents($certPath);
        $ssl = openssl_x509_parse($cert);
        return md5($this->array2string(array_reverse($ssl['issuer'])) . $ssl['serialNumber']);
    }

    /**
     * 从证书中提取公钥
     * @param $certPath
     * @return mixed
     */
    function getPublicKey($certPath)
    {
        $cert = file_get_contents($certPath);
        $pkey = openssl_pkey_get_public($cert);
        $keyData = openssl_pkey_get_details($pkey);
        $public_key = str_replace('-----BEGIN PUBLIC KEY-----', '', $keyData['key']);
        return trim(str_replace('-----END PUBLIC KEY-----', '', $public_key));
    }

    /**
     * 提取根证书序列号
     * @param $certPath string 根证书
     * @return string|null
     */
    function getRootCertSN($certPath)
    {
        $cert = file_get_contents($certPath);
        $array = explode("-----END CERTIFICATE-----", $cert);
        $SN = null;
        for ($i = 0; $i < count($array) - 1; $i++) {
            $ssl[$i] = openssl_x509_parse($array[$i] . "-----END CERTIFICATE-----");
            if(strpos($ssl[$i]['serialNumber'],'0x') === 0){
                $ssl[$i]['serialNumber'] = $this->hex2dec($ssl[$i]['serialNumberHex']);
            }
            if ($ssl[$i]['signatureTypeLN'] == "sha1WithRSAEncryption" || $ssl[$i]['signatureTypeLN'] == "sha256WithRSAEncryption") {
                if ($SN == null) {
                    $SN = md5($this->array2string(array_reverse($ssl[$i]['issuer'])) . $ssl[$i]['serialNumber']);
                } else {
                    $SN = $SN . "_" . md5($this->array2string(array_reverse($ssl[$i]['issuer'])) . $ssl[$i]['serialNumber']);
                }
            }
        }
        return $SN;
    }

    /**
     * 0x转高精度数字
     * @param $hex
     * @return int|string
     */
    private function hex2dec($hex)
    {
        $dec = 0;
        $len = strlen($hex);
        for ($i = 1; $i <= $len; $i++) {
            $dec = bcadd($dec, bcmul(strval(hexdec($hex[$i - 1])), bcpow('16', strval($len - $i))));
        }
        return $dec;
    }

    private function array2string($array)
    {
        $string = [];
        if ($array && is_array($array)) {
            foreach ($array as $key => $value) {
                $string[] = $key . '=' . $value;
            }
        }
        return implode(',', $string);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值