<?php
/*
 本代码由 焕岱科技 创建
 技术支持 WX：15285007200
 严禁反编译、逆向等任何形式的侵权行为，违者将追究法律责任
*/

namespace app\common\model\bbfxshop;

use think\Model;

use think\Db;

class Wxpay extends Model
{
    
    protected static $payment = [];
    
    public function __construct() {
        self::getSet();
    }
    
    public static function getSet($platform="wxapp",$level=-1){
        
        if(!empty(self::$payment)){
            return self::$payment;
        }
        
        $set = Db::name("platform")->where("id",UNIACID)->find();
        $payroute_id = 0;
        $result = \app\common\model\bbfxshop\Payroute::getPayData($level);
        if($result['code'] == 1){
            $pay = $result['data'];

            $set['mch_id'] = $pay['mch_id'];
            $set['paykey'] = $pay['paykey'];
            $set['cert'] = $pay['cert'];
            $set['certkey'] = $pay['certkey'];
            $set['publickeyId'] = $pay['publickeyId'];
            $set['publickey'] = $pay['publickey'];
            $set['wxapp_mch_id'] = $pay['wxapp_mch_id'];
            $set['wxapp_paykey'] = $pay['wxapp_paykey'];
            $set['wxapp_cert'] = $pay['wxapp_cert'];
            $set['wxapp_certkey'] = $pay['wxapp_certkey'];
            $set['wxapp_publickeyId'] = $pay['wxapp_publickeyId'];
            $set['wxapp_publickey'] = $pay['wxapp_publickey'];
            $set['wap_AppId'] = $pay['wap_AppId'];
            $set['wap_mch_id'] = $pay['wap_mch_id'];
            $set['wap_paykey'] = $pay['wap_paykey'];
            $set['wap_cert'] = $pay['wap_cert'];
            $set['wap_certkey'] = $pay['wap_certkey'];
            $set['zfb_appid'] = $pay['zfb_appid'];
            $set['zfb_privateKey'] = $pay['zfb_privateKey'];
            $set['zfb_publickey_cert'] = $pay['zfb_publickey_cert'];

            $payroute_id = $pay['id'];
            
        }
        
        $wxapp_cert = ROOT_PATH."public".replaceSiteroot($set['wxapp_cert']);
        $wxapp_cert_key = ROOT_PATH."public".replaceSiteroot($set['wxapp_certkey']);
        $gzh_cert = ROOT_PATH."public".replaceSiteroot($set['cert']);
        $gzh_cert_key = ROOT_PATH."public".replaceSiteroot($set['certkey']);
        $service_provider_cert = ROOT_PATH."public".replaceSiteroot($set['service_provider_cert']);
        $service_provider_certkey = ROOT_PATH."public".replaceSiteroot($set['service_provider_certkey']);
        $wap_cert = ROOT_PATH."public".replaceSiteroot($set['wap_cert']);
        $wap_cert_key = ROOT_PATH."public".replaceSiteroot($set['wap_certkey']);
        $wxapp_publickey = ROOT_PATH."public".replaceSiteroot($set['wxapp_publickey']);
        $gzh_publickey = ROOT_PATH."public".replaceSiteroot($set['publickey']);
        
        if($platform == "wxapp" || $platform == "WXAPP"){
            $appid = $set['wxapp_AppId'];
            $mch_id = $set['wxapp_mch_id'];
            $signkey = $set['wxapp_paykey'];
            $publickeyId = $set['wxapp_publickeyId'];
            $cert = $wxapp_cert;
            $cert_key = $wxapp_cert_key;
            $publickey = $wxapp_publickey;
        }
        else if($platform == "GZH"){
            $appid = $set['AppId'];
            $mch_id = $set['mch_id'];
            $signkey = $set['paykey'];
            $cert = $gzh_cert;
            $cert_key = $gzh_cert_key;
            $publickeyId = $set['publickeyId'];
            $publickey = $gzh_publickey;
        }
        else if($platform == "H5" || $platform == "APP"){
            $appid = $set['wap_AppId'];
            $mch_id = $set['wap_mch_id'];
            $signkey = $set['wap_paykey'];
            $cert = $wap_cert;
            $cert_key = $wap_cert_key;
            $publickeyId = "";
            $publickey = "";
        }
        else{
            $appid = "";
            $mch_id = "";
            $signkey = "";
            $cert = "";
            $cert_key = "";
            $publickeyId = "";
            $publickey = "";
        }
        
        self::$payment = array(
            "appid"=>$appid,
            "mchid"=>$mch_id,
            "signkey"=>$signkey,
            "cert"=>$cert,
            "cert_key"=>$cert_key,
            "publickeyId"=>$publickeyId,
            "publickey"=>$publickey,
            "gzh_appid"=>$set['AppId'],
            "gzh_mchid"=>$set['mch_id'],
            "gzh_signkey"=>$set['paykey'],
            "gzh_cert"=>$gzh_cert,
            "gzh_cert_key"=>$gzh_cert_key,
            "gzh_publickeyId"=>$set['publickeyId'],
            "gzh_publickey"=>$gzh_publickey,
            "wxapp_appid"=>$set['wxapp_AppId'],
            "wxapp_mchid"=>$set['wxapp_mch_id'],
            "wxapp_signkey"=>$set['wxapp_paykey'],
            "wxapp_publickeyId"=>$set['wxapp_publickeyId'],
            "wxapp_cert"=>$wxapp_cert,
            "wxapp_cert_key"=>$wxapp_cert_key,
            "wxapp_publickey"=>$wxapp_publickey,
            "service_provider_status"=>$set['service_provider_status'],
            "service_provider_appid"=>$set['service_provider_appid'],
            "service_provider_mchid"=>$set['service_provider_mchid'],
            "service_provider_paykey"=>$set['service_provider_paykey'],
            "service_provider_cert"=>$service_provider_cert,
            "service_provider_certkey"=>$service_provider_certkey,
            "platform_name"=>$set['name'],
            "payroute_id"=>$payroute_id,
        );
        
        return self::$payment;
    }
    
