findOrFail($id); // 验证记录状态 abort_if($record->audit_state !== WithdrawAuditStatus::PENDING->value, 400, '提现申请状态异常'); // 获取用户钱包 $wallet = $record->wallet; abort_if(!$wallet, 404, '钱包信息不存在'); // 验证冻结金额 abort_if($wallet->frozen_amount < $record->amount, 400, '钱包冻结金额异常'); if ($status === WithdrawAuditStatus::APPROVED->value) { // 审核通过,发起自动打款 $this->processApproved($record, $remark); } else { // 审核拒绝,解冻余额 $this->processRejected($record, $remark); } // 记录审核信息 $record->auditor = auth()->user()->name; $record->audit_time = now(); $record->audit_remark = $remark; $record->audit_state = $status; $record->save(); // 记录日志 Log::info('提现申请审核完成', [ 'withdraw_id' => $record->id, 'external_no' => $record->external_no, 'status' => $status, 'remark' => $remark, 'auditor' => $record->auditor ]); }); } /** * 处理审核通过 */ private function processApproved(WalletWithdrawRecord $record, ?string $remark): void { try { // 1. 更新提现记录状态为处理中 $record->state = WithdrawStatus::PROCESSING->value; $record->save(); // 获取钱包当前余额 $wallet = $record->wallet; $currentBalance = $wallet->available_balance; $currentRechargeBalance = $wallet->recharge_amount; // 2. 创建转账记录 $transRecord = WalletTransRecord::create([ 'trans_no' => $this->generateTransNo(), 'wallet_id' => $record->wallet_id, 'owner_id' => $record->id, 'owner_type' => 'withdraw', // 业务类型:提现 'trans_type' => 2, // 交易类型:支出 'amount' => $record->amount, 'before_balance' => $currentBalance, 'after_balance' => $currentBalance - $record->amount, 'before_recharge_balance' => $currentRechargeBalance, 'after_recharge_balance' => $currentRechargeBalance, 'trans_time' => now(), 'remark' => '提现打款', 'state' => TransactionStatus::PROCESSING->value, 'province' => $record->area_code ? substr($record->area_code, 0, 2) . '0000' : null, 'city' => $record->area_code ? substr($record->area_code, 0, 4) . '00' : null, 'district' => $record->area_code ]); // 3. 调用支付服务发起转账 // TODO: 对接实际的支付服务 $paymentResult = $this->processPayment($record); if ($paymentResult['success']) { // 4. 打款成功 // 4.1 更新提现记录状态 $record->state = WithdrawStatus::SUCCESS->value; $record->withdraw_time = now(); $record->trans_no = $paymentResult['trade_no']; $record->remark = '打款成功'; $record->save(); // 4.2 更新转账记录状态 $transRecord->state = TransactionStatus::SUCCESS->value; $transRecord->trans_no = $paymentResult['trade_no']; $transRecord->save(); // 4.3 扣除钱包冻结金额 $record->wallet->decrement('frozen_amount', $record->amount); // 4.4 扣除钱包总金额 $record->wallet->decrement('total_balance', $record->amount); } else { // 5. 打款失败 throw new \Exception($paymentResult['message'] ?? '打款失败'); } } catch (\Exception $e) { // 6. 处理异常 Log::error('提现打款失败', [ 'withdraw_id' => $record->id, 'error' => $e->getMessage() ]); // 6.1 更新提现记录状态 $record->state = WithdrawStatus::FAILED->value; $record->remark = $e->getMessage(); $record->save(); // 6.2 更新转账记录状态 if (isset($transRecord)) { $transRecord->state = TransactionStatus::FAILED->value; $transRecord->save(); } // 6.3 解冻余额 $this->unfreezeBalance($record); throw $e; } } /** * 处理审核拒绝 */ private function processRejected(WalletWithdrawRecord $record, ?string $remark): void { // 1. 更新提现记录状态 $record->state = WithdrawStatus::FAILED->value; $record->save(); // 2. 解冻余额 $this->unfreezeBalance($record); // 3. 记录日志 Log::info('提现申请已拒绝', [ 'withdraw_id' => $record->id, 'external_no' => $record->external_no, 'amount' => $record->amount, 'remark' => $remark ]); } /** * 解冻余额 */ private function unfreezeBalance(WalletWithdrawRecord $record): void { $wallet = $record->wallet; // 获取当前余额 $currentBalance = $wallet->available_balance; $currentRechargeBalance = $wallet->recharge_amount; // 从冻结金额中减少 $wallet->decrement('frozen_amount', $record->amount); // 增加可用余额 $wallet->increment('available_balance', $record->amount); // 记录解冻操作 WalletTransRecord::create([ 'trans_no' => $this->generateTransNo(), 'wallet_id' => $wallet->id, 'owner_id' => $record->id, 'owner_type' => 'withdraw', 'trans_type' => 1, // 交易类型:收入 'amount' => $record->amount, 'before_balance' => $currentBalance, 'after_balance' => $currentBalance + $record->amount, 'before_recharge_balance' => $currentRechargeBalance, 'after_recharge_balance' => $currentRechargeBalance, 'trans_time' => now(), 'remark' => '提现拒绝解冻', 'state' => TransactionStatus::SUCCESS->value ]); } /** * 生成交易流水号 */ private function generateTransNo(): string { return 'T' . date('YmdHis') . str_pad(random_int(1, 9999), 4, '0', STR_PAD_LEFT); } /** * 处理实际打款 * * @param WalletWithdrawRecord $record 提现记录 * @return array [ * 'success' => bool 是否成功 * 'trade_no' => string 交易流水号 * 'message' => string 结果信息 * ] * @throws \Exception */ private function processPayment(WalletWithdrawRecord $record): array { try { Log::info('开始处理提现打款', [ 'withdraw_id' => $record->id, 'withdraw_type' => $record->withdraw_type, 'amount' => $record->amount, 'account' => $record->withdraw_account ]); // 根据提现方式调用不同的转账接口 if ($record->withdraw_type === WithdrawMethod::WECHAT->value) { Log::info('使用微信转账方式', [ 'withdraw_id' => $record->id, 'config' => config('wechat.pay.default') ]); // 使用 EasyWeChat 支付实例 $config = config('wechat.pay.default'); $app = new \EasyWeChat\Pay\Application($config); // 生成商户订单号 $outTradeNo = 'W' . date('YmdHis') . str_pad($record->id, 6, '0', STR_PAD_LEFT); $requestData = [ 'partner_trade_no' => $outTradeNo, 'openid' => $record->withdraw_account, 'check_name' => 'NO_CHECK', // 不校验真实姓名 'amount' => (int)bcmul($record->amount, '100'), // 金额单位为分 'desc' => '提现到零钱', ]; Log::info('微信转账请求参数', [ 'withdraw_id' => $record->id, 'request_data' => $requestData ]); try { // 使用商家转账接口 $response = $app->getClient()->postJson('v3/fund-app/mch-transfer/transfer-bills', [ 'appid' => $config['app_id'], 'out_bill_no' => $outTradeNo, 'transfer_scene_id' => '1001', // 现金营销场景 'openid' => $record->withdraw_account, // 'user_name' => $record->withdraw_account_name, 'transfer_amount' => (int)bcmul($record->amount, '100'), 'transfer_remark' => '提现到零钱', 'transfer_scene_report_infos' => [ [ 'info_type' => '活动名称', 'info_content' => '用户提现' ], [ 'info_type' => '奖励说明', 'info_content' => '余额提现' ] ] ]); $result = $response->toArray(); Log::info('微信转账响应结果', [ 'withdraw_id' => $record->id, 'result' => $result ]); // 检查转账状态 if (isset($result['bill_id'])) { Log::info('微信转账成功', [ 'withdraw_id' => $record->id, 'bill_id' => $result['bill_id'], 'out_bill_no' => $outTradeNo ]); return [ 'success' => true, 'trade_no' => $result['bill_id'], 'message' => '微信转账成功' ]; } throw new \Exception($result['err_code_des'] ?? '微信转账失败'); } catch (\Exception $e) { Log::error('微信转账请求异常', [ 'withdraw_id' => $record->id, 'error' => $e->getMessage(), 'trace' => $e->getTraceAsString() ]); throw $e; } } elseif ($record->withdraw_type === WithdrawMethod::ALIPAY->value) { Log::info('使用支付宝转账方式', [ 'withdraw_id' => $record->id, 'config' => [ 'app_id' => config('alipay.app_id'), // 不记录敏感信息 ] ]); // 使用支付宝 SDK 调用转账接口 $config = [ 'app_id' => config('alipay.app_id'), 'private_key' => config('alipay.private_key'), 'alipay_public_key' => config('alipay.alipay_public_key'), ]; // 初始化支付宝 SDK \Alipay\EasySDK\Kernel\Factory::setOptions($config); $requestData = [ 'out_biz_no' => $record->external_no, 'trans_amount' => $record->amount, 'product_code' => 'TRANS_ACCOUNT_NO_PWD', 'biz_scene' => 'DIRECT_TRANSFER', 'payee_info' => [ 'identity' => $record->withdraw_account, 'identity_type' => 'ALIPAY_LOGON_ID', 'name' => $record->withdraw_account_name, ], 'remark' => '用户提现', ]; Log::info('支付宝转账请求参数', [ 'withdraw_id' => $record->id, 'request_data' => $requestData ]); $result = \Alipay\EasySDK\Kernel\Factory::payment()->common() ->transfer($requestData); Log::info('支付宝转账响应结果', [ 'withdraw_id' => $record->id, 'result' => json_encode($result) ]); if ($result->code === '10000') { Log::info('支付宝转账成功', [ 'withdraw_id' => $record->id, 'trade_no' => $result->order_id ]); return [ 'success' => true, 'trade_no' => $result->order_id, 'message' => '支付宝转账成功' ]; } throw new \Exception($result->sub_msg ?? '支付宝转账失败'); } elseif ($record->withdraw_type === WithdrawMethod::BANK->value) { Log::info('使用银行卡转账方式(模拟)', [ 'withdraw_id' => $record->id ]); // 银行卡转账暂时使用模拟逻辑 $tradeNo = 'BANK' . time() . random_int(1000, 9999); Log::info('模拟银行卡打款成功', [ 'withdraw_id' => $record->id, 'trade_no' => $tradeNo ]); return [ 'success' => true, 'trade_no' => $tradeNo, 'message' => '模拟银行卡打款成功' ]; } else { Log::error('不支持的提现方式', [ 'withdraw_id' => $record->id, 'withdraw_type' => $record->withdraw_type ]); throw new \Exception('不支持的提现方式'); } } catch (\Exception $e) { Log::error('转账处理失败', [ 'withdraw_id' => $record->id, 'withdraw_type' => $record->withdraw_type, 'error_message' => $e->getMessage(), 'error_trace' => $e->getTraceAsString() ]); return [ 'success' => false, 'trade_no' => '', 'message' => $e->getMessage() ]; } } }