403Webshell
Server IP : 65.1.209.187  /  Your IP : 216.73.216.144
Web Server : nginx/1.24.0
System : Linux ip-172-31-5-206 6.14.0-1015-aws #15~24.04.1-Ubuntu SMP Tue Sep 23 22:44:48 UTC 2025 x86_64
User :  ( 1001)
PHP Version : 8.3.6
Disable Function : NONE
MySQL : OFF  |  cURL : ON  |  WGET : ON  |  Perl : ON  |  Python : OFF  |  Sudo : ON  |  Pkexec : OFF
Directory :  /var/www/ttc_wordpress/wp-content/themes/ttc/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ Back ]     

Current File : /var/www/ttc_wordpress/wp-content/themes/ttc/functions.php
<?php

require_once ABSPATH . '/vendor/autoload.php';

// Define Template Path
define('MARPATH', get_template_directory_uri());

define('ISSPATH', get_template_directory_uri());
define('THEMEPATH', get_template_directory_uri());
add_filter('wpcf7_autop_or_not', '__return_false');


// Widgets
register_sidebar(array(
    'name' => 'Home right sidebar',
    'id' => 'home_right_1',
    'before_widget' => '',
    'after_widget' => '',
    'before_title' => '<h3>',
    'after_title' => '</h3>',
));
register_sidebar(array(
    'name' => 'Inner right sidebar',
    'id' => 'inner_right_1',
    'before_widget' => '',
    'after_widget' => '',
    'before_title' => '',
    'after_title' => '',
));


// rss thingy
add_theme_support('automatic-feed-links');

// Clean up the <head>
function removeHeadLinks() {
        remove_action('wp_head', 'rsd_link');
        remove_action('wp_head', 'wlwmanifest_link');
}

add_action('init', 'removeHeadLinks');
remove_action('wp_head', 'wp_generator');

/*
  Add Includes
 */
require_once('functions/functions.php');
require_once('functions/enqueue.php');
// require_once('wp_gopu_navwalker.php');
//
add_theme_support('post-thumbnails');

if (function_exists('add_image_size')) {
        add_image_size('banner', 1000, 820, true);
        add_image_size('package_grid', 352, 416, true);
        add_image_size('square', 280, 280, true);
         add_image_size('guest-rules-image', 600, 450, true); // 4:3 aspect ratio, cropped

}
add_filter('image_size_names_choose', 'add_guest_rules_image_size_to_media_library');
function add_guest_rules_image_size_to_media_library($sizes) {
    return array_merge($sizes, array(
        'guest-rules-image' => __('Guest Rules Image')
    ));
}

// Shortern Text---------------------------------------------------
function shortentext($text, $chars_limit) {
	    $text = strip_tags($text);
        $chars_text = strlen($text);
        $text = $text . " ";
        $text = substr($text, 0, $chars_limit);
        if ($chars_text > $chars_limit) {
                $text = $text . "...";
        }
        return $text;
}


// Add Java Script



// Excerpt in Page

function add_page_excerpt_support() {
        add_post_type_support('page', 'excerpt');
}

add_action('admin_init', 'add_page_excerpt_support');


// Display Image in column
// Add the posts and pages columns filter. They can both use the same function.
add_filter('manage_posts_columns', 'post_thumbnail_column', 5);
add_filter('manage_news_events_columns', 'post_thumbnail_column', 5);

// Add the column
function post_thumbnail_column($cols) {
        $cols['tcb_post_thumb'] = __('Photo');
        return $cols;
}

// Hook into the posts an pages column managing. Sharing function callback again.
add_action('manage_posts_custom_column', 'tcb_display_post_thumbnail_column', 5, 2);
add_action('manage_news_events_custom_column', 'tcb_display_post_thumbnail_column', 5, 2);

// Grab featured-thumbnail size post thumbnail and display it.
function tcb_display_post_thumbnail_column($col, $id) {
        switch ($col) {
                case 'tcb_post_thumb':
                        if (function_exists('the_post_thumbnail'))
                                echo the_post_thumbnail('thumbnail');
                        else
                                echo 'Not supported in theme';
                        break;
        }
}

add_filter('post_thumbnail_html', 'remove_width_attribute', 10);
add_filter('image_send_to_editor', 'remove_width_attribute', 10);

function remove_width_attribute($html) {
        $html = preg_replace('/(width|height)="\d*"\s/', "", $html);
        return $html;
}

//////////////////////////////////////////////////////////////////////////////////////////////////////
// wpnbr Pagination by Gopu
////////////////////////////////////////////////////////////////////////////

if (!function_exists('wpneighbour_pagenations')):

        function wpneighbour_pagenations($pages = '') {
                if ($pages == '') {
                        global $wp_query;
                        $pages = $wp_query->max_num_pages;
                        if (!$pages) {
                                $pages = 1;
                        }
                }
                global $wp_query;
                $large = 999999999;
                $current_page = max(1, get_query_var('paged'));
                $pagination_links = paginate_links(array(
                    'base' => str_replace(999999999, '%#%', esc_url(get_pagenum_link(999999999))),
                    'format' => '?paged=%#%',
                    'current' => $current_page,
                    'total' => $pages,
                    'type' => 'array',
                    'prev_text' => '<i class="fa fa-angle-double-left"></i>',
                    'next_text' => '<i class="fa fa-angle-double-right"></i>',
                ));
                // echo paginate_links(array(
                //     'base' => str_replace($large, '%#%', esc_url(get_pagenum_link($large))),
                //     'format' => '?paged=%#%',
                //     'current' => max(1, get_query_var('paged')),
                //     'total' => $pages,
                //     'type'=>'list'
                // ));
                if (is_array($pagination_links)) {
                    echo '<ul class="pagination">';

                    // Previous Page Link
                    // if ($current_page > 1) {
                    //     echo '<li>' . $pagination_links[0] . '</li>';
                    // }

                    foreach ($pagination_links as $link) {
                        if (strpos($link, 'current') !== false) {
                            echo '<li class="active"><a href="#">' . $link . '</a></li>';
                        } else {
                            echo '<li>' . $link . '</li>';
                        }
                    }

                    // Next Page Link
                    if ($current_page < $total_pages) {
                        echo '<li>' . end($pagination_links) . '</li>';
                    }

                    echo '</ul>';
                }
        }

endif; // end wpneighbour_pagenations
/////// Metabox Above Editor


// Enable SVG uploads only for trusted users.
function ttc_can_upload_svg() {
    return current_user_can('manage_options');
}

function allow_svg_upload($mimes) {
    if (!ttc_can_upload_svg()) {
        return $mimes;
    }

    $mimes['svg'] = 'image/svg+xml';
    return $mimes;
}
add_filter('upload_mimes', 'allow_svg_upload');

// Sanitize SVG payloads on upload and reject dangerous content.
function ttc_sanitize_svg_upload($file) {
    $extension = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
    if ($extension !== 'svg') {
        return $file;
    }

    if (!ttc_can_upload_svg()) {
        $file['error'] = __('SVG uploads are restricted to administrators.', 'ttc');
        return $file;
    }

    if (empty($file['tmp_name']) || !is_readable($file['tmp_name'])) {
        $file['error'] = __('Unable to validate SVG upload.', 'ttc');
        return $file;
    }

    $svg_content = file_get_contents($file['tmp_name']);
    if ($svg_content === false || trim($svg_content) === '') {
        $file['error'] = __('Invalid SVG file.', 'ttc');
        return $file;
    }

    if (!preg_match('/<svg[\s>]/i', $svg_content)) {
        $file['error'] = __('Invalid SVG markup.', 'ttc');
        return $file;
    }

    // Reject known dangerous patterns outright.
    if (preg_match('/<\?(?:php|=)|<script\b|<foreignObject\b|on\w+\s*=|javascript:/i', $svg_content)) {
        $file['error'] = __('SVG contains unsafe content and was rejected.', 'ttc');
        return $file;
    }

    // Additional cleanup for defensive depth.
    $svg_content = preg_replace('/<\?(?:php|=)[\s\S]*?\?>/i', '', $svg_content);
    $svg_content = preg_replace('/<script\b[^>]*>[\s\S]*?<\/script>/i', '', $svg_content);
    $svg_content = preg_replace('/<foreignObject\b[^>]*>[\s\S]*?<\/foreignObject>/i', '', $svg_content);
    $svg_content = preg_replace('/\son\w+\s*=\s*(".*?"|\'.*?\'|[^\s>]+)/i', '', $svg_content);
    $svg_content = preg_replace('/\s(?:href|xlink:href)\s*=\s*("|\')\s*javascript:[\s\S]*?\1/i', '', $svg_content);

    if (file_put_contents($file['tmp_name'], $svg_content) === false) {
        $file['error'] = __('Failed to sanitize SVG file.', 'ttc');
        return $file;
    }

    return $file;
}
add_filter('wp_handle_upload_prefilter', 'ttc_sanitize_svg_upload');

// Allow SVG files to be uploaded via WordPress media library.
function allow_svg_in_media_library($data, $file, $filename, $mimes) {
    if (!ttc_can_upload_svg()) {
        return $data;
    }

    $filetype = wp_check_filetype($filename, $mimes);
    if ('svg' === $filetype['ext']) {
        $data['type'] = 'image/svg+xml';
        $data['ext'] = 'svg';
    }

    return $data;
}
add_filter('wp_check_filetype_and_ext', 'allow_svg_in_media_library', 10, 4);

// AJAX handler for loading more gallery images
function load_more_gallery_images() {
    if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'gallery_ajax_nonce')) {
        wp_send_json_error('Invalid nonce');
        return;
    }

    $gallery_id = intval($_POST['gallery_id']);
    $page = intval($_POST['page']);
    $images_per_page = 8;
    $offset = ($page - 1) * $images_per_page;

    $gallery_details = get_field('gallery_details', $gallery_id);
    $gallery_images = isset($gallery_details['gallery_images']) && is_array($gallery_details['gallery_images'])
        ? $gallery_details['gallery_images']
        : array();

    // Get next set of images
    $paginated_images = array_slice($gallery_images, $offset, $images_per_page);

    ob_start();
    if ($paginated_images) {
        foreach ($paginated_images as $item) {
            if (($item['media_type'] ?? '') === 'image' && !empty($item['image'])) {
                $image = $item['image'];
                ?>
                <div class="gallery-grid-cell">
                    <div class="gallery-preview-frame gallery-detail-frame">
                        <a href="<?php echo esc_url($image['url']); ?>" class="gallery-preview-link">
                            <img class="gallery-preview-img"
                                 src="<?php echo esc_url($image['sizes']['medium'] ?? $image['url']); ?>"
                                 alt="<?php echo esc_attr($image['alt'] ?? ''); ?>"
                                 loading="lazy"
                                 decoding="async" />
                        </a>
                    </div>
                </div>
                <?php
            } elseif (($item['media_type'] ?? '') === 'video' && !empty($item['video'])) {
                $video = $item['video'];
                ?>
                <div class="gallery-grid-cell">
                    <div class="gallery-preview-frame gallery-detail-frame">
                        <img src="<?php echo esc_url(get_template_directory_uri() . '/images/v-play.png'); ?>" alt="" class="video-crl gallery-preview-play" width="31" height="31" />
                        <a class="video gallery-preview-link" href="<?php echo esc_url($video['url']); ?>">
                            <video class="gallery-preview-video" muted playsinline preload="metadata" aria-hidden="true">
                                <source src="<?php echo esc_url($video['url']); ?>"
                                        type="<?php echo esc_attr($video['mime_type'] ?? ''); ?>" />
                            </video>
                        </a>
                    </div>
                </div>
                <?php
            }
        }
    }
    $html = ob_get_clean();

    wp_send_json_success(array(
        'html' => $html,
        'hasMore' => (count($gallery_images) > ($offset + $images_per_page))
    ));
}
add_action('wp_ajax_load_more_gallery_images', 'load_more_gallery_images');
add_action('wp_ajax_nopriv_load_more_gallery_images', 'load_more_gallery_images');

