WithdrawalLog.php 12.0 KB
<?php

namespace app\admin\controller;

use app\admin\model\User;
use app\common\controller\Backend;
use app\common\library\CalculatedAmount;
use EasyWeChat\Factory;
use think\Db;
use think\exception\PDOException;
use think\exception\ValidateException;

/**
 * 提现申请管理
 *
 * @icon fa fa-circle-o
 */
class WithdrawalLog extends Backend {

	/**
	 * WithdrawalLog模型对象
	 * @var \app\admin\model\WithdrawalLog
	 */
	protected $model = null;

	public function _initialize() {
		parent::_initialize();
		$this->model = new \app\admin\model\WithdrawalLog;
		$this->view->assign( "statusList", $this->model->getStatusList() );
	}



	/**
	 * 默认生成的控制器所继承的父类中有index/add/edit/del/multi五个基础方法、destroy/restore/recyclebin三个回收站方法
	 * 因此在当前控制器中可不用编写增删改查的代码,除非需要自己控制这部分逻辑
	 * 需要将application/admin/library/traits/Backend.php中对应的方法复制到当前控制器,然后进行修改
	 */


	/**
	 * 查看
	 */
	public function index() {
		//当前是否为关联查询
		$this->relationSearch = true;
		//设置过滤方法
		$this->request->filter( ['strip_tags', 'trim'] );
		if ( $this->request->isAjax() ) {
			//如果发送的来源是Selectpage,则转发到Selectpage
			if ( $this->request->request( 'keyField' ) ) {
				return $this->selectpage();
			}
			list( $where, $sort, $order, $offset, $limit ) = $this->buildparams();
			$user_id = $this->request->get( 'user_id' );
			$list    = $this->model
				->with( ['user'] )
				->where( $where )
				->where( function ( $query ) use ( $user_id ) {
					if ( !empty( $user_id ) ) {
						$query->where( 'user_id', $user_id );
					}
				} )
				->order( $sort, $order )
				->paginate( $limit );

			foreach ( $list as $row ) {

				$row->getRelation( 'user' )->visible( ['username', 'mobile', 'promotion_no', 'is_promoter'] );
			}

			$result = array("total" => $list->total(), "rows" => $list->items());

			return json( $result );
		}
		return $this->view->fetch();
	}


	/**
	 * 同意-废弃
	 *
	 * 从2022年5月18日,原“企业付款到零钱”已升级为“商家转账到零钱”,已开通商户的功能使用暂不受影响,新开通商户可前往「产品中心 -商家转账到零钱」
	 * 以下为"企业付款到零钱",更改为agree“商家转账到零钱”
	 * */
	public function agrees( $ids ) {
		//查询申请信息
		$apply = $this->model->where( 'id', $ids )->find();
		Db::startTrans();
		//为用户进行转账
		$config  = config( 'wechat.payment' );
		$app     = Factory::payment( $config );
		$open_id = \db( 'third' )->where( 'user_id', $apply['user_id'] )->value( 'openid' );
		$result  = $app->transfer->toBalance( [
			'partner_trade_no' => $apply['order_no'], // 商户订单号,需保持唯一性(只能是字母或者数字,不能包含有符号)
			'openid'           => $open_id,
			'check_name'       => 'NO_CHECK', // NO_CHECK:不校验真实姓名, FORCE_CHECK:强校验真实姓名
			're_user_name'     => $apply['username'], // 如果 check_name 设置为FORCE_CHECK,则必填用户真实姓名
			'amount'           => $apply['arrival_price'] * 100, // 企业付款金额,单位为分
			'desc'             => '佣金提现', // 企业付款操作说明信息。必填
		] );
		if ( $result['return_code'] == 'SUCCESS' && $result['result_code'] == 'SUCCESS' ) {
			$userModel = new User();
			//更新用户的提现金额信息
			CalculatedAmount::calculation( $apply['user_id'] );
			//更改申请状态
			$apply_update = $this->model->where( 'id', $ids )->update( [
				'status'       => 1,
				'examine_time' => time(), //审核(通过/驳回)时间
				'account_time' => time(), //到账时间
			] );
			if ( $apply_update ) {
				Db::commit();
				$this->success( '已通过申请', $result['payment_time'] );
			} else {
				Db::rollback();
				$this->error( $result['err_code_des'] );
			}
		} else {
			Db::rollback();
			$this->error( $result );
		}

	}