    // 完结分账
    public static function profitsharingfinish($params){
        $payment = self::getSet($params['type'],$params['level']);
        
        $data = array(
            'mch_id' => $payment['mchid'],
            'appid' => $payment['appid'],
            'nonce_str'=>self::createNoncestr(), 
            'sign_type'=>'HMAC-SHA256',
            'out_order_no'=>$params['out_order_no'],
            'transaction_id'=>$params['transaction_id'],
            'description'=>$params['description']
        );
        
        if($payment['service_provider_status'] == 1){
            $data['sub_appid'] = $data['appid'];
            $data['sub_mch_id'] = $data['mch_id'];
            $data['appid'] = $payment['service_provider_appid'];
            $data['mch_id'] = $payment['service_provider_mchid'];
            $payment['signkey'] = $payment['service_provider_paykey'];
            $payment['cert'] = $payment['service_provider_cert'];
            $payment['cert_key'] = $payment['service_provider_certkey'];
        }
        
        $data['sign'] = self::getSign($data,$payment['signkey'],1);
        
        $xmlData = self::arrayToXml($data);
        $url = "https://api.mch.weixin.qq.com/secapi/pay/profitsharingfinish";
        
        $extras = array(
            "CURLOPT_SSLCERT"=>$payment['cert'],
            "CURLOPT_SSLKEY"=>$payment['cert_key']
        );
        
        $result = self::xmlToArray(self::postXmlCurl($xmlData, $url, 60,$extras));
        addFileLog("fz_profitsharingfinish_dd",json_encode($data));
        addFileLog("fz_profitsharingfinish",json_encode($result));
        if($result['return_code'] != 'SUCCESS') {
            return __json(-2, $result['return_msg']);
        }
        if($result['result_code'] != 'SUCCESS') {
            return __json(-3, $result['err_code'] . ': ' . $result['err_code_des']);
        }
        
        return __json(1, $result['return_msg'],$result);
    }
    
    // 发起分账
    public static function profitsharing($params){
        $payment = self::getSet($params['type'],$params['level']);
        
        $data = array(
            'mch_id' => $payment['mchid'],
            'appid' => $payment['appid'],
            'nonce_str'=>self::createNoncestr(), 
            'sign_type'=>'HMAC-SHA256',
            'out_order_no'=>$params['out_order_no'],
            'transaction_id'=>$params['transaction_id'],
            'receivers'=>json_encode($params['receivers'])
        );
        
        if($payment['service_provider_status'] == 1){
            $data['sub_appid'] = $data['appid'];
            $data['sub_mch_id'] = $data['mch_id'];
            $data['appid'] = $payment['service_provider_appid'];
            $data['mch_id'] = $payment['service_provider_mchid'];
            $payment['signkey'] = $payment['service_provider_paykey'];
            $payment['cert'] = $payment['service_provider_cert'];
            $payment['cert_key'] = $payment['service_provider_certkey'];
        }
        
        $data['sign'] = self::getSign($data,$payment['signkey'],1);
        // echo "<pre>";
        // print_r($data);die;
        $xmlData = self::arrayToXml($data);
        $url = "https://api.mch.weixin.qq.com/secapi/pay/profitsharing";
        
        $extras = array(
            "CURLOPT_SSLCERT"=>$payment['cert'],
            "CURLOPT_SSLKEY"=>$payment['cert_key']
        );
        
        $result = self::xmlToArray(self::postXmlCurl($xmlData, $url, 60,$extras));
        // file_put_contents("fz_profitsharing.txt",json_encode($result));
        if($result['return_code'] != 'SUCCESS') {
            return __json(-2, $result['return_msg']);
        }
        if($result['result_code'] != 'SUCCESS') {
            return __json(-3, $result['err_code'] . ': ' . $result['err_code_des']);
        }
        
        return __json(1, $result['return_msg'],$result);
    }
    
    // 删除分账接收方
    public static function profitsharingremovereceiver($params){
        $payment = self::getSet($params['type'],$params['level']);
        
        $data = array(
            'mch_id' => $payment['mchid'],
            'appid' => $payment['appid'],
            'nonce_str'=>self::createNoncestr(), 
            'sign_type'=>'HMAC-SHA256',
            'receiver'=>json_encode($params['receiver'])
        );
        
        if($payment['service_provider_status'] == 1){
            $data['sub_appid'] = $data['appid'];
            $data['sub_mch_id'] = $data['mch_id'];
            $data['appid'] = $payment['service_provider_appid'];
            $data['mch_id'] = $payment['service_provider_mchid'];
            $payment['signkey'] = $payment['service_provider_paykey'];
            $payment['cert'] = $payment['service_provider_cert'];
            $payment['cert_key'] = $payment['service_provider_certkey'];
        }
        
        $data['sign'] = self::getSign($data,$payment['signkey'],1);
        // echo "<pre>";
        // print_r($data);die;
        $xmlData = self::arrayToXml($data);
        $url = "https://api.mch.weixin.qq.com/pay/profitsharingremovereceiver";
        $result = self::xmlToArray(self::postXmlCurl($xmlData, $url, 60));
        // file_put_contents("fz_profitsharingremovereceiver.txt",json_encode($result));
        if($result['return_code'] != 'SUCCESS') {
            return __json(-2, $result['return_msg']);
        }
        if($result['result_code'] != 'SUCCESS') {
            return __json(-3, $result['err_code'] . ': ' . $result['err_code_des']);
        }
        
        return __json(1, $result['return_msg']);
    }
    
