fix(core): harden settlement and wallet integration

This commit is contained in:
wchino
2026-07-22 01:40:37 +08:00
parent 150c3e7ebd
commit 35f6e46958
54 changed files with 2564 additions and 359 deletions

View File

@@ -5,10 +5,13 @@ namespace App\Services\Ticket;
use App\Models\Draw;
use App\Models\WalletTxn;
use App\Models\TicketItem;
use App\Models\BetProvider;
use App\Models\TicketOrder;
use App\Support\PlayerFundingMode;
use Illuminate\Support\Facades\DB;
use App\Services\Player\PlayerCreditService;
use App\Services\Draw\DrawHallSnapshotBuilder;
use App\Services\Jackpot\JackpotContributionService;
use Illuminate\Support\Facades\DB;
final class TicketPendingConfirmReconcileService
{
@@ -17,6 +20,7 @@ final class TicketPendingConfirmReconcileService
private readonly JackpotContributionService $jackpotContribution,
private readonly DrawHallSnapshotBuilder $drawHallSnapshot,
private readonly TicketWalletService $ticketWallet,
private readonly PlayerCreditService $playerCredit,
) {}
/**
@@ -45,6 +49,13 @@ final class TicketPendingConfirmReconcileService
return 'skipped';
}
$player = $lockedOrder->player()->first();
if ($player !== null && PlayerFundingMode::usesCredit($player)) {
// 信用单的占额与 pending_confirm 在同一数据库事务提交;
// 能读到该状态即代表占额成功,无需依赖仅钱包盘才有的 bet_deduct 流水。
return $this->confirmOrder($lockedOrder);
}
$hasPostedDeduct = WalletTxn::query()
->where('biz_type', 'bet_deduct')
->where('biz_no', $lockedOrder->order_no)
@@ -114,6 +125,17 @@ final class TicketPendingConfirmReconcileService
private function refundStalePendingOrder(TicketOrder $lockedOrder, string $reasonCode): string
{
$player = $lockedOrder->player()->first();
if ($player !== null && PlayerFundingMode::usesCredit($player)) {
$this->playerCredit->reverseBetHold(
$player,
(int) $lockedOrder->total_actual_deduct,
(int) $lockedOrder->id,
);
return $this->refundPendingConfirmItems($lockedOrder, $reasonCode);
}
$hasPostedDeduct = WalletTxn::query()
->where('biz_type', 'bet_deduct')
->where('biz_no', $lockedOrder->order_no)
@@ -131,6 +153,15 @@ final class TicketPendingConfirmReconcileService
private function refundOrderWithoutDeduct(TicketOrder $lockedOrder): string
{
$player = $lockedOrder->player()->first();
if ($player !== null && PlayerFundingMode::usesCredit($player)) {
$this->playerCredit->reverseBetHold(
$player,
(int) $lockedOrder->total_actual_deduct,
(int) $lockedOrder->id,
);
}
return $this->refundPendingConfirmItems($lockedOrder, 'pending_confirm_timeout');
}
@@ -155,7 +186,7 @@ final class TicketPendingConfirmReconcileService
if ($locks !== []) {
$this->riskPool->release(
(int) $lockedOrder->draw_id,
(string) ($item->provider_code ?: \App\Models\BetProvider::DEFAULT_CODE),
(string) ($item->provider_code ?: BetProvider::DEFAULT_CODE),
$item,
$locks,
);