PluginProbe
Yatra – Travel Booking & Tour Operator Software / 3.0.5
Yatra – Travel Booking & Tour Operator Software v3.0.5
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 / Controllers / AuthController.php

AuthController.php in Yatra – Travel Booking & Tour Operator Software 3.0.5, at app/Controllers/AuthController.php

712 lines 28.9 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\Controllers;
6
7 /**
8 * Authentication Controller
9 * Handles user registration, login, email verification for Yatra customers
10 *
11 * @package Yatra
12 */
13 class AuthController
14 {
15 /**
16 * Register REST API routes
17 */
18 public static function registerRoutes(): void
19 {
20 register_rest_route('yatra/v1', '/auth/login', [
21 'methods' => 'POST',
22 'callback' => [self::class, 'login'],
23 'permission_callback' => '__return_true',
24 ]);
25
26 register_rest_route('yatra/v1', '/auth/register', [
27 'methods' => 'POST',
28 'callback' => [self::class, 'register'],
29 'permission_callback' => '__return_true',
30 ]);
31
32 register_rest_route('yatra/v1', '/auth/resend-verification', [
33 'methods' => 'POST',
34 'callback' => [self::class, 'resendVerification'],
35 'permission_callback' => '__return_true',
36 ]);
37 }
38
39 /**
40 * Register Yatra Customer role
41 */
42 public static function registerCustomerRole(): void
43 {
44 if (!get_role('yatra_customer')) {
45 add_role(
46 'yatra_customer',
47 __('Yatra Customer', 'yatra'),
48 [
49 'read' => true,
50 'edit_posts' => false,
51 'delete_posts' => false,
52 ]
53 );
54 }
55 }
56
57 /**
58 * Block unverified users from WordPress login
59 *
60 * @param \WP_User|\WP_Error|null $user
61 * @param string $username
62 * @param string $password
63 * @return \WP_User|\WP_Error|null
64 */
65 public static function blockUnverifiedUserLogin($user, string $username, string $password)
66 {
67 if (is_wp_error($user)) {
68 return $user;
69 }
70
71 if (!($user instanceof \WP_User)) {
72 return $user;
73 }
74
75 $has_verification_token = get_user_meta($user->ID, 'yatra_verification_token', true);
76 $email_verified = get_user_meta($user->ID, 'yatra_email_verified', true);
77
78 $needs_verification = !empty($has_verification_token);
79
80 if (!$needs_verification) {
81 $meta_exists = metadata_exists('user', $user->ID, 'yatra_email_verified');
82 $needs_verification = $meta_exists && !$email_verified;
83 }
84
85 if ($needs_verification) {
86 $resend_url = add_query_arg([
87 'action' => 'yatra_resend_verification',
88 'email' => urlencode($user->user_email),
89 '_wpnonce' => wp_create_nonce('yatra_resend_verification'),
90 ], wp_login_url());
91
92 $message = sprintf(
93 /* translators: %s: resend verification link */
94 __('<strong>Error:</strong> Please verify your email address before logging in. Check your inbox for the verification link. <br><br><a href="%s">Click here to resend verification email</a>', 'yatra'),
95 esc_url($resend_url)
96 );
97
98 return new \WP_Error('email_not_verified', $message);
99 }
100
101 return $user;
102 }
103
104 /**
105 * Handle resend verification from WordPress login page
106 */
107 public static function handleWpLoginResendVerification(): void
108 {
109 if (!isset($_GET['action']) || $_GET['action'] !== 'yatra_resend_verification') {
110 return;
111 }
112
113 // Verify nonce
114 if (!isset($_GET['_wpnonce']) || !wp_verify_nonce(sanitize_text_field($_GET['_wpnonce']), 'yatra_resend_verification')) {
115 wp_die(__('Your session has expired. Please go back and try again.', 'yatra'));
116 }
117
118 $email = isset($_GET['email']) ? sanitize_email(urldecode($_GET['email'])) : '';
119
120 if (empty($email) || !is_email($email)) {
121 wp_safe_redirect(add_query_arg('login_error', 'invalid_email', wp_login_url()));
122 exit;
123 }
124
125 $user = get_user_by('email', $email);
126
127 if (!$user) {
128 // Redirect with generic success message (don't reveal if email exists)
129 wp_safe_redirect(add_query_arg('checkemail', 'resent', wp_login_url()));
130 exit;
131 }
132
133 // Check if already verified
134 $email_verified = get_user_meta($user->ID, 'yatra_email_verified', true);
135 if ($email_verified === '1') {
136 wp_safe_redirect(add_query_arg('login_error', 'already_verified', wp_login_url()));
137 exit;
138 }
139
140 // Check rate limiting
141 $last_sent = get_user_meta($user->ID, 'yatra_verification_last_sent', true);
142 if ($last_sent && (time() - (int) $last_sent) < 120) {
143 $remaining = 120 - (time() - (int) $last_sent);
144 wp_safe_redirect(add_query_arg([
145 'login_error' => 'rate_limited',
146 'wait' => $remaining,
147 ], wp_login_url()));
148 exit;
149 }
150
151 // Generate new token and send email
152 $verification_token = wp_generate_password(32, false);
153 $secure_token = base64_encode($user->ID . '|' . $verification_token . '|' . time());
154 $secure_token = str_replace(['+', '/', '='], ['-', '_', ''], $secure_token);
155
156 update_user_meta($user->ID, 'yatra_verification_token', $verification_token);
157 update_user_meta($user->ID, 'yatra_verification_token_expiry', time() + (24 * 60 * 60));
158 update_user_meta($user->ID, 'yatra_verification_last_sent', time());
159
160 // Send email
161 $first_name = get_user_meta($user->ID, 'first_name', true) ?: $user->display_name;
162 self::sendVerificationEmail($user->ID, $email, $first_name, $secure_token, true);
163
164 wp_safe_redirect(add_query_arg('checkemail', 'resent', wp_login_url()));
165 exit;
166 }
167
168 /**
169 * Add custom messages to WordPress login page
170 */
171 public static function customLoginMessages(string $message): string
172 {
173 if (isset($_GET['checkemail']) && $_GET['checkemail'] === 'resent') {
174 $message = '<p class="message">' . __('A new verification link has been sent to your email address. Please check your inbox and spam folder.', 'yatra') . '</p>';
175 }
176
177 if (isset($_GET['email_verified']) && $_GET['email_verified'] === '1') {
178 $message = '<p class="message">' . __('Your email has been verified successfully! You can now log in.', 'yatra') . '</p>';
179 }
180
181 return $message;
182 }
183
184 /**
185 * Add custom error messages to WordPress login page
186 */
187 public static function customLoginErrors(\WP_Error $errors): \WP_Error
188 {
189 if (isset($_GET['login_error'])) {
190 switch ($_GET['login_error']) {
191 case 'invalid_email':
192 $errors->add('invalid_email', __('<strong>Error:</strong> Invalid email address.', 'yatra'));
193 break;
194 case 'already_verified':
195 $errors->add('already_verified', __('<strong>Notice:</strong> Your email is already verified. Please log in.', 'yatra'));
196 break;
197 case 'rate_limited':
198 $wait = isset($_GET['wait']) ? (int) $_GET['wait'] : 120;
199 $errors->add('rate_limited', sprintf(
200 /* translators: %d: number of seconds to wait */
201 __('<strong>Error:</strong> Please wait %d seconds before requesting another verification email.', 'yatra'),
202 $wait
203 ));
204 break;
205 }
206 }
207
208 return $errors;
209 }
210
211 /**
212 * Handle login request
213 */
214 public static function login(\WP_REST_Request $request): \WP_REST_Response
215 {
216 $username = sanitize_user($request->get_param('username') ?? '');
217 $password = $request->get_param('password') ?? '';
218 $remember = !empty($request->get_param('remember'));
219
220 if (empty($username) || empty($password)) {
221 return new \WP_REST_Response([
222 'success' => false,
223 'message' => __('Please enter your username/email and password.', 'yatra'),
224 ], 400);
225 }
226
227 // Get user to check verification status
228 $user_check = get_user_by('login', $username);
229 if (!$user_check) {
230 $user_check = get_user_by('email', $username);
231 }
232
233 // Check email verification for Yatra users
234 if ($user_check) {
235 $has_verification_token = get_user_meta($user_check->ID, 'yatra_verification_token', true);
236 $email_verified = get_user_meta($user_check->ID, 'yatra_email_verified', true);
237
238 if (!empty($has_verification_token)) {
239 return new \WP_REST_Response([
240 'success' => false,
241 'message' => __('Please verify your email address before logging in. Check your inbox for the verification link.', 'yatra'),
242 'needs_verification' => true,
243 'email' => $user_check->user_email,
244 ], 403);
245 }
246
247 $meta_exists = metadata_exists('user', $user_check->ID, 'yatra_email_verified');
248 if ($meta_exists && !$email_verified) {
249 return new \WP_REST_Response([
250 'success' => false,
251 'message' => __('Please verify your email address before logging in. Check your inbox for the verification link.', 'yatra'),
252 'needs_verification' => true,
253 'email' => $user_check->user_email,
254 ], 403);
255 }
256 }
257
258 // Authenticate
259 $user = wp_signon([
260 'user_login' => $username,
261 'user_password' => $password,
262 'remember' => $remember,
263 ], is_ssl());
264
265 if (is_wp_error($user)) {
266 $error_message = wp_strip_all_tags($user->get_error_message());
267 if (strpos($error_message, 'incorrect') !== false || strpos($error_message, 'Invalid') !== false) {
268 $error_message = __('Invalid username/email or password. Please try again.', 'yatra');
269 }
270 return new \WP_REST_Response([
271 'success' => false,
272 'message' => $error_message,
273 ], 401);
274 }
275
276 wp_set_current_user($user->ID);
277
278 return new \WP_REST_Response([
279 'success' => true,
280 'message' => __('Login successful! Redirecting...', 'yatra'),
281 'user_id' => $user->ID,
282 ]);
283 }
284
285 /**
286 * Handle registration request
287 */
288 public static function register(\WP_REST_Request $request): \WP_REST_Response
289 {
290 if (!\Yatra\Services\SettingsService::isEnabled('customer_registration')) {
291 return new \WP_REST_Response([
292 'success' => false,
293 'message' => __('New customer registration is disabled.', 'yatra'),
294 ], 403);
295 }
296
297 $first_name = sanitize_text_field($request->get_param('first_name') ?? '');
298 $last_name = sanitize_text_field($request->get_param('last_name') ?? '');
299 $email = sanitize_email($request->get_param('email') ?? '');
300 $phone = sanitize_text_field($request->get_param('phone') ?? '');
301 $password = $request->get_param('password') ?? '';
302 $confirm_password = $request->get_param('confirm_password') ?? '';
303
304 // Validation
305 if (empty($first_name)) {
306 return new \WP_REST_Response([
307 'success' => false,
308 'message' => __('Please enter your first name.', 'yatra'),
309 ], 400);
310 }
311
312 if (empty($last_name)) {
313 return new \WP_REST_Response([
314 'success' => false,
315 'message' => __('Please enter your last name.', 'yatra'),
316 ], 400);
317 }
318
319 if (empty($email) || !is_email($email)) {
320 return new \WP_REST_Response([
321 'success' => false,
322 'message' => __('Please enter a valid email address.', 'yatra'),
323 ], 400);
324 }
325
326 if (empty($password) || strlen($password) < 8) {
327 return new \WP_REST_Response([
328 'success' => false,
329 'message' => __('Password must be at least 8 characters long.', 'yatra'),
330 ], 400);
331 }
332
333 if ($password !== $confirm_password) {
334 return new \WP_REST_Response([
335 'success' => false,
336 'message' => __('Passwords do not match.', 'yatra'),
337 ], 400);
338 }
339
340 if (email_exists($email)) {
341 return new \WP_REST_Response([
342 'success' => false,
343 'message' => __('An account with this email already exists. Please login instead.', 'yatra'),
344 ], 409);
345 }
346
347 // Generate username
348 $username = sanitize_user(current(explode('@', $email)));
349 $original_username = $username;
350 $counter = 1;
351
352 while (username_exists($username)) {
353 $username = $original_username . $counter;
354 $counter++;
355 }
356
357 // Create user
358 $user_id = wp_create_user($username, $password, $email);
359
360 if (is_wp_error($user_id)) {
361 $error_message = wp_strip_all_tags($user_id->get_error_message());
362 return new \WP_REST_Response([
363 'success' => false,
364 'message' => $error_message,
365 ], 500);
366 }
367
368 // Assign Yatra Customer role
369 $user = new \WP_User($user_id);
370 $user->set_role('yatra_customer');
371
372 // Update user meta
373 wp_update_user([
374 'ID' => $user_id,
375 'first_name' => $first_name,
376 'last_name' => $last_name,
377 'display_name' => $first_name . ' ' . $last_name,
378 ]);
379
380 if (!empty($phone)) {
381 update_user_meta($user_id, 'billing_phone', $phone);
382 update_user_meta($user_id, 'phone', $phone);
383 }
384
385 // Email verification
386 update_user_meta($user_id, 'yatra_email_verified', '0');
387
388 $verification_token = wp_generate_password(32, false);
389 $secure_token = base64_encode($user_id . '|' . $verification_token . '|' . time());
390 $secure_token = str_replace(['+', '/', '='], ['-', '_', ''], $secure_token);
391
392 update_user_meta($user_id, 'yatra_verification_token', $verification_token);
393 update_user_meta($user_id, 'yatra_verification_token_expiry', time() + (24 * 60 * 60));
394
395 // Send verification email
396 self::sendVerificationEmail($user_id, $email, $first_name, $secure_token);
397
398 // Admin notification
399 wp_new_user_notification($user_id, null, 'admin');
400
401 return new \WP_REST_Response([
402 'success' => true,
403 'message' => __('Registration successful! Please check your email to verify your account before logging in.', 'yatra'),
404 'user_id' => $user_id,
405 'require_verification' => true,
406 ]);
407 }
408
409 /**
410 * Handle resend verification request
411 */
412 public static function resendVerification(\WP_REST_Request $request): \WP_REST_Response
413 {
414 $email = sanitize_email($request->get_param('email') ?? '');
415
416 if (empty($email) || !is_email($email)) {
417 return new \WP_REST_Response([
418 'success' => false,
419 'message' => __('Please enter a valid email address.', 'yatra'),
420 ], 400);
421 }
422
423 $user = get_user_by('email', $email);
424
425 if (!$user) {
426 return new \WP_REST_Response([
427 'success' => true,
428 'message' => __('If an account with this email exists and is pending verification, a new verification link has been sent.', 'yatra'),
429 ]);
430 }
431
432 $email_verified = get_user_meta($user->ID, 'yatra_email_verified', true);
433
434 if ($email_verified === '1' || $email_verified === 1 || $email_verified === true) {
435 return new \WP_REST_Response([
436 'success' => false,
437 'message' => __('This email is already verified. You can login now.', 'yatra'),
438 ], 400);
439 }
440
441 $meta_exists = metadata_exists('user', $user->ID, 'yatra_email_verified');
442 $has_verification_token = get_user_meta($user->ID, 'yatra_verification_token', true);
443
444 if (!$meta_exists && empty($has_verification_token)) {
445 return new \WP_REST_Response([
446 'success' => true,
447 'message' => __('If an account with this email exists and is pending verification, a new verification link has been sent.', 'yatra'),
448 ]);
449 }
450
451 // Rate limiting
452 $last_sent = get_user_meta($user->ID, 'yatra_verification_last_sent', true);
453 if ($last_sent && (time() - (int) $last_sent) < 120) {
454 $remaining = 120 - (time() - (int) $last_sent);
455 return new \WP_REST_Response([
456 'success' => false,
457 'message' => __('Please wait before requesting another verification email.', 'yatra'),
458 'rate_limited' => true,
459 'remaining_seconds' => $remaining,
460 ], 429);
461 }
462
463 // Generate new token
464 $verification_token = wp_generate_password(32, false);
465 $secure_token = base64_encode($user->ID . '|' . $verification_token . '|' . time());
466 $secure_token = str_replace(['+', '/', '='], ['-', '_', ''], $secure_token);
467
468 update_user_meta($user->ID, 'yatra_verification_token', $verification_token);
469 update_user_meta($user->ID, 'yatra_verification_token_expiry', time() + (24 * 60 * 60));
470 update_user_meta($user->ID, 'yatra_verification_last_sent', time());
471
472 // Send email
473 $first_name = get_user_meta($user->ID, 'first_name', true) ?: $user->display_name;
474 self::sendVerificationEmail($user->ID, $email, $first_name, $secure_token, true);
475
476 return new \WP_REST_Response([
477 'success' => true,
478 'message' => __('A new verification link has been sent to your email address. Please check your inbox.', 'yatra'),
479 ]);
480 }
481
482 /**
483 * Handle email verification
484 */
485 public static function handleEmailVerification(): void
486 {
487 $secure_token = (string) get_query_var('yatra_verify_email');
488 if ($secure_token === '' && isset($_GET['yatra_verify_email'])) {
489 $raw = wp_unslash($_GET['yatra_verify_email']);
490 $secure_token = is_string($raw) ? preg_replace('/[^a-zA-Z0-9_-]/', '', $raw) ?? '' : '';
491 }
492
493 if ($secure_token === '') {
494 self::showVerificationError(__('Invalid verification link.', 'yatra'));
495
496 return;
497 }
498
499 $previewToken = defined('YATRA_EMAIL_VERIFICATION_PREVIEW_TOKEN')
500 ? (string) YATRA_EMAIL_VERIFICATION_PREVIEW_TOKEN
501 : 'preview-verify-token';
502 if ($secure_token === $previewToken) {
503 self::showEmailVerificationPreviewNotice();
504
505 return;
506 }
507
508 // Decode token
509 $secure_token = str_replace(['-', '_'], ['+', '/'], $secure_token);
510 $decoded = base64_decode($secure_token);
511
512 if (!$decoded || strpos($decoded, '|') === false) {
513 self::showVerificationError(__('Invalid verification link.', 'yatra'));
514 return;
515 }
516
517 $parts = explode('|', $decoded);
518 if (count($parts) < 2) {
519 self::showVerificationError(__('Invalid verification link.', 'yatra'));
520 return;
521 }
522
523 $user_id = (int) $parts[0];
524 $token = $parts[1];
525
526 if ($user_id <= 0 || empty($token)) {
527 self::showVerificationError(__('Invalid verification link.', 'yatra'));
528 return;
529 }
530
531 $stored_token = get_user_meta($user_id, 'yatra_verification_token', true);
532 $token_expiry = get_user_meta($user_id, 'yatra_verification_token_expiry', true);
533
534 // Idempotent path: the token+expiry are deleted as soon as a successful
535 // verification completes (see below), so a second click on the same
536 // link previously fell into the "Invalid or expired" branch and made
537 // already-verified customers believe their account was broken. Detect
538 // the prior-success state explicitly and reuse the success page so the
539 // outcome is clear regardless of how many times the link is clicked.
540 $already_verified = get_user_meta($user_id, 'yatra_email_verified', true) === '1';
541 if ($already_verified) {
542 self::showVerificationSuccess(true);
543 return;
544 }
545
546 if (empty($stored_token) || $stored_token !== $token) {
547 self::showVerificationError(__('Invalid or expired verification link.', 'yatra'));
548 return;
549 }
550
551 if ($token_expiry && time() > (int) $token_expiry) {
552 self::showVerificationError(__('This verification link has expired. Please register again.', 'yatra'));
553 return;
554 }
555
556 // Mark as verified
557 update_user_meta($user_id, 'yatra_email_verified', '1');
558 delete_user_meta($user_id, 'yatra_verification_token');
559 delete_user_meta($user_id, 'yatra_verification_token_expiry');
560
561 self::showVerificationSuccess(false);
562 return;
563 }
564
565 /**
566 * Send verification email
567 */
568 private static function sendVerificationEmail(int $_user_id, string $email, string $first_name, string $secure_token, bool $isResend = false): void
569 {
570 $verificationUrl = function_exists('yatra_get_email_verification_url')
571 ? yatra_get_email_verification_url($secure_token)
572 : home_url('/yatra-verify-email/' . rawurlencode($secure_token) . '/');
573
574 $siteName = get_bloginfo('name');
575 $introParagraph = $isResend
576 ? sprintf(
577 /* translators: %s: site name */
578 __('You requested a new verification link for your account at %s. Click the button below to verify your email address.', 'yatra'),
579 $siteName
580 )
581 : sprintf(
582 /* translators: %s: site name */
583 __('Thank you for registering at %s. Please verify your email address to activate your account.', 'yatra'),
584 $siteName
585 );
586
587 $footerNote = $isResend
588 ? __('If you did not request this email, you can ignore it.', 'yatra')
589 : __('If you did not create this account, you can ignore this email.', 'yatra');
590
591 $expiryNoticeHtml = esc_html(
592 sprintf(
593 /* translators: %d: hours until link expiry */
594 __('This verification link expires in %d hours for your security.', 'yatra'),
595 24
596 )
597 );
598
599 \Yatra\Services\TransactionalEmailTemplateService::sendIfEnabled(
600 \Yatra\Services\TransactionalEmailTemplateService::TYPE_CUSTOMER_EMAIL_VERIFICATION,
601 $email,
602 [
603 'customer_first_name' => $first_name,
604 'customer_name' => $first_name,
605 'customer_email' => $email,
606 'verification_link' => $verificationUrl,
607 'intro_paragraph' => $introParagraph,
608 'footer_note' => $footerNote,
609 'expiry_notice_html' => $expiryNoticeHtml,
610 ]
611 );
612 }
613
614 /**
615 * Show verification error page (hard-fail dead-end with a route back home).
616 * Every string is translatable — operators run Yatra in many locales and
617 * the pre-3.0.5 hardcoded "Verification Failed" heading was untranslatable.
618 */
619 private static function showVerificationError(string $message): void
620 {
621 $heading = esc_html__('Verification Failed', 'yatra');
622 $cta = esc_html__('Go to Homepage', 'yatra');
623
624 wp_die(
625 '<div style="text-align: center; padding: 50px; font-family: -apple-system, BlinkMacSystemFont, \'Segoe UI\', Roboto, sans-serif;">
626 <h1 style="color: #dc2626; margin-bottom: 20px;">' . $heading . '</h1>
627 <p style="color: #4b5563; font-size: 16px; margin-bottom: 30px;">' . esc_html($message) . '</p>
628 <a href="' . esc_url(home_url()) . '" style="display: inline-block; background: #3b82f6; color: #fff; padding: 12px 24px; border-radius: 8px; text-decoration: none; font-weight: 600;">' . $cta . '</a>
629 </div>',
630 __('Verification Failed', 'yatra'),
631 ['response' => 400]
632 );
633 }
634
635 /**
636 * Render an unambiguous "email verified" confirmation page.
637 *
638 * The pre-3.0.5 flow silently 302-redirected to the checkout URL with a
639 * `?email_verified=1` flag, relying on `booking-auth.php` to surface a
640 * one-liner notice. That notice only renders when the auth form itself
641 * renders (no booking session / logged-in user → notice never shown), so
642 * customers regularly saw "nothing happened" after clicking the link.
643 *
644 * Now we render a dedicated success page with explicit confirmation, the
645 * verified email-state, and a primary CTA back into the booking flow.
646 * Idempotent: a second click on the same link reaches `$already=true`
647 * and shows "Your email is already verified" instead of the misleading
648 * "Invalid or expired link" error.
649 *
650 * @param bool $already True when the user has already been verified by an
651 * earlier click on the same link (idempotent path).
652 */
653 private static function showVerificationSuccess(bool $already): void
654 {
655 $checkoutUrl = function_exists('yatra_get_checkout_url')
656 ? yatra_get_checkout_url()
657 : home_url('/');
658 $continueUrl = add_query_arg(['email_verified' => '1'], $checkoutUrl);
659
660 $heading = $already
661 ? esc_html__('Email Already Verified', 'yatra')
662 : esc_html__('Email Verified', 'yatra');
663 $message = $already
664 ? esc_html__('Your email address is already verified — no further action is needed. You can continue with your booking.', 'yatra')
665 : esc_html__('Your email address has been verified successfully. You can now log in and continue with your booking.', 'yatra');
666 $cta = esc_html__('Continue to Checkout', 'yatra');
667 $homeCta = esc_html__('Go to Homepage', 'yatra');
668 $title = $already
669 ? __('Email Already Verified', 'yatra')
670 : __('Email Verified', 'yatra');
671
672 // Inline-only styling so the page renders correctly regardless of
673 // theme stylesheet load order (wp_die() can fire before themes
674 // enqueue their styles).
675 $body = '<div style="text-align: center; padding: 50px 20px; max-width: 520px; margin: 0 auto; font-family: -apple-system, BlinkMacSystemFont, \'Segoe UI\', Roboto, sans-serif;">'
676 . '<div style="display: inline-flex; align-items: center; justify-content: center; width: 72px; height: 72px; border-radius: 50%; background: #d1fae5; margin: 0 auto 24px;">'
677 . '<svg width="40" height="40" viewBox="0 0 24 24" fill="none" stroke="#059669" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">'
678 . '<polyline points="20 6 9 17 4 12"></polyline>'
679 . '</svg>'
680 . '</div>'
681 . '<h1 style="color: #065f46; margin: 0 0 12px; font-size: 26px;">' . $heading . '</h1>'
682 . '<p style="color: #4b5563; font-size: 16px; line-height: 1.6; margin: 0 0 28px;">' . $message . '</p>'
683 . '<a href="' . esc_url($continueUrl) . '" style="display: inline-block; background: #059669; color: #fff; padding: 12px 28px; border-radius: 8px; text-decoration: none; font-weight: 600; margin-right: 8px;">' . $cta . '</a>'
684 . '<a href="' . esc_url(home_url()) . '" style="display: inline-block; color: #4b5563; padding: 12px 16px; text-decoration: none; font-weight: 500;">' . $homeCta . '</a>'
685 . '</div>';
686
687 wp_die($body, $title, ['response' => 200]);
688 }
689
690 /**
691 * Inform users that the link is only for email template previews.
692 */
693 private static function showEmailVerificationPreviewNotice(): void
694 {
695 $body = '<div style="text-align: center; padding: 50px; max-width: 520px; margin: 0 auto; font-family: -apple-system, BlinkMacSystemFont, \'Segoe UI\', Roboto, sans-serif;">
696 <h1 style="color: #1e40af; margin-bottom: 16px;">' . esc_html__('Sample verification link', 'yatra') . '</h1>
697 <p style="color: #4b5563; font-size: 16px; line-height: 1.6; margin-bottom: 24px;">' . esc_html__(
698 'This URL is used only in email previews and test messages. It does not verify an account. Use the link from your real verification email to activate your account.',
699 'yatra'
700 ) . '</p>
701 <a href="' . esc_url(home_url()) . '" style="display: inline-block; background: #3b82f6; color: #fff; padding: 12px 24px; border-radius: 8px; text-decoration: none; font-weight: 600;">' . esc_html__('Go to homepage', 'yatra') . '</a>
702 </div>';
703
704 wp_die(
705 $body,
706 __('Email verification (preview)', 'yatra'),
707 ['response' => 200]
708 );
709 }
710 }
711
712