From 48dbda53494ee5d630bf364be96952e5c37400f0 Mon Sep 17 00:00:00 2001 From: weigang Date: Wed, 9 Sep 2020 13:19:06 +0800 Subject: [PATCH] =?UTF-8?q?=E5=95=86=E6=88=B7=E6=8F=90=E7=8E=B0=E7=94=B3?= =?UTF-8?q?=E8=AF=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Constants/v3/ErrorCode.php | 16 +++ app/Constants/v3/LogLabel.php | 10 ++ app/Controller/v3/WithdrawController.php | 108 +++++++++++++++++- .../v3/Implementations/PaymentService.php | 48 ++++++++ .../v3/Interfaces/PaymentServiceInterface.php | 1 + config/autoload/wechat.php | 7 +- config/routes.php | 1 + 7 files changed, 187 insertions(+), 4 deletions(-) diff --git a/app/Constants/v3/ErrorCode.php b/app/Constants/v3/ErrorCode.php index 7ed6387..9cfcb43 100644 --- a/app/Constants/v3/ErrorCode.php +++ b/app/Constants/v3/ErrorCode.php @@ -251,4 +251,20 @@ class ErrorCode extends AbstractConstants * @Message("优惠券使用失败") */ const COUPON_USE_FAILURE = 1202; + + /************************************/ + /* 提现相关 1251-1300 */ + /************************************/ + + /** + * 提现失败 + * @Message("提现失败") + */ + const STORE_WITHDRAW_FAIL = 1251; + + /** + * 可提现余额不足 + * @Message("可提现余额不足") + */ + const STORE_WITHDRAW_INSUFFICIENT_BALANCE = 1252; } \ No newline at end of file diff --git a/app/Constants/v3/LogLabel.php b/app/Constants/v3/LogLabel.php index c83fde0..4b0b65e 100644 --- a/app/Constants/v3/LogLabel.php +++ b/app/Constants/v3/LogLabel.php @@ -82,4 +82,14 @@ class LogLabel extends AbstractConstants */ const COUPON_REFUND_LOG = 'coupon_refund_log'; + /** + * @Message("提现失败") + */ + const STORE_WITHDRAW_FAIL_LOG = 'store_withdraw_fail_log'; + + /** + * @Message("付款到微信钱包失败") + */ + const PAY_TO_WX_FAIL_LOG = 'pay_to_wx_fail_log'; + } diff --git a/app/Controller/v3/WithdrawController.php b/app/Controller/v3/WithdrawController.php index f185438..4ccae31 100644 --- a/app/Controller/v3/WithdrawController.php +++ b/app/Controller/v3/WithdrawController.php @@ -2,26 +2,46 @@ namespace App\Controller\v3; +use App\Commons\Log; use App\Constants\v3\ErrorCode; +use App\Constants\v3\LogLabel; use App\Constants\v3\UserType; use App\Controller\BaseController; use App\Exception\ErrorCodeException; use App\Model\v3\FinancialRecord; use App\Model\v3\Store; +use App\Model\v3\StoreWithdrawal; +use App\Model\v3\User; use App\Model\v3\UserBalance; use App\Service\v3\Interfaces\FinancialRecordServiceInterface; +use App\Service\v3\Interfaces\PaymentServiceInterface; +use Hyperf\DbConnection\Db; use Hyperf\Di\Annotation\Inject; +use Hyperf\Snowflake\IdGeneratorInterface; +use Hyperf\Utils\ApplicationContext; use Hyperf\Validation\ValidationException; class WithdrawController extends BaseController { + /** + * @Inject + * @var Log + */ + protected $log; + /** * @Inject * @var FinancialRecordServiceInterface */ protected $financialService; + /** + * @Inject + * @var PaymentServiceInterface + */ + protected $paymentService; + /** * 商户提现页数据 * 1、可提现金额 @@ -44,8 +64,8 @@ class WithdrawController extends BaseController throw new ValidationException($validator); } - $userId = $this->request->input('user_id', 0); - $storeId = $this->request->input('store_id', 0); + $userId = $this->request->input('user_id'); + $storeId = $this->request->input('store_id'); $store = Store::query()->where(['user_id' => $userId, 'id' => $storeId])->first(); if (empty($store)) { throw new ErrorCodeException(ErrorCode::STORE_NOT_AVAILABLE); @@ -77,13 +97,95 @@ class WithdrawController extends BaseController return $this->success($data); } + /** + * 商户提现申请 + * 1、商户提现参数校验 + * 2、商户提现是否超过可提现金额 + * 3、提现是否秒到账,秒到账则打款并且做流水,否则就打到后台审核 + */ public function applyByStore() { + + $isDirect = config('wechat.withdrawal.is_direct'); + $min = config('wechat.withdrawal.min_amount'); + $max = config('wechat.withdrawal.max_amount'); $validator = $this->validationFactory->make( $this->request->all(), [ - 'user_id' => 'required|nonempty' + 'user_id' => 'required|nonempty|integer', + 'store_id' => 'required|nonempty|integer', + 'money' => "required|nonempty|numeric|between:{$min}, {$max}", ] ); + + if ($validator->fails()) { + throw new ValidationException($validator); + } + + $money = $this->request->input('money'); + $userId = $this->request->input('user_id'); + $storeId = $this->request->input('store_id'); + $store = Store::query()->where(['user_id' => $userId, 'id' => $storeId])->first(); + if (empty($store)) { + throw new ErrorCodeException(ErrorCode::STORE_NOT_AVAILABLE); + } + + // 校验余额 + $balance = UserBalance::query() + ->where(['source_id' => $store->user_id, 'user_type' => UserType::STORE]) + ->first(); + if ($money > $balance->balance) { + throw new ErrorCodeException(ErrorCode::STORE_WITHDRAW_INSUFFICIENT_BALANCE); + } + + Db::beginTransaction(); + try { + + $storeUser = User::query()->find($store->user_id); + $currentTime = time(); + + // 获取分布式全局ID + $generator = ApplicationContext::getContainer()->get(IdGeneratorInterface::class); + $globalOrderId = $generator->generate(); + + // 记录提现申请记录 + $withdraw = new StoreWithdrawal(); + $withdraw->trade_no = $globalOrderId; + $withdraw->store_id = $store->id; + $withdraw->name = $storeUser->real_name ?? $storeUser->nick_name; + $withdraw->tel = $storeUser->tel; + $withdraw->apply_cash = $money; + $withdraw->save(); + + // 扣除余额 + $balance->balance = bcsub($balance->balance, $money, 2); + $balance->save(); + + // 如果秒到账无需审核的话 + if ($isDirect) { + // 打款 + $res = $this->paymentService->payToWx( + bcmul($withdraw->apply_cash, 100 , 0), + $withdraw->trade_no, + $storeUser->openid, + $withdraw->name, + '商户提现打款' + ); + // 更新打款金额,审核时间等 + } + + Db::commit(); + + return $this->success([]); + } catch (\Exception $e) { + Db::rollBack(); + $this->log->event(LogLabel::STORE_WITHDRAW_FAIL_LOG, [ + 'exception' => $e->getMessage(), + 'withdraw' => json_encode($withdraw), + 'params' => json_encode(['balance' => $balance->balance, 'user_id' => $userId, 'store_id' => $storeId]), + ]); + throw new ErrorCodeException(ErrorCode::STORE_WITHDRAW_FAIL, $e->getMessage()); + } + } } \ No newline at end of file diff --git a/app/Service/v3/Implementations/PaymentService.php b/app/Service/v3/Implementations/PaymentService.php index 86e0f60..b1cfcab 100644 --- a/app/Service/v3/Implementations/PaymentService.php +++ b/app/Service/v3/Implementations/PaymentService.php @@ -12,6 +12,7 @@ use App\Model\v3\User; use App\Service\v3\Interfaces\GoodsActivityServiceInterface; use App\Service\v3\Interfaces\PaymentServiceInterface; use EasyWeChat\Factory; +use GuzzleHttp\Exception\GuzzleException; use Hyperf\Guzzle\CoroutineHandler; use Hyperf\Di\Annotation\Inject; @@ -97,6 +98,53 @@ class PaymentService implements PaymentServiceInterface // TODO: Implement undo() method. } + /** + * 企业付款到微信钱包 + * @param $money + * @param $tradeNo + * @param $openId + * @param $userName + * @param string $desc + * @param string $checkName + * @throws GuzzleException + */ + public function payToWx($money, $tradeNo, $openId, $userName, $desc = '', $checkName = 'NO_CHECK') + { + $config = config('wxpay'); + $app = Factory::payment($config); + $app['guzzle_handler'] = CoroutineHandler::class; + + $result = $app->transfer->toBalance([ + 'partner_trade_no' => $tradeNo, // 商户订单号,需保持唯一性(只能是字母或者数字,不能包含有符号) + 'openid' => $openId, + 'check_name' => $checkName, // NO_CHECK:不校验真实姓名, FORCE_CHECK:强校验真实姓名 + 're_user_name' => $userName, // 如果 check_name 设置为FORCE_CHECK,则必填用户真实姓名 + 'amount' => $money, // 企业付款金额,单位为分 + 'desc' => $desc, // 企业付款操作说明信息 + ]); + + if ($result['return_code'] != 'SUCCESS') { + $this->log->event(LogLabel::PAY_TO_WX_FAIL_LOG, [ + 'exception_payToWx' => $result['return_msg'], + 'result' => json_encode($result), + 'desc' => $desc + ]); + throw new ErrorCodeException(ErrorCode::PAYMENT_FAIL, $result['return_msg']); + } + + if ($result['result_code'] != 'SUCCESS') { + $this->log->event(LogLabel::PAY_TO_WX_FAIL_LOG, [ + 'exception_payToWx' => $result['err_code_des'], + 'result' => json_encode($result), + 'desc' => $desc + ]); + throw new ErrorCodeException(ErrorCode::PAYMENT_FAIL, $result['err_code_des']); + } + + return true; + + } + /** * 支付参数加签 * @param $parameters diff --git a/app/Service/v3/Interfaces/PaymentServiceInterface.php b/app/Service/v3/Interfaces/PaymentServiceInterface.php index 7370bac..426a8e0 100644 --- a/app/Service/v3/Interfaces/PaymentServiceInterface.php +++ b/app/Service/v3/Interfaces/PaymentServiceInterface.php @@ -7,4 +7,5 @@ interface PaymentServiceInterface public function do($globalOrderId, $money, $userId, $notifyUrl); public function check(); public function undo(); + public function payToWx($money, $tradeNo, $openId, $userName, $desc = '', $checkName = 'NO_CHECK'); } \ No newline at end of file diff --git a/config/autoload/wechat.php b/config/autoload/wechat.php index 9802ac9..653a5f4 100644 --- a/config/autoload/wechat.php +++ b/config/autoload/wechat.php @@ -14,5 +14,10 @@ return [ 'notify_url' => [ 'online' => env('SITE_HOST') . '/v3/wechat/notify/online', 'offline' => env('SITE_HOST') . '/v3/wechat/notify/offline', - ] + ], + 'withdrawal' => [ + 'is_direct' => env('WITHDRAW_IS_DIRECT'), + 'min_amount' => env('WITHDRAW_MIN_AMOUNT'), + 'max_amount' => env('WITHDRAW_MAX_AMOUNT'), + ], ]; \ No newline at end of file diff --git a/config/routes.php b/config/routes.php index 94e9764..1e7005a 100644 --- a/config/routes.php +++ b/config/routes.php @@ -138,6 +138,7 @@ Router::addGroup('/v3/', function () { Router::post('user/oflOrders', 'App\Controller\v3\OrderListController@offlineForUser'); Router::post('shopCart/delete', 'App\Controller\v3\ShopCartUpdateController@delete'); Router::post('withdraw/pageByStore', 'App\Controller\v3\WithdrawController@pageByStore'); + Router::post('withdraw/applyByStore', 'App\Controller\v3\WithdrawController@applyByStore'); },['middleware' => [\App\Middleware\Auth\ApiMiddleware::class, \App\Middleware\Auth\UserMiddleware::class]]); // 微信支付回调