    // 查询分账
    public static function profitsharingquery($params){

        $payment = self::getSet($params['type'],$params['level']);
        
        $data = array(
            'mch_id' => $payment['mchid'],
            // 'appid' => $payment['appid'],
            'nonce_str'=>self::createNoncestr(), 
            'sign_type'=>'HMAC-SHA256',
            'out_order_no'=>$params['order_trade_no'],
            'transaction_id'=>$params['transaction_id']
        );
        
        if($payment['service_provider_status'] == 1){
            // $data['sub_appid'] = $data['appid'];
            $data['sub_mch_id'] = $data['mch_id'];
            // $data['appid'] = $payment['service_provider_appid'];
            $data['mch_id'] = $payment['service_provider_mchid'];
            $payment['signkey'] = $payment['service_provider_paykey'];
            $payment['cert'] = $payment['service_provider_cert'];
            $payment['cert_key'] = $payment['service_provider_certkey'];
            
        }
        
        $data['sign'] = self::getSign($data,$payment['signkey'],1);
        // echo "<pre>";
        // print_r($data);die;
        
        $xmlData = self::arrayToXml($data);
        $url = "https://api.mch.weixin.qq.com/pay/profitsharingquery";
        
        $result = self::xmlToArray(self::postXmlCurl($xmlData, $url, 60));
        addFileLog("fz_profitsharingquery",json_encode($result));
        if($result['return_code'] != 'SUCCESS') {
            return __json(-2, $result['return_msg']);
        }
        if($result['result_code'] != 'SUCCESS') {
            return __json(-3, $result['err_code'] . ': ' . $result['err_code_des']);
        }
        
        return __json(1, $result['return_msg'],$result);
    }
    
    // 添加分账接收方
    public static function profitsharingaddreceiver($params){
        /*
        $params = array(
            "type"=>"wxapp",
            "receiver"=>array(
                "type"=>"PERSONAL_OPENID",
                "account"=>"openid",
                "relation_type"=>"USER"
            )
        );*/
        
        $payment = self::getSet($params['type'],$params['level']);
        
        $data = array(
            'mch_id' => $payment['mchid'],
            'appid' => $payment['appid'],
            'nonce_str'=>self::createNoncestr(), 
            'sign_type'=>'HMAC-SHA256',
            'receiver'=>json_encode($params['receiver'])
        );
        
        if($payment['service_provider_status'] == 1){
            $data['sub_appid'] = $data['appid'];
            $data['sub_mch_id'] = $data['mch_id'];
            $data['appid'] = $payment['service_provider_appid'];
            $data['mch_id'] = $payment['service_provider_mchid'];
            $payment['signkey'] = $payment['service_provider_paykey'];
            $payment['cert'] = $payment['service_provider_cert'];
            $payment['cert_key'] = $payment['service_provider_certkey'];
        }
        
        $data['sign'] = self::getSign($data,$payment['signkey'],1);
        // echo "<pre>";
        // print_r($data);die;
        $xmlData = self::arrayToXml($data);
        $url = "https://api.mch.weixin.qq.com/pay/profitsharingaddreceiver";
        $result = self::xmlToArray(self::postXmlCurl($xmlData, $url, 60));
        // file_put_contents("fz_profitsharingaddreceiver.txt",json_encode($result));
        if($result['return_code'] != 'SUCCESS') {
            return __json(-2, $result['return_msg']);
        }
        if($result['result_code'] != 'SUCCESS') {
            return __json(-3, $result['err_code'] . ': ' . $result['err_code_des']);
        }
        
        return __json(1, $result['return_msg']);
    }
    
    public static function pay($params){
        if(empty($params['type'])){
            $params['type'] = "wxapp";
        }
        $payment = self::getSet($params['type'],$params['level']);
        
        if($params['type'] == "wxapp" || $params['type'] == "GZH"){
            $params['trade_type'] = 'JSAPI';
        }
        else if($params['type'] == "H5" || $params['type'] == "APP"){
            $params['trade_type'] = 'MWEB';
        }
        // else if($params['type'] == "APP"){
        //     $params['trade_type'] = 'APP';
        // }
        
        $result = self::unifiedorder($params);
        
        if($result['return_code'] != 'SUCCESS') {
            return __json(-2, $result['return_msg']);
        }
        if($result['result_code'] != 'SUCCESS') {
            return __json(-3, $result['err_code'] . ': ' . $result['err_code_des']);
        }
        
        if($params['type'] == 'h5' || $params['type'] == 'H5' || $params['type'] == 'APP'){
            $data = ['mweb_url'=>$result['mweb_url']];
        }
        else{
            $timeStamp = time();
            
            $signkey = $payment['signkey'];
            if($payment['service_provider_status'] == 1){
                $signkey = $payment['service_provider_paykey'];
            }
            
            if($params['type'] == 'GZH'){
                $data = array(
                    'appId'=>$payment['appid'],
                    'timeStamp' => (string)$timeStamp, 
                    'nonceStr' => self::createNoncestr(), 
                    'package' => 'prepay_id=' . $result['prepay_id'],
                    'signType' => 'MD5'
                );
                $data['paySign'] = self::getSign($data,$signkey);
                $data['timestamp'] = $data['timeStamp'];
                unset($data['timeStamp']);
                unset($data['appId']);
            }
            else if($params['type'] == 'wxapp'){
                $data = array(
                    'appId' => $payment['appid'],
                    'timeStamp' => (string)$timeStamp, 
                    'nonceStr' => self::createNoncestr(), 
                    'package' => 'prepay_id=' . $result['prepay_id'],
                    'signType' => 'MD5'
                );
                $data['paySign'] = self::getSign($data,$signkey);
            }
            
            
            
        }
        
        addFileLog("pay222",json_encode($data));
        addFileLog("pay333",json_encode($payment));
        
        return __json(1,"统一下单成功",$data);
    }
    
