--- File: D:\projects\digitalvocano\Modules\PaypalGateway\config\config.php ---
<?php

return [
    'name' => 'PaypalGateway',
];


--- File: D:\projects\digitalvocano\Modules\PaypalGateway\database\seeders\PaypalGatewayDatabaseSeeder.php ---
<?php

namespace Modules\PaypalGateway\Database\Seeders;

use Illuminate\Database\Seeder;

class PaypalGatewayDatabaseSeeder extends Seeder
{
    /**
     * Run the database seeds.
     */
    public function run(): void
    {
        // $this->call([]);
    }
}


--- File: D:\projects\digitalvocano\Modules\PaypalGateway\Http\Controllers\Admin\PaypalConfigController.php ---
<?php

namespace Modules\PaypalGateway\Http\Controllers\Admin;

use App\Http\Controllers\Controller;
use App\Models\Setting; // Assuming your Setting model is in App\Models
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\Log;

class PaypalConfigController extends Controller
{
    protected $settingKeys = [
        'paypal_enabled',
        'paypal_client_id',
        'paypal_client_secret',
        'paypal_webhook_id', // Webhook ID from PayPal dashboard for verification
        'paypal_mode', // 'sandbox' or 'live'
    ];

    public function edit()
    {
        if (!function_exists('setting')) {
            return redirect()->route('admin.dashboard')->with('error', 'Settings helper function not found.');
        }

        $settings = [];
        foreach ($this->settingKeys as $key) {
            $settings[$key] = setting($key);
        }
        // Default mode to sandbox if not set
        $settings['paypal_mode'] = $settings['paypal_mode'] ?? 'sandbox';
        // Default enabled to '0' if not set
        if (is_null($settings['paypal_enabled'])) {
            $settings['paypal_enabled'] = '0';
        }


        return view('paypalgateway::admin.config', compact('settings'));
    }

    public function update(Request $request)
    {
        if (!function_exists('setting')) {
            return redirect()->route('admin.dashboard')->with('error', 'Settings helper function not found.');
        }

        $validated = $request->validate([
            'paypal_enabled' => 'nullable|boolean',
            'paypal_client_id' => 'nullable|string|max:255',
            'paypal_client_secret' => 'nullable|string|max:255',
            'paypal_webhook_id' => 'nullable|string|max:255',
            'paypal_mode' => 'required|in:sandbox,live',
        ]);

        try {
            // Define settings with their names, groups, and types
            $settingsData = [
                'paypal_enabled' => [
                    'value' => $request->input('paypal_enabled', '0'),
                    'name' => 'Enable PayPal Gateway',
                    'group' => 'Payment Gateways',
                    'type' => 'boolean'
                ],
                'paypal_mode' => [
                    'value' => $request->input('paypal_mode', 'sandbox'),
                    'name' => 'PayPal Mode',
                    'group' => 'Payment Gateways',
                    'type' => 'select'
                ],
                'paypal_client_id' => [
                    'value' => $request->input('paypal_client_id'),
                    'name' => 'PayPal Client ID',
                    'group' => 'Payment Gateways',
                    'type' => 'text'
                ],
                'paypal_webhook_id' => [
                    'value' => $request->input('paypal_webhook_id'),
                    'name' => 'PayPal Webhook ID',
                    'group' => 'Payment Gateways',
                    'type' => 'text'
                ],
            ];

            foreach ($settingsData as $key => $data) {
                Setting::setValue($key, $data['value'], $data['name'], $data['group'], $data['type']);
            }

            // Handle client secret separately: only update if a new value is provided
            if ($request->filled('paypal_client_secret')) {
                Setting::setValue('paypal_client_secret', $request->input('paypal_client_secret'), 'PayPal Client Secret', 'Payment Gateways', 'password');
            }

            Artisan::call('cache:clear');
            Artisan::call('config:clear');

            return redirect()->back()->with('success', 'PayPal settings updated successfully.');

        } catch (\Exception $e) {
            Log::error('Error updating PayPal settings: ' . $e->getMessage());
            return redirect()->back()->with('error', 'Failed to update PayPal settings. Please check the logs.');
        }
    }
}


--- File: D:\projects\digitalvocano\Modules\PaypalGateway\Http\Controllers\PaypalGatewayController.php ---
<?php

namespace Modules\PaypalGateway\Http\Controllers;

use App\Http\Controllers\Controller;
use Illuminate\Http\Request;

class PaypalGatewayController extends Controller
{
    /**
     * Display a listing of the resource.
     */
    public function index()
    {
        return view('paypalgateway::index');
    }

    /**
     * Show the form for creating a new resource.
     */
    public function create()
    {
        return view('paypalgateway::create');
    }

    /**
     * Store a newly created resource in storage.
     */
    public function store(Request $request) {}

    /**
     * Show the specified resource.
     */
    public function show($id)
    {
        return view('paypalgateway::show');
    }

    /**
     * Show the form for editing the specified resource.
     */
    public function edit($id)
    {
        return view('paypalgateway::edit');
    }

    /**
     * Update the specified resource in storage.
     */
    public function update(Request $request, $id) {}

    /**
     * Remove the specified resource from storage.
     */
    public function destroy($id) {}
}


--- File: D:\projects\digitalvocano\Modules\PaypalGateway\Http\Controllers\PaypalSubscriptionController.php ---
<?php

namespace Modules\PaypalGateway\Http\Controllers;

use App\Http\Controllers\Controller;
use App\Models\Subscription;
use App\Models\SubscriptionPlan;
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Log;
use Modules\PaypalGateway\Services\PaypalService;
use App\Services\CreditService; // Added
use App\Services\WalletService; // Added

class PaypalSubscriptionController extends Controller
{
    protected PaypalService $paypalService;
    protected CreditService $creditService; // Added
    protected WalletService $walletService; // Added

    public function __construct(PaypalService $paypalService, CreditService $creditService, WalletService $walletService)
    {
        $this->paypalService = $paypalService;
        $this->creditService = $creditService; // Added
        $this->walletService = $walletService; // Added
    }