	/**
	 * 驳回
	 */
	public function reject( $ids = null ) {
		$row = $this->model->get( $ids );
		if ( !$row ) {
			$this->error( __( 'No Results were found' ) );
		}
		$adminIds = $this->getDataLimitAdminIds();
		if ( is_array( $adminIds ) ) {
			if ( !in_array( $row[$this->dataLimitField], $adminIds ) ) {
				$this->error( __( 'You have no permission' ) );
			}
		}
		if ( $this->request->isPost() ) {
			$params = $this->request->post( "row/a" );
			if ( $params ) {
				$params = $this->preExcludeFields( $params );
				$result = false;
				Db::startTrans();
				try {
					//是否采用模型验证
					if ( $this->modelValidate ) {
						$name     = str_replace( "\\model\\", "\\validate\\", get_class( $this->model ) );
						$validate = is_bool( $this->modelValidate ) ? ( $this->modelSceneValidate ? $name . '.edit' : $name ) : $this->modelValidate;
						$row->validateFailException( true )->validate( $validate );
					}
					$params['status'] = - 1;
//                    $params['reasons_rejection'] = $reasons_rejection;
					$params['examine_time'] = time();


					$result = $row->allowField( true )->save( $params );
					Db::commit();
				} catch ( ValidateException $e ) {
					Db::rollback();
					$this->error( $e->getMessage() );
				} catch ( PDOException $e ) {
					Db::rollback();
					$this->error( $e->getMessage() );
				} catch ( Exception $e ) {
					Db::rollback();
					$this->error( $e->getMessage() );
				}
				if ( $result !== false ) {
					$this->success();
				} else {
					$this->error( __( 'No rows were updated' ) );
				}
			}
			$this->error( __( 'Parameter %s can not be empty', '' ) );
		}
		$this->view->assign( "row", $row );
		return $this->view->fetch();
	}

	//向微信发起提现申请
	public function agree( $ids ) {
		//查询申请信息
		$info    = $this->model->where( 'id', $ids )->find();
		$open_id = \db( 'third' )->where( 'user_id', $info['user_id'] )->value( 'openid' );
		if ( empty( $info ) ) {
			return false;
		}
		$url                             = 'https://api.mch.weixin.qq.com/v3/transfer/batches';
		$batch_name                      = '用户提现';
		$pars                            = [];
		$pars['appid']                   = 'wx239e35047fe80913';//直连商户的appid
		$pars['out_batch_no']            = 'cxb' . date( 'Ymd' ) . mt_rand( 1000, 9999 );//商户系统内部的商家批次单号,要求此参数只能由数字、大小写字母组成,在商户系统内部唯一
		$pars['batch_name']              = $batch_name;//该笔批量转账的名称
		$pars['batch_remark']            = $batch_name;//转账说明,UTF8编码,最多允许32个字符
		$pars['total_amount']            = intval( strval( $info['arrival_price'] * 100 ) );//转账总金额 单位为“分”
		$pars['total_num']               = 1;//转账总笔数
		$pars['transfer_detail_list'][0] = [
			'out_detail_no'   => $info['order_no'],
			'transfer_amount' => intval( strval( $info['arrival_price'] * 100 ) ),
			'transfer_remark' => $batch_name,
			'openid'          => $open_id,
		];
		//转账明细列表
		$token  = $this->getToken( $pars );//获取token
		$res    = $this->https_request( $url, $token, json_encode( $pars ) );//发送请求
		$resArr = json_decode( $res, true );
		if ( isset( $resArr['batch_id'] ) ) {
			//更改申请状态
			$apply_update = $this->model->where( 'id', $ids )->update( [
				'status'       => 0,
				'batch_id'     => $resArr['batch_id'],
				'out_batch_no' => $resArr['out_batch_no'],
				//'examine_time' => time(), //审核(通过/驳回)时间
				//'account_time' => time(), //到账时间
			] );
			$this->success( '已发起申请');
		}else{
			$this->error( '发起提现申请失败!', $resArr['message'] );
		}
		die();
	}

