PluginProbe
Yatra – Travel Booking & Tour Operator Software / 3.0.9
Yatra – Travel Booking & Tour Operator Software v3.0.9
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.9, at app/Services/EmailTemplateDefaults.php

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