    public function createSubscription(Request $request, SubscriptionPlan $plan)
    {
        if (setting('paypal_enabled', '0') != '1') {
            return redirect()->route('subscription.plans')->with('error', 'PayPal payments are currently disabled.');
        }

        /** @var User $user */
        $user = Auth::user();

        $activeSubscription = $user->subscriptions()
                                ->whereIn('status', ['active', 'trialing', 'ACTIVE']) // PayPal uses ACTIVE, PENDING, etc.
                                ->first();

        if ($activeSubscription) {
            if ($activeSubscription->subscription_plan_id == $plan->id && $activeSubscription->payment_gateway == 'paypal') {
                 return redirect()->route('subscription.plans')->with('info', 'You are already subscribed to this plan via PayPal.');
            }
            return redirect()->route('subscription.plans')->with('error', 'You already have an active subscription. Please manage it from your profile.');
        }

        try {
            $approvalLink = $this->paypalService->createPaypalSubscription($user, $plan);
            if ($approvalLink) {
                $pendingPaypalIdFromSession = session('paypal_subscription_id_pending');
                Log::info("PayPal CreateSubscription: Retrieved 'paypal_subscription_id_pending' from session.", ['session_paypal_id' => $pendingPaypalIdFromSession, 'user_id' => $user->id, 'plan_id' => $plan->id]);

                if (empty($pendingPaypalIdFromSession)) {
                    Log::error("PayPal CreateSubscription: 'paypal_subscription_id_pending' is empty in session. Cannot create local pending subscription.", ['user_id' => $user->id, 'plan_id' => $plan->id]);
                    return redirect()->route('subscription.plans')->with('error', 'Failed to initiate PayPal subscription due to a session issue. Please try again.');
                }

                // Create a local pending subscription record
                // The actual gateway_subscription_id will be confirmed upon execution
                $localPendingSubscription = null;
                try {
                    $localPendingSubscription = Subscription::create([
                        'user_id' => $user->id,
                        'subscription_plan_id' => $plan->id,
                        'payment_gateway' => 'paypalgateway',
                        'gateway_subscription_id' => $pendingPaypalIdFromSession,
                        'status' => 'pending_approval',
                        'price_at_purchase' => $plan->price,
                        'currency_at_purchase' => $plan->currency,
                    ]);
                } catch (\Illuminate\Database\QueryException $qe) {
                    Log::critical("PayPal CreateSubscription: DATABASE QUERY EXCEPTION during Subscription::create()", [
                        'user_id' => $user->id, 'plan_id' => $plan->id, 'attempted_paypal_id' => $pendingPaypalIdFromSession,
                        'error' => $qe->getMessage(), 'sql' => $qe->getSql(), 'bindings' => $qe->getBindings()
                    ]);
                    return redirect()->route('subscription.plans')->with('error', 'Critical error: Failed to record your pending PayPal subscription due to a database issue. Please contact support. (Ref: PENDING_DB_EX)');
                } catch (\Exception $e) { // Catch any other general exception during create
                    Log::critical("PayPal CreateSubscription: GENERAL EXCEPTION during Subscription::create()", ['user_id' => $user->id, 'plan_id' => $plan->id, 'attempted_paypal_id' => $pendingPaypalIdFromSession, 'error' => $e->getMessage()]);
                    return redirect()->route('subscription.plans')->with('error', 'Critical error: Failed to record your pending PayPal subscription. Please contact support. (Ref: PENDING_GEN_EX)');
                }

                if ($localPendingSubscription && $localPendingSubscription->id) {
                    Log::info("PayPal CreateSubscription: Successfully created local pending subscription.", ['local_subscription_id' => $localPendingSubscription->id, 'gateway_subscription_id_stored' => $localPendingSubscription->gateway_subscription_id]);
                } else {
                    Log::error("PayPal CreateSubscription: FAILED to create local pending subscription record in database.", ['user_id' => $user->id, 'plan_id' => $plan->id, 'attempted_paypal_id' => $pendingPaypalIdFromSession]);
                    return redirect()->route('subscription.plans')->with('error', 'Failed to record your pending PayPal subscription. Please try again.');
                }

                session()->forget('paypal_subscription_id_pending'); // Clean up session
                session()->forget('local_plan_id_pending');

                return redirect($approvalLink);
            }
            return redirect()->route('subscription.plans')->with('error', 'Could not initiate PayPal subscription.');
        } catch (\Exception $e) {
            Log::error("PayPal Create Subscription Error for user {$user->id}, plan {$plan->id}: " . $e->getMessage(), ['trace' => $e->getTraceAsString()]);
            return redirect()->route('subscription.plans')->with('error', 'Could not initiate PayPal subscription: ' . $e->getMessage());
        }
    }

    public function executeSubscription(Request $request)
    {
        $paypalSubscriptionId = $request->query('subscription_id'); // PayPal returns subscription_id on approval
        // The 'ba_token' and 'token' might also be present for older flows or different contexts.
        // For modern Subscriptions API, subscription_id is key.

        if (!$paypalSubscriptionId) {
            return redirect()->route('subscription.plans')->with('error', 'Invalid PayPal subscription approval.');
        }

        try {
            $paypalSubscriptionDetails = $this->paypalService->getPaypalSubscriptionDetails($paypalSubscriptionId);

            if ($paypalSubscriptionDetails && in_array(strtoupper($paypalSubscriptionDetails->status), ['ACTIVE', 'APPROVAL_PENDING'])) {
                /** @var User $user */
                // User might not be available directly from Auth if it's a webhook context later,
                // but for direct redirect, Auth::user() is fine.
                // For webhooks, you'd typically get user_id from metadata or the local subscription.
                $user = Auth::user();

                // Find the pending local subscription
                $localSubscription = Subscription::where('gateway_subscription_id', $paypalSubscriptionId)
                                        ->where('status', 'pending_approval')
                                        ->where('user_id', $user->id) // Ensure it's for the current user
                                        ->first();

                if ($localSubscription) {
                    // Deactivate other existing subscriptions for this user
                    $user->subscriptions()
                        ->where('id', '!=', $localSubscription->id)
                        ->whereIn('status', ['active', 'trialing', 'ACTIVE'])
                        ->update(['status' => 'cancelled', 'ends_at' => now(), 'cancelled_at' => now()]);

                    $localSubscription->status = strtolower($paypalSubscriptionDetails->status); // 'active'
                    $localSubscription->starts_at = isset($paypalSubscriptionDetails->start_time) ? \Carbon\Carbon::parse($paypalSubscriptionDetails->start_time) : now();
                    
                    $plan = $localSubscription->plan;
                    if ($plan) {
                        if ($plan->trial_period_days > 0 && strtoupper($paypalSubscriptionDetails->status) === 'ACTIVE') { // Or check if trial info is in $paypalSubscriptionDetails
                            $localSubscription->trial_ends_at = $localSubscription->starts_at->copy()->addDays($plan->trial_period_days);
                            // Determine ends_at based on trial and plan interval
                            $billingCycleAnchor = $localSubscription->trial_ends_at;
                        } else {
                            $billingCycleAnchor = $localSubscription->starts_at;
                        }
                        $localSubscription->ends_at = $billingCycleAnchor->copy()->add($plan->interval, $plan->interval_count);

                        // Award credits
                        if (function_exists('setting') && setting('credits_system_enabled', '0') == '1' && $plan->credits_awarded_on_purchase > 0) {
                            $this->creditService->awardCredits($user, $plan->credits_awarded_on_purchase, 'award_subscription_purchase', "Credits for {$plan->name} subscription", $localSubscription);
                        }
                        // Assign target role
                        if (!empty($plan->target_role) && class_exists(\Spatie\Permission\Models\Role::class) && \Spatie\Permission\Models\Role::where('name', $plan->target_role)->where('guard_name', 'web')->exists()) {
                            $user->syncRoles([$plan->target_role]);
                        }
                    }

                    $localSubscription->save();

                    return redirect()->route('dashboard')->with('success', 'PayPal subscription successfully activated!');
                } else {
                    Log::error("PayPal Execute: Local pending subscription not found for PayPal ID: {$paypalSubscriptionId}");
                    return redirect()->route('subscription.plans')->with('error', 'Could not find your pending PayPal subscription record.');
                }
            } else {
                Log::error("PayPal Execute: Subscription not active or details not found.", (array)$paypalSubscriptionDetails);
                return redirect()->route('subscription.plans')->with('error', 'PayPal subscription could not be activated.');
            }
        } catch (\Exception $e) {
            Log::error("PayPal Execute Subscription Error: " . $e->getMessage(), ['paypal_subscription_id' => $paypalSubscriptionId]);
            return redirect()->route('subscription.plans')->with('error', 'There was an issue activating your PayPal subscription: ' . $e->getMessage());
        }
    }