    //统一下单接口
    private static function unifiedorder($params) {
        $payment = self::$payment;
        // file_put_contents("pay222.txt",json_encode($payment));
        $payroute_id = intval($payment['payroute_id']);
        $url = 'https://api.mch.weixin.qq.com/pay/unifiedorder';
        $package = array(
            'appid' => $payment['appid'],
            'attach'=>$params['type'],
            'mch_id' => $payment['mchid'],
            'nonce_str' => self::createNoncestr(), 
            'body' => $params['title'],
            'out_trade_no'=> $params['order_trade_no'],
            'total_fee' => intval($params['fee'] * 100),
            'spbill_create_ip' => self::get_client_ip(),
            'notify_url' => request()->domain()."/api/pay/notify/i/".UNIACID,
            // 'openid' => $params['openid'],
            'profit_sharing'=>$params['profit_sharing'],
            'trade_type' => $params['trade_type'],
        );

        if(strlen($package['body']) > 127){
            $str = mb_str_split($package['body']);
            $body = "";
            foreach($str as $k => $v){
                if(strlen($body.$v) > 127){
                    break;
                }
                $body .= $v;
            }
            $package['body'] = $body;
        }

        
        
        $signkey = $payment['signkey'];
        if($payment['service_provider_status'] == 1){
            $package['appid'] = $payment['service_provider_appid'];
            $package['mch_id'] = $payment['service_provider_mchid'];
            $package['sub_appid'] = $payment['appid'];
            $package['sub_mch_id'] = $payment['mchid'];
            $signkey = $payment['service_provider_paykey'];
            if($params['type'] == 'wxapp' || $params['type'] == 'GZH'){
                $package['sub_openid'] = $params['openid'];
            }
        }
        else{
            if($params['type'] == 'wxapp' || $params['type'] == 'GZH'){
                $package['openid'] = $params['openid'];
            }
        }
        
        if($params['type'] == 'H5' || $params['type'] == 'APP'){
            $package['scene_info'] = '{"h5_info": {"type":"Wap","wap_url": "'.request()->domain().'/mobile'.'","wap_name": "'.$payment['platform_name'].'"}}';
        }
        
        //统一下单签名
        $package['sign'] = self::getSign($package,$signkey);
        
        // echo "<pre>";
        // print_r($package);die;
        addFileLog("pay555",json_encode($package));
        $xmlData = self::arrayToXml($package);
        
        $result = self::xmlToArray(self::postXmlCurl($xmlData, $url, 60));
        if($result['return_code'] != 'SUCCESS' || $result['result_code'] != 'SUCCESS') {
            addFileLog("pay_err1",json_encode($package));
            addFileLog("pay_err2",json_encode($result));
        }
        else{
            $payroute_level = -1;
            if($payroute_id > 0){
                $payroute_level = Db::name("bbfx_payroute_pay")->where(['id'=>$payroute_id])->value("level");
            }
            Db::name("bbfx_pay_order")->where(['order_trade_no'=>$package['out_trade_no']])->update(['mchid'=>$package['mch_id'],'payroute_id'=>$payroute_id,'payroute_level'=>$payroute_level]);
        }
        // echo "<pre>";
        // print_r($package);die;

        
        
        return $result;
    }
    
    /**
     * 支付通知验证
     */
    public function payVerify(){
        
        
        $input = file_get_contents('php://input');
        libxml_disable_entity_loader(true);
        
        if (!empty($input)) {
        	$obj = simplexml_load_string($input, 'SimpleXMLElement', LIBXML_NOCDATA);
        	$data = json_decode(json_encode($obj), true);
        	if (empty($data)) {
        		echo self::arrayToXml(['return_code' => 'FAIL', 'return_msg' => '数据解析失败']);
        		exit();
        	}
        
        	if ($data['result_code'] != 'SUCCESS' || $data['return_code'] != 'SUCCESS') {
        		echo self::arrayToXml(['return_code' => 'FAIL', 'return_msg' => empty($data['return_msg']) ? $data['err_code_des'] : $data['return_msg']]);
        		exit();
        	}

            $pay_order = Db::name("bbfx_pay_order")->where(['uniacid'=>UNIACID,'order_trade_no'=>$data['out_trade_no']])->find();
            $payroute_level = -1;
            if(!empty($pay_order)){
                $payroute_level = $pay_order['payroute_level'];
            }
            
            $payment = self::getSet($data['attach'],$payroute_level);
            
            $sign1 = $data['sign'];
            unset($data['sign']);
            
            $signkey = $payment['signkey'];
            if($payment['service_provider_status'] == 1){
                $signkey = $payment['service_provider_paykey'];
            }
            
            $sign2 = self::getSign($data,$signkey);
            if($sign1 === $sign2){
                return $data;
            }
            else{
                echo self::arrayToXml(['return_code'=>'FAIL','return_msg'=>'支付通知验证失败']);
        		exit();
            }
        }
        
        $result = array('return_code' => 'FAIL', 'return_msg' => '数据不存在');
		echo self::arrayToXml($result);
		exit();
    }

    // 查询订单
    public static function orderquery($params){
        $pay_order = Db::name("bbfx_pay_order")->where(['uniacid'=>UNIACID,'order_trade_no'=>$params['order_trade_no']])->find();
        $payroute_level = -1;
        if(!empty($pay_order)){
            $payroute_level = $pay_order['payroute_level'];
        }

        $payment = self::getSet($params['platform'],$payroute_level);
        $package = array(
            "appid"=>$payment['appid'],
            "mch_id"=>$payment['mchid'],
            "nonce_str"=>self::createNoncestr(),
            "out_trade_no"=>$params['order_trade_no'],
        ); 
        
        if(empty($package['appid'])){
            return __json(0, '缺少appid参数');
        }
        if(empty($package['mch_id'])){
            return __json(0, '缺少商户号参数');
        }
        if(empty($package['out_trade_no'])){
            return __json(0, '缺少商城订单编号参数');
        }
        
        $signkey = $payment['signkey'];
        if($payment['service_provider_status'] == 1){
            $signkey = $payment['service_provider_paykey'];
            $package['appid'] = $payment['service_provider_appid'];
            $package['mch_id'] = $payment['service_provider_mchid'];
            $package['sub_appid'] = $payment['appid'];
            $package['sub_mch_id'] = $payment['mchid'];
        }
        
        $package['sign'] = self::getSign($package,$signkey);
        
        $xmlData = self::arrayToXml($package);
        $url = "https://api.mch.weixin.qq.com/pay/orderquery";
        $result = self::xmlToArray(self::postXmlCurl($xmlData, $url, 60));
        
        $result = json_decode(json_encode($result),true);
        if($result['return_code'] != 'SUCCESS') {
            return __json(-2, $result['return_msg']);
        }
        if($result['result_code'] != 'SUCCESS') {
            return __json(-3, $result['err_code'] . ': ' . $result['err_code_des']);
        }
        
        return __json(1,"查询成功",$result);
    }
    
