UserWalletApply.php 10.6 KB
<?php

namespace addons\groupon\model;

use think\Model;
use addons\groupon\exception\Exception;
use think\Db;

/**
 * 钱包
 */
class UserWalletApply extends Model
{

    // 表名,不含前缀
    protected $name = 'groupon_user_wallet_apply';
    // 自动写入时间戳字段
    protected $autoWriteTimestamp = 'int';
    // 定义时间戳字段名
    protected $createTime = 'createtime';
    protected $updateTime = 'updatetime';
    protected $deleteTime = false;

    protected $hidden = ['actual_money', 'log', 'payment_json', 'updatetime'];

    // 追加属性
    protected $append = [
        'status_text',
        'apply_type_text'
    ];


    /**
     * 获取提现单号
     *
     * @param int $user_id
     * @return string
     */
    public static function getSn($user_id)
    {
        $rand = $user_id < 9999 ? mt_rand(100000, 99999999) : mt_rand(100, 99999);
        $order_sn = date('Yhis') . $rand;

        $id = str_pad($user_id, (24 - strlen($order_sn)), '0', STR_PAD_BOTH);

        return 'W' . $order_sn . $id;
    }


    public static function getWithdrawConfig()
    {
        $config = \addons\groupon\model\Config::where('name', 'withdraw')->find();
        return json_decode($config['value'], true);
    }

    // 提现记录
    public static function getList() {
        $user = User::info();

        $walletApplys = self::where(['user_id' => $user->id])->order('id desc')->paginate(10);

        return $walletApplys;
    }


    // 申请提现
    public static function apply ($type, $money) {
        $user = User::info();
        $config = self::getWithdrawConfig();
        if (!in_array($type, $config['methods'])) {
            throw new Exception('暂不支持该提现方式');
        }

        $min = round(floatval($config['min']), 2);
        $max = round(floatval($config['max']), 2);
        $service_fee = round(floatval($config['service_fee']), 3);      // 三位小数

        // 检查最小提现金额
        if (bccomp($money, $min) === -1 || $money <= 0) {
            throw new \Exception('提现金额不能少于 ' . $min . '元');
        }

        // 检查最大提现金额
        if ($max && bccomp($money, $max) === 1 ) {
            throw new \Exception('提现金额不能大于 ' . $max . '元');
        }

        // 计算手续费
        $charge = bcmul($money, $service_fee, 2);
        if (bccomp($user->money, $charge + $money) === -1) {
            throw new \Exception('可提现余额不足');
        }

        // 检查提现账户信息
        $bank = \addons\groupon\model\UserBank::info($type, false);

        $apply = \think\Db::transaction(function () use ($user, $money, $charge, $service_fee, $type, $bank) {

            // 添加提现记录
            $platform = request()->header('platform');

            $apply = new self();
            $apply->apply_sn = self::getSn($user->id);
            $apply->user_id = $user->id;
            $apply->money = $money;
            $apply->charge_money = $charge;
            $apply->service_fee = $service_fee;
            $apply->apply_type = $type;
            $apply->platform = $platform;
            switch ($type) {
                case 'wechat':
                    $applyInfo = [
                        '微信用户' => $bank['real_name'],
                        '微信ID'  => $bank['card_no'],
                    ];
                    break;
                case 'bank':
                    $applyInfo = [
                        '真实姓名' => $bank['real_name'],
                        '开户行' => $bank['bank_name'],
                        '银行卡号' => $bank['card_no']
                    ];
                    break;
            }
            if (!isset($applyInfo)) {
                throw new Exception('您的提现信息有误');
            }
            $apply->apply_info = $applyInfo;

            $apply->status = 0;
            $apply->save();
            self::handleLog($apply, '用户发起提现申请');
            // 扣除用户余额
            User::money(- ($money + $charge), $user->id, 'cash', $apply->id);

            return $apply;
        });

        // 检查是否执行自动打款
        $autoCheck = false;
        if ($type !== 'bank' && $config['wechat_alipay_auto']) {
            $autoCheck = true;
        }

        if ($autoCheck) {
            try {
                $apply = self::handleAgree($apply);
                $apply = self::handleWithdraw($apply);
            } catch (\Exception $e) {
                // 拦截,统一返回自动打款失败
                throw new Exception('自动打款失败,请联系管理员');
            }
        }

        return $apply;
    }


    public static function handleLog($apply, $oper_info)
    {
        $log = $apply->log;
        $oper = \addons\groupon\library\Oper::set();
        $log[] = [
            'oper_type' => $oper['oper_type'],
            'oper_id' => $oper['oper_id'],
            'oper_info' => $oper_info,
            'oper_time' => time()
        ];
        $apply->log = $log;
        $apply->save();
        return $apply;
    }

    // 同意
    public static function handleAgree($apply)
    {
        if ($apply->status != 0) {
            throw new Exception('请勿重复操作');
        }
        $apply->status = 1;
        $apply->save();
        return self::handleLog($apply, '同意提现申请');
    }