    public function cancelSubscriptionPage(Request $request)
    {
        // User cancelled from PayPal's approval page
        return redirect()->route('subscription.plans')->with('info', 'Your PayPal subscription process was cancelled.');
    }

    public function cancelActiveSubscription(Request $request)
    {
        /** @var User $user */
        $user = Auth::user();
        $activeSubscription = $user->subscriptions()
            ->where('payment_gateway', 'paypalgateway') // Consistent key
            ->where('status', 'active') // Or other active-like statuses
            ->orderBy('created_at', 'desc')
            ->first();

        if (!$activeSubscription) {
            return redirect()->back()->with('info', 'No active PayPal subscription found to cancel.');
        }

        if ($this->paypalService->cancelSubscriptionAtGateway($activeSubscription)) {
            $activeSubscription->status = 'cancelled';
            $activeSubscription->cancelled_at = now();
            $activeSubscription->save();
            return redirect()->back()->with('success', 'Your PayPal subscription has been cancelled.');
        }
        return redirect()->back()->with('error', 'Failed to cancel PayPal subscription. Please try again or contact support.');
    }

    // --- Wallet Deposit Methods ---

    public function initializeWalletDeposit(Request $request)
    {
        if (setting('paypal_enabled', '0') != '1' || setting('allow_wallet_deposits', '0') != '1') {
            return redirect()->route('user.wallet.deposit.form')->with('error', 'PayPal deposits are currently disabled.');
        }

        $request->validate(['amount' => 'required|numeric|min:1']);
        $amount = (float) $request->input('amount');
        /** @var User $user */
        $user = Auth::user();
        $currency = strtoupper(setting('currency_code', 'USD'));

        try {
            $response = $this->paypalService->createWalletDepositOrder($user, $amount, $currency);

            if ($response && $response->statusCode == 201) { // 201 Created
                $order = $response->result;
                // Store PayPal Order ID in session to verify on callback
                session(['paypal_wallet_deposit_order_id' => $order->id]);

                foreach ($order->links as $link) {
                    if ($link->rel == 'approve') {
                        return redirect()->away($link->href);
                    }
                }
            }
            Log::error('PayPal Initialize Wallet Deposit Failed', ['response_status' => $response->statusCode ?? 'N/A', 'response_body' => $response->result ?? 'N/A']);
            return redirect()->route('user.wallet.deposit.form')->with('error', 'Could not initiate PayPal deposit. Please try again.');
        } catch (\Exception $e) {
            Log::error("PayPal Initialize Wallet Deposit Error for user {$user->id}, amount {$amount}: " . $e->getMessage());
            return redirect()->route('user.wallet.deposit.form')->with('error', 'An error occurred while initiating deposit with PayPal: ' . $e->getMessage());
        }
    }

    public function handleWalletDepositCallback(Request $request)
    {
        $paypalOrderId = session('paypal_wallet_deposit_order_id');
        $token = $request->query('token'); // This is the PayPal Order ID
        $payerId = $request->query('PayerID'); // PayerID is also sent

        /** @var User $user */
        $user = Auth::user();

        if (!$paypalOrderId || $paypalOrderId !== $token) {
            Log::error("PayPal Wallet Deposit Callback: Session Order ID mismatch or missing.", ['session_order_id' => $paypalOrderId, 'token' => $token]);
            return redirect()->route('user.wallet.deposit.form')->with('error', 'Invalid PayPal session for deposit.');
        }

        try {
            $response = $this->paypalService->captureWalletDepositOrder($paypalOrderId);

            if ($response && $response->statusCode == 201 && isset($response->result->status) && $response->result->status == 'COMPLETED') {
                $capture = $response->result->purchase_units[0]->payments->captures[0];
                $amountDeposited = (float) $capture->amount->value;
                $currency = $capture->amount->currency_code;

                // Idempotency check
                $existingTransaction = \App\Models\WalletTransaction::where('gateway_transaction_id', $capture->id)->first();
                if ($existingTransaction) {
                    Log::info("PayPal Wallet Deposit: Capture ID {$capture->id} already processed.");
                    return redirect()->route('user.wallet.history')->with('success', 'Your deposit was successful.');
                }

                $this->walletService->deposit($user, $amountDeposited, $currency, 'paypalgateway', $capture->id, "Wallet deposit via PayPal");
                session()->forget('paypal_wallet_deposit_order_id');
                return redirect()->route('user.wallet.history')->with('success', 'Successfully deposited ' . $currency . ' ' . number_format($amountDeposited, 2) . ' to your wallet.');
            }
            Log::error('PayPal Wallet Deposit Capture Failed', ['response_status' => $response->statusCode ?? 'N/A', 'response_body' => $response->result ?? 'N/A']);
            return redirect()->route('user.wallet.deposit.form')->with('error', 'PayPal payment capture failed for deposit.');
        } catch (\Exception $e) {
            Log::error("PayPal Wallet Deposit Callback Error: " . $e->getMessage(), ['paypal_order_id' => $paypalOrderId]);
            return redirect()->route('user.wallet.deposit.form')->with('error', 'An error occurred during PayPal deposit verification: ' . $e->getMessage());
        }
    }

    public function handleWalletDepositCancel(Request $request)
    {
        session()->forget('paypal_wallet_deposit_order_id');
        return redirect()->route('user.wallet.deposit.form')->with('info', 'Your PayPal wallet deposit was cancelled.');
    }
}


--- File: D:\projects\digitalvocano\Modules\PaypalGateway\Http\Controllers\PaypalWebhookController.php ---
<?php

namespace Modules\PaypalGateway\Http\Controllers;

use App\Http\Controllers\Controller;
use App\Models\Subscription;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use Modules\PaypalGateway\Services\PaypalService;

class PaypalWebhookController extends Controller
{
    protected PaypalService $paypalService;

    public function __construct(PaypalService $paypalService)
    {
        $this->paypalService = $paypalService;
    }

