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

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

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