# fluent-cart/1.6.4/app/Http/Controllers/FrontendControllers/CustomerProfileController.php

FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler, version 1.6.4. 631 lines.

- Page: https://pluginprobe.com/plugins/fluent-cart/1.6.4/code/app/Http/Controllers/FrontendControllers/CustomerProfileController.php
- Raw: https://pluginprobe.com/plugins/fluent-cart/1.6.4/raw/app/Http/Controllers/FrontendControllers/CustomerProfileController.php
- Modified: 2026-08-20T12:34:14+00:00

Line numbers below start at 1. Link to a line or a range by appending a fragment to the
page URL, for example `https://pluginprobe.com/plugins/fluent-cart/1.6.4/code/app/Http/Controllers/FrontendControllers/CustomerProfileController.php#L10-L20`.

```php
<?php

namespace FluentCart\App\Http\Controllers\FrontendControllers;

//use FluentCart\Api\Resource\FrontendResource\CustomerResource;
use FluentCart\Api\Resource\CustomerAddressResource;
use FluentCart\Api\Resource\CustomerResource;
use FluentCart\Api\Resource\FrontendResource\OrderResource;
use FluentCart\Api\Resource\OrderDownloadPermissionResource;
use FluentCart\App\App;
use FluentCart\App\Helpers\Helper;
use FluentCart\App\Helpers\Status;
use FluentCart\App\Http\Controllers\Controller;
use FluentCart\App\Http\Requests\FrontendRequests\CustomerRequests\CustomerProfileAccountDetailsRequest;
use FluentCart\App\Http\Requests\FrontendRequests\CustomerRequests\CustomerProfileRequest;
use FluentCart\App\Models\Customer;
use FluentCart\App\Models\CustomerAddresses;
use FluentCart\App\Models\Meta;
use FluentCart\App\Models\Order;
use FluentCart\App\Models\OrderItem;
use FluentCart\App\Models\ProductDownload;
use FluentCart\App\Models\ProductVariation;
use FluentCart\App\Models\Subscription;
use FluentCart\App\Services\FileSystem\FileManager;
use FluentCart\App\Services\FrontendView;
use FluentCart\App\Services\Payments\PaymentHelper;
use FluentCart\App\Services\PlanUpgradeService;
use FluentCart\App\Services\URL;
use FluentCart\Framework\Database\Orm\Builder;
use FluentCart\Framework\Database\Orm\Relations\HasMany;
use FluentCart\Framework\Http\Request\Request;
use FluentCart\Framework\Support\Arr;
use FluentCart\Framework\Support\Collection;
use FluentCartPro\App\Hooks\Handlers\UpgradeHandler;
use FluentCartPro\App\Modules\Licensing\Models\License;
use FluentCartPro\App\Modules\Licensing\Models\LicenseActivation;
use FluentCartPro\App\Modules\Licensing\Services\LicenseHelper;


class CustomerProfileController extends BaseFrontendController
{
    /**
     * Portal surfaces an add-on may attach a section to. Each value maps to the
     * hook `fluent_cart/customer_portal/{value}`.
     */
    const PORTAL_SECTION_FILTERS = [
        'profile_sections'
    ];

    /**
     * Handle the request to retrieve the customer's orders.
     *
     * This method checks if the user is logged in, retrieves the current customer,
     * and searches for their orders based on the provided search parameters.
     *
     * @param Request $request The incoming HTTP request.
     * @return array
     */
    public function index(Request $request)
    {
        $customer = CustomerResource::getCurrentCustomer();


        if (!$customer) {
            return apply_filters('fluent_cart/customer_dashboard_data', [
                'message'        => __('Success', 'fluent-cart'),
                'dashboard_data' => [
                    'orders' => []
                ],
                'sections_parts' => [
                    'before_orders_table' => '',
                    'after_orders_table'  => ''
                ]
            ], [
                'customer' => null
            ]);
        }


        $orders = Order::query()
            ->with(['order_items' => function ($query) {
                $query->select('id', 'order_id', 'object_id', 'post_title', 'title', 'quantity', 'payment_type', 'line_meta', 'other_info');
            }])
            ->where('customer_id', $customer->id)
            ->where(function ($query) {
                $query
                    ->where(function ($query) {
                        $query->where('parent_id', '')->orWhereNull('parent_id');
                    })
                    ->orWhere('type', '!=', 'renewal');
            })
            ->withCount('renewals')
            ->orderBy('created_at', 'DESC')
            ->limit(5)
            ->get();

        $orders = $orders->map(function ($order) {
            return [
                'created_at'     => $order->created_at->format('Y-m-d H:i:s'),
                'invoice_no'     => $order->invoice_no,
                'total_amount'   => $order->total_amount,
                'currency'       => $order->currency,
                'uuid'           => $order->uuid,
                'type'           => $order->type,
                'status'         => $order->status,
                'renewals_count' => $order->renewals_count,
                'order_items'    => $order->order_items->map(function ($item) {
                    return [
                        'id'                 => $item->id,
                        'post_title'         => $item->post_title,
                        'title'              => $item->title,
                        'variation_display_title'    => $item->variation_display_title,
                        'quantity'           => $item->quantity,
                        'payment_type'       => $item->payment_type,
                        'line_meta'          => [
                            'bundle_parent_item_id' => Arr::get($item, 'line_meta.bundle_parent_item_id', null),
                        ]
                    ];
                }),
            ];
        });

        return apply_filters('fluent_cart/customer_dashboard_data', [
            'message'        => __('Success', 'fluent-cart'),
            'dashboard_data' => [
                'orders' => $orders
            ],
            'sections_parts' => [
                'before_orders_table' => '',
                'after_orders_table'  => ''
            ]
        ], [
            'customer' => $customer
        ]);
    }

    public function getCustomerProfileDetails()
    {
        // Get the current logged-in customer
        $customer = CustomerResource::getCurrentCustomer();

        if (!$customer) {
            // get current user by id
            $userId = get_current_user_id();
            $currentUser = get_user_by('ID', $userId);

            // get user first_name and last_name from usermeta
            $currentUser->data->first_name = get_user_meta($userId, 'first_name', true);
            $currentUser->data->last_name = get_user_meta($userId, 'last_name', true);


            return $this->sendSuccess([
                'message' => __('Success', 'fluent-cart'),
                'data'    => [
                    'first_name'       => $currentUser->data->first_name,
                    'last_name'        => $currentUser->data->last_name,
                    'user_login'       => $currentUser->data->user_login,
                    'user_email'       => $currentUser->data->user_email,
                    'email'            => $currentUser->data->user_email,
                    'user_nicename'    => $currentUser->data->user_nicename,
                    'display_name'     => $currentUser->data->display_name,
                    'billing_address'  => [],
                    'shipping_address' => [],
                    'not_a_customer'   => true
                ]
            ]);
        }

        // Fetch the customer along with the related WordPress user
        $customerData = Customer::query()
            ->where('id', $customer->id)
            ->with(['billing_address', 'shipping_address'])
            ->first()
            ->toArray();

        $userData = Arr::only($customerData, [
            'first_name',
            'last_name',
            'email',
            'billing_address',
            'shipping_address'
        ]);
        // Combine the customer and WordPress user data in the response
        return $this->sendSuccess([
            'message' => __('Success', 'fluent-cart'),
            'data'    => $userData
        ]);
    }

    /**
     * Return the add-on sections registered for one customer-portal surface.
     *
     * Each entry carries an UNCOMPILED Vue component string plus its payload;
     * the portal SPA compiles it in the browser (see the shared
     * DynamicTemplateParser). This is how an add-on renders inside the SPA at
     * all — the router's route table is static and cannot be extended.
     *
     * @param Request $request
     * @return \WP_REST_Response
     */
    public function getSections(Request $request): \WP_REST_Response
    {
        // `?filter[]=x` makes this an array, and casting one to string emits a
        // warning a caller could raise at will.
        $requestedFilter = $request->get('filter', '');
        $filter = is_string($requestedFilter) ? sanitize_text_field($requestedFilter) : '';

        // Allowlisted, never interpolated from the raw request value: building
        // a hook name out of caller input would let anyone fire arbitrary
        // filters through this endpoint.
        if (!in_array($filter, self::PORTAL_SECTION_FILTERS, true)) {
            return $this->sendError([
                'message' => __('Unknown portal section group.', 'fluent-cart')
            ], 422);
        }

        $customer = CustomerResource::getCurrentCustomer();

        // A logged-in WP user who has never bought anything is not a customer.
        // Short-circuit rather than firing the filter with a null customer,
        // so no add-on has to remember to handle that case correctly.
        if (!$customer) {
            return $this->sendSuccess([
                'message'  => __('Success', 'fluent-cart'),
                'sections' => []
            ]);
        }

        $sections = apply_filters('fluent_cart/customer_portal/' . $filter, [], [
            'customer' => $customer
        ]);

        return $this->sendSuccess([
            'message'  => __('Success', 'fluent-cart'),
            'sections' => $this->formatPortalSections($sections)
        ]);
    }

    /**
     * Normalise whatever add-ons returned into the shape the SPA renders, and
     * drop entries with nothing to compile.
     *
     * @param mixed $sections
     * @return array
     */
    private function formatPortalSections($sections): array
    {
        if (!is_array($sections)) {
            return [];
        }

        $formatted = [];

        foreach ($sections as $sectionKey => $section) {
            if (!is_array($section) || empty($section['component'])) {
                continue;
            }

            $key = Arr::get($section, 'key', $sectionKey);
            $type = Arr::get($section, 'type', 'vue-template');

            // sanitize_key()/sanitize_text_field() are scalar-only and throw on
            // an array in PHP 8. A malformed add-on entry should drop out here,
            // not 500 the whole endpoint.
            if (!is_scalar($section['component']) || !is_scalar($key) || !is_scalar($type)) {
                continue;
            }

            $formatted[] = [
                'key'       => sanitize_key($key),
                'type'      => sanitize_text_field($type),
                'component' => (string)$section['component'],
                'payload'   => Arr::get($section, 'payload', [])
            ];
        }

        return $formatted;
    }

    public function updateCustomerProfileDetails(CustomerProfileAccountDetailsRequest $request): \WP_REST_Response
    {
        $errorResponse = $this->checkUserLoggedIn();

        // Check if there is an error response and return it if exists
        if ($errorResponse !== null) {
            return $errorResponse;
        }
        // Get the current logged-in customer
        $customer = CustomerResource::getCurrentCustomer();

        if (!$customer) {
            return $this->sendError([
                'message' => __('Customer not found', 'fluent-cart')
            ]);
        }

        // Validate the request data for customer and addresses
        $validatedData = $request->getSafe($request->sanitize());
        $firstName = Arr::get($validatedData, 'first_name');
        $lastName = Arr::get($validatedData, 'last_name');

        // Update the customer with the validated data
        $customer->first_name = $firstName;
        $customer->last_name = $lastName;
        $customerUpdate = $customer->save();


        if (is_wp_error($customerUpdate)) {
            return $this->sendError([
                'message' => $customerUpdate->get_error_message()
            ]);
        }

        // Also update the WordPress user profile
        $name = trim($firstName . ' ' . $lastName);
        $wpResult = wp_update_user([
            'ID'           => $customer->user_id,
            'first_name'   => $firstName,
            'last_name'    => $lastName,
            'display_name' => $name,
        ]);

        if (is_wp_error($wpResult)) {
            return $this->sendError([
                'message' => $wpResult->get_error_message()
            ]);
        }

        return $this->sendSuccess([
            'message' => __('Profile updated successfully', 'fluent-cart'),
        ]);
    }

    /**
     * Helper method to validate email uniqueness
     */
    private function validateEmailUniqueness($email, $currentCustomerId): ?\WP_REST_Response
    {
        $customerId = $currentCustomerId;
        $userId = Customer::query()->find($customerId)->user_id;

        // Fetch the current customer's email to compare
        $currentEmail = Customer::query()->find($customerId)->email;

        // Check if the email is different from the current user's email and exists in WordPress users table
        if ($email !== $currentEmail) {
            // Check if the email exists in the WordPress users table
            $emailExistsInWp = get_user_by('email', $email);

            if ($emailExistsInWp && $emailExistsInWp->ID !== $userId) {
                return $this->sendError([
                    'message' => __('The email address is already in use in WordPress users.', 'fluent-cart')
                ]);
            }
        }

        // Check if the email exists in the Customer table, excluding the current customer
        $emailExistsInCustomer = Customer::query()->where('email', $email)->where('id', '!=', $customerId)->exists();

        if ($emailExistsInCustomer) {
            return $this->sendError([
                'message' => __('The email address is already in use in the customer records.', 'fluent-cart')
            ]);
        }

        return null; // Return null mean email is unique
    }

    public function createCustomerProfileAddress(CustomerProfileRequest $request): \WP_REST_Response
    {
        // Get the current logged-in customer
        $customer = CustomerResource::getCurrentCustomer(true);

        // Sanitize and retrieve the request data
        $data = CustomerAddressResource::normalizeBusinessFields($request->getSafe($request->sanitize()));

        // Attempt to create a new address for the logged-in customer
        $isCreated = CustomerAddressResource::create(
            $data,
            ['id' => $customer->id]
        );

        // Check if there was an error during the creation process, return the error if one occurred
        if (is_wp_error($isCreated)) {
            return $this->sendError([
                'message' => $isCreated->get_error_message()
            ]);
        }

        // Return the success response
        return $this->response->sendSuccess($isCreated);

    }

    public function updateCustomerProfileAddress(CustomerProfileRequest $request): \WP_REST_Response
    {
        // Call the method and store the response
        $errorResponse = $this->checkUserLoggedIn();

        // Check if there is an error response and return it if exists
        if ($errorResponse !== null) {
            return $errorResponse;
        }

        // Get the current logged-in customer
        $customer = CustomerResource::getCurrentCustomer();

        // Sanitize and retrieve the request data
        $data = CustomerAddressResource::normalizeBusinessFields($request->getSafe($request->sanitize()));

        // Retrieve the address ID from the request
        $id = $request->getSafe('id', 'intval');


        $address = CustomerAddresses::query()->findOrFail($id);

        if ($address->customer_id != $customer->id) {
            return $this->sendError([
                'message' => __('You are not authorized to update this address', 'fluent-cart')
            ]);
        }

        // Proceed with the update since IDs match
        $isUpdated = CustomerAddressResource::update($data, $id);

        // Check for errors during the update process
        if (is_wp_error($isUpdated)) {
            return $this->sendError([
                'message' => $isUpdated->get_error_message()
            ]);
        }

        // Return the success response
        return $this->response->sendSuccess($isUpdated);

    }

    public function makePrimaryCustomerProfileAddress(Request $request): \WP_REST_Response
    {
        // Call the method and store the response
        $errorResponse = $this->checkUserLoggedIn();
        // Check if there is an error response and return it if exists
        if ($errorResponse !== null) {
            // Return error if user is not logged in
            return $errorResponse;
        }

        $customer = CustomerResource::getCurrentCustomer();

        $id = $request->getSafe('addressId', 'intval');


        $address = CustomerAddresses::query()->findOrFail($id);

        if ($address->customer_id != $customer->id) {
            return $this->sendError([
                'message' => __('You are not authorized to update this address', 'fluent-cart')
            ]);
        }

        $isUpdated = CustomerAddressResource::makePrimary(
            $customer->id,
            $request->getSafe('addressId', 'intval'),
            $request->getSafe('type', 'sanitize_text_field')
        );

        // Check for errors during the update process
        if (is_wp_error($isUpdated)) {
            return $this->sendError([
                'message' => $isUpdated->get_error_message()
            ]);
        }

        // Return the success response
        return $this->response->sendSuccess($isUpdated);
    }

    public function deleteCustomerProfileAddress(Request $request)
    {
        // Call the method and store the response
        $errorResponse = $this->checkUserLoggedIn();
        if ($errorResponse !== null) {
            return $errorResponse;
        }

        $id = $request->getSafe('addressId', 'intval');
        if (!$id) {
            return $this->sendError([
                'message' => __('Address ID is required', 'fluent-cart')
            ]);
        }

        $customer = CustomerResource::getCurrentCustomer();

        $address = CustomerAddresses::query()->findOrFail($id);

        if ($address->customer_id != $customer->id) {
            return $this->sendError([
                'message' => __('You are not authorized to update this address', 'fluent-cart')
            ]);
        }

        $isDeleted = CustomerAddressResource::delete($id);

        if (is_wp_error($isDeleted)) {
            return $this->sendError([
                'message' => $isDeleted->get_error_message()
            ]);
        }
        return $this->response->sendSuccess($isDeleted);
    }

    public function getDownloads(Request $request): \WP_REST_Response
    {
        $page = $request->get('page', 1);
        $perPage = $request->get('per_page', 10);

        $errorResponse = $this->checkUserLoggedIn();

        if ($errorResponse !== null) {
            return $this->sendSuccess([
                'message'      => __('Success', 'fluent-cart'),
                'data'         => [],
                'total'        => 0,
                'per_page'     => $perPage,
                'current_page' => $page,
                'last_page'    => 1
            ]);
        }

        $customer = CustomerResource::getCurrentCustomer();

        $orderItems = OrderItem::query()
            ->with('variants')
            ->withWhereHas('order', function ($query) use ($customer) {
                $query->where('customer_id', $customer->id)
                    ->where(function (Builder $query) {
                        $query->whereIn('payment_status', Status::getOrderPaymentSuccessStatuses());
                    });
            })
            ->whereHas('product_downloads')
            ->get();

        $productIds = $orderItems->pluck('post_id')->unique()->values();
        $orders = $orderItems->pluck('order');

        // Extract all unique variation IDs from the customer's orders
        $variationIds = $orders->pluck('order_items')
            ->flatten()
            ->pluck('variants.id')
            ->filter()
            ->unique()
            ->values();

        $downloads = ProductDownload::query()->whereIn('post_id', $productIds)->get()->filter(function ($download) use ($variationIds) {
            $ids = $download->product_variation_id;
            return empty($ids) || array_intersect($variationIds->toArray(), $ids);
        });

        $orderIdMapByPostID = [];
        foreach ($orderItems as $orderItem) {
            if (!isset($orderIdMapByPostID[$orderItem->post_id])) {
                $orderIdMapByPostID[$orderItem->post_id] = [];
            }
            $orderIdMapByPostID[$orderItem->post_id][] = $orderItem->order_id;
        }

        $total = $downloads->count();
        $paginated = $downloads->forPage($page, $perPage)->values();


        $data = $paginated->map(function ($download) use ($orderIdMapByPostID) {
            return [
                'file_size'    => $download->file_size,
                'title'        => $download->title,
                'download_url' => Helper::generateDownloadFileLink(
                    $download,
                    Arr::get($orderIdMapByPostID, $download->post_id)
                ),
            ];
        })->values();

        return $this->sendSuccess([
            'message'   => __('Success', 'fluent-cart'),
            'downloads' => [
                'data'         => $data,
                'total'        => $total,
                'per_page'     => $perPage,
                'current_page' => $page,
                'last_page'    => (int)ceil($total / $perPage),
            ]
        ]);
    }

    /*
     * Get upgradable paths for a given variation
    */
    public function getUpgradePaths(Request $request, $orderHash)
    {

        $currentCustomer = CustomerResource::getCurrentCustomer();
        if (!$currentCustomer) {
            return $this->sendError([
                'message' => __('You must be logged in to view upgrade paths.', 'fluent-cart')
            ]);
        }

        $order = Order::query()->where('uuid', $orderHash)
            ->where('customer_id', $currentCustomer->id)
            ->first();

        if (!$order) {
            return $this->sendError([
                'message' => __('Order not found or you do not have permission to view it.', 'fluent-cart')
            ]);
        }

        $variationId = $request->get('variation_id');
        if (!$variationId) {
            return [];
        }

        $upgradePaths = PlanUpgradeService::getUpgardePathsFromVariation($variationId, $orderHash);

        return $this->sendSuccess([
            'upgradePaths' => $upgradePaths
        ]);
    }

}

```