    public function handleWebhook(Request $request)
    {
        $rawBody = $request->getContent();
        $headers = $request->headers->all(); // Get all headers as an array

        // **IMPORTANT: Implement robust webhook signature verification here!**
        // The $this->paypalService->verifyWebhookSignature() is a placeholder.
        // You MUST verify the signature according to PayPal's documentation.
        // if (!$this->paypalService->verifyWebhookSignature($headers, $rawBody)) {
        //     Log::warning('PayPal Webhook: Invalid signature or verification failed.');
        //     return response()->json(['status' => 'error', 'message' => 'Signature verification failed'], 400);
        // }

        $event = json_decode($rawBody);

        if (!$event || !isset($event->event_type)) {
            Log::error('PayPal Webhook: Invalid event data received.');
            return response()->json(['status' => 'error', 'message' => 'Invalid event data'], 400);
        }

        Log::info('PayPal Webhook Received:', (array) $event);

        $eventType = strtoupper($event->event_type); // PayPal event types are usually uppercase
        $resource = $event->resource ?? null;

        switch ($eventType) {
            case 'BILLING.SUBSCRIPTION.ACTIVATED':
            case 'BILLING.SUBSCRIPTION.UPDATED':
                if ($resource && isset($resource->id)) {
                    $this->handleSubscriptionUpdate($resource);
                }
                break;

            case 'BILLING.SUBSCRIPTION.CANCELLED':
            case 'BILLING.SUBSCRIPTION.EXPIRED':
            case 'BILLING.SUBSCRIPTION.SUSPENDED':
                if ($resource && isset($resource->id)) {
                    $this->handleSubscriptionStateChange($resource, strtolower($resource->status ?? 'cancelled'));
                }
                break;

            case 'PAYMENT.SALE.COMPLETED': // For recurring payments
                if ($resource && isset($resource->billing_agreement_id)) {
                    // This event confirms a recurring payment.
                    // The BILLING.SUBSCRIPTION.UPDATED event usually handles date changes.
                    // You might want to log this or ensure the subscription is still active.
                    $this->ensureSubscriptionActive($resource->billing_agreement_id);
                }
                break;

            // Add more event types as needed based on PayPal's documentation
            // e.g., BILLING.SUBSCRIPTION.PAYMENT.FAILED

            default:
                Log::info('PayPal Webhook: Received unhandled event type ' . $eventType);
        }

        return response()->json(['status' => 'success']);
    }

    protected function handleSubscriptionUpdate($paypalSubscriptionResource)
    {
        $localSubscription = Subscription::where('gateway_subscription_id', $paypalSubscriptionResource->id)->first();
        if ($localSubscription) {
            $localSubscription->status = strtolower($paypalSubscriptionResource->status); // e.g., 'active'
            // Update start_time, agreement_details.next_billing_date etc.
            // $localSubscription->starts_at = \Carbon\Carbon::parse($paypalSubscriptionResource->start_time);
            // $localSubscription->ends_at = isset($paypalSubscriptionResource->agreement_details->next_billing_date) ? \Carbon\Carbon::parse($paypalSubscriptionResource->agreement_details->next_billing_date) : null;
            $localSubscription->save();
            Log::info("PayPal Webhook: Updated local subscription ID {$localSubscription->id} to status {$localSubscription->status}");
        } else {
            // Potentially create a new subscription if it's an activation and doesn't exist
            Log::warning("PayPal Webhook: Received update for unknown PayPal subscription ID: {$paypalSubscriptionResource->id}");
        }
    }

    protected function handleSubscriptionStateChange($paypalSubscriptionResource, string $newStatus)
    {
        $localSubscription = Subscription::where('gateway_subscription_id', $paypalSubscriptionResource->id)->first();
        if ($localSubscription) {
            $localSubscription->status = $newStatus;
            if (in_array($newStatus, ['cancelled', 'expired', 'suspended'])) {
                $localSubscription->cancelled_at = $localSubscription->cancelled_at ?? now();
            }
            $localSubscription->save();
            Log::info("PayPal Webhook: Changed local subscription ID {$localSubscription->id} to status {$newStatus}");
        }
    }

    protected function ensureSubscriptionActive(string $paypalSubscriptionId) {
        $localSubscription = Subscription::where('gateway_subscription_id', $paypalSubscriptionId)->first();
        if ($localSubscription && $localSubscription->status !== 'active') {
            // Potentially reactivate or log if a payment comes through for an inactive sub
            Log::info("PayPal Webhook: Payment received for subscription {$paypalSubscriptionId}, current local status: {$localSubscription->status}");
        }
    }
}


--- File: D:\projects\digitalvocano\Modules\PaypalGateway\Providers\EventServiceProvider.php ---
<?php

namespace Modules\PaypalGateway\Providers;

use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;

class EventServiceProvider extends ServiceProvider
{
    /**
     * The event handler mappings for the application.
     *
     * @var array<string, array<int, string>>
     */
    protected $listen = [];

    /**
     * Indicates if events should be discovered.
     *
     * @var bool
     */
    protected static $shouldDiscoverEvents = true;

    /**
     * Configure the proper event listeners for email verification.
     */
    protected function configureEmailVerification(): void {}
}


--- File: D:\projects\digitalvocano\Modules\PaypalGateway\Providers\PaypalGatewayServiceProvider.php ---
<?php

namespace Modules\PaypalGateway\Providers;

use Illuminate\Support\ServiceProvider;
use Modules\PaypalGateway\Services\PaypalService; // Import the service
use Illuminate\Support\Facades\Blade;
use Nwidart\Modules\Traits\PathNamespace;

class PaypalGatewayServiceProvider extends ServiceProvider
{
    use PathNamespace;
    /**
     * @var string $moduleName
     */
    protected string $moduleName = 'PaypalGateway';

    /**
     * @var string $moduleNameLower
     */
    protected string $moduleNameLower = 'paypalgateway';

    /**
     * Boot the application events.
     *
     * @return void
     */
    public function boot(): void
    {
        $this->registerTranslations();
        $this->registerConfig();
        $this->registerViews();
        $this->loadMigrationsFrom(module_path($this->moduleName, 'Database/Migrations'));
        // Route loading is handled by the main app's RouteServiceProvider loop for modules.
        // $this->loadRoutesFrom(module_path($this->moduleName, 'Routes/web.php'));
        // $this->loadRoutesFrom(module_path($this->moduleName, 'Routes/admin.php'));
        // $this->loadRoutesFrom(module_path($this->moduleName, 'Routes/api.php'));
    }

    /**
     * Register the service provider.
     *
     * @return void
     */

    public function register(): void
    {
        $this->app->register(RouteServiceProvider::class); // It's good practice to register this

        $this->app->singleton(PaypalService::class, function ($app) {
            return new PaypalService();
        });
    }

    protected function registerConfig(): void
    {
        $this->publishes([
            module_path($this->moduleName, 'config/config.php') => config_path($this->moduleNameLower . '.php'),
        ], 'config');
        $this->mergeConfigFrom(
            module_path($this->moduleName, 'config/config.php'), $this->moduleNameLower
        );
    }

    public function registerViews(): void
    {
        $viewPath = resource_path('views/modules/'.$this->moduleNameLower);
        $sourcePath = module_path($this->moduleName, 'resources/views');

        $this->publishes([$sourcePath => $viewPath], ['views', $this->moduleNameLower.'-module-views']);

        $this->loadViewsFrom(array_merge($this->getPublishableViewPaths(), [$sourcePath]), $this->moduleNameLower);

        Blade::componentNamespace(config('modules.namespace').'\\' . $this->moduleName . '\\View\\Components', $this->moduleNameLower);
    }

    public function registerTranslations(): void
    {
        $langPath = resource_path('lang/modules/' . $this->moduleNameLower);

        if (is_dir($langPath)) {
            $this->loadTranslationsFrom($langPath, $this->moduleNameLower);
            $this->loadJsonTranslationsFrom($langPath);
        } else {
            $this->loadTranslationsFrom(module_path($this->moduleName, 'lang'), $this->moduleNameLower);
            $this->loadJsonTranslationsFrom(module_path($this->moduleName, 'lang'));
        }
    }

