Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
| Total | |
0.00% |
0 / 26 |
|
0.00% |
0 / 1 |
CRAP | |
0.00% |
0 / 1 |
| ReleaseExpiredOnlineOrders | |
0.00% |
0 / 26 |
|
0.00% |
0 / 1 |
56 | |
0.00% |
0 / 1 |
| handle | |
0.00% |
0 / 26 |
|
0.00% |
0 / 1 |
56 | |||
| 1 | <?php |
| 2 | |
| 3 | namespace App\Console\Commands; |
| 4 | |
| 5 | use App\Enums\OrderStatus; |
| 6 | use App\Enums\PaymentStatus; |
| 7 | use App\Models\Payment; |
| 8 | use App\Services\Stock\InventoryService; |
| 9 | use Illuminate\Console\Command; |
| 10 | use Illuminate\Support\Facades\DB; |
| 11 | |
| 12 | class ReleaseExpiredOnlineOrders extends Command |
| 13 | { |
| 14 | protected $signature = 'orders:release-expired-stock'; |
| 15 | |
| 16 | protected $description = 'Cancel expired unpaid online orders and return their reserved stock'; |
| 17 | |
| 18 | public function handle(InventoryService $inventory): int |
| 19 | { |
| 20 | $minutes = max(5, (int) settings('checkout.unpaid_order_hold_minutes', 30)); |
| 21 | $released = 0; |
| 22 | |
| 23 | Payment::query() |
| 24 | ->whereNotNull('gateway') |
| 25 | ->where('status', PaymentStatus::Pending->value) |
| 26 | ->where('created_at', '<=', now()->subMinutes($minutes)) |
| 27 | ->with('order') |
| 28 | ->chunkById(100, function ($payments) use ($inventory, &$released) { |
| 29 | foreach ($payments as $payment) { |
| 30 | DB::transaction(function () use ($payment, $inventory, &$released) { |
| 31 | $payment = Payment::lockForUpdate()->find($payment->id); |
| 32 | $order = $payment?->order()->lockForUpdate()->first(); |
| 33 | if (! $payment || ! $order || $payment->status !== PaymentStatus::Pending->value |
| 34 | || in_array($order->status, [OrderStatus::Cancelled->value, OrderStatus::Returned->value, OrderStatus::Refunded->value], true)) { |
| 35 | return; |
| 36 | } |
| 37 | $inventory->restoreOrder($order); |
| 38 | $order->update(['status' => OrderStatus::Cancelled->value]); |
| 39 | $payment->update([ |
| 40 | 'status' => PaymentStatus::Failed->value, |
| 41 | 'payment_note' => trim(($payment->payment_note ? $payment->payment_note . '; ' : '') . 'Expired before payment confirmation; stock released.'), |
| 42 | ]); |
| 43 | $released++; |
| 44 | }); |
| 45 | } |
| 46 | }); |
| 47 | |
| 48 | $this->info("Released stock for {$released} expired online order(s)."); |
| 49 | |
| 50 | return self::SUCCESS; |
| 51 | } |
| 52 | } |