PluginProbe
Yatra – Travel Booking & Tour Operator Software / 3.0.7
Yatra – Travel Booking & Tour Operator Software v3.0.7
3.0.16 3.0.15 3.0.14 3.0.14.1 3.0.14.2 3.0.12 3.0.13 3.0.11 3.0.10 3.0.9 3.0.8 3.0.7 3.0.6 3.0.5 3.0.5.1 3.0.4 3.0.3 3.0.2.9 3.0.2.7 3.0.2.8 3.0.2.6 trunk 1.0.0 2.0.0 2.0.1 All 84 releases
yatra / templates / partials / booking-form-fields.php

booking-form-fields.php in Yatra – Travel Booking & Tour Operator Software 3.0.7, at templates/partials/booking-form-fields.php

883 lines 39.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Booking Form Fields Partial
4 *
5 * Renders form fields based on configuration from Settings -> Booking Form
6 * Dynamic form customization requires the Dynamic Form Field module (Pro).
7 *
8 * @package Yatra
9 *
10 * Expected variables:
11 * - $trip_id
12 * - $trip_slug (optional)
13 * - $total_travelers
14 * - $deposit_required
15 * - $deposit_percentage
16 * - $partial_payment
17 * - $partial_payment_percentage
18 * - $enabled_gateways
19 *
20 * Form Configuration Structure:
21 * - contact_form: Lead traveler contact information (prefix: contact_)
22 * - emergency_contact_form: Emergency contact details (prefix: emergency_)
23 * - traveler_form: Individual traveler details (array format: travelers[1][field_id])
24 */
25
26 if (!defined('ABSPATH')) {
27 exit;
28 }
29
30 // Check if Dynamic Form Field module is enabled (Pro feature)
31 $is_dynamic_form_enabled = apply_filters('yatra_dynamic_form_field_enabled', false);
32
33 // Get form configuration - returns default config if module disabled, custom config if enabled
34 $form_config = yatra_get_booking_form_config();
35
36
37 // Country list — pulls from the canonical FormatHelper source so every
38 // dropdown (admin, public booking, Pro modules) shows the same full
39 // ISO-3166-1 list. Operators that want a "popular countries" subset
40 // or custom ordering apply the `yatra_countries_list` filter.
41 $countries = \Yatra\Helpers\FormatHelper::getCountries();
42
43 /**
44 * Render a single form field based on configuration
45 *
46 * @param array $field Field configuration
47 * @param string $prefix Field name prefix (e.g., 'contact_', 'emergency_')
48 * @param array $countries Country list for country fields
49 * @param string $custom_name Optional custom field name (for array-style names like travelers[1][field_id])
50 * @param string $custom_id Optional custom field ID
51 */
52 if (!function_exists('yatra_render_form_field')) {
53 function yatra_render_form_field($field, $prefix = '', $countries = [], $custom_name = null, $custom_id = null) {
54 if (empty($field['enabled'])) {
55 return;
56 }
57
58 // Display-only text block: render admin-authored content (safe HTML) at this
59 // position between fields. It is NOT an input — no label, name, value or
60 // required handling — so it submits nothing and is never validated.
61 if (($field['type'] ?? '') === 'text_block') {
62 $tb_width = 'yatra-field-full';
63 if (($field['width'] ?? '') === 'half') {
64 $tb_width = 'yatra-field-half';
65 } elseif (($field['width'] ?? '') === 'third') {
66 $tb_width = 'yatra-field-third';
67 }
68 ?>
69 <div class="yatra-form-group yatra-form-text-block <?php echo esc_attr($tb_width); ?>">
70 <?php echo wpautop(wp_kses_post(yatra_translate_form_string($field['content'] ?? ''))); ?>
71 </div>
72 <?php
73 return;
74 }
75
76 $field_id = $custom_id ? esc_attr($custom_id) : esc_attr($prefix . $field['id']);
77 $field_name = $custom_name ? esc_attr($custom_name) : esc_attr($prefix . $field['id']);
78 $required = !empty($field['required']);
79 $required_attr = $required ? 'required' : '';
80 $required_star = $required ? '<span class="required">*</span>' : '';
81
82 $prefill_value = '';
83 if ($custom_name === null && $prefix === 'contact_') {
84 global $booking;
85 if (isset($booking) && is_object($booking)) {
86 $prop = 'contact_' . ($field['id'] ?? '');
87 if (!empty($prop) && isset($booking->{$prop}) && $booking->{$prop} !== null && $booking->{$prop} !== '') {
88 $prefill_value = (string) $booking->{$prop};
89 }
90 }
91 }
92
93 $width_class = '';
94 if (!empty($field['width'])) {
95 switch ($field['width']) {
96 case 'half':
97 $width_class = 'yatra-field-half';
98 break;
99 case 'third':
100 $width_class = 'yatra-field-third';
101 break;
102 default:
103 $width_class = 'yatra-field-full';
104 }
105 }
106 $field_type = $field['type'] ?? 'text';
107 ?>
108 <div class="yatra-form-group <?php echo esc_attr($width_class); ?> <?php echo $field_type === 'checkbox' ? 'yatra-form-group--checkbox' : ''; ?>">
109 <?php if ($field_type !== 'checkbox') : ?>
110 <label for="<?php echo $field_id; ?>">
111 <?php echo esc_html(yatra_translate_form_string($field['label'])); ?> <?php echo $required_star; ?>
112 </label>
113 <?php endif; ?>
114 <?php
115 switch ($field['type']) {
116 case 'checkbox':
117 $is_checked = ($prefill_value === '1' || $prefill_value === 'on' || $prefill_value === 'true');
118 ?>
119 <label for="<?php echo $field_id; ?>" class="yatra-inline-checkbox-label">
120 <input
121 type="checkbox"
122 id="<?php echo $field_id; ?>"
123 name="<?php echo $field_name; ?>"
124 value="1"
125 <?php checked($is_checked, true, false); ?>
126 <?php echo $required_attr; ?>
127 />
128 <span><?php echo esc_html(yatra_translate_form_string($field['label'])); ?><?php echo $required_star; ?></span>
129 </label>
130 <?php
131 break;
132 case 'select':
133 ?>
134 <select id="<?php echo $field_id; ?>" name="<?php echo $field_name; ?>" <?php echo $required_attr; ?>>
135 <option value=""><?php echo esc_html(yatra_translate_form_string($field['placeholder'] ?? '') ?: __('Select...', 'yatra')); ?></option>
136 <?php if (!empty($field['options'])) : ?>
137 <?php foreach ($field['options'] as $option) : ?>
138 <option value="<?php echo esc_attr($option['value']); ?>" <?php echo selected($prefill_value, (string) ($option['value'] ?? ''), false); ?>>
139 <?php echo esc_html(yatra_translate_form_string($option['label'] ?? '')); ?>
140 </option>
141 <?php endforeach; ?>
142 <?php endif; ?>
143 </select>
144 <?php
145 break;
146
147 case 'country':
148 ?>
149 <select id="<?php echo $field_id; ?>" name="<?php echo $field_name; ?>" <?php echo $required_attr; ?>>
150 <option value=""><?php echo esc_html(yatra_translate_form_string($field['placeholder'] ?? '') ?: __('Select Country', 'yatra')); ?></option>
151 <?php foreach ($countries as $code => $name) : ?>
152 <option value="<?php echo esc_attr($code); ?>" <?php echo selected($prefill_value, (string) $code, false); ?>>
153 <?php echo esc_html($name); ?>
154 </option>
155 <?php endforeach; ?>
156 </select>
157 <?php
158 break;
159
160 case 'textarea':
161 ?>
162 <textarea
163 id="<?php echo $field_id; ?>"
164 name="<?php echo $field_name; ?>"
165 placeholder="<?php echo esc_attr(yatra_translate_form_string($field['placeholder'] ?? '')); ?>"
166 <?php echo $required_attr; ?>
167 rows="3"
168 ><?php echo esc_textarea($prefill_value); ?></textarea>
169 <?php
170 break;
171
172 case 'date':
173 $min_attr = '';
174 ?>
175 <input
176 type="date"
177 id="<?php echo $field_id; ?>"
178 name="<?php echo $field_name; ?>"
179 <?php echo $min_attr; ?>
180 <?php echo $required_attr; ?>
181 value="<?php echo esc_attr($prefill_value); ?>"
182 >
183 <?php
184 break;
185
186 default: // text, email, tel, number
187 ?>
188 <input
189 type="<?php echo esc_attr($field['type']); ?>"
190 id="<?php echo $field_id; ?>"
191 name="<?php echo $field_name; ?>"
192 placeholder="<?php echo esc_attr(yatra_translate_form_string($field['placeholder'] ?? '')); ?>"
193 <?php echo $required_attr; ?>
194 value="<?php echo esc_attr($prefill_value); ?>"
195 >
196 <?php
197 break;
198 }
199 ?>
200 </div>
201 <?php
202 }
203 } // End function_exists check for yatra_render_form_field
204
205 /**
206 * Render a form section with all its fields
207 */
208 if (!function_exists('yatra_render_form_section')) {
209 function yatra_render_form_section($section_config, $prefix = '', $countries = []) {
210 if (isset($section_config['enabled']) && !$section_config['enabled']) {
211 return;
212 }
213
214 $fields = $section_config['fields'] ?? [];
215
216 // Sort fields by order
217 usort($fields, function($a, $b) {
218 return ($a['order'] ?? 0) - ($b['order'] ?? 0);
219 });
220
221 // Group fields by section (for subsections like dietary)
222 $grouped_fields = [];
223 $current_section = null;
224
225 foreach ($fields as $field) {
226 if (!empty($field['enabled'])) {
227 $section = $field['section'] ?? 'main';
228 if (!isset($grouped_fields[$section])) {
229 $grouped_fields[$section] = [];
230 }
231 $grouped_fields[$section][] = $field;
232 }
233 }
234 ?>
235 <div class="yatra-booking-section">
236 <h2 class="yatra-section-title"><?php echo esc_html(yatra_translate_form_string($section_config['title'] ?? '')); ?></h2>
237 <?php if (!empty($section_config['description'])) : ?>
238 <p class="yatra-section-description"><?php echo esc_html(yatra_translate_form_string($section_config['description'])); ?></p>
239 <?php endif; ?>
240
241 <?php foreach ($grouped_fields as $section_key => $section_fields) : ?>
242 <?php if ($section_key !== 'main') : ?>
243 <div class="yatra-traveler-subsection">
244 <h4 class="yatra-subsection-title">
245 <?php
246 switch ($section_key) {
247 case 'passport':
248 esc_html_e('Additional details', 'yatra');
249 break;
250 case 'dietary_medical':
251 esc_html_e('Dietary & Medical Requirements', 'yatra');
252 break;
253 default:
254 echo esc_html(ucwords(str_replace('_', ' ', $section_key)));
255 }
256 ?>
257 </h4>
258 <?php endif; ?>
259
260 <div class="yatra-form-row">
261 <?php foreach ($section_fields as $field) : ?>
262 <?php yatra_render_form_field($field, $prefix, $countries); ?>
263 <?php endforeach; ?>
264 </div>
265
266 <?php if ($section_key !== 'main') : ?>
267 </div>
268 <?php endif; ?>
269 <?php endforeach; ?>
270 </div>
271 <?php
272 }
273 } // End function_exists check for yatra_render_form_section
274 ?>
275
276 <!-- Hidden Fields -->
277 <input type="hidden" name="trip_id" value="<?php echo esc_attr($trip_id); ?>">
278 <?php if (!empty($trip_slug)) : ?>
279 <input type="hidden" name="trip_slug" value="<?php echo esc_attr($trip_slug); ?>">
280 <?php endif; ?>
281
282 <?php if ($is_remaining_payment) : ?>
283 <input type="hidden" name="is_remaining_payment" value="1">
284 <?php if (!empty($existing_booking_id)) : ?>
285 <input type="hidden" name="existing_booking_id" value="<?php echo esc_attr($existing_booking_id); ?>">
286 <?php endif; ?>
287 <?php if (!empty($booking_reference)) : ?>
288 <input type="hidden" name="booking_reference" value="<?php echo esc_attr($booking_reference); ?>">
289 <?php endif; ?>
290 <?php if ($remaining_amount !== null) : ?>
291 <input type="hidden" name="remaining_amount" value="<?php echo esc_attr($remaining_amount); ?>">
292 <?php endif; ?>
293 <?php if (!empty($booking->amount_paid)) : ?>
294 <input type="hidden" name="amount_paid" value="<?php echo esc_attr($booking->amount_paid); ?>">
295 <?php endif; ?>
296 <?php if (!empty($booking->total_amount)) : ?>
297 <input type="hidden" name="total_amount" value="<?php echo esc_attr($booking->total_amount); ?>">
298 <?php endif; ?>
299 <?php endif; ?>
300
301 <?php if (!$is_remaining_payment) : ?>
302 <!-- Contact Form Section -->
303 <?php
304 $contact_config = $form_config['contact_form'] ?? [];
305 if (!empty($contact_config)) {
306 yatra_render_form_section($contact_config, 'contact_', $countries);
307 }
308 ?>
309
310 <!-- Emergency Contact Section -->
311 <?php
312 $emergency_config = $form_config['emergency_contact_form'] ?? [];
313 if (!empty($emergency_config) && (!isset($emergency_config['enabled']) || $emergency_config['enabled'])) {
314 yatra_render_form_section($emergency_config, 'emergency_', $countries);
315 }
316 ?>
317
318 <!-- Traveler Information Section -->
319 <?php
320 $traveler_config = $form_config['traveler_form'] ?? [];
321 ?>
322 <?php endif; ?>
323
324 <?php
325 $traveler_config = $form_config['traveler_form'] ?? [];
326 $traveler_count = isset($total_travelers) ? max(1, (int)$total_travelers) : 1;
327
328 // Check if we have traveler-based pricing info
329 $pricing_type = isset($pricing_type) ? $pricing_type : 'regular';
330 $price_types = isset($price_types) ? $price_types : [];
331 $traveler_counts = isset($traveler_counts) ? $traveler_counts : [];
332
333 /**
334 * Coerce JSON / serialized / object values to a list for foreach-safe iteration.
335 *
336 * @param mixed $value
337 * @return array<int|string, mixed>
338 */
339 $yatra_normalize_booking_arrayish = static function ($value): array {
340 if ($value === null || $value === false || $value === '') {
341 return [];
342 }
343 if (is_array($value)) {
344 return $value;
345 }
346 if ($value instanceof \stdClass) {
347 return (array) $value;
348 }
349 if (is_string($value)) {
350 $trim = trim($value);
351 if ($trim === '') {
352 return [];
353 }
354 $decoded = json_decode($value, true);
355 if (json_last_error() === JSON_ERROR_NONE && is_array($decoded)) {
356 return $decoded;
357 }
358 if (function_exists('maybe_unserialize')) {
359 $un = maybe_unserialize($value);
360 if (is_array($un)) {
361 return $un;
362 }
363 }
364 }
365
366 return [];
367 };
368
369 $price_types = $yatra_normalize_booking_arrayish($price_types);
370 $traveler_counts = $yatra_normalize_booking_arrayish($traveler_counts);
371
372 // Build traveler-to-category mapping for traveler-based pricing
373 $traveler_category_map = [];
374 if ($pricing_type === 'traveler_based' && !empty($price_types) && !empty($traveler_counts)) {
375 $traveler_index = 1;
376 foreach ($price_types as $index => $pt) {
377 $pt = (object) $pt;
378 $category_id = $pt->category_id ?? $index;
379 $category_label = $pt->category_label ?? __('Traveler', 'yatra');
380 $count = isset($traveler_counts[$category_id]) ? (int) $traveler_counts[$category_id] : 0;
381
382 for ($c = 1; $c <= $count; $c++) {
383 $traveler_category_map[$traveler_index] = [
384 'category_id' => $category_id,
385 'category_label' => $category_label,
386 'category_index' => $c,
387 ];
388 $traveler_index++;
389 }
390 }
391 }
392
393 $price_candidates = [
394 isset($trip->price) ? (float) $trip->price : 0,
395 isset($booking->session_price) ? (float) $booking->session_price : 0,
396 isset($trip->sale_price) ? (float) $trip->sale_price : 0,
397 isset($trip->original_price) ? (float) $trip->original_price : 0,
398 ];
399
400 $effective_trip_price = 0;
401 foreach ($price_candidates as $candidate) {
402 if ($candidate > 0) {
403 $effective_trip_price = $candidate;
404 break;
405 }
406 }
407
408 $traveler_price_rows = [];
409 $calculated_total = 0;
410
411 if ($pricing_type === 'traveler_based' && !empty($price_types)) {
412 foreach ($price_types as $index => $pt) {
413 $pt = (object) $pt;
414 $category_id = $pt->category_id ?? $index;
415 $category_label = $pt->category_label ?? __('Traveler', 'yatra');
416 $category_price = isset($pt->effective_price) ? (float) $pt->effective_price : ($pt->sale_price ?? $pt->discounted_price ?? $pt->original_price ?? 0);
417 $count = isset($traveler_counts[$category_id]) ? (int) $traveler_counts[$category_id] : ($index === 0 ? 1 : 0);
418 // Single source of truth for the line amount (per-person × count, flat
419 // per-group, or per-block group pricing). Defaults to the prior flat/
420 // multiply behaviour when group_overflow is absent.
421 $pt_pricing_mode = $pt->pricing_mode ?? 'per_person';
422 $subtotal = \Yatra\Services\TripPricingService::categoryLineSubtotal($pt, $count, $category_price);
423
424 $age_info = '';
425 if (isset($pt->age_min) || isset($pt->age_max)) {
426 if (isset($pt->age_min) && isset($pt->age_max)) {
427 /* translators: 1: minimum age, 2: maximum age. */
428 $age_info = sprintf(__('(Age %1$d-%2$d)', 'yatra'), $pt->age_min, $pt->age_max);
429 } elseif (isset($pt->age_min)) {
430 /* translators: %d: minimum age. */
431 $age_info = sprintf(__('(Age %d+)', 'yatra'), $pt->age_min);
432 } else {
433 /* translators: %d: maximum age. */
434 $age_info = sprintf(__('(Up to age %d)', 'yatra'), $pt->age_max);
435 }
436 }
437
438 $traveler_price_rows[] = [
439 'category_id' => $category_id,
440 'category_label' => $category_label,
441 'category_price' => $category_price,
442 'count' => $count,
443 'subtotal' => $subtotal,
444 'pricing_mode' => $pt_pricing_mode,
445 'age_info' => $age_info,
446 ];
447
448 $calculated_total += $subtotal;
449
450 if ($effective_trip_price <= 0 && $category_price > 0) {
451 $effective_trip_price = $category_price;
452 }
453 }
454 }
455
456 if ($effective_trip_price <= 0) {
457 $effective_trip_price = 0;
458 }
459
460 $initial_total_amount = $calculated_total > 0
461 ? $calculated_total
462 : ($effective_trip_price * max(1, (int) $total_travelers));
463
464 if ($initial_total_amount <= 0 && $effective_trip_price > 0) {
465 $initial_total_amount = $effective_trip_price * max(1, (int) $total_travelers);
466 }
467
468 $initial_due_amount = $initial_total_amount;
469
470 ?>
471
472 <?php
473 // Honour the Traveler section's enable flag (Pro Dynamic Form module), mirroring
474 // the Contact/Emergency sections. The default config has no `enabled` key, so an
475 // un-customised/Free site treats the section as enabled and renders as before.
476 $traveler_section_enabled = !isset($traveler_config['enabled']) || (bool) $traveler_config['enabled'];
477 ?>
478 <?php if (!empty($traveler_config) && $traveler_section_enabled && !$is_remaining_payment) : ?>
479 <div class="yatra-booking-section">
480 <h2 class="yatra-section-title"><?php echo esc_html(yatra_translate_form_string($traveler_config['title'] ?? '') ?: __('Traveler Information', 'yatra')); ?></h2>
481 <?php if (!empty($traveler_config['description'])) : ?>
482 <p class="yatra-section-description"><?php echo esc_html(yatra_translate_form_string($traveler_config['description'])); ?></p>
483 <?php endif; ?>
484
485 <div id="yatra-travelers-container">
486 <?php
487 $traveler_fields = $traveler_config['fields'] ?? [];
488 usort($traveler_fields, function($a, $b) {
489 return ($a['order'] ?? 0) - ($b['order'] ?? 0);
490 });
491
492 // Group fields by section
493 $grouped_traveler_fields = [];
494 foreach ($traveler_fields as $field) {
495 if (!empty($field['enabled'])) {
496 $section = $field['section'] ?? 'main';
497 if (!isset($grouped_traveler_fields[$section])) {
498 $grouped_traveler_fields[$section] = [];
499 }
500 $grouped_traveler_fields[$section][] = $field;
501 }
502 }
503
504 for ($i = 1; $i <= $traveler_count; $i++) :
505 // Determine traveler label based on category if traveler-based pricing
506 if (!empty($traveler_category_map[$i])) {
507 $category_info = $traveler_category_map[$i];
508 $category_label = $category_info['category_label'];
509 $category_index = $category_info['category_index'];
510 /* translators: 1: traveler category label (e.g. Adult, Child), 2: sequential index within that category. */
511 $traveler_label = sprintf(__('%1$s %2$d', 'yatra'), $category_label, $category_index);
512 if ($i === 1) {
513 $traveler_label .= ' (' . __('Lead Traveler', 'yatra') . ')';
514 }
515 } else {
516 /* translators: %d: traveler sequence number. */
517 $traveler_label = ($i === 1) ? __('Traveler 1 (Lead Traveler)', 'yatra') : sprintf(__('Traveler %d', 'yatra'), $i);
518 }
519 ?>
520 <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; ?>>
521 <div class="yatra-traveler-header">
522 <h3 class="yatra-traveler-title"><?php echo esc_html($traveler_label); ?></h3>
523 <?php if ($i > 1) : ?>
524 <span class="yatra-traveler-note"><?php esc_html_e('Additional traveler', 'yatra'); ?></span>
525 <?php endif; ?>
526 </div>
527
528 <?php foreach ($grouped_traveler_fields as $section_key => $section_fields) : ?>
529 <?php if ($section_key !== 'main') : ?>
530 <div class="yatra-traveler-subsection">
531 <h4 class="yatra-subsection-title">
532 <?php
533 switch ($section_key) {
534 case 'passport':
535 esc_html_e('Additional details', 'yatra');
536 break;
537 case 'dietary_medical':
538 esc_html_e('Dietary & Medical Requirements', 'yatra');
539 break;
540 default:
541 echo esc_html(ucwords(str_replace('_', ' ', $section_key)));
542 }
543 ?>
544 </h4>
545 <?php endif; ?>
546
547 <div class="yatra-form-row">
548 <?php foreach ($section_fields as $field) :
549 $field_id = "traveler-{$i}-" . esc_attr($field['id']);
550 $field_name = "travelers[{$i}][" . esc_attr($field['id']) . "]";
551 yatra_render_form_field($field, '', $countries, $field_name, $field_id);
552 endforeach; ?>
553 </div>
554
555 <?php if ($section_key !== 'main') : ?>
556 </div>
557 <?php endif; ?>
558 <?php endforeach; ?>
559 </div>
560 <?php endfor; ?>
561 </div>
562 </div>
563 <?php endif; ?>
564
565 <!-- Payment Method Section -->
566 <?php
567 /**
568 * Filter to check if flexible payments module is enabled (Pro feature)
569 * When enabled, deposit and partial payment options will be available
570 */
571 $flexible_payments_enabled = apply_filters('yatra_flexible_payments_enabled', false);
572
573 /**
574 * Filter to get payment method options.
575 *
576 * Pro module (FlexiblePayments) can add deposit/partial payment options here.
577 * We pass `trip_id` so the Pro module can look up per-trip overrides
578 * (trip.deposit_amount / trip.deposit_percentage) and expose the deposit
579 * option even when the site-wide "deposit_required" flag is off — i.e. a
580 * single trip can opt in to a custom deposit without enabling deposits for
581 * the whole catalogue.
582 */
583 $payment_method_options = apply_filters('yatra_payment_method_options', [], [
584 'trip_id' => isset($trip_id) ? (int) $trip_id : 0,
585 'deposit_required' => $deposit_required,
586 'deposit_percentage' => $deposit_percentage,
587 'partial_payment' => $partial_payment,
588 'partial_payment_percentage' => $partial_payment_percentage,
589 ]);
590
591 $has_flexible_options = $flexible_payments_enabled && !empty($payment_method_options);
592 ?>
593 <?php if (!$is_remaining_payment && $has_flexible_options) : ?>
594 <div class="yatra-booking-section">
595 <h2 class="yatra-section-title"><?php esc_html_e('Payment Method', 'yatra'); ?></h2>
596
597 <div class="yatra-payment-methods">
598 <label class="yatra-payment-option">
599 <input type="radio" name="payment_method" value="full" checked>
600 <span class="yatra-payment-label">
601 <strong><?php esc_html_e('Pay in Full', 'yatra'); ?></strong>
602 <span><?php esc_html_e('Pay the total amount now', 'yatra'); ?></span>
603 </span>
604 </label>
605
606 <?php foreach ($payment_method_options as $option_key => $option) : ?>
607 <label class="yatra-payment-option">
608 <input type="radio" name="payment_method" value="<?php echo esc_attr($option['value']); ?>">
609 <span class="yatra-payment-label">
610 <strong><?php echo esc_html($option['label']); ?></strong>
611 <span><?php echo esc_html($option['description']); ?></span>
612 </span>
613 </label>
614 <?php endforeach; ?>
615 </div>
616 </div>
617 <?php endif; ?>
618
619 <!-- Payment Gateway Section -->
620 <?php
621 // Always use the live registry list (same as BookingPageHandler). Do not intersect with $enabled_gateways
622 // from older templates/sessions — that caused only stale gateways (e.g. Pay Later) to show.
623 $display_gateways = [];
624 if (class_exists('Yatra\PaymentGateways\PaymentGatewayRegistry')) {
625 $registry = \Yatra\PaymentGateways\PaymentGatewayRegistry::getInstance();
626 foreach ($registry->getForCheckout() as $gateway) {
627 if (!empty($gateway['id'])) {
628 $display_gateways[$gateway['id']] = $gateway;
629 }
630 }
631 }
632 ?>
633 <?php if (!empty($display_gateways)) : ?>
634 <div class="yatra-booking-section">
635 <h2 class="yatra-section-title"><?php esc_html_e('Select Payment Gateway', 'yatra'); ?></h2>
636
637 <div class="yatra-gateway-options">
638 <?php
639 $first = true;
640 foreach ($display_gateways as $gateway_id => $gateway) :
641 $icon = !empty($gateway['icon']) ? $gateway['icon'] : plugins_url('assets/images/payment-placeholder.png', dirname(__DIR__));
642 ?>
643 <div class="yatra-gateway-option-wrapper">
644 <label class="yatra-gateway-option">
645 <input type="radio" name="payment_gateway" value="<?php echo esc_attr($gateway_id); ?>" <?php checked($first); ?>>
646 <span class="yatra-gateway-icon-wrap">
647 <img src="<?php echo esc_url($icon ?? ''); ?>" alt="<?php echo esc_attr($gateway['title'] ?? ''); ?>" class="yatra-gateway-icon">
648 </span>
649 <span class="yatra-gateway-content">
650 <strong class="yatra-gateway-title"><?php echo esc_html($gateway['title']); ?></strong>
651 <span class="yatra-gateway-desc"><?php echo esc_html($gateway['description']); ?></span>
652 <?php if (empty($gateway['is_configured'])) : ?>
653 <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>
654 <?php endif; ?>
655 </span>
656 </label>
657 <div class="yatra-gateway-extra" id="yatra-gateway-extra-<?php echo esc_attr($gateway_id); ?>" data-gateway="<?php echo esc_attr($gateway_id); ?>"></div>
658 </div>
659 <?php
660 $first = false;
661 endforeach;
662 ?>
663 </div>
664 </div>
665 <?php endif; ?>
666
667 <?php if (!$is_remaining_payment) : ?>
668 <!-- Special Requests -->
669 <div class="yatra-booking-section">
670 <h2 class="yatra-section-title"><?php esc_html_e('Special Requests', 'yatra'); ?></h2>
671 <p class="yatra-section-description"><?php esc_html_e('Any special requests or notes for your trip (optional)', 'yatra'); ?></p>
672
673 <div class="yatra-form-group yatra-field-full">
674 <textarea
675 id="special-requests"
676 name="special_requests"
677 rows="4"
678 placeholder="<?php esc_attr_e('E.g., dietary requirements, accessibility needs, celebration requests...', 'yatra'); ?>"
679 ></textarea>
680 </div>
681 </div>
682 <?php endif; ?>
683
684 <!-- Account Creation Section (only for guests) -->
685 <?php
686 $allow_guest_checkout = \Yatra\Services\SettingsService::isEnabled('allow_guest_checkout');
687 $require_login = \Yatra\Services\SettingsService::isEnabled('require_login');
688
689 // Show account section if user is not logged in
690 if (!is_user_logged_in() && !$is_remaining_payment) :
691 ?>
692 <div class="yatra-booking-section yatra-account-section">
693 <h2 class="yatra-section-title"><?php esc_html_e('Account', 'yatra'); ?></h2>
694
695 <?php if ($require_login) : ?>
696 <!-- Login Required Message -->
697 <div class="yatra-login-required-notice" style="background: #fef3c7; border: 1px solid #f59e0b; padding: 16px; border-radius: 8px; margin-bottom: 16px;">
698 <div style="display: flex; align-items: flex-start; gap: 12px;">
699 <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#d97706" stroke-width="2" style="flex-shrink: 0;">
700 <circle cx="12" cy="12" r="10"></circle>
701 <line x1="12" y1="8" x2="12" y2="12"></line>
702 <line x1="12" y1="16" x2="12.01" y2="16"></line>
703 </svg>
704 <div>
705 <p style="font-weight: 600; color: #92400e; margin: 0 0 8px 0;"><?php esc_html_e('Login Required', 'yatra'); ?></p>
706 <p style="color: #a16207; margin: 0 0 12px 0; font-size: 14px;">
707 <?php esc_html_e('You must be logged in to complete this booking. Please log in or create an account.', 'yatra'); ?>
708 </p>
709 <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;">
710 <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
711 <path d="M15 3h4a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2h-4"></path>
712 <polyline points="10 17 15 12 10 7"></polyline>
713 <line x1="15" y1="12" x2="3" y2="12"></line>
714 </svg>
715 <?php esc_html_e('Log In', 'yatra'); ?>
716 </a>
717 <span style="margin: 0 12px; color: #a16207;"><?php esc_html_e('or', 'yatra'); ?></span>
718 <a href="<?php echo esc_url(wp_registration_url()); ?>" style="color: #3b82f6; text-decoration: none; font-weight: 500;">
719 <?php esc_html_e('Create an Account', 'yatra'); ?>
720 </a>
721 </div>
722 </div>
723 </div>
724 <input type="hidden" name="login_required" value="1">
725
726 <?php elseif (!$allow_guest_checkout) : ?>
727 <!-- Account Required - Must Create Account -->
728 <p class="yatra-section-description" style="margin-bottom: 16px;">
729 <?php esc_html_e('Please create an account to complete your booking. This allows you to manage your bookings and receive important updates.', 'yatra'); ?>
730 </p>
731
732 <div class="yatra-form-row">
733 <div class="yatra-form-group yatra-field-half">
734 <label for="account_password">
735 <?php esc_html_e('Password', 'yatra'); ?> <span class="required">*</span>
736 </label>
737 <input type="password" id="account_password" name="account_password" required minlength="8"
738 placeholder="<?php esc_attr_e('Minimum 8 characters', 'yatra'); ?>">
739 </div>
740 <div class="yatra-form-group yatra-field-half">
741 <label for="account_password_confirm">
742 <?php esc_html_e('Confirm Password', 'yatra'); ?> <span class="required">*</span>
743 </label>
744 <input type="password" id="account_password_confirm" name="account_password_confirm" required minlength="8"
745 placeholder="<?php esc_attr_e('Re-enter password', 'yatra'); ?>">
746 </div>
747 </div>
748 <p class="yatra-field-hint" style="font-size: 13px; color: #6b7280; margin-top: 8px;">
749 <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;">
750 <circle cx="12" cy="12" r="10"></circle>
751 <path d="M12 16v-4"></path>
752 <path d="M12 8h.01"></path>
753 </svg>
754 <?php esc_html_e('Your email address will be used as your username.', 'yatra'); ?>
755 </p>
756 <input type="hidden" name="create_account" value="1">
757
758 <?php else : ?>
759 <!-- Guest Checkout Allowed - Optional Account Creation -->
760 <p class="yatra-section-description" style="margin-bottom: 16px;">
761 <?php esc_html_e('Create an account to easily manage your bookings and receive travel updates.', 'yatra'); ?>
762 </p>
763
764 <label class="yatra-checkbox-label" style="margin-bottom: 16px;">
765 <input type="checkbox" name="create_account" id="create-account" value="1">
766 <span><?php esc_html_e('Create an account for easier booking management', 'yatra'); ?></span>
767 </label>
768
769 <div class="yatra-account-fields" id="yatra-account-fields" style="display: none;">
770 <div class="yatra-form-row">
771 <div class="yatra-form-group yatra-field-half">
772 <label for="account_password">
773 <?php esc_html_e('Password', 'yatra'); ?> <span class="required">*</span>
774 </label>
775 <input type="password" id="account_password" name="account_password" minlength="8"
776 placeholder="<?php esc_attr_e('Minimum 8 characters', 'yatra'); ?>">
777 </div>
778 <div class="yatra-form-group yatra-field-half">
779 <label for="account_password_confirm">
780 <?php esc_html_e('Confirm Password', 'yatra'); ?> <span class="required">*</span>
781 </label>
782 <input type="password" id="account_password_confirm" name="account_password_confirm" minlength="8"
783 placeholder="<?php esc_attr_e('Re-enter password', 'yatra'); ?>">
784 </div>
785 </div>
786 <p class="yatra-field-hint" style="font-size: 13px; color: #6b7280; margin-top: 8px;">
787 <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;">
788 <circle cx="12" cy="12" r="10"></circle>
789 <path d="M12 16v-4"></path>
790 <path d="M12 8h.01"></path>
791 </svg>
792 <?php esc_html_e('Your email address will be used as your username.', 'yatra'); ?>
793 </p>
794 </div>
795 <?php endif; ?>
796 </div>
797 <?php elseif (!is_user_logged_in()) : ?>
798 <!-- User is logged in -->
799 <div class="yatra-booking-section yatra-logged-in-notice">
800 <div style="display: flex; align-items: center; gap: 12px; padding: 12px 16px; background: #ecfdf5; border: 1px solid #10b981; border-radius: 8px;">
801 <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#059669" stroke-width="2">
802 <path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"></path>
803 <polyline points="22 4 12 14.01 9 11.01"></polyline>
804 </svg>
805 <div>
806 <span style="font-weight: 500; color: #065f46;">
807 <?php
808 $current_user = wp_get_current_user();
809 printf(
810 /* translators: %s: User display name */
811 esc_html__('Logged in as %s', 'yatra'),
812 '<strong>' . esc_html($current_user->display_name) . '</strong>'
813 );
814 ?>
815 </span>
816 <a href="<?php echo esc_url(wp_logout_url(get_permalink())); ?>" style="margin-left: 12px; color: #059669; font-size: 13px;">
817 <?php esc_html_e('Log out', 'yatra'); ?>
818 </a>
819 </div>
820 </div>
821 </div>
822 <?php endif; ?>
823
824 <!-- Terms & Conditions -->
825 <div class="yatra-booking-section">
826 <div class="yatra-terms-container">
827 <?php
828 $termsPageId = 0;
829 $privacyPageId = 0;
830 if (class_exists('\\Yatra\\Services\\SettingsService')) {
831 $termsPageId = \Yatra\Services\SettingsService::getInt('terms_page_id', 0);
832 $privacyPageId = \Yatra\Services\SettingsService::getInt('privacy_policy_page_id', 0);
833 } else {
834 $termsPageId = (int) get_option('yatra_terms_page_id', 0);
835 $privacyPageId = (int) get_option('yatra_privacy_policy_page_id', 0);
836 }
837
838 $termsUrl = $termsPageId > 0 ? get_permalink($termsPageId) : '';
839 $privacyUrl = $privacyPageId > 0 ? get_permalink($privacyPageId) : '';
840 if ($privacyUrl === '' && function_exists('get_privacy_policy_url')) {
841 $privacyUrl = (string) get_privacy_policy_url();
842 }
843 ?>
844
845 <label class="yatra-checkbox-label">
846 <input type="checkbox" name="accept_terms" id="accept-terms" required>
847 <span>
848 <?php
849 printf(
850 /* translators: %s: Terms and Conditions link */
851 esc_html__('I have read and agree to the %s', 'yatra'),
852 $termsUrl
853 ? '<a href="' . esc_url($termsUrl) . '" target="_blank" rel="noopener noreferrer">' . esc_html__('Terms and Conditions', 'yatra') . '</a>'
854 : '<span>' . esc_html__('Terms and Conditions', 'yatra') . '</span>'
855 );
856 ?>
857 <span class="required">*</span>
858 </span>
859 </label>
860
861 <label class="yatra-checkbox-label">
862 <input type="checkbox" name="accept_privacy" id="accept-privacy" required>
863 <span>
864 <?php
865 printf(
866 /* translators: %s: Privacy Policy link */
867 esc_html__('I agree to the %s and consent to my data being processed', 'yatra'),
868 $privacyUrl
869 ? '<a href="' . esc_url($privacyUrl) . '" target="_blank" rel="noopener noreferrer">' . esc_html__('Privacy Policy', 'yatra') . '</a>'
870 : '<span>' . esc_html__('Privacy Policy', 'yatra') . '</span>'
871 );
872 ?>
873 <span class="required">*</span>
874 </span>
875 </label>
876
877 <label class="yatra-checkbox-label">
878 <input type="checkbox" name="subscribe_newsletter" id="subscribe-newsletter">
879 <span><?php esc_html_e('Subscribe to our newsletter for travel tips and exclusive offers', 'yatra'); ?></span>
880 </label>
881 </div>
882 </div>
883