    public function provides(): array
    {
        return [];
    }

    private function getPublishableViewPaths(): array
    {
        $paths = [];
        foreach (config('view.paths') as $path) {
            if (is_dir($path.'/modules/'.$this->moduleNameLower)) {
                $paths[] = $path.'/modules/'.$this->moduleNameLower;
            }
        }

        return $paths;
    }
}


--- File: D:\projects\digitalvocano\Modules\PaypalGateway\Providers\RouteServiceProvider.php ---
<?php

namespace Modules\PaypalGateway\Providers;

use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Route;


class RouteServiceProvider extends ServiceProvider
{
    protected string $name = 'PaypalGateway'; // Module name
    protected string $moduleNamespace = 'Modules\PaypalGateway\Http\Controllers';

    /**
     * Called before routes are registered.
     *
     * Register any model bindings or pattern based filters.
     */
    public function boot(): void
    {
        parent::boot();
    }

    /**
     * Define the routes for the application.
     */
    public function map(): void
    {
        $this->mapWebRoutes();
        // $this->mapAdminRoutes(); // Admin routes are loaded by the main app's RouteServiceProvider
        $this->mapApiRoutes();
    }

    /**
     * Define the "web" routes for the application.
     *
     * These routes all receive session state, CSRF protection, etc.
     */
    protected function mapWebRoutes(): void
    {
        // The main app's RouteServiceProvider already applies the 'web' middleware.
        // This ensures controllers in web.php are correctly namespaced.
        Route::middleware('web')
            ->namespace($this->moduleNamespace)
            ->group(module_path($this->name, '/routes/web.php'));
    }

    // protected function mapAdminRoutes(): void // This method is not needed as main RSP handles admin routes
    // {
        // The main app's RouteServiceProvider handles loading admin routes,
        // applying 'web' and 'IsAdminMiddleware', prefixing with 'admin/paypalgateway',
        // naming with 'admin.paypalgateway.', and namespacing to 'Modules\PaypalGateway\Http\Controllers\Admin'.
    // }

    /**
     * Define the "api" routes for the application.
     */
    protected function mapApiRoutes(): void
    {
        // The main app's RouteServiceProvider applies 'api' middleware,
        // 'api/paypalgateway' prefix, 'api.paypalgateway.' name prefix,
        // and the base 'Modules\PaypalGateway\Http\Controllers' namespace.
        Route::namespace($this->moduleNamespace) // Assuming API controllers are directly under Http/Controllers
            ->namespace($this->moduleNamespace) // Assuming API controllers are directly under Http/Controllers
            ->group(module_path($this->name, '/routes/api.php'));
    }
}


--- File: D:\projects\digitalvocano\Modules\PaypalGateway\resources\views\admin\form\toggle-switch.blade.php ---


--- File: D:\projects\digitalvocano\Modules\PaypalGateway\resources\views\admin\partials\alerts.blade.php ---


--- File: D:\projects\digitalvocano\Modules\PaypalGateway\resources\views\admin\config.blade.php ---
@extends('layouts.admin') {{-- Use your admin layout --}}

@section('title', 'PayPal Gateway Settings')
@section('header_title', 'PayPal Gateway Configuration')

@section('content')
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-xl sm:rounded-lg">
    <div class="p-6 lg:p-8">
        <h1 class="text-2xl font-medium text-gray-900 dark:text-white mb-6">
            PayPal Settings
        </h1>

        @include('admin.partials.alerts') {{-- For success/error messages --}}

        <form action="{{ route('admin.paypalgateway.settings.update') }}" method="POST">
            @csrf
            @method('PUT')

            <div class="space-y-6">
                <!-- Enable PayPal -->
                <div>
                    @php
                        $id = 'paypal_enabled';
                        $name = 'paypal_enabled';
                        $label = 'Enable PayPal Gateway';
                        $value = '1';
                        $checked = old('paypal_enabled', $settings['paypal_enabled'] ?? '0') == '1';
                        $helpText = 'Toggle this to activate or deactivate the PayPal payment gateway.';
                    @endphp
                    <label for="{{ $id }}" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">{{ $label }}</label>
                    <label class="relative inline-flex items-center cursor-pointer mt-1">
                        <input type="checkbox" id="{{ $id }}" name="{{ $name }}" class="sr-only peer" value="{{ $value }}" {{ $checked ? 'checked' : '' }}>
                        <div class="w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-blue-300 dark:peer-focus:ring-blue-800 rounded-full peer dark:bg-gray-700 peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all dark:border-gray-600 peer-checked:bg-blue-600"></div>
                    </label>
                    @if($helpText)
                        <p class="mt-1 text-xs text-gray-500 dark:text-gray-400">{{ $helpText }}</p>
                    @endif
                    @error($name) <p class="mt-2 text-sm text-red-600 dark:text-red-400">{{ $message }}</p> @enderror
                </div>

                <!-- PayPal Mode -->
                <div>
                    <label for="paypal_mode" class="block text-sm font-medium text-gray-700 dark:text-gray-300">PayPal Mode</label>
                    <select name="paypal_mode" id="paypal_mode" required
                            class="mt-1 block w-full border-gray-300 dark:border-gray-600 dark:bg-gray-700 dark:text-gray-300 focus:border-indigo-500 dark:focus:border-indigo-600 focus:ring-indigo-500 dark:focus:ring-indigo-600 rounded-md shadow-sm">
                        <option value="sandbox" {{ old('paypal_mode', $settings['paypal_mode'] ?? 'sandbox') == 'sandbox' ? 'selected' : '' }}>Sandbox (Testing)</option>
                        <option value="live" {{ old('paypal_mode', $settings['paypal_mode'] ?? '') == 'live' ? 'selected' : '' }}>Live (Production)</option>
                    </select>
                    @error('paypal_mode') <p class="mt-2 text-sm text-red-600 dark:text-red-400">{{ $message }}</p> @enderror
                </div>

                <!-- PayPal Client ID -->
                <div>
                    <label for="paypal_client_id" class="block text-sm font-medium text-gray-700 dark:text-gray-300">PayPal Client ID</label>
                    <input type="text" name="paypal_client_id" id="paypal_client_id" value="{{ old('paypal_client_id', $settings['paypal_client_id'] ?? '') }}"
                           class="mt-1 block w-full border-gray-300 dark:border-gray-600 dark:bg-gray-700 dark:text-gray-300 focus:border-indigo-500 dark:focus:border-indigo-600 focus:ring-indigo-500 dark:focus:ring-indigo-600 rounded-md shadow-sm">
                    @error('paypal_client_id') <p class="mt-2 text-sm text-red-600 dark:text-red-400">{{ $message }}</p> @enderror
                </div>

                <!-- PayPal Client Secret -->
                <div>
                    <label for="paypal_client_secret" class="block text-sm font-medium text-gray-700 dark:text-gray-300">PayPal Client Secret</label>
                    <input type="password" name="paypal_client_secret" id="paypal_client_secret" value="" {{-- Removed value for security --}}
                           class="mt-1 block w-full border-gray-300 dark:border-gray-600 dark:bg-gray-700 dark:text-gray-300 focus:border-indigo-500 dark:focus:border-indigo-600 focus:ring-indigo-500 dark:focus:ring-indigo-600 rounded-md shadow-sm"
                           placeholder="Leave blank to keep current secret">
                    <p class="text-xs text-gray-500 dark:text-gray-400 mt-1">Enter a new secret to update it. Leave blank to keep the existing one.</p>
                    @error('paypal_client_secret') <p class="mt-2 text-sm text-red-600 dark:text-red-400">{{ $message }}</p> @enderror
                </div>

                <!-- PayPal Webhook ID -->
                <div>
                    <label for="paypal_webhook_id" class="block text-sm font-medium text-gray-700 dark:text-gray-300">PayPal Webhook ID</label>
                    <input type="text" name="paypal_webhook_id" id="paypal_webhook_id" value="{{ old('paypal_webhook_id', $settings['paypal_webhook_id'] ?? '') }}"
                           class="mt-1 block w-full border-gray-300 dark:border-gray-600 dark:bg-gray-700 dark:text-gray-300 focus:border-indigo-500 dark:focus:border-indigo-600 focus:ring-indigo-500 dark:focus:ring-indigo-600 rounded-md shadow-sm">
                    <p class="text-xs text-gray-500 dark:text-gray-400 mt-1">Get this from your PayPal Developer Dashboard after creating a webhook.</p>
                    @error('paypal_webhook_id') <p class="mt-2 text-sm text-red-600 dark:text-red-400">{{ $message }}</p> @enderror
                </div>

            </div>

            <div class="mt-8 flex justify-end">
                <button type="submit"
                        class="inline-flex items-center px-4 py-2 bg-gray-800 dark:bg-gray-200 border border-transparent rounded-md font-semibold text-xs text-white dark:text-gray-800 uppercase tracking-widest hover:bg-gray-700 dark:hover:bg-white focus:bg-gray-700 dark:focus:bg-white active:bg-gray-900 dark:active:bg-gray-300 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 dark:focus:ring-offset-gray-800 transition ease-in-out duration-150">
                    Save Settings
                </button>
            </div>
        </form>
    </div>
