PluginProbe
Yatra – Travel Booking & Tour Operator Software / trunk
Yatra – Travel Booking & Tour Operator Software vtrunk
3.0.14 3.0.14.1 3.0.14.2 3.0.12 3.0.13 3.0.11 3.0.10 3.0.9 3.0.8 3.0.7 3.0.6 3.0.5 3.0.5.1 3.0.4 3.0.3 3.0.2.9 3.0.2.7 3.0.2.8 3.0.2.6 trunk 1.0.0 2.0.0 2.0.1 2.0.10 2.0.11 All 82 releases
yatra / app / Services / EmailTemplateDefaults.php

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

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