find($this->orderId); if (! $order) { return; } if (! in_array($order->status, [Order::STATUS_PENDING, Order::STATUS_PENDING_PAYMENT], true)) { return; } $additional = $order->payment?->additional ?? []; $gatewayOrderId = $additional['gateway_order_id'] ?? $additional['paypal_order_id'] ?? null; $method = $order->payment?->method; if (! $gatewayOrderId || $method !== 'paypal_smart_button') { Event::dispatch('bagistoapi.payment.reconcile.no-gateway-info', $order); return; } try { $gatewayOrder = app(SmartButton::class)->getOrder($gatewayOrderId); $status = (string) ($gatewayOrder->result->status ?? ''); $captures = $gatewayOrder->result->purchase_units[0]->payments->captures ?? []; if (! empty($captures) || in_array(strtoupper($status), ['COMPLETED', 'CAPTURED'], true)) { /* * Capture exists but Bagisto is still pending - a * callback was missed. Surface this for ops review. */ Event::dispatch('bagistoapi.payment.reconcile.captured', [ 'order' => $order, 'gateway_status' => $status, 'gateway_order' => $gatewayOrder->result ?? null, ]); return; } if (in_array(strtoupper($status), ['VOIDED', 'EXPIRED', 'PAYER_ACTION_REQUIRED'], true)) { Event::dispatch('bagistoapi.payment.reconcile.voided', $order); } $cancelled = DB::transaction(function () use ($orderRepository, $order, $gatewayOrderId): bool { /* * Re-lock and re-check inside a transaction right before * cancellation to avoid racing with a late success callback. */ $orderModelClass = OrderProxy::modelClass(); $lockedOrder = $orderModelClass::query()->whereKey($order->id)->lockForUpdate()->first(); if (! $lockedOrder) { return false; } if (! in_array($lockedOrder->status, [Order::STATUS_PENDING, Order::STATUS_PENDING_PAYMENT], true)) { return false; } $lockedAdditional = $lockedOrder->payment?->additional ?? []; $lockedGatewayOrderId = $lockedAdditional['gateway_order_id'] ?? $lockedAdditional['paypal_order_id'] ?? null; if (! $lockedGatewayOrderId || $lockedGatewayOrderId !== $gatewayOrderId) { return false; } return (bool) $orderRepository->cancel($lockedOrder,true); }); if ($cancelled) { Event::dispatch('bagistoapi.payment.reconcile.cancelled', $order); } } catch (\Throwable $e) { Log::warning('ReconcilePendingPaymentJob: gateway lookup failed', [ 'order_id' => $order->id, 'gateway_order_id' => $gatewayOrderId, 'error' => $e->getMessage(), ]); /* * The gateway didn't tell us anything useful. Let the * retry mechanism take over - we'd rather try again than * cancel a possibly-paid order. */ throw $e; } } }