PluginProbe
Yatra – Travel Booking & Tour Operator Software / 3.0.13
Yatra – Travel Booking & Tour Operator Software v3.0.13
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 2.0.10 2.0.11 All 82 releases
yatra / app / Services / EmailTemplateDefaults.php

EmailTemplateDefaults.php in Yatra – Travel Booking & Tour Operator Software 3.0.13, at app/Services/EmailTemplateDefaults.php

1,617 lines 79.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 declare(strict_types=1);
4
5 namespace Yatra\Services;
6
7 /**
8 * Default subject + HTML for transactional settings (free) and Pro automation seed/backfill.
9 * Uses {{merge_tags}} compatible with TransactionalEmailTemplateService and Pro EmailAutomationService.
10 */
11 final class EmailTemplateDefaults
12 {
13 /**
14 * Option keys (without yatra_ prefix) for SettingsController / InstallerService.
15 *
16 * @return array<string, string>
17 */
18 public static function settingsOptionDefaults(): array
19 {
20 $defaults = [
21 'email_tpl_booking_subject' => '✈️ [{{site_name}}] Booking update · {{booking_reference}}',
22 'email_tpl_booking_body' => self::htmlBookingCustomer(),
23 'email_tpl_payment_subject' => '�
24 [{{site_name}}] Payment received · {{booking_reference}}',
25 'email_tpl_payment_body' => self::htmlPaymentCustomer(),
26 'email_tpl_partial_payment_subject' => '💳 [{{site_name}}] Part payment received · {{booking_reference}}',
27 'email_tpl_partial_payment_body' => self::htmlPartialPaymentCustomer(),
28 'email_tpl_cancellation_subject' => '📋 [{{site_name}}] Booking cancelled · {{booking_reference}}',
29 'email_tpl_cancellation_body' => self::htmlCancellationCustomer(),
30 'email_tpl_reminder_subject' => '🗓 [{{site_name}}] Your trip is coming up · {{booking_reference}}',
31 'email_tpl_reminder_body' => self::htmlReminderCustomer(),
32 'email_tpl_admin_booking_subject' => '🔔 [{{site_name}}] New booking · {{booking_reference}} (#{{booking_id}})',
33 'email_tpl_admin_booking_body' => self::htmlAdminNewBooking(),
34 'email_tpl_admin_payment_subject' => '�
35 [{{site_name}}] Payment received · {{booking_reference}} (#{{booking_id}})',
36 'email_tpl_admin_payment_body' => self::htmlAdminPaymentReceived(),
37 'email_tpl_admin_cancellation_subject' => '📋 [{{site_name}}] Booking cancelled · {{booking_reference}} (#{{booking_id}})',
38 'email_tpl_admin_cancellation_body' => self::htmlAdminBookingCancelled(),
39 'email_tpl_trip_consent_subject' => '📝 [{{site_name}}] Action required · {{form_name}}',
40 'email_tpl_trip_consent_body' => self::htmlTripConsentRequest(),
41 'email_tpl_customer_verification_subject' => '✉️ [{{site_name}}] Verify your email address',
42 'email_tpl_customer_verification_body' => self::htmlCustomerEmailVerification(),
43 // Guest-checkout verification (distinct from account registration above).
44 // Subject mirrors the render's hardcoded fallback; body reuses the
45 // customer-verification HTML so the email is identical until an operator
46 // customizes it. Registering these keys here is required so the settings
47 // save accepts them (SettingsController whitelists by default_settings)
48 // and the admin template editor pre-fills them.
49 'email_tpl_guest_verification_subject' => '✉️ [{{site_name}}] Verify your email to complete your booking',
50 'email_tpl_guest_verification_body' => self::htmlCustomerEmailVerification(),
51 // Account email change: confirmation link to the NEW address, and a
52 // security notice to the OLD address once the change is confirmed.
53 'email_tpl_account_email_change_subject' => '✉️ [{{site_name}}] Confirm your new email address',
54 'email_tpl_account_email_change_body' => self::htmlAccountEmailChangeRequest(),
55 'email_tpl_account_email_changed_subject' => '🔔 [{{site_name}}] Your email address was changed',
56 'email_tpl_account_email_changed_body' => self::htmlAccountEmailChanged(),
57 'email_tpl_booking_completed_subject' => '🌟 [{{site_name}}] Thanks for traveling · {{booking_reference}}',
58 'email_tpl_booking_completed_body' => self::htmlTripCompleted(),
59 'email_tpl_booking_expired_customer_subject' => '⏱️ [{{site_name}}] Booking expired · {{booking_reference}}',
60 'email_tpl_booking_expired_customer_body' => self::htmlBookingExpiredCustomer(),
61 'email_tpl_admin_booking_expired_subject' => '⏱️ [{{site_name}}] Booking expired · {{booking_reference}} (#{{booking_id}})',
62 'email_tpl_admin_booking_expired_body' => self::htmlAdminBookingExpired(),
63 'email_tpl_scheduled_payment_reminder_subject' => '💳 [{{site_name}}] Upcoming payment · {{booking_reference}}',
64 'email_tpl_scheduled_payment_reminder_body' => self::htmlScheduledPaymentReminder(),
65 'email_tpl_scheduled_payment_succeeded_subject' => '�
66 [{{site_name}}] Scheduled payment received · {{booking_reference}}',
67 'email_tpl_scheduled_payment_succeeded_body' => self::htmlScheduledPaymentSucceeded(),
68 'email_tpl_scheduled_payment_failed_subject' => '⚠️ [{{site_name}}] Payment issue · {{booking_reference}}',
69 'email_tpl_scheduled_payment_failed_body' => self::htmlScheduledPaymentFailed(),
70 'email_tpl_admin_scheduled_payment_failed_subject' => '⚠️ [{{site_name}}] Scheduled payment failed · {{booking_reference}}',
71 'email_tpl_admin_scheduled_payment_failed_body' => self::htmlAdminScheduledPaymentFailed(),
72 'email_tpl_enquiry_admin_subject' => '💬 [{{site_name}}] New enquiry · {{customer_name}}',
73 'email_tpl_enquiry_admin_body' => self::htmlEnquiryAdmin(),
74 'email_tpl_enquiry_received_subject' => '✉️ [{{site_name}}] We received your message',
75 'email_tpl_enquiry_received_body' => self::htmlEnquiryCustomer(),
76 'email_tpl_enquiry_response_subject' => '💬 [{{site_name}}] Re: your enquiry',
77 'email_tpl_enquiry_response_body' => self::htmlEnquiryResponse(),
78 'email_tpl_review_request_subject' => ' [{{site_name}}] How was {{trip_name}}?',
79 'email_tpl_review_request_body' => self::htmlReviewRequest(),
80 'email_tpl_abandoned_booking_recovery_first_subject' => '🛒 [{{site_name}}] Complete your booking',
81 'email_tpl_abandoned_booking_recovery_first_body' => self::htmlAbandonedBookingRecoveryFirst(),
82 'email_tpl_abandoned_booking_recovery_second_subject' => ' [{{site_name}}] Still interested? Your booking is waiting',
83 'email_tpl_abandoned_booking_recovery_second_body' => self::htmlAbandonedBookingRecoverySecond(),
84 'email_tpl_abandoned_booking_recovery_final_subject' => '⚠️ [{{site_name}}] Final reminder: complete your booking',
85 'email_tpl_abandoned_booking_recovery_final_body' => self::htmlAbandonedBookingRecoveryFinal(),
86 ];
87
88 // Every template also gets an opt-in Bcc / Cc option, derived from its
89 // own subject key so the two lists can never drift apart (and so a
90 // template type added later picks them up for free). Empty by default:
91 // no address configured means no copy is sent and nothing changes for
92 // existing sites. Registered here because this array doubles as the
93 // whitelist of settings the REST API will persist.
94 foreach (array_keys($defaults) as $key) {
95 if (substr($key, -8) !== '_subject') {
96 continue;
97 }
98
99 $base = substr($key, 0, -8);
100 $defaults[$base . '_bcc'] = '';
101 $defaults[$base . '_cc'] = '';
102 }
103
104 return $defaults;
105 }
106
107 /**
108 * Full subject + body for Pro system templates (DB). Null = no bundled default for that key.
109 *
110 * @return array{subject: string, body: string}|null
111 */
112 public static function proSystemTemplate(string $templateKey): ?array
113 {
114 $map = [
115 'new_booking' => [
116 'subject' => '✈️ Booking confirmation · {{booking_reference}}',
117 'body' => self::htmlBookingCustomer(),
118 ],
119 'booking_payment' => [
120 'subject' => '�
121 Payment received · {{booking_reference}}',
122 'body' => self::htmlPaymentCustomer(),
123 ],
124 'booking_confirmation' => [
125 'subject' => '✈️ Your booking · {{booking_reference}}',
126 'body' => self::htmlBookingCustomer(),
127 ],
128 'booking_confirmed' => [
129 'subject' => '🎉 You\'re confirmed · {{booking_reference}}',
130 'body' => self::htmlBookingConfirmed(),
131 ],
132 'admin_new_booking' => [
133 'subject' => '🔔 New booking · {{booking_reference}} (#{{booking_id}})',
134 'body' => self::htmlAdminNewBooking(),
135 ],
136 'booking_cancelled' => [
137 'subject' => '📋 Booking cancelled · {{booking_reference}}',
138 'body' => self::htmlCancellationCustomer(),
139 ],
140 'booking_completed' => [
141 'subject' => '🌟 Thanks for traveling with us · {{booking_reference}}',
142 'body' => self::htmlTripCompleted(),
143 ],
144 'payment_received' => [
145 'subject' => '�
146 Payment received · {{booking_reference}}',
147 'body' => self::htmlPaymentCustomer(),
148 ],
149 'partial_payment_received' => [
150 'subject' => '💳 Part payment received · {{booking_reference}}',
151 'body' => self::htmlPartialPaymentCustomer(),
152 ],
153 'payment_reminder' => [
154 'subject' => '💳 Payment reminder · {{booking_reference}}',
155 'body' => self::htmlPaymentReminder(),
156 ],
157 'enquiry_received' => [
158 'subject' => '✉️ We received your message',
159 'body' => self::htmlEnquiryCustomer(),
160 ],
161 'enquiry_admin' => [
162 'subject' => '💬 New enquiry · {{customer_name}}',
163 'body' => self::htmlEnquiryAdmin(),
164 ],
165 'enquiry_response' => [
166 'subject' => '💬 Re: your enquiry',
167 'body' => self::htmlEnquiryResponse(),
168 ],
169 'trip_reminder' => [
170 'subject' => '🗓 Reminder · {{trip_name}}',
171 'body' => self::htmlReminderCustomer(),
172 ],
173 'review_request' => [
174 'subject' => ' How was {{trip_name}}?',
175 'body' => self::htmlReviewRequest(),
176 ],
177 'trip_consent_request' => [
178 'subject' => '📝 [{{site_name}}] Action required · {{form_name}}',
179 'body' => self::htmlTripConsentRequest(),
180 ],
181 'customer_email_verification' => [
182 'subject' => '✉️ [{{site_name}}] Verify your email address',
183 'body' => self::htmlCustomerEmailVerification(),
184 ],
185 'guest_email_verification' => [
186 // Guest-checkout verification reuses the customer-verification
187 // body — the flow is identical (click a magic link to prove
188 // ownership) and the booking-specific copy is injected at
189 // send-time via {{intro_paragraph}} / {{footer_note}}.
190 'subject' => '✉️ [{{site_name}}] Verify your email to complete your booking',
191 'body' => self::htmlCustomerEmailVerification(),
192 ],
193 'account_email_change_request' => [
194 'subject' => '✉️ [{{site_name}}] Confirm your new email address',
195 'body' => self::htmlAccountEmailChangeRequest(),
196 ],
197 'account_email_changed' => [
198 'subject' => '🔔 [{{site_name}}] Your email address was changed',
199 'body' => self::htmlAccountEmailChanged(),
200 ],
201 'booking_expired_customer' => [
202 'subject' => '⏱️ [{{site_name}}] Booking expired · {{booking_reference}}',
203 'body' => self::htmlBookingExpiredCustomer(),
204 ],
205 'admin_booking_expired' => [
206 'subject' => '⏱️ [{{site_name}}] Booking expired · {{booking_reference}} (#{{booking_id}})',
207 'body' => self::htmlAdminBookingExpired(),
208 ],
209 'scheduled_payment_reminder' => [
210 'subject' => '💳 [{{site_name}}] Upcoming payment · {{booking_reference}}',
211 'body' => self::htmlScheduledPaymentReminder(),
212 ],
213 'scheduled_payment_succeeded' => [
214 'subject' => '�
215 [{{site_name}}] Scheduled payment received · {{booking_reference}}',
216 'body' => self::htmlScheduledPaymentSucceeded(),
217 ],
218 'scheduled_payment_failed' => [
219 'subject' => '⚠️ [{{site_name}}] Payment issue · {{booking_reference}}',
220 'body' => self::htmlScheduledPaymentFailed(),
221 ],
222 'admin_scheduled_payment_failed' => [
223 'subject' => '⚠️ [{{site_name}}] Scheduled payment failed · {{booking_reference}}',
224 'body' => self::htmlAdminScheduledPaymentFailed(),
225 ],
226 'abandoned_booking_recovery_first' => [
227 'subject' => '🛒 [{{site_name}}] Complete your booking',
228 'body' => self::htmlAbandonedBookingRecoveryFirst(),
229 ],
230 'abandoned_booking_recovery_second' => [
231 'subject' => ' [{{site_name}}] Still interested? Your booking is waiting',
232 'body' => self::htmlAbandonedBookingRecoverySecond(),
233 ],
234 'abandoned_booking_recovery_final' => [
235 'subject' => '⚠️ [{{site_name}}] Final reminder: complete your booking',
236 'body' => self::htmlAbandonedBookingRecoveryFinal(),
237 ],
238 ];
239
240 return $map[$templateKey] ?? null;
241 }
242
243 /**
244 * Fallback HTML when settings templates are empty (escaped runtime variables).
245 *
246 * @param array<string, string> $v
247 */
248 public static function fallbackTransactionalBooking(array $v): string
249 {
250 $name = esc_html($v['customer_first_name'] ?: $v['customer_name'] ?: __('there', 'yatra'));
251 $site = esc_html($v['site_name'] ?? get_bloginfo('name'));
252
253 if (!empty($v['details_html_only'])) {
254 $inner = '<p style="margin:0 0 16px;font-size:17px;color:#0f172a;">'
255 . sprintf(
256 /* translators: %s: customer name */
257 esc_html__('Hello %s,', 'yatra'),
258 $name
259 )
260 . '</p>'
261 . '<p style="margin:0 0 20px;color:#475569;">'
262 . esc_html($v['intro_paragraph'] ?? __('Thank you for your booking. Here are your details:', 'yatra'))
263 . '</p>'
264 . wp_kses_post((string) ($v['details_html'] ?? ''))
265 . (!empty($v['footer_note']) ? '<p style="margin:24px 0 0;color:#475569;">' . wp_kses_post((string) $v['footer_note']) . '</p>' : '');
266
267 return EmailTemplateLayout::customer(
268 '✈️',
269 __('Booking update', 'yatra'),
270 $inner,
271 $site,
272 __('Your booking details are inside.', 'yatra')
273 );
274 }
275
276 $rows = array_values(array_filter([
277 ['label' => __('Reference', 'yatra'), 'value' => esc_html($v['booking_reference'] ?? '')],
278 ['label' => __('Trip', 'yatra'), 'value' => esc_html($v['trip_name'] ?? '')],
279 ['label' => __('Travel date', 'yatra'), 'value' => esc_html($v['travel_date'] ?? '')],
280 ['label' => __('Travelers', 'yatra'), 'value' => esc_html($v['travelers_count'] ?? '')],
281 ['label' => __('Total', 'yatra'), 'value' => esc_html($v['total_amount_formatted'] ?? '')],
282 ['label' => __('Amount due now', 'yatra'), 'value' => esc_html($v['amount_due_formatted'] ?? '')],
283 ], static function (array $row): bool {
284 return $row['value'] !== '';
285 }));
286
287 $inner = '<p style="margin:0 0 16px;font-size:17px;color:#0f172a;">'
288 . sprintf(
289 /* translators: %s: customer name */
290 esc_html__('Hello %s,', 'yatra'),
291 $name
292 )
293 . '</p>'
294 . '<p style="margin:0 0 8px;color:#475569;">'
295 . esc_html($v['intro_paragraph'] ?? __('Thank you for your booking. Here are your details:', 'yatra'))
296 . '</p>'
297 . EmailTemplateLayout::detailCard($rows)
298 . (!empty($v['details_html']) ? '<div style="margin:16px 0 0;">' . wp_kses_post((string) $v['details_html']) . '</div>' : '')
299 . (!empty($v['footer_note']) ? '<p style="margin:24px 0 0;color:#475569;">' . wp_kses_post((string) $v['footer_note']) . '</p>' : '');
300
301 return EmailTemplateLayout::customer(
302 '✈️',
303 __('Booking update', 'yatra'),
304 $inner,
305 $site,
306 __('Your booking details are inside.', 'yatra')
307 );
308 }
309
310 /**
311 * @param array<string, string> $v
312 */
313 public static function fallbackTransactionalPayment(array $v): string
314 {
315 $name = esc_html($v['customer_first_name'] ?: $v['customer_name'] ?: __('Customer', 'yatra'));
316 $site = esc_html($v['site_name'] ?? get_bloginfo('name'));
317 $rows = array_values(array_filter([
318 ['label' => __('Booking reference', 'yatra'), 'value' => esc_html($v['booking_reference'] ?? '')],
319 ['label' => __('Amount paid', 'yatra'), 'value' => esc_html($v['payment_amount_formatted'] ?? '')],
320 ['label' => __('Method', 'yatra'), 'value' => esc_html($v['payment_method'] ?? '')],
321 ['label' => __('Transaction ID', 'yatra'), 'value' => esc_html($v['transaction_id'] ?? '')],
322 ], static function (array $row): bool {
323 return $row['value'] !== '';
324 }));
325
326 $inner = '<p style="margin:0 0 16px;font-size:17px;color:#0f172a;">'
327 . sprintf(
328 /* translators: %s: customer name */
329 esc_html__('Dear %s,', 'yatra'),
330 $name
331 )
332 . '</p>'
333 . '<p style="margin:0 0 8px;color:#475569;">'
334 . esc_html__('We’ve received your payment thank you! Here’s a quick summary.', 'yatra')
335 . '</p>'
336 . EmailTemplateLayout::detailCard($rows);
337
338 return EmailTemplateLayout::customer(
339 '�
340 ',
341 __('Payment received', 'yatra'),
342 $inner,
343 $site,
344 __('Payment confirmation', 'yatra')
345 );
346 }
347
348 /**
349 * Part-payment received — a balance is still outstanding.
350 *
351 * Wording differs from the full-payment email on purpose: it confirms what
352 * was received AND states what is still owed, which is the whole reason this
353 * template is separate.
354 *
355 * @param array<string, string> $v
356 */
357 public static function fallbackTransactionalPartialPayment(array $v): string
358 {
359 // Null-coalescing rather than `?:` so this renders cleanly even when a
360 // caller passes a sparse variable set — this fallback runs on sites that
361 // upgraded before the template's body option was seeded.
362 $name = esc_html(
363 ($v['customer_first_name'] ?? '') ?: ($v['customer_name'] ?? '') ?: __('Customer', 'yatra')
364 );
365 $site = esc_html($v['site_name'] ?? get_bloginfo('name'));
366 $rows = array_values(array_filter([
367 ['label' => __('Booking reference', 'yatra'), 'value' => esc_html($v['booking_reference'] ?? '')],
368 ['label' => __('Amount received', 'yatra'), 'value' => esc_html($v['payment_amount_formatted'] ?? '')],
369 ['label' => __('Paid so far', 'yatra'), 'value' => esc_html($v['amount_paid_formatted'] ?? '')],
370 ['label' => __('Balance remaining', 'yatra'), 'value' => esc_html($v['amount_due_formatted'] ?? '')],
371 ['label' => __('Booking total', 'yatra'), 'value' => esc_html($v['total_amount_formatted'] ?? '')],
372 ['label' => __('Method', 'yatra'), 'value' => esc_html($v['payment_method'] ?? '')],
373 ['label' => __('Transaction ID', 'yatra'), 'value' => esc_html($v['transaction_id'] ?? '')],
374 ], static function (array $row): bool {
375 return $row['value'] !== '';
376 }));
377
378 $inner = '<p style="margin:0 0 16px;font-size:17px;color:#0f172a;">'
379 . sprintf(
380 /* translators: %s: customer name */
381 esc_html__('Dear %s,', 'yatra'),
382 $name
383 )
384 . '</p>'
385 . '<p style="margin:0 0 8px;color:#475569;">'
386 . esc_html__('Thank you — we’ve received your part payment. Your booking is not yet paid in full, so the remaining balance is shown below.', 'yatra')
387 . '</p>'
388 . EmailTemplateLayout::detailCard($rows);
389
390 return EmailTemplateLayout::customer(
391 '💳',
392 __('Part payment received', 'yatra'),
393 $inner,
394 $site,
395 __('Payment update', 'yatra')
396 );
397 }
398
399 /**
400 * @param array<string, string> $v
401 */
402 public static function fallbackTransactionalCancellation(array $v): string
403 {
404 $name = esc_html($v['customer_first_name'] ?: $v['customer_name'] ?: __('Customer', 'yatra'));
405 $site = esc_html($v['site_name'] ?? get_bloginfo('name'));
406 $rows = array_values(array_filter([
407 ['label' => __('Reference', 'yatra'), 'value' => esc_html($v['booking_reference'] ?? '')],
408 ['label' => __('Trip', 'yatra'), 'value' => esc_html($v['trip_name'] ?? '')],
409 ['label' => __('Travel date', 'yatra'), 'value' => esc_html($v['travel_date'] ?? '')],
410 ], static function (array $row): bool {
411 return $row['value'] !== '';
412 }));
413
414 $inner = '<p style="margin:0 0 16px;font-size:17px;color:#0f172a;">'
415 . sprintf(
416 /* translators: %s: customer name */
417 esc_html__('Hello %s,', 'yatra'),
418 $name
419 )
420 . '</p>'
421 . '<p style="margin:0 0 20px;color:#475569;">'
422 . esc_html__('Your booking has been cancelled as requested. If you have questions, just reply to this email.', 'yatra')
423 . '</p>'
424 . EmailTemplateLayout::detailCard($rows);
425
426 return EmailTemplateLayout::customer(
427 '📋',
428 __('Booking cancelled', 'yatra'),
429 $inner,
430 $site,
431 __('Booking cancellation notice', 'yatra')
432 );
433 }
434
435 /**
436 * @param array<string, string> $v
437 */
438 public static function fallbackTransactionalReminder(array $v): string
439 {
440 $name = esc_html($v['customer_name'] ?: __('Traveler', 'yatra'));
441 $site = esc_html($v['site_name'] ?? get_bloginfo('name'));
442 $days = esc_html((string) ($v['reminder_days'] ?? $v['days_until_trip'] ?? ''));
443
444 $rows = array_values(array_filter([
445 ['label' => __('Reference', 'yatra'), 'value' => esc_html($v['booking_reference'] ?? '')],
446 ['label' => __('Trip', 'yatra'), 'value' => esc_html($v['trip_name'] ?? '')],
447 ['label' => __('Travel date', 'yatra'), 'value' => esc_html($v['travel_date'] ?? '')],
448 ['label' => __('Travelers', 'yatra'), 'value' => esc_html($v['travelers_count'] ?? '')],
449 ], static function (array $row): bool {
450 return $row['value'] !== '';
451 }));
452
453 $balanceRow = (!empty($v['amount_due_formatted']) && isset($v['amount_due_formatted']) && $v['amount_due_formatted'] !== yatra_format_price(0))
454 ? [['label' => __('Balance due', 'yatra'), 'value' => esc_html((string) $v['amount_due_formatted'])]]
455 : [];
456 if ($balanceRow !== []) {
457 $rows = array_merge($rows, $balanceRow);
458 }
459
460 $inner = '<p style="margin:0 0 16px;font-size:17px;color:#0f172a;">'
461 . sprintf(
462 /* translators: %s: customer name */
463 esc_html__('Dear %s,', 'yatra'),
464 $name
465 )
466 . '</p>'
467 . '<p style="margin:0 0 20px;color:#475569;">'
468 . ($days !== ''
469 ? sprintf(
470 /* translators: %s: number of days */
471 esc_html__('Your adventure starts in %s days — we can’t wait to see you!', 'yatra'),
472 $days
473 )
474 : esc_html__('Your trip is coming up soon — we look forward to seeing you!', 'yatra'))
475 . '</p>'
476 . EmailTemplateLayout::detailCard($rows)
477 . (!empty($v['reminder_extra_html']) ? '<div style="margin:20px 0 0;">' . wp_kses_post($v['reminder_extra_html']) . '</div>' : '');
478
479 return EmailTemplateLayout::customer(
480 '🗓️',
481 __('Trip reminder', 'yatra'),
482 $inner,
483 $site,
484 __('Upcoming trip reminder', 'yatra')
485 );
486 }
487
488 /**
489 * @param array<string, string> $v
490 */
491 public static function fallbackTransactionalTripConsent(array $v): string
492 {
493 $recipient = esc_html($v['recipient_name'] ?: __('Traveler', 'yatra'));
494 $site = esc_html($v['site_name'] ?? get_bloginfo('name'));
495 $formNameRaw = (string) ($v['form_name'] ?? '');
496 $formNameEsc = esc_html($formNameRaw);
497 $link = esc_url((string) ($v['consent_link'] ?? home_url('/')));
498 $rows = array_values(array_filter([
499 ['label' => __('Form', 'yatra'), 'value' => $formNameEsc],
500 ['label' => __('Trip', 'yatra'), 'value' => esc_html($v['trip_name'] ?? '')],
501 ['label' => __('Travel date', 'yatra'), 'value' => esc_html($v['travel_date'] ?? '')],
502 ['label' => __('Booking reference', 'yatra'), 'value' => esc_html($v['booking_reference'] ?? '')],
503 ], static function (array $row): bool {
504 return $row['value'] !== '';
505 }));
506
507 $notice = !empty($v['expiry_notice_html'])
508 ? '<div style="margin:16px 0 0;padding:12px 16px;background:#fff7ed;border:1px solid #fed7aa;border-radius:10px;font-size:14px;color:#9a3412;">'
509 . wp_kses_post((string) $v['expiry_notice_html'])
510 . '</div>'
511 : '';
512
513 $testBlock = !empty($v['consent_test_notice_html'])
514 ? '<div style="margin:0 0 20px;padding:12px 16px;background:#f0fdfa;border:1px solid #99f6e4;border-radius:10px;font-size:14px;color:#115e59;">'
515 . wp_kses_post((string) $v['consent_test_notice_html'])
516 . '</div>'
517 : '';
518
519 $inner = $testBlock
520 . '<p style="margin:0 0 16px;font-size:17px;color:#0f172a;">'
521 . sprintf(
522 /* translators: %s: customer name */
523 esc_html__('Hello %s,', 'yatra'),
524 $recipient
525 )
526 . '</p>'
527 . '<p style="margin:0 0 20px;color:#475569;">'
528 . sprintf(
529 /* translators: %s: consent form title */
530 esc_html__('Please complete and sign “%s” before your trip.', 'yatra'),
531 $formNameRaw !== '' ? $formNameEsc : esc_html__('your consent form', 'yatra')
532 )
533 . '</p>'
534 . EmailTemplateLayout::detailCard($rows)
535 . $notice
536 . EmailTemplateLayout::button($link, __('Sign consent form', 'yatra'))
537 . '<p style="margin:20px 0 0;font-size:13px;color:#64748b;">'
538 . esc_html__('If the button does not work, copy this link into your browser:', 'yatra')
539 . '</p>'
540 . '<p style="margin:8px 0 0;font-size:13px;word-break:break-all;"><a href="' . $link . '" style="color:#0d9488;">' . $link . '</a></p>';
541
542 return EmailTemplateLayout::customer(
543 '📝',
544 __('Consent required', 'yatra'),
545 $inner,
546 $site,
547 __('Sign your trip consent form', 'yatra')
548 );
549 }
550
551 /**
552 * @param array<string, string> $v
553 */
554 public static function fallbackTransactionalCustomerEmailVerification(array $v): string
555 {
556 $firstRaw = (string) ($v['customer_first_name'] ?? '');
557 if ($firstRaw === '') {
558 $firstRaw = (string) ($v['customer_name'] ?? '');
559 }
560 $first = esc_html($firstRaw !== '' ? $firstRaw : __('there', 'yatra'));
561 $site = esc_html($v['site_name'] ?? get_bloginfo('name'));
562 $link = esc_url((string) ($v['verification_link'] ?? home_url('/')));
563 $intro = isset($v['intro_paragraph']) ? esc_html((string) $v['intro_paragraph']) : '';
564 $footer = isset($v['footer_note']) ? esc_html((string) $v['footer_note']) : '';
565 $expiryInner = !empty($v['expiry_notice_html'])
566 ? wp_kses_post((string) $v['expiry_notice_html'])
567 : esc_html(
568 sprintf(
569 /* translators: %d: hours until link expiry */
570 __('This verification link expires in %d hours for your security.', 'yatra'),
571 24
572 )
573 );
574 $expiry = '<div style="margin:20px 0 0;padding:12px 16px;background:#f8fafc;border:1px solid #e2e8f0;border-radius:10px;font-size:14px;color:#475569;">'
575 . $expiryInner
576 . '</div>';
577
578 $inner = '<p style="margin:0 0 16px;font-size:17px;color:#0f172a;">'
579 . sprintf(
580 /* translators: %s: customer name */
581 esc_html__('Hello %s,', 'yatra'),
582 $first
583 )
584 . '</p>'
585 . ($intro !== '' ? '<p style="margin:0 0 20px;color:#475569;">' . $intro . '</p>' : '')
586 . EmailTemplateLayout::button($link, __('Verify email address', 'yatra'))
587 . $expiry
588 . ($footer !== '' ? '<p style="margin:24px 0 0;font-size:14px;color:#64748b;">' . $footer . '</p>' : '')
589 . '<p style="margin:20px 0 0;font-size:13px;color:#64748b;">'
590 . esc_html__('If the button does not work, copy this link into your browser:', 'yatra')
591 . '</p>'
592 . '<p style="margin:8px 0 0;font-size:13px;word-break:break-all;"><a href="' . $link . '" style="color:#0d9488;">' . $link . '</a></p>';
593
594 return EmailTemplateLayout::customer(
595 '✉️',
596 __('Verify your email', 'yatra'),
597 $inner,
598 $site,
599 __('Account verification', 'yatra')
600 );
601 }
602
603 /**
604 * Fallback HTML for the account email-change confirmation (empty saved template).
605 *
606 * @param array<string, string> $v
607 */
608 public static function fallbackTransactionalAccountEmailChangeRequest(array $v): string
609 {
610 $firstRaw = (string) ($v['customer_first_name'] ?? '');
611 if ($firstRaw === '') {
612 $firstRaw = (string) ($v['customer_name'] ?? '');
613 }
614 $first = esc_html($firstRaw !== '' ? $firstRaw : __('there', 'yatra'));
615 $site = esc_html($v['site_name'] ?? get_bloginfo('name'));
616 $link = esc_url((string) ($v['verification_link'] ?? home_url('/')));
617 $newEmail = esc_html((string) ($v['new_email'] ?? ''));
618 $intro = isset($v['intro_paragraph']) ? esc_html((string) $v['intro_paragraph']) : '';
619 $footer = isset($v['footer_note']) ? esc_html((string) $v['footer_note']) : '';
620
621 $newBlock = $newEmail !== ''
622 ? '<div style="margin:0 0 20px;padding:12px 16px;background:#f8fafc;border:1px solid #e2e8f0;border-radius:10px;font-size:14px;color:#475569;">'
623 . esc_html__('New email address:', 'yatra') . ' <strong>' . $newEmail . '</strong></div>'
624 : '';
625
626 $inner = '<p style="margin:0 0 16px;font-size:17px;color:#0f172a;">'
627 . sprintf(/* translators: %s: customer name */ esc_html__('Hello %s,', 'yatra'), $first)
628 . '</p>'
629 . ($intro !== '' ? '<p style="margin:0 0 20px;color:#475569;">' . $intro . '</p>' : '')
630 . $newBlock
631 . EmailTemplateLayout::button($link, __('Confirm email change', 'yatra'))
632 . ($footer !== '' ? '<p style="margin:24px 0 0;font-size:14px;color:#64748b;">' . $footer . '</p>' : '')
633 . '<p style="margin:20px 0 0;font-size:13px;color:#64748b;">'
634 . esc_html__('If the button does not work, copy this link into your browser:', 'yatra')
635 . '</p>'
636 . '<p style="margin:8px 0 0;font-size:13px;word-break:break-all;"><a href="' . $link . '" style="color:#0d9488;">' . $link . '</a></p>';
637
638 return EmailTemplateLayout::customer(
639 '✉️',
640 __('Confirm your new email', 'yatra'),
641 $inner,
642 $site,
643 __('Account security', 'yatra')
644 );
645 }
646
647 /**
648 * Fallback HTML for the "email changed" security notice (empty saved template).
649 *
650 * @param array<string, string> $v
651 */
652 public static function fallbackTransactionalAccountEmailChanged(array $v): string
653 {
654 $firstRaw = (string) ($v['customer_first_name'] ?? '');
655 if ($firstRaw === '') {
656 $firstRaw = (string) ($v['customer_name'] ?? '');
657 }
658 $first = esc_html($firstRaw !== '' ? $firstRaw : __('there', 'yatra'));
659 $site = esc_html($v['site_name'] ?? get_bloginfo('name'));
660 $newEmail = esc_html((string) ($v['new_email'] ?? ''));
661 $intro = isset($v['intro_paragraph']) ? esc_html((string) $v['intro_paragraph']) : '';
662 $footer = isset($v['footer_note']) ? esc_html((string) $v['footer_note']) : '';
663
664 $newBlock = $newEmail !== ''
665 ? '<div style="margin:0 0 20px;padding:12px 16px;background:#f8fafc;border:1px solid #e2e8f0;border-radius:10px;font-size:14px;color:#475569;">'
666 . esc_html__('New email address:', 'yatra') . ' <strong>' . $newEmail . '</strong></div>'
667 : '';
668
669 $inner = '<p style="margin:0 0 16px;font-size:17px;color:#0f172a;">'
670 . sprintf(/* translators: %s: customer name */ esc_html__('Hello %s,', 'yatra'), $first)
671 . '</p>'
672 . ($intro !== '' ? '<p style="margin:0 0 20px;color:#475569;">' . $intro . '</p>' : '')
673 . $newBlock
674 . ($footer !== '' ? '<p style="margin:24px 0 0;font-size:14px;color:#64748b;">' . $footer . '</p>' : '');
675
676 return EmailTemplateLayout::customer(
677 '🔔',
678 __('Your email address was changed', 'yatra'),
679 $inner,
680 $site,
681 __('Account security', 'yatra')
682 );
683 }
684
685 /**
686 * Fallback HTML for admin new-booking notice (empty saved template).
687 *
688 * @param array<string, string> $v
689 */
690 /**
691 * Fallback HTML for admin payment-received notice.
692 *
693 * @param array<string, string> $v
694 */
695 public static function fallbackAdminPaymentReceived(array $v): string
696 {
697 $site = esc_html($v['site_name'] ?? get_bloginfo('name'));
698 $rows = array_values(array_filter([
699 ['label' => __('Booking ID', 'yatra'), 'value' => esc_html($v['booking_id'] ?? '')],
700 ['label' => __('Reference', 'yatra'), 'value' => esc_html($v['booking_reference'] ?? '')],
701 ['label' => __('Customer', 'yatra'), 'value' => esc_html($v['customer_name'] ?? '')],
702 ['label' => __('Amount', 'yatra'), 'value' => esc_html($v['payment_amount_formatted'] ?? '')],
703 ['label' => __('Method', 'yatra'), 'value' => esc_html($v['payment_method'] ?? '')],
704 ['label' => __('Transaction ID', 'yatra'), 'value' => esc_html($v['transaction_id'] ?? '')],
705 ], static function (array $row): bool {
706 return trim((string) ($row['value'] ?? '')) !== '';
707 }));
708
709 $adminUrl = esc_url($v['admin_url'] ?? admin_url('admin.php?page=yatra'));
710
711 $inner = '<p style="margin:0 0 20px;color:#475569;">'
712 . esc_html__('A payment was recorded for the booking below.', 'yatra')
713 . '</p>'
714 . EmailTemplateLayout::detailCard($rows)
715 . '<table role="presentation" cellpadding="0" cellspacing="0" border="0" style="margin:28px 0 0;"><tr>'
716 . '<td style="border-radius:10px;background:#2563eb;">'
717 . '<a href="' . $adminUrl . '" style="display:inline-block;padding:14px 24px;font-family:\'Segoe UI\',Roboto,Arial,sans-serif;font-size:15px;font-weight:600;color:#ffffff;text-decoration:none;border-radius:10px;">'
718 . esc_html__('Open in Yatra admin', 'yatra')
719 . '</a></td></tr></table>';
720
721 return EmailTemplateLayout::admin(
722 '�
723 ',
724 __('Payment received', 'yatra'),
725 $inner,
726 $site . ' · ' . esc_html__('Admin notification', 'yatra')
727 );
728 }
729
730 /**
731 * Fallback HTML for admin booking-cancelled notice.
732 *
733 * @param array<string, string> $v
734 */
735 public static function fallbackAdminBookingCancelled(array $v): string
736 {
737 $site = esc_html($v['site_name'] ?? get_bloginfo('name'));
738 $rows = array_values(array_filter([
739 ['label' => __('Booking ID', 'yatra'), 'value' => esc_html($v['booking_id'] ?? '')],
740 ['label' => __('Reference', 'yatra'), 'value' => esc_html($v['booking_reference'] ?? '')],
741 ['label' => __('Customer', 'yatra'), 'value' => esc_html($v['customer_name'] ?? '')],
742 ['label' => __('Trip', 'yatra'), 'value' => esc_html($v['trip_name'] ?? '')],
743 ['label' => __('Travel date', 'yatra'), 'value' => esc_html($v['travel_date'] ?? '')],
744 ], static function (array $row): bool {
745 return trim((string) ($row['value'] ?? '')) !== '';
746 }));
747
748 $adminUrl = esc_url($v['admin_url'] ?? admin_url('admin.php?page=yatra'));
749
750 $inner = '<p style="margin:0 0 20px;color:#475569;">'
751 . esc_html__('A booking was cancelled. Summary below.', 'yatra')
752 . '</p>'
753 . EmailTemplateLayout::detailCard($rows)
754 . '<table role="presentation" cellpadding="0" cellspacing="0" border="0" style="margin:28px 0 0;"><tr>'
755 . '<td style="border-radius:10px;background:#2563eb;">'
756 . '<a href="' . $adminUrl . '" style="display:inline-block;padding:14px 24px;font-family:\'Segoe UI\',Roboto,Arial,sans-serif;font-size:15px;font-weight:600;color:#ffffff;text-decoration:none;border-radius:10px;">'
757 . esc_html__('Open in Yatra admin', 'yatra')
758 . '</a></td></tr></table>';
759
760 return EmailTemplateLayout::admin(
761 '📋',
762 __('Booking cancelled', 'yatra'),
763 $inner,
764 $site . ' · ' . esc_html__('Admin notification', 'yatra')
765 );
766 }
767
768 public static function fallbackAdminNewBooking(array $v): string
769 {
770 $site = esc_html($v['site_name'] ?? get_bloginfo('name'));
771 $rows = array_values(array_filter([
772 ['label' => __('Reference', 'yatra'), 'value' => esc_html($v['booking_reference'] ?? '')],
773 ['label' => __('Booking ID', 'yatra'), 'value' => esc_html($v['booking_id'] ?? '')],
774 ['label' => __('Customer', 'yatra'), 'value' => esc_html($v['customer_name'] ?? '')],
775 ['label' => __('Email', 'yatra'), 'value' => esc_html($v['customer_email'] ?? '')],
776 ['label' => __('Phone', 'yatra'), 'value' => esc_html($v['customer_phone'] ?? '')],
777 ['label' => __('Trip', 'yatra'), 'value' => esc_html($v['trip_name'] ?? '')],
778 ['label' => __('Travel date', 'yatra'), 'value' => esc_html($v['travel_date'] ?? '')],
779 ['label' => __('Travelers', 'yatra'), 'value' => esc_html($v['travelers_count'] ?? '')],
780 ['label' => __('Total', 'yatra'), 'value' => esc_html($v['total_amount_formatted'] ?? '')],
781 ['label' => __('Booking status', 'yatra'), 'value' => esc_html($v['booking_status'] ?? '')],
782 ['label' => __('Payment status', 'yatra'), 'value' => esc_html($v['payment_status'] ?? '')],
783 ], static function (array $row): bool {
784 return trim((string) ($row['value'] ?? '')) !== '';
785 }));
786
787 $adminUrl = esc_url($v['admin_url'] ?? admin_url('admin.php?page=yatra'));
788 $siteUrl = esc_url($v['site_url'] ?? home_url('/'));
789
790 $inner = '<p style="margin:0 0 20px;color:#475569;">'
791 . esc_html__('A new booking just landed. Review the snapshot below and open the admin to take action.', 'yatra')
792 . '</p>'
793 . EmailTemplateLayout::detailCard($rows)
794 . '<table role="presentation" cellpadding="0" cellspacing="0" border="0" style="margin:28px 0 0;"><tr>'
795 . '<td style="border-radius:10px;background:#2563eb;">'
796 . '<a href="' . $adminUrl . '" style="display:inline-block;padding:14px 24px;font-family:\'Segoe UI\',Roboto,Arial,sans-serif;font-size:15px;font-weight:600;color:#ffffff;text-decoration:none;border-radius:10px;">'
797 . esc_html__('Open in Yatra admin', 'yatra')
798 . '</a></td></tr></table>'
799 . '<p style="margin:20px 0 0;font-size:13px;color:#64748b;">'
800 . esc_html__('Site:', 'yatra') . ' <a href="' . $siteUrl . '" style="color:#2563eb;">' . esc_html($siteUrl) . '</a>'
801 . '</p>';
802
803 return EmailTemplateLayout::admin(
804 '🔔',
805 __('New booking', 'yatra'),
806 $inner,
807 $site . ' · ' . esc_html__('Admin notification', 'yatra')
808 );
809 }
810
811 private static function htmlBookingCustomer(): string
812 {
813 $inner = '<p style="margin:0 0 16px;font-size:17px;color:#0f172a;">Hello {{customer_name}},</p>'
814 . '<p style="margin:0 0 20px;color:#475569;">{{intro_paragraph}}</p>'
815 . EmailTemplateLayout::detailCard([
816 ['label' => 'Reference', 'value' => '{{booking_reference}}'],
817 ['label' => 'Trip', 'value' => '{{trip_name}}'],
818 ['label' => 'Travel date', 'value' => '{{travel_date}}'],
819 ['label' => 'Travelers', 'value' => '{{travelers_count}}'],
820 ['label' => 'Total', 'value' => '{{total_amount_formatted}}'],
821 ['label' => 'Amount due now', 'value' => '{{amount_due_formatted}}'],
822 ])
823 . '{{details_html}}'
824 . '<p style="margin:24px 0 0;color:#475569;">{{footer_note}}</p>'
825 . '<p style="margin:20px 0 0;"><a href="{{trip_url}}" style="color:#0d9488;font-weight:600;text-decoration:none;">' . esc_html__('View trip details ', 'yatra') . '</a></p>';
826
827 return EmailTemplateLayout::customer(
828 '✈️',
829 'Booking update',
830 $inner,
831 '{{site_name}}',
832 'Booking update'
833 );
834 }
835
836 private static function htmlBookingConfirmed(): string
837 {
838 $inner = '<p style="margin:0 0 16px;font-size:17px;color:#0f172a;">Hello {{customer_name}},</p>'
839 . '<p style="margin:0 0 20px;color:#475569;">'
840 . esc_html__('Great news your booking is confirmed. Save this email for your records.', 'yatra')
841 . '</p>'
842 . EmailTemplateLayout::detailCard([
843 ['label' => 'Reference', 'value' => '{{booking_reference}}'],
844 ['label' => 'Trip', 'value' => '{{trip_name}}'],
845 ['label' => 'Travel date', 'value' => '{{travel_date}}'],
846 ['label' => 'Travelers', 'value' => '{{travelers_count}}'],
847 ['label' => 'Total', 'value' => '{{total_amount_formatted}}'],
848 ])
849 . '<p style="margin:20px 0 0;"><a href="{{trip_url}}" style="color:#0d9488;font-weight:600;text-decoration:none;">' . esc_html__('Trip page ', 'yatra') . '</a></p>';
850
851 return EmailTemplateLayout::customer(
852 '🎉',
853 __("You're all set", 'yatra'),
854 $inner,
855 '{{site_name}}',
856 'Booking confirmed'
857 );
858 }
859
860 private static function htmlAdminPaymentReceived(): string
861 {
862 $inner = '<p style="margin:0 0 20px;color:#475569;">'
863 . esc_html__('A payment was recorded for the booking below.', 'yatra')
864 . '</p>'
865 . EmailTemplateLayout::detailCard([
866 ['label' => 'Booking ID', 'value' => '{{booking_id}}'],
867 ['label' => 'Reference', 'value' => '{{booking_reference}}'],
868 ['label' => 'Customer', 'value' => '{{customer_name}}'],
869 ['label' => 'Amount', 'value' => '{{payment_amount_formatted}}'],
870 ['label' => 'Method', 'value' => '{{payment_method}}'],
871 ['label' => 'Transaction ID', 'value' => '{{transaction_id}}'],
872 ])
873 . '<table role="presentation" cellpadding="0" cellspacing="0" border="0" style="margin:28px 0 0;"><tr>'
874 . '<td style="border-radius:10px;background:#2563eb;">'
875 . '<a href="{{admin_url}}" style="display:inline-block;padding:14px 24px;font-family:\'Segoe UI\',Roboto,Arial,sans-serif;font-size:15px;font-weight:600;color:#ffffff;text-decoration:none;border-radius:10px;">'
876 . esc_html__('Open in Yatra admin', 'yatra')
877 . '</a></td></tr></table>';
878
879 return EmailTemplateLayout::admin(
880 '�
881 ',
882 'Payment received',
883 $inner,
884 '{{site_name}} · ' . esc_html__('Admin notification', 'yatra')
885 );
886 }
887
888 private static function htmlAdminBookingCancelled(): string
889 {
890 $inner = '<p style="margin:0 0 20px;color:#475569;">'
891 . esc_html__('A booking was cancelled. Summary below.', 'yatra')
892 . '</p>'
893 . EmailTemplateLayout::detailCard([
894 ['label' => 'Booking ID', 'value' => '{{booking_id}}'],
895 ['label' => 'Reference', 'value' => '{{booking_reference}}'],
896 ['label' => 'Customer', 'value' => '{{customer_name}}'],
897 ['label' => 'Trip', 'value' => '{{trip_name}}'],
898 ['label' => 'Travel date', 'value' => '{{travel_date}}'],
899 ])
900 . '<table role="presentation" cellpadding="0" cellspacing="0" border="0" style="margin:28px 0 0;"><tr>'
901 . '<td style="border-radius:10px;background:#2563eb;">'
902 . '<a href="{{admin_url}}" style="display:inline-block;padding:14px 24px;font-family:\'Segoe UI\',Roboto,Arial,sans-serif;font-size:15px;font-weight:600;color:#ffffff;text-decoration:none;border-radius:10px;">'
903 . esc_html__('Open in Yatra admin', 'yatra')
904 . '</a></td></tr></table>';
905
906 return EmailTemplateLayout::admin(
907 '📋',
908 'Booking cancelled',
909 $inner,
910 '{{site_name}} · ' . esc_html__('Admin notification', 'yatra')
911 );
912 }
913
914 private static function htmlAdminNewBooking(): string
915 {
916 $inner = '<p style="margin:0 0 20px;color:#475569;">'
917 . esc_html__('A new booking just landed. Review the snapshot below and open the admin to take action.', 'yatra')
918 . '</p>'
919 . EmailTemplateLayout::detailCard([
920 ['label' => 'Reference', 'value' => '{{booking_reference}}'],
921 ['label' => 'Booking ID', 'value' => '{{booking_id}}'],
922 ['label' => 'Customer', 'value' => '{{customer_name}}'],
923 ['label' => 'Email', 'value' => '{{customer_email}}'],
924 ['label' => 'Phone', 'value' => '{{customer_phone}}'],
925 ['label' => 'Trip', 'value' => '{{trip_name}}'],
926 ['label' => 'Travel date', 'value' => '{{travel_date}}'],
927 ['label' => 'Travelers', 'value' => '{{travelers_count}}'],
928 ['label' => 'Total', 'value' => '{{total_amount_formatted}}'],
929 ['label' => 'Booking status', 'value' => '{{booking_status}}'],
930 ['label' => 'Payment status', 'value' => '{{payment_status}}'],
931 ])
932 . '<table role="presentation" cellpadding="0" cellspacing="0" border="0" style="margin:28px 0 0;"><tr>'
933 . '<td style="border-radius:10px;background:#2563eb;">'
934 . '<a href="{{admin_url}}" style="display:inline-block;padding:14px 24px;font-family:\'Segoe UI\',Roboto,Arial,sans-serif;font-size:15px;font-weight:600;color:#ffffff;text-decoration:none;border-radius:10px;">'
935 . esc_html__('Open in Yatra admin', 'yatra')
936 . '</a></td></tr></table>'
937 . '<p style="margin:20px 0 0;font-size:13px;color:#64748b;">'
938 . esc_html__('Site:', 'yatra') . ' <a href="{{site_url}}" style="color:#2563eb;">{{site_url}}</a>'
939 . '</p>';
940
941 return EmailTemplateLayout::admin(
942 '🔔',
943 'New booking',
944 $inner,
945 '{{site_name}} · ' . esc_html__('Admin notification', 'yatra')
946 );
947 }
948
949 private static function htmlPaymentCustomer(): string
950 {
951 $inner = '<p style="margin:0 0 16px;font-size:17px;color:#0f172a;">Dear {{customer_name}},</p>'
952 . '<p style="margin:0 0 20px;color:#475569;">'
953 . esc_html__('We’ve successfully received your payment. Thank you you’re one step closer to takeoff!', 'yatra')
954 . '</p>'
955 . EmailTemplateLayout::detailCard([
956 ['label' => 'Booking reference', 'value' => '{{booking_reference}}'],
957 ['label' => 'Amount paid', 'value' => '{{payment_amount_formatted}}'],
958 ['label' => 'Method', 'value' => '{{payment_method}}'],
959 ['label' => 'Transaction ID', 'value' => '{{transaction_id}}'],
960 ]);
961
962 return EmailTemplateLayout::customer(
963 '�
964 ',
965 'Payment received',
966 $inner,
967 '{{site_name}}',
968 'Payment received'
969 );
970 }
971
972 /**
973 * Editor pre-fill for the part-payment template ({{merge tags}} intact).
974 */
975 private static function htmlPartialPaymentCustomer(): string
976 {
977 $inner = '<p style="margin:0 0 16px;font-size:17px;color:#0f172a;">Dear {{customer_name}},</p>'
978 . '<p style="margin:0 0 20px;color:#475569;">'
979 . esc_html__('Thank you — we’ve received your part payment. The remaining balance for your booking is shown below.', 'yatra')
980 . '</p>'
981 . EmailTemplateLayout::detailCard([
982 ['label' => 'Booking reference', 'value' => '{{booking_reference}}'],
983 ['label' => 'Amount received', 'value' => '{{payment_amount_formatted}}'],
984 ['label' => 'Paid so far', 'value' => '{{amount_paid_formatted}}'],
985 ['label' => 'Balance remaining', 'value' => '{{amount_due_formatted}}'],
986 ['label' => 'Booking total', 'value' => '{{total_amount_formatted}}'],
987 ['label' => 'Method', 'value' => '{{payment_method}}'],
988 ]);
989
990 return EmailTemplateLayout::customer(
991 '💳',
992 'Part payment received',
993 $inner,
994 '{{site_name}}',
995 'Payment update'
996 );
997 }
998
999 private static function htmlCancellationCustomer(): string
1000 {
1001 $inner = '<p style="margin:0 0 16px;font-size:17px;color:#0f172a;">Hello {{customer_name}},</p>'
1002 . '<p style="margin:0 0 20px;color:#475569;">'
1003 . esc_html__('This email confirms that your booking has been cancelled. If this wasn’t expected, please contact us.', 'yatra')
1004 . '</p>'
1005 . EmailTemplateLayout::detailCard([
1006 ['label' => 'Reference', 'value' => '{{booking_reference}}'],
1007 ['label' => 'Trip', 'value' => '{{trip_name}}'],
1008 ['label' => 'Travel date', 'value' => '{{travel_date}}'],
1009 ])
1010 . '<p style="margin:24px 0 0;"><a href="{{trip_url}}" style="color:#0d9488;font-weight:600;text-decoration:none;">' . esc_html__('Browse more trips →', 'yatra') . '</a></p>';
1011
1012 return EmailTemplateLayout::customer(
1013 '📋',
1014 'Booking cancelled',
1015 $inner,
1016 '{{site_name}}',
1017 'Booking cancelled'
1018 );
1019 }
1020
1021 private static function htmlTripCompleted(): string
1022 {
1023 $inner = '<p style="margin:0 0 16px;font-size:17px;color:#0f172a;">Hello {{customer_name}},</p>'
1024 . '<p style="margin:0 0 20px;color:#475569;">'
1025 . esc_html__('We hope you loved', 'yatra') . ' <strong style="color:#0f172a;">{{trip_name}}</strong>. '
1026 . esc_html__('Thank you for choosing us — we’d be delighted to host you again.', 'yatra')
1027 . '</p>'
1028 . EmailTemplateLayout::detailCard([
1029 ['label' => 'Reference', 'value' => '{{booking_reference}}'],
1030 ['label' => 'Trip', 'value' => '{{trip_name}}'],
1031 ]);
1032
1033 return EmailTemplateLayout::customer(
1034 '🌟',
1035 'Trip complete',
1036 $inner,
1037 '{{site_name}}',
1038 'Thank you for traveling with us'
1039 );
1040 }
1041
1042 private static function htmlReminderCustomer(): string
1043 {
1044 $inner = '<p style="margin:0 0 16px;font-size:17px;color:#0f172a;">Dear {{customer_name}},</p>'
1045 . '<p style="margin:0 0 20px;color:#475569;">'
1046 . esc_html__('Friendly heads-up: your departure is in', 'yatra')
1047 . ' <strong style="color:#0f172a;">{{reminder_days}}</strong> '
1048 . esc_html__('days. Here’s what you need at a glance.', 'yatra')
1049 . '</p>'
1050 . EmailTemplateLayout::detailCard([
1051 ['label' => 'Reference', 'value' => '{{booking_reference}}'],
1052 ['label' => 'Trip', 'value' => '{{trip_name}}'],
1053 ['label' => 'Travel date', 'value' => '{{travel_date}}'],
1054 ['label' => 'Travelers', 'value' => '{{travelers_count}}'],
1055 ['label' => 'Balance due', 'value' => '{{amount_due_formatted}}'],
1056 ])
1057 . '{{reminder_extra_html}}';
1058
1059 return EmailTemplateLayout::customer(
1060 '🗓️',
1061 'Trip reminder',
1062 $inner,
1063 '{{site_name}}',
1064 'Trip reminder'
1065 );
1066 }
1067
1068 private static function htmlTripConsentRequest(): string
1069 {
1070 $cta = '<table role="presentation" cellpadding="0" cellspacing="0" border="0" style="margin:28px 0;"><tr>'
1071 . '<td style="border-radius:10px;background:#0d9488;">'
1072 . '<a href="{{consent_link}}" style="display:inline-block;padding:14px 28px;font-family:\'Segoe UI\',Roboto,Arial,sans-serif;font-size:15px;font-weight:600;color:#ffffff;text-decoration:none;border-radius:10px;">'
1073 . esc_html__('Sign consent form', 'yatra')
1074 . '</a></td></tr></table>';
1075
1076 $inner = '<p style="margin:0 0 16px;font-size:17px;color:#0f172a;">Hello {{recipient_name}},</p>'
1077 . '<p style="margin:0 0 20px;color:#475569;">'
1078 . esc_html__('Please review and sign the consent form for your upcoming trip.', 'yatra')
1079 . '</p>'
1080 . '{{consent_test_notice_html}}'
1081 . EmailTemplateLayout::detailCard([
1082 ['label' => 'Form', 'value' => '{{form_name}}'],
1083 ['label' => 'Trip', 'value' => '{{trip_name}}'],
1084 ['label' => 'Travel date', 'value' => '{{travel_date}}'],
1085 ['label' => 'Booking reference', 'value' => '{{booking_reference}}'],
1086 ])
1087 . '{{expiry_notice_html}}'
1088 . $cta
1089 . '<p style="margin:20px 0 0;font-size:13px;color:#64748b;">'
1090 . esc_html__('If the button does not work, copy this link into your browser:', 'yatra')
1091 . '</p>'
1092 . '<p style="margin:8px 0 0;font-size:13px;word-break:break-all;"><a href="{{consent_link}}" style="color:#0d9488;">{{consent_link}}</a></p>';
1093
1094 return EmailTemplateLayout::customer(
1095 '📝',
1096 'Consent required',
1097 $inner,
1098 '{{site_name}}',
1099 'Trip consent'
1100 );
1101 }
1102
1103 private static function htmlCustomerEmailVerification(): string
1104 {
1105 $cta = '<table role="presentation" cellpadding="0" cellspacing="0" border="0" style="margin:28px 0;"><tr>'
1106 . '<td style="border-radius:10px;background:#0d9488;">'
1107 . '<a href="{{verification_link}}" style="display:inline-block;padding:14px 28px;font-family:\'Segoe UI\',Roboto,Arial,sans-serif;font-size:15px;font-weight:600;color:#ffffff;text-decoration:none;border-radius:10px;">'
1108 . esc_html__('Verify email address', 'yatra')
1109 . '</a></td></tr></table>';
1110
1111 $inner = '<p style="margin:0 0 16px;font-size:17px;color:#0f172a;">Hello {{customer_first_name}},</p>'
1112 . '<p style="margin:0 0 20px;color:#475569;">{{intro_paragraph}}</p>'
1113 . $cta
1114 . '<div style="margin:20px 0 0;padding:12px 16px;background:#f8fafc;border:1px solid #e2e8f0;border-radius:10px;font-size:14px;color:#475569;">'
1115 . '{{expiry_notice_html}}'
1116 . '</div>'
1117 . '<p style="margin:24px 0 0;font-size:14px;color:#64748b;">{{footer_note}}</p>'
1118 . '<p style="margin:20px 0 0;font-size:13px;color:#64748b;">'
1119 . esc_html__('If the button does not work, copy this link into your browser:', 'yatra')
1120 . '</p>'
1121 . '<p style="margin:8px 0 0;font-size:13px;word-break:break-all;"><a href="{{verification_link}}" style="color:#0d9488;">{{verification_link}}</a></p>';
1122
1123 return EmailTemplateLayout::customer(
1124 '✉️',
1125 __('Verify your email', 'yatra'),
1126 $inner,
1127 '{{site_name}}',
1128 __('Account verification', 'yatra')
1129 );
1130 }
1131
1132 private static function htmlAccountEmailChangeRequest(): string
1133 {
1134 $cta = '<table role="presentation" cellpadding="0" cellspacing="0" border="0" style="margin:28px 0;"><tr>'
1135 . '<td style="border-radius:10px;background:#0d9488;">'
1136 . '<a href="{{verification_link}}" style="display:inline-block;padding:14px 28px;font-family:\'Segoe UI\',Roboto,Arial,sans-serif;font-size:15px;font-weight:600;color:#ffffff;text-decoration:none;border-radius:10px;">'
1137 . esc_html__('Confirm email change', 'yatra')
1138 . '</a></td></tr></table>';
1139
1140 $inner = '<p style="margin:0 0 16px;font-size:17px;color:#0f172a;">Hello {{customer_first_name}},</p>'
1141 . '<p style="margin:0 0 20px;color:#475569;">{{intro_paragraph}}</p>'
1142 . '<div style="margin:0 0 20px;padding:12px 16px;background:#f8fafc;border:1px solid #e2e8f0;border-radius:10px;font-size:14px;color:#475569;">'
1143 . esc_html__('New email address:', 'yatra') . ' <strong>{{new_email}}</strong>'
1144 . '</div>'
1145 . $cta
1146 . '<p style="margin:24px 0 0;font-size:14px;color:#64748b;">{{footer_note}}</p>'
1147 . '<p style="margin:20px 0 0;font-size:13px;color:#64748b;">'
1148 . esc_html__('If the button does not work, copy this link into your browser:', 'yatra')
1149 . '</p>'
1150 . '<p style="margin:8px 0 0;font-size:13px;word-break:break-all;"><a href="{{verification_link}}" style="color:#0d9488;">{{verification_link}}</a></p>';
1151
1152 return EmailTemplateLayout::customer(
1153 '✉️',
1154 __('Confirm your new email', 'yatra'),
1155 $inner,
1156 '{{site_name}}',
1157 __('Account security', 'yatra')
1158 );
1159 }
1160
1161 private static function htmlAccountEmailChanged(): string
1162 {
1163 $inner = '<p style="margin:0 0 16px;font-size:17px;color:#0f172a;">Hello {{customer_first_name}},</p>'
1164 . '<p style="margin:0 0 20px;color:#475569;">{{intro_paragraph}}</p>'
1165 . '<div style="margin:0 0 20px;padding:12px 16px;background:#f8fafc;border:1px solid #e2e8f0;border-radius:10px;font-size:14px;color:#475569;">'
1166 . esc_html__('New email address:', 'yatra') . ' <strong>{{new_email}}</strong>'
1167 . '</div>'
1168 . '<p style="margin:24px 0 0;font-size:14px;color:#64748b;">{{footer_note}}</p>';
1169
1170 return EmailTemplateLayout::customer(
1171 '🔔',
1172 __('Your email address was changed', 'yatra'),
1173 $inner,
1174 '{{site_name}}',
1175 __('Account security', 'yatra')
1176 );
1177 }
1178
1179 private static function htmlPaymentReminder(): string
1180 {
1181 $inner = '<p style="margin:0 0 16px;font-size:17px;color:#0f172a;">Hello {{customer_name}},</p>'
1182 . '<p style="margin:0 0 20px;color:#475569;">'
1183 . esc_html__('This is a gentle nudge about your booking', 'yatra')
1184 . ' <strong>{{booking_reference}}</strong> '
1185 . esc_html__('for', 'yatra')
1186 . ' <strong>{{trip_name}}</strong>. '
1187 . esc_html__('If a balance remains, please settle it before your travel date ({{travel_date}}).', 'yatra')
1188 . '</p>'
1189 . EmailTemplateLayout::detailCard([
1190 ['label' => 'Amount due', 'value' => '{{amount_due_formatted}}'],
1191 ['label' => 'Trip', 'value' => '{{trip_name}}'],
1192 ['label' => 'Travel date', 'value' => '{{travel_date}}'],
1193 ]);
1194
1195 return EmailTemplateLayout::customer(
1196 '💳',
1197 'Payment reminder',
1198 $inner,
1199 '{{site_name}}',
1200 'Payment reminder'
1201 );
1202 }
1203
1204 private static function htmlEnquiryCustomer(): string
1205 {
1206 $inner = '<p style="margin:0 0 16px;font-size:17px;color:#0f172a;">Hello {{customer_name}},</p>'
1207 . '<p style="margin:0 0 20px;color:#475569;">'
1208 . esc_html__('Thanks for reaching out about', 'yatra')
1209 . ' <strong style="color:#0f172a;">{{trip_name}}</strong>. '
1210 . esc_html__('We’ve received your message and our team will reply shortly.', 'yatra')
1211 . '</p>'
1212 . '<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="background:#f0fdfa;border:1px solid #99f6e4;border-radius:12px;margin:8px 0 0;">'
1213 . '<tr><td style="padding:16px 20px;font-size:14px;color:#115e59;">✉️ '
1214 . esc_html__('Typical reply time: within one business day.', 'yatra')
1215 . '</td></tr></table>';
1216
1217 return EmailTemplateLayout::customer(
1218 '✉️',
1219 'Message received',
1220 $inner,
1221 '{{site_name}}',
1222 'Enquiry received'
1223 );
1224 }
1225
1226 private static function htmlEnquiryAdmin(): string
1227 {
1228 $inner = '<p style="margin:0 0 20px;color:#475569;">'
1229 . esc_html__('Someone submitted an enquiry from your site. Follow up from the details below.', 'yatra')
1230 . '</p>'
1231 . EmailTemplateLayout::detailCard([
1232 ['label' => 'Name', 'value' => '{{customer_name}}'],
1233 ['label' => 'Email', 'value' => '{{customer_email}}'],
1234 ['label' => 'Phone', 'value' => '{{customer_phone}}'],
1235 ['label' => 'Trip', 'value' => '{{trip_name}}'],
1236 ])
1237 . '<p style="margin:20px 0 8px;font-size:12px;font-weight:700;color:#64748b;text-transform:uppercase;letter-spacing:0.06em;">'
1238 . esc_html__('Message', 'yatra')
1239 . '</p>'
1240 . '<div style="background:#f8fafc;border:1px solid #e2e8f0;border-radius:12px;padding:18px 20px;color:#334155;line-height:1.65;">{{message}}</div>';
1241
1242 return EmailTemplateLayout::admin(
1243 '💬',
1244 'New enquiry',
1245 $inner,
1246 '{{site_name}} · ' . esc_html__('Admin', 'yatra')
1247 );
1248 }
1249
1250 private static function htmlEnquiryResponse(): string
1251 {
1252 $inner = '<p style="margin:0 0 16px;font-size:17px;color:#0f172a;">Hello {{customer_name}},</p>'
1253 . '<p style="margin:0 0 20px;color:#475569;">'
1254 . esc_html__('Here’s our reply to your enquiry:', 'yatra')
1255 . '</p>'
1256 . '<div style="background:#f8fafc;border-left:4px solid #0d9488;border-radius:0 12px 12px 0;padding:20px 24px;color:#334155;line-height:1.7;">{{response}}</div>'
1257 . '<p style="margin:24px 0 0;font-size:14px;color:#64748b;">'
1258 . esc_html__('Questions? Just reply to this email.', 'yatra')
1259 . '</p>';
1260
1261 return EmailTemplateLayout::customer(
1262 '💬',
1263 'Our reply',
1264 $inner,
1265 '{{site_name}}',
1266 'Enquiry response'
1267 );
1268 }
1269
1270 private static function htmlReviewRequest(): string
1271 {
1272 $inner = '<p style="margin:0 0 16px;font-size:17px;color:#0f172a;">Hello {{customer_name}},</p>'
1273 . '<p style="margin:0 0 20px;color:#475569;">'
1274 . esc_html__('We hope', 'yatra')
1275 . ' <strong>{{trip_name}}</strong> '
1276 . esc_html__('was unforgettable. Would you share a quick review? It helps other travelers and our team.', 'yatra')
1277 . '</p>'
1278 . '<table role="presentation" cellpadding="0" cellspacing="0" border="0" style="margin:8px 0 24px;"><tr>'
1279 . '<td style="border-radius:10px;background:#f59e0b;">'
1280 . '<a href="{{review_url}}" style="display:inline-block;padding:14px 28px;font-family:\'Segoe UI\',Roboto,Arial,sans-serif;font-size:15px;font-weight:600;color:#1c1917;text-decoration:none;border-radius:10px;">'
1281 . '' . esc_html__('Leave a review', 'yatra')
1282 . '</a></td></tr></table>'
1283 . '<p style="margin:0;font-size:13px;color:#94a3b8;">'
1284 . esc_html__('Takes about a minute — thank you!', 'yatra')
1285 . '</p>';
1286
1287 return EmailTemplateLayout::customer(
1288 '',
1289 'How was your trip?',
1290 $inner,
1291 '{{site_name}}',
1292 'Review request'
1293 );
1294 }
1295
1296 private static function htmlBookingExpiredCustomer(): string
1297 {
1298 $inner = '<p style="margin:0 0 16px;font-size:17px;color:#0f172a;">Hello {{customer_name}},</p>'
1299 . '<p style="margin:0 0 20px;color:#475569;">'
1300 . esc_html__('Your booking was cancelled automatically because payment was not received in time.', 'yatra')
1301 . '</p>'
1302 . EmailTemplateLayout::detailCard([
1303 ['label' => 'Reference', 'value' => '{{booking_reference}}'],
1304 ['label' => 'Trip', 'value' => '{{trip_name}}'],
1305 ['label' => 'Travel date', 'value' => '{{travel_date}}'],
1306 ['label' => 'Policy', 'value' => '{{expiry_policy_note}}'],
1307 ])
1308 . '<p style="margin:24px 0 0;"><a href="{{trip_url}}" style="color:#0d9488;font-weight:600;text-decoration:none;">'
1309 . esc_html__('Browse trips again →', 'yatra')
1310 . '</a></p>';
1311
1312 return EmailTemplateLayout::customer(
1313 '⏱️',
1314 __('Booking expired', 'yatra'),
1315 $inner,
1316 '{{site_name}}',
1317 __('Booking expired', 'yatra')
1318 );
1319 }
1320
1321 private static function htmlAdminBookingExpired(): string
1322 {
1323 $inner = '<p style="margin:0 0 20px;color:#475569;">'
1324 . esc_html__('A booking was cancelled automatically due to non-payment (expiry policy).', 'yatra')
1325 . '</p>'
1326 . EmailTemplateLayout::detailCard([
1327 ['label' => 'Reference', 'value' => '{{booking_reference}}'],
1328 ['label' => 'Booking ID', 'value' => '{{booking_id}}'],
1329 ['label' => 'Customer', 'value' => '{{customer_name}}'],
1330 ['label' => 'Email', 'value' => '{{customer_email}}'],
1331 ['label' => 'Trip', 'value' => '{{trip_name}}'],
1332 ])
1333 . '<table role="presentation" cellpadding="0" cellspacing="0" border="0" style="margin:28px 0 0;"><tr>'
1334 . '<td style="border-radius:10px;background:#2563eb;">'
1335 . '<a href="{{admin_url}}" style="display:inline-block;padding:14px 24px;font-family:\'Segoe UI\',Roboto,Arial,sans-serif;font-size:15px;font-weight:600;color:#ffffff;text-decoration:none;border-radius:10px;">'
1336 . esc_html__('Open in Yatra admin', 'yatra')
1337 . '</a></td></tr></table>';
1338
1339 return EmailTemplateLayout::admin(
1340 '⏱️',
1341 __('Booking expired', 'yatra'),
1342 $inner,
1343 '{{site_name}} · ' . esc_html__('Admin', 'yatra')
1344 );
1345 }
1346
1347 private static function htmlScheduledPaymentReminder(): string
1348 {
1349 $inner = '<p style="margin:0 0 16px;font-size:17px;color:#0f172a;">Hello {{customer_first_name}},</p>'
1350 . '<p style="margin:0 0 20px;color:#475569;">'
1351 . esc_html__('This is a reminder that a scheduled payment is coming up for your booking.', 'yatra')
1352 . '</p>'
1353 . EmailTemplateLayout::detailCard([
1354 ['label' => 'Reference', 'value' => '{{booking_reference}}'],
1355 ['label' => 'Amount', 'value' => '{{scheduled_amount_formatted}}'],
1356 ['label' => 'Scheduled date', 'value' => '{{scheduled_date_formatted}}'],
1357 ['label' => 'Type', 'value' => '{{payment_type_label}}'],
1358 ])
1359 . '<p style="margin:24px 0 0;font-size:14px;color:#64748b;">'
1360 . esc_html__('If you need to update your payment method, contact us before the charge date.', 'yatra')
1361 . '</p>';
1362
1363 return EmailTemplateLayout::customer(
1364 '💳',
1365 __('Upcoming payment', 'yatra'),
1366 $inner,
1367 '{{site_name}}',
1368 __('Scheduled payment reminder', 'yatra')
1369 );
1370 }
1371
1372 private static function htmlScheduledPaymentSucceeded(): string
1373 {
1374 $inner = '<p style="margin:0 0 16px;font-size:17px;color:#0f172a;">Hello {{customer_first_name}},</p>'
1375 . '<p style="margin:0 0 20px;color:#475569;">'
1376 . esc_html__('Your scheduled payment was processed successfully.', 'yatra')
1377 . '</p>'
1378 . EmailTemplateLayout::detailCard([
1379 ['label' => 'Reference', 'value' => '{{booking_reference}}'],
1380 ['label' => 'Amount charged', 'value' => '{{scheduled_amount_formatted}}'],
1381 ['label' => 'Type', 'value' => '{{payment_type_label}}'],
1382 ['label' => 'Balance due now', 'value' => '{{balance_after_formatted}}'],
1383 ]);
1384
1385 return EmailTemplateLayout::customer(
1386 '�
1387 ',
1388 __('Payment received', 'yatra'),
1389 $inner,
1390 '{{site_name}}',
1391 __('Scheduled payment', 'yatra')
1392 );
1393 }
1394
1395 private static function htmlScheduledPaymentFailed(): string
1396 {
1397 $inner = '<p style="margin:0 0 16px;font-size:17px;color:#0f172a;">Hello {{customer_first_name}},</p>'
1398 . '<p style="margin:0 0 20px;color:#475569;">'
1399 . '{{failure_intro_html}}'
1400 . '</p>'
1401 . EmailTemplateLayout::detailCard([
1402 ['label' => 'Reference', 'value' => '{{booking_reference}}'],
1403 ['label' => 'Amount', 'value' => '{{scheduled_amount_formatted}}'],
1404 ['label' => 'Details', 'value' => '{{failure_reason}}'],
1405 ])
1406 . '<p style="margin:24px 0 0;font-size:14px;color:#64748b;">{{failure_followup_html}}</p>';
1407
1408 return EmailTemplateLayout::customer(
1409 '⚠️',
1410 __('Payment issue', 'yatra'),
1411 $inner,
1412 '{{site_name}}',
1413 __('Scheduled payment', 'yatra')
1414 );
1415 }
1416
1417 private static function htmlAdminScheduledPaymentFailed(): string
1418 {
1419 $inner = '<p style="margin:0 0 20px;color:#475569;">'
1420 . esc_html__('A scheduled payment permanently failed after retries.', 'yatra')
1421 . '</p>'
1422 . EmailTemplateLayout::detailCard([
1423 ['label' => 'Reference', 'value' => '{{booking_reference}}'],
1424 ['label' => 'Customer', 'value' => '{{customer_name}}'],
1425 ['label' => 'Email', 'value' => '{{customer_email}}'],
1426 ['label' => 'Amount', 'value' => '{{scheduled_amount_formatted}}'],
1427 ['label' => 'Error', 'value' => '{{failure_reason}}'],
1428 ])
1429 . '<table role="presentation" cellpadding="0" cellspacing="0" border="0" style="margin:28px 0 0;"><tr>'
1430 . '<td style="border-radius:10px;background:#2563eb;">'
1431 . '<a href="{{admin_url}}" style="display:inline-block;padding:14px 24px;font-family:\'Segoe UI\',Roboto,Arial,sans-serif;font-size:15px;font-weight:600;color:#ffffff;text-decoration:none;border-radius:10px;">'
1432 . esc_html__('Open in Yatra admin', 'yatra')
1433 . '</a></td></tr></table>';
1434
1435 return EmailTemplateLayout::admin(
1436 '⚠️',
1437 __('Scheduled payment failed', 'yatra'),
1438 $inner,
1439 '{{site_name}} · ' . esc_html__('Admin', 'yatra')
1440 );
1441 }
1442
1443 private static function htmlAbandonedBookingRecoveryBase(string $headline, string $subLabel): string
1444 {
1445 $inner = '<p style="margin:0 0 16px;font-size:17px;color:#0f172a;">Hello {{customer_name}},</p>'
1446 . '<p style="margin:0 0 20px;color:#475569;">{{recovery_intro_html}}</p>'
1447 . EmailTemplateLayout::detailCard([
1448 ['label' => 'Trip', 'value' => '{{trip_name}}'],
1449 ['label' => 'Reminder', 'value' => '{{recovery_reminder_label}}'],
1450 ])
1451 . '<table role="presentation" cellpadding="0" cellspacing="0" border="0" style="margin:28px 0;"><tr>'
1452 . '<td style="border-radius:10px;background:#0d9488;">'
1453 . '<a href="{{recovery_link}}" style="display:inline-block;padding:14px 28px;font-family:\'Segoe UI\',Roboto,Arial,sans-serif;font-size:15px;font-weight:600;color:#ffffff;text-decoration:none;border-radius:10px;">'
1454 . esc_html__('Continue booking', 'yatra')
1455 . '</a></td></tr></table>'
1456 . '<p style="margin:20px 0 0;font-size:13px;color:#64748b;">'
1457 . esc_html__('If you did not start this booking, you can ignore this email.', 'yatra')
1458 . '</p>';
1459
1460 return EmailTemplateLayout::customer(
1461 '🛒',
1462 $headline,
1463 $inner,
1464 '{{site_name}}',
1465 $subLabel
1466 );
1467 }
1468
1469 private static function htmlAbandonedBookingRecoveryFirst(): string
1470 {
1471 return self::htmlAbandonedBookingRecoveryBase(
1472 __('Complete your booking', 'yatra'),
1473 __('Abandoned checkout · First reminder', 'yatra')
1474 );
1475 }
1476
1477 private static function htmlAbandonedBookingRecoverySecond(): string
1478 {
1479 return self::htmlAbandonedBookingRecoveryBase(
1480 __('Still interested?', 'yatra'),
1481 __('Abandoned checkout · Second reminder', 'yatra')
1482 );
1483 }
1484
1485 private static function htmlAbandonedBookingRecoveryFinal(): string
1486 {
1487 return self::htmlAbandonedBookingRecoveryBase(
1488 __('Final reminder', 'yatra'),
1489 __('Abandoned checkout · Final reminder', 'yatra')
1490 );
1491 }
1492
1493 public static function fallbackTransactionalBookingCompleted(array $v): string
1494 {
1495 return TransactionalEmailTemplateService::parseMergeTags(self::htmlTripCompleted(), TransactionalEmailTemplateService::mergeTemplateVariables($v));
1496 }
1497
1498 public static function fallbackTransactionalBookingExpiredCustomer(array $v): string
1499 {
1500 return TransactionalEmailTemplateService::parseMergeTags(self::htmlBookingExpiredCustomer(), TransactionalEmailTemplateService::mergeTemplateVariables($v));
1501 }
1502
1503 public static function fallbackAdminBookingExpired(array $v): string
1504 {
1505 return TransactionalEmailTemplateService::parseMergeTags(self::htmlAdminBookingExpired(), TransactionalEmailTemplateService::mergeTemplateVariables($v));
1506 }
1507
1508 public static function fallbackTransactionalScheduledPaymentReminder(array $v): string
1509 {
1510 return TransactionalEmailTemplateService::parseMergeTags(self::htmlScheduledPaymentReminder(), TransactionalEmailTemplateService::mergeTemplateVariables($v));
1511 }
1512
1513 public static function fallbackTransactionalScheduledPaymentSucceeded(array $v): string
1514 {
1515 return TransactionalEmailTemplateService::parseMergeTags(self::htmlScheduledPaymentSucceeded(), TransactionalEmailTemplateService::mergeTemplateVariables($v));
1516 }
1517
1518 public static function fallbackTransactionalScheduledPaymentFailed(array $v): string
1519 {
1520 return TransactionalEmailTemplateService::parseMergeTags(self::htmlScheduledPaymentFailed(), TransactionalEmailTemplateService::mergeTemplateVariables($v));
1521 }
1522
1523 public static function fallbackAdminScheduledPaymentFailed(array $v): string
1524 {
1525 return TransactionalEmailTemplateService::parseMergeTags(self::htmlAdminScheduledPaymentFailed(), TransactionalEmailTemplateService::mergeTemplateVariables($v));
1526 }
1527
1528 public static function fallbackTransactionalEnquiryAdmin(array $v): string
1529 {
1530 return TransactionalEmailTemplateService::parseMergeTags(self::htmlEnquiryAdmin(), TransactionalEmailTemplateService::mergeTemplateVariables($v));
1531 }
1532
1533 public static function fallbackTransactionalEnquiryReceived(array $v): string
1534 {
1535 return TransactionalEmailTemplateService::parseMergeTags(self::htmlEnquiryCustomer(), TransactionalEmailTemplateService::mergeTemplateVariables($v));
1536 }
1537
1538 public static function fallbackTransactionalEnquiryResponse(array $v): string
1539 {
1540 return TransactionalEmailTemplateService::parseMergeTags(self::htmlEnquiryResponse(), TransactionalEmailTemplateService::mergeTemplateVariables($v));
1541 }
1542
1543 public static function fallbackTransactionalReviewRequest(array $v): string
1544 {
1545 return TransactionalEmailTemplateService::parseMergeTags(self::htmlReviewRequest(), TransactionalEmailTemplateService::mergeTemplateVariables($v));
1546 }
1547
1548 public static function fallbackTransactionalAbandonedBookingRecoveryFirst(array $v): string
1549 {
1550 return TransactionalEmailTemplateService::parseMergeTags(self::htmlAbandonedBookingRecoveryFirst(), TransactionalEmailTemplateService::mergeTemplateVariables($v));
1551 }
1552
1553 public static function fallbackTransactionalAbandonedBookingRecoverySecond(array $v): string
1554 {
1555 return TransactionalEmailTemplateService::parseMergeTags(self::htmlAbandonedBookingRecoverySecond(), TransactionalEmailTemplateService::mergeTemplateVariables($v));
1556 }
1557
1558 public static function fallbackTransactionalAbandonedBookingRecoveryFinal(array $v): string
1559 {
1560 return TransactionalEmailTemplateService::parseMergeTags(self::htmlAbandonedBookingRecoveryFinal(), TransactionalEmailTemplateService::mergeTemplateVariables($v));
1561 }
1562
1563 /**
1564 * Merge-tag variables for enquiry templates (core wp_mail path when Pro does not own the channel).
1565 *
1566 * @param object $enquiry Row from EnquiryRepository::findWithTrip()
1567 */
1568 private static function enquiryVariablesForTemplates(object $enquiry, string $responsePlain): array
1569 {
1570 return TransactionalEmailTemplateService::variablesFromEnquiry($enquiry, $responsePlain);
1571 }
1572
1573 /**
1574 * Full HTML body matching Pro/system enquiry_received (for free core sends).
1575 */
1576 public static function renderCoreEnquiryCustomerConfirmationHtml(object $enquiry): string
1577 {
1578 $vars = self::enquiryVariablesForTemplates($enquiry, '');
1579
1580 return TransactionalEmailTemplateService::parseMergeTags(self::htmlEnquiryCustomer(), $vars);
1581 }
1582
1583 /**
1584 * Full HTML body matching Pro/system enquiry_admin.
1585 */
1586 public static function renderCoreEnquiryAdminNotificationHtml(object $enquiry): string
1587 {
1588 $vars = self::enquiryVariablesForTemplates($enquiry, '');
1589
1590 return TransactionalEmailTemplateService::parseMergeTags(self::htmlEnquiryAdmin(), $vars);
1591 }
1592
1593 /**
1594 * Full HTML body matching Pro/system enquiry_response.
1595 */
1596 public static function renderCoreEnquiryResponseHtml(object $enquiry, string $response): string
1597 {
1598 $vars = self::enquiryVariablesForTemplates($enquiry, $response);
1599
1600 return TransactionalEmailTemplateService::parseMergeTags(self::htmlEnquiryResponse(), $vars);
1601 }
1602
1603 /**
1604 * Review reminder (cron): same shell as Pro review_request template.
1605 */
1606 public static function renderCoreReviewReminderHtml(string $customerName, string $tripName, string $reviewUrl): string
1607 {
1608 $vars = [
1609 'customer_name' => $customerName,
1610 'trip_name' => $tripName,
1611 'review_url' => esc_url($reviewUrl),
1612 ];
1613
1614 return TransactionalEmailTemplateService::parseMergeTags(self::htmlReviewRequest(), $vars);
1615 }
1616 }
1617