</div>
@endsection


--- File: D:\projects\digitalvocano\Modules\PaypalGateway\resources\views\components\layouts\master.blade.php ---
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">

    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <meta name="csrf-token" content="{{ csrf_token() }}">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">

        <title>PaypalGateway Module - {{ config('app.name', 'Laravel') }}</title>

        <meta name="description" content="{{ $description ?? '' }}">
        <meta name="keywords" content="{{ $keywords ?? '' }}">
        <meta name="author" content="{{ $author ?? '' }}">

        <!-- Fonts -->
        <link rel="preconnect" href="https://fonts.bunny.net">
        <link href="https://fonts.bunny.net/css?family=figtree:400,500,600&display=swap" rel="stylesheet" />

        {{-- Vite CSS --}}
        {{-- {{ module_vite('build-paypalgateway', 'resources/assets/sass/app.scss') }} --}}
    </head>

    <body>
        {{ $slot }}

        {{-- Vite JS --}}
        {{-- {{ module_vite('build-paypalgateway', 'resources/assets/js/app.js') }} --}}
    </body>


--- File: D:\projects\digitalvocano\Modules\PaypalGateway\resources\views\index.blade.php ---
<x-paypalgateway::layouts.master>
    <h1>Hello World</h1>

    <p>Module: {!! config('paypalgateway.name') !!}</p>
</x-paypalgateway::layouts.master>


--- File: D:\projects\digitalvocano\Modules\PaypalGateway\routes\admin.php ---
<?php

use Illuminate\Support\Facades\Route;
use Modules\PaypalGateway\Http\Controllers\Admin\PaypalConfigController;
use App\Http\Middleware\IsAdminMiddleware; // Assuming this is your admin check middleware

Route::get('settings', [PaypalConfigController::class, 'edit'])->name('settings.edit');
Route::put('settings', [PaypalConfigController::class, 'update'])->name('settings.update');


--- File: D:\projects\digitalvocano\Modules\PaypalGateway\routes\api.php ---
<?php

use Illuminate\Support\Facades\Route;
use Modules\PaypalGateway\Http\Controllers\PaypalGatewayController;

Route::middleware(['auth:sanctum'])->prefix('v1')->group(function () {
    Route::apiResource('paypalgateways', PaypalGatewayController::class)->names('paypalgateway');
});


--- File: D:\projects\digitalvocano\Modules\PaypalGateway\routes\web.php ---
<?php

use Illuminate\Support\Facades\Route;
use Modules\PaypalGateway\Http\Controllers\PaypalSubscriptionController;
use Modules\PaypalGateway\Http\Controllers\PaypalWebhookController;

/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
*/

// Webhook - should generally be exempt from CSRF
Route::post('webhooks/paypal', [PaypalWebhookController::class, 'handleWebhook'])->name('webhooks.paypal.handle');

Route::middleware(['web', 'auth']) // User must be authenticated
     ->prefix('subscribe/paypal')
     ->name('subscription.paypal.')
     ->group(function () {
         // The {subscriptionPlan:slug} will use route model binding
         Route::post('create-agreement/{subscriptionPlan:slug}', [PaypalSubscriptionController::class, 'createSubscription'])->name('create.subscription');
         Route::get('execute-agreement', [PaypalSubscriptionController::class, 'executeSubscription'])->name('execute.subscription'); // Success
         Route::get('cancel-agreement', [PaypalSubscriptionController::class, 'cancelSubscriptionPage'])->name('cancel.subscription.page'); // User initiated cancel from PayPal
         Route::post('cancel-active', [PaypalSubscriptionController::class, 'cancelActiveSubscription'])->name('cancel.active'); // User cancels from app
     });

Route::middleware(['web', 'auth'])
    ->prefix('wallet/paypal')
    ->name('wallet.paypal.')
    ->group(function () {
        // The WalletController redirects to this route with amount in POST/GET
        Route::match(['get', 'post'], 'initialize-deposit', [PaypalSubscriptionController::class, 'initializeWalletDeposit'])->name('initializeDeposit');
        Route::get('deposit-callback', [PaypalSubscriptionController::class, 'handleWalletDepositCallback'])->name('depositCallback');
        Route::get('deposit-cancel', [PaypalSubscriptionController::class, 'handleWalletDepositCancel'])->name('depositCancel');
    });


--- File: D:\projects\digitalvocano\Modules\PaypalGateway\Services\PaypalService.php ---
<?php
namespace Modules\PaypalGateway\Services;

use App\Models\Subscription;
use App\Models\SubscriptionPlan;
use App\Models\User;
use Illuminate\Support\Facades\Log;
use PayPalCheckoutSdk\Core\PayPalHttpClient;
use PayPalCheckoutSdk\Core\SandboxEnvironment;
use PayPalCheckoutSdk\Core\ProductionEnvironment;
use PayPalCheckoutSdk\Orders\OrdersCreateRequest;
use PayPalCheckoutSdk\Orders\OrdersCaptureRequest;
// Subscription-specific request classes might not be in the installed paypal-checkout-sdk version
// We will construct these requests manually if needed.
// use PayPalCheckoutSdk\Subscriptions\SubscriptionsCreateRequest;
// use PayPalCheckoutSdk\Subscriptions\SubscriptionsGetRequest;
// use PayPalCheckoutSdk\Subscriptions\SubscriptionsCancelRequest;

