PluginProbe
Yatra – Travel Booking & Tour Operator Software / 3.0.16
Yatra – Travel Booking & Tour Operator Software v3.0.16
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
← All changes | templates/booking-confirmation.php +388 -34 3.0.2.9 → 3.0.16 View file →
@@ -30,8 +30,52 @@
30 30 // Format dates
31 31 $travel_date_formatted = \Yatra\Helpers\FormatHelper::formatDate((string) ($booking->travel_date ?? ''));
32 32 $booking_date_formatted = \Yatra\Helpers\FormatHelper::formatDateTime((string) ($booking->created_at ?? ''));
33 33
34 +// "Balance just paid" context detection.
35 +// A `?balance=paid` query arg is appended by the remaining-payment flows
36 +// (see PaymentGatewayController::create_remaining_balance_intent and
37 +// BookingSessionController::process_remaining_payment). We only render the
38 +// "balance paid" copy when BOTH the flag is present AND the booking actually
39 +// reports a paid status — that way a stale bookmarked URL or a tampered
40 +// query string can't show misleading content if a balance is still due.
41 +$is_balance_paid_request = isset($_GET['balance']) && sanitize_key((string) $_GET['balance']) === 'paid';
42 +$booking_payment_status = (string) ($booking->payment_status ?? '');
43 +$booking_amount_due = (float) ($booking->amount_due ?? 0);
44 +$booking_is_fully_paid = $booking_payment_status === 'paid' || $booking_amount_due <= 0.01;
45 +$show_balance_paid_banner = $is_balance_paid_request && $booking_is_fully_paid;
46 +
47 +// A gateway may redirect back here with `?payment=cancelled` (user aborted the
48 +// payment at the provider) or `?payment=failed`. Show an informative notice only
49 +// when the booking is not already fully paid, so a genuinely-paid booking that
50 +// happens to carry a stale query arg never shows a misleading "cancelled" message.
51 +$payment_result_flag = isset($_GET['payment']) ? sanitize_key((string) $_GET['payment']) : '';
52 +$show_payment_cancelled_notice = in_array($payment_result_flag, ['cancelled', 'failed'], true) && !$booking_is_fully_paid;
53 +
54 +// Resolve the most recent gateway used on this booking.
55 +// `$booking->payment_gateway` is the *initial* method chosen at booking time
56 +// (e.g. Pay Later), which is misleading when the customer later settled the
57 +// balance via a different gateway (e.g. Stripe). Show the latest payment's
58 +// gateway in the Payment Method line; fall back to the booking's stored
59 +// gateway when no payment row exists yet.
60 +$display_payment_gateway = (string) ($booking->payment_gateway ?? '');
61 +if (!empty($booking->id)) {
62 + try {
63 + $latest_payment = (new \Yatra\Repositories\PaymentRepository())->findLatestByBookingId((int) $booking->id);
64 + if ($latest_payment) {
65 + $latest_gateway = (string) ($latest_payment->payment_gateway ?? $latest_payment->gateway ?? '');
66 + if ($latest_gateway !== '') {
67 + $display_payment_gateway = $latest_gateway;
68 + }
69 + }
70 + } catch (\Throwable $e) {
71 + // Repository unavailable for some reason — keep the booking's stored gateway.
72 + }
73 +}
74 +
75 +// Resolved customer account URL (configurable via Settings → Permalink → Account base).
76 +$yatra_account_url = home_url('/' . \Yatra\Services\SettingsService::getAccountBase());
77 +
34 78 // Get status color
35 79 $status_colors = [
36 80 'pending' => ['bg' => '#fef3c7', 'text' => '#d97706', 'label' => 'Pending'],
37 81 'confirmed' => ['bg' => '#d1fae5', 'text' => '#059669', 'label' => 'Confirmed'],
@@ -43,8 +87,21 @@
43 87 ];
44 88
45 89 $status_style = $status_colors[$booking->status] ?? $status_colors['pending'];
46 90
91 +// Ensure Font Awesome CSS is available for icon-picker rendered icons (fa-solid/fa-regular).
92 +// This template can be displayed outside normal "trip page" contexts, so we explicitly
93 +// register/enqueue the shared stylesheet dependencies before printing the header.
94 +if (class_exists(\Yatra\Providers\FrontendAssetsProvider::class)) {
95 + \Yatra\Providers\FrontendAssetsProvider::registerCoreFrontendStylesheets();
96 + if (wp_style_is('yatra-fontawesome-6', 'registered')) {
97 + wp_enqueue_style('yatra-fontawesome-6');
98 + }
99 + if (wp_style_is('yatra-common', 'registered')) {
100 + wp_enqueue_style('yatra-common');
101 + }
102 +}
103 +
47 104 yatra_get_header();
48 105
49 106 // Hook for Pro modules to add custom scripts (Facebook Pixel, etc.)
50 107 do_action('yatra_booking_confirmation_header', $booking);
@@ -61,10 +118,12 @@
61 118 <polyline points="9,12 12,15 16,10"></polyline>
62 119 </svg>
63 120 </div>
64 121 <h1 class="yatra-confirmation-title">
65 - <?php
66 - if ($booking->status === 'confirmed') {
122 + <?php
123 + if ($show_balance_paid_banner) {
124 + esc_html_e('Balance Paid!', 'yatra');
125 + } elseif ($booking->status === 'confirmed') {
67 126 esc_html_e('Booking Confirmed!', 'yatra');
68 127 } elseif ($booking->status === 'pending') {
69 128 esc_html_e('Booking Received!', 'yatra');
70 129 } elseif ($booking->status === 'cancelled') {
@@ -74,10 +133,12 @@
74 133 }
75 134 ?>
76 135 </h1>
77 136 <p class="yatra-confirmation-subtitle">
78 - <?php
79 - if ($booking->status === 'confirmed') {
137 + <?php
138 + if ($show_balance_paid_banner) {
139 + esc_html_e('Your remaining balance has been received. This booking is now fully paid.', 'yatra');
140 + } elseif ($booking->status === 'confirmed') {
80 141 esc_html_e('Thank you for your booking. We look forward to hosting you!', 'yatra');
81 142 } elseif ($booking->status === 'pending') {
82 143 esc_html_e('Thank you for your booking. Your booking is pending confirmation.', 'yatra');
83 144 } elseif ($booking->status === 'cancelled') {
@@ -92,10 +153,58 @@
92 153 <span class="yatra-ref-code"><?php echo esc_html($booking->reference); ?></span>
93 154 </div>
94 155 </div>
95 156
157 + <?php if ($show_balance_paid_banner) : ?>
158 + <div class="yatra-balance-paid-banner" role="status" aria-live="polite" style="margin: 16px 0; padding: 14px 18px; background: #d1fae5; border: 1px solid #6ee7b7; border-left: 4px solid #059669; border-radius: 8px; color: #065f46; display: flex; gap: 12px; align-items: flex-start;">
159 + <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="flex: 0 0 22px; width: 22px; height: 22px; margin-top: 1px;">
160 + <path stroke-linecap="round" stroke-linejoin="round" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"/>
161 + </svg>
162 + <div style="flex: 1; line-height: 1.45;">
163 + <strong style="display: block; margin-bottom: 2px;">
164 + <?php esc_html_e('Balance payment received.', 'yatra'); ?>
165 + </strong>
166 + <span>
167 + <?php
168 + printf(
169 + wp_kses(
170 + /* translators: 1: booking reference number (HTML <strong> wrapped), 2: link labelled "payment history" pointing to the customer account page (HTML <a> wrapped). */
171 + __('You\'ve completed payment for booking %1$s. A receipt has been emailed to you. View or download your %2$s anytime from your account.', 'yatra'),
172 + ['a' => ['href' => true], 'strong' => []]
173 + ),
174 + '<strong>' . esc_html((string) ($booking->reference ?? '')) . '</strong>',
175 + '<a href="' . esc_url(add_query_arg('tab', 'payments', $yatra_account_url)) . '">' . esc_html__('payment history', 'yatra') . '</a>'
176 + );
177 + ?>
178 + </span>
179 + </div>
180 + </div>
181 + <?php endif; ?>
182 +
183 + <?php if ($show_payment_cancelled_notice) : ?>
184 + <div class="yatra-payment-cancelled-banner" role="status" aria-live="polite" style="margin: 16px 0; padding: 14px 18px; background: #fef3c7; border: 1px solid #fcd34d; border-left: 4px solid #d97706; border-radius: 8px; color: #92400e; display: flex; gap: 12px; align-items: flex-start;">
185 + <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="flex: 0 0 22px; width: 22px; height: 22px; margin-top: 1px;">
186 + <circle cx="12" cy="12" r="10"></circle>
187 + <line x1="12" y1="8" x2="12" y2="12"></line>
188 + <line x1="12" y1="16" x2="12.01" y2="16"></line>
189 + </svg>
190 + <div style="flex: 1; line-height: 1.45;">
191 + <strong style="display: block; margin-bottom: 2px;">
192 + <?php esc_html_e('Payment not completed.', 'yatra'); ?>
193 + </strong>
194 + <span>
195 + <?php esc_html_e('Your payment was cancelled and no charge was made. Your booking has been saved — you can complete payment anytime from your account.', 'yatra'); ?>
196 + </span>
197 + </div>
198 + </div>
199 + <?php endif; ?>
200 +
96 201 <?php
97 - // Resolve featured image URL (handle attachment ID or direct URL)
202 + // Resolve featured image URL (handle attachment ID or direct URL).
203 + // Falls back to the bundled trip-placeholder.svg so the confirmation
204 + // card always renders with a visual on the left — never an empty
205 + // text-only block, regardless of whether the operator set a featured
206 + // image for the trip.
98 207 $featured_image_url = '';
99 208 if (!empty($booking->featured_image)) {
100 209 if (is_numeric($booking->featured_image)) {
101 210 $featured_image_url = wp_get_attachment_url((int) $booking->featured_image) ?: '';
@@ -102,8 +211,14 @@
102 211 } else {
103 212 $featured_image_url = $booking->featured_image;
104 213 }
105 214 }
215 + if ($featured_image_url === '') {
216 + $featured_image_url = YATRA_PLUGIN_URL . 'assets/images/trip-placeholder.svg';
217 + $featured_image_is_placeholder = true;
218 + } else {
219 + $featured_image_is_placeholder = false;
220 + }
106 221 ?>
107 222
108 223 <div class="yatra-confirmation-content">
109 224 <!-- Trip Summary Card -->
@@ -108,23 +223,66 @@
108 223 <div class="yatra-confirmation-content">
109 224 <!-- Trip Summary Card -->
110 225 <div class="yatra-confirmation-card yatra-trip-summary-card">
111 226 <div class="yatra-trip-summary">
112 - <?php if (!empty($featured_image_url)) : ?>
113 - <div class="yatra-trip-image">
114 - <img src="<?php echo esc_url($featured_image_url); ?>" alt="<?php echo esc_attr($booking->trip_title); ?>">
227 + <div class="yatra-trip-image<?php echo $featured_image_is_placeholder ? ' yatra-trip-image--placeholder' : ''; ?>">
228 + <img src="<?php echo esc_url($featured_image_url); ?>"
229 + alt="<?php echo esc_attr($booking->trip_title); ?>"
230 + loading="lazy"
231 + onerror="this.onerror=null;this.src='<?php echo esc_url(YATRA_PLUGIN_URL . 'assets/images/trip-placeholder.svg'); ?>';this.parentNode.classList.add('yatra-trip-image--placeholder');">
115 232 </div>
116 - <?php endif; ?>
117 233
118 234 <div class="yatra-trip-info">
119 235 <h2 class="yatra-trip-name"><?php echo esc_html($booking->trip_title); ?></h2>
120 236
121 237 <?php if (!empty($booking->trip_average_rating) && $booking->trip_review_count >= 0) : ?>
238 + <?php
239 + // The confirmation page isn't a "booking page" (it has its
240 + // own route), so booking.css — which colours the rating
241 + // stars amber — is never enqueued here and the filled stars
242 + // fell back to the grey base colour. The rest of this page
243 + // is inline-styled, so rather than loading the whole
244 + // stylesheet (and risk restyling it) the rating rules are
245 + // scoped inline here, mirroring assets/css/booking.css.
246 + ?>
247 + <?php
248 + // IMPORTANT: these rules are deliberately scoped to
249 + // `.yatra-trip-rating .yatra-rating-stars .yatra-star*`
250 + // (specificity 0,3,0 / 0,4,1) and set `fill` directly on
251 + // the SVG — NOT just `color` + `fill="currentColor"`.
252 + // trip.css / booking.css define `.yatra-rating-stars
253 + // .yatra-star { color:#d1d5db }` (0,2,0) and colour the
254 + // "filled" state with a `.filled` (dot) class, not the
255 + // `-filled` (hyphen) class this markup uses. If either
256 + // stylesheet is present on the confirmation page, its
257 + // grey base out-specifies a plain `.yatra-star-filled`
258 + // and its amber rule never matches — so every star showed
259 + // grey even at 4.8. Winning on specificity + an explicit
260 + // fill makes the amber stick regardless of what's loaded.
261 + ?>
262 + <style>
263 + .yatra-trip-rating{display:flex;align-items:center;gap:12px;margin-bottom:12px}
264 + .yatra-trip-rating .yatra-rating-stars{display:inline-flex;gap:4px}
265 + .yatra-trip-rating .yatra-rating-stars .yatra-star{width:20px;height:20px;color:#e2e8f0;font-size:0;line-height:0}
266 + .yatra-trip-rating .yatra-rating-stars .yatra-star svg{width:100%;height:100%;display:block;fill:currentColor}
267 + .yatra-trip-rating .yatra-rating-stars .yatra-star-filled{color:#f59e0b}
268 + .yatra-trip-rating .yatra-rating-stars .yatra-star-filled svg{fill:#f59e0b}
269 + .yatra-trip-rating .yatra-rating-stars .yatra-star-half{position:relative;color:#e2e8f0}
270 + .yatra-trip-rating .yatra-rating-stars .yatra-star-half svg{fill:#e2e8f0}
271 + .yatra-trip-rating .yatra-rating-stars .yatra-star-half::after{content:"";position:absolute;top:0;left:0;bottom:0;width:50%;overflow:hidden;background:#f59e0b;-webkit-mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpolygon points='12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26'/%3E%3C/svg%3E") no-repeat left center / 200% 100%;mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpolygon points='12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26'/%3E%3C/svg%3E") no-repeat left center / 200% 100%}
272 + .yatra-trip-rating .yatra-rating-meta strong{color:#0f172a;margin-right:6px}
273 + .yatra-trip-rating .yatra-rating-meta span{color:#64748b;font-size:14px}
274 + </style>
122 275 <div class="yatra-trip-rating">
123 276 <div class="yatra-rating-stars">
124 277 <?php
125 - $filled_stars = floor($booking->trip_average_rating);
126 - $has_half = ($booking->trip_average_rating - $filled_stars) >= 0.5;
278 + // Shared with the reviews block and listing cards so every
279 + // surface draws the same rating. Rounds to the nearest half:
280 + // flooring drew 4.9 as four-and-a-half stars, which looked
281 + // wrong beside the printed "4.9".
282 + $star_parts = yatra_rating_star_parts($booking->trip_average_rating);
283 + $filled_stars = $star_parts['full'];
284 + $has_half = $star_parts['half'];
127 285 for ($i = 1; $i <= 5; $i++) :
128 286 $class = $i <= $filled_stars ? 'yatra-star-filled' : ($has_half && $i === $filled_stars + 1 ? 'yatra-star-half' : '');
129 287 ?>
130 288 <span class="yatra-star <?php echo esc_attr($class); ?>">
@@ -135,10 +293,15 @@
135 293 <div class="yatra-rating-meta">
136 294 <strong><?php echo esc_html(number_format($booking->trip_average_rating, 1)); ?></strong>
137 295 <span>
138 296 <?php
139 - /* translators: %d review count */
140 - printf(esc_html(_n('%d Review', '%d Reviews', (int) $booking->trip_review_count, 'yatra')), (int) $booking->trip_review_count);
297 + printf(
298 + esc_html(
299 + /* translators: %d: number of reviews. */
300 + _n('%d Review', '%d Reviews', (int) $booking->trip_review_count, 'yatra')
301 + ),
302 + (int) $booking->trip_review_count
303 + );
141 304 ?>
142 305 </span>
143 306 </div>
144 307 </div>
@@ -144,24 +307,72 @@
144 307 </div>
145 308 <?php endif; ?>
146 309
147 310 <div class="yatra-trip-meta">
148 - <?php if (!empty($booking->duration_days)) : ?>
311 + <?php if (!empty($booking->duration_days) || !empty($booking->duration_hours)) : ?>
149 312 <span class="yatra-meta-item">
150 313 <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
151 314 <circle cx="12" cy="12" r="10"></circle>
152 315 <polyline points="12 6 12 12 16 14"></polyline>
153 316 </svg>
154 - <?php printf(esc_html__('%d Days / %d Nights', 'yatra'), (int) $booking->duration_days, (int) $booking->duration_nights); ?>
317 + <?php
318 + $days = (int) ($booking->duration_days ?? 0);
319 + $nights = (int) ($booking->duration_nights ?? 0);
320 + $hours = (int) ($booking->duration_hours ?? 0);
321 +
322 + if ($hours > 0) {
323 + // Hour-based day tour: "8 Hours", not "1 Day".
324 + printf(
325 + esc_html(
326 + /* translators: %d: number of hours. */
327 + _n('%d Hour', '%d Hours', $hours, 'yatra')
328 + ),
329 + $hours
330 + );
331 + } elseif ($days > 0 && $nights > 0) {
332 + printf(
333 + /* translators: 1: number of days, 2: number of nights. */
334 + esc_html__('%1$d Days / %2$d Nights', 'yatra'),
335 + $days,
336 + $nights
337 + );
338 + } elseif ($days > 0) {
339 + printf(
340 + esc_html(
341 + /* translators: %d: number of days. */
342 + _n('%d Day', '%d Days', $days, 'yatra')
343 + ),
344 + $days
345 + );
346 + } elseif ($nights > 0) {
347 + printf(
348 + esc_html(
349 + /* translators: %d: number of nights. */
350 + _n('%d Night', '%d Nights', $nights, 'yatra')
351 + ),
352 + $nights
353 + );
354 + }
355 + ?>
155 356 </span>
156 357 <?php endif; ?>
157 358
158 359 <?php if (!empty($booking->difficulty_level)) : ?>
159 360 <span class="yatra-meta-item">
160 - <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
161 - <polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"></polygon>
162 - </svg>
163 - <?php echo esc_html(ucfirst($booking->difficulty_level)); ?>
361 + <?php
362 + $difficulty = \Yatra\Models\Trip::resolveDifficultyDisplay(
363 + (string) $booking->difficulty_level
364 + );
365 +
366 + // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
367 + echo yatra_stored_picker_icon_markup(
368 + $difficulty['icon_picker'] ?? null,
369 + '',
370 + 'yatra-icon-sm'
371 + );
372 +
373 + echo esc_html($difficulty['level'] ?? (string) $booking->difficulty_level);
374 + ?>
164 375 </span>
165 376 <?php endif; ?>
166 377 </div>
167 378
@@ -210,18 +421,8 @@
210 421 </div>
211 422 </div>
212 423 <?php endif; ?>
213 424
214 - <?php if (!empty($booking->trip_attributes_list)) : ?>
215 - <div class="yatra-trip-tags">
216 - <span class="yatra-tag-label"><?php esc_html_e('Features:', 'yatra'); ?></span>
217 - <div class="yatra-tag-group">
218 - <?php foreach ($booking->trip_attributes_list as $attribute) : ?>
219 - <span class="yatra-tag-item yatra-attribute-tag"><?php echo esc_html($attribute); ?></span>
220 - <?php endforeach; ?>
221 - </div>
222 - </div>
223 - <?php endif; ?>
224 425 </div>
225 426 </div>
226 427 </div>
227 428
@@ -522,18 +723,37 @@
522 723 </div>
523 724 <?php endif; ?>
524 725
525 726 <?php if ($amount_due > 0): ?>
727 + <?php
728 + // Label the "due now" amount by payment type so a
729 + // deposit/partial reads clearly instead of just "Due Now".
730 + $payment_method_type = (string) ($booking->payment_method ?? 'full');
731 + $remaining_balance = max(0.0, $total_amount - $amount_paid - $amount_due);
732 + if ($payment_method_type === 'deposit') {
733 + $due_now_label = __('Deposit Due Now', 'yatra');
734 + } elseif ($payment_method_type === 'partial') {
735 + $due_now_label = __('Partial Payment Due Now', 'yatra');
736 + } else {
737 + $due_now_label = __('Due Now', 'yatra');
738 + }
739 + ?>
526 740 <div class="yatra-payment-row yatra-due-row">
527 - <span><strong><?php esc_html_e('Due Now', 'yatra'); ?></strong></span>
741 + <span><strong><?php echo esc_html($due_now_label); ?></strong></span>
528 742 <span><strong><?php echo esc_html(yatra_format_price($amount_due, $booking->currency)); ?></strong></span>
529 743 </div>
744 + <?php if ($payment_method_type !== 'full' && $remaining_balance > 0.01): ?>
745 + <div class="yatra-payment-row" style="margin-top: 6px;">
746 + <span><?php esc_html_e('Remaining Balance', 'yatra'); ?></span>
747 + <span><?php echo esc_html(yatra_format_price($remaining_balance, $booking->currency)); ?></span>
748 + </div>
530 749 <?php endif; ?>
750 + <?php endif; ?>
531 751 </div>
532 752
533 753 <div class="yatra-payment-method">
534 754 <span class="yatra-pm-label"><?php esc_html_e('Payment Method:', 'yatra'); ?></span>
535 - <span class="yatra-pm-value"><?php echo esc_html(ucwords(str_replace('_', ' ', $booking->payment_gateway))); ?></span>
755 + <span class="yatra-pm-value"><?php echo esc_html(yatra_payment_gateway_label((string) $display_payment_gateway)); ?></span>
536 756 </div>
537 757 </div>
538 758
539 759 <!-- Contact Info Card -->
@@ -667,10 +887,10 @@
667 887 ],
668 888 rest_url('yatra/v1/payment/' . (int) $latestPayment->id . '/invoice')
669 889 );
670 890 ?>
671 - <a href="<?php echo esc_url($invoiceUrl); ?>"
672 - target="_blank"
891 + <a href="<?php echo esc_url($invoiceUrl); ?>"
892 + target="_blank"
673 893 rel="noopener"
674 894 class="yatra-btn yatra-btn-primary">
675 895 <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
676 896 <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path>
@@ -678,8 +898,33 @@
678 898 <line x1="12" y1="15" x2="12" y2="3"></line>
679 899 </svg>
680 900 <?php esc_html_e('Download Invoice', 'yatra'); ?>
681 901 </a>
902 + <?php
903 + // No completed payment yet (e.g. Bank Transfer / offline): offer a
904 + // pro-forma invoice that carries the payment instructions so the
905 + // customer knows how to pay. Uses a booking-scoped invoice token.
906 + elseif ((float) ($booking->amount_due ?? $booking->total_amount ?? 0) > 0) :
907 + $proformaToken = \Yatra\Controllers\PaymentGatewayController::issueInvoiceToken(0, (int) $booking->id);
908 + $proformaUrl = add_query_arg(
909 + [
910 + 'download' => '1',
911 + 'invoice_token' => $proformaToken,
912 + ],
913 + rest_url('yatra/v1/booking/' . (int) $booking->id . '/invoice')
914 + );
915 + ?>
916 + <a href="<?php echo esc_url($proformaUrl); ?>"
917 + target="_blank"
918 + rel="noopener"
919 + class="yatra-btn yatra-btn-primary">
920 + <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
921 + <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path>
922 + <polyline points="7 10 12 15 17 10"></polyline>
923 + <line x1="12" y1="15" x2="12" y2="3"></line>
924 + </svg>
925 + <?php esc_html_e('Download Invoice', 'yatra'); ?>
926 + </a>
682 927 <?php endif; ?>
683 928
684 929 <button type="button" data-yatra-print="confirmation" class="yatra-btn yatra-btn-outline">
685 930 <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
@@ -829,8 +1074,68 @@
829 1074 color: #111827;
830 1075 margin: 0 0 12px;
831 1076 }
832 1077
1078 +/* Star rating. This confirmation page only enqueues yatra-common + fontawesome,
1079 + not booking.css/trip.css where these live, so without them the inline SVG
1080 + stars render with no size or colour (invisible). Mirrors booking.css. */
1081 +.yatra-trip-rating {
1082 + display: flex;
1083 + align-items: center;
1084 + gap: 12px;
1085 + margin-bottom: 12px;
1086 +}
1087 +
1088 +.yatra-rating-stars {
1089 + display: inline-flex;
1090 + gap: 4px;
1091 +}
1092 +
1093 +.yatra-star {
1094 + width: 20px;
1095 + height: 20px;
1096 + color: #e2e8f0;
1097 +}
1098 +
1099 +.yatra-star svg {
1100 + width: 100%;
1101 + height: 100%;
1102 +}
1103 +
1104 +.yatra-star-filled {
1105 + color: #f59e0b;
1106 +}
1107 +
1108 +/* Half star: base star stays grey; overlay the left half in amber via an
1109 + SVG mask (size-independent, so it tracks the star's dimensions). */
1110 +.yatra-star-half {
1111 + position: relative;
1112 + color: #e2e8f0;
1113 +}
1114 +
1115 +.yatra-star-half::after {
1116 + content: "";
1117 + position: absolute;
1118 + top: 0;
1119 + left: 0;
1120 + bottom: 0;
1121 + width: 50%;
1122 + overflow: hidden;
1123 + background: #f59e0b;
1124 + -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpolygon points='12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26'/%3E%3C/svg%3E") no-repeat left center / 200% 100%;
1125 + mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpolygon points='12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26'/%3E%3C/svg%3E") no-repeat left center / 200% 100%;
1126 +}
1127 +
1128 +.yatra-rating-meta strong {
1129 + color: #0f172a;
1130 + margin-right: 6px;
1131 +}
1132 +
1133 +.yatra-rating-meta span {
1134 + color: #64748b;
1135 + font-size: 14px;
1136 +}
1137 +
833 1138 .yatra-trip-meta {
834 1139 display: flex;
835 1140 gap: 20px;
836 1141 flex-wrap: wrap;
@@ -878,8 +1183,13 @@
878 1183 .yatra-trip-summary {
879 1184 flex-direction: column;
880 1185 text-align: center;
881 1186 }
1187 +
1188 + .yatra-info-grid {
1189 + grid-template-columns: 1fr;
1190 + gap: 12px;
1191 + }
882 1192 }
883 1193
884 1194 .yatra-info-grid {
885 1195 display: grid;
@@ -890,13 +1200,18 @@
890 1200 .yatra-info-item {
891 1201 display: flex;
892 1202 flex-direction: column;
893 1203 gap: 4px;
1204 + padding: 12px 14px;
1205 + background: #f9fafb;
1206 + border: 1px solid #eef2f7;
1207 + border-radius: 12px;
894 1208 }
895 1209
896 1210 .yatra-info-label {
897 1211 font-size: 13px;
898 1212 color: #6b7280;
1213 + line-height: 1.2;
899 1214 }
900 1215
901 1216 .yatra-info-value {
902 1217 font-size: 15px;
@@ -901,8 +1216,10 @@
901 1216 .yatra-info-value {
902 1217 font-size: 15px;
903 1218 font-weight: 600;
904 1219 color: #111827;
1220 + line-height: 1.25;
1221 + text-align: left;
905 1222 }
906 1223
907 1224 .yatra-status-badge {
908 1225 display: inline-block;
@@ -977,8 +1294,44 @@
977 1294 .yatra-discount-row span:last-child {
978 1295 color: #22c55e;
979 1296 }
980 1297
1298 +/* Bank-transfer details (markup injected by Yatra Pro's Bank Transfer gateway
1299 + into this page). The card reuses this page's .yatra-confirmation-card /
1300 + .yatra-card-title classes, but its rows had no styling at all, so the values
1301 + sat immediately after their label while every other row on the page is
1302 + flex/space-between — the bank values were the only ones not flush right.
1303 + Styling lives here because this template owns the confirmation page's CSS
1304 + and Pro enqueues no stylesheet on this page. */
1305 +.yatra-bank-info-grid {
1306 + border: 1px solid #e5e7eb;
1307 + border-radius: 8px;
1308 + overflow: hidden;
1309 +}
1310 +
1311 +.yatra-bank-info-item {
1312 + display: flex;
1313 + justify-content: space-between;
1314 + align-items: baseline;
1315 + gap: 12px;
1316 + padding: 12px 16px;
1317 + border-bottom: 1px solid #e5e7eb;
1318 +}
1319 +
1320 +.yatra-bank-info-item:last-child {
1321 + border-bottom: none;
1322 +}
1323 +
1324 +.yatra-bank-label {
1325 + color: #6b7280;
1326 + flex-shrink: 0;
1327 +}
1328 +
1329 +.yatra-bank-value {
1330 + text-align: right;
1331 + word-break: break-word;
1332 +}
1333 +
981 1334 .yatra-payment-method {
982 1335 margin-top: 16px;
983 1336 padding: 12px;
984 1337 background: white;
@@ -1376,9 +1729,10 @@
1376 1729 /* Payment summary rows */
1377 1730 body.yatra-printing-confirmation .yatra-payment-rows {
1378 1731 margin: 0 !important;
1379 1732 }
1380 - body.yatra-printing-confirmation .yatra-payment-row {
1733 + body.yatra-printing-confirmation .yatra-payment-row,
1734 + body.yatra-printing-confirmation .yatra-bank-info-item {
1381 1735 display: flex !important;
1382 1736 justify-content: space-between !important;
1383 1737 align-items: baseline !important;
1384 1738 gap: 12pt !important;