'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 = get_query_var('yatra_verify_email');
if (empty($secure_token)) {
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
{
$verification_url = home_url('/yatra-verify-email/' . $secure_token . '/');
$site_name = get_bloginfo('name');
$subject = sprintf(__('[%s] Please verify your email address', 'yatra'), $site_name);
$intro = $isResend
? __("You requested a new verification link for your account at %s.", 'yatra')
: __("Thank you for registering at %s.", 'yatra');
$message = sprintf(
__("Hello %s,\n\n" . $intro . "\n\nPlease click the link below to verify your email address:\n\n%s\n\nThis link will expire in 24 hours.\n\nIf you did not " . ($isResend ? "request this" : "create this account") . ", please ignore this email.\n\nBest regards,\n%s", 'yatra'),
$first_name,
$site_name,
$verification_url,
$site_name
);
$headers = ['Content-Type: text/plain; charset=UTF-8'];
wp_mail($email, $subject, $message, $headers);
}
/**
* Show verification error page
*/
private static function showVerificationError(string $message): void
{
wp_die(
'',
__('Verification Failed', 'yatra'),
['response' => 400]
);
}
}