isMockMode() ? $this->mockZarinpalService : $this->zarinpalService; } public function redirectToGateway(Request $request): JsonResponse { $user = $request->user(); if (!$user) { return response()->json(['message' => 'Unauthorized'], 401); } $validated = $request->validate([ 'amount' => ['required', 'numeric', 'min:10000'], 'description' => ['nullable', 'string', 'max:500'], 'frontend_callback' => ['nullable', 'url', 'max:500'], ]); $wallet = $user->wallet ?? Wallet::create([ 'user_id' => $user->id, 'balance' => 0, ]); if ($wallet->isFrozen()) { return response()->json([ 'message' => 'کیف پول شما مسدود است.', ], 403); } try { $transaction = $this->walletService->requestDeposit( wallet: $wallet, amount: $validated['amount'], description: $validated['description'] ?? 'شارژ آنلاین کیف پول', user: $user, gateway: PaymentGateway::ZARINPAL ); $gateway = $this->getGatewayService(); $paymentData = $gateway->requestPayment( amount: $validated['amount'], description: $validated['description'] ?? 'شارژ کیف پول IFNEX', mobile: $user->phone, email: $user->email ); $metadata = $transaction->metadata ?? []; $metadata['zarinpal_authority'] = $paymentData['authority']; $metadata['frontend_callback'] = $validated['frontend_callback'] ?? null; $metadata['is_mock'] = $this->isMockMode(); $transaction->update(['metadata' => $metadata]); return response()->json([ 'success' => true, 'message' => 'در حال انتقال به درگاه پرداخت...', 'payment_url' => $paymentData['payment_url'], 'transaction_id' => $transaction->id, 'authority' => $paymentData['authority'], 'amount' => $paymentData['amount'], 'is_mock' => $this->isMockMode(), ]); } catch (\Exception $e) { Log::error('Payment gateway error', [ 'user_id' => $user->id, 'error' => $e->getMessage(), ]); return response()->json([ 'success' => false, 'message' => 'خطا در ارتباط با درگاه پرداخت: ' . $e->getMessage(), ], 500); } } public function callback(Request $request): RedirectResponse { $authority = $request->input('Authority'); $status = $request->input('Status'); Log::info('Payment callback received', [ 'authority' => $authority, 'status' => $status, ]); // پیدا کردن تراکنش با JSON_EXTRACT $transaction = WalletTransaction::whereRaw( "JSON_EXTRACT(metadata, '$.zarinpal_authority') = ?", [$authority] )->first(); // Fallback برای MySQL قدیمی‌تر if (!$transaction) { $transaction = WalletTransaction::where('metadata->zarinpal_authority', $authority)->first(); } if (!$transaction) { Log::warning('Payment callback: transaction not found', [ 'authority' => $authority, ]); return redirect(config('ifnex.zarinpal.frontend_failure_url', '/') . '?' . http_build_query([ 'status' => 'error', 'message' => 'تراکنش یافت نشد', ])); } // ✅ اصلاح: تعریف $frontendCallback در ابتدای متد (قبل از هر استفاده) $frontendCallback = $transaction->metadata['frontend_callback'] ?? config('ifnex.zarinpal.frontend_failure_url', '/'); // بررسی اینکه تراکنش قبلاً پردازش نشده باشد if ($transaction->status !== TransactionStatus::PENDING) { Log::info('Transaction already processed', [ 'transaction_id' => $transaction->id, 'current_status' => $transaction->status->value, ]); if ($transaction->status === TransactionStatus::COMPLETED) { return redirect($frontendCallback . '?' . http_build_query([ 'status' => 'success', 'transaction_id' => $transaction->id, 'ref_id' => $transaction->gateway_reference_id ?? '', 'amount' => $transaction->amount, 'message' => 'این تراکنش قبلاً پردازش شده است', ])); } return redirect($frontendCallback . '?' . http_build_query([ 'status' => 'failed', 'transaction_id' => $transaction->id, 'message' => 'این تراکنش قبلاً ناموفق شده است', ])); } // کاربر پرداخت را لغو کرده if ($status !== 'OK') { $this->walletService->failTransaction($transaction, 'کاربر پرداخت را لغو کرد.'); return redirect($frontendCallback . '?' . http_build_query([ 'status' => 'failed', 'transaction_id' => $transaction->id, 'message' => 'پرداخت ناموفق بود', ])); } // تایید پرداخت با درگاه try { Log::info('Verifying payment', [ 'transaction_id' => $transaction->id, 'authority' => $authority, 'current_status' => $transaction->status->value, ]); $gateway = $this->getGatewayService(); $verification = $gateway->verifyPayment( authority: $authority, expectedAmount: $transaction->amount ); Log::info('Payment verification result', [ 'transaction_id' => $transaction->id, 'verification' => $verification, ]); if (!$verification['success']) { $this->walletService->failTransaction( $transaction, 'تأیید درگاه ناموفق: ' . ($verification['error_message'] ?? 'نامشخص') ); return redirect($frontendCallback . '?' . http_build_query([ 'status' => 'failed', 'transaction_id' => $transaction->id, 'message' => 'تأیید پرداخت ناموفق بود', ])); } $this->walletService->completeDeposit( transaction: $transaction, gatewayReferenceId: $verification['ref_id'] ?? $authority ); // اگر تراکنش مربوط به سفارش است، status سفارش را تغییر بده $metadata = $transaction->metadata ?? []; if (!empty($metadata['is_order_payment']) && !empty($metadata['shipment_id'])) { $shipment = \App\Models\Shipment::find($metadata['shipment_id']); if ($shipment && $shipment->status === \App\Enums\ShipmentStatus::Approved) { $shipment->update(['status' => \App\Enums\ShipmentStatus::Processed]); } } Log::info('Payment completed successfully', [ 'transaction_id' => $transaction->id, 'amount' => $transaction->amount, ]); return redirect($frontendCallback . '?' . http_build_query([ 'status' => 'success', 'transaction_id' => $transaction->id, 'ref_id' => $verification['ref_id'] ?? '', 'amount' => $transaction->amount, ])); } catch (\Exception $e) { Log::error('Payment callback error', [ 'transaction_id' => $transaction->id, 'error' => $e->getMessage(), 'file' => $e->getFile(), 'line' => $e->getLine(), 'trace' => $e->getTraceAsString(), ]); $this->walletService->failTransaction($transaction, 'خطای سیستمی: ' . $e->getMessage()); return redirect($frontendCallback . '?' . http_build_query([ 'status' => 'failed', 'transaction_id' => $transaction->id, 'message' => 'خطای سیستمی: ' . $e->getMessage(), ])); } } public function checkStatus(Request $request, $transactionId): JsonResponse { $user = $request->user(); $transaction = WalletTransaction::where('id', $transactionId) ->whereHas('wallet', fn ($q) => $q->where('user_id', $user->id)) ->first(); if (!$transaction) { return response()->json(['message' => 'تراکنش یافت نشد'], 404); } return response()->json([ 'transaction_id' => $transaction->id, 'status' => $transaction->status->label(), 'status_code' => $transaction->status->value, 'amount' => $transaction->amount, 'description' => $transaction->description, 'reference_id' => $transaction->gateway_reference_id, 'created_at' => $transaction->created_at->toIso8601String(), ]); } }