use PayPalHttp\HttpException;
use PayPalHttp\HttpRequest; // For manually creating requests

class PaypalService
{
    protected $client;
    protected $clientId;
    protected $clientSecret;
    protected $mode;
    protected $webhookId;

    public function __construct()
    {
        if (!function_exists('setting')) {
            throw new \Exception("Settings helper function not found. Ensure it's globally available.");
        }

        $this->clientId = setting('paypal_client_id');
        $this->clientSecret = setting('paypal_client_secret');
        $this->mode = setting('paypal_mode', 'sandbox');
        $this->webhookId = setting('paypal_webhook_id');

        if ($this->clientId && $this->clientSecret) {
            Log::info("PayPalService initialized with Mode: {$this->mode}");
            $environment = ($this->mode === 'live')
                ? new ProductionEnvironment($this->clientId, $this->clientSecret)
                : new SandboxEnvironment($this->clientId, $this->clientSecret);
            $this->client = new PayPalHttpClient($environment);
        } else {
            Log::error('PayPalService not fully configured. Client ID or Secret might be missing.');
        }
    }

    protected function ensureClientIsAvailable()
    {
        if (!$this->client) {
            Log::error('PayPal client is not configured. Client ID or Secret might be missing.');
            throw new \Exception('PayPal client is not configured. Please set credentials in admin settings.');
        }
    }

    public function isConfigured(): bool
    {
        return !empty($this->clientId) && !empty($this->clientSecret) && $this->client !== null;
    }

    /**
     * Create a PayPal subscription.
     *
     * @param User $user
     * @param SubscriptionPlan $localPlan
     * @return string|null The approval link or null on failure
     * @throws \Exception
     */
    public function createPaypalSubscription(User $user, SubscriptionPlan $localPlan): ?string
    {
        $this->ensureClientIsAvailable();

        if (empty($localPlan->paypal_plan_id)) {
            Log::error("PayPal Plan ID is missing for local plan ID: {$localPlan->id} ({$localPlan->name})");
            throw new \Exception("PayPal Plan ID is not configured for the '{$localPlan->name}' subscription plan.");
        }

        // Manually construct the request for creating a subscription
        // Endpoint: /v1/billing/subscriptions
        $request = new HttpRequest("/v1/billing/subscriptions", "POST");
        $request->headers["Content-Type"] = "application/json";
        $request->headers["Prefer"] = "return=representation"; // To get the full resource representation back

        // Set the request body
        $request->body = [
            'plan_id' => $localPlan->paypal_plan_id,
            'subscriber' => [
                'name' => [
                    // More robust name splitting if first_name/last_name are not separate attributes
                    'given_name' => $user->first_name ?? (explode(' ', $user->name, 2)[0] ?? $user->name),
                    'surname' => $user->last_name ?? (explode(' ', $user->name, 2)[1] ?? ''), // Use empty string if no surname part
                ],
                'email_address' => $user->email,
            ],
            'application_context' => [
                'brand_name' => config('app.name'),
                'locale' => 'en-US',
                'shipping_preference' => 'NO_SHIPPING',
                'user_action' => 'SUBSCRIBE_NOW',
                'return_url' => route('subscription.paypal.execute.subscription'),
                'cancel_url' => route('subscription.paypal.cancel.subscription.page'),
            ],
        ];

        try {
            $response = $this->client->execute($request);
            if ($response->statusCode == 201 && isset($response->result->id)) { // 201 Created
                foreach ($response->result->links as $link) {
                    if ($link->rel === 'approve') {
                        session(['paypal_subscription_id_pending' => $response->result->id]);
                        session(['local_plan_id_pending' => $localPlan->id]); // Optional: if needed on return
                        return $link->href;
                    }
                }
            }
            Log::error("PayPal Subscription Create: No approval link found or unexpected status.", (array)$response);
        } catch (HttpException $e) {
            $debugId = $e->getHeaders()['PayPal-Debug-Id'] ?? 'N/A';
            $errorDetails = json_decode($e->getMessage(), true); // Attempt to get structured error
            Log::error("PayPal Subscription Create Error - Debug ID: {$debugId}", [
                'message' => $errorDetails['message'] ?? $e->getMessage(),
                'details' => $errorDetails['details'] ?? $errorDetails ?? $e->getMessage(),
                'statusCode' => $e->statusCode
            ]);
            throw new \Exception("PayPal API Error during subscription creation: " . ($errorDetails['message'] ?? $e->getMessage()));
        }
        return null;
    }

    /**
     * Get details of a PayPal subscription.
     *
     * @param string $paypalSubscriptionId
     * @return \stdClass|null
     * @throws \Exception
     */
    public function getPaypalSubscriptionDetails(string $paypalSubscriptionId): ?\stdClass
    {
        $this->ensureClientIsAvailable();
        // Manually construct the request for getting subscription details
        // Endpoint: /v1/billing/subscriptions/{id}
        $request = new HttpRequest("/v1/billing/subscriptions/" . $paypalSubscriptionId, "GET");
        $request->headers["Content-Type"] = "application/json";

        // $request = new SubscriptionsGetRequest($paypalSubscriptionId); // If the class existed

        try {
            $response = $this->client->execute($request);
            return $response->result;
        } catch (HttpException $e) {
            $debugId = $e->getHeaders()['PayPal-Debug-Id'] ?? 'N/A';
            $errorDetails = json_decode($e->getMessage(), true);
            Log::error("PayPal Get Subscription Details Error for ID {$paypalSubscriptionId} - Debug ID: {$debugId}", [
                'message' => $errorDetails['message'] ?? $e->getMessage(),
                'details' => $errorDetails['details'] ?? $errorDetails ?? $e->getMessage(),
                'statusCode' => $e->statusCode
            ]);
            return null;
        }
    }

    /**
     * Cancel a PayPal subscription at the gateway.
     *
     * @param Subscription $localSubscription
     * @return bool
     * @throws \Exception
     */
    public function cancelSubscriptionAtGateway(Subscription $localSubscription): bool
    {
        $this->ensureClientIsAvailable();
        if (!$localSubscription->gateway_subscription_id) {
            Log::error("Cannot cancel PayPal subscription for local subscription ID {$localSubscription->id}: missing gateway_subscription_id.");
            return false;
        }

        // Manually construct the request for cancelling a subscription
        // Endpoint: /v1/billing/subscriptions/{id}/cancel
        $request = new HttpRequest("/v1/billing/subscriptions/" . $localSubscription->gateway_subscription_id . "/cancel", "POST");
        $request->headers["Content-Type"] = "application/json";
        $request->body = ['reason' => 'Subscription cancelled by user from application.'];

        // $request = new SubscriptionsCancelRequest($localSubscription->gateway_subscription_id); // If the class existed

        try {
            $response = $this->client->execute($request);
            // PayPal returns 204 No Content on success
            if ($response->statusCode == 204) {
                Log::info("PayPal subscription {$localSubscription->gateway_subscription_id} cancelled successfully at gateway.");
                return true;
            }
            Log::warning("PayPal subscription cancellation for {$localSubscription->gateway_subscription_id} returned status: {$response->statusCode}");
            return false;
        } catch (HttpException $e) {
            $debugId = $e->getHeaders()['PayPal-Debug-Id'] ?? 'N/A';
            $errorDetails = json_decode($e->getMessage(), true);
            Log::error("Error cancelling PayPal subscription {$localSubscription->gateway_subscription_id} - Debug ID: {$debugId}", [
                'message' => $errorDetails['message'] ?? $e->getMessage(),
                'details' => $errorDetails['details'] ?? $errorDetails ?? $e->getMessage(),
                'statusCode' => $e->statusCode
            ]);
            return false;
        }
    }

