'POST',
'callback' => [self::class, 'login'],
'permission_callback' => '__return_true',
]);
register_rest_route('yatra/v1', '/auth/register', [
'methods' => 'POST',
'callback' => [self::class, 'register'],
'permission_callback' => '__return_true',
]);
register_rest_route('yatra/v1', '/auth/resend-verification', [
'methods' => 'POST',
'callback' => [self::class, 'resendVerification'],
'permission_callback' => '__return_true',
]);
}
/**
* Register Yatra Customer role
*/
public static function registerCustomerRole(): void
{
if (!get_role('yatra_customer')) {
add_role(
'yatra_customer',
__('Yatra Customer', 'yatra'),
[
'read' => true,
'edit_posts' => false,
'delete_posts' => false,
]
);
}
}
/**
* Block unverified users from WordPress login
*
* @param \WP_User|\WP_Error|null $user
* @param string $username
* @param string $password
* @return \WP_User|\WP_Error|null
*/
public static function blockUnverifiedUserLogin($user, string $username, string $password)
{
if (is_wp_error($user)) {
return $user;
}
if (!($user instanceof \WP_User)) {
return $user;
}
$has_verification_token = get_user_meta($user->ID, 'yatra_verification_token', true);
$email_verified = get_user_meta($user->ID, 'yatra_email_verified', true);
$needs_verification = !empty($has_verification_token);
if (!$needs_verification) {
$meta_exists = metadata_exists('user', $user->ID, 'yatra_email_verified');
$needs_verification = $meta_exists && !$email_verified;
}
if ($needs_verification) {
$resend_url = add_query_arg([
'action' => 'yatra_resend_verification',
'email' => urlencode($user->user_email),
'_wpnonce' => wp_create_nonce('yatra_resend_verification'),
], wp_login_url());
$message = sprintf(
/* translators: %s: resend verification link */
__('Error: Please verify your email address before logging in. Check your inbox for the verification link.
Click here to resend verification email', 'yatra'),
esc_url($resend_url)
);
return new \WP_Error('email_not_verified', $message);
}
return $user;
}
/**
* Handle resend verification from WordPress login page
*/
public static function handleWpLoginResendVerification(): void
{
if (!isset($_GET['action']) || $_GET['action'] !== 'yatra_resend_verification') {
return;
}
// Verify nonce
if (!isset($_GET['_wpnonce']) || !wp_verify_nonce(sanitize_text_field($_GET['_wpnonce']), 'yatra_resend_verification')) {
wp_die(__('Your session has expired. Please go back and try again.', 'yatra'));
}
$email = isset($_GET['email']) ? sanitize_email(urldecode($_GET['email'])) : '';
if (empty($email) || !is_email($email)) {
wp_safe_redirect(add_query_arg('login_error', 'invalid_email', wp_login_url()));
exit;
}
$user = get_user_by('email', $email);
if (!$user) {
// Redirect with generic success message (don't reveal if email exists)
wp_safe_redirect(add_query_arg('checkemail', 'resent', wp_login_url()));
exit;
}
// Check if already verified
$email_verified = get_user_meta($user->ID, 'yatra_email_verified', true);
if ($email_verified === '1') {
wp_safe_redirect(add_query_arg('login_error', 'already_verified', wp_login_url()));
exit;
}
// Check rate limiting
$last_sent = get_user_meta($user->ID, 'yatra_verification_last_sent', true);
if ($last_sent && (time() - (int) $last_sent) < 120) {
$remaining = 120 - (time() - (int) $last_sent);
wp_safe_redirect(add_query_arg([
'login_error' => 'rate_limited',
'wait' => $remaining,
], wp_login_url()));
exit;
}
// Generate new token and send email
$verification_token = wp_generate_password(32, false);
$secure_token = base64_encode($user->ID . '|' . $verification_token . '|' . time());
$secure_token = str_replace(['+', '/', '='], ['-', '_', ''], $secure_token);
update_user_meta($user->ID, 'yatra_verification_token', $verification_token);
update_user_meta($user->ID, 'yatra_verification_token_expiry', time() + (24 * 60 * 60));
update_user_meta($user->ID, 'yatra_verification_last_sent', time());
// Send email
$first_name = get_user_meta($user->ID, 'first_name', true) ?: $user->display_name;
self::sendVerificationEmail($user->ID, $email, $first_name, $secure_token, true);
wp_safe_redirect(add_query_arg('checkemail', 'resent', wp_login_url()));
exit;
}
/**
* Add custom messages to WordPress login page
*/
public static function customLoginMessages(string $message): string
{
if (isset($_GET['checkemail']) && $_GET['checkemail'] === 'resent') {
$message = '
' . __('A new verification link has been sent to your email address. Please check your inbox and spam folder.', 'yatra') . '
';
}
if (isset($_GET['email_verified']) && $_GET['email_verified'] === '1') {
$message = '' . __('Your email has been verified successfully! You can now log in.', 'yatra') . '
';
}
return $message;
}
/**
* Add custom error messages to WordPress login page
*/
public static function customLoginErrors(\WP_Error $errors): \WP_Error
{
if (isset($_GET['login_error'])) {
switch ($_GET['login_error']) {
case 'invalid_email':
$errors->add('invalid_email', __('Error: Invalid email address.', 'yatra'));
break;
case 'already_verified':
$errors->add('already_verified', __('Notice: Your email is already verified. Please log in.', 'yatra'));
break;
case 'rate_limited':
$wait = isset($_GET['wait']) ? (int) $_GET['wait'] : 120;
$errors->add('rate_limited', sprintf(
/* translators: %d: number of seconds to wait */
__('Error: Please wait %d seconds before requesting another verification email.', 'yatra'),
$wait
));
break;
}
}
return $errors;
}
/**
* Handle login request
*/
public static function login(\WP_REST_Request $request): \WP_REST_Response
{
$username = sanitize_user($request->get_param('username') ?? '');
$password = $request->get_param('password') ?? '';
$remember = !empty($request->get_param('remember'));
if (empty($username) || empty($password)) {
return new \WP_REST_Response([
'success' => false,
'message' => __('Please enter your username/email and password.', 'yatra'),
], 400);
}
// Get user to check verification status
$user_check = get_user_by('login', $username);
if (!$user_check) {
$user_check = get_user_by('email', $username);
}
// Check email verification for Yatra users
if ($user_check) {
$has_verification_token = get_user_meta($user_check->ID, 'yatra_verification_token', true);
$email_verified = get_user_meta($user_check->ID, 'yatra_email_verified', true);
if (!empty($has_verification_token)) {
return new \WP_REST_Response([
'success' => false,
'message' => __('Please verify your email address before logging in. Check your inbox for the verification link.', 'yatra'),
'needs_verification' => true,
'email' => $user_check->user_email,
], 403);
}
$meta_exists = metadata_exists('user', $user_check->ID, 'yatra_email_verified');
if ($meta_exists && !$email_verified) {
return new \WP_REST_Response([
'success' => false,
'message' => __('Please verify your email address before logging in. Check your inbox for the verification link.', 'yatra'),
'needs_verification' => true,
'email' => $user_check->user_email,
], 403);
}
}
// Authenticate
$user = wp_signon([
'user_login' => $username,
'user_password' => $password,
'remember' => $remember,
], is_ssl());
if (is_wp_error($user)) {
$error_message = wp_strip_all_tags($user->get_error_message());
if (strpos($error_message, 'incorrect') !== false || strpos($error_message, 'Invalid') !== false) {
$error_message = __('Invalid username/email or password. Please try again.', 'yatra');
}
return new \WP_REST_Response([
'success' => false,
'message' => $error_message,
], 401);
}
wp_set_current_user($user->ID);
return new \WP_REST_Response([
'success' => true,
'message' => __('Login successful! Redirecting...', 'yatra'),
'user_id' => $user->ID,
]);
}
/**
* Handle registration request
*/
public static function register(\WP_REST_Request $request): \WP_REST_Response
{
if (!\Yatra\Services\SettingsService::isEnabled('customer_registration')) {
return new \WP_REST_Response([
'success' => false,
'message' => __('New customer registration is disabled.', 'yatra'),
], 403);
}
$first_name = sanitize_text_field($request->get_param('first_name') ?? '');
$last_name = sanitize_text_field($request->get_param('last_name') ?? '');
$email = sanitize_email($request->get_param('email') ?? '');
$phone = sanitize_text_field($request->get_param('phone') ?? '');
$password = $request->get_param('password') ?? '';
$confirm_password = $request->get_param('confirm_password') ?? '';
// Validation
if (empty($first_name)) {
return new \WP_REST_Response([
'success' => false,
'message' => __('Please enter your first name.', 'yatra'),
], 400);
}
if (empty($last_name)) {
return new \WP_REST_Response([
'success' => false,
'message' => __('Please enter your last name.', 'yatra'),
], 400);
}
if (empty($email) || !is_email($email)) {
return new \WP_REST_Response([
'success' => false,
'message' => __('Please enter a valid email address.', 'yatra'),
], 400);
}
if (empty($password) || strlen($password) < 8) {
return new \WP_REST_Response([
'success' => false,
'message' => __('Password must be at least 8 characters long.', 'yatra'),
], 400);
}
if ($password !== $confirm_password) {
return new \WP_REST_Response([
'success' => false,
'message' => __('Passwords do not match.', 'yatra'),
], 400);
}
if (email_exists($email)) {
return new \WP_REST_Response([
'success' => false,
'message' => __('An account with this email already exists. Please login instead.', 'yatra'),
], 409);
}
// Generate username
$username = sanitize_user(current(explode('@', $email)));
$original_username = $username;
$counter = 1;
while (username_exists($username)) {
$username = $original_username . $counter;
$counter++;
}
// Create user
$user_id = wp_create_user($username, $password, $email);
if (is_wp_error($user_id)) {
$error_message = wp_strip_all_tags($user_id->get_error_message());
return new \WP_REST_Response([
'success' => false,
'message' => $error_message,
], 500);
}
// Assign Yatra Customer role
$user = new \WP_User($user_id);
$user->set_role('yatra_customer');
// Update user meta
wp_update_user([
'ID' => $user_id,
'first_name' => $first_name,
'last_name' => $last_name,
'display_name' => $first_name . ' ' . $last_name,
]);
if (!empty($phone)) {
update_user_meta($user_id, 'billing_phone', $phone);
update_user_meta($user_id, 'phone', $phone);
}
// Email verification
update_user_meta($user_id, 'yatra_email_verified', '0');
$verification_token = wp_generate_password(32, false);
$secure_token = base64_encode($user_id . '|' . $verification_token . '|' . time());
$secure_token = str_replace(['+', '/', '='], ['-', '_', ''], $secure_token);
update_user_meta($user_id, 'yatra_verification_token', $verification_token);
update_user_meta($user_id, 'yatra_verification_token_expiry', time() + (24 * 60 * 60));
// Send verification email
self::sendVerificationEmail($user_id, $email, $first_name, $secure_token);
// Admin notification
wp_new_user_notification($user_id, null, 'admin');
return new \WP_REST_Response([
'success' => true,
'message' => __('Registration successful! Please check your email to verify your account before logging in.', 'yatra'),
'user_id' => $user_id,
'require_verification' => true,
]);
}
/**
* Handle resend verification request
*/
public static function resendVerification(\WP_REST_Request $request): \WP_REST_Response
{
$email = sanitize_email($request->get_param('email') ?? '');
if (empty($email) || !is_email($email)) {
return new \WP_REST_Response([
'success' => false,
'message' => __('Please enter a valid email address.', 'yatra'),
], 400);
}
$user = get_user_by('email', $email);
if (!$user) {
return new \WP_REST_Response([
'success' => true,
'message' => __('If an account with this email exists and is pending verification, a new verification link has been sent.', 'yatra'),
]);
}
$email_verified = get_user_meta($user->ID, 'yatra_email_verified', true);
if ($email_verified === '1' || $email_verified === 1 || $email_verified === true) {
return new \WP_REST_Response([
'success' => false,
'message' => __('This email is already verified. You can login now.', 'yatra'),
], 400);
}
$meta_exists = metadata_exists('user', $user->ID, 'yatra_email_verified');
$has_verification_token = get_user_meta($user->ID, 'yatra_verification_token', true);
if (!$meta_exists && empty($has_verification_token)) {
return new \WP_REST_Response([
'success' => true,
'message' => __('If an account with this email exists and is pending verification, a new verification link has been sent.', 'yatra'),
]);
}
// Rate limiting
$last_sent = get_user_meta($user->ID, 'yatra_verification_last_sent', true);
if ($last_sent && (time() - (int) $last_sent) < 120) {
$remaining = 120 - (time() - (int) $last_sent);
return new \WP_REST_Response([
'success' => false,
'message' => __('Please wait before requesting another verification email.', 'yatra'),
'rate_limited' => true,
'remaining_seconds' => $remaining,
], 429);
}
// Generate new token
$verification_token = wp_generate_password(32, false);
$secure_token = base64_encode($user->ID . '|' . $verification_token . '|' . time());
$secure_token = str_replace(['+', '/', '='], ['-', '_', ''], $secure_token);
update_user_meta($user->ID, 'yatra_verification_token', $verification_token);
update_user_meta($user->ID, 'yatra_verification_token_expiry', time() + (24 * 60 * 60));
update_user_meta($user->ID, 'yatra_verification_last_sent', time());
// Send email
$first_name = get_user_meta($user->ID, 'first_name', true) ?: $user->display_name;
self::sendVerificationEmail($user->ID, $email, $first_name, $secure_token, true);
return new \WP_REST_Response([
'success' => true,
'message' => __('A new verification link has been sent to your email address. Please check your inbox.', 'yatra'),
]);
}
/**
* Handle email verification
*/
public static function handleEmailVerification(): void
{
$secure_token = (string) get_query_var('yatra_verify_email');
if ($secure_token === '' && isset($_GET['yatra_verify_email'])) {
$raw = wp_unslash($_GET['yatra_verify_email']);
$secure_token = is_string($raw) ? preg_replace('/[^a-zA-Z0-9_-]/', '', $raw) ?? '' : '';
}
if ($secure_token === '') {
self::showVerificationError(__('Invalid verification link.', 'yatra'));
return;
}
$previewToken = defined('YATRA_EMAIL_VERIFICATION_PREVIEW_TOKEN')
? (string) YATRA_EMAIL_VERIFICATION_PREVIEW_TOKEN
: 'preview-verify-token';
if ($secure_token === $previewToken) {
self::showEmailVerificationPreviewNotice();
return;
}
// Decode token
$secure_token = str_replace(['-', '_'], ['+', '/'], $secure_token);
$decoded = base64_decode($secure_token);
if (!$decoded || strpos($decoded, '|') === false) {
self::showVerificationError(__('Invalid verification link.', 'yatra'));
return;
}
$parts = explode('|', $decoded);
if (count($parts) < 2) {
self::showVerificationError(__('Invalid verification link.', 'yatra'));
return;
}
$user_id = (int) $parts[0];
$token = $parts[1];
if ($user_id <= 0 || empty($token)) {
self::showVerificationError(__('Invalid verification link.', 'yatra'));
return;
}
$stored_token = get_user_meta($user_id, 'yatra_verification_token', true);
$token_expiry = get_user_meta($user_id, 'yatra_verification_token_expiry', true);
if (empty($stored_token) || $stored_token !== $token) {
self::showVerificationError(__('Invalid or expired verification link.', 'yatra'));
return;
}
if ($token_expiry && time() > (int) $token_expiry) {
self::showVerificationError(__('This verification link has expired. Please register again.', 'yatra'));
return;
}
// Mark as verified
update_user_meta($user_id, 'yatra_email_verified', '1');
delete_user_meta($user_id, 'yatra_verification_token');
delete_user_meta($user_id, 'yatra_verification_token_expiry');
$redirect_url = add_query_arg(['email_verified' => '1'], yatra_get_checkout_url());
wp_safe_redirect($redirect_url);
exit;
}
/**
* Send verification email
*/
private static function sendVerificationEmail(int $_user_id, string $email, string $first_name, string $secure_token, bool $isResend = false): void
{
$verificationUrl = function_exists('yatra_get_email_verification_url')
? yatra_get_email_verification_url($secure_token)
: home_url('/yatra-verify-email/' . rawurlencode($secure_token) . '/');
$siteName = get_bloginfo('name');
$introParagraph = $isResend
? sprintf(
/* translators: %s: site name */
__('You requested a new verification link for your account at %s. Click the button below to verify your email address.', 'yatra'),
$siteName
)
: sprintf(
/* translators: %s: site name */
__('Thank you for registering at %s. Please verify your email address to activate your account.', 'yatra'),
$siteName
);
$footerNote = $isResend
? __('If you did not request this email, you can ignore it.', 'yatra')
: __('If you did not create this account, you can ignore this email.', 'yatra');
$expiryNoticeHtml = esc_html(
sprintf(
/* translators: %d: hours until expiry */
__('This verification link expires in %d hours for your security.', 'yatra'),
24
)
);
\Yatra\Services\TransactionalEmailTemplateService::sendIfEnabled(
\Yatra\Services\TransactionalEmailTemplateService::TYPE_CUSTOMER_EMAIL_VERIFICATION,
$email,
[
'customer_first_name' => $first_name,
'customer_name' => $first_name,
'verification_link' => $verificationUrl,
'intro_paragraph' => $introParagraph,
'footer_note' => $footerNote,
'expiry_notice_html' => $expiryNoticeHtml,
]
);
}
/**
* Show verification error page
*/
private static function showVerificationError(string $message): void
{
wp_die(
'',
__('Verification Failed', 'yatra'),
['response' => 400]
);
}
/**
* Inform users that the link is only for email template previews.
*/
private static function showEmailVerificationPreviewNotice(): void
{
$body = '
' . esc_html__('Sample verification link', 'yatra') . '
' . esc_html__(
'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.',
'yatra'
) . '
' . esc_html__('Go to homepage', 'yatra') . '
';
wp_die(
$body,
__('Email verification (preview)', 'yatra'),
['response' => 200]
);
}
}