// +---------------------------------------------------------------------- declare (strict_types=1); namespace app\store\controller\user; use app\common\library\helper; use app\common\library\wechat\WxPay; use app\common\model\user\UserIdcards; use app\store\controller\Controller; use app\store\model\User as UserModel; use app\store\model\user\Withdraw as WithdrawModel; use think\cache\driver\Redis; use app\store\model\Wxapp as WxappModel; use app\api\model\User as UserApiModel; use app\common\service\Export as ExportService; use app\common\enum\order\orderGoods\ExportStatus as ExportStatusEnum; /** * 用户提现 * Class Withdraw * @package app\store\controller\user */ class Withdraw extends Controller { public function list() { $params = $this->request->param(); $model = new WithdrawModel; $list = $model->getList($params); return $this->renderSuccess(compact('list')); } /** * 提现列表导出功能 * @return array * @author: zq * @Time: 2022/2/22 10:43 */ public function orderExport(){ $param = $this->request->param(); /* if(isset($param['id']) && empty($param['id'])){ return $this->renderError('请勾选订单商品后再导出'); }*/ $model = new WithdrawModel(); $data = $model->listExport($param); $res = ExportService::export($data['data'],$data['header'],$data['filename'],'列表','Xls'); return $this->renderSuccess($res,'导出成功'); } public function dataCount() { $arr = []; $arr['all_num'] = WithdrawModel::count(); $arr['audit_0'] = WithdrawModel::where("audit", 0)->count(); $arr['audit_1'] = WithdrawModel::where("audit", 1)->count(); $arr['audit_2'] = $arr['all_num'] - $arr['audit_0'] - $arr['audit_1']; $arr['pay_state_0'] = WithdrawModel::where("pay_state", 0)->count(); $arr['pay_state_1'] = WithdrawModel::where("pay_state", 1)->count(); $arr['pay_state_2'] = WithdrawModel::where("pay_state", 2)->count(); $arr['pay_state_3'] = WithdrawModel::where("pay_state", 3)->count(); return $this->renderSuccess(compact('arr')); } public function detail($id = 0) { $detail = WithdrawModel::with(['user'])->where('id', $id)->find(); if (empty($detail)) { return $this->renderError('参数无效'); } $detail->user->promote_user_count = UserApiModel::countSubordinates($detail->user_id, $detail->shop_id, $detail->role); // 直推人数 $detail->user->promote_user_name = UserModel::where('user_id',$detail->user->upper_user_id)->value('nick_name') ?? ''; $detail->user->idcard = UserIdcards::where('user_id',$detail->user_id)->field('real_name')->find() ?? ''; return $this->renderSuccess(compact('detail')); } //审核 public function audit(int $id) { $params = $this->postForm(); $detail = WithdrawModel::with(['user'])->where('id', $id)->find(); if (empty($detail)) { return $this->renderError('参数无效'); } $user = $detail->user; if ($detail->audit == 0 && $params['audit'] != $detail->audit) { $detail->audit = $params['audit']; $detail->audit_remark = $params['audit_remark'] ?? ''; $detail->audit_time = date("Y-m-d H:i:s"); $detail->save(); if ($detail->audit == 2) { $user->ktxyj_amount += $detail->amount;//转账失败返回提现金额给用户 $user->frozen_yj_amount -= $detail->amount;//转账失败从冻结金额中扣除单笔提现金额 $user->save(); } // if ($audit == 2) { //// $user->wjsyj_amount = $user->wjsyj_amount - $detail->amount; //// $user->save(); // $title = '审核不通过'; // $content = '非常抱歉,您的提现申请审核不通过.原因:' . $audit_remark; // } else { // $title = '审核通过'; // $content = '恭喜您,您的提现申请已通过,工作人员将尽快为您打款,请留意支付宝信息。'; // } // $ids = $detail->user_id; // $url = config('chef.serv_cookhome') . '/api/shop_pushes?title=' . $title . '&content=' . $content . '&ids=' . $ids . '&url=' . urlencode('zd://chefPush?target=shop_push_my_withdrawal&id=' . $id); // helper::curlRequest($url); return $this->renderSuccess('审核成功'); } else { return $this->renderError('已经审核过了'); } } //去支付 public function topay($id) { $params = $this->postForm(); $detail = WithdrawModel::with(['user'])->where('id', $id)->find(); $user = $detail->user; if ($detail->pay_state == 1) { return $this->renderError("已支付,请勿重复付款"); } if ($detail->pay_state == 3) { return $this->renderError("打款中,请勿重复付款"); } if ($detail->audit != 1) { return $this->renderError("未审核通过,不能付款"); } $pay_amount = helper::bcsub($detail->amount, $detail->tax, 2); if ($detail['transaction_type'] == 1) { // 支付宝提现 $ret = $this->aliWithdrawal($detail, $pay_amount); $pay_type = 1; } elseif ($detail['transaction_type'] == 2) { // 微信提现 $params['remark'] = $params['remark'] ?: '佣金提现'; // 默认打款备注信息 $ret = $this->wxWithdrawal($detail, $pay_amount, $params['remark']); $pay_type = 2; } else { return $this->renderError('提现方式不正确'); } $storeInfo = $this->store; if ($ret['code'] == 1) { $detail->remark = $params['remark']; $detail->pay_state = 1; $detail->pay_type = $pay_type; $detail->pay_time = date("Y-m-d H:i:s"); $detail->pay_transaction_no = $ret['pay_transaction_no']; //微信或支付宝支付流水号,暂时只记录了微信的流水号,支付宝流水号没保存20220413 $detail->third_trans_no = $ret['payment_no']??''; $detail->admin_id = $storeInfo['user']['store_user_id']; $detail->pay_amount = $pay_amount; $user->frozen_yj_amount -= $detail->amount;//提现成功从冻结金额中扣除单笔提现金额 $user->have_withdrew_money += $detail->amount; $user->save(); $detail->save(); return $this->renderSuccess('付款成功'); } else { $reason = $ret['reason']; $detail->not_pass_resaon = $reason; $detail->pay_state = $pay_type; $detail->admin_id = $storeInfo['user']['store_user_id']; $user->ktxyj_amount += $detail->amount;//转账失败返回提现金额给用户 $user->frozen_yj_amount -= $detail->amount;//转账失败从冻结金额中扣除单笔提现金额 $user->save(); $detail->save(); return $this->renderError($reason ?? '付款失败'); } } /** * 支付宝提现 * @param object $detail 提现申请信息 * @param string $pay_amount 付款金额 * @return array */ private function aliWithdrawal($detail, $pay_amount) { $redis = new Redis(config('cache.stores.redis')); $redis->set('fx_pay_' . $detail->user_id, 1, 600); $url = config('chef.serv_cookhome') . '/api/shop/pay_ali_account?account=' . $detail->tx_account . '&amount=' . $pay_amount . '&real_name=' . $detail->real_name . "&user_id=" . $detail->user_id; $res = helper::curlRequest($url); $arr = json_decode($res, true); if ($arr['ret'] == 1) { return ['code' => 1, 'msg' => '付款成功', 'pay_transaction_no' => $arr['order_no']]; } else { return ['code' => 0, 'msg' => '付款失败', 'reason' => $arr['reason']]; } } /** * 小程序提现 * todo 定时任务轮询打款中状态的提现订单, * 调用微信查询订单接口,直到返回打款成功, * 才将状态改为已打款,并更新用户账号资金。 * * @param object $detail 提现申请信息 * @param string $pay_amount 付款金额 * @return array */ private function wxWithdrawal($detail, $pay_amount, $remark = '') { // 验证限制 $this->checkPayLimit($detail->user_id, $pay_amount); $orderNo = WithdrawModel::makePayTransactionNo(); $user = $detail->user; $wxPay = new WxPay(static::getWxConfig()); $res = $wxPay->transfers($orderNo, $user->open_id, $pay_amount, $remark); // if ($res === true) { if (isset($res['status']) && $res['status'] === true) { //todo 先注释掉测试一下提现20220412,感觉像之前遗留的多余的一行代码 //$wxPay->transfers($orderNo, $user->open_id, $pay_amount, $remark); return ['code' => 1, 'msg' => '付款成功', 'pay_transaction_no' => $orderNo,'payment_no'=>$res['payment_no']]; } else { // 判断各种失败状态 // 当返回错误码为“SYSTEMERROR”时,请不要更换商户订单号,一定要使用原商户订单号重试,否则可能造成重复支付等资金风险。 if ($res['data']['err_code'] == 'SYSTEMERROR') { // 将当前提现单状态改为打款中,然后后台操作人可以重复进行打款操作,直到成功为止。 $detail->pay_state = 3; // 3-打款中 $detail->admin_id = $this->store['user']['store_user_id']; $detail->pay_transaction_no = $orderNo; $detail->save(); return $this->renderError('付款失败,请稍后重试'); } return ['code' => 0, 'msg' => $res['msg'] ?? '付款失败', 'reason' => $res['msg']]; } } /** * 尝试重试打款 * 用同一个商户订单号向微信发起打款请求 * 当打款成功时 */ public function repay($id) { $params = $this->postForm(); $detail = WithdrawModel::with(['user'])->where('id', $id)->find(); $user = $detail->user; if ($detail->pay_state != 3) { return $this->renderError("非打款中状态,请问重复付款"); } if ($detail->audit != 1) { return $this->renderError("未审核通过,不能付款"); } $pay_amount = helper::bcsub($detail->amount, $detail->tax, 2); if ($detail['transaction_type'] == 1) { // 支付宝提现 $pay_type = 1; } elseif ($detail['transaction_type'] == 2) { // 微信提现 $ret = $this->wxRepay($detail, $pay_amount, $params['remark']); $pay_type = 2; } else { return $this->renderError('提现方式不正确'); } $storeInfo = $this->store; if ($ret['code'] == 1) { $detail->remark = $params['remark']; $detail->pay_state = 1; $detail->pay_type = $pay_type; $detail->pay_time = date("Y-m-d H:i:s"); $detail->pay_transaction_no = $ret['pay_transaction_no']; $detail->admin_id = $storeInfo['user']['store_user_id']; $detail->pay_amount = $pay_amount; $user->frozen_yj_amount -= $detail->amount;//提现成功从冻结金额中扣除单笔提现金额 $user->have_withdrew_money += $detail->amount; $user->save(); $detail->save(); return $this->renderSuccess('付款成功'); } else { return $this->renderError($reason ?? '付款失败'); } } /** * 微信重试打款 * todo 请勿付款和查询并发处理 */ private function wxRepay($detail, $pay_amount, $remark = '') { $orderNo = $detail->pay_transaction_no; if (empty($orderNo)) { return $this->renderError('提现单异常'); } // 验证限制 $this->checkPayLimit($detail->user_id, $pay_amount); // 查询付款状态 $wxPay = new WxPay(static::getWxConfig()); $queryRes = $wxPay->getTransferInfo($orderNo); if ($queryRes === true) { // 查询结果为付款成功 则更新状态 return ['code' => 1, 'msg' => '付款成功', 'pay_transaction_no' => $orderNo]; } $user = $detail->user; $res = $wxPay->transfers($orderNo, $user->open_id, $pay_amount, $remark); //if ($res === true) { if (isset($res['status']) && $res['status'] === true) { //todo 先注释掉测试一下提现20220412,感觉像之前遗留的多余的一行代码 //$wxPay->transfers($orderNo, $user->open_id, $pay_amount, $remark); return ['code' => 1, 'msg' => '付款成功', 'pay_transaction_no' => $orderNo,'payment_no'=>$res['payment_no']]; } else { // 判断各种失败状态 // 当返回错误码为“SYSTEMERROR”时,请不要更换商户订单号,一定要使用原商户订单号重试,否则可能造成重复支付等资金风险。 if ($res['data']['err_code'] == 'SYSTEMERROR') { return $this->renderError('付款失败,请稍后重试'); } return ['code' => 0, 'msg' => $res['msg'] ?? '付款失败', 'reason' => $res['msg']]; } } /** * 检查用户提现限制 */ private function checkPayLimit($userId, $pay_amount) { // 判断用户是否当日提现次数超过10次,大于10次提示“您今日提现过于频繁,请次日再来~” $dayCount = WithdrawModel::where('user_id', $userId) ->whereTime('pay_time', 'today') ->count(); if ($dayCount > 10) { return $this->renderError('您今日提现过于频繁,请次日再来~'); } // 判断用户是否当日提现金额超过5000元,当日累计申请提现(含本次)大于5000元时提示“您今日累计提现数值较大,请次日再来~” $dayAmount = WithdrawModel::where('user_id', $userId) ->whereTime('pay_time', 'today') ->sum('pay_amount'); if ($dayAmount + $pay_amount > 5000) { return $this->renderError('您今日累计提现数值较大,请次日再来'); } } /** * 获取微信支付配置 * @return array * @throws BaseException * @throws \think\db\exception\DataNotFoundException * @throws \think\db\exception\DbException * @throws \think\db\exception\ModelNotFoundException */ private static function getWxConfig() { return WxappModel::getWxappCache(getStoreId()); } }