# mstore-api/trunk/controllers/flutter-user.php

MStore API – Create Native Android &amp; iOS Apps On The Cloud, version trunk. 2,053 lines.

- Page: https://pluginprobe.com/plugins/mstore-api/trunk/code/controllers/flutter-user.php
- Raw: https://pluginprobe.com/plugins/mstore-api/trunk/raw/controllers/flutter-user.php
- Modified: 2026-08-20T08:36:54+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/mstore-api/trunk/code/controllers/flutter-user.php#L10-L20`.

```php
<?php

if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

require_once(__DIR__ . '/flutter-base.php');
require_once(__DIR__ . '/helpers/apple-sign-in-helper.php');
require_once(__DIR__ . '/helpers/facebook-jwt-helper.php');
require_once(__DIR__ . '/helpers/firebase-phone-auth-helper.php');

class FlutterUserController extends FlutterBaseController
{
    private function mstore_get_network_site_name() {
        if ( function_exists( 'get_network' ) ) {
            $network = get_network();
            if ( $network && isset( $network->site_name ) ) {
                return $network->site_name;
            }
        }

        return wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES );
    }

    private $allowed_profile_meta_keys = array(
        'billing_first_name',
        'billing_last_name',
        'billing_company',
        'billing_address_1',
        'billing_address_2',
        'billing_city',
        'billing_state',
        'billing_postcode',
        'billing_country',
        'billing_email',
        'billing_phone',
        'shipping_first_name',
        'shipping_last_name',
        'shipping_company',
        'shipping_address_1',
        'shipping_address_2',
        'shipping_city',
        'shipping_state',
        'shipping_postcode',
        'shipping_country',
        'shipping_email',
        'shipping_phone',
    );

    /**
     * Endpoint namespace
     *
     * @var string
     */
    protected $namespace = 'api/flutter_user';

    public function __construct() {}

    private function sanitize_profile_text($value)
    {
        if (is_scalar($value)) {
            return sanitize_text_field(wp_unslash((string)$value));
        }

        return '';
    }

    private function sanitize_profile_meta_value($meta_key, $value)
    {
        if (!is_scalar($value)) {
            return '';
        }

        $value = wp_unslash((string)$value);

        switch ($meta_key) {
            case 'billing_email':
            case 'shipping_email':
                return sanitize_email($value);
            case 'billing_postcode':
            case 'shipping_postcode':
                return wc_clean($value);
            default:
                return sanitize_text_field($value);
        }
    }

    /// Jetpack -> Settings -> Security -> Account Protection
    private function is_jetpack_account_protection_enabled()
    {
        if (!class_exists('Automattic\\Jetpack\\Account_Protection\\Account_Protection')) {
            return false;
        }

        try {
            $account_protection = Automattic\Jetpack\Account_Protection\Account_Protection::instance();
            if (!method_exists($account_protection, 'is_enabled')) {
                return false;
            }

            return (bool) $account_protection->is_enabled();
        } catch (Throwable $e) {
            return false;
        }
    }

    private function is_jetpack_password_compromised($password)
    {
        if (!$this->is_jetpack_account_protection_enabled()) {
            return false;
        }

        if (!class_exists('Automattic\\Jetpack\\Account_Protection\\Validation_Service')) {
            return false;
        }

        try {
            $validation_service = new Automattic\Jetpack\Account_Protection\Validation_Service();
            return (bool) $validation_service->is_leaked_password((string) $password);
        } catch (Throwable $e) {
            return false;
        }
    }

    private function sanitize_meta_data($meta_data)
    {
        $sanitized_meta_data = array();

        if (!is_array($meta_data)) {
            return $sanitized_meta_data;
        }

        foreach ($meta_data as $item) {
            if (!is_object($item) && !is_array($item)) {
                continue;
            }

            $key = is_array($item) ? ($item['key'] ?? null) : ($item->key ?? null);
            $value = is_array($item) ? ($item['value'] ?? null) : ($item->value ?? null);

            if (!is_string($key) || !in_array($key, $this->allowed_profile_meta_keys, true)) {
                continue;
            }

            if (!is_scalar($value)) {
                continue;
            }

            $sanitized_value = $this->sanitize_profile_meta_value($key, $value);
            $sanitized_meta_data[$key] = $sanitized_value;
        }

        return $sanitized_meta_data;
    }

    public function register_routes()
    {
        register_rest_route($this->namespace, '/reset-password', array(
            array(
                'methods' => 'POST',
                'callback' => array($this, 'reset_password'),
                'permission_callback' => function () {
                    return parent::checkApiPermission();
                }
            ),
        ));

        register_rest_route($this->namespace, '/notification', array(
            array(
                'methods' => 'POST',
                'callback' => array($this, 'chat_notification'),
                'permission_callback' => function () {
                    return parent::checkApiPermission();
                }
            ),
        ));

        register_rest_route($this->namespace, '/sign_up', array(
            array(
                'methods' => 'POST',
                'callback' => array($this, 'register'),
                'permission_callback' => function () {
                    return parent::checkApiPermission();
                }
            ),
        ));
        register_rest_route($this->namespace, '/sign_up_2', array(
            array(
                'methods' => 'POST',
                'callback' => array($this, 'register'),
                'permission_callback' => function () {
                    return parent::checkApiPermission();
                }
            ),
        ));

        register_rest_route($this->namespace, '/register', array(
            array(
                'methods' => 'POST',
                'callback' => array($this, 'register'),
                'permission_callback' => function () {
                    return parent::checkApiPermission();
                }
            ),
        ));

        register_rest_route($this->namespace, '/generate_auth_cookie', array(
            array(
                'methods' => 'POST',
                'callback' => array($this, 'generate_auth_cookie'),
                'permission_callback' => function () {
                    return parent::checkApiPermission();
                }
            ),
        ));

        register_rest_route($this->namespace, '/fb_connect', array(
            array(
                'methods' => 'GET',
                'callback' => array($this, 'fb_connect'),
                'permission_callback' => function () {
                    return parent::checkApiPermission();
                }
            ),
        ));

        register_rest_route($this->namespace, '/sms_login', array(
            array(
                'methods' => 'GET',
                'callback' => array($this, 'sms_login'),
                'permission_callback' => function () {
                    return parent::checkApiPermission();
                }
            ),
        ));

        register_rest_route($this->namespace, '/firebase_sms', array(
            array(
                'methods' => 'POST',
                'callback' => function ($request) {
                    $phone = $this->firebase_sms_verify_id_token($request);
                    if (is_wp_error($phone)) {
                        return $phone;
                    }
                    return $this->firebase_sms_login($phone);
                },
                'permission_callback' => function () {
                    return parent::checkApiPermission();
                }
            ),
        ));

        register_rest_route($this->namespace, '/firebase_sms_v2', array(
            array(
                'methods' => 'POST',
                'callback' => function ($request) {
                    $phone = $this->firebase_sms_verify_id_token($request);
                    if (is_wp_error($phone)) {
                        return $phone;
                    }
                    return $this->firebase_sms_login_v2($phone);
                },
                'permission_callback' => function () {
                    return parent::checkApiPermission();
                }
            ),
        ));

        register_rest_route($this->namespace, '/apple_login_2', array(
            array(
                'methods' => 'POST',
                'callback' => array($this, 'apple_login'),
                'permission_callback' => function () {
                    return parent::checkApiPermission();
                }
            ),
        ));

        register_rest_route($this->namespace, '/google_login', array(
            array(
                'methods' => 'GET',
                'callback' => array($this, 'google_login'),
                'permission_callback' => function () {
                    return parent::checkApiPermission();
                }
            ),
        ));

        register_rest_route($this->namespace, '/post_comment', array(
            array(
                'methods' => 'GET',
                'callback' => array($this, 'post_comment'),
                'permission_callback' => function () {
                    return parent::checkApiPermission();
                }
            ),
        ));

        register_rest_route($this->namespace, '/get_currentuserinfo', array(
            array(
                'methods' => 'GET',
                'callback' => array($this, 'get_currentuserinfo'),
                'permission_callback' => function () {
                    return parent::checkApiPermission();
                }
            ),
        ));

        register_rest_route($this->namespace, '/get_points', array(
            array(
                'methods' => 'GET',
                'callback' => array($this, 'get_points'),
                'permission_callback' => function () {
                    return parent::checkApiPermission();
                }
            ),
        ));

        register_rest_route($this->namespace, '/update_user_profile', array(
            array(
                'methods' => 'POST',
                'callback' => array($this, 'update_user_profile'),
                'permission_callback' => function () {
                    return parent::checkApiPermission();
                }
            ),
        ));

        register_rest_route($this->namespace, '/checkout', array(
            array(
                'methods' => 'POST',
                'callback' => array($this, 'prepare_checkout'),
                'permission_callback' => function () {
                    return parent::checkApiPermission();
                }
            ),
        ));

        register_rest_route($this->namespace, '/get_currency_rates', array(
            array(
                'methods' => 'GET',
                'callback' => array($this, 'get_currency_rates'),
                'permission_callback' => function () {
                    return parent::checkApiPermission();
                }
            ),
        ));

        register_rest_route($this->namespace, '/get_countries', array(
            array(
                'methods' => 'GET',
                'callback' => array($this, 'get_countries'),
                'permission_callback' => function () {
                    return parent::checkApiPermission();
                }
            ),
        ));

        register_rest_route($this->namespace, '/get_states', array(
            array(
                'methods' => 'GET',
                'callback' => array($this, 'get_states'),
                'permission_callback' => function () {
                    return parent::checkApiPermission();
                }
            ),
        ));

        register_rest_route($this->namespace, '/check-user', array(
            array(
                'methods' => 'GET',
                'callback' => array($this, 'check_user'),
                'permission_callback' => function () {
                    return parent::checkApiPermission();
                }
            ),
        ));

        register_rest_route($this->namespace, '/digits/register/check', array(
            array(
                'methods' => 'POST',
                'callback' => array($this, 'digits_register_check'),
                'permission_callback' => function () {
                    return parent::checkApiPermission();
                }
            ),
        ));

        register_rest_route($this->namespace, '/digits/register', array(
            array(
                'methods' => 'POST',
                'callback' => array($this, 'digits_register'),
                'permission_callback' => function () {
                    return parent::checkApiPermission();
                }
            ),
        ));

        register_rest_route($this->namespace, '/digits/login/check', array(
            array(
                'methods' => 'POST',
                'callback' => array($this, 'digits_login_check'),
                'permission_callback' => function () {
                    return parent::checkApiPermission();
                }
            ),
        ));

        register_rest_route($this->namespace, '/digits/login', array(
            array(
                'methods' => 'POST',
                'callback' => array($this, 'digits_login'),
                'permission_callback' => function () {
                    return parent::checkApiPermission();
                }
            ),
        ));

        register_rest_route($this->namespace, '/digits/send_otp', array(
            array(
                'methods' => 'POST',
                'callback' => array($this, 'digits_send_otp'),
                'permission_callback' => function () {
                    return parent::checkApiPermission();
                }
            ),
        ));

        register_rest_route($this->namespace, '/digits/resend_otp', array(
            array(
                'methods' => 'POST',
                'callback' => array($this, 'digits_resend_otp'),
                'permission_callback' => function () {
                    return parent::checkApiPermission();
                }
            ),
        ));

        register_rest_route($this->namespace, '/delete_account', array(
            array(
                'methods' => WP_REST_Server::DELETABLE,
                'callback' => array($this, 'delete_account'),
                'permission_callback' => array($this, 'custom_delete_item_permissions_check'),
            ),
        ));
    }


    /**
     * Simple per-IP throttle for endpoints that must stay public.
     *
     * @param string $action Bucket name.
     * @param int    $limit  Allowed requests within the window.
     * @param int    $window Window length in seconds.
     * @return bool True when the request is allowed.
     */
    private function check_request_rate_limit($action, $limit = 20, $window = 300)
    {
        $ip = isset($_SERVER['REMOTE_ADDR']) ? sanitize_text_field(wp_unslash($_SERVER['REMOTE_ADDR'])) : '';
        if ('' === $ip) {
            return true;
        }
        $key = 'mstore_rl_' . md5($action . '|' . $ip);
        $count = (int)get_transient($key);
        if ($count >= $limit) {
            return false;
        }
        set_transient($key, $count + 1, $window);
        return true;
    }

    public function check_user($request)
    {
        // This endpoint is used before login (registration / phone login), so it cannot
        // require authentication. Throttle it instead to prevent account enumeration.
        if (!$this->check_request_rate_limit('check_user')) {
            return parent::sendError("too_many_requests", "Too many requests. Please try again later.", 429);
        }

        $phone = isset($request['phone']) ? preg_replace('/[^\d+\-().\s]/', '', sanitize_text_field($request['phone'])) : null;
        $username = isset($request['username']) ? sanitize_text_field($request['username']) : null;
        if (isset($phone)) {
            $args = array('meta_key' => 'registered_phone_number', 'meta_value' => $phone);
            $search_users = get_users($args);
            if (empty($search_users)) {
                return false;
            }
        }
        if (isset($username)) {
            if (strpos($username, '@')) {
                $user_data = get_user_by('email', trim(wp_unslash($username)));
            } else {
                $login = trim($username);
                $user_data = get_user_by('login', $login);
            }
            if (empty($user_data)) {
                return false;
            }
        }

        return true;
    }


    public function reset_password()
    {
        $json = file_get_contents('php://input');
        $params = json_decode($json, TRUE);
        $usernameReq = $params["user_login"];

        if (empty($usernameReq) || !is_string($usernameReq)) {
            return parent::sendError("empty_username", "Enter a username or email address.", 400);
        } elseif (strpos($usernameReq, '@')) {
            $user_data = get_user_by('email', trim(wp_unslash($usernameReq)));
            if (empty($user_data)) {
                return parent::sendError("invalid_email", "There is no account with that username or email address.", 404);
            }
        } else {
            $login = trim($usernameReq);
            $user_data = get_user_by('login', $login);
        }
        if (!$user_data) {
            return parent::sendError("invalid_email", "There is no account with that username or email address.", 404);
        }

        $user_login = $user_data->user_login;
        $user_email = $user_data->user_email;
        $key = get_password_reset_key($user_data);

        if (is_wp_error($key)) {
            return $key;
        }

        if (is_multisite()) {
            $site_name = $this->mstore_get_network_site_name();
        } else {
            $site_name = wp_specialchars_decode(get_option('blogname'), ENT_QUOTES);
        }

        $message = __('Someone has requested a password reset for the following account:', 'mstore-api') . "\r\n\r\n";
        $message .= sprintf(
            /* translators: %s: site name. */
            __('Site Name: %s', 'mstore-api'),
            $site_name
        ) . "\r\n\r\n";
        $message .= sprintf(
            /* translators: %s: user login. */
            __('Username: %s', 'mstore-api'),
            $user_login
        ) . "\r\n\r\n";
        $message .= __('If this was a mistake, just ignore this email and nothing will happen.', 'mstore-api') . "\r\n\r\n";
        $message .= __('To reset your password, visit the following address:', 'mstore-api') . "\r\n\r\n";
        $message .= network_site_url("wp-login.php?action=rp&key=$key&login=" . rawurlencode($user_login), 'login') . "\r\n";
        $title = sprintf(
            /* translators: %s: site name. */
            __('[%s] Password Reset', 'mstore-api'),
            $site_name
        );
        $title = apply_filters('retrieve_password_title', $title, $user_login, $user_data);
        $message = apply_filters('retrieve_password_message', $message, $key, $user_login, $user_data);

        wp_mail($user_email, wp_specialchars_decode($title), $message);

        return new WP_REST_Response(array(
            'status' => 'success',
        ), 200);;
    }

    public function register()
    {
        if (!get_option('users_can_register')) {
            return parent::sendError("disabled_register", "Registration is not enabled.", 400);
        }
        $json = file_get_contents('php://input');
        $params = json_decode($json, TRUE);

        // Backward compatible: accept 'username'/'email' or 'user_login'/'user_email'
        $user_login = $params['user_login'] ?? $params['username'] ?? '';
        $user_email = $params['user_email'] ?? $params['email'] ?? '';
        $user_pass = $params['user_pass'] ?? '';

        // If user_login is an email, extract local part as login and use full value as email
        if (is_email($user_login)) {
            if (empty($user_email)) {
                $user_email = $user_login;
            }
            $user_login = explode('@', $user_login)[0];
        }

        $user_login = sanitize_user($user_login, true);
        if (empty($user_login)) {
            return parent::sendError("invalid_username", "Username is invalid.", 400);
        }

        if (array_key_exists('referral_code', $params)) {
            $referralCodeReq = $params["referral_code"];
        }

        if (isset($params['wcfm_membership_application_status'])) {
            $wcfm_membership_application_status = $params['wcfm_membership_application_status'];
        }

        if (isset($params["seconds"])) {
            $seconds = (int)$params["seconds"];
        } else {
            $seconds = 1209600;
        }

        // WP core: user_login is required
        if (empty($user_login) || !validate_username($user_login)) {
            return parent::sendError("invalid_username", "Username is invalid.", 400);
        }
        if (username_exists($user_login)) {
            return parent::sendError("existed_username", "Username already exists.", 400);
        }

        // WP core: user_email is optional, but if provided must be valid and unique
        if (!empty($user_email)) {
            $user_email = sanitize_email($user_email);
            if (!is_email($user_email)) {
                return parent::sendError("invalid_email", "E-mail address is invalid.", 400);
            }
            if (email_exists($user_email)) {
                return parent::sendError("existed_email", "E-mail address is already in use.", 400);
            }
        }

        // WP core: user_pass is optional, auto-generate if not provided.
        // Jetpack: reject passwords found in public data breaches when supplied by the client.
        if (empty($user_pass)) {
            $user_pass = wp_generate_password();
        } elseif ($this->is_jetpack_password_compromised($user_pass)) {
            return parent::sendError(
                'compromised_password',
                'This password has been found in a public data breach. Please choose a stronger, unique password.',
                400
            );
        }

        // Normalize params for the allowed_params loop
        $params['user_login'] = $user_login;
        $params['user_email'] = $user_email;
        $params['user_pass'] = $user_pass;

        $allowed_params = array(
            'user_login',
            'user_email',
            'user_pass',
            'display_name',
            'user_url',
            'nickname',
            'first_name',
            'last_name',
            'description',
            'rich_editing',
            'user_registered',
            'jabber',
            'aim',
            'yim',
            'comment_shortcuts',
            'admin_color',
            'use_ssl',
            'show_admin_bar_front',
        );

        $dataRequest = $params;

        foreach ($dataRequest as $field => $value) {
            if (in_array($field, $allowed_params)) {
                $user[$field] = trim(sanitize_text_field($value));
            }
        }

        $default_role = class_exists('WooCommerce') ? 'customer' : get_option('default_role');

        // Define safe roles that can be set during self-registration (non-elevated roles)
        $safe_registration_roles = array('seller', 'wcfm_vendor', 'wcfm_delivery_boy', 'driver', 'owner', 'customer', 'subscriber');

        $requested_role = '';
        if (array_key_exists('role', $params)) {
            $requested_role = sanitize_key($params['role']);
        }

        // Security: Prevent unauthenticated self-registration from setting elevated roles
        // Allow safe vendor/delivery roles, but require authentication for admin/manager roles
        if (!empty($requested_role) && get_role($requested_role)) {
            if (in_array($requested_role, $safe_registration_roles, true)) {
                // Safe role: can be set without authentication (seller, delivery, etc.)
                $user['role'] = $requested_role;
            } elseif (is_user_logged_in() && current_user_can('create_users')) {
                // Elevated role: requires authentication and create_users capability
                $user['role'] = $requested_role;
            } else {
                // Not safe and not authorized: use default
                $user['role'] = $default_role;
            }
        } else {
            $user['role'] = $default_role;
        }
        $_POST['user_role'] = $user['role']; //fix to register account with role in listeo

        if (isset($referralCodeReq) && $referralCodeReq) {
            $_COOKIE['woo_wallet_referral'] = sanitize_text_field(wp_unslash($referralCodeReq));
        }

        $user_id = wp_insert_user($user);

        if (is_wp_error($user_id)) {
            return parent::sendError($user_id->get_error_code(), $user_id->get_error_message(), 400);
        }

        // Reapply role to override WooCommerce's automatic assignment
        if (isset($user['role']) && !empty($user['role'])) {
            $wp_user = new WP_User($user_id);
            $wp_user->set_role($user['role']);
        }

        if (isset($params["phone"])) {
            $phone = preg_replace('/[^\d+\-().\s]/', '', sanitize_text_field($params["phone"]));
            update_user_meta($user_id, 'billing_phone', $phone);
            update_user_meta($user_id, 'registered_phone_number', $phone);
        }

        wp_new_user_notification($user_id, null, 'both');

        if (isset($wcfm_membership_application_status) && $wcfm_membership_application_status == 'pending') {
            // Check if WCFM is configured for auto-approval
            $auto_approve = false;
            if (is_plugin_active('wc-multivendor-marketplace/wc-multivendor-marketplace.php') && class_exists('WCFMmp')) {
                $wcfm_membership_options = get_option('wcfm_membership_options', array());
                $membership_reject_rules = isset($wcfm_membership_options['membership_reject_rules']) ?
                                           $wcfm_membership_options['membership_reject_rules'] : array();
                $required_approval = isset($membership_reject_rules['required_approval']) ?
                                     $membership_reject_rules['required_approval'] : 'no';

                // 'no' = no approval needed = auto-approve TRUE
                $auto_approve = ($required_approval === 'no');
            }

            // Set vendor meta data
            update_user_meta($user_id, 'store_name', $user['display_name']);

            //fix crash when approve membership in WCFM
            $wcfmvm_static_infos = (array) get_user_meta($user_id, 'wcfmvm_static_infos', true);
            $wcfm_phone = isset($params["phone"]) ? preg_replace('/[^\d+\-().\s]/', '', sanitize_text_field($params["phone"])) : '';
            $wcfmvm_static_infos['phone'] = $wcfm_phone;
            update_user_meta($user_id, 'wcfmvm_static_infos', $wcfmvm_static_infos);
            update_user_meta($user_id, 'billing_phone', $wcfm_phone);

            if ($auto_approve && get_role('wcfm_vendor')) {
                // Auto-approve: upgrade to wcfm_vendor role
                $wp_user = new WP_User($user_id);
                $wp_user->set_role('wcfm_vendor');
            } else {
                // Manual approval: keep as subscriber and send email to admin
                update_user_meta($user_id, 'temp_wcfm_membership', true);
                global $WCFMvm;
                if (is_object($WCFMvm) && method_exists($WCFMvm, 'send_approval_reminder_admin')) {
                    $WCFMvm->send_approval_reminder_admin($user_id);
                }
            }
        }

        if (isset($params['dokan_enable_selling'])) {
            // Check if Dokan is configured for auto-approval
            if (is_plugin_active('dokan-lite/dokan.php') || is_plugin_active('dokan-pro/dokan-pro.php')) {
                $dokan_settings = (array) get_option('dokan_selling', array());
                $auto_approve = isset($dokan_settings['new_seller_enable_selling']) &&
                                $dokan_settings['new_seller_enable_selling'] === 'automatically';

                // Set 'yes' if auto-approval is enabled, otherwise 'no' (pending)
                update_user_meta($user_id, 'dokan_enable_selling', $auto_approve ? 'yes' : 'no');
            } else {
                // Fallback: default to pending if Dokan is not active
                update_user_meta($user_id, 'dokan_enable_selling', 'no');
            }
        }
        $cookie = generateCookieByUserId($user_id, $seconds);

        return array(
            "cookie" => $cookie,
            "user_id" => $user_id,
        );
    }


    private function get_shipping_address($userId)
    {
        $shipping = [];

        $shipping["first_name"] = get_user_meta($userId, 'shipping_first_name', true);
        $shipping["last_name"] = get_user_meta($userId, 'shipping_last_name', true);
        $shipping["company"] = get_user_meta($userId, 'shipping_company', true);
        $shipping["address_1"] = get_user_meta($userId, 'shipping_address_1', true);
        $shipping["address_2"] = get_user_meta($userId, 'shipping_address_2', true);
        $shipping["city"] = get_user_meta($userId, 'shipping_city', true);
        $shipping["state"] = get_user_meta($userId, 'shipping_state', true);
        $shipping["postcode"] = get_user_meta($userId, 'shipping_postcode', true);
        $shipping["country"] = get_user_meta($userId, 'shipping_country', true);
        $shipping["email"] = get_user_meta($userId, 'shipping_email', true);
        $shipping["phone"] = get_user_meta($userId, 'shipping_phone', true);

        if (empty($shipping["first_name"]) && empty($shipping["last_name"]) && empty($shipping["company"]) && empty($shipping["address_1"]) && empty($shipping["address_2"]) && empty($shipping["city"]) && empty($shipping["state"]) && empty($shipping["postcode"]) && empty($shipping["country"]) && empty($shipping["email"]) && empty($shipping["phone"])) {
            return null;
        }
        return $shipping;
    }

    private function get_billing_address($userId)
    {
        $billing = [];

        $billing["first_name"] = get_user_meta($userId, 'billing_first_name', true);
        $billing["last_name"] = get_user_meta($userId, 'billing_last_name', true);
        $billing["company"] = get_user_meta($userId, 'billing_company', true);
        $billing["address_1"] = get_user_meta($userId, 'billing_address_1', true);
        $billing["address_2"] = get_user_meta($userId, 'billing_address_2', true);
        $billing["city"] = get_user_meta($userId, 'billing_city', true);
        $billing["state"] = get_user_meta($userId, 'billing_state', true);
        $billing["postcode"] = get_user_meta($userId, 'billing_postcode', true);
        $billing["country"] = get_user_meta($userId, 'billing_country', true);
        $billing["email"] = get_user_meta($userId, 'billing_email', true);
        $billing["phone"] = get_user_meta($userId, 'billing_phone', true);

        if (empty($billing["first_name"]) && empty($billing["last_name"]) && empty($billing["company"]) && empty($billing["address_1"]) && empty($billing["address_2"]) && empty($billing["city"]) && empty($billing["state"]) && empty($billing["postcode"]) && empty($billing["country"]) && empty($billing["email"]) && empty($billing["phone"])) {
            return null;
        }

        return $billing;
    }

    function getResponseUserInfo($user)
    {
        $shipping = $this->get_shipping_address($user->ID);
        $billing = $this->get_billing_address($user->ID);
        $avatar = get_user_meta($user->ID, 'user_avatar', true);
        if (!isset($avatar) || $avatar == "" || is_bool($avatar)) {
            $avatar = get_avatar_url($user->ID);
        } else {
            $avatar = $avatar[0];
        }
        $is_driver_available = false;

        if (mstore_is_lddfw_active()) {
            $is_driver_available = filter_var(
                get_user_meta($user->ID, 'lddfw_driver_availability', true),
                FILTER_VALIDATE_BOOLEAN
            );
        } else if (mstore_is_ddwc_active()) {
            $is_driver_available = filter_var(
                get_user_meta($user->ID, 'ddwc_driver_availability', true),
                FILTER_VALIDATE_BOOLEAN
            );
        } else {
            $is_driver_available = in_array('administrator', $user->roles) || in_array('wcfm_delivery_boy', $user->roles);
        }

        // Check order status change capability
        $order_status_change = false;

        // Check vendor auto approval setting
        $vendor_auto_approve_selling = false;

        // Check for Dokan
        if (is_plugin_active('dokan-lite/dokan.php') || is_plugin_active('dokan-pro/dokan-pro.php')) {
            $dokan_settings = (array) get_option('dokan_selling', array());

            // Order Status Change capability
            $order_status_change = isset($dokan_settings['order_status_change']) ?
                filter_var($dokan_settings['order_status_change'], FILTER_VALIDATE_BOOLEAN) : false;

            // Enable Selling option (select field: 'automatically' or 'manual')
            $vendor_auto_approve_selling = isset($dokan_settings['new_seller_enable_selling']) ?
                ($dokan_settings['new_seller_enable_selling'] === 'automatically') : false;
        }
        // Check for WCFM Core (only if Dokan is not active)
        elseif (is_plugin_active('wc-frontend-manager/wc_frontend_manager.php') && class_exists('WCFM')) {
            global $WCFM;

            // Order Status Change capability
            $order_status_change = $WCFM->wcfm_vendor_support->wcfm_vendor_has_capability($user->ID, 'order_status_update');

            // Check for WCFM Marketplace (vendor auto-approval setting)
            if (is_plugin_active('wc-multivendor-marketplace/wc-multivendor-marketplace.php') && class_exists('WCFMmp')) {
                // Required Approval setting from WCFM Marketplace
                $wcfm_membership_options = get_option('wcfm_membership_options', array());
                $membership_reject_rules = isset($wcfm_membership_options['membership_reject_rules']) ?
                                           $wcfm_membership_options['membership_reject_rules'] : array();
                $required_approval = isset($membership_reject_rules['required_approval']) ?
                                     $membership_reject_rules['required_approval'] : 'no';

                // 'yes' = requires approval = auto-approve FALSE
                // 'no' = no approval needed = auto-approve TRUE
                $vendor_auto_approve_selling = ($required_approval === 'no');
            }
        }

        // If user is admin, always allow order status change
        if (in_array('administrator', $user->roles)) {
            $order_status_change = true;
        }

        return array(
            "id" => $user->ID,
            "username" => $user->user_login,
            "nicename" => $user->user_nicename,
            "email" => $user->user_email,
            "url" => $user->user_url,
            "registered" => $user->user_registered,
            "displayname" => $user->display_name,
            "firstname" => $user->first_name,
            "lastname" => $user->last_name,
            "nickname" => $user->nickname,
            "description" => $user->user_description,
            "capabilities" => $user->wp_capabilities,
            "role" => $user->roles,
            "shipping" => $shipping,
            "billing" => $billing,
            "avatar" => $avatar,
            "is_driver_available" => $is_driver_available,
            "dokan_enable_selling" => $user->dokan_enable_selling,
            "order_status_change" => (bool)$order_status_change,
            "vendor_auto_approve_selling" => (bool)$vendor_auto_approve_selling
        );
    }

    public function generate_auth_cookie()
    {
        $json = file_get_contents('php://input');
        $params = json_decode($json, TRUE);
        if (!isset($params["username"]) || !isset($params["password"])) {
            return parent::sendError("invalid_login", "Invalid params", 400);
        }
        $username = $params["username"];
        $password = $params["password"];
        if (!is_string($username) || !is_string($password)) {
            return parent::sendError("invalid_login", "Invalid request format.", 400);
        }

        if (isset($params["seconds"])) {
            $seconds = (int)$params["seconds"];
        } else {
            $seconds = 1209600;
        }

        if ($this->is_jetpack_account_protection_enabled()) {
            $candidate_user = is_email($username)
                ? get_user_by('email', $username)
                : get_user_by('login', $username);

            if ($candidate_user instanceof WP_User && wp_check_password($password, $candidate_user->user_pass, $candidate_user->ID)) {
                if ($this->is_jetpack_password_compromised($password)) {
                    return parent::sendError(
                        'compromised_password',
                        'Your password has been found in a public data breach. Please reset your password via email before logging in.',
                        401
                    );
                }
            }
        }

        $_POST['action'] = 'listeoajaxlogin'; //fix to return json if login error in listeo
        $user = wp_authenticate($username, $password);

        if (is_wp_error($user)) {
            $error_code = $user->get_error_code();
            if ($error_code === 'compromised_password') {
                return parent::sendError(
                    'compromised_password',
                    'Your password has been found in a public data breach. Please reset your password via email before logging in.',
                    401
                );
            }
            return parent::sendError($user->get_error_code(), "Invalid username/email and/or password.", 401);
        }

        if (get_user_meta($user->ID, 'b2bking_account_approved', true) === 'no') {
            return parent::sendError("account_pending_approval", "Your account is pending approval.", 401);
        }

        $cookie = generateCookieByUserId($user->ID, $seconds);

        return array(
            "cookie" => $cookie,
            "cookie_name" => LOGGED_IN_COOKIE,
            "user" => $this->getResponseUserInfo($user),
        );
    }

    function createSocialAccount($email, $name, $firstName, $lastName)
    {
        $email_exists = email_exists($email);
        if ($email_exists) {
            $user = get_user_by('email', $email);
            $user_id = $user->ID;
        } else {
            // Extract and sanitize the username from the email local part
            $userName = sanitize_user(explode('@', $email)[0], true);

            // Fall back to a random name if the local part had no valid characters
            if (empty($userName)) {
                $userName = 'user_' . time() . '_' . wp_rand(1000, 9999);
            }

            // Append an incrementing counter to guarantee uniqueness
            $baseUserName = $userName;
            $i = 0;
            while (username_exists($userName)) {
                $i++;
                $userName = $baseUserName . '.' . $i;
            }
            $random_password = wp_generate_password($length = 12, $include_standard_special_chars = false);
            $userdata = array(
                'user_login' => $userName,
                'user_email' => $email,
                'user_pass' => $random_password,
                'display_name' => $name,
                'first_name' => $firstName,
                'last_name' => $lastName
            );
            $user_id = wp_insert_user($userdata);
            if (is_wp_error($user_id)) {
                return $user_id;
            }
        }

        $cookie = generateCookieByUserId($user_id);
        $user = get_userdata($user_id);

        $response['wp_user_id'] = $user_id;
        $response['cookie'] = $cookie;
        $response['user_login'] = $user->user_login;
        $response['user'] = $this->getResponseUserInfo($user);
        return $response;
    }

    public function fb_connect($request)
    {
        $fields = 'id,name,first_name,last_name,email';
        $access_token = $request["access_token"];
        if (!isset($access_token)) {
            return parent::sendError("invalid_login", "You must include a 'access_token' variable. Get the valid access_token for this app from Facebook API.", 400);
        }

        $result = [];

        // If token is an AuthenticationToken (in case of limited login for
        // iOS), validate the JWT and return the payload
        $jwt = FacebookJWTHelper::validateJWT($access_token);

        if ($jwt['success']) {
            $decodedPayload = $jwt['decoded']['payload'];
            $result["email"] = $decodedPayload->email;
            $result["name"] = $decodedPayload->name;
            $result["first_name"] = $decodedPayload->given_name;
            $result["last_name"] = $decodedPayload->family_name;
        } else {
            $url = 'https://graph.facebook.com/me/?fields=' . $fields . '&access_token=' . $access_token;
            $payload = wp_remote_retrieve_body(wp_remote_get($url));
            $result = json_decode($payload, true);
        }

        if (isset($result["email"])) {
            return $this->createSocialAccount($result["email"], $result['name'], $result['first_name'], $result['last_name']);
        } else {
            return parent::sendError("invalid_login", "Your 'access_token' did not return email of the user. Without 'email' user can't be logged in or registered. Get user email extended permission while joining the Facebook app.", 400);
        }
    }

    public function sms_login($request)
    {
        $access_token = $request["access_token"];
        if (!isset($access_token)) {
            return parent::sendError("invalid_login", "You must include a 'access_token' variable. Get the valid access_token for this app from Facebook API.", 400);
        }
        $url = 'https://graph.accountkit.com/v1.3/me/?access_token=' . $access_token;

        $WP_Http_Curl = new WP_Http_Curl();
        $result = $WP_Http_Curl->request($url, array(
            'method' => 'GET',
            'timeout' => 5,
            'redirection' => 5,
            'httpversion' => '1.0',
            'blocking' => true,
            'headers' => array(),
            'body' => null,
            'cookies' => array(),
        ));

        $result = json_decode($result, true);

        if (isset($result["phone"])) {
            $user_name = $result["phone"]["number"];
            $user_email = $result["phone"]["number"] . "@flutter.io";
            return $this->createSocialAccount($user_email, $user_name, $user_name, "");
        } else {
            return parent::sendError("invalid_login", "Your 'access_token' did not return email of the user. Without 'email' user can't be logged in or registered. Get user email extended permission while joining the Facebook app.", 400);
        }
    }

    private function firebase_sms_verify_id_token($request)
    {
        $json = file_get_contents('php://input');
        $params = json_decode($json, TRUE);

        $id_token = $params["id_token"];
        if (!isset($id_token)) {
            return parent::sendError("invalid_login", "id_token is required", 400);
        }

        $helper = new FirebasePhoneAuthHelper();
        $result = $helper->verify_id_token($id_token);

        if (is_wp_error($result)) {
            return $result;
        }
        if ($result == false) {
            return parent::sendError("invalid_login", "id_token is invalid.", 400);
        }
        return $result;
    }

    /**
     * Phone number spellings an existing account may have been created under.
     *
     * Before signature verification was added, the token payload was run through
     * urldecode(), which turns the '+' of an E.164 number into a space that trim()
     * then removed - so accounts created by older builds are keyed on the digits
     * alone. Proper base64url decoding keeps the '+', so both spellings have to be
     * considered when locating an existing account, or returning users silently
     * get a brand new one.
     *
     * @param string $phone
     * @return string[] Most canonical first.
     */
    private function firebase_phone_candidates($phone)
    {
        $candidates = array($phone);

        $stripped = ltrim($phone, '+');
        if ($stripped !== '' && $stripped !== $phone) {
            $candidates[] = $stripped;
        }

        return $candidates;
    }

    private function firebase_login_domain()
    {
        $domain = $_SERVER['SERVER_NAME'] == 'default_server' ? $_SERVER['HTTP_HOST'] : $_SERVER['SERVER_NAME'];
        if (count(explode(".", $domain)) == 1) {
            $domain = "flutter.io";
        }
        return $domain;
    }

    private function firebase_sms_login($phone)
    {
        if (!isset($phone)) {
            return parent::sendError("invalid_login", "You must include a 'phone' variable.", 400);
        }
        $domain = $this->firebase_login_domain();

        $user_name = $phone;
        $user_email = $phone . "@" . $domain;

        // Reuse the account an older build created for this number rather than
        // creating a duplicate under the new spelling.
        if (!email_exists($user_email)) {
            foreach ($this->firebase_phone_candidates($phone) as $candidate) {
                $legacy_email = $candidate . "@" . $domain;
                if (email_exists($legacy_email)) {
                    $user_email = $legacy_email;
                    $user_name  = $candidate;
                    break;
                }
            }
        }

        return $this->createSocialAccount($user_email, $user_name, $user_name, "");
    }

    private function firebase_sms_login_v2($phone)
    {
        if (!isset($phone)) {
            return parent::sendError("invalid_login", "You must include a 'phone' variable.", 400);
        }

        // registered_phone_number is stored in whichever spelling the app sent at
        // registration time, so try the canonical form before the legacy one.
        $search_users = array();
        foreach ($this->firebase_phone_candidates($phone) as $candidate) {
            $search_users = get_users(array(
                'meta_key'   => 'registered_phone_number',
                'meta_value' => $candidate,
            ));
            if (!empty($search_users)) {
                break;
            }
        }

        if (empty($search_users)) {
            $domain = $this->firebase_login_domain();

            $user = false;
            foreach ($this->firebase_phone_candidates($phone) as $candidate) {
                $user = get_user_by('email', $candidate . "@" . $domain);
                if ($user) {
                    break;
                }
            }

            if (!$user) {
                return parent::sendError("invalid_login", "User does not exist", 400);
            }
            $cookie = generateCookieByUserId($user->ID);
            $response['wp_user_id'] = $user->ID;
            $response['cookie'] = $cookie;
            $response['user_login'] = $user->user_login;
            $response['user'] = $this->getResponseUserInfo($user);
            return $response;
        }
        if (count($search_users) > 1) {
            return parent::sendError("invalid_login", "Too many users with the same phone number", 400);
        }
        $user = $search_users[0];
        $cookie = generateCookieByUserId($user->ID);
        $response['wp_user_id'] = $user->ID;
        $response['cookie'] = $cookie;
        $response['user_login'] = $user->user_login;
        $response['user'] = $this->getResponseUserInfo($user);
        return $response;
    }


    function jwtDecode($token)
    {
        $splitToken = explode(".", $token);
        $payloadBase64 = $splitToken[1]; // Payload is always the index 1
        $decodedPayload = json_decode(urldecode(base64_decode($payloadBase64)), true);
        return $decodedPayload;
    }

    public function apple_login($request)
    {
        $json = file_get_contents('php://input');
        $params = json_decode($json, TRUE);
        $authorization_code = $params["authorization_code"];
        $firstName = $params["first_name"];
        $lastName = $params["last_name"];
        $teamId = $params["team_id"];
        $bundleId = $params["bundle_id"];
        if (!FlutterAppleSignInUtils::is_file_existed()) {
            return parent::sendError("invalid_login", "You need to upload AuthKey_XXXX.p8 file to MStore Api plugin", 400);
        }
        $token = AppleSignInHelper::generate_token($bundleId, $teamId, $authorization_code);
        if ($token == false || is_wp_error($token)) {
            return is_wp_error($token) ? $token : parent::sendError("invalid_login",  "Invalid authorization_code", 400);
        }
        $decoded = $this->jwtDecode($token);
        $user_email = $decoded["email"];
        if (!isset($user_email)) {
            return parent::sendError("invalid_login", "Can't get the email to create account.", 400);
        }
        $display_name = explode("@", $user_email)[0];
        if (isset($firstName) && isset($lastName) && !empty($firstName)) {
            $display_name = $firstName . ' ' . $lastName;
        } else {
            $firstName =  $display_name;
            $lastName = "";
        }

        return $this->createSocialAccount($user_email, $display_name, $firstName, $lastName);
    }

    public function google_login($request)
    {
        $access_token = $request["access_token"];
        if (!isset($access_token)) {
            return parent::sendError("invalid_login", "You must include a 'access_token' variable. Get the valid access_token for this app from Google API.", 400);
        }

        $url = 'https://www.googleapis.com/oauth2/v1/userinfo?alt=json&access_token=' . $access_token;

        $result = wp_remote_retrieve_body(wp_remote_get($url));

        $result = json_decode($result, true);
        if (isset($result["email"])) {
            $firstName = $result["given_name"];
            $lastName = $result["family_name"];
            $email = $result["email"];
            $display_name = $firstName . " " . $lastName;
            return $this->createSocialAccount($email, $display_name, $firstName, $lastName);
        } else {
            return parent::sendError("invalid_login", "Your 'token' did not return email of the user. Without 'email' user can't be logged in or registered. Get user email extended permission while joining the Google app.", 400);
        }
    }

    /*
     * Post commment function
     */
    public function post_comment($request)
    {
        $cookie = $request["cookie"];
        $user_id = validateCookieLogin($cookie);
        if (is_wp_error($user_id)) {
            return $user_id;
        }
        if (!$request["post_id"]) {
            return parent::sendError("invalid_data", "No post specified. Include 'post_id' var in your request.", 400);
        } elseif (!$request["content"]) {
            return parent::sendError("invalid_data", "Please include 'content' var in your request.", 400);
        }

        $comment_approved = 0;
        $user_info = get_userdata($user_id);
        $time = current_time('mysql');
        $agent = filter_has_var(INPUT_SERVER, 'HTTP_USER_AGENT') ? filter_input(INPUT_SERVER, 'HTTP_USER_AGENT') : 'Mozilla';
        $ips = filter_has_var(INPUT_SERVER, 'REMOTE_ADDR') ? filter_input(INPUT_SERVER, 'REMOTE_ADDR') : '127.0.0.1';
        $data = array(
            'comment_post_ID' => $request["post_id"],
            'comment_author' => $user_info->user_login,
            'comment_author_email' => $user_info->user_email,
            'comment_author_url' => $user_info->user_url,
            'comment_content' => $request["content"],
            'comment_type' => '',
            'comment_parent' => 0,
            'user_id' => $user_info->ID,
            'comment_author_IP' => $ips,
            'comment_agent' => $agent,
            'comment_date' => $time,
            'comment_approved' => $comment_approved,
        );
        //print_r($data);
        $comment_id = wp_insert_comment($data);
        //add metafields
        $meta = json_decode(stripcslashes($request["meta"]), true);
        //extra function
        add_comment_meta($comment_id, 'rating', $meta['rating']);
        add_comment_meta($comment_id, 'verified', 0);

        return array(
            "comment_id" => $comment_id,
        );
    }

    public function get_currentuserinfo($request)
    {
        $cookie = $request["cookie"];
        if (isset($request["token"])) {
            $cookie = mstore_decode_user_cookie($request["token"]);
        }
        $user_id = validateCookieLogin($cookie);
        if (is_wp_error($user_id)) {
            return $user_id;
        }
        $user = get_userdata($user_id);
        return array(
            "user" => $this->getResponseUserInfo($user)
        );
    }

    /**
     * Get Point Reward by User ID
     *
     * @return void
     */
    function get_points($request)
    {
        global $wc_points_rewards;

        if (!class_exists('WC_Points_Rewards_Manager') || !class_exists('WC_Points_Rewards_Points_Log') || !isset($wc_points_rewards)) {
            return parent::send_invalid_plugin_error("You need to install WooCommerce Points and Rewards plugin to use this api");
        }

        $auth_user_id = $this->get_authenticated_user_id($request);
        if (is_wp_error($auth_user_id)) {
            return $auth_user_id;
        }

        $user_id = isset($request['user_id']) ? (int)$request['user_id'] : 0;
        if (empty($user_id)) {
            $user_id = $auth_user_id;
        }

        // A user may only read their own points balance and log.
        if ($user_id !== $auth_user_id && !user_can($auth_user_id, 'list_users')) {
            return parent::sendError("unauthorized", "You are not allowed to do this", 401);
        }

        $current_page = isset($request['page']) ? (int)$request['page'] : 0;

        $points_balance = WC_Points_Rewards_Manager::get_users_points($user_id);
        $points_label = $wc_points_rewards->get_points_label($points_balance);
        $count = apply_filters('wc_points_rewards_my_account_points_events', 5, $user_id);
        $current_page = empty($current_page) ? 1 : absint($current_page);

        $args = array(
            'calc_found_rows' => true,
            'orderby' => array(
                'field' => 'date',
                'order' => 'DESC',
            ),
            'per_page' => $count,
            'paged' => $current_page,
            'user' => $user_id,
        );
        $total_rows = WC_Points_Rewards_Points_Log::$found_rows;
        $events = WC_Points_Rewards_Points_Log::get_points_log_entries($args);

        return array(
            'points_balance' => $points_balance,
            'points_label' => $points_label,
            'total_rows' => $total_rows,
            'page' => $current_page,
            'count' => $count,
            'events' => $events
        );
    }

    /**
     * Resolve the user making the request from the cookie/token parameter,
     * the User-Cookie header or the current WordPress authentication context.
     *
     * @param WP_REST_Request $request Current request.
     * @return int|WP_Error Authenticated user ID or an error when not logged in.
     */
    private function get_authenticated_user_id($request)
    {
        $cookie = null;
        if (isset($request["token"]) && is_string($request["token"])) {
            $cookie = mstore_decode_user_cookie($request["token"]);
        } elseif (isset($request["cookie"]) && is_string($request["cookie"])) {
            $cookie = $request["cookie"];
        } elseif (is_object($request) && method_exists($request, 'get_header')) {
            $header_cookie = $request->get_header("User-Cookie");
            if (!empty($header_cookie)) {
                $cookie = get_header_user_cookie($header_cookie);
            }
        }

        if (!empty($cookie)) {
            $user_id = validateCookieLogin($cookie);
            if (!is_wp_error($user_id)) {
                return (int)$user_id;
            }
        }

        // Fallback to any other authentication layer (JWT, application password, cookie).
        $current_user_id = get_current_user_id();
        if (!empty($current_user_id)) {
            return (int)$current_user_id;
        }

        return parent::sendError("unauthorized", "You are not allowed to do this", 401);
    }

    function update_user_profile()
    {
        $json = file_get_contents('php://input');
        $params = json_decode($json);
        if (!is_object($params)) {
            return new WP_Error("invalid_request", "Invalid request payload.", array('status' => 400));
        }
        if (!isset($params->cookie) || !is_string($params->cookie) || '' === trim($params->cookie)) {
            return new WP_Error("invalid_cookie", "Missing or invalid cookie parameter.", array('status' => 400));
        }
        $cookie = $params->cookie;
        $user_id = validateCookieLogin($cookie);
        if (is_wp_error($user_id)) {
            return $user_id;
        }

        // WP core: ID is the only required field for wp_update_user
        $user_update = array('ID' => $user_id);
        $pending_meta_updates = array();
        $pending_avatar = null;
        if (isset($params->user_pass)) {
            $user_update['user_pass'] = $params->user_pass;
        }
        if (isset($params->user_nicename)) {
            if (!is_scalar($params->user_nicename)) {
                return new WP_Error("invalid_user_nicename", "Invalid user nicename.", array('status' => 400));

            }
            $user_update['user_nicename'] = sanitize_title((string) $params->user_nicename);
        }

        if (isset($params->user_email)) {
            if (!is_scalar($params->user_email)) {
                return new WP_Error("invalid_user_email", "Invalid email address.", array('status' => 400));
            }

            $user_email = sanitize_email((string) $params->user_email);
            if ($user_email === '' || !is_email($user_email)) {
                return new WP_Error("invalid_user_email", "Invalid email address.", array('status' => 400));
            }
            $user_update['user_email'] = $user_email;
        }

        if (isset($params->user_url)) {
            if (!is_scalar($params->user_url)) {
                return new WP_Error("invalid_user_url", "Invalid user URL.", array('status' => 400));
            }

            $raw_user_url = (string) $params->user_url;
            $user_url = esc_url_raw($raw_user_url);
            if ($raw_user_url !== '' && $user_url === '') {
                return new WP_Error("invalid_user_url", "Invalid user URL.", array('status' => 400));
            }

            $user_update['user_url'] = $user_url;
        }
        if (isset($params->display_name)) {
            $user_update['display_name'] = $this->sanitize_profile_text($params->display_name);
        }
        if (isset($params->first_name)) {
            $first_name = $this->sanitize_profile_text($params->first_name);
            $user_update['first_name'] = $first_name;
            $pending_meta_updates['shipping_first_name'] = $first_name;
            $pending_meta_updates['billing_first_name'] = $first_name;
        }
        if (isset($params->last_name)) {
            $last_name = $this->sanitize_profile_text($params->last_name);
            $user_update['last_name'] = $last_name;
            $pending_meta_updates['shipping_last_name'] = $last_name;
            $pending_meta_updates['billing_last_name'] = $last_name;
        }
        if (isset($params->phone)) {
            $phone = $this->sanitize_profile_text($params->phone);
            $pending_meta_updates['shipping_phone'] = $phone;
            $pending_meta_updates['billing_phone'] = $phone;
        }
        if (isset($params->shipping_company)) {
            $shipping_company = $this->sanitize_profile_text($params->shipping_company);
            $pending_meta_updates['shipping_company'] = $shipping_company;
            $pending_meta_updates['billing_company'] = $shipping_company;
        }
        if (isset($params->shipping_state)) {
            $shipping_state = $this->sanitize_profile_text($params->shipping_state);
            $pending_meta_updates['shipping_state'] = $shipping_state;
            $pending_meta_updates['billing_state'] = $shipping_state;
        }
        if (isset($params->shipping_address_1)) {
            $shipping_address_1 = $this->sanitize_profile_text($params->shipping_address_1);
            $pending_meta_updates['shipping_address_1'] = $shipping_address_1;
            $pending_meta_updates['billing_address_1'] = $shipping_address_1;
        }
        if (isset($params->shipping_address_2)) {
            $shipping_address_2 = $this->sanitize_profile_text($params->shipping_address_2);
            $pending_meta_updates['shipping_address_2'] = $shipping_address_2;
            $pending_meta_updates['billing_address_2'] = $shipping_address_2;
        }
        if (isset($params->shipping_city)) {
            $shipping_city = $this->sanitize_profile_text($params->shipping_city);
            $pending_meta_updates['shipping_city'] = $shipping_city;
            $pending_meta_updates['billing_city'] = $shipping_city;
        }
        if (isset($params->shipping_country)) {
            $shipping_country = $this->sanitize_profile_text($params->shipping_country);
            $pending_meta_updates['shipping_country'] = $shipping_country;
            $pending_meta_updates['billing_country'] = $shipping_country;
        }
        if (isset($params->shipping_postcode)) {
            $shipping_postcode = wc_clean(wp_unslash((string)$params->shipping_postcode));
            $pending_meta_updates['shipping_postcode'] = $shipping_postcode;
            $pending_meta_updates['billing_postcode'] = $shipping_postcode;
        }
        $pending_meta_updates = array_merge(
            $pending_meta_updates,
            $this->sanitize_meta_data($params->meta_data ?? null)
        );

        if (isset($params->avatar)) {
            $pending_avatar = $params->avatar;
        }


        $user_data = wp_update_user($user_update);

        if (is_wp_error($user_data)) {
            return $user_data;
        }

        foreach ($pending_meta_updates as $meta_key => $meta_value) {
            update_user_meta($user_id, $meta_key, $meta_value, '');
        }

        if ($pending_avatar !== null) {
            $count = 1;
            try {
                $attachment_id = upload_image_from_mobile($pending_avatar, $count, $user_id);
                $url = wp_get_attachment_image_src($attachment_id);
                update_user_meta($user_id, 'user_avatar', $url, '');
            } catch (Exception $e) {
                return new WP_Error("invalid_avatar", $e->getMessage(), array('status' => 400));
            }
        }

        $user = get_userdata($user_id);

        if (isset($params->deviceToken)) {
            $device_token = $this->sanitize_profile_text($params->deviceToken);
            if (isset($params->is_manager) && $params->is_manager) {
                update_user_meta($user_id, "mstore_manager_device_token", $device_token);
            } else if (isset($params->is_delivery) && $params->is_delivery) {
                update_user_meta($user_id, "mstore_delivery_device_token", $device_token);
            }
            if (!isset($params->is_delivery) && !isset($params->is_manager)) {
                update_user_meta($user_id, "mstore_device_token", $device_token);
            }
            if (in_array('wcfm_delivery_boy', (array)$user->roles) || in_array('driver', (array)$user->roles)) {
                update_user_meta($user_id, "mstore_delivery_device_token", $device_token);
            }
        }

        return $this->getResponseUserInfo($user);
    }

    function prepare_checkout()
    {
        global $json_api;
        $json = file_get_contents('php://input');
        $params = json_decode($json);
        $order = $params->order;
        if (!isset($order)) {
            return parent::sendError("invalid_checkout", "You must include a 'order' var in your request", 400);
        }
        global $wpdb;
        $table_name = $wpdb->prefix . "mstore_checkout";

        $code = md5(wp_rand() . strtotime("now"));
        $success = $wpdb->insert(
            $table_name,
            array(
                'code' => $code,
                'order' => $order
            )
        );
        if ($success) {
            return $code;
        } else {
            return parent::sendError("error_insert_database", "Can't insert to database", 400);
        }
    }

    public function get_currency_rates()
    {
        global $woocommerce_wpml;

        if (!empty($woocommerce_wpml->multi_currency) && !empty($woocommerce_wpml->settings['currencies_order'])) {
            return $woocommerce_wpml->settings['currency_options'];
        }
        return parent::send_invalid_plugin_error("WooCommerce WPML hasn't been installed yet.");
    }

    public function get_countries()
    {
        $wc_countries = new WC_Countries();
        $array = $wc_countries->get_countries();
        $keys = array_keys($array);
        $countries = array();
        for ($i = 0; $i < count($keys); $i++) {
            $countries[] = ["code" => $keys[$i], "name" => $array[$keys[$i]]];
        }
        return $countries;
    }

    public function get_states($request)
    {
        $wc_countries = new WC_Countries();
        $array = $wc_countries->get_states($request["country_code"]);
        if ($array) {
            $keys = array_keys($array);
            $states = array();
            for ($i = 0; $i < count($keys); $i++) {
                $states[] = ["code" => $keys[$i], "name" => $array[$keys[$i]]];
            }
            return $states;
        } else {
            return [];
        }
    }

    function chat_notification()
    {
        $json = file_get_contents('php://input');
        $params = json_decode($json, TRUE);
        $token = $params['token'];
        if (isset($token)) {
            $cookie = mstore_decode_user_cookie($token);
        } else {
            return parent::sendError("unauthorized", "You are not allowed to do this", 401);
        }
        $user_id = validateCookieLogin($cookie);
        if (is_wp_error($user_id)) {
            return $user_id;
        }
        $receiver_email = $params['receiver'];
        $sender_name = $params['sender'];
        if (is_email($sender_name)) {
            $sender = get_user_by('email', $sender_name);
            $sender_name = $sender->display_name;
        }
        $receiver = get_user_by('email', $receiver_email);

        if (!$receiver) {
            return parent::sendError("invalid_user", "User does not exist in this world. Please re-check user's existence with the Creator :)", 401);
        }

        $message = $params['message'];

        pushNotificationForUser($receiver->ID, $sender_name, $message);
        if (!is_plugin_active('onesignal-free-web-push-notifications/onesignal.php')) { //fix duplicate notification if onesignal
            pushNotificationForVendor($receiver->ID, $sender_name, $message);
        }
    }

    function mstore_digrest_set_variables()
    {
        $json = file_get_contents('php://input');
        $params = json_decode($json, TRUE);

        $_POST['digits'] = 1;

        if (function_exists('dig_isWhatsAppEnabled') && dig_isWhatsAppEnabled() && !empty($params['whatsapp'])) {
            $_POST['whatsapp'] = 1;
        }

        if (isset($params['type'])) {
            $type = $params['type'];
            if ($type == 'login') {
                $_REQUEST['login'] = 1;
            }
            if ($type == 'register') {
                $_REQUEST['login'] = 2;
            } else if ($type == 'resetpass') {
                $_REQUEST['login'] = 3;
            } else if ($type == 'update') {
                $_REQUEST['login'] = 11;
            }
        } else {
            $_REQUEST['login'] = 2;
        }

        if (isset($params['mobile'])) {
            $_POST['digits_reg_mail'] = $params['mobile'];
        }

        if (!empty($params['email'])) {
            $_POST['dig_reg_mail'] = $params['email'];
        }

        if (!empty($params['username'])) {
            $_POST['digits_reg_username'] = $params['username'];
            $_POST['digits_reg_name'] = $params['username'];
        } else if (isset($params['country_code']) && isset($params['mobile'])) {
            $phone_username = preg_replace('/[^0-9]/', '', $params['country_code'] . $params['mobile']);
            $_POST['digits_reg_username'] = $phone_username;
            $_POST['digits_reg_name'] = $phone_username;
        }

        if (isset($params['name'])) {
            $_POST['digits_reg_name'] = $params['name'];
        }
        if (isset($params['last_name'])) {
            $_POST['digits_reg_lastname'] = $params['last_name'];
        }
        if (isset($params['country_code'])) {
            $_POST['digregcode'] = $params['country_code'];
        }
        if (isset($params['otp'])) {
            $_POST['dig_otp'] = $params['otp'];
        }
        $_POST['ftoken'] = $params['ftoken'] ?? '';
        $_REQUEST['ftoken'] = $params['ftoken'] ?? '';

        $_REQUEST['csrf'] = wp_create_nonce('crsf-otp');
        $_POST['csrf'] = wp_create_nonce('crsf-otp');

        $_POST['dig_nounce'] = wp_create_nonce('dig_form');
        $_POST['crsf-otp'] = wp_create_nonce('crsf-otp');

        if (isset($params['password'])) {
            $_POST['digits_reg_password'] = $params['password'];
        } else {
            $_POST['digits_reg_password'] = wp_generate_password();
        }

        $reg_custom_fields_data = get_option("dig_reg_custom_field_data", "e30=");
        if (!empty($reg_custom_fields_data)) {
            $reg_custom_fields = stripslashes(base64_decode($reg_custom_fields_data));
            $reg_custom_fields = json_decode($reg_custom_fields, true);
            if (is_array($reg_custom_fields)) {
                foreach ($reg_custom_fields as $key => $values) {
                    $required = $values['required'];
                    if ($required == 1) {
                        $meta_key = function_exists('cust_dig_filter_string') ? cust_dig_filter_string($values['meta_key']) : sanitize_key($values['meta_key']);
                        $post_index = 'digits_reg_' . $meta_key;
                        $_POST[$post_index] = '1';
                    }
                }
            }
        }
        $_REQUEST['json'] = 1;

        if (isset($params['referral_code'])) {
            $_COOKIE['woo_wallet_referral'] = sanitize_text_field(wp_unslash($params['referral_code']));
        }
    }

    function digits_register_check()
    {
        if (!function_exists('digits_create_user')) {
            return parent::sendError("plugin_not_found", "Please install the DIGITS: Wordpress Mobile Number Signup and Login plugin", 400);
        }

        $json = file_get_contents('php://input');
        $params = json_decode($json, TRUE);

        if (empty($params['country_code'])) {
            return parent::sendError("invalid_country_code", 'Country code is required', 400);
        }

        if (empty($params['mobile'])) {
            return parent::sendError("invalid_mobile", 'Mobile is required', 400);
        }

        $mob = $params['country_code'] . $params['mobile'];
        $mobuser = getUserFromPhone($mob);
        if ($mobuser != null  || username_exists($mob)) {
            return parent::sendError("existed_mobile", 'Mobile Number already in use!', 400);
        }

        if (!empty($params['email']) && email_exists($params['email'])) {
            return parent::sendError("existed_email", 'Email already in use!', 400);
        }

        if (!empty($params['username']) && username_exists($params['username'])) {
            return parent::sendError("existed_username", 'Username already in use!', 400);
        }

        return  true;
    }

    function digits_register()
    {
        if (!function_exists('digits_create_user')) {
            return parent::sendError("plugin_not_found", "Please install the DIGITS: Wordpress Mobile Number Signup and Login plugin", 400);
        }

        define('REST_REQUEST', true);
        $this->mstore_digrest_set_variables();
        $userId = null;
        add_filter('digits_user_created_response', function ($data, $user_id) {
            $data['user_id'] = $user_id;
            return $data;
        }, 10, 2);
        $data = digits_create_user();
        define('REST_REQUEST', false);
        remove_filter('digits_user_created_response', '__return_false', 10);

        if ($data['success'] === false) {
            $message = explode("<br />",  $data['data']['msg'])[0];

            // Some Digits setups still require an email field at plugin level.
            if (empty($_POST['dig_reg_mail']) && preg_match('/email/i', wp_strip_all_tags($message))) {
                return parent::sendError("email_required", "This Digits configuration requires email. Please provide email or disable email-required in Digits settings.", 400);
            }

            return parent::sendError("invalid_data", $message, 400);
        } else {
            $user_id = $data['user_id'];

            $json = file_get_contents('php://input');
            $params = json_decode($json, TRUE);
            if (!empty($params['country_code']) && !empty($params['mobile'])) {
                $country_code = preg_replace('/[^\d+]/', '', sanitize_text_field($params['country_code']));
                $mobile = preg_replace('/[^\d]/', '', sanitize_text_field($params['mobile']));
                $phone = $country_code . $mobile;
                update_user_meta($user_id, 'billing_phone', $phone);
                update_user_meta($user_id, 'registered_phone_number', $phone);
                update_user_meta($user_id, 'digt_countrycode', $country_code);
                update_user_meta($user_id, 'digits_phone_no', $mobile);
                update_user_meta($user_id, 'digits_phone', $phone);
            }

            $update_data = array('ID' => $user_id);
            if (!empty($params['name'])) {
                $update_data['first_name'] = sanitize_text_field($params['name']);
                $update_data['display_name'] = sanitize_text_field($params['name']);
            }
            if (!empty($params['last_name'])) {
                $update_data['last_name'] = sanitize_text_field($params['last_name']);
                if (!empty($params['name'])) {
                    $update_data['display_name'] = sanitize_text_field($params['name'] . ' ' . $params['last_name']);
                }
            }
            if (!empty($params['email'])) {
                $update_data['user_email'] = sanitize_email($params['email']);
            }
            if (count($update_data) > 1) {
                wp_update_user($update_data);
            }

            $cookie = generateCookieByUserId($user_id);
            $user = get_userdata($user_id);

            $response['wp_user_id'] = $user_id;
            $response['cookie'] = $cookie;
            $response['user_login'] = $user->user_login;
            $response['user'] = $this->getResponseUserInfo($user);
            return $response;
        }
    }

    function digits_login_check()
    {
        if (!function_exists('digits_create_user')) {
            return parent::sendError("plugin_not_found", "Please install the DIGITS: Wordpress Mobile Number Signup and Login plugin", 400);
        }

        $json = file_get_contents('php://input');
        $params = json_decode($json, TRUE);

        if (empty($params['country_code'])) {
            return parent::sendError("invalid_country_code", 'Country code is required', 400);
        }

        if (empty($params['mobile'])) {
            return parent::sendError("invalid_mobile", 'Mobile is required', 400);
        }

        $mob = $params['country_code'] . $params['mobile'];
        $mobuser = getUserFromPhone($mob);
        if ($mobuser == null) {
            return parent::sendError("not_existed_mobile", 'Phone number is not registered!', 400);
        }

        return  true;
    }

    function digits_login()
    {
        if (!function_exists('dig_validateMobileNumber')) {
            return parent::sendError("plugin_not_found", "Please install the DIGITS: Wordpress Mobile Number Signup and Login plugin", 400);
        }

        $this->mstore_digrest_set_variables();

        $otp = $_POST['dig_otp'];
        $validateMob = dig_validateMobileNumber($_POST['digregcode'], $_POST['digits_reg_mail'], $otp, null, 1, null, false);

        if ($validateMob['success'] === false) {
            return parent::sendError("invalid_data", $validateMob['msg'], 400);
        }

        $user = getUserFromPhone($validateMob['countrycode'] . $validateMob['mobile']);
        $cookie = generateCookieByUserId($user->ID);
        $response['wp_user_id'] = $user->ID;
        $response['cookie'] = $cookie;
        $response['user_login'] = $user->user_login;
        $response['user'] = $this->getResponseUserInfo($user);
        return $response;
    }

    function digits_send_otp()
    {
        if (!function_exists('digits_create_user')) {
            return parent::sendError("plugin_not_found", "Please install the DIGITS: Wordpress Mobile Number Signup and Login plugin", 400);
        }

        $json = file_get_contents('php://input');
        $params = json_decode($json, TRUE);

        if (empty($params['country_code'])) {
            return parent::sendError("invalid_country_code", 'Country code is required', 400);
        }

        if (empty($params['mobile'])) {
            return parent::sendError("invalid_mobile", 'Mobile is required', 400);
        }

        $_REQUEST['countrycode'] =  $params['country_code'];
        $_REQUEST['mobileNo'] =  $params['mobile'];
        $_REQUEST['type'] =  $params['type'];

        $this->mstore_digrest_set_variables();


        $_REQUEST['csrf'] = wp_create_nonce('dig_form');
        $_POST['csrf'] = wp_create_nonce('dig_form');

        do_action('wp_ajax_nopriv_digits_check_mob');
    }

    function digits_resend_otp()
    {
        if (!function_exists('digits_resendotp')) {
            return parent::sendError("plugin_not_found", "Please install the DIGITS: Wordpress Mobile Number Signup and Login plugin", 400);
        }

        $json = file_get_contents('php://input');
        $params = json_decode($json, TRUE);

        if (empty($params['country_code'])) {
            return parent::sendError("invalid_country_code", 'Country code is required', 400);
        }

        if (empty($params['mobile'])) {
            return parent::sendError("invalid_mobile", 'Mobile is required', 400);
        }

        $_REQUEST['countrycode'] =  $params['country_code'];
        $_REQUEST['mobileNo'] =  $params['mobile'];
        $_REQUEST['type'] =  $params['type'];

        $this->mstore_digrest_set_variables();


        $_REQUEST['csrf'] = wp_create_nonce('dig_form');
        $_POST['csrf'] = wp_create_nonce('dig_form');

        digits_resendotp();
    }


    function custom_delete_item_permissions_check($request)
    {
        $cookie = get_header_user_cookie($request->get_header("User-Cookie"));
        if (isset($cookie) && $cookie != null && parent::checkApiPermission()) {
            $user_id = validateCookieLogin($cookie);
            if (is_wp_error($user_id)) {
                return false;
            }
            $request["id"] = $user_id;
            return true;
        } else {
            return false;
        }
    }

    function delete_account($request)
    {
        if (checkWhiteListAccounts($request["id"])) {
            return parent::sendError("invalid_account", "This account can't delete", 400);
        } else {
            require_once(ABSPATH . 'wp-admin/includes/user.php');
            return wp_delete_user($request["id"]);
        }
    }
}

```
