$type, 'member_id' => $user->member_id, 'amount' => $amount, 'channel' => self::CHANNEL, 'order_no' => PaymentOrderService::createOrderNo('sim' . $type . '_', $user->member_id), 'remark' => '模拟充值', 'status' => $type === PaymentOrderService::TYPE_SELF_PAY ? PaymentOrderService::STATUS_AUDIT : PaymentOrderService::STATUS_PROCESS] + PaymentOrderService::feeFields($amount, 0)); if ($result === 'fail') { $order->status = PaymentOrderService::STATUS_FAIL; $order->remark = '模拟充值失败'; $order->save(); } elseif ($result === 'success') { if ($type === PaymentOrderService::TYPE_PAY) { if (!PaymentOrderService::simulatePaySuccess($order)) throw new Exception('模拟充值入账失败', HttpStatus::CUSTOM_ERROR); } else { self::approveCompanyDeposit((int) $order->id); } } return $order->refresh(); } /** 与后台“人工充值审核通过”相同的入账:订单置成功、加可用余额、写“充值”流水。 */ private static function approveCompanyDeposit(int $id): void { DB::transaction(function () use ($id) { $order = PaymentOrder::where('id', $id)->where('type', PaymentOrderService::TYPE_SELF_PAY)->lockForUpdate()->first(); if (!$order || (int) $order->status !== PaymentOrderService::STATUS_AUDIT) throw new Exception('订单状态不可审核', HttpStatus::CUSTOM_ERROR); $order->status = PaymentOrderService::STATUS_SUCCESS; $order->state = 1; $order->save(); $wallet = Wallet::where('member_id', $order->member_id)->lockForUpdate()->first(); if (!$wallet) throw new Exception('钱包不存在', HttpStatus::CUSTOM_ERROR); $before = (string) $wallet->available_balance; $after = bcadd($before, (string) $order->amount, 10); $wallet->available_balance = $after; $wallet->save(); $log = BalanceLogService::addLog($order->member_id, $order->amount, $before, $after, '充值', $order->id, '模拟充值'); \App\Services\RechargeBonusService::grant('payment_order', $order, $log); }); } /** 人工提现申请:校验打码量、冻结资金、建待审核提现单。 */ public static function withdrawApply($memberId, $amount): PaymentOrder { SimulationSettings::assertEnabled(); $user = SimulationSettings::member($memberId); $amount = SimulationSettings::assertAmount($amount); if (SimulationSettings::get('simulation_wager_bypass') === '1' && WageringService::enabled()) { $wallet = Wallet::where('member_id', $user->member_id)->first(); if ($wallet) WageringService::resetCycle($wallet, 'simulation', null, '模拟提现跳过打码量'); } $data = ['type' => PaymentOrderService::TYPE_SELF_PAYOUT, 'member_id' => $user->member_id, 'amount' => $amount, 'channel' => self::CHANNEL, 'order_no' => PaymentOrderService::createOrderNo('sim4_', $user->member_id), 'bank_name' => '模拟银行', 'account' => (string) $user->first_name, 'card_no' => 'SIM' . $user->member_id, 'status' => PaymentOrderService::STATUS_STAY] + PaymentOrderService::feeFields($amount, 0); [$order] = WithdrawFundsService::hold((string) $user->member_id, $amount, '人工提现', '模拟提现', WithdrawFundsService::PAYMENT_ORDERS, function () use ($data, $user) { $pending = PaymentOrder::where('type', PaymentOrderService::TYPE_SELF_PAYOUT)->where('member_id', $user->member_id) ->where('status', PaymentOrderService::STATUS_STAY)->lockForUpdate()->first(['id']); if ($pending) throw new Exception('该模拟会员已有一笔待审核的提现', HttpStatus::CUSTOM_ERROR); return PaymentOrder::create($data); }); return $order->refresh(); } /** 审核模拟提现:通过则扣除冻结余额,拒绝则退回可用余额。 */ public static function withdrawAudit(int $id, bool $approve, string $remark = ''): PaymentOrder { SimulationSettings::assertEnabled(); $order = PaymentOrder::where('id', $id)->where('type', PaymentOrderService::TYPE_SELF_PAYOUT)->first(); if (!$order) throw new Exception('提现单不存在', HttpStatus::CUSTOM_ERROR); SimulationSettings::member($order->member_id); if ($approve) { DB::transaction(function () use ($id) { $locked = PaymentOrder::where('id', $id)->where('status', PaymentOrderService::STATUS_STAY)->lockForUpdate()->first(); if (!$locked) throw new Exception('提现单已处理', HttpStatus::CUSTOM_ERROR); $locked->status = PaymentOrderService::STATUS_SUCCESS; $locked->save(); WithdrawFundsService::settle(WithdrawFundsService::PAYMENT_ORDERS, $locked); }); } else { $result = PaymentOrderService::withdrawalFailed($id, $remark !== '' ? $remark : '模拟提现拒绝'); if ($result['code'] !== 0) throw new Exception($result['msg'], HttpStatus::CUSTOM_ERROR); } return $order->refresh(); } /** 人工调账:加款记“人工充值”,扣款记“人工扣款”,与后台上下分一致。 */ public static function adjust($memberId, $amount, string $direction, string $remark) { SimulationSettings::assertEnabled(); $user = SimulationSettings::member($memberId); $amount = SimulationSettings::assertAmount($amount); return DB::transaction(function () use ($user, $amount, $direction, $remark) { $wallet = Wallet::where('member_id', $user->member_id)->lockForUpdate()->first(); if (!$wallet) throw new Exception('钱包不存在', HttpStatus::CUSTOM_ERROR); $delta = $direction === 'debit' ? bcsub('0', $amount, 2) : $amount; $before = (string) $wallet->available_balance; $after = bcadd($before, $delta, 10); if (bccomp($after, '0', 10) < 0) throw new Exception('可用余额不足', HttpStatus::CUSTOM_ERROR); $wallet->available_balance = $after; $wallet->save(); return BalanceLogService::addLog($user->member_id, $delta, $before, $after, $direction === 'debit' ? '人工扣款' : '人工充值', null, $remark); }); } public static function freeze($memberId, $amount, string $reason, int $adminId, bool $unfreeze = false): array { SimulationSettings::assertEnabled(); $user = SimulationSettings::member($memberId); $amount = SimulationSettings::assertAmount($amount); $requestId = 'sim-' . bin2hex(random_bytes(8)); return $unfreeze ? WalletFreezeService::unfreeze($requestId, $adminId, (string) $user->member_id, $amount, '0', $reason) : WalletFreezeService::freeze($requestId, $adminId, (string) $user->member_id, $amount, '0', $reason); } }