    // 微信退款
    public static function refund($params){

        $pay_order = Db::name("bbfx_pay_order")->where(['uniacid'=>UNIACID,'order_trade_no'=>$params['order_trade_no']])->find();
        $payroute_level = -1;
        if(!empty($pay_order)){
            $payroute_level = $pay_order['payroute_level'];
        }
        
        $payment = self::getSet($params['platform'],$payroute_level);
        
        $package = array(
            "appid"=>$payment['appid'],
            "mch_id"=>$payment['mchid'],
            "nonce_str"=>self::createNoncestr(),
            "out_trade_no"=>$params['order_trade_no'],
            "out_refund_no"=>$params['out_refund_no'],
            "total_fee"=>round($params['total_fee']*100),
            "refund_fee"=>round($params['refund_fee']*100),
        ); 
        
        if(empty($package['appid'])){
            return __json(0, '缺少appid参数');
        }
        if(empty($package['mch_id'])){
            return __json(0, '缺少商户号参数');
        }
        if(empty($package['out_trade_no'])){
            return __json(0, '缺少商城订单编号参数');
        }
        if(empty($package['out_refund_no'])){
            return __json(0, '缺少退款订单编号参数');
        }
        if(empty($package['total_fee'])){
            return __json(0, '缺少订单金额参数');
        }
        if(empty($package['out_refund_no'])){
            return __json(0, '缺少退款金额参数');
        }
        
        
        $extras = array(
            "CURLOPT_SSLCERT"=>$payment['cert'],
            "CURLOPT_SSLKEY"=>$payment['cert_key']
        );
        
        $signkey = $payment['signkey'];
        if($payment['service_provider_status'] == 1){
            $signkey = $payment['service_provider_paykey'];
            $package['appid'] = $payment['service_provider_appid'];
            $package['mch_id'] = $payment['service_provider_mchid'];
            $package['sub_appid'] = $payment['appid'];
            $package['sub_mch_id'] = $payment['mchid'];
            $extras = array(
                "CURLOPT_SSLCERT"=>$payment['service_provider_cert'],
                "CURLOPT_SSLKEY"=>$payment['service_provider_certkey']
            );
        }
        
        if(!file_exists($extras['CURLOPT_SSLCERT']) || !strstr($extras['CURLOPT_SSLCERT'], '.pem')){
            return __json(0, '缺少支付证书文件');
        }
        if(!file_exists($extras['CURLOPT_SSLKEY']) || !strstr($extras['CURLOPT_SSLKEY'], '.pem')){
            return __json(0, '缺少支付证书秘钥文件');
        }
        
        $package['sign'] = self::getSign($package,$signkey);

        addFileLog("r111",json_encode($package));
        
        $xmlData = self::arrayToXml($package);
        $url = "https://api.mch.weixin.qq.com/secapi/pay/refund";
        $result = self::xmlToArray(self::postXmlCurl($xmlData, $url, 60,$extras));
        
        $result = json_decode(json_encode($result),true);
        if($result['return_code'] != 'SUCCESS') {
            return __json(-2, $result['return_msg']);
        }
        if($result['result_code'] != 'SUCCESS') {
            return __json(-3, $result['err_code'] . ': ' . $result['err_code_des']);
        }
        
        return __json(1,"退款成功");
    }

    
    /**
     * @notes 企业付款到零钱
     * @params $openid openid
     */
    public function mmpaymkttransfers($openid = '', $money = 0,$partner_trade_no,$desc = '企业付款'){
        
        $payment = self::getSet();
        
        if (empty($openid)) {
			return __json(0, 'openid不能为空');
		}
		
		
		$cert = $payment['cert'];
		$cert_key = $payment['cert_key'];
		if(empty($cert) || empty($cert_key)){
		    return __json(0, '请配置支付证书');
		}
		
		
		$package = array(
		    'mch_appid' => $payment['appid'], 
		    'mchid' => $payment['mchid'], 
		    'nonce_str' => self::createNoncestr(), 
		    'partner_trade_no' => $partner_trade_no, 
		    'openid' => $openid, 
		    'check_name' => 'NO_CHECK', 
		    'amount' => round($money*100), //付款金额，单位为分 
		    'desc' => $desc, //付款描述 
		    'spbill_create_ip' => self::get_client_ip()
		);
		
		$extras = array(
            "CURLOPT_SSLCERT"=>$payment['cert'],
            "CURLOPT_SSLKEY"=>$payment['cert_key']
        );
        
        $package['sign'] = self::getSign($package,$payment['signkey']);
        $xmlData = self::arrayToXml($package);
        $url = "https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers";
        $result = self::xmlToArray(self::postXmlCurl($xmlData, $url, 60,$extras));
        
        
        if($result['return_code'] != 'SUCCESS') {
            return self::result(-2, $result['return_msg']);
        }
        if($result['result_code'] != 'SUCCESS') {
            return self::result(-3, $result['err_code'] . ': ' . $result['err_code_des']);
        }
        
        
        addFileLog("111333",json_encode($result));
 
        return self::result(1, "打款成功",$result);
		
    }
    