    // 处理打款
    public static function handleWithdraw($apply)
    {
        $withDrawStatus = false;
        if ($apply->status != 1) {
            throw new Exception('请勿重复操作');
        }
        if ($apply->apply_type !== 'bank') {
            $withDrawStatus = self::handleTransfer($apply);
        } else {
            $withDrawStatus = true;
        }
        if ($withDrawStatus) {
            $apply->status = 2;
            $apply->actual_money = $apply->money;
            $apply->save();
            return self::handleLog($apply, '已打款');
        }
        return $apply;
    }

    // 拒绝
    public static function handleReject($apply, $rejectInfo)
    {
        if ($apply->status != 0 && $apply->status != 1) {
            throw new Exception('请勿重复操作');
        }
        $apply->status = -1;
        $apply->save();
        User::money($apply->money + $apply->charge_money, $apply->user_id, 'cash_error', $apply->id);
        return self::handleLog($apply, '拒绝:' . $rejectInfo);
    }

    // 企业付款提现
    private static function handleTransfer($apply)
    {
        $type = $apply->apply_type;
        $platform = $apply->platform;

        try {
            if ($type == 'wechat') {
                $payload = [
                    'partner_trade_no' => $apply->apply_sn,
                    'openid' => $apply->apply_info['微信ID'],
                    'check_name' => 'NO_CHECK',
                    'amount' => $apply->money,
                    'desc' => "用户[{$apply->apply_info['微信用户']}]提现"
                ];
            } elseif ($type == 'alipay') {
                $payload = [
                    'out_biz_no' => $apply->apply_sn,
                    'trans_amount' => $apply->money,
                    'product_code' => 'TRANS_ACCOUNT_NO_PWD',
                    'biz_scene' => 'DIRECT_TRANSFER',
                    // 'order_title' => '余额提现到',
                    'remark' => '用户提现',
                    'payee_info' => [
                        'identity' => $apply->apply_info['支付宝账户'],
                        'identity_type' => 'ALIPAY_LOGON_ID',
                        'name' => $apply->apply_info['真实姓名'],
                    ]
                ];
            }
        } catch (\Exception $e) {
            throw new Exception('提现信息不正确');
        }

        try {
            $pay_version = pay_version();

            if ($pay_version == 'v3') {
                $payService = new \addons\groupon\library\payv3\PayServiceV3($type, $platform);
                list($code, $response) = $payService->transfer($payload);
            } else {
                $pay = new \addons\groupon\library\PayService($type, $platform, '', 'transfer');
                list($code, $response) = $pay->transfer($payload);
            }

            if ($code === 1) {
                $apply->payment_json = json_encode($response, JSON_UNESCAPED_UNICODE);
                $apply->save();
                return true;
            } else {
                // 提现失败
                throw new \Exception(json_encode($response, JSON_UNESCAPED_UNICODE));
            }
        } catch (\Exception $e) {
            \think\Log::error('提现失败:' . ' 行号:' . $e->getLine() . '文件:' . $e->getFile() . '错误信息:' . $e->getMessage());
            throw new \Exception($e->getMessage());
        }
        return false;
    }


    /**
     * 提现类型列表
     */
    public function getApplyTypeList()
    {
        return ['bank' => '银行卡', 'wechat' => '微信零钱'];
    }



    /**
     * 提现类型中文
     */
    public function getApplyTypeTextAttr($value, $data)
    {
        $value = isset($data['apply_type']) ? $data['apply_type'] : '';
        $list = $this->getApplyTypeList();
        return isset($list[$value]) ? $list[$value] : '';
    }

    /**
     * 提现信息
     */
    public function getApplyInfoAttr($value, $data)
    {
        $value = isset($data['apply_info']) ? $data['apply_info'] : $value;
        return json_decode($value, true);
    }

    /**
     * 提现信息 格式转换
     */
    public function setApplyInfoAttr($value, $data)
    {
        $value = isset($data['apply_info']) ? $data['apply_info'] : $value;
        $applyInfo = json_encode($value, JSON_UNESCAPED_UNICODE);
        return $applyInfo;
    }

    public function getStatusTextAttr($value, $data)
    {
        switch ($data['status']) {
            case 0:
                $status_name = '审核中';
                break;
            case 1:
                $status_name = '处理中';
                break;
            case 2:
                $status_name = '已处理';
                break;
            case -1:
                $status_name = '已拒绝';
                break;
            default:
                $status_name = '';
        }

        return $status_name;
    }


    /**
     * 获取日志字段数组
     */
    public function getLogAttr($value, $data)
    {
        $value = array_filter((array)json_decode($value, true));
        return (array)$value;
    }

    /**
     * 设置日志字段
     * @param mixed $value
     * @return string
     */
    public function setLogAttr($value)
    {
        $value = is_object($value) || is_array($value) ? json_encode($value, JSON_UNESCAPED_UNICODE) : $value;
        return $value;
    }

}