Plugin Directory

Changeset 3427655


Ignore:
Timestamp:
12/26/2025 11:24:15 AM (3 months ago)
Author:
rijensky
Message:

Update to version 2.3.9

Location:
otppal/trunk
Files:
2 added
7 edited

Legend:

Unmodified
Added
Removed
  • otppal/trunk/assets/js/integrations/otppal-login.js

    r3427485 r3427655  
    136136        const redirectTo = currentRedirectUrl || (typeof otppalLogin !== 'undefined' ? otppalLogin.redirectTo : '');
    137137
     138        // Get country code from login lightbox if available
     139        let countryCode = null;
     140        if (loginLightbox && loginLightbox.countryCode) {
     141            countryCode = loginLightbox.countryCode;
     142        }
     143
    138144        $.ajax({
    139145            url: apiUrl,
     
    146152                hash: hash,
    147153                otp: otpCode,
    148                 redirect_to: redirectTo
     154                redirect_to: redirectTo,
     155                country_code: countryCode
    149156            }),
    150157            success: function(response) {
  • otppal/trunk/assets/js/otppal-lightbox.js

    r3427485 r3427655  
    130130        this.otpHash = null;
    131131        this.phoneNumber = '';
     132        this.countryCode = null;
    132133        this.isLoadingOTP = false;
    133134        this.$modal = null;
     
    547548            }
    548549
     550            // Get country code from phone input
     551            let countryCode = null;
     552            if (this.phoneInput && this.phoneInput.countrySelect) {
     553                countryCode = this.phoneInput.countrySelect.find(':selected').val();
     554            }
     555
    549556            // Use local WordPress REST API endpoint (proxies to server)
    550557            const apiUrl = (typeof otppalConfig !== 'undefined' && otppalConfig.restUrl)
     
    559566                },
    560567                data: JSON.stringify({
    561                     phone
     568                    phone: phone,
     569                    country_code: countryCode
    562570                }),
    563571                success: function(response) {
     
    566574                    }
    567575                   
    568                     if (response.success && response.hash) {
    569                         self.otpHash = response.hash;
     576                    // Check for hash in response (handle different response formats)
     577                    const hash = response.hash || (response.data && response.data.hash);
     578                    const isSuccess = response.success !== false && hash; // Treat as success if hash exists and success is not explicitly false
     579                   
     580                    if (hash) {
     581                        self.otpHash = hash;
    570582                        self.phoneNumber = phone;
     583                        self.countryCode = countryCode;
    571584                        // Move to OTP state (whether from loading or phone state)
    572585                        self.setState('otp');
     
    579592                            }
    580593                        }
    581                         self.setError(response.message || __('Failed to send OTP. Please try again.', 'otppal'));
     594                        // Extract error message from various possible response formats
     595                        const errorMsg = response.message
     596                            || (response.data && response.data.message)
     597                            || (response.data && response.data.data && response.data.data.message)
     598                            || __('Failed to send OTP. Please try again.', 'otppal');
     599                        self.setError(errorMsg);
    582600                    }
    583601                },
  • otppal/trunk/includes/class-otppal-api.php

    r3427485 r3427655  
    3030        public function send_otp($phone)
    3131        {
    32             return new WP_Error('otppal_not_configured', $this->api_key);
    3332            if (empty($this->api_key)) {
    3433                return new WP_Error('otppal_not_configured', 'OtpPal API key must be configured.');
  • otppal/trunk/includes/class-otppal-user-lookup.php

    r3423725 r3427655  
    1313    {
    1414        /**
     15         * Cached country dial codes array
     16         * Loaded from data/country-dial-codes.php file
     17         */
     18        private static $country_dial_codes = null;
     19
     20        /**
    1521         * Normalize phone number for comparison
    1622         * Removes all non-digit characters
     
    2632
    2733        /**
     34         * Get dial code for a country code
     35         * Loads country codes from the dedicated file
     36         *
     37         * @param string $country_code ISO country code (e.g., 'IL', 'US', 'il', 'us')
     38         * @return string|null Dial code (e.g., '972', '1') or null if not found
     39         */
     40        private static function get_dial_code($country_code)
     41        {
     42            if (empty($country_code)) {
     43                return null;
     44            }
     45
     46            // Load country codes file if not already loaded
     47            if (self::$country_dial_codes === null) {
     48                $codes_file = OTPPAL_PATH . 'data/country-dial-codes.php';
     49                if (file_exists($codes_file)) {
     50                    self::$country_dial_codes = require $codes_file;
     51                } else {
     52                    self::$country_dial_codes = array();
     53                }
     54            }
     55
     56            // Convert to lowercase for lookup (file uses lowercase keys)
     57            $country_code_lower = strtolower($country_code);
     58
     59            if (isset(self::$country_dial_codes[$country_code_lower])) {
     60                return self::$country_dial_codes[$country_code_lower];
     61            }
     62
     63            return null;
     64        }
     65
     66        /**
    2867         * Find user by phone number
    2968         * Searches in WooCommerce billing_phone and custom otppal_phone meta
    3069         *
    3170         * @param string $phone Phone number (will be normalized)
     71         * @param string|null $country_code ISO country code (e.g., 'IL', 'US') for better format matching
    3272         * @return int|null User ID if found, null otherwise
    3373         */
    34         public static function find_user_by_phone($phone)
     74        public static function find_user_by_phone($phone, $country_code = null)
    3575        {
    3676            if (empty($phone)) {
     
    4686            // First, try WooCommerce billing_phone if WooCommerce is active
    4787            if (class_exists('WooCommerce') || function_exists('wc_get_customer')) {
    48                 $user_id = self::find_user_by_meta('billing_phone', $normalized_phone);
     88                $user_id = self::find_user_by_meta('billing_phone', $normalized_phone, $country_code);
    4989                if ($user_id) {
    5090                    return $user_id;
    5191                }
     92
     93                // Also check shipping_phone (WooCommerce stores phone in both locations)
     94                $user_id = self::find_user_by_meta('shipping_phone', $normalized_phone, $country_code);
     95                if ($user_id) {
     96                    return $user_id;
     97                }
    5298            }
    5399
    54100            // Fallback to custom otppal_phone meta
    55             $user_id = self::find_user_by_meta('otppal_phone', $normalized_phone);
     101            $user_id = self::find_user_by_meta('otppal_phone', $normalized_phone, $country_code);
    56102            if ($user_id) {
    57103                return $user_id;
     
    68114         * @param string $meta_key Meta key to search
    69115         * @param string $phone Phone number (already normalized)
     116         * @param string|null $country_code ISO country code for generating correct format variations
    70117         * @return int|null User ID if found, null otherwise
    71118         */
    72         private static function find_user_by_meta($meta_key, $normalized_phone)
     119        private static function find_user_by_meta($meta_key, $normalized_phone, $country_code = null)
    73120        {
    74121            global $wpdb;
     
    87134            }
    88135
    89             // Generate alternative formats to try
    90             // If phone starts with country code (e.g., 972), also try without it
    91             // If phone doesn't start with 0, try with leading 0
    92             $formats_to_try = array($normalized_phone);
    93            
    94             // If international format (starts with country code like 972), try without country code
    95             if (strlen($normalized_phone) > 9 && (substr($normalized_phone, 0, 3) === '972' || substr($normalized_phone, 0, 2) === '1')) {
    96                 // Remove country code and try
    97                 if (substr($normalized_phone, 0, 3) === '972') {
    98                     $formats_to_try[] = substr($normalized_phone, 3); // Remove 972
    99                     $formats_to_try[] = '0' . substr($normalized_phone, 3); // Add leading 0
    100                 } elseif (substr($normalized_phone, 0, 2) === '1') {
    101                     $formats_to_try[] = substr($normalized_phone, 1); // Remove 1
    102                 }
    103             } else {
    104                 // If national format (starts with 0), try with country code
    105                 if (substr($normalized_phone, 0, 1) === '0') {
    106                     $formats_to_try[] = substr($normalized_phone, 1); // Remove leading 0
    107                     $formats_to_try[] = '972' . substr($normalized_phone, 1); // Add country code (Israel)
    108                 } else {
    109                     // No leading 0, try adding it
    110                     $formats_to_try[] = '0' . $normalized_phone;
    111                     $formats_to_try[] = '972' . $normalized_phone; // Add country code
    112                 }
    113             }
     136            // Generate format variations for the search phone
     137            $search_formats = self::generate_phone_formats($normalized_phone, $country_code);
    114138
    115139            // Compare normalized phone numbers
    116140            foreach ($results as $row) {
    117141                $stored_phone = self::normalize_phone($row->meta_value);
    118                
    119                 // Try exact match first
    120                 if (in_array($stored_phone, $formats_to_try, true)) {
    121                     return (int) $row->user_id;
    122                 }
    123                
    124                 // Also try comparing stored phone in different formats
    125                 $stored_formats = array($stored_phone);
    126                 if (strlen($stored_phone) > 9 && substr($stored_phone, 0, 3) === '972') {
    127                     $stored_formats[] = substr($stored_phone, 3);
    128                     $stored_formats[] = '0' . substr($stored_phone, 3);
    129                 } elseif (substr($stored_phone, 0, 1) === '0') {
    130                     $stored_formats[] = substr($stored_phone, 1);
    131                     $stored_formats[] = '972' . substr($stored_phone, 1);
    132                 }
    133                
     142
     143                // Generate format variations for the stored phone
     144                $stored_formats = self::generate_phone_formats($stored_phone, $country_code);
     145
    134146                // Check if any format matches
    135                 foreach ($formats_to_try as $format) {
    136                     if (in_array($format, $stored_formats, true)) {
     147                foreach ($search_formats as $search_format) {
     148                    if (in_array($search_format, $stored_formats, true)) {
    137149                        return (int) $row->user_id;
    138150                    }
     
    141153
    142154            return null;
     155        }
     156
     157        /**
     158         * Generate all possible phone number format variations for comparison
     159         * Handles international (with country code), national (with leading 0), and local formats
     160         *
     161         * @param string $phone Normalized phone (digits only)
     162         * @param string|null $country_code ISO country code (e.g., 'IL', 'US')
     163         * @return array Array of all possible format variations
     164         */
     165        private static function generate_phone_formats($phone, $country_code = null)
     166        {
     167            $formats = array($phone); // Always include original
     168
     169            $dial_code = null;
     170            if ($country_code) {
     171                $dial_code = self::get_dial_code($country_code);
     172            }
     173
     174            // If country code provided and we have dial code
     175            if ($dial_code) {
     176                $dial_code_digits = $dial_code;
     177                $dial_code_length = strlen($dial_code_digits);
     178
     179                // Check if phone starts with dial code
     180                if (substr($phone, 0, $dial_code_length) === $dial_code_digits) {
     181                    // Phone has dial code - generate formats without it
     182                    $without_dial = substr($phone, $dial_code_length);
     183                    $formats[] = $without_dial;
     184                    $formats[] = '0' . $without_dial; // Add leading 0
     185                } else {
     186                    // Phone doesn't have dial code - generate formats with it
     187                    $formats[] = $dial_code_digits . $phone; // Add dial code
     188
     189                    // If phone starts with 0, also try without 0 and with dial code
     190                    if (substr($phone, 0, 1) === '0') {
     191                        $without_zero = substr($phone, 1);
     192                        $formats[] = $without_zero;
     193                        $formats[] = $dial_code_digits . $without_zero;
     194                    } else {
     195                        // No leading 0, try adding it
     196                        $formats[] = '0' . $phone;
     197                    }
     198                }
     199            } else {
     200                // No country code provided - fall back to common patterns
     201                // Try to detect if phone starts with common dial codes
     202                $common_dial_codes = array('972', '1', '44', '33', '49', '39', '34', '7', '81', '86', '91');
     203
     204                foreach ($common_dial_codes as $code) {
     205                    $code_length = strlen($code);
     206                    if (substr($phone, 0, $code_length) === $code) {
     207                        // Phone starts with this dial code
     208                        $without_code = substr($phone, $code_length);
     209                        $formats[] = $without_code;
     210                        $formats[] = '0' . $without_code;
     211                        break;
     212                    }
     213                }
     214
     215                // If phone starts with 0, generate formats without it
     216                if (substr($phone, 0, 1) === '0') {
     217                    $without_zero = substr($phone, 1);
     218                    $formats[] = $without_zero;
     219
     220                    // Try adding common dial codes
     221                    foreach ($common_dial_codes as $code) {
     222                        $formats[] = $code . $without_zero;
     223                    }
     224                } else {
     225                    // No leading 0, try adding it
     226                    $formats[] = '0' . $phone;
     227
     228                    // Try adding common dial codes
     229                    foreach ($common_dial_codes as $code) {
     230                        $formats[] = $code . $phone;
     231                    }
     232                }
     233            }
     234
     235            // Remove duplicates and return
     236            return array_unique($formats);
    143237        }
    144238
  • otppal/trunk/otppal.php

    r3427485 r3427655  
    2121// Define plugin constants
    2222if (!defined('OTPPAL_VERSION')) {
    23     define('OTPPAL_VERSION', '2.3.8');
     23    define('OTPPAL_VERSION', '2.3.9');
    2424}
    2525if (!defined('OTPPAL_PATH')) {
  • otppal/trunk/public/class-otppal-public.php

    r3423725 r3427655  
    240240
    241241            $phone = $request->get_param('phone');
     242            $country_code = $request->get_param('country_code');
     243
    242244            if (empty($phone)) {
    243245                return new WP_Error(
     
    254256            // If OTP login is enabled, check user existence before sending OTP
    255257            if (get_option('otppal_enable_otp_login', '0') === '1') {
    256                 // Check if user exists with this phone number
    257                 $user_id = OtpPal_User_Lookup::find_user_by_phone($phone);
     258                // Check if user exists with this phone number, passing country code for better matching
     259                $user_id = OtpPal_User_Lookup::find_user_by_phone($phone, $country_code);
    258260
    259261                if (!$user_id) {
     
    457459            $hash = $request->get_param('hash');
    458460            $otp = $request->get_param('otp');
     461            $country_code = $request->get_param('country_code');
    459462
    460463            // Validate required parameters
     
    487490            }
    488491
    489             // Find user by phone number
    490             $user_id = OtpPal_User_Lookup::find_user_by_phone($phone);
     492            // Find user by phone number, passing country code for better matching
     493            $user_id = OtpPal_User_Lookup::find_user_by_phone($phone, $country_code);
    491494
    492495            if (!$user_id) {
  • otppal/trunk/readme.txt

    r3427485 r3427655  
    66Tested up to: 6.9
    77Requires PHP: 7.4
    8 Stable tag: 2.3.8
     8Stable tag: 2.3.9
    99License: GPL2
    1010License URI: https://www.gnu.org/licenses/gpl-2.0.html
    1111
    12 Client-side plugin for OtpPal Server – Send and verify OTP messages via WhatsApp.
     12Client-side plugin for OtpPal – Send and verify OTPs via WhatsApp.
    1313
    1414== Description ==
    1515
    16 OtpPal is a client-side WordPress plugin that connects to your OtpPal Server installation to send and verify OTP (One-Time Password) messages via WhatsApp. This plugin enables secure phone number verification for your WordPress forms, helping prevent spam and ensuring legitimate user submissions.
     16OtpPal is a client-side WordPress plugin that connects to otppal.com to send and verify OTP (One-Time Password) messages via WhatsApp. This plugin enables secure phone number verification for your WordPress forms, helping prevent spam and ensuring legitimate user submissions.
    1717
    1818**Key Features:**
     
    2323* **Easy Configuration**: Simple setup through WordPress admin panel
    2424* **Secure Verification**: Server-side validation ensures secure OTP verification
    25 * **Rate Limiting**: Built-in protection against abuse (handled by OtpPal Server)
     25* **Rate Limiting**: Built-in protection against abuse (OtpPal.com)
    2626* **Customizable UI**: Clean, responsive lightbox interface for OTP input
    2727
     
    37371. Upload the plugin files to `/wp-content/plugins/otppal` directory, or install through the WordPress plugins screen directly
    38382. Activate the plugin through the 'Plugins' menu in WordPress
    39 3. Ensure you have an active OtpPal Server installation
    40 4. Go to OtpPal > Settings in WordPress admin to configure the plugin
    41 5. Enter your OtpPal Server URL and API key
    42 6. Configure phone format preferences (international or national)
     393. Go to OtpPal > Settings in WordPress admin to configure the plugin
     404. Enter your API key (get it at https://otppal.com)
     415. Configure phone format preferences (international or national)
    4342
    4443== Configuration ==
    4544
    46451. Navigate to **OtpPal > Settings** in your WordPress admin panel
    47 2. Enter your **OtpPal Server URL** (e.g., https://otppal.com or your server URL)
    48 3. Enter your **API key** from the OtpPal Server
    49 4. Choose your preferred **Phone Format**:
     462. Enter your OtpPal **API key**
     473. Choose your preferred **Phone Format**:
    5048   * International: Users enter phone numbers with country code (e.g., +1234567890)
    5149   * National: Users enter phone numbers without country code (e.g., 1234567890)
    52 5. If using National format, select a **Default Country** for phone number validation
    53 6. Click **Save Settings**
     504. If using National format, select a **Default Country** for phone number validation
     515. Click **Save Settings**
    5452
    5553== Usage ==
     
    8179**Note:** `[otppal_verify]` and `[otppal_phone]` cannot be used together in the same form.
    8280
    83 **WPForms, Gravity Forms, Fluent Forms, Ninja Forms, Formidable Forms, SureForms:**
     81**WPForms, Gravity Forms, Fluent Forms, Ninja Forms, Formidable Forms, SureForms, Elementor Pro:**
    8482
    8583For these builders, OtpPal uses CSS classes on existing fields instead of custom shortcodes:
     
    110108== Frequently Asked Questions ==
    111109
    112 = Do I need an OtpPal Server installation? =
    113 
    114 Yes, this plugin requires a separate OtpPal Server installation. This plugin is the client-side component that communicates with your server.
     110= Does this plugin enable OTP login for my users?
     111
     112Yes! with a fully customizable login button. Just enable it in the settings, and use the shortcode to embed it anywhere on your site.
    115113
    116114= How do I get an API key? =
    117115
    118 You can obtain an API key from your OtpPal Server installation. Check your server's documentation for API key generation.
     116You can obtain an API key from OtpPal at https://otppal.com.
    119117
    120118= Does this work with other form plugins? =
    121119
    122 Currently, OtpPal supports Contact Form 7. Support for other form plugins may be added in future versions.
     120OtpPal supports many different integrations including Contact Form 7, Elementor Pro, WPForms, Gravity Forms, Fluent Forms, Ninja Forms, Formidable Forms, and SureForms. See otppal.com for more information.
    123121
    124122= What phone formats are supported? =
    125123
    126 The plugin supports both international format (with country code) and national format (without country code). You can configure this in the plugin settings.
     124The plugin supports both international format (with country code) and national format (without country code). You can configure this in the plugin settings. When using international format, the plugin automatically detects the user's country based on their IP address for a better user experience.
    127125
    128126= Is my data secure? =
    129127
    130 Yes. The plugin uses secure REST API communication with your OtpPal Server. API keys are stored securely in WordPress options and never exposed to the frontend.
     128Yes. The plugin uses secure REST API communication with the OtpPal Servers. Phone numbers aren't saved anywhere.
    131129
    132130= Can I customize the OTP verification interface? =
     
    145143
    146144== Changelog ==
     145= 2.3.9 =
     146* Enhanced phone number lookup to support both billing and shipping phone fields
     147* Improved OTP lightbox state transitions after errors
     148* Fixed compatibility issues with WooCommerce admin classes
     149
     150= 2.3.8 =
     151* Stability improvements
     152
     153= 2.3.7 =
     154* Stability improvements
     155
     156= 2.3.6 =
     157* Country autodetect on international format
     158
     159= 2.3.5 =
     160* Security and UI enhancements
     161
     162= 2.3.4 =
     163* Security enhancements
     164
     165= 2.3.3 =
     166* Security enhancements
     167
     168= 2.3.2 =
     169* Javascript improvements
     170
     171= 2.3.1 =
     172* Stability improvements
     173
     174= 2.3.0 =
     175* Rebuild user lookup
     176
     177= 2.2.0 =
     178* Rebuild plugin to server authentication
     179
     180= 2.1.0 =
     181* Add customization options
     182
     183= 2.0.0 =
     184* Improve security
     185* Add OTP login
     186
     187= 1.0.3 =
     188* Add form plugin integrations
     189
     190= 1.0.2 =
     191* Add form plugin integrations
     192
     193= 1.0.1 =
     194* Stability improvements
    147195
    148196= 1.0.0 =
     
    152200* OTP verification via WhatsApp
    153201* Admin settings page
    154 * REST API integration with OtpPal Server
     202* REST API integration with the OtpPal server
    155203
    156204== Upgrade Notice ==
     205= 2.3.9 =
     206Enhanced phone number matching and improved error handling in OTP verification flow.
     207
     208= 2.3.8 =
     209Rebuild plugin with security enhancements and new json api url structure
    157210
    158211= 1.0.0 =
    159 Initial release of OtpPal client plugin. Requires OtpPal Server installation.
     212Initial release of OtpPal client plugin.
    160213
    161214== Support ==
    162215
    163 For support, please visit the plugin's support forum or contact the plugin author. For issues related to OtpPal Server, please refer to your server's documentation.
     216For support, please visit the plugin's support forum or contact the plugin author.
Note: See TracChangeset for help on using the changeset viewer.