// Add this function to check setup
function check_pdf_setup() {
    $theme_dir = get_template_directory();
    $fpdf_path = $theme_dir . '/lib/fpdf.php';
    $font_path = $theme_dir . '/lib/font';

    $issues = array();

    if (!file_exists($fpdf_path)) {
        $issues[] = "FPDF file not found at: " . $fpdf_path;
    }

    if (!file_exists($font_path)) {
        $issues[] = "Font directory not found at: " . $font_path;
    }

    if (!is_readable($fpdf_path)) {
        $issues[] = "FPDF file is not readable. Please check file permissions.";
    }

    return empty($issues) ? true : $issues;
}

// Add this action to show admin notices if there are issues
function pdf_setup_admin_notice() {
    $check = check_pdf_setup();
    if ($check !== true) {
        echo '<div class="error"><p><strong>PDF Generation Setup Issues:</strong></p><ul>';
        foreach ($check as $issue) {
            echo '<li>' . esc_html($issue) . '</li>';
        }
        echo '</ul></div>';
    }
}
add_action('admin_notices', 'pdf_setup_admin_notice');
// Include PDF generator
require_once(get_template_directory() . '/pdf-generator.php');

// Hook the PDF generation function
add_action('template_redirect', 'generate_affiliate_clubs_pdf');


//login
// Hide admin bar from front-end
add_filter('show_admin_bar', '__return_false');


// Hide admin bar from front-end except for administrators
function custom_show_admin_bar($show) {
    if (!current_user_can('administrator')) {
        return false;
    }
    return $show;
}
add_filter('show_admin_bar', 'custom_show_admin_bar');

// Disable admin bar preference in user profile for non-administrators
function remove_admin_bar_prefs() {
    if (!current_user_can('administrator')) {
        remove_action('personal_options', '_admin_bar_preferences');
    }
}
add_action('admin_init', 'remove_admin_bar_prefs');

// Add custom CSS to remove admin bar space for non-administrators
function remove_admin_bar_space() {
    if (!is_admin() && !current_user_can('administrator')) {
        echo '<style type="text/css">
            html { margin-top: 0 !important; }
            * html body { margin-top: 0 !important; }
        </style>';
    }
}
add_action('wp_head', 'remove_admin_bar_space', 99);

// Add custom logout redirect
function custom_logout_redirect($redirect_to, $requested_redirect_to, $user) {
    return get_permalink(365); // Change this to your login page URL
}
add_filter('logout_redirect', 'custom_logout_redirect', 10, 3);

// Create custom role on theme activation
function create_ttc_member_role() {
    // Remove the role if it exists
    if (get_role('ttc-website-member')) {
        remove_role('ttc-website-member');
    }

    // Add the custom role
    add_role(
        'ttc-website-member',
        'TTC Website Member',
        array(
            'read'                      => true,  // Allows a user to read
            'edit_posts'                => false, // Disable post creation
            'delete_posts'              => false, // Disable post deletion
            'upload_files'              => false, // Disable file uploads
            'publish_posts'             => false, // Disable publishing
            'ttc_view_members'          => true,  // Custom capability to view member content
            'ttc_book_courts'           => true,  // Custom capability to book courts
            'ttc_view_events'           => true,  // Custom capability to view events
            'ttc_register_events'       => true,  // Custom capability to register for events
            'ttc_view_notices'          => true,  // Custom capability to view notices
            'ttc_update_profile'        => true,  // Custom capability to update profile
        )
    );
}

// Register the activation hook
register_activation_hook(__FILE__, 'create_ttc_member_role');

// Ensure role exists even if theme was activated before
function ensure_ttc_member_role_exists() {
    if (!get_role('ttc-website-member')) {
        create_ttc_member_role();
    }
}
add_action('init', 'ensure_ttc_member_role_exists');

// Register custom capabilities
function register_ttc_capabilities() {
    $role = get_role('administrator');

    // Add custom capabilities to administrator role
    $custom_caps = array(
        'ttc_view_members',
        'ttc_book_courts',
        'ttc_view_events',
        'ttc_register_events',
        'ttc_view_notices',
        'ttc_update_profile'
    );

    foreach ($custom_caps as $cap) {
        if (!$role->has_cap($cap)) {
            $role->add_cap($cap);
        }
    }
}
add_action('admin_init', 'register_ttc_capabilities');

// Function to check if user has TTC member capabilities
function is_ttc_member($user_id = null) {
    if (!$user_id) {
        $user_id = get_current_user_id();
    }

    $user = get_userdata($user_id);
    return $user && in_array('ttc-website-member', (array) $user->roles);
}



// Custom Walker Class for Nav Menu
class TTC_Menu_Walker extends Walker_Nav_Menu {
    public function start_lvl(&$output, $depth = 0, $args = array()) {
        $output .= "<div class='dropdown-menu' aria-labelledby='dropdown01'>";
    }

    public function end_lvl(&$output, $depth = 0, $args = array()) {
        $output .= "</div>";
    }

    public function start_el(&$output, $item, $depth = 0, $args = array(), $id = 0) {
        $classes = empty($item->classes) ? array() : (array) $item->classes;

        // Check if item has children
        $has_children = in_array('menu-item-has-children', $classes);

        if ($depth === 0) {
            $output .= "<li class='" . ($has_children ? 'dropdown' : '') . "'>";

            $output .= "<a href='" . esc_url($item->url) . "' class='nav-link" .
                      ($has_children ? ' dropdown-toggle' : '') . "'" .
                      ($has_children ? " id='dropdown01' data-toggle='dropdown' aria-haspopup='true' aria-expanded='false'" : "") .
                      ">";

            $output .= esc_html($item->title);
            $output .= "</a>";
        } else {
            // For dropdown items
            $output .= "<a class='dropdown-item page-scroll' href='" . esc_url($item->url) . "'>";
            $output .= esc_html($item->title);
            $output .= "</a>";

            // Add divider after each dropdown item except the last one
            if ($depth === 1) {
                // Get the next menu item
                global $wp_menu_items;
                if (!isset($wp_menu_items)) {
                    $wp_menu_items = wp_get_nav_menu_items(get_nav_menu_locations()['primary-menu']);
                }

                // Find current item position
                $current_position = array_search($item->ID, array_column($wp_menu_items, 'ID'));
                $next_item = isset($wp_menu_items[$current_position + 1]) ? $wp_menu_items[$current_position + 1] : null;

                // Add divider if there's a next item and it's at the same depth
                if ($next_item && $next_item->menu_item_parent === $item->menu_item_parent) {
                    $output .= "<div class='dropdown-divider'></div>";
                }
            }
        }
    }

    public function end_el(&$output, $item, $depth = 0, $args = array()) {
        if ($depth === 0) {
            $output .= "</li>";
        }
    }
}

// Register Navigation Menu
function register_ttc_menus() {
    register_nav_menus(array(
        'primary-menu' => __('Primary Menu', 'ttc'),
    ));
}
add_action('init', 'register_ttc_menus');


// Register REST API for Location Manager
function register_location_manager_api() {
    // Register the states endpoint
    register_rest_route('location-manager/v1', '/states/(?P<country_id>\d+)', array(
        'methods' => 'GET',
        'callback' => 'api_get_states',
        'permission_callback' => '__return_true',
        'args' => array(
            'country_id' => array(
                'required' => true,
                'validate_callback' => function($param) {
                    return is_numeric($param);
                }
            )
        )
    ));

    // Register the districts endpoint
    register_rest_route('location-manager/v1', '/districts/(?P<state_id>\d+)', array(
        'methods' => 'GET',
        'callback' => 'api_get_districts',
        'permission_callback' => '__return_true',
        'args' => array(
            'state_id' => array(
                'required' => true,
                'validate_callback' => function($param) {
                    return is_numeric($param);
                }
            )
        )
    ));
}
add_action('rest_api_init', 'register_location_manager_api');

// API callback for states
function api_get_states($request) {
    $country_id = $request['country_id'];

    global $wpdb;
    $states = $wpdb->get_results($wpdb->prepare(
        "SELECT id, state_name
         FROM {$wpdb->prefix}custom_states
         WHERE country_id = %d AND status = 1
         ORDER BY state_name ASC",
        $country_id
    ));

    return rest_ensure_response($states);
}

// API callback for districts
function api_get_districts($request) {
    $state_id = $request['state_id'];

    global $wpdb;
    $districts = $wpdb->get_results($wpdb->prepare(
        "SELECT id, district_name
         FROM {$wpdb->prefix}custom_districts
         WHERE state_id = %d AND status = 1
         ORDER BY district_name ASC",
        $state_id
    ));

    return rest_ensure_response($districts);
}

// ======= Razorpay Integration =======
function razorpay_enqueue_scripts() {
    wp_enqueue_script('jquery');
    wp_enqueue_script('razorpay-checkout', 'https://checkout.razorpay.com/v1/checkout.js', [], null, true);
    wp_enqueue_script('razorpay-custom', get_template_directory_uri() . '/Script/razorpay-scripts.js', ['jquery'], null, true);

    wp_localize_script('razorpay-custom', 'razorpay_ajax', [
        'ajax_url' => admin_url('admin-ajax.php'),
        'key_id'   => RAZORPAY_KEY_ID,
        'color'   => RAZORPAY_COLOR,
        'currency'   => RAZORPAY_CURRENCY,
        'nonce' => wp_create_nonce('razorpay_payment_nonce'),
    ]);
}

add_action('wp_enqueue_scripts', 'razorpay_enqueue_scripts');