    /**
     * @notes 商家转账到零钱
     * @params $openid openid
     */
    public static function transfer($openid = '', $money = 0,$out_batch_no, $trade_no = '', $user_name = '', $desc = '',$platform='wxapp'){
        
        $payment = self::getSet($platform);
        
        if (empty($openid)) {
			return __json(0, 'openid不能为空');
		}
		
		$cert = $payment['cert'];
		$cert_key = $payment['cert_key'];
		if(empty($cert) || empty($cert_key)){
		    return __json(0, '请配置支付证书');
		}
		
		$payment = array(
		    "sub_appid"=>$payment['appid'],
		    "sub_mch_id"=>$payment['mchid'],
		    "apikey"=>$payment['signkey'],
		    "publickey"=>$payment['publickey'],
		    "cert_path"=>$cert,
		    "key_path"=>$cert_key
		);
		
		if(empty($payment['sub_appid'])){
		    return __json(0, 'appid不能为空');
		}
		if(empty($payment['sub_mch_id'])){
		    return __json(0, '商户号不能为空');
		}
		if(empty($payment['apikey'])){
		    return __json(0, '支付秘钥不能为空');
		}
        
        // $out_batch_no = date("YmdHis").random(6,true);
        //请求URL
        $url = 'https://api.mch.weixin.qq.com/v3/transfer/batches';
        //请求方式
        $http_method = 'POST';
        //请求参数
        $data = [
            'appid' => $payment['sub_appid'],//申请商户号的appid或商户号绑定的appid（企业号corpid即为此appid）
            'out_batch_no' => $out_batch_no,//商户系统内部的商家批次单号，要求此参数只能由数字、大小写字母组成，在商户系统内部唯一
            'batch_name' => $desc,//该笔批量转账的名称
            'batch_remark' => $desc,//转账说明，UTF8编码，最多允许32个字符
            'total_amount' => round($money*100),//转账金额单位为“分”。转账总金额必须与批次内所有明细转账金额之和保持一致，否则无法发起转账操作
            'total_num' => 1,//一个转账批次单最多发起三千笔转账。转账总笔数必须与批次内所有明细之和保持一致，否则无法发起转账操作
            'transfer_detail_list' => [
                [//发起批量转账的明细列表，最多三千笔
                    'out_detail_no' => $trade_no,//商户系统内部区分转账批次单下不同转账明细单的唯一标识，要求此参数只能由数字、大小写字母组成
                    'transfer_amount' => round($money*100),//转账金额单位为分
                    'transfer_remark' => $desc,//单条转账备注（微信用户会收到该备注），UTF8编码，最多允许32个字符
                    'openid' => $openid,//openid是微信用户在公众号appid下的唯一用户标识（appid不同，则获取到的openid就不同），可用于永久标记一个用户
                ]]
        ];
        if ($money >= 2000) {
            if(empty($user_name)){
                return __json(0, '金额 >= 2000元时，收款用户真实姓名必填');
            }
            
            $data['transfer_detail_list'][0]['user_name'] = self::getEncrypt($user_name,$payment);
            
        }
        $token  = self::token($url,$http_method,$data,$payment);//获取token
        // file_put_contents("11133366.txt",json_encode($data));
        // return __json(1, 'appid不能为空');
        $result = self::https_request($url,json_encode($data),$token);//发送请求
        $result = json_decode($result,true);
 
        if(!isset($result['create_time'])) {//批次受理失败
            return __json(-2, $result['message']);
        }
        
        //批次受理成功，进行操作
        // file_put_contents("111333.txt",json_encode($result));
 
        return __json(1, "打款成功",$result);
    }
    