	//提现明细查询,成功则更新状态
	public function check( $ids ) {
		//查询申请信息
		$info = $this->model->where( 'id', $ids )->find();
		if ( empty( $info ) ) {
			return false;
		}
		$url = 'https://api.mch.weixin.qq.com/v3/transfer/batches/out-batch-no/' . $info['out_batch_no'] . '/details/out-detail-no/' . $info['order_no'];
//		$url = 'https://api.mch.weixin.qq.com/v3/transfer/batches';

		$pars                  = [];
//		$pars['appid']         = 'wx239e35047fe80913';//直连商户的appid
//		$pars['out-batch-no']  = $info['order_no'];
//		$pars['out-detail-no'] = $info['out_batch_no'];

		//转账明细列表
		$token  = $this->getToken( $pars, $url, 'GET' );//获取token
		$res    = $this->https_request( $url, $token );//发送请求, json_encode( $pars )
		$resArr = json_decode( $res, true );
		//dump( $resArr );
		//根据返回的具体数据 判断更新申请状态
		if ( isset( $resArr['detail_status'] ) && $resArr['detail_status'] == 'SUCCESS') {
		//更新用户的提现金额信息
		CalculatedAmount::calculation( $info['user_id'] );
			//更改申请状态
			$apply_update = $this->model->where( 'id', $ids )->update( [
				'status'       => 1, //审核状态:1=审核通过,0=处理中,-1=已驳回,2=待发起提现,3=提现失败
				'examine_time' => time(), //审核(通过/驳回)时间
				'account_time' => time(), //到账时间
			] );
			$this->success( '已通过申请', $resArr['detail_status'] );
		}elseif( isset( $resArr['detail_status'] ) && $resArr['detail_status'] == 'PROCESSING'){
			$this->error( '正在处理中,转账结果尚未明确!');
		}elseif (isset( $resArr['detail_status'] ) && $resArr['detail_status'] == 'FAIL'){
			dump($res);
			$this->error( '处理转账失败',$resArr['fail_reason']);
		}elseif(isset( $resArr['code'] ) && $resArr['code'] == 'NOT_FOUND'){
			$this->error( '记录不存在,请确认手机已完成转账操作!');
		}
		die();
	}

	public function getToken( $pars, $url = 'https://api.mch.weixin.qq.com/v3/transfer/batches', $method = 'POST' ) {
//		$url         = 'https://api.mch.weixin.qq.com/v3/transfer/batches';
		$http_method = $method;//请求方法(GET,POST,PUT)
		$timestamp   = time();//请求时间戳
		$url_parts   = parse_url( $url );//获取请求的绝对URL
		$nonce       = $timestamp . rand( '10000', '99999' );//请求随机串
		$body        = $pars ? json_encode( (object) $pars ) : '';//请求报文主体
		$stream_opts = [
			"ssl" => [
				"verify_peer"      => false,
				"verify_peer_name" => false,
			]
		];
		//证书
		$apiclient_cert_path = ROOT_PATH.'/extend/cert/apiclient_cert.pem';
		$apiclient_key_path  = ROOT_PATH.'/extend/cert/apiclient_key.pem';

		$apiclient_cert_arr = openssl_x509_parse( file_get_contents( $apiclient_cert_path, false, stream_context_create( $stream_opts ) ) );
		$serial_no          = $apiclient_cert_arr['serialNumberHex'];//证书序列号
		$mch_private_key    = file_get_contents( $apiclient_key_path, false, stream_context_create( $stream_opts ) );//密钥

		$merchant_id   = '1624790770';//商户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
		return $token;
	}

	function https_request( $url, $token, $data = null ) {
		$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:WECHATPAY2-SHA256-RSA2048 ' . $token,
			'Accept: application/json',
			'Content-Type: application/json; charset=utf-8',
			'User-Agent:/',
		];
		if ( !empty( $headers ) ) {
			curl_setopt( $curl, CURLOPT_HTTPHEADER, $headers );
		}
		$output = curl_exec( $curl );
		curl_close( $curl );
		return $output;
	}
}