function create_razorpay_order() {
    if (!isset($_POST['nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'])), 'razorpay_payment_nonce')) {
        wp_send_json_error(['error' => 'Security verification failed. Please refresh the page and try again.']);
        return;
    }

    $api = new \Razorpay\Api\Api(RAZORPAY_KEY_ID, RAZORPAY_KEY_SECRET);

    $dueId = isset($_POST['dueId']) ? intval($_POST['dueId']) : 0;
    $paymentAmount = isset($_POST['payment_amount']) ? round(floatval(wp_unslash($_POST['payment_amount'])), 2) : 0.0;
    $paymentType = isset($_POST['payment_type']) ? sanitize_key(wp_unslash($_POST['payment_type'])) : 'full';
    $partial_payment_enabled = ttc_is_partial_payment_enabled();

    if (!in_array($paymentType, ['full', 'partial', 'advance'], true)) {
        $paymentType = 'full';
    }

    // Handle advance payments (dueId = 0)
    if ($dueId === 0) {
        if ($paymentAmount <= 0) {
            wp_send_json_error(['error' => 'Invalid payment amount']);
            return;
        }

        // For advance payments, we need to get member details from the session or request
        // For now, we'll create a generic order for advance payment
        $amountInCents = intval($paymentAmount * 100); // Convert to cents/paise

        $orderData = [
            'receipt' => uniqid(),
            'amount' => $amountInCents,
            'currency' => RAZORPAY_CURRENCY,
            'payment_capture' => 1,
            'notes' => [
                'due_id' => 0, // Advance payment
                'payment_type' => 'advance',
                'payment_amount' => $paymentAmount,
                'created_at' => current_time('mysql')
            ]
        ];

        $order = $api->order->create($orderData);

        wp_send_json_success([
            'id' => $order['id'],
            'amount' => $orderData['amount'],
        ]);
        return;
    }

    // Regular payment due processing
    $paymentDetails = get_payment_due_details($dueId);
    
    if (!$paymentDetails) {
        wp_send_json_error(['error' => 'Invalid payment due ID']);
        return;
    }

    if (!in_array($paymentDetails['status'], ['pending', 'overdue'], true)) {
        wp_send_json_error(['error' => 'This due is no longer payable']);
        return;
    }

    // Get the membership number from the member post
    $membership_number = get_field('membership_number', $paymentDetails['member_id']);
    if (!$membership_number) {
        wp_send_json_error(['error' => 'Invalid membership number']);
        return;
    }

    $remainingAmount = isset($paymentDetails['remaining_amount']) && $paymentDetails['remaining_amount'] !== null
        ? floatval($paymentDetails['remaining_amount'])
        : floatval($paymentDetails['amount']);

    if ($remainingAmount <= 0) {
        wp_send_json_error(['error' => 'This due has already been paid']);
        return;
    }

    if (!$partial_payment_enabled && $paymentType === 'partial') {
        wp_send_json_error(['error' => 'Partial payment is disabled']);
        return;
    }

    if ($paymentType === 'full') {
        $amount = round($remainingAmount, 2);
    } else {
        // Partial payment or any fallback uses the submitted amount with strict bounds.
        $amount = $paymentAmount;
        if ($amount <= 0 || $amount > round($remainingAmount, 2)) {
            wp_send_json_error(['error' => 'Invalid payment amount for this due']);
            return;
        }
    }

    $amountInCents = intval($amount * 100); // Convert to cents/paise

    $orderData = [
        'receipt' => uniqid(),
        'amount' => $amountInCents,
        'currency' => RAZORPAY_CURRENCY,
        'payment_capture' => 1,
        'notes' => [
            'due_id' => $dueId,
            'membership_number' => $membership_number,
            'payment_type' => $paymentType,
            'original_amount' => $paymentDetails['amount'],
            'payment_amount' => $amount,
            'created_at' => current_time('mysql')
        ]
    ];

    $order = $api->order->create($orderData);

    wp_send_json_success([
        'id' => $order['id'],
        'amount' => $orderData['amount'],
    ]);
}

function update_payment_due_successfully_paid($dueId, $razorpayPaymentId, $paymentAmount = null, $paymentType = 'full') {
    global $wpdb;
    $payment_dues_table = $wpdb->prefix . 'payment_dues';
    $payment_history_table = $wpdb->prefix . 'payment_history';

    // Get current due details
    $due = get_payment_due_details($dueId);
    if (!$due) {
        throw new Exception('Payment due not found');
    }

    // Calculate new remaining amount
    $remainingAmount = $due['remaining_amount'] ?? $due['amount'];
    if ($paymentAmount === null) {
        $paymentAmount = $remainingAmount;
    }

    // Insert into payment history
    $wpdb->insert(
        $payment_history_table,
        array(
            'payment_due_id' => $dueId,
            'member_id' => $due['member_id'],
            'amount' => $paymentAmount,
            'payment_date' => current_time('mysql'),
            'transaction_id' => $razorpayPaymentId,
            'payment_type' => $paymentType,
            'created_at' => current_time('mysql')
        ),
        array('%d', '%d', '%f', '%s', '%s', '%s', '%s')
    );

    if ($wpdb->last_error) {
        throw new Exception('Failed to insert payment history: ' . $wpdb->last_error);
    }

    // Calculate new remaining amount
    $newRemainingAmount = $remainingAmount - $paymentAmount;
    
    // Determine new status
    $newStatus = $newRemainingAmount <= 0 ? 'paid' : 'pending';

    $data = [
        'status' => $newStatus,
        'remaining_amount' => max(0, $newRemainingAmount),
        'updated_at' => current_time('mysql')
    ];

    // Set original amount if this is the first payment
    if (empty($due['original_amount'])) {
        $data['original_amount'] = $due['amount'];
    }

    $updated = $wpdb->update(
        $payment_dues_table,
        $data,
        ['id' => $dueId],
        ['%s', '%f', '%s', '%f'],
        ['%d']
    );

    if ($updated === false) {
        throw new Exception('Failed to update payment due: ' . $wpdb->last_error);
    }

    // Get updated payment due details
    $updatedDue = get_payment_due_details($dueId);
    
    return [
        'success' => $updated,
        'payment_due' => $updatedDue
    ];
}

function get_payment_due_details($dueId) {
    global $wpdb;
    
    if (empty($dueId) || !is_numeric($dueId)) {
        return false; // Invalid ID
    }

    $table_name = $wpdb->prefix . 'payment_dues';

    $due = $wpdb->get_row(
        $wpdb->prepare(
            "SELECT * FROM $table_name WHERE id = %d",
            $dueId
        ),
        ARRAY_A // Return as associative array
    );

    return $due ? $due : false;
}

function verify_razorpay_payment() {
    if (!isset($_POST['nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'])), 'razorpay_payment_nonce')) {
        wp_send_json_error(['message' => 'Security verification failed. Please refresh the page and try again.']);
        return;
    }

    $api = new \Razorpay\Api\Api(RAZORPAY_KEY_ID, RAZORPAY_KEY_SECRET);

    try {
        $razorpay_order_id = isset($_POST['razorpay_order_id']) ? sanitize_text_field(wp_unslash($_POST['razorpay_order_id'])) : '';
        $razorpay_payment_id = isset($_POST['razorpay_payment_id']) ? sanitize_text_field(wp_unslash($_POST['razorpay_payment_id'])) : '';
        $razorpay_signature = isset($_POST['razorpay_signature']) ? sanitize_text_field(wp_unslash($_POST['razorpay_signature'])) : '';

        if ($razorpay_order_id === '' || $razorpay_payment_id === '' || $razorpay_signature === '') {
            throw new Exception('Invalid payment verification payload');
        }

        $clientAmount = isset($_POST['payment_amount']) ? round(floatval(wp_unslash($_POST['payment_amount'])), 2) : null;
        $paymentType = isset($_POST['payment_type']) ? sanitize_key(wp_unslash($_POST['payment_type'])) : 'full';
        $dueId = isset($_POST['due_id']) ? intval($_POST['due_id']) : 0;
        $partial_payment_enabled = ttc_is_partial_payment_enabled();

        if (!in_array($paymentType, ['full', 'partial', 'advance'], true)) {
            $paymentType = 'full';
        }

        $attributes = [
            'razorpay_order_id' => $razorpay_order_id,
            'razorpay_payment_id' => $razorpay_payment_id,
            'razorpay_signature' => $razorpay_signature,
        ];

        $api->utility->verifyPaymentSignature($attributes);

        // Use gateway-confirmed values for integrity checks and ledger updates.
        $gatewayPayment = $api->payment->fetch($razorpay_payment_id);
        $gatewayAmount = round(floatval($gatewayPayment['amount']) / 100, 2);
        $gatewayOrderId = sanitize_text_field((string) $gatewayPayment['order_id']);
        $gatewayStatus = sanitize_key((string) $gatewayPayment['status']);

        if ($gatewayOrderId !== $razorpay_order_id) {
            throw new Exception('Order ID mismatch detected');
        }

        if (!in_array($gatewayStatus, ['captured', 'authorized'], true)) {
            throw new Exception('Payment status is not valid for settlement');
        }

        if ($gatewayAmount <= 0) {
            throw new Exception('Invalid payment amount received from gateway');
        }

        // Handle advance payments (dueId = 0)
        if ($dueId === 0) {
            if ($paymentType !== 'advance') {
                $paymentType = 'advance';
            }

            // For advance payments, we need to get member details from the session
            // This would need to be implemented based on how you store member info during the quick pay process
            // For now, we'll create a generic advance payment record
            
            global $wpdb;
            $payment_history_table = $wpdb->prefix . 'payment_history';

            // Insert into payment history for advance payment
            $wpdb->insert(
                $payment_history_table,
                array(
                    'payment_due_id' => 0, // No specific due
                    'member_id' => 0, // Will need to be updated based on session
                    'amount' => $gatewayAmount,
                    'payment_date' => current_time('mysql'),
                    'transaction_id' => $razorpay_payment_id,
                    'payment_type' => 'advance',
                    'created_at' => current_time('mysql')
                ),
                array('%d', '%d', '%f', '%s', '%s', '%s', '%s')
            );

            if ($wpdb->last_error) {
                throw new Exception('Failed to insert advance payment history: ' . $wpdb->last_error);
            }

            wp_send_json_success([
                'message' => 'Advance payment successful',
                'payment_id' => $razorpay_payment_id,
                'amount' => $gatewayAmount
            ]);
            return;
        }

        $paymentDetails = get_payment_due_details($dueId);
        if (!$paymentDetails) {
            throw new Exception('Invalid payment due ID');
        }

        if (!in_array($paymentDetails['status'], ['pending', 'overdue'], true)) {
            throw new Exception('This due is no longer payable');
        }

        $remainingAmount = isset($paymentDetails['remaining_amount']) && $paymentDetails['remaining_amount'] !== null
            ? floatval($paymentDetails['remaining_amount'])
            : floatval($paymentDetails['amount']);
        $remainingAmount = round($remainingAmount, 2);

        if ($paymentType === 'partial') {
            if (!$partial_payment_enabled) {
                throw new Exception('Partial payment is disabled');
            }

            if ($gatewayAmount <= 0 || $gatewayAmount > $remainingAmount) {
                throw new Exception('Invalid partial payment amount');
            }
        } else {
            // Full payment must settle the remaining amount exactly.
            if (abs($gatewayAmount - $remainingAmount) > 0.01) {
                throw new Exception('Payment amount mismatch');
            }
            $paymentType = 'full';
        }

        // Optional anti-tampering check between posted amount and gateway amount.
        if ($clientAmount !== null && abs($clientAmount - $gatewayAmount) > 0.01) {
            throw new Exception('Payment amount mismatch');
        }

        // Regular payment due processing
        $update_result = update_payment_due_successfully_paid(
            $dueId, 
            $razorpay_payment_id,
            $gatewayAmount,
            $paymentType
        );

        if (!$update_result['success']) {
            throw new Exception('Failed to update payment due');
        }

        // Extract member ID from payment due data
        error_log('Update result: ' . print_r($update_result, true));
        $member_id = $update_result['payment_due']['member_id'];
        
        // Get WordPress user ID from member post
        $user_id = get_field('member_user_id', $member_id);
        if (!$user_id) {
            throw new Exception('No WordPress user found for member ID: ' . $member_id);
        }
        
        // Get member post ID linked to this user (should be the same as member_id)
        $member_post_id = get_user_meta($user_id, 'member_post_id', true);
        
        // Initialize member details
        $member_name = '';
        $member_email = '';
        $membership_number = '';
        
        // Get data from ACF fields
        $acf_first_name = get_field('first_name', $member_id);
        $acf_last_name = get_field('last_name', $member_id);
        $acf_email = get_field('email', $member_id);
        $acf_membership_number = get_field('membership_number', $member_id);
        
        // Use ACF fields if available
        $member_name = ($acf_first_name && $acf_last_name) ? $acf_first_name . ' ' . $acf_last_name : '';
        $member_email = $acf_email ?: '';
        $membership_number = $acf_membership_number ?: '';
        
        // Fallback to user meta if ACF fields are empty
        if (empty($member_name)) {
            $first_name = get_user_meta($user_id, 'first_name', true);
            $last_name = get_user_meta($user_id, 'last_name', true);
            $member_name = ($first_name && $last_name) ? $first_name . ' ' . $last_name : '';
        }
        
        if (empty($member_email)) {
            $user = get_userdata($user_id);
            $member_email = $user ? $user->user_email : '';
        }
        
        if (empty($membership_number)) {
            $membership_number = get_user_meta($user_id, 'membership_number', true);
        }

        // Send payment notification email
        $email_result = send_payment_notification_email(
            $member_name,
            $member_email,
            $membership_number,
            $gatewayAmount
        );

        // Log email status
        if (!$email_result['success']) {
            error_log('Payment notification email failed: ' . $email_result['message']);
        }

        wp_send_json_success(['verified' => 1]);
    } catch (\Razorpay\Api\Errors\SignatureVerificationError $e) {
        wp_send_json_success(['verificationError' => 1]);
    } catch (Exception $e) {
        wp_send_json_error(['message' => $e->getMessage()]);
    }
}

