resolveTransactionId($request); $fallback = $this->resolveFallbackUrl(); if (! $transactionId) { return redirect()->to($fallback); } try { $transaction = $this->transactions->retrieve($transactionId); } catch (PaddleException $exception) { Log::warning('Paddle return failed to load transaction', [ 'transaction_id' => $transactionId, 'error' => $exception->getMessage(), 'status' => $exception->status(), ]); return redirect()->to($fallback); } $customData = $this->extractCustomData($transaction); $status = Str::lower((string) ($transaction['status'] ?? '')); $successUrl = $customData['success_url'] ?? null; $cancelUrl = $customData['cancel_url'] ?? $customData['return_url'] ?? null; $target = $this->isSuccessStatus($status) ? $successUrl : $cancelUrl; $target = $this->resolveSafeRedirect($target, $fallback); return redirect()->to($target); } protected function resolveTransactionId(Request $request): ?string { $candidate = $request->query('_ptxn') ?? $request->query('ptxn') ?? $request->query('transaction_id'); if (! is_string($candidate) || $candidate === '') { return null; } return $candidate; } protected function resolveFallbackUrl(): string { return rtrim((string) config('app.url', url('/')), '/') ?: url('/'); } /** * @param array $transaction * @return array */ protected function extractCustomData(array $transaction): array { $customData = Arr::get($transaction, 'custom_data', []); if (! is_array($customData)) { $customData = []; } $legacy = Arr::get($transaction, 'customData'); if (is_array($legacy)) { $customData = array_merge($customData, $legacy); } $metadata = Arr::get($transaction, 'metadata'); if (is_array($metadata)) { $customData = array_merge($customData, $metadata); } return $customData; } protected function isSuccessStatus(string $status): bool { return in_array($status, ['completed', 'paid'], true); } protected function resolveSafeRedirect(?string $target, string $fallback): string { if (! $target) { return $fallback; } if (Str::startsWith($target, ['/'])) { return $target; } $appHost = parse_url($fallback, PHP_URL_HOST); $targetHost = parse_url($target, PHP_URL_HOST); if ($appHost && $targetHost && Str::lower($appHost) === Str::lower($targetHost)) { return $target; } return $fallback; } }