# yatra/3.0.11/templates/partials/booking-form-fields.php

Yatra – Travel Booking &amp; Tour Operator Software, version 3.0.11. 1,000 lines.

- Page: https://pluginprobe.com/plugins/yatra/3.0.11/code/templates/partials/booking-form-fields.php
- Raw: https://pluginprobe.com/plugins/yatra/3.0.11/raw/templates/partials/booking-form-fields.php
- Modified: 2026-07-20T12:05:54+00:00

Line numbers below start at 1. Link to a line or a range by appending a fragment to the
page URL, for example `https://pluginprobe.com/plugins/yatra/3.0.11/code/templates/partials/booking-form-fields.php#L10-L20`.

```php
<?php
/**
 * Booking Form Fields Partial
 * 
 * Renders form fields based on configuration from Settings -> Booking Form
 * Dynamic form customization requires the Dynamic Form Field module (Pro).
 * 
 * @package Yatra
 * 
 * Expected variables:
 * - $trip_id
 * - $trip_slug (optional)
 * - $total_travelers
 * - $deposit_required
 * - $deposit_percentage
 * - $partial_payment
 * - $partial_payment_percentage
 * - $enabled_gateways
 * 
 * Form Configuration Structure:
 * - contact_form: Lead traveler contact information (prefix: contact_)
 * - emergency_contact_form: Emergency contact details (prefix: emergency_)
 * - traveler_form: Individual traveler details (array format: travelers[1][field_id])
 */

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

// Check if Dynamic Form Field module is enabled (Pro feature)
$is_dynamic_form_enabled = apply_filters('yatra_dynamic_form_field_enabled', false);

// Get form configuration - returns default config if module disabled, custom config if enabled
$form_config = yatra_get_booking_form_config();


// Country list — pulls from the canonical FormatHelper source so every
// dropdown (admin, public booking, Pro modules) shows the same full
// ISO-3166-1 list. Operators that want a "popular countries" subset
// or custom ordering apply the `yatra_countries_list` filter.
$countries = \Yatra\Helpers\FormatHelper::getCountries();

/**
 * Render a single form field based on configuration
 * 
 * @param array $field Field configuration
 * @param string $prefix Field name prefix (e.g., 'contact_', 'emergency_')
 * @param array $countries Country list for country fields
 * @param string $custom_name Optional custom field name (for array-style names like travelers[1][field_id])
 * @param string $custom_id Optional custom field ID
 */
if (!function_exists('yatra_render_form_field')) {
function yatra_render_form_field($field, $prefix = '', $countries = [], $custom_name = null, $custom_id = null) {
    if (empty($field['enabled'])) {
        return;
    }

    // Display-only text block: render admin-authored content (safe HTML) at this
    // position between fields. It is NOT an input — no label, name, value or
    // required handling — so it submits nothing and is never validated.
    if (($field['type'] ?? '') === 'text_block') {
        $tb_width = 'yatra-field-full';
        if (($field['width'] ?? '') === 'half') {
            $tb_width = 'yatra-field-half';
        } elseif (($field['width'] ?? '') === 'third') {
            $tb_width = 'yatra-field-third';
        }
        ?>
        <div class="yatra-form-group yatra-form-text-block <?php echo esc_attr($tb_width); ?>" data-applies-to="<?php echo esc_attr($field['applies_to'] ?? 'all'); ?>">
            <?php echo wpautop(wp_kses_post(yatra_translate_form_string($field['content'] ?? ''))); ?>
        </div>
        <?php
        return;
    }

    $field_id = $custom_id ? esc_attr($custom_id) : esc_attr($prefix . $field['id']);
    $field_name = $custom_name ? esc_attr($custom_name) : esc_attr($prefix . $field['id']);
    $required = !empty($field['required']);
    $required_attr = $required ? 'required' : '';
    $required_star = $required ? '<span class="required">*</span>' : '';

    $prefill_value = '';
    if ($custom_name === null && $prefix === 'contact_') {
        global $booking;
        if (isset($booking) && is_object($booking)) {
            $prop = 'contact_' . ($field['id'] ?? '');
            if (!empty($prop) && isset($booking->{$prop}) && $booking->{$prop} !== null && $booking->{$prop} !== '') {
                $prefill_value = (string) $booking->{$prop};
            }
        }
    }
    
    $width_class = '';
    if (!empty($field['width'])) {
        switch ($field['width']) {
            case 'half':
                $width_class = 'yatra-field-half';
                break;
            case 'third':
                $width_class = 'yatra-field-third';
                break;
            default:
                $width_class = 'yatra-field-full';
        }
    }
    $field_type = $field['type'] ?? 'text';
    ?>
    <div class="yatra-form-group <?php echo esc_attr($width_class); ?> <?php echo $field_type === 'checkbox' ? 'yatra-form-group--checkbox' : ''; ?>" data-applies-to="<?php echo esc_attr($field['applies_to'] ?? 'all'); ?>">
        <?php if ($field_type !== 'checkbox') : ?>
        <label for="<?php echo $field_id; ?>">
            <?php echo esc_html(yatra_translate_form_string($field['label'])); ?> <?php echo $required_star; ?>
        </label>
        <?php endif; ?>
        <?php
        switch ($field['type']) {
            case 'checkbox':
                $is_checked = ($prefill_value === '1' || $prefill_value === 'on' || $prefill_value === 'true');
                ?>
                <label for="<?php echo $field_id; ?>" class="yatra-inline-checkbox-label">
                    <input
                        type="checkbox"
                        id="<?php echo $field_id; ?>"
                        name="<?php echo $field_name; ?>"
                        value="1"
                        <?php checked($is_checked, true, false); ?>
                        <?php echo $required_attr; ?>
                    />
                    <span><?php echo esc_html(yatra_translate_form_string($field['label'])); ?><?php echo $required_star; ?></span>
                </label>
                <?php
                break;
            case 'select':
                ?>
                <select id="<?php echo $field_id; ?>" name="<?php echo $field_name; ?>" <?php echo $required_attr; ?>>
                    <option value=""><?php echo esc_html(yatra_translate_form_string($field['placeholder'] ?? '') ?: __('Select...', 'yatra')); ?></option>
                    <?php if (!empty($field['options'])) : ?>
                        <?php foreach ($field['options'] as $option) : ?>
                            <option value="<?php echo esc_attr($option['value']); ?>" <?php echo selected($prefill_value, (string) ($option['value'] ?? ''), false); ?>>
                                <?php echo esc_html(yatra_translate_form_string($option['label'] ?? '')); ?>
                            </option>
                        <?php endforeach; ?>
                    <?php endif; ?>
                </select>
                <?php
                break;
                
            case 'country':
                // Flag base is handed to the field (same contract as the phone
                // widget) so country-select.js can build flag URLs without
                // knowing the plugin URL. The select still renders and submits
                // normally — the widget is a progressive enhancement over it.
                $country_flag_base = \defined('YATRA_PLUGIN_URL') ? YATRA_PLUGIN_URL . 'assets/img/flags/' : '';
                ?>
                <select id="<?php echo $field_id; ?>" name="<?php echo $field_name; ?>" <?php echo $required_attr; ?>
                        data-yatra-country-select
                        data-flag-base="<?php echo esc_url($country_flag_base); ?>">
                    <option value=""><?php echo esc_html(yatra_translate_form_string($field['placeholder'] ?? '') ?: __('Select Country', 'yatra')); ?></option>
                    <?php foreach ($countries as $code => $name) : ?>
                        <option value="<?php echo esc_attr($code); ?>" <?php echo selected($prefill_value, (string) $code, false); ?>>
                            <?php echo esc_html($name); ?>
                        </option>
                    <?php endforeach; ?>
                </select>
                <?php
                break;
                
            case 'textarea':
                ?>
                <textarea 
                    id="<?php echo $field_id; ?>" 
                    name="<?php echo $field_name; ?>" 
                    placeholder="<?php echo esc_attr(yatra_translate_form_string($field['placeholder'] ?? '')); ?>"
                    <?php echo $required_attr; ?>
                    rows="3"
                ><?php echo esc_textarea($prefill_value); ?></textarea>
                <?php
                break;
                
            case 'date':
                $min_attr = '';
                // Date-of-birth-style fields are bounded to today and flagged so
                // booking.js can upgrade them to a picker with fast year jumping
                // (a native date popup steps one month at a time). The class is
                // the JS hook; if JS/flatpickr is unavailable the input stays a
                // working native <input type="date">.
                $is_dob = (stripos($field_id, 'birth') !== false || stripos($field_id, 'dob') !== false
                    || stripos($field_name, 'birth') !== false || stripos($field_name, 'dob') !== false);
                $dob_attr = $is_dob ? ' data-yatra-dob="1" max="' . esc_attr(date('Y-m-d')) . '"' : '';
                // Native date inputs ignore the placeholder, but once booking.js
                // upgrades the field to a flatpickr text input the placeholder is
                // what keeps it visually consistent with the other text fields
                // (otherwise it renders blank). Use the field's own placeholder
                // when set, else a format hint matching flatpickr's Y-m-d output.
                $date_placeholder = !empty($field['placeholder'])
                    ? yatra_translate_form_string($field['placeholder'])
                    : 'YYYY-MM-DD';
                ?>
                <input
                    type="date"
                    class="yatra-js-date"
                    id="<?php echo $field_id; ?>"
                    name="<?php echo $field_name; ?>"
                    placeholder="<?php echo esc_attr($date_placeholder); ?>"
                    <?php echo $min_attr; ?>
                    <?php echo $dob_attr; ?>
                    <?php echo $required_attr; ?>
                    value="<?php echo esc_attr($prefill_value); ?>"
                >
                <?php
                break;
                
            default: // text, email, tel, number
                // Phone (tel) fields get the international country-code widget
                // UNLESS it has been explicitly disabled in the (Pro) form
                // builder. A missing key means ON, so every existing form and
                // customized field keeps the widget without needing a re-save.
                $is_phone_widget = (($field['type'] ?? '') === 'tel')
                    && (!array_key_exists('show_country_code', $field) || !empty($field['show_country_code']));

                if ($is_phone_widget) {
                    // Split the stored value for display: an international value
                    // ("+9779806015400") is parsed into country + national part;
                    // a legacy bare number is left in the number box under the
                    // default country (never rewritten on display).
                    $pv = (string) $prefill_value;
                    $detected = \Yatra\Helpers\FormatHelper::detectPhoneCountry($pv);
                    if ($detected !== null) {
                        $p_iso = $detected['iso'];
                        $p_dial = $detected['dial'];
                        $p_national = $detected['national'];
                    } else {
                        $p_iso = \Yatra\Helpers\FormatHelper::getDefaultPhoneCountry();
                        $p_dial = \Yatra\Helpers\FormatHelper::getDialingCode($p_iso);
                        $p_national = $pv;
                    }

                    // Companion hidden field holding the chosen ISO. Array-style
                    // names (travelers[0][phone]) become travelers[0][phone_country];
                    // the server combines dial code + national number on submit.
                    if (preg_match('/^(.*)\[([^\]]+)\]$/', $field_name, $mm)) {
                        $companion_name = $mm[1] . '[' . $mm[2] . '_country]';
                    } else {
                        $companion_name = $field_name . '_country';
                    }

                    $flag_base = \defined('YATRA_PLUGIN_URL') ? YATRA_PLUGIN_URL . 'assets/img/flags/' : '';
                    ?>
                    <div class="yatra-phone-field" data-yatra-phone
                         data-default-iso="<?php echo esc_attr($p_iso); ?>"
                         data-flag-base="<?php echo esc_url($flag_base); ?>">
                        <button type="button" class="yatra-phone-country" aria-haspopup="listbox" aria-expanded="false"
                                aria-label="<?php esc_attr_e('Select country dialing code', 'yatra'); ?>">
                            <img class="yatra-phone-flag" src="<?php echo esc_url($flag_base . strtolower($p_iso) . '.svg'); ?>"
                                 alt="" width="22" height="16" loading="lazy">
                            <span class="yatra-phone-dial">+<?php echo esc_html($p_dial); ?></span>
                            <span class="yatra-phone-caret" aria-hidden="true"></span>
                        </button>
                        <input
                            type="tel"
                            class="yatra-phone-number"
                            id="<?php echo $field_id; ?>"
                            name="<?php echo $field_name; ?>"
                            value="<?php echo esc_attr($p_national); ?>"
                            placeholder="<?php echo esc_attr(yatra_translate_form_string($field['placeholder'] ?? '')); ?>"
                            inputmode="tel"
                            autocomplete="tel-national"
                            <?php echo $required_attr; ?>
                        >
                        <input type="hidden" class="yatra-phone-iso"
                               name="<?php echo esc_attr($companion_name); ?>"
                               value="<?php echo esc_attr($p_iso); ?>">
                    </div>
                    <?php
                } else {
                    ?>
                    <input
                        type="<?php echo esc_attr($field['type']); ?>"
                        id="<?php echo $field_id; ?>"
                        name="<?php echo $field_name; ?>"
                        placeholder="<?php echo esc_attr(yatra_translate_form_string($field['placeholder'] ?? '')); ?>"
                        <?php echo $required_attr; ?>
                        value="<?php echo esc_attr($prefill_value); ?>"
                    >
                    <?php
                }
                break;
        }
        ?>
    </div>
    <?php
}
} // End function_exists check for yatra_render_form_field

/**
 * Render a form section with all its fields
 */
if (!function_exists('yatra_render_form_section')) {
function yatra_render_form_section($section_config, $prefix = '', $countries = []) {
    if (isset($section_config['enabled']) && !$section_config['enabled']) {
        return;
    }
    
    $fields = $section_config['fields'] ?? [];
    
    // Sort fields by order
    usort($fields, function($a, $b) {
        return ($a['order'] ?? 0) - ($b['order'] ?? 0);
    });
    
    // Group fields by section (for subsections like dietary)
    $grouped_fields = [];
    $current_section = null;
    
    foreach ($fields as $field) {
        if (!empty($field['enabled'])) {
            $section = $field['section'] ?? 'main';
            if (!isset($grouped_fields[$section])) {
                $grouped_fields[$section] = [];
            }
            $grouped_fields[$section][] = $field;
        }
    }
    ?>
    <div class="yatra-booking-section">
        <h2 class="yatra-section-title"><?php echo esc_html(yatra_translate_form_string($section_config['title'] ?? '')); ?></h2>
        <?php if (!empty($section_config['description'])) : ?>
            <p class="yatra-section-description"><?php echo esc_html(yatra_translate_form_string($section_config['description'])); ?></p>
        <?php endif; ?>
        
        <?php foreach ($grouped_fields as $section_key => $section_fields) : ?>
            <?php if ($section_key !== 'main') : ?>
                <div class="yatra-traveler-subsection">
                    <h4 class="yatra-subsection-title">
                        <?php
                        switch ($section_key) {
                            case 'passport':
                                esc_html_e('Additional details', 'yatra');
                                break;
                            case 'dietary_medical':
                                esc_html_e('Dietary & Medical Requirements', 'yatra');
                                break;
                            default:
                                echo esc_html(ucwords(str_replace('_', ' ', $section_key)));
                        }
                        ?>
                    </h4>
            <?php endif; ?>
            
            <div class="yatra-form-row">
                <?php foreach ($section_fields as $field) : ?>
                    <?php yatra_render_form_field($field, $prefix, $countries); ?>
                <?php endforeach; ?>
            </div>
            
            <?php if ($section_key !== 'main') : ?>
                </div>
            <?php endif; ?>
        <?php endforeach; ?>
    </div>
    <?php
}
} // End function_exists check for yatra_render_form_section
?>

<!-- Hidden Fields -->
<input type="hidden" name="trip_id" value="<?php echo esc_attr($trip_id); ?>">
<?php if (!empty($trip_slug)) : ?>
<input type="hidden" name="trip_slug" value="<?php echo esc_attr($trip_slug); ?>">
<?php endif; ?>

<?php if ($is_remaining_payment) : ?>
    <input type="hidden" name="is_remaining_payment" value="1">
    <?php if (!empty($existing_booking_id)) : ?>
        <input type="hidden" name="existing_booking_id" value="<?php echo esc_attr($existing_booking_id); ?>">
    <?php endif; ?>
    <?php if (!empty($booking_reference)) : ?>
        <input type="hidden" name="booking_reference" value="<?php echo esc_attr($booking_reference); ?>">
    <?php endif; ?>
    <?php if ($remaining_amount !== null) : ?>
        <input type="hidden" name="remaining_amount" value="<?php echo esc_attr($remaining_amount); ?>">
    <?php endif; ?>
    <?php if (!empty($booking->amount_paid)) : ?>
        <input type="hidden" name="amount_paid" value="<?php echo esc_attr($booking->amount_paid); ?>">
    <?php endif; ?>
    <?php if (!empty($booking->total_amount)) : ?>
        <input type="hidden" name="total_amount" value="<?php echo esc_attr($booking->total_amount); ?>">
    <?php endif; ?>
<?php endif; ?>

<?php if (!$is_remaining_payment) : ?>
    <!-- Contact Form Section -->
    <?php 
    $contact_config = $form_config['contact_form'] ?? [];
    if (!empty($contact_config)) {
        yatra_render_form_section($contact_config, 'contact_', $countries);
    }
    ?>

    <!-- Emergency Contact Section -->
    <?php 
    $emergency_config = $form_config['emergency_contact_form'] ?? [];
    if (!empty($emergency_config) && (!isset($emergency_config['enabled']) || $emergency_config['enabled'])) {
        yatra_render_form_section($emergency_config, 'emergency_', $countries);
    }
    ?>

    <!-- Traveler Information Section -->
    <?php 
    $traveler_config = $form_config['traveler_form'] ?? [];
    ?>
<?php endif; ?>

<?php 
$traveler_config = $form_config['traveler_form'] ?? [];
$traveler_count = isset($total_travelers) ? max(1, (int)$total_travelers) : 1;

// Check if we have traveler-based pricing info
$pricing_type = isset($pricing_type) ? $pricing_type : 'regular';
$price_types = isset($price_types) ? $price_types : [];
$traveler_counts = isset($traveler_counts) ? $traveler_counts : [];

/**
 * Coerce JSON / serialized / object values to a list for foreach-safe iteration.
 *
 * @param mixed $value
 * @return array<int|string, mixed>
 */
$yatra_normalize_booking_arrayish = static function ($value): array {
    if ($value === null || $value === false || $value === '') {
        return [];
    }
    if (is_array($value)) {
        return $value;
    }
    if ($value instanceof \stdClass) {
        return (array) $value;
    }
    if (is_string($value)) {
        $trim = trim($value);
        if ($trim === '') {
            return [];
        }
        $decoded = json_decode($value, true);
        if (json_last_error() === JSON_ERROR_NONE && is_array($decoded)) {
            return $decoded;
        }
        if (function_exists('maybe_unserialize')) {
            $un = maybe_unserialize($value);
            if (is_array($un)) {
                return $un;
            }
        }
    }

    return [];
};

$price_types = $yatra_normalize_booking_arrayish($price_types);
$traveler_counts = $yatra_normalize_booking_arrayish($traveler_counts);

// Build traveler-to-category mapping for traveler-based pricing
$traveler_category_map = [];
if ($pricing_type === 'traveler_based' && !empty($price_types) && !empty($traveler_counts)) {
    $traveler_index = 1;
    foreach ($price_types as $index => $pt) {
        $pt = (object) $pt;
        $category_id = $pt->category_id ?? $index;
        $category_label = $pt->category_label ?? __('Traveler', 'yatra');
        $count = isset($traveler_counts[$category_id]) ? (int) $traveler_counts[$category_id] : 0;
        
        for ($c = 1; $c <= $count; $c++) {
            $traveler_category_map[$traveler_index] = [
                'category_id' => $category_id,
                'category_label' => $category_label,
                'category_index' => $c,
            ];
            $traveler_index++;
        }
    }
}

$price_candidates = [
    isset($trip->price) ? (float) $trip->price : 0,
    isset($booking->session_price) ? (float) $booking->session_price : 0,
    isset($trip->sale_price) ? (float) $trip->sale_price : 0,
    isset($trip->original_price) ? (float) $trip->original_price : 0,
];

$effective_trip_price = 0;
foreach ($price_candidates as $candidate) {
    if ($candidate > 0) {
        $effective_trip_price = $candidate;
        break;
    }
}

$traveler_price_rows = [];
$calculated_total = 0;

if ($pricing_type === 'traveler_based' && !empty($price_types)) {
    foreach ($price_types as $index => $pt) {
        $pt = (object) $pt;
        $category_id = $pt->category_id ?? $index;
        $category_label = $pt->category_label ?? __('Traveler', 'yatra');
        $category_price = isset($pt->effective_price) ? (float) $pt->effective_price : ($pt->sale_price ?? $pt->discounted_price ?? $pt->original_price ?? 0);
        $count = isset($traveler_counts[$category_id]) ? (int) $traveler_counts[$category_id] : ($index === 0 ? 1 : 0);
        // Single source of truth for the line amount (per-person × count, flat
        // per-group, or per-block group pricing). Defaults to the prior flat/
        // multiply behaviour when group_overflow is absent.
        $pt_pricing_mode = $pt->pricing_mode ?? 'per_person';
        $subtotal = \Yatra\Services\TripPricingService::categoryLineSubtotal($pt, $count, $category_price);

        $age_info = '';
        if (isset($pt->age_min) || isset($pt->age_max)) {
            if (isset($pt->age_min) && isset($pt->age_max)) {
                /* translators: 1: minimum age, 2: maximum age. */
                $age_info = sprintf(__('(Age %1$d-%2$d)', 'yatra'), $pt->age_min, $pt->age_max);
            } elseif (isset($pt->age_min)) {
                /* translators: %d: minimum age. */
                $age_info = sprintf(__('(Age %d+)', 'yatra'), $pt->age_min);
            } else {
                /* translators: %d: maximum age. */
                $age_info = sprintf(__('(Up to age %d)', 'yatra'), $pt->age_max);
            }
        }

        $traveler_price_rows[] = [
            'category_id' => $category_id,
            'category_label' => $category_label,
            'category_price' => $category_price,
            'count' => $count,
            'subtotal' => $subtotal,
            'pricing_mode' => $pt_pricing_mode,
            'age_info' => $age_info,
        ];

        $calculated_total += $subtotal;

        if ($effective_trip_price <= 0 && $category_price > 0) {
            $effective_trip_price = $category_price;
        }
    }
}

if ($effective_trip_price <= 0) {
    $effective_trip_price = 0;
}

$initial_total_amount = $calculated_total > 0
    ? $calculated_total
    : ($effective_trip_price * max(1, (int) $total_travelers));

if ($initial_total_amount <= 0 && $effective_trip_price > 0) {
    $initial_total_amount = $effective_trip_price * max(1, (int) $total_travelers);
}

$initial_due_amount = $initial_total_amount;

?>

<?php
// Honour the Traveler section's enable flag (Pro Dynamic Form module), mirroring
// the Contact/Emergency sections. The default config has no `enabled` key, so an
// un-customised/Free site treats the section as enabled and renders as before.
$traveler_section_enabled = !isset($traveler_config['enabled']) || (bool) $traveler_config['enabled'];
?>
<?php if (!empty($traveler_config) && $traveler_section_enabled && !$is_remaining_payment) : ?>
<div class="yatra-booking-section">
    <h2 class="yatra-section-title"><?php echo esc_html(yatra_translate_form_string($traveler_config['title'] ?? '') ?: __('Traveler Information', 'yatra')); ?></h2>
    <?php if (!empty($traveler_config['description'])) : ?>
        <p class="yatra-section-description"><?php echo esc_html(yatra_translate_form_string($traveler_config['description'])); ?></p>
    <?php endif; ?>
    
    <div id="yatra-travelers-container">
        <?php 
        $traveler_fields = $traveler_config['fields'] ?? [];
        usort($traveler_fields, function($a, $b) {
            return ($a['order'] ?? 0) - ($b['order'] ?? 0);
        });
        
        // Group fields by section
        $grouped_traveler_fields = [];
        foreach ($traveler_fields as $field) {
            if (!empty($field['enabled'])) {
                $section = $field['section'] ?? 'main';
                if (!isset($grouped_traveler_fields[$section])) {
                    $grouped_traveler_fields[$section] = [];
                }
                $grouped_traveler_fields[$section][] = $field;
            }
        }
        
        for ($i = 1; $i <= $traveler_count; $i++) : 
            // Determine traveler label based on category if traveler-based pricing
            if (!empty($traveler_category_map[$i])) {
                $category_info = $traveler_category_map[$i];
                $category_label = $category_info['category_label'];
                $category_index = $category_info['category_index'];
                /* translators: 1: traveler category label (e.g. Adult, Child), 2: sequential index within that category. */
                $traveler_label = sprintf(__('%1$s %2$d', 'yatra'), $category_label, $category_index);
                if ($i === 1) {
                    $traveler_label .= ' (' . __('Lead Traveler', 'yatra') . ')';
                }
            } else {
            /* translators: %d: traveler sequence number. */
            $traveler_label = ($i === 1) ? __('Traveler 1 (Lead Traveler)', 'yatra') : sprintf(__('Traveler %d', 'yatra'), $i);
            }

            // Fields targeted at the lead traveler (applies_to === 'lead') render
            // only for Traveler 1; everything else ('all' / unset) renders for
            // every traveler. Empty subsections are dropped so additional
            // travelers don't get an orphan heading.
            $traveler_grouped_fields = [];
            foreach ($grouped_traveler_fields as $sk => $sf) {
                $applicable = array_values(array_filter($sf, function ($f) use ($i) {
                    return ($f['applies_to'] ?? 'all') !== 'lead' || $i === 1;
                }));
                if (!empty($applicable)) {
                    $traveler_grouped_fields[$sk] = $applicable;
                }
            }
        ?>
        <div class="yatra-traveler-form" data-traveler-index="<?php echo esc_attr($i); ?>" <?php if (!empty($traveler_category_map[$i])): ?>data-category-id="<?php echo esc_attr($traveler_category_map[$i]['category_id']); ?>" data-category-label="<?php echo esc_attr($traveler_category_map[$i]['category_label']); ?>"<?php endif; ?>>
            <div class="yatra-traveler-header">
                <h3 class="yatra-traveler-title"><?php echo esc_html($traveler_label); ?></h3>
                <?php if ($i > 1) : ?>
                    <span class="yatra-traveler-note"><?php esc_html_e('Additional traveler', 'yatra'); ?></span>
                <?php endif; ?>
            </div>
            
            <?php foreach ($traveler_grouped_fields as $section_key => $section_fields) : ?>
                <?php if ($section_key !== 'main') : ?>
                    <div class="yatra-traveler-subsection">
                        <h4 class="yatra-subsection-title">
                            <?php
                            switch ($section_key) {
                                case 'passport':
                                    esc_html_e('Additional details', 'yatra');
                                    break;
                                case 'dietary_medical':
                                    esc_html_e('Dietary & Medical Requirements', 'yatra');
                                    break;
                                default:
                                    echo esc_html(ucwords(str_replace('_', ' ', $section_key)));
                            }
                            ?>
                        </h4>
                <?php endif; ?>
                
                <div class="yatra-form-row">
                    <?php foreach ($section_fields as $field) : 
                        $field_id = "traveler-{$i}-" . esc_attr($field['id']);
                        $field_name = "travelers[{$i}][" . esc_attr($field['id']) . "]";
                        yatra_render_form_field($field, '', $countries, $field_name, $field_id);
                    endforeach; ?>
                </div>
                
                <?php if ($section_key !== 'main') : ?>
                    </div>
                <?php endif; ?>
            <?php endforeach; ?>
        </div>
        <?php endfor; ?>
    </div>
</div>
<?php endif; ?>

<!-- Payment Method Section -->
<?php 
/**
 * Filter to check if flexible payments module is enabled (Pro feature)
 * When enabled, deposit and partial payment options will be available
 */
$flexible_payments_enabled = apply_filters('yatra_flexible_payments_enabled', false);

/**
 * Filter to get payment method options.
 *
 * Pro module (FlexiblePayments) can add deposit/partial payment options here.
 * We pass `trip_id` so the Pro module can look up per-trip overrides
 * (trip.deposit_amount / trip.deposit_percentage) and expose the deposit
 * option even when the site-wide "deposit_required" flag is off — i.e. a
 * single trip can opt in to a custom deposit without enabling deposits for
 * the whole catalogue.
 */
$payment_method_options = apply_filters('yatra_payment_method_options', [], [
    'trip_id'                    => isset($trip_id) ? (int) $trip_id : 0,
    'deposit_required'           => $deposit_required,
    'deposit_percentage'         => $deposit_percentage,
    'partial_payment'            => $partial_payment,
    'partial_payment_percentage' => $partial_payment_percentage,
]);

$has_flexible_options = $flexible_payments_enabled && !empty($payment_method_options);
?>
<?php if (!$is_remaining_payment && $has_flexible_options) : ?>
<div class="yatra-booking-section">
    <h2 class="yatra-section-title"><?php esc_html_e('Payment Method', 'yatra'); ?></h2>
    
    <div class="yatra-payment-methods">
        <label class="yatra-payment-option">
            <input type="radio" name="payment_method" value="full" checked>
            <span class="yatra-payment-label">
                <strong><?php esc_html_e('Pay in Full', 'yatra'); ?></strong>
                <span><?php esc_html_e('Pay the total amount now', 'yatra'); ?></span>
            </span>
        </label>
        
        <?php foreach ($payment_method_options as $option_key => $option) : ?>
        <label class="yatra-payment-option">
            <input type="radio" name="payment_method" value="<?php echo esc_attr($option['value']); ?>">
            <span class="yatra-payment-label">
                <strong><?php echo esc_html($option['label']); ?></strong>
                <span><?php echo esc_html($option['description']); ?></span>
            </span>
        </label>
        <?php endforeach; ?>
    </div>
</div>
<?php endif; ?>

<!-- Payment Gateway Section -->
<?php
// Always use the live registry list (same as BookingPageHandler). Do not intersect with $enabled_gateways
// from older templates/sessions — that caused only stale gateways (e.g. Pay Later) to show.
$display_gateways = [];
if (class_exists('Yatra\PaymentGateways\PaymentGatewayRegistry')) {
    $registry = \Yatra\PaymentGateways\PaymentGatewayRegistry::getInstance();
    foreach ($registry->getForCheckout() as $gateway) {
        if (!empty($gateway['id'])) {
            $display_gateways[$gateway['id']] = $gateway;
        }
    }
}
?>
<?php if (!empty($display_gateways)) : ?>
<div class="yatra-booking-section">
    <h2 class="yatra-section-title"><?php esc_html_e('Select Payment Gateway', 'yatra'); ?></h2>
    
    <div class="yatra-gateway-options">
        <?php 
        $first = true;
        foreach ($display_gateways as $gateway_id => $gateway) : 
            $icon = !empty($gateway['icon']) ? $gateway['icon'] : plugins_url('assets/images/payment-placeholder.png', dirname(__DIR__));
        ?>
        <div class="yatra-gateway-option-wrapper">
            <label class="yatra-gateway-option">
                <input type="radio" name="payment_gateway" value="<?php echo esc_attr($gateway_id); ?>" <?php checked($first); ?>>
                <span class="yatra-gateway-icon-wrap">
                    <img src="<?php echo esc_url($icon ?? ''); ?>" alt="<?php echo esc_attr($gateway['title'] ?? ''); ?>" class="yatra-gateway-icon">
                </span>
                <span class="yatra-gateway-content">
                    <strong class="yatra-gateway-title"><?php echo esc_html($gateway['title']); ?></strong>
                    <span class="yatra-gateway-desc"><?php echo esc_html($gateway['description']); ?></span>
                    <?php if (empty($gateway['is_configured'])) : ?>
                        <span class="yatra-gateway-desc yatra-gateway-setup-required"><?php esc_html_e('Setup incomplete — finish configuration under Yatra → Settings → Payment, or choose another method.', 'yatra'); ?></span>
                    <?php endif; ?>
                </span>
            </label>
            <div class="yatra-gateway-extra" id="yatra-gateway-extra-<?php echo esc_attr($gateway_id); ?>" data-gateway="<?php echo esc_attr($gateway_id); ?>"></div>
        </div>
        <?php 
        $first = false;
        endforeach; 
        ?>
    </div>
</div>
<?php endif; ?>

<?php if (!$is_remaining_payment) : ?>
    <!-- Special Requests -->
    <div class="yatra-booking-section">
        <h2 class="yatra-section-title"><?php esc_html_e('Special Requests', 'yatra'); ?></h2>
        <p class="yatra-section-description"><?php esc_html_e('Any special requests or notes for your trip (optional)', 'yatra'); ?></p>
        
        <div class="yatra-form-group yatra-field-full">
            <textarea 
                id="special-requests" 
                name="special_requests" 
                rows="4" 
                placeholder="<?php esc_attr_e('E.g., dietary requirements, accessibility needs, celebration requests...', 'yatra'); ?>"
            ></textarea>
        </div>
    </div>
<?php endif; ?>

<!-- Account Creation Section (only for guests) -->
<?php
$allow_guest_checkout = \Yatra\Services\SettingsService::isEnabled('allow_guest_checkout');
$require_login = \Yatra\Services\SettingsService::isEnabled('require_login');
// Account CREATION is gated by the same setting the backend enforces
// (AuthController rejects registration when this is off). When registration is
// disabled we never offer account-creation UI.
$registration_enabled = \Yatra\Services\SettingsService::isEnabled('customer_registration');

// Show the account section only when it has something to render: login is
// required, guest checkout is off (must authenticate), or registration is on
// (optional/required account creation). When registration is OFF and guests are
// allowed, there is nothing to show — proceed as a pure guest.
if (!is_user_logged_in() && !$is_remaining_payment
    && ($require_login || !$allow_guest_checkout || $registration_enabled)) :
?>
<div class="yatra-booking-section yatra-account-section">
    <h2 class="yatra-section-title"><?php esc_html_e('Account', 'yatra'); ?></h2>
    
    <?php if ($require_login || (!$allow_guest_checkout && !$registration_enabled)) : ?>
        <!-- Login Required Message (login required, OR guests off with registration disabled) -->
        <div class="yatra-login-required-notice" style="background: #fef3c7; border: 1px solid #f59e0b; padding: 16px; border-radius: 8px; margin-bottom: 16px;">
            <div style="display: flex; align-items: flex-start; gap: 12px;">
                <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#d97706" stroke-width="2" style="flex-shrink: 0;">
                    <circle cx="12" cy="12" r="10"></circle>
                    <line x1="12" y1="8" x2="12" y2="12"></line>
                    <line x1="12" y1="16" x2="12.01" y2="16"></line>
                </svg>
                <div>
                    <p style="font-weight: 600; color: #92400e; margin: 0 0 8px 0;"><?php esc_html_e('Login Required', 'yatra'); ?></p>
                    <p style="color: #a16207; margin: 0 0 12px 0; font-size: 14px;">
                        <?php echo $registration_enabled
                            ? esc_html__('You must be logged in to complete this booking. Please log in or create an account.', 'yatra')
                            : esc_html__('You must be logged in to complete this booking. Please log in to continue.', 'yatra'); ?>
                    </p>
                    <a href="<?php echo esc_url(wp_login_url(get_permalink())); ?>" class="yatra-login-btn" style="display: inline-flex; align-items: center; gap: 8px; padding: 10px 20px; background: #3b82f6; color: #fff; text-decoration: none; border-radius: 6px; font-weight: 500; font-size: 14px;">
                        <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                            <path d="M15 3h4a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2h-4"></path>
                            <polyline points="10 17 15 12 10 7"></polyline>
                            <line x1="15" y1="12" x2="3" y2="12"></line>
                        </svg>
                        <?php esc_html_e('Log In', 'yatra'); ?>
                    </a>
                    <?php if ($registration_enabled) : ?>
                    <span style="margin: 0 12px; color: #a16207;"><?php esc_html_e('or', 'yatra'); ?></span>
                    <a href="<?php echo esc_url(wp_registration_url()); ?>" style="color: #3b82f6; text-decoration: none; font-weight: 500;">
                        <?php esc_html_e('Create an Account', 'yatra'); ?>
                    </a>
                    <?php endif; ?>
                </div>
            </div>
        </div>
        <input type="hidden" name="login_required" value="1">
        
    <?php elseif (!$allow_guest_checkout && $registration_enabled) : ?>
        <!-- Account Required - Must Create Account (registration enabled) -->
        <p class="yatra-section-description" style="margin-bottom: 16px;">
            <?php esc_html_e('Please create an account to complete your booking. This allows you to manage your bookings and receive important updates.', 'yatra'); ?>
        </p>
        
        <div class="yatra-form-row">
            <div class="yatra-form-group yatra-field-half">
                <label for="account_password">
                    <?php esc_html_e('Password', 'yatra'); ?> <span class="required">*</span>
                </label>
                <input type="password" id="account_password" name="account_password" required minlength="8" 
                       placeholder="<?php esc_attr_e('Minimum 8 characters', 'yatra'); ?>">
            </div>
            <div class="yatra-form-group yatra-field-half">
                <label for="account_password_confirm">
                    <?php esc_html_e('Confirm Password', 'yatra'); ?> <span class="required">*</span>
                </label>
                <input type="password" id="account_password_confirm" name="account_password_confirm" required minlength="8"
                       placeholder="<?php esc_attr_e('Re-enter password', 'yatra'); ?>">
            </div>
        </div>
        <p class="yatra-field-hint" style="font-size: 13px; color: #6b7280; margin-top: 8px;">
            <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="display: inline-block; vertical-align: middle; margin-right: 4px;">
                <circle cx="12" cy="12" r="10"></circle>
                <path d="M12 16v-4"></path>
                <path d="M12 8h.01"></path>
            </svg>
            <?php esc_html_e('Your email address will be used as your username.', 'yatra'); ?>
        </p>
        <input type="hidden" name="create_account" value="1">
        
    <?php elseif ($registration_enabled) : ?>
        <!-- Guest Checkout Allowed - Optional Account Creation (registration enabled) -->
        <p class="yatra-section-description" style="margin-bottom: 16px;">
            <?php esc_html_e('Create an account to easily manage your bookings and receive travel updates.', 'yatra'); ?>
        </p>
        
        <label class="yatra-checkbox-label" style="margin-bottom: 16px;">
            <input type="checkbox" name="create_account" id="create-account" value="1">
            <span><?php esc_html_e('Create an account for easier booking management', 'yatra'); ?></span>
        </label>
        
        <div class="yatra-account-fields" id="yatra-account-fields" style="display: none;">
            <div class="yatra-form-row">
                <div class="yatra-form-group yatra-field-half">
                    <label for="account_password">
                        <?php esc_html_e('Password', 'yatra'); ?> <span class="required">*</span>
                    </label>
                    <input type="password" id="account_password" name="account_password" minlength="8"
                           autocomplete="new-password"
                           placeholder="<?php esc_attr_e('Minimum 8 characters', 'yatra'); ?>">
                </div>
                <div class="yatra-form-group yatra-field-half">
                    <label for="account_password_confirm">
                        <?php esc_html_e('Confirm Password', 'yatra'); ?> <span class="required">*</span>
                    </label>
                    <input type="password" id="account_password_confirm" name="account_password_confirm" minlength="8"
                           autocomplete="new-password"
                           placeholder="<?php esc_attr_e('Re-enter password', 'yatra'); ?>">
                </div>
            </div>
            <p class="yatra-field-hint" style="font-size: 13px; color: #6b7280; margin-top: 8px;">
                <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="display: inline-block; vertical-align: middle; margin-right: 4px;">
                    <circle cx="12" cy="12" r="10"></circle>
                    <path d="M12 16v-4"></path>
                    <path d="M12 8h.01"></path>
                </svg>
                <?php esc_html_e('Your email address will be used as your username.', 'yatra'); ?>
            </p>
        </div>
    <?php endif; ?>
</div>
<?php elseif (is_user_logged_in()) : ?>
    <!-- User is logged in -->
    <div class="yatra-booking-section yatra-logged-in-notice">
        <div style="display: flex; align-items: center; gap: 12px; padding: 12px 16px; background: #ecfdf5; border: 1px solid #10b981; border-radius: 8px;">
            <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#059669" stroke-width="2">
                <path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"></path>
                <polyline points="22 4 12 14.01 9 11.01"></polyline>
            </svg>
            <div>
                <span style="font-weight: 500; color: #065f46;">
                    <?php 
                    $current_user = wp_get_current_user();
                    printf(
                        /* translators: %s: User display name */
                        esc_html__('Logged in as %s', 'yatra'),
                        '<strong>' . esc_html($current_user->display_name) . '</strong>'
                    ); 
                    ?>
                </span>
                <a href="<?php echo esc_url(wp_logout_url(get_permalink())); ?>" style="margin-left: 12px; color: #059669; font-size: 13px;">
                    <?php esc_html_e('Log out', 'yatra'); ?>
                </a>
            </div>
        </div>
    </div>
<?php endif; ?>

<!-- Terms & Conditions -->
<div class="yatra-booking-section">
    <div class="yatra-terms-container">
        <?php
        $termsPageId = 0;
        $privacyPageId = 0;
        if (class_exists('\\Yatra\\Services\\SettingsService')) {
            $termsPageId = \Yatra\Services\SettingsService::getInt('terms_page_id', 0);
            $privacyPageId = \Yatra\Services\SettingsService::getInt('privacy_policy_page_id', 0);
        } else {
            $termsPageId = (int) get_option('yatra_terms_page_id', 0);
            $privacyPageId = (int) get_option('yatra_privacy_policy_page_id', 0);
        }

        $termsUrl = $termsPageId > 0 ? get_permalink($termsPageId) : '';
        $privacyUrl = $privacyPageId > 0 ? get_permalink($privacyPageId) : '';
        if ($privacyUrl === '' && function_exists('get_privacy_policy_url')) {
            $privacyUrl = (string) get_privacy_policy_url();
        }
        ?>

        <label class="yatra-checkbox-label">
            <input type="checkbox" name="accept_terms" id="accept-terms" required>
            <span>
                <?php 
                printf(
                    /* translators: %s: Terms and Conditions link */
                    esc_html__('I have read and agree to the %s', 'yatra'),
                    $termsUrl
                        ? '<a href="' . esc_url($termsUrl) . '" target="_blank" rel="noopener noreferrer">' . esc_html__('Terms and Conditions', 'yatra') . '</a>'
                        : '<span>' . esc_html__('Terms and Conditions', 'yatra') . '</span>'
                ); 
                ?>
                <span class="required">*</span>
            </span>
        </label>
        
        <label class="yatra-checkbox-label">
            <input type="checkbox" name="accept_privacy" id="accept-privacy" required>
            <span>
                <?php 
                printf(
                    /* translators: %s: Privacy Policy link */
                    esc_html__('I agree to the %s and consent to my data being processed', 'yatra'),
                    $privacyUrl
                        ? '<a href="' . esc_url($privacyUrl) . '" target="_blank" rel="noopener noreferrer">' . esc_html__('Privacy Policy', 'yatra') . '</a>'
                        : '<span>' . esc_html__('Privacy Policy', 'yatra') . '</span>'
                ); 
                ?>
                <span class="required">*</span>
            </span>
        </label>
        
        <label class="yatra-checkbox-label">
            <input type="checkbox" name="subscribe_newsletter" id="subscribe-newsletter">
            <span><?php esc_html_e('Subscribe to our newsletter for travel tips and exclusive offers', 'yatra'); ?></span>
        </label>
    </div>
</div>

```