add_action('wp_ajax_create_razorpay_order', 'create_razorpay_order'); // For logged-in users
add_action('wp_ajax_nopriv_create_razorpay_order', 'create_razorpay_order'); // For guests
add_action('wp_ajax_verify_razorpay_payment', 'verify_razorpay_payment'); // For logged-in users
add_action('wp_ajax_nopriv_verify_razorpay_payment', 'verify_razorpay_payment'); // For guests


/**
 * Send payment notification email to admin
 * 
 * @param string $member_name Member's full name
 * @param string $member_email Member's email address
 * @param string $member_id Member's ID
 * @param float $amount Payment amount
 * @return array Array containing success status and message
 */
function send_payment_notification_email($member_name, $member_email, $member_id, $amount) {
    $ttc_primary_email = get_option('ttc_primary_email', '');
    
    if (!$ttc_primary_email) {
        return [
            'success' => false,
            'message' => 'Email not sent: TTC primary email not configured'
        ];
    }

    // Get WordPress timezone setting
    $timezone_string = get_option('timezone_string');
    if (empty($timezone_string)) {
        // Fallback to UTC if timezone is not set
        $timezone_string = 'UTC';
    }
    
    // Create DateTime object with WordPress timezone
    $date = new DateTime('now', new DateTimeZone($timezone_string));
    $formatted_date = $date->format('F j, Y, g:i a');

    $subject = 'New Payment Received - TTC Member Payment';
    
    // Create HTML email template
    $message = '
    <html>
    <head>
        <style>
            body { font-family: Arial, sans-serif; line-height: 1.6; color: #333; }
            .container { max-width: 600px; margin: 0 auto; padding: 20px; }
            .header { background-color: #f8f9fa; padding: 20px; border-radius: 5px; margin-bottom: 20px; }
            .details { background-color: #ffffff; padding: 20px; border: 1px solid #dee2e6; border-radius: 5px; }
            .footer { margin-top: 20px; font-size: 12px; color: #6c757d; }
            .amount-container { 
                margin-top: 25px; 
                padding: 20px; 
                background-color: #f8f9fa; 
                border-radius: 5px;
                text-align: center;
            }
            .amount-label {
                font-size: 16px;
                color: #495057;
                margin-bottom: 10px;
                text-transform: uppercase;
                letter-spacing: 1px;
            }
            .amount-value { 
                font-size: 32px; 
                color: #28a745; 
                font-weight: bold;
                display: block;
                margin: 10px 0;
            }
            .amount-date {
                font-size: 14px;
                color: #6c757d;
                margin-top: 5px;
            }
        </style>
    </head>
    <body>
        <div class="container">
            <div class="header">
                <h2>New Payment Received</h2>
            </div>
            <div class="details">
                <p>A new payment has been received from a TTC member.</p>
                <p><strong>Member Details:</strong></p>
                <ul>
                    <li><strong>Name:</strong> ' . esc_html($member_name) . '</li>
                    <li><strong>Email:</strong> ' . esc_html($member_email) . '</li>
                    <li><strong>Member ID:</strong> ' . esc_html($member_id) . '</li>
                </ul>
                <div class="amount-container">
                    <span class="amount-label">Payment Amount</span>
                    <span class="amount-value">₹' . esc_html($amount) . '</span>
                    <span class="amount-date">Paid on ' . esc_html($formatted_date) . ' (' . esc_html($timezone_string) . ')</span>
                </div>
            </div>
            <div class="footer">
                <p>This is an automated message from the TTC website. Please do not reply to this email.</p>
            </div>
        </div>
    </body>
    </html>';

    $headers = [
        'Content-Type: text/html; charset=UTF-8',
        'From: TTC Website <noreply@ttc.com>'
    ];
    
    $attachments = []; // Optional: Add file paths here if needed
    
    $sent = wp_mail($ttc_primary_email, $subject, $message, $headers, $attachments);
    
    if ($sent) {
        return [
            'success' => true,
            'message' => 'Payment notification email sent successfully to: ' . $ttc_primary_email
        ];
    } else {
        return [
            'success' => false,
            'message' => 'Failed to send payment notification email to: ' . $ttc_primary_email
        ];
    }
}

// ======= Admin Scripts - In-House Developer  =======

function enqueue_admin_ajax_script() {
    wp_enqueue_script('jquery');
    wp_enqueue_script('admin-scripts', get_template_directory_uri() . '/Script/admin-scripts.js', ['jquery'], null, true);
    
    wp_localize_script('admin-scripts', 'admin_ajax', [
        'ajax_url' => admin_url('admin-ajax.php'),
        'admin_action_nonce' => wp_create_nonce('admin_action_nonce'),
    ]);
}

function handle_verify_user_email() {
    check_ajax_referer('admin_action_nonce', 'nonce');

    if (!current_user_can('manage_options')) {
        wp_send_json_error(['error' => 'unauthorized']);
    }

    $user_id = isset($_POST['user_id']) ? intval($_POST['user_id']) : 0;

    if (!$user_id || !get_user_by('ID', $user_id)) {
        wp_send_json_error(['error' => 'invalid userId']);
    }

    update_user_meta($user_id, 'email_verified', true);

    wp_send_json_success(['user_email_verified' => 1]);
}

add_action('admin_enqueue_scripts', 'enqueue_admin_ajax_script');
add_action('wp_ajax_verify_user_email', 'handle_verify_user_email');

// Add new function to get payment history
function get_payment_history($dueId) {
    global $wpdb;
    $payment_history_table = $wpdb->prefix . 'payment_history';
    $payment_dues_table = $wpdb->prefix . 'payment_dues';
    
    // Get the due details
    $due = $wpdb->get_row(
        $wpdb->prepare(
            "SELECT original_amount, remaining_amount 
             FROM $payment_dues_table 
             WHERE id = %d",
            $dueId
        )
    );

    if (!$due) {
        return false;
    }

    // Get payment history from new table
    $history = $wpdb->get_results(
        $wpdb->prepare(
            "SELECT 
                amount,
                payment_date as date,
                transaction_id,
                payment_type as type,
                payment_method,
                receipt_file,
                receipt_number
             FROM $payment_history_table 
             WHERE payment_due_id = %d
             ORDER BY payment_date DESC",
            $dueId
        )
    );

    return [
        'history' => $history,
        'original_amount' => $due->original_amount,
        'remaining_amount' => $due->remaining_amount
    ];
}

// ======= TTC Settings Page =======
function ttc_add_settings_page() {
    add_options_page(
        'TTC Settings',           // Page title
        'TTC Settings',           // Menu title
        'manage_options',         // Capability required
        'ttc-settings',           // Menu slug
        'ttc_settings_page_html'  // Callback function
    );
}

function ttc_register_settings() {
    // Register settings section
    add_settings_section(
        'ttc_email_settings_section',    // ID
        'Email Settings',                // Title
        'ttc_email_settings_section_callback', // Callback
        'ttc-settings'                   // Page
    );

    // Register payment settings section
    add_settings_section(
        'ttc_payment_settings_section',    // ID
        'Payment Settings',                // Title
        'ttc_payment_settings_section_callback', // Callback
        'ttc-settings'                   // Page
    );

    // Register the primary email setting
    register_setting('ttc-settings', 'ttc_primary_email', [
        'type' => 'string',
        'sanitize_callback' => 'sanitize_email',
        'default' => ''
    ]);

    // Register the enable member login setting
    register_setting('ttc-settings', 'ttc_enable_member_login', [
        'type' => 'boolean',
        'sanitize_callback' => 'rest_sanitize_boolean',
        'default' => false
    ]);

    // Register the enable partial payment setting
    register_setting('ttc-settings', 'ttc_enable_partial_payment', [
        'type' => 'boolean',
        'sanitize_callback' => 'rest_sanitize_boolean',
        'default' => true
    ]);

    // Register OTP security settings
    register_setting('ttc-settings', 'ttc_enable_otp_security', [
        'type' => 'boolean',
        'sanitize_callback' => 'rest_sanitize_boolean',
        'default' => true
    ]);

    register_setting('ttc-settings', 'ttc_otp_request_limit', [
        'type' => 'integer',
        'sanitize_callback' => 'absint',
        'default' => 3
    ]);

    register_setting('ttc-settings', 'ttc_otp_rate_limit_minutes', [
        'type' => 'integer',
        'sanitize_callback' => 'absint',
        'default' => 15
    ]);

    register_setting('ttc-settings', 'ttc_otp_resend_limit', [
        'type' => 'integer',
        'sanitize_callback' => 'absint',
        'default' => 2
    ]);

    register_setting('ttc-settings', 'ttc_otp_resend_minutes', [
        'type' => 'integer',
        'sanitize_callback' => 'absint',
        'default' => 10
    ]);

    register_setting('ttc-settings', 'ttc_otp_max_attempts', [
        'type' => 'integer',
        'sanitize_callback' => 'absint',
        'default' => 5
    ]);

    register_setting('ttc-settings', 'ttc_otp_expiry_minutes', [
        'type' => 'integer',
        'sanitize_callback' => 'absint',
        'default' => 10
    ]);

    // Add the settings fields
    add_settings_field(
        'ttc_primary_email',                    // ID
        'TTC Primary Email',                    // Label
        'ttc_primary_email_setting_field_html', // Callback
        'ttc-settings',                         // Page
        'ttc_email_settings_section'            // Section
    );

    add_settings_field(
        'ttc_enable_member_login',              // ID
        'Enable Member Login',                  // Label
        'ttc_enable_member_login_setting_field_html', // Callback
        'ttc-settings',                         // Page
        'ttc_email_settings_section'            // Section
    );

    add_settings_field(
        'ttc_enable_partial_payment',           // ID
        'Enable Partial Payment',               // Label
        'ttc_enable_partial_payment_setting_field_html', // Callback
        'ttc-settings',                         // Page
        'ttc_payment_settings_section'          // Section
    );

    // Add OTP security settings fields
    add_settings_field(
        'ttc_enable_otp_security',              // ID
        'Enable OTP Security Features',         // Label
        'ttc_enable_otp_security_setting_field_html', // Callback
        'ttc-settings',                         // Page
        'ttc_payment_settings_section'          // Section
    );

    add_settings_field(
        'ttc_otp_request_limit',                // ID
        'OTP Request Rate Limit',               // Label
        'ttc_otp_request_limit_setting_field_html', // Callback
        'ttc-settings',                         // Page
        'ttc_payment_settings_section'          // Section
    );

    add_settings_field(
        'ttc_otp_rate_limit_minutes',           // ID
        'OTP Rate Limit Time Period (minutes)', // Label
        'ttc_otp_rate_limit_minutes_setting_field_html', // Callback
        'ttc-settings',                         // Page
        'ttc_payment_settings_section'          // Section
    );

    add_settings_field(
        'ttc_otp_resend_limit',                 // ID
        'OTP Resend Rate Limit',                // Label
        'ttc_otp_resend_limit_setting_field_html', // Callback
        'ttc-settings',                         // Page
        'ttc_payment_settings_section'          // Section
    );

    add_settings_field(
        'ttc_otp_resend_minutes',               // ID
        'OTP Resend Time Period (minutes)',     // Label
        'ttc_otp_resend_minutes_setting_field_html', // Callback
        'ttc-settings',                         // Page
        'ttc_payment_settings_section'          // Section
    );

    add_settings_field(
        'ttc_otp_max_attempts',                 // ID
        'Maximum OTP Attempts',                 // Label
        'ttc_otp_max_attempts_setting_field_html', // Callback
        'ttc-settings',                         // Page
        'ttc_payment_settings_section'          // Section
    );

    add_settings_field(
        'ttc_otp_expiry_minutes',               // ID
        'OTP Expiry Time (minutes)',            // Label
        'ttc_otp_expiry_minutes_setting_field_html', // Callback
        'ttc-settings',                         // Page
        'ttc_payment_settings_section'          // Section
    );
}

function ttc_email_settings_section_callback() {
    echo '<p>Configure email settings for TTC system communications.</p>';
}

function ttc_payment_settings_section_callback() {
    echo '<p>Configure payment settings for TTC system functionality.</p>';
}

function ttc_primary_email_setting_field_html() {
    $value = get_option('ttc_primary_email', '');
    echo '<input type="email" id="ttc_primary_email_setting" name="ttc_primary_email" value="' . esc_attr($value) . '" class="regular-text" />';
    echo '<p class="description">This is the primary email for the system to communicate, for example: payment notifications, etc.</p>';
}

function ttc_enable_member_login_setting_field_html() {
    $value = get_option('ttc_enable_member_login', false);
    echo '<input type="checkbox" id="ttc_enable_member_login_setting" name="ttc_enable_member_login" value="1" ' . checked(1, $value, false) . ' />';
    echo '<label for="ttc_enable_member_login_setting">Enable member login functionality.</label>';
    echo '<p class="description">If checked, members will be able to log in to the website using their membership number and email.</p>';
}

function ttc_enable_partial_payment_setting_field_html() {
    $value = get_option('ttc_enable_partial_payment', true);
    echo '<input type="checkbox" id="ttc_enable_partial_payment_setting" name="ttc_enable_partial_payment" value="1" ' . checked(1, $value, false) . ' />';
    echo '<label for="ttc_enable_partial_payment_setting">Enable partial payment functionality. If unchecked, only full payments are allowed.</label>';
}

function ttc_enable_otp_security_setting_field_html() {
    $value = get_option('ttc_enable_otp_security', true);
    echo '<input type="checkbox" id="ttc_enable_otp_security_setting" name="ttc_enable_otp_security" value="1" ' . checked(1, $value, false) . ' />';
    echo '<label for="ttc_enable_otp_security_setting">Enable OTP security features. If unchecked, OTP will not be required for payment.</label>';
}

function ttc_otp_request_limit_setting_field_html() {
    $value = get_option('ttc_otp_request_limit', 3);
    echo '<input type="number" id="ttc_otp_request_limit_setting" name="ttc_otp_request_limit" value="' . esc_attr($value) . '" class="small-text" />';
    echo '<p class="description">Enter the maximum number of OTP requests per time period. Default is 3.</p>';
}

function ttc_otp_rate_limit_minutes_setting_field_html() {
    $value = get_option('ttc_otp_rate_limit_minutes', 15);
    echo '<input type="number" id="ttc_otp_rate_limit_minutes_setting" name="ttc_otp_rate_limit_minutes" value="' . esc_attr($value) . '" class="small-text" />';
    echo '<p class="description">Enter the time period in minutes for OTP request rate limiting. Default is 15 minutes.</p>';
}

function ttc_otp_resend_limit_setting_field_html() {
    $value = get_option('ttc_otp_resend_limit', 2);
    echo '<input type="number" id="ttc_otp_resend_limit_setting" name="ttc_otp_resend_limit" value="' . esc_attr($value) . '" class="small-text" />';
    echo '<p class="description">Enter the maximum number of OTP resends per time period. Default is 2.</p>';
}

function ttc_otp_resend_minutes_setting_field_html() {
    $value = get_option('ttc_otp_resend_minutes', 10);
    echo '<input type="number" id="ttc_otp_resend_minutes_setting" name="ttc_otp_resend_minutes" value="' . esc_attr($value) . '" class="small-text" />';
    echo '<p class="description">Enter the time period in minutes for OTP resend rate limiting. Default is 10 minutes.</p>';
}

function ttc_otp_max_attempts_setting_field_html() {
    $value = get_option('ttc_otp_max_attempts', 5);
    echo '<input type="number" id="ttc_otp_max_attempts_setting" name="ttc_otp_max_attempts" value="' . esc_attr($value) . '" class="small-text" />';
    echo '<p class="description">Enter the maximum number of OTP attempts. Default is 5.</p>';
}

function ttc_otp_expiry_minutes_setting_field_html() {
    $value = get_option('ttc_otp_expiry_minutes', 10);
    echo '<input type="number" id="ttc_otp_expiry_minutes_setting" name="ttc_otp_expiry_minutes" value="' . esc_attr($value) . '" class="small-text" />';
    echo '<p class="description">Enter the OTP expiry time in minutes. Default is 10 minutes.</p>';
}

function ttc_settings_page_html() {
    // Check user capabilities
    if (!current_user_can('manage_options')) {
        return;
    }

    ?>
    <div class="wrap">
        <h1><?php echo esc_html(get_admin_page_title()); ?></h1>
        
        <div class="ttc-settings-container">
            <form action="options.php" method="post">
                <?php
                settings_fields('ttc-settings');
                do_settings_sections('ttc-settings');
                submit_button('Save Settings');
                ?>
            </form>
        </div>
        
        <div class="ttc-settings-info" style="margin-top: 30px; padding: 20px; background: #f9f9f9; border-left: 4px solid #0073aa;">
            <h3>About TTC Settings</h3>
            <p>This page contains all the configuration settings for the TTC (Table Tennis Club) website functionality.</p>
            <p><strong>Email Settings:</strong> Configure email addresses for system communications like payment notifications, member communications, etc.</p>
            <p><strong>Member Login Settings:</strong> Control member authentication features including login functionality for registered members.</p>
            <p><strong>Payment Settings:</strong> Configure payment options including partial payment functionality and OTP security features for Quick Pay.</p>
            <p><strong>OTP Security Features:</strong> Control the security settings for Quick Pay verification including rate limiting, attempt limits, and expiry times. You can disable OTP entirely for testing or less secure environments.</p>
        </div>
    </div>
    
    <style>
        .ttc-settings-container {
            max-width: 800px;
            margin-top: 20px;
        }
        .ttc-settings-info {
            max-width: 800px;
        }
        .ttc-settings-info h3 {
            margin-top: 0;
            color: #0073aa;
        }
    </style>
    <?php
}

// Hook the admin menu and settings registration
add_action('admin_menu', 'ttc_add_settings_page');
add_action('admin_init', 'ttc_register_settings');

/**
 * Helper function to check if partial payment is enabled
 */
function ttc_is_partial_payment_enabled() {
    return get_option('ttc_enable_partial_payment', true);
}

/**
 * Helper function to check if OTP security is enabled
 */
function ttc_is_otp_security_enabled() {
    return get_option('ttc_enable_otp_security', true);
}

/**
 * Helper function to get OTP request rate limit
 */
function ttc_get_otp_request_limit() {
    return get_option('ttc_otp_request_limit', 3);
}

/**
 * Helper function to get OTP rate limit time period in minutes
 */
function ttc_get_otp_rate_limit_minutes() {
    return get_option('ttc_otp_rate_limit_minutes', 15);
}

/**
 * Helper function to get OTP resend rate limit
 */
function ttc_get_otp_resend_limit() {
    return get_option('ttc_otp_resend_limit', 2);
}

/**
 * Helper function to get OTP resend time period in minutes
 */
function ttc_get_otp_resend_minutes() {
    return get_option('ttc_otp_resend_minutes', 10);
}

/**
 * Helper function to get maximum OTP attempts
 */
function ttc_get_otp_max_attempts() {
    return get_option('ttc_otp_max_attempts', 5);
}

/**
 * Helper function to get OTP expiry time in minutes
 */
function ttc_get_otp_expiry_minutes() {
    return get_option('ttc_otp_expiry_minutes', 10);
}

/**
 * Quick Pay - Find Member Dues by Member ID or Email
 */
function find_member_dues() {
    // Verify nonce
    if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'quick_pay_nonce')) {
        wp_send_json_error('Security verification failed. Please refresh the page and try again.');
    }

    // Get and sanitize member identifier
    $member_identifier = isset($_POST['member_identifier']) ? sanitize_text_field($_POST['member_identifier']) : '';
    
    if (empty($member_identifier)) {
        wp_send_json_error('Please enter a Member ID or Email address.');
    }

    // Get and sanitize member identifier
    $member_identifier = isset($_POST['member_identifier']) ? sanitize_text_field($_POST['member_identifier']) : '';
    
    // Remove all whitespace from member identifier
    $member_identifier = preg_replace('/\s+/', '', $member_identifier);
    
    if (empty($member_identifier)) {
        wp_send_json_error('Please enter a Member ID or Email address.');
    }

    // Determine if input is email or membership number
    $is_email = is_email($member_identifier);
    
    // Build query to find member
    $meta_query = array();
    
    if ($is_email) {
        // Search by email
        $meta_query[] = array(
            'key' => 'email',
            'value' => $member_identifier,
            'compare' => '='
        );
    } else {
        // Search by membership number
        $meta_query[] = array(
            'key' => 'membership_number',
            'value' => $member_identifier,
            'compare' => '='
        );
    }

    // Query for member
    $member_query = new WP_Query(array(
        'post_type' => 'member',
        'posts_per_page' => 1,
        'meta_query' => $meta_query,
        'post_status' => 'publish'
    ));

    if (!$member_query->have_posts()) {
        $identifier_type = $is_email ? 'email address' : 'Member ID';
        wp_send_json_error("No member found with the provided {$identifier_type}. Please check your details and try again.");
    }

    // Get member details
    $member_query->the_post();
    $member_id = get_the_ID();
    $member_name = get_the_title();
    $member_status = get_field('member_status', $member_id);
    $membership_number = get_field('membership_number', $member_id);
    $email = get_field('email', $member_id);
    wp_reset_postdata();

    // Check if member is active
    if ($member_status !== 'active') {
        $status_message = '';
        switch ($member_status) {
            case 'inactive':
                $status_message = 'Your membership is currently inactive. Please contact the club office to activate your membership.';
                break;
            case 'suspended':
                $status_message = 'Your membership has been suspended. Please contact the club office for assistance.';
                break;
            case 'expired':
                $status_message = 'Your membership has expired. Please renew your membership to continue.';
                break;
            default:
                $status_message = 'Your membership status requires attention. Please contact the club office.';
        }
        wp_send_json_error($status_message);
    }

    // Get payment dues for this member
    global $wpdb;
    $table_name = $wpdb->prefix . 'payment_dues';
    
    $payment_dues = $wpdb->get_results(
        $wpdb->prepare(
            "SELECT * FROM $table_name
            WHERE member_id = %d AND status IN ('pending', 'overdue')
            ORDER BY due_date ASC",
            $member_id
        )
    );

    if (empty($payment_dues)) {
        // No dues - continue to show member info and empty dues list
    }

    // Build HTML response
    $html = '<div class="space-y-4">';
    
    // Member info header
    $html .= '<div class="bg-gray-50 p-4 rounded-lg mb-6">';
    $html .= '<h4 class="text-lg font-semibold text-gray-800 mb-2">Member Information</h4>';
    $html .= '<div class="grid grid-cols-1 md:grid-cols-3 gap-4 text-sm">';
    $html .= '<div><span class="font-medium text-gray-600">Name:</span> ' . esc_html($member_name) . '</div>';
    $html .= '<div><span class="font-medium text-gray-600">Membership No.:</span> ' . esc_html($membership_number) . '</div>';
    $html .= '<div><span class="font-medium text-gray-600">Email:</span> ' . esc_html($email) . '</div>';
    $html .= '</div>';
    $html .= '</div>';

    // Dues list
    $html .= '<h4 class="text-lg font-semibold text-gray-800 mb-4">Pending Dues</h4>';
    $html .= '<div class="space-y-4">';
    
    // Check if partial payment is enabled
    $partial_payment_enabled = ttc_is_partial_payment_enabled();
    
    if (empty($payment_dues)) {
        // No dues - create a dummy entry showing 0 amount
        $html .= '<div class="bg-white rounded-xl border border-gray-200 shadow-sm p-6">';
        $html .= '<div class="flex flex-col lg:flex-row lg:items-center lg:justify-between gap-4">';
        
        // Left Section
        $html .= '<div class="flex-1">';
        
        // Get the due as of date from options
        $due_as_of_date = get_option('ttc_last_bulk_upload_due_as_of_date', '');
        if ($due_as_of_date) {
            $formatted_due_as_of_date = date('F j, Y', strtotime($due_as_of_date));
            $html .= '<h3 class="text-xl font-semibold text-gray-800 mb-2">Dues as of ' . esc_html($formatted_due_as_of_date) . '</h3>';
        } else {
            $html .= '<h3 class="text-xl font-semibold text-gray-800 mb-2">No Pending Dues</h3>';
        }
        
        if ($partial_payment_enabled) {
            // Show Original Amount and Remaining Amount when partial payment is enabled
            $html .= '<div class="grid grid-cols-2 lg:grid-cols-4 gap-4 text-sm">';
            $html .= '<div class="flex flex-col">';
            $html .= '<span class="text-gray-500 text-xs">Original Amount</span>';
            $html .= '<span class="text-base font-medium text-gray-800">₹0.00</span>';
            $html .= '</div>';
            $html .= '<div class="flex flex-col">';
            $html .= '<span class="text-gray-500 text-xs">Remaining Amount</span>';
            $html .= '<span class="text-base font-medium text-gray-800">₹0.00</span>';
            $html .= '</div>';
            $html .= '<div class="flex flex-col">';
            $html .= '<span class="text-gray-500 text-xs">Due Date</span>';
            $html .= '<span class="text-base font-medium text-gray-800">-</span>';
            $html .= '</div>';
            $html .= '<div class="flex flex-col">';
            $html .= '<span class="text-gray-500 text-xs">Status</span>';
            $html .= '<span class="text-base font-medium text-green-600">No Dues</span>';
            $html .= '</div>';
            $html .= '</div>';
        } else {
            // Show only Amount when partial payment is disabled
            $html .= '<div class="grid grid-cols-2 lg:grid-cols-3 gap-4 text-sm">';
            $html .= '<div class="flex flex-col">';
            $html .= '<span class="text-gray-500 text-xs">Amount</span>';
            $html .= '<span class="text-base font-medium text-gray-800">₹0.00</span>';
            $html .= '</div>';
            $html .= '<div class="flex flex-col">';
            $html .= '<span class="text-gray-500 text-xs">Due Date</span>';
            $html .= '<span class="text-base font-medium text-gray-800">-</span>';
            $html .= '</div>';
            $html .= '<div class="flex flex-col">';
            $html .= '<span class="text-gray-500 text-xs">Status</span>';
            $html .= '<span class="text-base font-medium text-green-600">No Dues</span>';
            $html .= '</div>';
            $html .= '</div>';
        }
        
        $html .= '</div>';
        
        // Right Section - Payment Button (enabled for advance payments)
        $html .= '<div class="flex-shrink-0">';
        $html .= '<button onclick="openPaymentModal(event)" ';
        $html .= 'class="inline-flex items-center justify-center px-6 py-3 text-sm font-medium text-white bg-green-600 rounded-lg hover:bg-green-700 focus:ring-4 focus:ring-green-300 transition-colors duration-200" ';
        $html .= 'data-due-id="0" ';
        $html .= 'data-due-amount="0.00" ';
        $html .= 'data-original-amount="0.00" ';
        $html .= 'data-advance-payment="true">';
        $html .= 'Make Advance Payment';
        $html .= '</button>';
        $html .= '</div>';
        
        $html .= '</div>';
        $html .= '</div>';
    } else {
        // Has dues - show dues list
        foreach ($payment_dues as $due) {
            $html .= '<div class="bg-white rounded-xl border border-gray-200 shadow-sm hover:shadow-md transition-shadow duration-200">';
            $html .= '<div class="flex flex-col lg:flex-row lg:items-center lg:justify-between gap-4 p-6">';
            
            // Left Section
            $html .= '<div class="flex-1">';
            
            // Get the due as of date from options and format the title
            $due_as_of_date = get_option('ttc_last_bulk_upload_due_as_of_date', '');
            if ($due_as_of_date && $due->is_bulk_uploaded) {
                $formatted_due_as_of_date = date('F j, Y', strtotime($due_as_of_date));
                $html .= '<h3 class="text-xl font-semibold text-gray-800 mb-2">Due as of ' . esc_html($formatted_due_as_of_date) . '</h3>';
            } else {
                $html .= '<h3 class="text-xl font-semibold text-gray-800 mb-2">' . esc_html($due->title) . '</h3>';
            }
            
            if ($partial_payment_enabled) {
                // Show Original Amount and Remaining Amount when partial payment is enabled
                $html .= '<div class="grid grid-cols-2 lg:grid-cols-4 gap-4 text-sm">';
                $html .= '<div class="flex flex-col">';
                $html .= '<span class="text-gray-500 text-xs">Original Amount</span>';
                $html .= '<span class="text-base font-medium text-gray-800">₹' . esc_html(number_format($due->original_amount ?? $due->amount, 2)) . '</span>';
                $html .= '</div>';
                $html .= '<div class="flex flex-col">';
                $html .= '<span class="text-gray-500 text-xs">Remaining Amount</span>';
                $html .= '<span class="text-base font-medium text-gray-800">₹' . esc_html(number_format($due->remaining_amount ?? $due->amount, 2)) . '</span>';
                $html .= '</div>';
                $html .= '<div class="flex flex-col">';
                $html .= '<span class="text-gray-500 text-xs">Due Date</span>';
                $html .= '<span class="text-base font-medium text-gray-800">' . esc_html(date('d M Y', strtotime($due->due_date))) . '</span>';
                $html .= '</div>';
                $html .= '<div class="flex flex-col">';
                $html .= '<span class="text-gray-500 text-xs">Status</span>';
                $html .= '<span class="text-base font-medium ' . ($due->status === 'overdue' ? 'text-red-600' : 'text-yellow-600') . '">' . esc_html(ucfirst($due->status)) . '</span>';
                $html .= '</div>';
                $html .= '</div>';
            } else {
                // Show only Amount when partial payment is disabled
                $html .= '<div class="grid grid-cols-2 lg:grid-cols-3 gap-4 text-sm">';
                $html .= '<div class="flex flex-col">';
                $html .= '<span class="text-gray-500 text-xs">Amount</span>';
                $html .= '<span class="text-base font-medium text-gray-800">₹' . esc_html(number_format($due->remaining_amount ?? $due->amount, 2)) . '</span>';
                $html .= '</div>';
                $html .= '<div class="flex flex-col">';
                $html .= '<span class="text-gray-500 text-xs">Due Date</span>';
                $html .= '<span class="text-base font-medium text-gray-800">' . esc_html(date('d M Y', strtotime($due->due_date))) . '</span>';
                $html .= '</div>';
                $html .= '<div class="flex flex-col">';
                $html .= '<span class="text-gray-500 text-xs">Status</span>';
                $html .= '<span class="text-base font-medium ' . ($due->status === 'overdue' ? 'text-red-600' : 'text-yellow-600') . '">' . esc_html(ucfirst($due->status)) . '</span>';
                $html .= '</div>';
                $html .= '</div>';
            }
            
            $html .= '</div>';
            
            // Right Section - Payment Button
            $html .= '<div class="flex-shrink-0">';
            $html .= '<button onclick="openPaymentModal(event)" ';
            $html .= 'class="inline-flex items-center justify-center px-6 py-3 text-sm font-medium text-white bg-green-600 rounded-lg hover:bg-green-700 focus:ring-4 focus:ring-green-300 transition-colors duration-200" ';
            $html .= 'data-due-id="' . esc_attr($due->id) . '" ';
            $html .= 'data-due-amount="' . esc_attr(number_format($due->remaining_amount ?? $due->amount, 2)) . '" ';
            $html .= 'data-original-amount="' . esc_attr(number_format($due->original_amount ?? $due->amount, 2)) . '">';
            $html .= 'Make Payment';
            $html .= '</button>';
            $html .= '</div>';
            
            $html .= '</div>';
            $html .= '</div>';
        }
    }
    
    $html .= '</div>';
    $html .= '</div>';

    wp_send_json_success(array('html' => $html));
}

// Register AJAX actions
add_action('wp_ajax_find_member_dues', 'find_member_dues');
add_action('wp_ajax_nopriv_find_member_dues', 'find_member_dues');

/**
 * Send OTP for Quick Pay verification
 */
function send_otp_for_quick_pay() {
    // Verify nonce
    if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'quick_pay_nonce')) {
        wp_send_json_error('Security verification failed. Please refresh the page and try again.');
    }

    // Get and sanitize member identifier
    $member_identifier = isset($_POST['member_identifier']) ? sanitize_text_field($_POST['member_identifier']) : '';
    
    if (empty($member_identifier)) {
        wp_send_json_error('Please enter a Member ID or Email address.');
    }

    // Get and sanitize member identifier
    $member_identifier = isset($_POST['member_identifier']) ? sanitize_text_field($_POST['member_identifier']) : '';
    
    // Remove all whitespace from member identifier
    $member_identifier = preg_replace('/\s+/', '', $member_identifier);
    
    if (empty($member_identifier)) {
        wp_send_json_error('Please enter a Member ID or Email address.');
    }

    // Check rate limiting (max 3 attempts per 15 minutes per IP)
    $ip_address = $_SERVER['REMOTE_ADDR'];
    $rate_limit_key = 'otp_rate_limit_' . md5($ip_address);
    $rate_limit_data = get_transient($rate_limit_key);
    
    if ($rate_limit_data && $rate_limit_data['count'] >= 3) {
        $remaining_time = $rate_limit_data['expires'] - time();
        wp_send_json_error("Too many OTP requests. Please wait {$remaining_time} seconds before trying again.");
    }

    // Check if OTP security is enabled
    if (ttc_is_otp_security_enabled()) {
        // Check rate limiting using configurable settings
        $ip_address = $_SERVER['REMOTE_ADDR'];
        $rate_limit_key = 'otp_rate_limit_' . md5($ip_address);
        $rate_limit_data = get_transient($rate_limit_key);
        
        $request_limit = ttc_get_otp_request_limit();
        $rate_limit_minutes = ttc_get_otp_rate_limit_minutes();
        
        if ($rate_limit_data && $rate_limit_data['count'] >= $request_limit) {
            $remaining_time = $rate_limit_data['expires'] - time();
            wp_send_json_error("Too many OTP requests. Please wait {$remaining_time} seconds before trying again.");
        }
    }

    // Determine if input is email or membership number
    $is_email = is_email($member_identifier);
    
    // Build query to find member
    $meta_query = array();
    
    if ($is_email) {
        // Search by email
        $meta_query[] = array(
            'key' => 'email',
            'value' => $member_identifier,
            'compare' => '='
        );
    } else {
        // Search by membership number
        $meta_query[] = array(
            'key' => 'membership_number',
            'value' => $member_identifier,
            'compare' => '='
        );
    }

    // Query for member
    $member_query = new WP_Query(array(
        'post_type' => 'member',
        'posts_per_page' => 1,
        'meta_query' => $meta_query,
        'post_status' => 'publish'
    ));

    if (!$member_query->have_posts()) {
        $identifier_type = $is_email ? 'email address' : 'Member ID';
        wp_send_json_error("No member found with the provided {$identifier_type}. Please check your details and try again.");
    }

    // Get member details
    $member_query->the_post();
    $member_id = get_the_ID();
    $member_name = get_the_title();
    $member_status = get_field('member_status', $member_id);
    $membership_number = get_field('membership_number', $member_id);
    $email = get_field('email', $member_id);
    wp_reset_postdata();

    // Check if member is active
    if ($member_status !== 'active') {
        $status_message = '';
        switch ($member_status) {
            case 'inactive':
                $status_message = 'Your membership is currently inactive. Please contact the club office to activate your membership.';
                break;
            case 'suspended':
                $status_message = 'Your membership has been suspended. Please contact the club office for assistance.';
                break;
            case 'expired':
                $status_message = 'Your membership has expired. Please renew your membership to continue.';
                break;
            default:
                $status_message = 'Your membership status requires attention. Please contact the club office.';
        }
        wp_send_json_error($status_message);
    }

    // Generate OTP
    $otp = sprintf('%06d', mt_rand(0, 999999));
    $otp_expires = time() + (10 * 60); // 10 minutes expiry

    // Generate OTP using configurable expiry time
    $otp = sprintf('%04d', mt_rand(0, 9999));
    $expiry_minutes = ttc_get_otp_expiry_minutes();
    $otp_expires = time() + ($expiry_minutes * 60);

    // Store OTP in transient with member info
    $otp_key = 'quick_pay_otp_' . md5($email . $membership_number);
    $otp_data = array(
        'otp' => $otp,
        'expires' => $otp_expires,
        'member_id' => $member_id,
        'member_email' => $email,
        'member_name' => $member_name,
        'membership_number' => $membership_number,
        'attempts' => 0
    );
    set_transient($otp_key, $otp_data, 10 * 60); // 10 minutes

    // Update rate limiting
    if (!$rate_limit_data) {
        $rate_limit_data = array('count' => 1, 'expires' => time() + (15 * 60));
    } else {
        $rate_limit_data['count']++;
    }
    set_transient($rate_limit_key, $rate_limit_data, 15 * 60);

    // Generate OTP using configurable expiry time
    $otp = sprintf('%04d', mt_rand(0, 9999));
    $expiry_minutes = ttc_get_otp_expiry_minutes();
    $otp_expires = time() + ($expiry_minutes * 60);

    // Store OTP in transient with member info
    $otp_key = 'quick_pay_otp_' . md5($email . $membership_number);
    $otp_data = array(
        'otp' => $otp,
        'expires' => $otp_expires,
        'member_id' => $member_id,
        'member_email' => $email,
        'member_name' => $member_name,
        'membership_number' => $membership_number,
        'attempts' => 0
    );
    set_transient($otp_key, $otp_data, $expiry_minutes * 60);

    // Update rate limiting if OTP security is enabled
    if (ttc_is_otp_security_enabled()) {
        if (!$rate_limit_data) {
            $rate_limit_data = array('count' => 1, 'expires' => time() + ($rate_limit_minutes * 60));
        } else {
            $rate_limit_data['count']++;
        }
        set_transient($rate_limit_key, $rate_limit_data, $rate_limit_minutes * 60);
    }

    // Send OTP email
    $email_sent = send_otp_email($email, $member_name, $otp);
    
    if (!$email_sent) {
        wp_send_json_error('Failed to send verification code. Please try again later.');
    }

    wp_send_json_success(array(
        'member_id' => $member_id,
        'member_email' => $email,
        'member_name' => $member_name,
        'membership_number' => $membership_number
    ));
}

