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 +227 -9 3.0.6 → 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
@@ -172,8 +179,26 @@
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 202 // Resolve featured image URL (handle attachment ID or direct URL).
178 203 // Falls back to the bundled trip-placeholder.svg so the confirmation
179 204 // card always renders with a visual on the left — never an empty
@@ -209,13 +234,55 @@
209 234 <div class="yatra-trip-info">
210 235 <h2 class="yatra-trip-name"><?php echo esc_html($booking->trip_title); ?></h2>
211 236
212 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>
213 275 <div class="yatra-trip-rating">
214 276 <div class="yatra-rating-stars">
215 277 <?php
216 - $filled_stars = floor($booking->trip_average_rating);
217 - $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'];
218 285 for ($i = 1; $i <= 5; $i++) :
219 286 $class = $i <= $filled_stars ? 'yatra-star-filled' : ($has_half && $i === $filled_stars + 1 ? 'yatra-star-half' : '');
220 287 ?>
221 288 <span class="yatra-star <?php echo esc_attr($class); ?>">
@@ -240,9 +307,9 @@
240 307 </div>
241 308 <?php endif; ?>
242 309
243 310 <div class="yatra-trip-meta">
244 - <?php if (!empty($booking->duration_days)) : ?>
311 + <?php if (!empty($booking->duration_days) || !empty($booking->duration_hours)) : ?>
245 312 <span class="yatra-meta-item">
246 313 <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
247 314 <circle cx="12" cy="12" r="10"></circle>
248 315 <polyline points="12 6 12 12 16 14"></polyline>
@@ -249,11 +316,21 @@
249 316 </svg>
250 317 <?php
251 318 $days = (int) ($booking->duration_days ?? 0);
252 319 $nights = (int) ($booking->duration_nights ?? 0);
320 + $hours = (int) ($booking->duration_hours ?? 0);
253 321
254 - if ($days > 0 && $nights > 0) {
322 + if ($hours > 0) {
323 + // Hour-based day tour: "8 Hours", not "1 Day".
255 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(
256 333 /* translators: 1: number of days, 2: number of nights. */
257 334 esc_html__('%1$d Days / %2$d Nights', 'yatra'),
258 335 $days,
259 336 $nights
@@ -646,18 +723,37 @@
646 723 </div>
647 724 <?php endif; ?>
648 725
649 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 + ?>
650 740 <div class="yatra-payment-row yatra-due-row">
651 - <span><strong><?php esc_html_e('Due Now', 'yatra'); ?></strong></span>
741 + <span><strong><?php echo esc_html($due_now_label); ?></strong></span>
652 742 <span><strong><?php echo esc_html(yatra_format_price($amount_due, $booking->currency)); ?></strong></span>
653 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>
654 749 <?php endif; ?>
750 + <?php endif; ?>
655 751 </div>
656 752
657 753 <div class="yatra-payment-method">
658 754 <span class="yatra-pm-label"><?php esc_html_e('Payment Method:', 'yatra'); ?></span>
659 - <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>
660 756 </div>
661 757 </div>
662 758
663 759 <!-- Contact Info Card -->
@@ -791,10 +887,10 @@
791 887 ],
792 888 rest_url('yatra/v1/payment/' . (int) $latestPayment->id . '/invoice')
793 889 );
794 890 ?>
795 - <a href="<?php echo esc_url($invoiceUrl); ?>"
796 - target="_blank"
891 + <a href="<?php echo esc_url($invoiceUrl); ?>"
892 + target="_blank"
797 893 rel="noopener"
798 894 class="yatra-btn yatra-btn-primary">
799 895 <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
800 896 <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path>
@@ -802,8 +898,33 @@
802 898 <line x1="12" y1="15" x2="12" y2="3"></line>
803 899 </svg>
804 900 <?php esc_html_e('Download Invoice', 'yatra'); ?>
805 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>
806 927 <?php endif; ?>
807 928
808 929 <button type="button" data-yatra-print="confirmation" class="yatra-btn yatra-btn-outline">
809 930 <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
@@ -953,8 +1074,68 @@
953 1074 color: #111827;
954 1075 margin: 0 0 12px;
955 1076 }
956 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 +
957 1138 .yatra-trip-meta {
958 1139 display: flex;
959 1140 gap: 20px;
960 1141 flex-wrap: wrap;
@@ -1113,8 +1294,44 @@
1113 1294 .yatra-discount-row span:last-child {
1114 1295 color: #22c55e;
1115 1296 }
1116 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 +
1117 1334 .yatra-payment-method {
1118 1335 margin-top: 16px;
1119 1336 padding: 12px;
1120 1337 background: white;
@@ -1512,9 +1729,10 @@
1512 1729 /* Payment summary rows */
1513 1730 body.yatra-printing-confirmation .yatra-payment-rows {
1514 1731 margin: 0 !important;
1515 1732 }
1516 - body.yatra-printing-confirmation .yatra-payment-row {
1733 + body.yatra-printing-confirmation .yatra-payment-row,
1734 + body.yatra-printing-confirmation .yatra-bank-info-item {
1517 1735 display: flex !important;
1518 1736 justify-content: space-between !important;
1519 1737 align-items: baseline !important;
1520 1738 gap: 12pt !important;