    /**
     * Create a PayPal order for a one-time wallet deposit.
     *
     * @param \App\Models\User $user
     * @param float $amount
     * @param string $currency
     * @return \PayPalHttp\HttpResponse|null
     * @throws \PayPalHttp\IOException
     * @throws \Exception
     */
    public function createWalletDepositOrder(\App\Models\User $user, float $amount, string $currency = 'USD')
    {
        $this->ensureClientIsAvailable();

        $request = new OrdersCreateRequest();
        $request->prefer('return=representation');
        $request->body = [
            'intent' => 'CAPTURE',
            'application_context' => [
                'return_url' => route('wallet.paypal.depositCallback'),
                'cancel_url' => route('wallet.paypal.depositCancel'),
                'brand_name' => config('app.name'),
                'user_action' => 'PAY_NOW',
            ],
            'purchase_units' => [[
                'description' => 'Wallet Deposit for ' . config('app.name'),
                'amount' => [
                    'currency_code' => strtoupper($currency),
                    'value' => number_format($amount, 2, '.', ''),
                ],
                'custom_id' => 'DEPOSIT-' . $user->id . '-' . time(), // Custom ID for tracking
            ]],
        ];

        try {
            return $this->client->execute($request);
        } catch (HttpException $e) {
            $debugId = $e->getHeaders()['PayPal-Debug-Id'] ?? 'N/A';
            $errorDetails = json_decode($e->getMessage(), true);
            Log::error('PayPal Create Wallet Deposit Order Error - Debug ID: ' . $debugId, [
                'message' => $errorDetails['message'] ?? $e->getMessage(),
                'details' => $errorDetails['details'] ?? $errorDetails ?? $e->getMessage(),
                'statusCode' => $e->statusCode
            ]);
            throw $e;
        }
    }

    /**
     * Capture a PayPal order for a wallet deposit.
     *
     * @param string $paypalOrderId
     * @return \PayPalHttp\HttpResponse|null
     * @throws \PayPalHttp\IOException
     * @throws \Exception
     */
    public function captureWalletDepositOrder(string $paypalOrderId)
    {
        $this->ensureClientIsAvailable();
        $request = new OrdersCaptureRequest($paypalOrderId);
        $request->prefer('return=representation');
        
        try {
            return $this->client->execute($request);
        } catch (HttpException $e) {
            $debugId = $e->getHeaders()['PayPal-Debug-Id'] ?? 'N/A';
            $errorDetails = json_decode($e->getMessage(), true);
            Log::error('PayPal Capture Wallet Deposit Order Error for Order ID ' . $paypalOrderId . ' - Debug ID: ' . $debugId, [
                'message' => $errorDetails['message'] ?? $e->getMessage(),
                'details' => $errorDetails['details'] ?? $errorDetails ?? $e->getMessage(),
                'statusCode' => $e->statusCode
            ]);
            throw $e;
        }
    }

    /**
     * Verify PayPal Webhook Signature.
     *
     * @param array $headers All request headers
     * @param string $rawBody The raw request body
     * @return bool
     * @throws \Exception
     */
    public function verifyWebhookSignature(array $headers, string $rawBody): bool
    {
        $this->ensureClientIsAvailable();
        if (!$this->webhookId) {
            Log::error('PayPal Webhook ID is not configured. Cannot verify signature.');
            return false;
        }

        // Extract necessary headers. Ensure they are lowercase as Laravel might convert them.
        $transmissionId = $headers['paypal-transmission-id'][0] ?? null;
        $transmissionTime = $headers['paypal-transmission-time'][0] ?? null;
        $certUrl = $headers['paypal-cert-url'][0] ?? null;
        $authAlgo = $headers['paypal-auth-algo'][0] ?? null;
        $transmissionSig = $headers['paypal-transmission-sig'][0] ?? null;

        if (!$transmissionId || !$transmissionTime || !$certUrl || !$authAlgo || !$transmissionSig) {
            Log::error('PayPal Webhook: Missing required headers for signature verification.');
            return false;
        }

        // IMPORTANT: The PayPal PHP SDK v2 does not include a helper method for webhook signature verification.
        // You MUST implement this by making an API call to PayPal's /v1/notifications/verify-webhook-signature endpoint.
        // Refer to PayPal documentation: https://developer.paypal.com/docs/api/webhooks/v1/#verify-webhook-signature_post

        $payload = [
            'auth_algo' => $authAlgo,
            'cert_url' => $certUrl,
            'transmission_id' => $transmissionId,
            'transmission_sig' => $transmissionSig,
            'transmission_time' => $transmissionTime,
            'webhook_id' => $this->webhookId,
            'webhook_event' => json_decode($rawBody) // PayPal expects the event body as a JSON object
        ];

        // You'll need to use an HTTP client (like Guzzle, or Laravel's Http facade if available globally)
        // to make a POST request to the PayPal API endpoint for verification.
        // Example using a conceptual HTTP client:
        /*
        try {
            $environment = ($this->mode === 'live')
                ? new ProductionEnvironment($this->clientId, $this->clientSecret)
                : new SandboxEnvironment($this->clientId, $this->clientSecret);
            $verifyUrl = $environment->baseUrl() . '/v1/notifications/verify-webhook-signature'; // Construct the full URL

            // $httpClient = new \GuzzleHttp\Client(); // Or your preferred HTTP client
            // $response = $httpClient->post($verifyUrl, [
            //     'headers' => ['Content-Type' => 'application/json', 'Authorization' => 'Bearer YOUR_ACCESS_TOKEN'], // You need an access token
            //     'json' => $payload
            // ]);
            // $responseData = json_decode($response->getBody()->getContents(), true);
            // return isset($responseData['verification_status']) && $responseData['verification_status'] === 'SUCCESS';
        } catch (\Exception $e) {
            Log::error('PayPal Webhook: Error during signature verification API call.', ['error' => $e->getMessage()]);
            return false;
        }
        */

        Log::critical('PayPal Webhook Signature Verification: CRITICAL - Full cryptographic verification via API call is NOT YET IMPLEMENTED. This is a major security risk in production. The current check is INSECURE.');
        // Returning false by default to highlight that this needs proper implementation.
        // For development, you might temporarily return true AFTER understanding the risks.
        return false; // MUST BE REPLACED WITH ACTUAL VERIFICATION
    }
}