/**
 * Verify OTP and return member dues
 */
function verify_otp() {
    // Verify nonce
    if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'quick_pay_nonce')) {
        wp_send_json_error('Security verification failed. Please refresh the page and try again.');
    }

    $otp_code = isset($_POST['otp_code']) ? sanitize_text_field($_POST['otp_code']) : '';
    $member_email = isset($_POST['member_email']) ? sanitize_email($_POST['member_email']) : '';
    $membership_number = isset($_POST['membership_number']) ? sanitize_text_field($_POST['membership_number']) : '';

    if (empty($otp_code) || empty($member_email) || empty($membership_number)) {
        wp_send_json_error('Invalid verification data. Please try again.');
    }

    // Get stored OTP data
    $otp_key = 'quick_pay_otp_' . md5($member_email . $membership_number);
    $otp_data = get_transient($otp_key);

    if (!$otp_data) {
        wp_send_json_error('Verification code has expired. Please request a new code.');
    }

    // Check if OTP has expired
    if (time() > $otp_data['expires']) {
        delete_transient($otp_key);
        wp_send_json_error('Verification code has expired. Please request a new code.');
    }

    // Check attempts limit using configurable settings
    $max_attempts = ttc_get_otp_max_attempts();
    if ($otp_data['attempts'] >= $max_attempts) {
        delete_transient($otp_key);
        wp_send_json_error('Too many failed attempts. Please request a new verification code.');
    }

    // Verify OTP
    if ($otp_code !== $otp_data['otp']) {
        // Increment attempts
        $otp_data['attempts']++;
        set_transient($otp_key, $otp_data, 10 * 60);
        
        $remaining_attempts = $max_attempts - $otp_data['attempts'];
        wp_send_json_error("Invalid verification code. {$remaining_attempts} attempts remaining.");
    }

    // OTP is valid - delete it and get member dues
    delete_transient($otp_key);

    // Get payment dues for this member
    global $wpdb;
    $table_name = $wpdb->prefix . 'payment_dues';
    
    $payment_dues = $wpdb->get_results(
        $wpdb->prepare(
            "SELECT * FROM $table_name
            WHERE member_id = %d AND status IN ('pending', 'overdue')
            ORDER BY due_date ASC",
            $otp_data['member_id']
        )
    );

    // Build HTML response
    $html = '<div class="space-y-4">';
    
    // Member info header
    $html .= '<div class="bg-gray-50 p-4 rounded-lg mb-6">';
    $html .= '<h4 class="text-lg font-semibold text-gray-800 mb-2">Member Information</h4>';
    $html .= '<div class="grid grid-cols-1 md:grid-cols-3 gap-4 text-sm">';
    $html .= '<div><span class="font-medium text-gray-600">Name:</span> ' . esc_html($otp_data['member_name']) . '</div>';
    $html .= '<div><span class="font-medium text-gray-600">Membership No.:</span> ' . esc_html($otp_data['membership_number']) . '</div>';
    $html .= '<div><span class="font-medium text-gray-600">Email:</span> ' . esc_html($otp_data['member_email']) . '</div>';
    $html .= '</div>';
    $html .= '</div>';

    // Dues list
    $html .= '<h4 class="text-lg font-semibold text-gray-800 mb-4">Pending Dues</h4>';
    $html .= '<div class="space-y-4">';
    
    // Check if partial payment is enabled
    $partial_payment_enabled = ttc_is_partial_payment_enabled();
    
    if (empty($payment_dues)) {
        // No dues - create a dummy entry showing 0 amount
        $html .= '<div class="bg-white rounded-xl border border-gray-200 shadow-sm p-6">';
        $html .= '<div class="flex flex-col lg:flex-row lg:items-center lg:justify-between gap-4">';
        
        // Left Section
        $html .= '<div class="flex-1">';
        
        // Get the due as of date from options
        $due_as_of_date = get_option('ttc_last_bulk_upload_due_as_of_date', '');
        if ($due_as_of_date) {
            $formatted_due_as_of_date = date('F j, Y', strtotime($due_as_of_date));
            $html .= '<h3 class="text-xl font-semibold text-gray-800 mb-2">Dues as of ' . esc_html($formatted_due_as_of_date) . '</h3>';
        } else {
            $html .= '<h3 class="text-xl font-semibold text-gray-800 mb-2">No Pending Dues</h3>';
        }
        
        if ($partial_payment_enabled) {
            // Show Original Amount and Remaining Amount when partial payment is enabled
            $html .= '<div class="grid grid-cols-2 lg:grid-cols-4 gap-4 text-sm">';
            $html .= '<div class="flex flex-col">';
            $html .= '<span class="text-gray-500 text-xs">Original Amount</span>';
            $html .= '<span class="text-base font-medium text-gray-800">₹0.00</span>';
            $html .= '</div>';
            $html .= '<div class="flex flex-col">';
            $html .= '<span class="text-gray-500 text-xs">Remaining Amount</span>';
            $html .= '<span class="text-base font-medium text-gray-800">₹0.00</span>';
            $html .= '</div>';
            $html .= '<div class="flex flex-col">';
            $html .= '<span class="text-gray-500 text-xs">Due Date</span>';
            $html .= '<span class="text-base font-medium text-gray-800">-</span>';
            $html .= '</div>';
            $html .= '<div class="flex flex-col">';
            $html .= '<span class="text-gray-500 text-xs">Status</span>';
            $html .= '<span class="text-base font-medium text-green-600">No Dues</span>';
            $html .= '</div>';
            $html .= '</div>';
        } else {
            // Show only Amount when partial payment is disabled
            $html .= '<div class="grid grid-cols-2 lg:grid-cols-3 gap-4 text-sm">';
            $html .= '<div class="flex flex-col">';
            $html .= '<span class="text-gray-500 text-xs">Amount</span>';
            $html .= '<span class="text-base font-medium text-gray-800">₹0.00</span>';
            $html .= '</div>';
            $html .= '<div class="flex flex-col">';
            $html .= '<span class="text-gray-500 text-xs">Due Date</span>';
            $html .= '<span class="text-base font-medium text-gray-800">-</span>';
            $html .= '</div>';
            $html .= '<div class="flex flex-col">';
            $html .= '<span class="text-gray-500 text-xs">Status</span>';
            $html .= '<span class="text-base font-medium text-green-600">No Dues</span>';
            $html .= '</div>';
            $html .= '</div>';
        }
        
        $html .= '</div>';
        
        // Right Section - Payment Button (enabled for advance payments)
        $html .= '<div class="flex-shrink-0">';
        $html .= '<button onclick="openPaymentModal(event)" ';
        $html .= 'class="inline-flex items-center justify-center px-6 py-3 text-sm font-medium text-white bg-green-600 rounded-lg hover:bg-green-700 focus:ring-4 focus:ring-green-300 transition-colors duration-200" ';
        $html .= 'data-due-id="0" ';
        $html .= 'data-due-amount="0.00" ';
        $html .= 'data-original-amount="0.00" ';
        $html .= 'data-advance-payment="true">';
        $html .= 'Make Advance Payment';
        $html .= '</button>';
        $html .= '</div>';
        
        $html .= '</div>';
        $html .= '</div>';
    } else {
        // Has dues - show dues list
        foreach ($payment_dues as $due) {
            $html .= '<div class="bg-white rounded-xl border border-gray-200 shadow-sm hover:shadow-md transition-shadow duration-200">';
            $html .= '<div class="flex flex-col lg:flex-row lg:items-center lg:justify-between gap-4 p-6">';
            
            // Left Section
            $html .= '<div class="flex-1">';
            
            // Get the due as of date from options and format the title
            $due_as_of_date = get_option('ttc_last_bulk_upload_due_as_of_date', '');
            if ($due_as_of_date && $due->is_bulk_uploaded) {
                $formatted_due_as_of_date = date('F j, Y', strtotime($due_as_of_date));
                $html .= '<h3 class="text-xl font-semibold text-gray-800 mb-2">Due as of ' . esc_html($formatted_due_as_of_date) . '</h3>';
            } else {
                $html .= '<h3 class="text-xl font-semibold text-gray-800 mb-2">' . esc_html($due->title) . '</h3>';
            }
            
            if ($partial_payment_enabled) {
                // Show Original Amount and Remaining Amount when partial payment is enabled
                $html .= '<div class="grid grid-cols-2 lg:grid-cols-4 gap-4 text-sm">';
                $html .= '<div class="flex flex-col">';
                $html .= '<span class="text-gray-500 text-xs">Original Amount</span>';
                $html .= '<span class="text-base font-medium text-gray-800">₹' . esc_html(number_format($due->original_amount ?? $due->amount, 2)) . '</span>';
                $html .= '</div>';
                $html .= '<div class="flex flex-col">';
                $html .= '<span class="text-gray-500 text-xs">Remaining Amount</span>';
                $html .= '<span class="text-base font-medium text-gray-800">₹' . esc_html(number_format($due->remaining_amount ?? $due->amount, 2)) . '</span>';
                $html .= '</div>';
                $html .= '<div class="flex flex-col">';
                $html .= '<span class="text-gray-500 text-xs">Due Date</span>';
                $html .= '<span class="text-base font-medium text-gray-800">' . esc_html(date('d M Y', strtotime($due->due_date))) . '</span>';
                $html .= '</div>';
                $html .= '<div class="flex flex-col">';
                $html .= '<span class="text-gray-500 text-xs">Status</span>';
                $html .= '<span class="text-base font-medium ' . ($due->status === 'overdue' ? 'text-red-600' : 'text-yellow-600') . '">' . esc_html(ucfirst($due->status)) . '</span>';
                $html .= '</div>';
                $html .= '</div>';
            } else {
                // Show only Amount when partial payment is disabled
                $html .= '<div class="grid grid-cols-2 lg:grid-cols-3 gap-4 text-sm">';
                $html .= '<div class="flex flex-col">';
                $html .= '<span class="text-gray-500 text-xs">Amount</span>';
                $html .= '<span class="text-base font-medium text-gray-800">₹' . esc_html(number_format($due->remaining_amount ?? $due->amount, 2)) . '</span>';
                $html .= '</div>';
                $html .= '<div class="flex flex-col">';
                $html .= '<span class="text-gray-500 text-xs">Due Date</span>';
                $html .= '<span class="text-base font-medium text-gray-800">' . esc_html(date('d M Y', strtotime($due->due_date))) . '</span>';
                $html .= '</div>';
                $html .= '<div class="flex flex-col">';
                $html .= '<span class="text-gray-500 text-xs">Status</span>';
                $html .= '<span class="text-base font-medium ' . ($due->status === 'overdue' ? 'text-red-600' : 'text-yellow-600') . '">' . esc_html(ucfirst($due->status)) . '</span>';
                $html .= '</div>';
                $html .= '</div>';
            }
            
            $html .= '</div>';
            
            // Right Section - Payment Button
            $html .= '<div class="flex-shrink-0">';
            $html .= '<button onclick="openPaymentModal(event)" ';
            $html .= 'class="inline-flex items-center justify-center px-6 py-3 text-sm font-medium text-white bg-green-600 rounded-lg hover:bg-green-700 focus:ring-4 focus:ring-green-300 transition-colors duration-200" ';
            $html .= 'data-due-id="' . esc_attr($due->id) . '" ';
            $html .= 'data-due-amount="' . esc_attr(number_format($due->remaining_amount ?? $due->amount, 2)) . '" ';
            $html .= 'data-original-amount="' . esc_attr(number_format($due->original_amount ?? $due->amount, 2)) . '">';
            $html .= 'Make Payment';
            $html .= '</button>';
            $html .= '</div>';
            
            $html .= '</div>';
            $html .= '</div>';
        }
    }
    
    $html .= '</div>';
    $html .= '</div>';

    wp_send_json_success(array('html' => $html));
}