    /**
     * @notes 商家转账到零钱V3(新版本)
     * @params $openid openid
     */
    public static function newTransfer($openid = '', $money = 0,$out_bill_no, $user_name = '', $transfer_remark = '收益提现',$platform='wxapp'){
        
        

        $payment = self::getSet($platform);
        
        if (empty($openid)) {
			return __json(0, 'openid不能为空');
		}

        if($money < 0.1){
            return __json(0, '转账到微信零钱金额不能小于0.1元');
        }
        
		$publickeyId = $payment['publickeyId'];
		$cert = $payment['cert'];
		$cert_key = $payment['cert_key'];
		if(empty($cert) || empty($cert_key)){
		    return __json(0, '请配置支付证书');
		}
		
		$payment = array(
		    "sub_appid"=>$payment['appid'],
		    "sub_mch_id"=>$payment['mchid'],
		    "apikey"=>$payment['signkey'],
		    "publickey"=>$payment['publickey'],
		    "cert_path"=>$cert,
		    "key_path"=>$cert_key
		);
		
		if(empty($payment['sub_appid'])){
		    return __json(0, 'appid不能为空');
		}
		if(empty($payment['sub_mch_id'])){
		    return __json(0, '商户号不能为空');
		}
		if(empty($payment['apikey'])){
		    return __json(0, '支付秘钥不能为空');
		}

        $merch_transfer_scene = Db::name("bbfx_base")->where(['uniacid'=>UNIACID])->value("merch_transfer_scene");
        if(empty($merch_transfer_scene)){
            $merch_transfer_scene = '1005';
        }
        
        // $out_batch_no = date("YmdHis").random(6,true);
        //请求URL
        $url = 'https://api.mch.weixin.qq.com/v3/fund-app/mch-transfer/transfer-bills';
        //请求方式
        $http_method = 'POST';
        //请求参数
        $data = [
            'appid' => $payment['sub_appid'],// 小程序APPID或公众号APPID
            'out_bill_no' => $out_bill_no,//商户单号
            'transfer_scene_id'=>$merch_transfer_scene,// 转账场景ID,可前往“商户平台-产品中心-商家转账”中申请
            'openid'=>$openid,// 收款用户OpenID
            // 'user_name'=>'',//收款用户姓名,转账金额 >= 2,000元时，该笔明细必须填写
            'transfer_amount'=>round($money*100),// 转账金额单位为“分”。
            'transfer_remark'=>$transfer_remark,// 转账备注
            // 'notify_url'=>'',// 通知地址
            // 'user_recv_perception'=>'劳务报酬',//用户收款感知，固定值，根据转账场景ID填写
            'transfer_scene_report_infos'=>[
                [
                    'info_type'=>'岗位类型',// 信息类型
                    'info_content'=>'代理商'//信息内容
                ],
                [
                    'info_type'=>'报酬说明',// 信息类型
                    'info_content'=>'代理商收益提现'//信息内容
                ]
            ]// 转账场景报备信息,根据转账场景ID填写，1005：固定数据，岗位类型，报酬说明
        ];

        if($merch_transfer_scene =='1005'){
            // 佣金报酬
            $data['transfer_scene_report_infos'] = [
                [
                    'info_type'=>'岗位类型',// 固定值
                    'info_content'=>'代理商'//信息内容
                ],
                [
                    'info_type'=>'报酬说明',// 固定值
                    'info_content'=>'代理商收益提现'//信息内容
                ]
            ];
        }
        else if($merch_transfer_scene =='1000'){
            // 现金营销
            $data['transfer_scene_report_infos'] = [
                [
                    'info_type'=>'活动名称',// 固定值
                    'info_content'=>'分享有奖励'//信息内容
                ],
                [
                    'info_type'=>'奖励说明',// 固定值
                    'info_content'=>'用户分享奖励'//信息内容
                ]
            ];
        }
        else if($merch_transfer_scene =='1002'){
            // 行政补贴
            $data['transfer_scene_report_infos'] = [
                [
                    'info_type'=>'补贴类型',// 固定值
                    'info_content'=>'佣金补贴'//信息内容
                ]
            ];
        }
        
        if ($money >= 2000) {
            if(empty($user_name)){
                return __json(0, '金额 >= 2000元时，收款用户真实姓名必填');
            }
            
            $data['user_name'] = self::getEncrypt($user_name,$payment);
            
        }
        else{
            if(!empty($user_name)){
                // $data['user_name'] = self::getEncrypt($user_name,$payment);
            }
        }

        addFileLog("tt3",json_encode($data));
        // return __json(0, '调试中');

        $token  = self::token($url,$http_method,$data,$payment);//获取token
        
        // file_put_contents("tt4.txt",json_encode($payment));
        // return __json(0, 'appid不能为空');

        $header = [];
        if(!empty($publickeyId)){
            $header = ['Wechatpay-Serial: '.$publickeyId];
        }

        $result = self::https_request($url,json_encode($data),$token,$header);//发送请求
        $result = json_decode($result,true);
 
        if(!isset($result['create_time'])) {//批次受理失败
            addFileLog("newTransfer_err_data",json_encode($data));
            addFileLog("newTransfer_err",json_encode($result));
            return __json(-2, $result['message']);
        }
        // 提示：此IP地址不允许调用接口，请按开发指引设置
        // 查询本机外网IPv4地址：curl 4.aifuwu.net
        // 查询本机外网IPv6地址：curl 6.aifuwu.net
        // 检测网络是IPv4还是IPv6访问优先：curl ip.aifuwu.net
        // IPv6访问优先则需要填IPv6地址
       
        addFileLog("newTransfer",json_encode($result));
 
        return __json(1, "创建单据成功",$result);
    }
    
    // 查询商家转账
    public static function queryNewTransfer($out_bill_no,$platform){
        $url = "https://api.mch.weixin.qq.com/v3/fund-app/mch-transfer/transfer-bills/out-bill-no/{$out_bill_no}";
        $payment = self::getSet($platform);
		
		$cert = $payment['cert'];
		$cert_key = $payment['cert_key'];
		if(empty($cert) || empty($cert_key)){
		    return __json(0, '请配置支付证书');
		}
		
		$payment = array(
		    "sub_appid"=>$payment['appid'],
		    "sub_mch_id"=>$payment['mchid'],
		    "apikey"=>$payment['signkey'],
		    "cert_path"=>$cert,
		    "key_path"=>$cert_key
		);
		
		if(empty($payment['sub_appid'])){
		    return __json(0, 'appid不能为空');
		}
		if(empty($payment['sub_mch_id'])){
		    return __json(0, '商户号不能为空');
		}
		if(empty($payment['apikey'])){
		    return __json(0, '支付秘钥不能为空');
		}
		
		$token  = self::token($url,'GET','',$payment);//获取token
        $result = self::https_request($url,'',$token);//发送请求
        
        $result = json_decode($result,true);
 
        if(!isset($result['create_time'])) {
            return __json(-2, $result['message']);
        }
       
        addFileLog("tt2",json_encode($result));
 
        return __json(1, "查询单据成功",$result);
    }
    
    public static function get_client_ip() {
        $ip = $_SERVER['REMOTE_ADDR'];
        if (isset($_SERVER['HTTP_CLIENT_IP']) && preg_match('/^([0-9]{1,3}\.){3}[0-9]{1,3}$/', $_SERVER['HTTP_CLIENT_IP'])) {
            $ip = $_SERVER['HTTP_CLIENT_IP'];
        } elseif(isset($_SERVER['HTTP_X_FORWARDED_FOR']) AND preg_match_all('#\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}#s', $_SERVER['HTTP_X_FORWARDED_FOR'], $matches)) {
            foreach ($matches[0] AS $xip) {
                if (!preg_match('#^(10|172\.16|192\.168)\.#', $xip)) {
                    $ip = $xip;
                    break;
                }
            }
        }
        return $ip;
    }
 
