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 +261 -32 3.0.4 → 3.0.16 View file →
@@ -43,8 +43,15 @@
43 43 $booking_amount_due = (float) ($booking->amount_due ?? 0);
44 44 $booking_is_fully_paid = $booking_payment_status === 'paid' || $booking_amount_due <= 0.01;
45 45 $show_balance_paid_banner = $is_balance_paid_request && $booking_is_fully_paid;
46 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 +
47 54 // Resolve the most recent gateway used on this booking.
48 55 // `$booking->payment_gateway` is the *initial* method chosen at booking time
49 56 // (e.g. Pay Later), which is misleading when the customer later settled the
50 57 // balance via a different gateway (e.g. Stripe). Show the latest payment's
@@ -158,10 +165,10 @@
158 165 </strong>
159 166 <span>
160 167 <?php
161 168 printf(
162 - /* translators: 1: booking reference number (HTML <strong> wrapped), 2: link labelled "payment history" pointing to the customer account page (HTML <a> wrapped) */
163 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). */
164 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'),
165 172 ['a' => ['href' => true], 'strong' => []]
166 173 ),
167 174 '<strong>' . esc_html((string) ($booking->reference ?? '')) . '</strong>',
@@ -172,10 +179,32 @@
172 179 </div>
173 180 </div>
174 181 <?php endif; ?>
175 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 +
176 201 <?php
177 - // 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.
178 207 $featured_image_url = '';
179 208 if (!empty($booking->featured_image)) {
180 209 if (is_numeric($booking->featured_image)) {
181 210 $featured_image_url = wp_get_attachment_url((int) $booking->featured_image) ?: '';
@@ -182,8 +211,14 @@
182 211 } else {
183 212 $featured_image_url = $booking->featured_image;
184 213 }
185 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 + }
186 221 ?>
187 222
188 223 <div class="yatra-confirmation-content">
189 224 <!-- Trip Summary Card -->
@@ -188,23 +223,66 @@
188 223 <div class="yatra-confirmation-content">
189 224 <!-- Trip Summary Card -->
190 225 <div class="yatra-confirmation-card yatra-trip-summary-card">
191 226 <div class="yatra-trip-summary">
192 - <?php if (!empty($featured_image_url)) : ?>
193 - <div class="yatra-trip-image">
194 - <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');">
195 232 </div>
196 - <?php endif; ?>
197 233
198 234 <div class="yatra-trip-info">
199 235 <h2 class="yatra-trip-name"><?php echo esc_html($booking->trip_title); ?></h2>
200 236
201 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>
202 275 <div class="yatra-trip-rating">
203 276 <div class="yatra-rating-stars">
204 277 <?php
205 - $filled_stars = floor($booking->trip_average_rating);
206 - $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'];
207 285 for ($i = 1; $i <= 5; $i++) :
208 286 $class = $i <= $filled_stars ? 'yatra-star-filled' : ($has_half && $i === $filled_stars + 1 ? 'yatra-star-half' : '');
209 287 ?>
210 288 <span class="yatra-star <?php echo esc_attr($class); ?>">
@@ -215,10 +293,15 @@
215 293 <div class="yatra-rating-meta">
216 294 <strong><?php echo esc_html(number_format($booking->trip_average_rating, 1)); ?></strong>
217 295 <span>
218 296 <?php
219 - /* translators: %d review count */
220 - 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 + );
221 304 ?>
222 305 </span>
223 306 </div>
224 307 </div>
@@ -224,9 +307,9 @@
224 307 </div>
225 308 <?php endif; ?>
226 309
227 310 <div class="yatra-trip-meta">
228 - <?php if (!empty($booking->duration_days)) : ?>
311 + <?php if (!empty($booking->duration_days) || !empty($booking->duration_hours)) : ?>
229 312 <span class="yatra-meta-item">
230 313 <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
231 314 <circle cx="12" cy="12" r="10"></circle>
232 315 <polyline points="12 6 12 12 16 14"></polyline>
@@ -233,25 +316,40 @@
233 316 </svg>
234 317 <?php
235 318 $days = (int) ($booking->duration_days ?? 0);
236 319 $nights = (int) ($booking->duration_nights ?? 0);
320 + $hours = (int) ($booking->duration_hours ?? 0);
237 321
238 - if ($days > 0 && $nights > 0) {
322 + if ($hours > 0) {
323 + // Hour-based day tour: "8 Hours", not "1 Day".
239 324 printf(
240 - esc_html__('%d Days / %d Nights', 'yatra'),
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'),
241 335 $days,
242 336 $nights
243 337 );
244 338 } elseif ($days > 0) {
245 339 printf(
246 - /* translators: %d day count */
247 - esc_html(_n('%d Day', '%d Days', $days, 'yatra')),
340 + esc_html(
341 + /* translators: %d: number of days. */
342 + _n('%d Day', '%d Days', $days, 'yatra')
343 + ),
248 344 $days
249 345 );
250 346 } elseif ($nights > 0) {
251 347 printf(
252 - /* translators: %d night count */
253 - esc_html(_n('%d Night', '%d Nights', $nights, 'yatra')),
348 + esc_html(
349 + /* translators: %d: number of nights. */
350 + _n('%d Night', '%d Nights', $nights, 'yatra')
351 + ),
254 352 $nights
255 353 );
256 354 }
257 355 ?>
@@ -323,18 +421,8 @@
323 421 </div>
324 422 </div>
325 423 <?php endif; ?>
326 424
327 - <?php if (!empty($booking->trip_attributes_list)) : ?>
328 - <div class="yatra-trip-tags">
329 - <span class="yatra-tag-label"><?php esc_html_e('Features:', 'yatra'); ?></span>
330 - <div class="yatra-tag-group">
331 - <?php foreach ($booking->trip_attributes_list as $attribute) : ?>
332 - <span class="yatra-tag-item yatra-attribute-tag"><?php echo esc_html($attribute); ?></span>
333 - <?php endforeach; ?>
334 - </div>
335 - </div>
336 - <?php endif; ?>
337 425 </div>
338 426 </div>
339 427 </div>
340 428
@@ -635,18 +723,37 @@
635 723 </div>
636 724 <?php endif; ?>
637 725
638 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 + ?>
639 740 <div class="yatra-payment-row yatra-due-row">
640 - <span><strong><?php esc_html_e('Due Now', 'yatra'); ?></strong></span>
741 + <span><strong><?php echo esc_html($due_now_label); ?></strong></span>
641 742 <span><strong><?php echo esc_html(yatra_format_price($amount_due, $booking->currency)); ?></strong></span>
642 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>
643 749 <?php endif; ?>
750 + <?php endif; ?>
644 751 </div>
645 752
646 753 <div class="yatra-payment-method">
647 754 <span class="yatra-pm-label"><?php esc_html_e('Payment Method:', 'yatra'); ?></span>
648 - <span class="yatra-pm-value"><?php echo esc_html(ucwords(str_replace('_', ' ', (string) $display_payment_gateway))); ?></span>
755 + <span class="yatra-pm-value"><?php echo esc_html(yatra_payment_gateway_label((string) $display_payment_gateway)); ?></span>
649 756 </div>
650 757 </div>
651 758
652 759 <!-- Contact Info Card -->
@@ -780,10 +887,10 @@
780 887 ],
781 888 rest_url('yatra/v1/payment/' . (int) $latestPayment->id . '/invoice')
782 889 );
783 890 ?>
784 - <a href="<?php echo esc_url($invoiceUrl); ?>"
785 - target="_blank"
891 + <a href="<?php echo esc_url($invoiceUrl); ?>"
892 + target="_blank"
786 893 rel="noopener"
787 894 class="yatra-btn yatra-btn-primary">
788 895 <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
789 896 <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path>
@@ -791,8 +898,33 @@
791 898 <line x1="12" y1="15" x2="12" y2="3"></line>
792 899 </svg>
793 900 <?php esc_html_e('Download Invoice', 'yatra'); ?>
794 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>
795 927 <?php endif; ?>
796 928
797 929 <button type="button" data-yatra-print="confirmation" class="yatra-btn yatra-btn-outline">
798 930 <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
@@ -942,8 +1074,68 @@
942 1074 color: #111827;
943 1075 margin: 0 0 12px;
944 1076 }
945 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 +
946 1138 .yatra-trip-meta {
947 1139 display: flex;
948 1140 gap: 20px;
949 1141 flex-wrap: wrap;
@@ -1102,8 +1294,44 @@
1102 1294 .yatra-discount-row span:last-child {
1103 1295 color: #22c55e;
1104 1296 }
1105 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 +
1106 1334 .yatra-payment-method {
1107 1335 margin-top: 16px;
1108 1336 padding: 12px;
1109 1337 background: white;
@@ -1501,9 +1729,10 @@
1501 1729 /* Payment summary rows */
1502 1730 body.yatra-printing-confirmation .yatra-payment-rows {
1503 1731 margin: 0 !important;
1504 1732 }
1505 - body.yatra-printing-confirmation .yatra-payment-row {
1733 + body.yatra-printing-confirmation .yatra-payment-row,
1734 + body.yatra-printing-confirmation .yatra-bank-info-item {
1506 1735 display: flex !important;
1507 1736 justify-content: space-between !important;
1508 1737 align-items: baseline !important;
1509 1738 gap: 12pt !important;