/**
 * Resend OTP for Quick Pay
 */
function resend_otp() {
    // Verify nonce
    if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'quick_pay_nonce')) {
        wp_send_json_error('Security verification failed. Please refresh the page and try again.');
    }

    $member_id = isset($_POST['member_id']) ? intval($_POST['member_id']) : 0;
    $member_email = isset($_POST['member_email']) ? sanitize_email($_POST['member_email']) : '';
    $member_name = isset($_POST['member_name']) ? sanitize_text_field($_POST['member_name']) : '';
    $membership_number = isset($_POST['membership_number']) ? sanitize_text_field($_POST['membership_number']) : '';

    if (!$member_id || !$member_email || !$member_name || !$membership_number) {
        wp_send_json_error('Invalid member data. Please try again.');
    }

    // Check rate limiting for resend (max 2 resends per 10 minutes per email)
    $resend_rate_limit_key = 'otp_resend_limit_' . md5($member_email);
    $resend_rate_limit_data = get_transient($resend_rate_limit_key);
    
    if ($resend_rate_limit_data && $resend_rate_limit_data['count'] >= 2) {
        $remaining_time = $resend_rate_limit_data['expires'] - time();
        wp_send_json_error("Too many resend requests. Please wait {$remaining_time} seconds before trying again.");
    }

    // Check rate limiting for resend using configurable settings
    if (ttc_is_otp_security_enabled()) {
        $resend_rate_limit_key = 'otp_resend_limit_' . md5($member_email);
        $resend_rate_limit_data = get_transient($resend_rate_limit_key);
        
        $resend_limit = ttc_get_otp_resend_limit();
        $resend_minutes = ttc_get_otp_resend_minutes();
        
        if ($resend_rate_limit_data && $resend_rate_limit_data['count'] >= $resend_limit) {
            $remaining_time = $resend_rate_limit_data['expires'] - time();
            wp_send_json_error("Too many resend requests. Please wait {$remaining_time} seconds before trying again.");
        }
    }

    // Generate new OTP
    $otp = sprintf('%06d', mt_rand(0, 999999));
    $otp_expires = time() + (10 * 60); // 10 minutes expiry

    // Generate new OTP using configurable expiry time
    $otp = sprintf('%04d', mt_rand(0, 9999));
    $expiry_minutes = ttc_get_otp_expiry_minutes();
    $otp_expires = time() + ($expiry_minutes * 60);

    // Store new OTP data
    $otp_key = 'quick_pay_otp_' . md5($member_email . $membership_number);
    $otp_data = array(
        'otp' => $otp,
        'expires' => $otp_expires,
        'member_id' => $member_id,
        'member_email' => $member_email,
        'member_name' => $member_name,
        'membership_number' => $membership_number,
        'attempts' => 0
    );
    set_transient($otp_key, $otp_data, 10 * 60);

    // Update resend rate limiting
    if (!$resend_rate_limit_data) {
        $resend_rate_limit_data = array('count' => 1, 'expires' => time() + (10 * 60));
    } else {
        $resend_rate_limit_data['count']++;
    }
    set_transient($resend_rate_limit_key, $resend_rate_limit_data, 10 * 60);

    // Generate new OTP using configurable expiry time
    $otp = sprintf('%06d', mt_rand(0, 999999));
    $expiry_minutes = ttc_get_otp_expiry_minutes();
    $otp_expires = time() + ($expiry_minutes * 60);

    // Store new OTP data
    $otp_key = 'quick_pay_otp_' . md5($member_email . $membership_number);
    $otp_data = array(
        'otp' => $otp,
        'expires' => $otp_expires,
        'member_id' => $member_id,
        'member_email' => $member_email,
        'member_name' => $member_name,
        'membership_number' => $membership_number,
        'attempts' => 0
    );
    set_transient($otp_key, $otp_data, $expiry_minutes * 60);

    // Update resend rate limiting if OTP security is enabled
    if (ttc_is_otp_security_enabled()) {
        if (!$resend_rate_limit_data) {
            $resend_rate_limit_data = array('count' => 1, 'expires' => time() + ($resend_minutes * 60));
        } else {
            $resend_rate_limit_data['count']++;
        }
        set_transient($resend_rate_limit_key, $resend_rate_limit_data, $resend_minutes * 60);
    }

    // Send new OTP email
    $email_sent = send_otp_email($member_email, $member_name, $otp);
    
    if (!$email_sent) {
        wp_send_json_error('Failed to resend verification code. Please try again later.');
    }

    wp_send_json_success('Verification code resent successfully.');
}