    /**
     * @notes 获取签名
     */
    public static function token($url,$http_method,$data,$config)
    {
        $timestamp   = time();//请求时间戳
        $url_parts   = parse_url($url);//获取请求的绝对URL
        $nonce       = $timestamp.rand('10000','99999');//请求随机串
        $body        = empty($data) ? '' : json_encode((object)$data);//请求报文主体
        $stream_opts = [
            "ssl" => [
                "verify_peer"=>false,
                "verify_peer_name"=>false,
            ]
        ];
 
        $apiclient_cert_arr = openssl_x509_parse(file_get_contents($config['cert_path'],false, stream_context_create($stream_opts)));
        $serial_no          = $apiclient_cert_arr['serialNumberHex'];//证书序列号
        // $serial_no = "";
        // print_r($serial_no);die;
        $mch_private_key    = file_get_contents($config['key_path'],false, stream_context_create($stream_opts));//密钥
        $merchant_id = $config['sub_mch_id'];//商户id
        $canonical_url = ($url_parts['path'] . (!empty($url_parts['query']) ? "?${url_parts['query']}" : ""));
        $message = $http_method."\n".
            $canonical_url."\n".
            $timestamp."\n".
            $nonce."\n".
            $body."\n";
        openssl_sign($message, $raw_sign, $mch_private_key, 'sha256WithRSAEncryption');
        $sign = base64_encode($raw_sign);//签名
        $schema = 'WECHATPAY2-SHA256-RSA2048';
        $token = sprintf('mchid="%s",nonce_str="%s",timestamp="%d",serial_no="%s",signature="%s"',
            $merchant_id, $nonce, $timestamp, $serial_no, $sign);//微信返回token
        // print_r($serial_no);
        // echo "<pre>";
        // print_r($token);die;
        return $schema.' '.$token;
    }
  
    /**
     * @notes 发送请求
     */
    public static function https_request($url,$data,$token,$headers1=[])
    {
        $curl = curl_init();
        curl_setopt($curl, CURLOPT_URL, (string)$url);
        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);
        curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE);
        if (!empty($data)){
            curl_setopt($curl, CURLOPT_POST, 1);
            curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
        }
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
        //添加请求头
        $headers = [
            'Authorization:'.$token,
            'Accept: application/json',
            'Content-Type: application/json; charset=utf-8',
            'User-Agent:Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36',
        ];

        if(!empty($headers1)){
            $headers  = array_merge($headers,$headers1);
        }

        if(!empty($headers)){
            curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
        }
        $output = curl_exec($curl);
        curl_close($curl);
        
        return $output;
    }
 
    /**
     * @notes 敏感信息加解密
     */
    public static function getEncrypt($str,$config)
    {
        //$str是待加密字符串
        $public_key = file_get_contents($config['publickey']);
        $encrypted = '';
        if (openssl_public_encrypt($str, $encrypted, $public_key, OPENSSL_PKCS1_OAEP_PADDING)) {
            //base64编码
            $sign = base64_encode($encrypted);
        } else {
            throw new \Exception('敏感信息加密失败，请检查微信支付公钥！');
        }
        return $sign;
    }
    
    public static function createNoncestr($length = 32) {
        $chars = "abcdefghijklmnopqrstuvwxyz0123456789";
        $str = "";
        for ($i = 0; $i < $length; $i++) {
            $str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
        }
        return $str;
    }
    
    public static function getSign($Obj,$signkey) {
        foreach ($Obj as $k => $v) {
            $Parameters[$k] = $v;
        }
        //签名步骤一：按字典序排序参数
        ksort($Parameters);
        $String = self::formatBizQueryParaMap($Parameters, false);
        //签名步骤二：在string后加入KEY
        $String = $String . "&key=" . $signkey;
        //签名步骤三：MD5加密
        
        if($Obj['sign_type'] == 'HMAC-SHA256'){
            $String = hash_hmac('sha256', $String, $signkey);
        }
        else{
            $String = md5($String);
        }
        
        //签名步骤四：所有字符转为大写
        $result_ = strtoupper($String);
        return $result_;
    }


    ///作用：格式化参数，签名过程需要使用
    public static function formatBizQueryParaMap($paraMap, $urlencode) {
        $buff = "";
        ksort($paraMap);
        foreach ($paraMap as $k => $v) {
            if ($urlencode) {
                $v = urlencode($v);
            }
            $buff .= $k . "=" . $v . "&";
        }
        $reqPar = '';
        if (strlen($buff) > 0) {
            $reqPar = substr($buff, 0, strlen($buff) - 1);
        }
        return $reqPar;
    }
    
    public static function postXmlCurl($xml, $url, $second = 30,$extra = array()) 
    {
        
        $ch = curl_init();
        //设置超时
        curl_setopt($ch, CURLOPT_TIMEOUT, $second);
        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);
        
        
        if (!empty($extra) && is_array($extra)) {
    		$headers = array();
    		foreach ($extra as $opt => $value) {
    			if (strexists($opt, 'CURLOPT_')) {
    				curl_setopt($ch, constant($opt), $value);
    			} elseif (is_numeric($opt)) {
    				curl_setopt($ch, $opt, $value);
    			} else {
    				$headers[] = "{$opt}: {$value}";
    			}
    		}
    		if (!empty($headers)) {
    			curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
    		}
    	}


        curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 20);
        curl_setopt($ch, CURLOPT_TIMEOUT, 40);
        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 \Exception("curl出错，错误码:$error");
            return false;
        }
    }
    
    
    
    //数组转换成xml
    public static function arrayToXml($arr) {
        $xml = "<root>";
        foreach ($arr as $key => $val) {
            if (is_array($val)) {
                $xml .= "<" . $key . ">" . self::arrayToXml($val) . "</" . $key . ">";
            } else {
                $xml .= "<" . $key . ">" . $val . "</" . $key . ">";
            }
        }
        $xml .= "</root>";
        return $xml;
    }


    //xml转换成数组
    public static function xmlToArray($xml) {


        //禁止引用外部xml实体 


        libxml_disable_entity_loader(true);


        $xmlstring = simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA);


        $val = json_decode(json_encode($xmlstring), true);


        return $val;
    }
}