JezK
Edit File: advaxe-otp-ultimate.php
<?php /** * Plugin Name: Advaxe Custom Register with OTP (Ultimate V5) * Description: All-in-one WooCommerce Auth with Tabs, Custom Field Control, Page Protection, Smart Redirects & Custom OTP Format. * Version: 5.0 * Author: Advaxe */ if ( ! defined( 'ABSPATH' ) ) exit; class Advaxe_Custom_Register_OTP { const OPTION_GROUP = 'advaxe_otp_settings_group'; const OPTION_NAME = 'advaxe_otp_settings'; const OTP_KEY_PREFIX = 'advaxe_reg_otp_'; const RESET_OTP_KEY_PREFIX = 'advaxe_reset_otp_'; const RATE_LIMIT_TRANSIENT_PREFIX = 'advaxe_otp_rate_limit_'; public function __construct() { // Single Shortcode add_shortcode('advaxe_auth', [$this, 'render_auth_container']); // AJAX actions add_action('wp_ajax_nopriv_advaxe_send_otp', [$this, 'send_otp']); add_action('wp_ajax_nopriv_advaxe_verify_and_register', [$this, 'verify_and_register']); add_action('wp_ajax_nopriv_advaxe_register_no_otp', [$this, 'register_no_otp']); add_action('wp_ajax_nopriv_advaxe_process_login', [$this, 'process_login']); add_action('wp_ajax_nopriv_advaxe_send_reset_otp', [$this, 'send_reset_otp']); add_action('wp_ajax_nopriv_advaxe_verify_reset_otp_and_set_password', [$this, 'verify_reset_otp_and_set_password']); // Disable default WC registration add_filter('woocommerce_enable_myaccount_registration', '__return_false'); // Admin & Assets add_action('admin_menu', [$this, 'add_admin_menu']); add_action('admin_init', [$this, 'settings_init']); add_action('wp_head', [$this, 'print_inline_styles']); // Page Protection Redirect add_action('template_redirect', [$this, 'protect_pages_redirect']); } /** * -------------------------------------------------------------------------- * 1. ADMIN SETTINGS * -------------------------------------------------------------------------- */ public function add_admin_menu() { add_menu_page('Advaxe OTP', 'Advaxe OTP', 'manage_options', 'advaxe-otp-main', [$this, 'settings_page_general'], 'dashicons-shield', 55); add_submenu_page('advaxe-otp-main', 'General Settings', 'General Settings', 'manage_options', 'advaxe-otp-main', [$this, 'settings_page_general']); add_submenu_page('advaxe-otp-main', 'Field Control', 'Field Control', 'manage_options', 'advaxe-otp-fields', [$this, 'settings_page_fields']); add_submenu_page('advaxe-otp-main', 'Access & Redirects', 'Access & Redirects', 'manage_options', 'advaxe-otp-access', [$this, 'settings_page_access']); add_submenu_page('advaxe-otp-main', 'Design & Text', 'Design & Text', 'manage_options', 'advaxe-otp-design', [$this, 'settings_page_design']); } public function settings_init() { register_setting(self::OPTION_GROUP, self::OPTION_NAME, [$this, 'settings_sanitize']); // --- Tab 1: General (API & Methods) --- add_settings_section('advaxe_api_section', 'API & OTP Settings', null, 'advaxe-otp-main'); $gen_fields = [ 'sms_net_bd_api_key' => 'SMS.net.bd API Key', 'otp_company_name' => 'OTP Company Name', 'otp_expiry_minutes' => 'OTP Expiry (Minutes)', 'reg_method' => 'Registration Method', 'forgot_method' => 'Forgot Password Method', ]; foreach ($gen_fields as $id => $title) { add_settings_field($id, $title, [$this, 'render_field_general'], 'advaxe-otp-main', 'advaxe_api_section', ['id' => $id]); } // --- Tab 2: Field Control (Required/Optional/Hidden) --- add_settings_section('advaxe_field_section', 'Registration Fields Visibility', null, 'advaxe-otp-fields'); $field_controls = [ 'field_req_name' => 'Name Field', 'field_req_email' => 'Email Field', 'field_req_phone' => 'Phone Field', ]; foreach ($field_controls as $id => $title) { add_settings_field($id, $title, [$this, 'render_field_control'], 'advaxe-otp-fields', 'advaxe_field_section', ['id' => $id]); } // --- Tab 3: Access & Redirects --- add_settings_section('advaxe_access_section', 'Protection & Redirection', null, 'advaxe-otp-access'); $access_fields = [ 'redirect_after_login' => 'Redirect After Success Login/Reg', 'redirect_logged_in_user' => 'Redirect Already Logged-in User', 'enable_protection' => 'Enable Page Protection', 'protected_urls' => 'Protected Page URLs (Comma separated)', 'login_page_url' => 'Login Page URL (Destination)', ]; foreach ($access_fields as $id => $title) { add_settings_field($id, $title, [$this, 'render_access_field'], 'advaxe-otp-access', 'advaxe_access_section', ['id' => $id]); } // --- Tab 4: Design --- add_settings_section('advaxe_design_section', 'Appearance & Labels', null, 'advaxe-otp-design'); $design_fields = [ 'btn_bg_color' => 'Button Background Color', 'btn_text_color' => 'Button Text Color', 'btn_text_login' => 'Login Button Text', 'btn_text_reg' => 'Register Button Text', 'btn_text_otp' => 'Send OTP Button Text', ]; foreach ($design_fields as $id => $title) { add_settings_field($id, $title, [$this, 'render_design_field'], 'advaxe-otp-design', 'advaxe_design_section', ['id' => $id]); } } /** * Settings Sanitize: Preserves data across tabs */ public function settings_sanitize($input) { $existing = get_option(self::OPTION_NAME, []); if (!is_array($existing)) $existing = []; $merged = array_merge($existing, $input); $out = $merged; // General $out['sms_net_bd_api_key'] = sanitize_text_field($out['sms_net_bd_api_key'] ?? ''); $out['otp_company_name'] = sanitize_text_field($out['otp_company_name'] ?? get_bloginfo('name')); $out['otp_expiry_minutes'] = absint($out['otp_expiry_minutes'] ?? 5); $out['reg_method'] = sanitize_text_field($out['reg_method'] ?? 'both'); // Field Control $out['field_req_name'] = sanitize_text_field($out['field_req_name'] ?? 'required'); $out['field_req_email'] = sanitize_text_field($out['field_req_email'] ?? 'required'); $out['field_req_phone'] = sanitize_text_field($out['field_req_phone'] ?? 'required'); // Access $out['redirect_after_login'] = esc_url_raw($out['redirect_after_login'] ?? ''); $out['redirect_logged_in_user'] = esc_url_raw($out['redirect_logged_in_user'] ?? ''); // Handle Checkbox "Enable Protection" if(isset($_POST['option_page']) && $_POST['option_page'] === self::OPTION_GROUP && isset($input['login_page_url'])) { $out['enable_protection'] = isset($input['enable_protection']) ? 'yes' : 'no'; } elseif(!isset($out['enable_protection'])) { $out['enable_protection'] = 'no'; } $out['protected_urls'] = sanitize_textarea_field($out['protected_urls'] ?? ''); $out['login_page_url'] = esc_url_raw($out['login_page_url'] ?? ''); // Design $out['btn_bg_color'] = sanitize_hex_color($out['btn_bg_color'] ?? '#378d12'); return $out; } // --- Render Callbacks --- public function settings_page_general() { echo '<div class="wrap"><h1>General Settings</h1><form action="options.php" method="post">'; settings_fields(self::OPTION_GROUP); do_settings_sections('advaxe-otp-main'); submit_button(); echo '</form></div>'; } public function settings_page_fields() { echo '<div class="wrap"><h1>Field Visibility & Requirements</h1><form action="options.php" method="post">'; settings_fields(self::OPTION_GROUP); do_settings_sections('advaxe-otp-fields'); submit_button(); echo '</form></div>'; } public function settings_page_access() { echo '<div class="wrap"><h1>Access Control & Redirects</h1><form action="options.php" method="post">'; settings_fields(self::OPTION_GROUP); do_settings_sections('advaxe-otp-access'); submit_button(); echo '</form></div>'; } public function settings_page_design() { echo '<div class="wrap"><h1>Design & Text</h1><form action="options.php" method="post">'; settings_fields(self::OPTION_GROUP); do_settings_sections('advaxe-otp-design'); submit_button(); echo '</form></div>'; } private function get_opts() { return get_option(self::OPTION_NAME, [ 'sms_net_bd_api_key'=>'', 'otp_company_name'=>get_bloginfo('name'), 'otp_expiry_minutes'=>5, 'reg_method'=>'both', 'forgot_method'=>'both', 'field_req_name'=>'required', 'field_req_email'=>'required', 'field_req_phone'=>'required', 'redirect_after_login'=>'', 'redirect_logged_in_user'=>'', 'enable_protection'=>'no', 'protected_urls'=>'', 'login_page_url'=>'', 'btn_bg_color'=>'#378d12', 'btn_text_color'=>'#ffffff', 'btn_text_login'=>'Log In', 'btn_text_reg'=>'Register', 'btn_text_otp'=>'Send OTP' ]); } public function render_field_general($a) { $o=$this->get_opts(); $id=$a['id']; $val=$o[$id]; if($id==='reg_method' || $id==='forgot_method') { echo '<select name="'.self::OPTION_NAME.'['.$id.']">'; echo '<option value="both" '.selected($val,'both',false).'>Both (Email & Phone)</option>'; echo '<option value="email" '.selected($val,'email',false).'>Only Email</option>'; echo '<option value="phone" '.selected($val,'phone',false).'>Only Phone</option>'; if($id==='reg_method') echo '<option value="none" '.selected($val,'none',false).'>None (No OTP)</option>'; echo '</select>'; } elseif($id==='otp_expiry_minutes') { echo '<input type="number" name="'.self::OPTION_NAME.'['.$id.']" value="'.esc_attr($val).'" class="small-text"> minutes'; } else { echo '<input type="text" name="'.self::OPTION_NAME.'['.$id.']" value="'.esc_attr($val).'" class="regular-text">'; if($id === 'otp_company_name') echo '<p class="description">Used in SMS: "Your OTP is ... for [Company Name]"</p>'; } } public function render_field_control($a) { $o=$this->get_opts(); $id=$a['id']; $val=$o[$id]; echo '<select name="'.self::OPTION_NAME.'['.$id.']">'; echo '<option value="required" '.selected($val,'required',false).'>Required</option>'; echo '<option value="optional" '.selected($val,'optional',false).'>Optional</option>'; echo '<option value="hidden" '.selected($val,'hidden',false).'>Hidden (Disable)</option>'; echo '</select>'; } public function render_access_field($a) { $o=$this->get_opts(); $id=$a['id']; $val=$o[$id]; if($id === 'enable_protection') { echo '<input type="checkbox" name="'.self::OPTION_NAME.'['.$id.']" value="yes" '.checked($val,'yes',false).'> Enable protection for specific pages'; } elseif($id === 'protected_urls') { echo '<textarea name="'.self::OPTION_NAME.'['.$id.']" rows="4" cols="50" class="large-text" placeholder="/my-account, /checkout">'.esc_textarea($val).'</textarea>'; echo '<p class="description">Enter relative paths (e.g. <code>/my-account</code>) separated by commas. These pages will redirect logged-out users.</p>'; } elseif($id === 'login_page_url') { echo '<input type="text" name="'.self::OPTION_NAME.'['.$id.']" value="'.esc_attr($val).'" class="regular-text" placeholder="https://site.com/login">'; echo '<p class="description">Where to redirect logged-out users (The page with [advaxe_auth]).</p>'; } else { echo '<input type="text" name="'.self::OPTION_NAME.'['.$id.']" value="'.esc_attr($val).'" class="regular-text">'; echo '<p class="description">Leave empty for default behavior.</p>'; } } public function render_design_field($a) { $o=$this->get_opts(); $id=$a['id']; $t=(strpos($id,'color')!==false)?'color':'text'; echo '<input type="'.$t.'" name="'.self::OPTION_NAME.'['.$id.']" value="'.esc_attr($o[$id]??'').'" class="regular-text">'; } /** * -------------------------------------------------------------------------- * 2. REDIRECT LOGIC (Logged Out Protection) * -------------------------------------------------------------------------- */ public function protect_pages_redirect() { if(is_user_logged_in()) return; // Only for guests $opts = $this->get_opts(); if($opts['enable_protection'] !== 'yes') return; $target_url = $opts['login_page_url']; if(empty($target_url)) return; $protected_list = explode(',', $opts['protected_urls']); $current_uri = $_SERVER['REQUEST_URI']; foreach($protected_list as $path) { $path = trim($path); if(empty($path)) continue; if(strpos($current_uri, $path) !== false) { if(strpos($target_url, $current_uri) === false) { wp_redirect($target_url); exit; } } } } /** * -------------------------------------------------------------------------- * 3. CSS & STYLES * -------------------------------------------------------------------------- */ public function print_inline_styles() { $o = $this->get_opts(); ?> <style> #advaxe-auth-wrapper { max-width: 480px; margin: 40px auto; background: #fff; border: 1px solid #e5e5e5; border-radius: 8px; box-shadow: 0 4px 15px rgba(0,0,0,0.05); overflow: hidden; font-family: sans-serif; } .advaxe-tabs { display: flex; border-bottom: 1px solid #e5e5e5; background: #f9f9f9; } .advaxe-tab-item { flex: 1; text-align: center; padding: 15px; cursor: pointer; font-weight: 600; color: #777; transition: 0.3s; } .advaxe-tab-item:hover { background: #f0f0f0; } .advaxe-tab-item.active { background: #fff; color: <?php echo $o['btn_bg_color']; ?>; border-bottom: 2px solid <?php echo $o['btn_bg_color']; ?>; } .advaxe-auth-view { display: none; padding: 30px 25px; } .advaxe-auth-view.active { display: block; animation: fadeIn 0.4s; } @keyframes fadeIn { from{opacity:0;} to{opacity:1;} } .advaxe-form-group { margin-bottom: 15px; } .advaxe-form-group label { display: block; margin-bottom: 6px; font-weight: 500; color: #333; font-size: 14px; } .advaxe-form-control { width: 100%; padding: 12px; border: 1px solid #ddd; border-radius: 5px; font-size: 15px; box-sizing: border-box; } .advaxe-form-control:focus { border-color: <?php echo $o['btn_bg_color']; ?>; outline: none; } .advaxe-btn { display: block; width: 100%; padding: 14px; margin-top: 15px; background-color: <?php echo $o['btn_bg_color']; ?>; color: <?php echo $o['btn_text_color']; ?>; border: none; border-radius: 5px; font-size: 16px; font-weight: 600; cursor: pointer; } .advaxe-btn:hover { opacity: 0.9; } .advaxe-btn:disabled { background-color: #ccc; cursor: not-allowed; } .advaxe-text-center { text-align: center; margin-top: 20px; font-size: 14px; color: #666; } .advaxe-link { color: <?php echo $o['btn_bg_color']; ?>; text-decoration: none; font-weight: 500; cursor: pointer; } .advaxe-resp-msg { margin-top: 15px; padding: 10px; border-radius: 4px; text-align: center; font-size: 14px; } .advaxe-loading { display: inline-block; width: 14px; height: 14px; border: 2px solid #ccc; border-top-color: #333; border-radius: 50%; animation: spin 1s infinite linear; margin-right: 5px; vertical-align: middle; } @keyframes spin { 100% {transform:rotate(360deg);} } #advaxe-otp-timer { font-size: 13px; color: #888; text-align: center; margin-bottom: 10px; } </style> <?php } /** * -------------------------------------------------------------------------- * 4. SINGLE SHORTCODE RENDERER * -------------------------------------------------------------------------- */ public function render_auth_container($atts) { $opts = $this->get_opts(); // LOGGED IN REDIRECT LOGIC if ( is_user_logged_in() ) { $redirect_logged_in = !empty($opts['redirect_logged_in_user']) ? $opts['redirect_logged_in_user'] : wc_get_page_permalink('myaccount'); return '<div class="advaxe-resp-msg" style="color:green; border:1px solid green; padding:20px;"> You are already logged in. Redirecting... <script>setTimeout(function(){ window.location.href = "'.esc_url($redirect_logged_in).'"; }, 1500);</script> <br><a href="'.esc_url($redirect_logged_in).'">Click here if not redirected</a> </div>'; } $req_name = $opts['field_req_name']; $req_email = $opts['field_req_email']; $req_phone = $opts['field_req_phone']; ob_start(); ?> <div id="advaxe-auth-wrapper"> <div class="advaxe-tabs"> <div class="advaxe-tab-item active" data-target="view-login">Login</div> <div class="advaxe-tab-item" data-target="view-register">Register</div> </div> <div id="view-login" class="advaxe-auth-view active"> <form id="advaxe-login-form"> <div class="advaxe-form-group"><label>Username / Email / Phone</label><input type="text" name="username_email_phone" class="advaxe-form-control" required></div> <div class="advaxe-form-group"><label>Password</label><input type="password" name="password" class="advaxe-form-control" required></div> <?php wp_nonce_field('advaxe_login', 'advaxe_login_nonce'); ?> <button type="submit" class="advaxe-btn"><?php echo esc_html($opts['btn_text_login']); ?></button> <div class="advaxe-resp-msg"></div> <div class="advaxe-text-center"> <span class="advaxe-link switch-view" data-target="view-forgot">Forgot Password?</span> </div> </form> </div> <div id="view-register" class="advaxe-auth-view"> <form id="advaxe-register-form"> <?php if($req_name !== 'hidden'): ?> <div class="advaxe-form-group"><label>Name <?php echo ($req_name=='required')?'*':''; ?></label> <input type="text" name="name" class="advaxe-form-control" <?php echo ($req_name=='required')?'required':''; ?>> </div> <?php endif; ?> <?php if($req_phone !== 'hidden'): ?> <div class="advaxe-form-group"><label>Phone <?php echo ($req_phone=='required')?'*':''; ?></label> <input type="text" name="phone" class="advaxe-form-control" pattern="[0-9]{11,15}" <?php echo ($req_phone=='required')?'required':''; ?>> </div> <?php endif; ?> <?php if($req_email !== 'hidden'): ?> <div class="advaxe-form-group"><label>Email <?php echo ($req_email=='required')?'*':''; ?></label> <input type="email" name="email" class="advaxe-form-control" <?php echo ($req_email=='required')?'required':''; ?>> </div> <?php endif; ?> <div class="advaxe-form-group"><label>Password *</label><input type="password" name="password" class="advaxe-form-control" required minlength="6"></div> <?php $rm = $opts['reg_method']; if ($rm === 'both' && $req_email!=='hidden' && $req_phone!=='hidden'): ?> <div class="advaxe-form-group"><label>Verify Via</label><select name="verify_via" class="advaxe-form-control"><option value="email">Email</option><option value="phone">Phone</option></select></div> <?php elseif($rm === 'phone' || ($rm==='both' && $req_email==='hidden')): ?> <input type="hidden" name="verify_via" value="phone"> <?php elseif($rm === 'email' || ($rm==='both' && $req_phone==='hidden')): ?> <input type="hidden" name="verify_via" value="email"> <?php else: ?> <input type="hidden" name="verify_via" value="none"> <?php endif; ?> <?php wp_nonce_field('advaxe_reg', 'advaxe_nonce'); ?> <?php if ($rm === 'none'): ?> <button type="submit" class="advaxe-btn direct-reg"><?php echo esc_html($opts['btn_text_reg']); ?></button> <?php else: ?> <button type="button" id="advaxe-send-otp" class="advaxe-btn"><?php echo esc_html($opts['btn_text_otp']); ?></button> <div id="advaxe-otp-wrap" style="display:none;"> <div class="advaxe-form-group"><label>OTP Code</label><input type="text" name="otp_code" class="advaxe-form-control" maxlength="6" placeholder="Enter 6-digit code"></div> <p id="advaxe-otp-timer"></p> <button type="submit" id="advaxe-complete-reg" class="advaxe-btn"><?php echo esc_html($opts['btn_text_reg']); ?></button> </div> <?php endif; ?> <div class="advaxe-resp-msg"></div> </form> </div> <div id="view-forgot" class="advaxe-auth-view"> <form id="advaxe-forgot-form"> <h3 style="text-align:center;margin-top:0;">Reset Password</h3> <div class="advaxe-form-group"><label>Email or Phone</label><input type="text" name="user_identifier" class="advaxe-form-control" required></div> <?php $fm = $opts['forgot_method']; if ($fm === 'both'): ?> <div class="advaxe-form-group"><label>Verify Via</label><select name="verify_via" class="advaxe-form-control"><option value="email">Email</option><option value="phone">Phone</option></select></div> <?php else: ?> <input type="hidden" name="verify_via" value="<?php echo esc_attr($fm); ?>"> <?php endif; ?> <?php wp_nonce_field('advaxe_forgot_pass', 'advaxe_forgot_pass_nonce'); ?> <button type="button" id="advaxe-send-reset-otp" class="advaxe-btn"><?php echo esc_html($opts['btn_text_otp']); ?></button> <div id="advaxe-reset-otp-wrap" style="display:none;"> <div class="advaxe-form-group"><label>OTP Code</label><input type="text" name="otp_code" class="advaxe-form-control" maxlength="6"></div> <p id="advaxe-reset-otp-timer"></p> <div class="advaxe-form-group"><label>New Password</label><input type="password" name="new_password" class="advaxe-form-control" required minlength="6"></div> <div class="advaxe-form-group"><label>Confirm Password</label><input type="password" name="confirm_new_password" class="advaxe-form-control" required></div> <button type="submit" id="advaxe-set-new-pass" class="advaxe-btn">Set New Password</button> </div> <div class="advaxe-resp-msg"></div> <div class="advaxe-text-center"><span class="advaxe-link switch-view" data-target="view-login">Back to Login</span></div> </form> </div> </div> <script> (function($){ // --- TAB & VIEW SWITCHING --- $('.advaxe-tab-item').on('click', function(){ var t = $(this).data('target'); $('.advaxe-tab-item').removeClass('active'); $(this).addClass('active'); $('.advaxe-auth-view').removeClass('active'); $('#'+t).addClass('active'); }); $('.switch-view').on('click', function(){ var t = $(this).data('target'); $('.advaxe-auth-view').removeClass('active'); $('#'+t).addClass('active'); $('.advaxe-tab-item').removeClass('active'); if(t==='view-login') $('.advaxe-tab-item[data-target="view-login"]').addClass('active'); if(t==='view-register') $('.advaxe-tab-item[data-target="view-register"]').addClass('active'); }); // --- UTILS --- function msg(el, txt, type){ var color = (type=='success')?'green':(type=='error'?'red':'black'); var load = (type=='loading')?'<span class="advaxe-loading"></span>':''; el.find('.advaxe-resp-msg').html('<span style="color:'+color+'">'+load+txt+'</span>'); } function timer(dur, display, btn, wrap) { var t=dur, m, s; var inv = setInterval(function(){ m=parseInt(t/60,10); s=parseInt(t%60,10); display.text('Valid for: '+m+':'+(s<10?'0'+s:s)); if(--t<0){ clearInterval(inv); display.text('Expired'); btn.prop('disabled',false); wrap.slideUp(); } },1000); return inv; } // --- LOGIN --- $('#advaxe-login-form').on('submit', function(e){ e.preventDefault(); var f=$(this); var b=f.find('button'); msg(f,'Logging in...','loading'); b.prop('disabled',true); $.post('<?php echo admin_url('admin-ajax.php'); ?>', f.serialize()+'&action=advaxe_process_login', function(r){ if(r.success){ msg(f,r.data.message,'success'); setTimeout(function(){window.location=r.data.redirect},1000); } else{ msg(f,r.data.message,'error'); b.prop('disabled',false); } }); }); // --- REGISTER --- var regForm = $('#advaxe-register-form'); var otpInv; // NO OTP regForm.find('.direct-reg').on('click', function(e){ e.preventDefault(); var b=$(this); msg(regForm,'Registering...','loading'); b.prop('disabled',true); $.post('<?php echo admin_url('admin-ajax.php'); ?>', regForm.serialize()+'&action=advaxe_register_no_otp', function(r){ if(r.success){ msg(regForm,r.data.message,'success'); setTimeout(function(){window.location=r.data.redirect},1500); } else{ msg(regForm,r.data.message,'error'); b.prop('disabled',false); } }); }); // SEND OTP $('#advaxe-send-otp').on('click', function(e){ e.preventDefault(); var b=$(this); // Simple front validation var reqP = '<?php echo $opts['field_req_phone']; ?>'; var reqE = '<?php echo $opts['field_req_email']; ?>'; var ph = regForm.find('input[name="phone"]').val(); var em = regForm.find('input[name="email"]').val(); if(reqP!=='hidden' && !ph && reqE!=='hidden' && !em) { msg(regForm,'Please fill required fields','error'); return; } msg(regForm,'Sending OTP...','loading'); b.prop('disabled',true); $.post('<?php echo admin_url('admin-ajax.php'); ?>', regForm.serialize()+'&action=advaxe_send_otp', function(r){ if(r.success){ msg(regForm,r.data.message,'success'); $('#advaxe-otp-wrap').slideDown(); otpInv = timer(<?php echo $opts['otp_expiry_minutes']; ?>*60, $('#advaxe-otp-timer'), b, $('#advaxe-otp-wrap')); } else { msg(regForm,r.data.message,'error'); b.prop('disabled',false); } }); }); // VERIFY REG $('#advaxe-complete-reg').on('click', function(e){ e.preventDefault(); var b=$(this); msg(regForm,'Verifying...','loading'); b.prop('disabled',true); $.post('<?php echo admin_url('admin-ajax.php'); ?>', regForm.serialize()+'&action=advaxe_verify_and_register', function(r){ if(r.success){ clearInterval(otpInv); msg(regForm,r.data.message,'success'); setTimeout(function(){window.location=r.data.redirect},1500); } else { msg(regForm,r.data.message,'error'); b.prop('disabled',false); } }); }); // --- FORGOT PASSWORD --- var forgForm = $('#advaxe-forgot-form'); var forgInv; $('#advaxe-send-reset-otp').on('click', function(){ var b=$(this); msg(forgForm,'Sending OTP...','loading'); b.prop('disabled',true); $.post('<?php echo admin_url('admin-ajax.php'); ?>', forgForm.serialize()+'&action=advaxe_send_reset_otp', function(r){ if(r.success){ msg(forgForm,r.data.message,'success'); $('#advaxe-reset-otp-wrap').slideDown(); forgInv = timer(<?php echo $opts['otp_expiry_minutes']; ?>*60, $('#advaxe-reset-otp-timer'), b, $('#advaxe-reset-otp-wrap')); } else { msg(forgForm,r.data.message,'error'); b.prop('disabled',false); } }); }); $('#advaxe-set-new-pass').on('click', function(e){ e.preventDefault(); var b=$(this); msg(forgForm,'Resetting...','loading'); b.prop('disabled',true); $.post('<?php echo admin_url('admin-ajax.php'); ?>', forgForm.serialize()+'&action=advaxe_verify_reset_otp_and_set_password', function(r){ if(r.success){ clearInterval(forgInv); msg(forgForm,r.data.message,'success'); setTimeout(function(){window.location=r.data.redirect},1500); } else { msg(forgForm,r.data.message,'error'); b.prop('disabled',false); } }); }); })(jQuery); </script> <?php return ob_get_clean(); } /** * -------------------------------------------------------------------------- * 5. LOGIC Handlers (AJAX) * -------------------------------------------------------------------------- */ private function get_success_redirect() { $o = $this->get_opts(); return !empty($o['redirect_after_login']) ? $o['redirect_after_login'] : wc_get_page_permalink('myaccount'); } // UPDATED USERNAME LOGIC: Name based private function create_user_helper($email, $phone, $pass, $name) { $base = sanitize_user(str_replace(' ', '', strtolower($name)), true); if(empty($base)) $base = 'user'; $username = $base; $i = 1; while(username_exists($username)){ $username = $base . $i++; } if(empty($email)) $email = $username . '@noemail.com'; $uid = wc_create_new_customer($email, $username, $pass, ['first_name'=>$name,'billing_first_name'=>$name,'billing_phone'=>$phone]); if(!is_wp_error($uid)){ if(!empty($phone)) update_user_meta($uid,'billing_phone',$phone); update_user_meta($uid,'billing_first_name',$name); } return $uid; } public function register_no_otp() { check_ajax_referer('advaxe_reg','advaxe_nonce'); $o=$this->get_opts(); $p = isset($_POST['phone']) ? $_POST['phone'] : ''; $e = isset($_POST['email']) ? $_POST['email'] : ''; $n = isset($_POST['name']) ? $_POST['name'] : ''; if($o['field_req_email']=='required' && empty($e)) wp_send_json_error(['message'=>'Email is required']); if($o['field_req_phone']=='required' && empty($p)) wp_send_json_error(['message'=>'Phone is required']); if($o['field_req_name'] =='required' && empty($n)) wp_send_json_error(['message'=>'Name is required']); if(!empty($e) && email_exists($e)) wp_send_json_error(['message'=>'Email already exists.']); if(!empty($p) && !empty(get_users(['meta_key'=>'billing_phone','meta_value'=>$p]))) wp_send_json_error(['message'=>'Phone already exists.']); $uid = $this->create_user_helper($e,$p,$_POST['password'],$n); if(is_wp_error($uid)) wp_send_json_error(['message'=>$uid->get_error_message()]); wc_set_customer_auth_cookie($uid); wp_send_json_success(['message'=>'Registration Successful!','redirect'=>$this->get_success_redirect()]); } public function send_otp() { check_ajax_referer('advaxe_reg','advaxe_nonce'); $o=$this->get_opts(); $via=$_POST['verify_via']; $p = isset($_POST['phone']) ? $_POST['phone'] : ''; $e = isset($_POST['email']) ? $_POST['email'] : ''; $n = isset($_POST['name']) ? $_POST['name'] : ''; if($via=='phone' && empty($p)) wp_send_json_error(['message'=>'Phone number is required for OTP.']); if($via=='email' && empty($e)) wp_send_json_error(['message'=>'Email is required for OTP.']); if(!empty($e) && email_exists($e)) wp_send_json_error(['message'=>'Email already exists.']); if(!empty($p) && !empty(get_users(['meta_key'=>'billing_phone','meta_value'=>$p]))) wp_send_json_error(['message'=>'Phone already exists.']); $dest=($via=='phone')?$p:$e; if($this->limit($dest)) wp_send_json_error(['message'=>'Too many attempts. Wait a bit.']); $otp=wp_rand(100000,999999); $mins=$o['otp_expiry_minutes']; set_transient(self::OTP_KEY_PREFIX.md5($dest), [ 'otp'=>$otp,'name'=>$n,'phone'=>$p,'email'=>$e,'password'=>$_POST['password'] ], $mins*60); $this->notify($via, $e, $p, $otp, 'Register', $mins); wp_send_json_success(['message'=>'OTP Sent Successfully.']); } public function verify_and_register() { check_ajax_referer('advaxe_reg','advaxe_nonce'); $via = $_POST['verify_via']; $dest=($via=='phone')?$_POST['phone']:$_POST['email']; $d=get_transient(self::OTP_KEY_PREFIX.md5($dest)); if(!$d || $d['otp'] != $_POST['otp_code']) wp_send_json_error(['message'=>'Invalid or Expired OTP']); $uid=$this->create_user_helper($d['email'], $d['phone'], $d['password'], $d['name']); if(is_wp_error($uid)) wp_send_json_error(['message'=>$uid->get_error_message()]); delete_transient(self::OTP_KEY_PREFIX.md5($dest)); wc_set_customer_auth_cookie($uid); wp_send_json_success(['message'=>'Verified & Registered!','redirect'=>$this->get_success_redirect()]); } public function process_login() { check_ajax_referer('advaxe_login','advaxe_login_nonce'); $in=$_POST['username_email_phone']; $u=get_user_by('email',$in)?:get_user_by('login',$in); if(!$u){ $us=get_users(['meta_key'=>'billing_phone','meta_value'=>$in,'number'=>1]); if($us)$u=$us[0]; } if(!$u) wp_send_json_error(['message'=>'User not found.']); $s=wp_signon(['user_login'=>$u->user_login,'user_password'=>$_POST['password']],false); if(is_wp_error($s)) wp_send_json_error(['message'=>'Wrong password.']); wc_set_customer_auth_cookie($s->ID); wp_send_json_success(['message'=>'Login Success!','redirect'=>$this->get_success_redirect()]); } public function send_reset_otp() { check_ajax_referer('advaxe_forgot_pass','advaxe_forgot_pass_nonce'); $in=$_POST['user_identifier']; $via=$_POST['verify_via']; $u=($via=='email')?get_user_by('email',$in):null; if(!$u && $via=='phone'){ $us=get_users(['meta_key'=>'billing_phone','meta_value'=>$in,'number'=>1]); if($us)$u=$us[0]; } if(!$u) wp_send_json_error(['message'=>'User not found.']); if($this->limit('reset_'.$in)) wp_send_json_error(['message'=>'Too many attempts.']); $otp=wp_rand(100000,999999); $mins=$this->get_opts()['otp_expiry_minutes']; set_transient(self::RESET_OTP_KEY_PREFIX.md5($in),['otp'=>$otp,'uid'=>$u->ID],$mins*60); $this->notify($via,$u->user_email,$in,$otp,'Reset Password',$mins); wp_send_json_success(['message'=>'OTP Sent.']); } public function verify_reset_otp_and_set_password() { check_ajax_referer('advaxe_forgot_pass','advaxe_forgot_pass_nonce'); if($_POST['new_password']!==$_POST['confirm_new_password']) wp_send_json_error(['message'=>'Passwords mismatch.']); $k=self::RESET_OTP_KEY_PREFIX.md5($_POST['user_identifier']); $d=get_transient($k); if(!$d || $d['otp']!=$_POST['otp_code']) wp_send_json_error(['message'=>'Invalid OTP.']); wp_set_password($_POST['new_password'],$d['uid']); delete_transient($k); wc_set_customer_auth_cookie($d['uid']); wp_send_json_success(['message'=>'Password reset!','redirect'=>$this->get_success_redirect()]); } // 5. HELPER: NOTIFY WITH CUSTOM FORMAT private function notify($via, $e, $p, $otp, $type, $m){ $opts = $this->get_opts(); $company = $opts['otp_company_name'] ? $opts['otp_company_name'] : 'Advaxe'; // CUSTOM FORMAT: "Your OTP is [OTP] for [Company]" $msg = "Your OTP is $otp for $company"; if($via=='email') { wp_mail($e, "$company - $type OTP", $msg); } else { $k=$opts['sms_net_bd_api_key']; if($k) wp_remote_post('https://api.sms.net.bd/sendsms',['body'=>['api_key'=>$k,'msg'=>$msg,'to'=>$p]]); } } private function limit($id){ $k=self::RATE_LIMIT_TRANSIENT_PREFIX.md5($id); $c=get_transient($k); if($c===false){set_transient($k,1,60);return false;} if($c>=3)return true; set_transient($k,$c+1,60); return false; } } new Advaxe_Custom_Register_OTP();