/**
 * Send OTP email to member
 */
function send_otp_email($email, $member_name, $otp) {
    $subject = 'TTC Quick Pay - Email Verification Code';
    
    // Get expiry minutes for the email template
    $expiry_minutes = ttc_get_otp_expiry_minutes();
    
    // Create HTML email template
    $message = '
    <html>
    <head>
        <style>
            body { font-family: Arial, sans-serif; line-height: 1.6; color: #333; }
            .container { max-width: 600px; margin: 0 auto; padding: 20px; }
            .header { background-color: #f8f9fa; padding: 20px; border-radius: 5px; margin-bottom: 20px; }
            .otp-container { 
                margin: 25px 0; 
                padding: 20px; 
                background-color: #f8f9fa; 
                border-radius: 5px;
                text-align: center;
            }
            .otp-code { 
                font-size: 32px; 
                color: #28a745; 
                font-weight: bold;
                letter-spacing: 8px;
                display: block;
                margin: 15px 0;
                font-family: monospace;
            }
            .footer { margin-top: 20px; font-size: 12px; color: #6c757d; }
        </style>
    </head>
    <body>
        <div class="container">
            <div class="header">
                <h2>TTC Quick Pay Verification</h2>
            </div>
            <p>Dear ' . esc_html($member_name) . ',</p>
            <p>You have requested to view your payment dues through TTC Quick Pay. To proceed, please use the verification code below:</p>
            
            <div class="otp-container">
                <span class="otp-code">' . esc_html($otp) . '</span>
                <p><strong>This code will expire in ' . esc_html($expiry_minutes) . ' minutes.</strong></p>
            </div>
            
            <p>If you did not request this verification code, please ignore this email.</p>
            
            <div class="footer">
                <p>This is an automated message from the TTC website. Please do not reply to this email.</p>
            </div>
        </div>
    </body>
    </html>';

    $headers = [
        'Content-Type: text/html; charset=UTF-8',
        'From: TTC Website <noreply@ttc.com>'
    ];
    
    return wp_mail($email, $subject, $message, $headers);
}

// Register AJAX actions for OTP functionality
add_action('wp_ajax_send_otp_for_quick_pay', 'send_otp_for_quick_pay');
add_action('wp_ajax_nopriv_send_otp_for_quick_pay', 'send_otp_for_quick_pay');
add_action('wp_ajax_verify_otp', 'verify_otp');
add_action('wp_ajax_nopriv_verify_otp', 'verify_otp');
add_action('wp_ajax_resend_otp', 'resend_otp');
add_action('wp_ajax_nopriv_resend_otp', 'resend_otp');

?>

Youez - 2016 - github.com/yon3zu
LinuXploit