| Server IP : 104.171.130.249 / Your IP : 216.73.216.146 Web Server : nginx/1.25.5 System : Linux 0dac65491b7e 6.8.0-134-generic #134-Ubuntu SMP PREEMPT_DYNAMIC Fri Jun 26 18:43:11 UTC 2026 x86_64 User : root ( 0) PHP Version : 8.4.23 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : OFF | Perl : ON | Python : OFF | Sudo : OFF | Pkexec : OFF Directory : /var/www/html/wp-content/themes/matreshka/inc/ |
Upload File : |
<?php
/**
* Multi-step Registration: Phone → Profile → Email → Address
* SMS verification via sms.ru API
*/
// SMS API key must be in wp-config.php:
// define( 'MATRESHKA_SMS_API_ID', 'your-api-key-here' );
if ( ! defined( 'MATRESHKA_SMS_API_ID' ) ) {
return; // SMS API key not configured
}
define( 'MATRESHKA_SMS_CODE_TTL', 300 ); // 5 min
define( 'MATRESHKA_SMS_COOLDOWN', 60 ); // 60 sec between sends
define( 'MATRESHKA_SMS_IP_LIMIT', 10 ); // max SMS per IP per hour
define( 'MATRESHKA_EMAIL_TOKEN_TTL', 86400 ); // 24h email token expiry
// === ROUTE: /login/ (universal: login + registration) ===
add_action( 'init', function() {
add_rewrite_rule( '^login/?$', 'index.php?matreshka_register=1', 'top' );
add_rewrite_rule( '^register/?$', 'index.php?matreshka_register=1', 'top' ); // backward compat
} );
add_filter( 'query_vars', function( $vars ) {
$vars[] = 'matreshka_register';
return $vars;
} );
add_filter( 'template_include', function( $template ) {
if ( get_query_var( 'matreshka_register' ) ) {
$t = get_theme_file_path( 'page-registration.php' );
if ( file_exists( $t ) ) return $t;
}
return $template;
} );
// Auto-flush rewrite rules once
add_action( 'init', function() {
if ( ! get_option( 'matreshka_reg_rewrite_v2' ) ) {
flush_rewrite_rules();
update_option( 'matreshka_reg_rewrite_v2', 1 );
}
}, 99 );
// === REDIRECT /my-account/ → /login/ for guests ===
add_action( 'template_redirect', function() {
// Skip admin area and login pages
if ( is_admin() || strpos( $_SERVER['REQUEST_URI'], 'wp-login' ) !== false || strpos( $_SERVER['REQUEST_URI'], 'wp-admin' ) !== false ) {
return;
}
// Logged-in user with completed registration → redirect /login/ to home
if ( is_user_logged_in() && get_query_var( 'matreshka_register' ) ) {
$step = get_user_meta( get_current_user_id(), '_matreshka_reg_step', true );
if ( $step === 'complete' ) {
wp_safe_redirect( home_url( '/' ) );
exit;
}
}
if ( ! is_user_logged_in() && function_exists( 'is_account_page' ) && is_account_page() ) {
wp_safe_redirect( home_url( '/login/' ) );
exit;
}
} );
// === EMAIL VERIFICATION HANDLER ===
add_action( 'init', function() {
if ( ! isset( $_GET['verify_email'], $_GET['uid'] ) ) return;
$token = sanitize_text_field( $_GET['verify_email'] );
$user_id = absint( $_GET['uid'] );
$saved = get_user_meta( $user_id, '_matreshka_email_token', true );
if ( ! $saved || ! hash_equals( $saved, $token ) ) {
wp_redirect( home_url( '/login/?error=invalid_token' ) );
exit;
}
// Check token expiry
$token_time = get_user_meta( $user_id, '_matreshka_email_token_time', true );
if ( $token_time && ( time() - (int) $token_time ) > MATRESHKA_EMAIL_TOKEN_TTL ) {
delete_user_meta( $user_id, '_matreshka_email_token' );
delete_user_meta( $user_id, '_matreshka_email_token_time' );
wp_redirect( home_url( '/login/?error=token_expired' ) );
exit;
}
update_user_meta( $user_id, '_matreshka_email_verified', 1 );
delete_user_meta( $user_id, '_matreshka_email_token' );
$step = get_user_meta( $user_id, '_matreshka_reg_step', true );
if ( (int) $step === 3 ) {
update_user_meta( $user_id, '_matreshka_reg_step', 4 );
}
if ( ! is_user_logged_in() ) {
wp_set_current_user( $user_id );
wp_set_auth_cookie( $user_id );
}
wp_redirect( home_url( '/login/?email_verified=1' ) );
exit;
}, 5 );
// === ENQUEUE ASSETS ===
add_action( 'wp_enqueue_scripts', function() {
if ( ! get_query_var( 'matreshka_register' ) ) return;
wp_enqueue_style( 'matreshka-registration', get_template_directory_uri() . '/assets/css/registration.css', [ 'matreshka-bootstrap' ], '1.3.2' );
wp_enqueue_script( 'matreshka-registration', get_template_directory_uri() . '/assets/js/registration.js', [ 'jquery' ], '1.3.1', true );
$step = 1;
if ( is_user_logged_in() ) {
$step = get_user_meta( get_current_user_id(), '_matreshka_reg_step', true ) ?: 2;
if ( $step === 'complete' ) $step = 'complete';
}
wp_localize_script( 'matreshka-registration', 'matreshka_reg', [
'ajax_url' => admin_url( 'admin-ajax.php' ),
'nonce' => wp_create_nonce( 'matreshka_reg' ),
'step' => $step,
'cooldown' => MATRESHKA_SMS_COOLDOWN,
'home_url' => home_url(),
'email_verified' => isset( $_GET['email_verified'] ) ? 1 : 0,
] );
} );
// === HELPERS ===
/**
* Check if IP is public (not private/local)
*/
function matreshka_is_public_ip( $ip ) {
if ( ! filter_var( $ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE ) ) {
return false;
}
return true;
}
/**
* Send SMS via sms.ru API (POST method, as recommended by docs)
* Docs: https://sms.ru/api/send
*
* @param string $phone Phone number (e.g. 79991234567)
* @param string $message SMS text (UTF-8)
* @param string|null $user_ip User IP for anti-fraud (optional)
* @return array ['ok' => bool, 'gateway' => mixed]
*/
function matreshka_sms_send( $phone, $message, $user_ip = null ) {
$params = [
'api_id' => MATRESHKA_SMS_API_ID,
'to' => $phone,
'msg' => $message,
'json' => 1,
];
// Pass user IP for sms.ru anti-fraud protection (only if public IP)
if ( $user_ip && matreshka_is_public_ip( $user_ip ) ) {
$params['ip'] = $user_ip;
}
$resp = wp_remote_post( 'https://sms.ru/sms/send', [
'timeout' => 15,
'body' => $params,
] );
if ( is_wp_error( $resp ) ) {
return [ 'ok' => false, 'gateway' => 'WP Error: ' . $resp->get_error_message() ];
}
$raw = wp_remote_retrieve_body( $resp );
$body = json_decode( $raw, true );
$ok = isset( $body['status'] ) && $body['status'] === 'OK'
&& isset( $body['sms'][ $phone ]['status'] ) && $body['sms'][ $phone ]['status'] === 'OK';
return [ 'ok' => $ok, 'gateway' => $body ?: $raw ];
}
function matreshka_send_verification_email( $user_id, $email ) {
$token = wp_generate_password( 48, false );
update_user_meta( $user_id, '_matreshka_email_token', $token );
update_user_meta( $user_id, '_matreshka_email_token_time', time() );
$url = home_url( "/login/?verify_email={$token}&uid={$user_id}" );
$name = get_user_meta( $user_id, 'first_name', true ) ?: 'друг';
$logo = defined( 'MATRESHKA_LOGO_URL' ) ? MATRESHKA_LOGO_URL : '';
$year = date( 'Y' );
$logo_html = $logo
? '<img src="' . esc_url( $logo ) . '" alt="Матрёшка" style="height:60px;display:block;margin:0 auto 8px;" />'
: '<h1 style="color:#ffffff;margin:0;font-size:24px;font-weight:700;letter-spacing:0.5px;">Матрёшка</h1>';
$html = '
<!DOCTYPE html>
<html lang="ru">
<head><meta charset="UTF-8"></head>
<body style="margin:0;padding:0;background-color:#F4F4F4;font-family:Arial,Helvetica,sans-serif;">
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="background-color:#F4F4F4;padding:32px 16px;">
<tr><td align="center">
<table role="presentation" width="500" cellpadding="0" cellspacing="0" style="background-color:#ffffff;border-radius:16px;overflow:hidden;max-width:500px;">
<!-- Header -->
<tr>
<td style="background:linear-gradient(135deg,#A92231 0%,#E02842 100%);padding:15px 24px;text-align:center;">
' . $logo_html . '
</td>
</tr>
<!-- Body -->
<tr>
<td style="padding:32px 24px;">
<p style="color:#333333;font-size:18px;margin:0 0 8px;font-weight:700;">Привет, ' . esc_html( $name ) . '! 👋</p>
<p style="color:#666666;font-size:15px;line-height:1.6;margin:0 0 24px;">Спасибо за регистрацию. Для завершения подтвердите вашу электронную почту, нажав на кнопку ниже.</p>
<table role="presentation" width="100%" cellpadding="0" cellspacing="0">
<tr><td align="center" style="padding:8px 0 24px;">
<a href="' . esc_url( $url ) . '" style="display:inline-block;background:linear-gradient(90deg,#A92231,#E02842);color:#ffffff;padding:16px 48px;border-radius:32px;text-decoration:none;font-weight:700;font-size:16px;letter-spacing:0.3px;">Подтвердить email</a>
</td></tr>
</table>
<p style="color:#999999;font-size:13px;line-height:1.5;margin:0 0 16px;">Если кнопка не работает, скопируйте и вставьте эту ссылку в браузер:</p>
<p style="color:#A92231;font-size:12px;word-break:break-all;margin:0 0 24px;">' . esc_url( $url ) . '</p>
<hr style="border:none;border-top:1px solid #EEEEEE;margin:24px 0;" />
<p style="color:#BBBBBB;font-size:12px;line-height:1.5;margin:0;">Если вы не регистрировались в Матрёшке — просто проигнорируйте это письмо. Ссылка действительна 24 часа.</p>
</td>
</tr>
<!-- Footer -->
<tr>
<td style="background-color:#FAFAFA;padding:20px 24px;text-align:center;border-top:1px solid #EEEEEE;">
<p style="color:#BBBBBB;font-size:12px;margin:0;">© ' . $year . ' Матрёшка. Все права защищены.</p>
</td>
</tr>
</table>
</td></tr>
</table>
</body>
</html>';
wp_mail( $email, 'Матрёшка — подтверждение email', $html, [ 'Content-Type: text/html; charset=UTF-8' ] );
}
// === AJAX: SEND SMS ===
add_action( 'wp_ajax_nopriv_matreshka_reg_send_sms', 'matreshka_reg_send_sms' );
add_action( 'wp_ajax_matreshka_reg_send_sms', 'matreshka_reg_send_sms' );
function matreshka_reg_send_sms() {
check_ajax_referer( 'matreshka_reg' );
$phone = preg_replace( '/\D/', '', sanitize_text_field( $_POST['phone'] ?? '' ) );
if ( strlen( $phone ) !== 11 || $phone[0] !== '7' ) {
wp_send_json_error( [ 'message' => 'Неверный номер телефона' ] );
}
// IP-based rate limit
$ip = $_SERVER['REMOTE_ADDR'] ?? '0.0.0.0';
$ip_key = 'matreshka_sms_ip_' . md5( $ip );
$ip_hits = (int) get_transient( $ip_key );
if ( $ip_hits >= MATRESHKA_SMS_IP_LIMIT ) {
wp_send_json_error( [ 'message' => 'Слишком много запросов. Попробуйте позже.' ] );
}
$key = 'matreshka_sms_' . $phone;
$existing = get_transient( $key );
if ( $existing && ( time() - $existing['sent_at'] ) < MATRESHKA_SMS_COOLDOWN ) {
$wait = MATRESHKA_SMS_COOLDOWN - ( time() - $existing['sent_at'] );
wp_send_json_error( [ 'message' => "Подождите {$wait} сек.", 'wait' => $wait ] );
}
$code = str_pad( random_int( 0, 999999 ), 6, '0', STR_PAD_LEFT );
set_transient( $key, [
'code' => $code,
'sent_at' => time(),
'attempts' => 0,
], MATRESHKA_SMS_CODE_TTL );
$result = matreshka_sms_send( $phone, "Матрёшка: ваш код {$code}", $ip );
if ( ! $result['ok'] ) {
wp_send_json_error( [
'message' => 'Ошибка отправки SMS. Попробуйте позже.',
// 'gateway' => $result['gateway'], // DEBUG: uncomment to see sms.ru response
] );
}
// Increment IP counter
set_transient( $ip_key, $ip_hits + 1, HOUR_IN_SECONDS );
wp_send_json_success( [ 'message' => 'Код отправлен', 'cooldown' => MATRESHKA_SMS_COOLDOWN ] );
}
// === AJAX: VERIFY CODE ===
add_action( 'wp_ajax_nopriv_matreshka_reg_verify_code', 'matreshka_reg_verify_code' );
add_action( 'wp_ajax_matreshka_reg_verify_code', 'matreshka_reg_verify_code' );
function matreshka_reg_verify_code() {
check_ajax_referer( 'matreshka_reg' );
$phone = preg_replace( '/\D/', '', sanitize_text_field( $_POST['phone'] ?? '' ) );
$code = sanitize_text_field( $_POST['code'] ?? '' );
$key = 'matreshka_sms_' . $phone;
$data = get_transient( $key );
if ( ! $data ) {
wp_send_json_error( [ 'message' => 'Код истёк. Запросите новый.' ] );
}
if ( $data['attempts'] >= 5 ) {
delete_transient( $key );
wp_send_json_error( [ 'message' => 'Слишком много попыток. Запросите новый код.' ] );
}
if ( $data['code'] !== $code ) {
$data['attempts']++;
set_transient( $key, $data, MATRESHKA_SMS_CODE_TTL );
wp_send_json_error( [ 'message' => 'Неверный код' ] );
}
delete_transient( $key );
// Check existing user with this phone
$users = get_users( [ 'meta_key' => '_matreshka_phone', 'meta_value' => $phone, 'number' => 1 ] );
if ( ! empty( $users ) ) {
$user = $users[0];
wp_set_current_user( $user->ID );
wp_set_auth_cookie( $user->ID );
$step = get_user_meta( $user->ID, '_matreshka_reg_step', true );
if ( $step === 'complete' ) {
wp_send_json_success( [ 'step' => 'complete', 'redirect' => home_url(), 'nonce' => wp_create_nonce( 'matreshka_reg' ) ] );
}
wp_send_json_success( [ 'step' => (int) $step, 'nonce' => wp_create_nonce( 'matreshka_reg' ) ] );
}
// Create new user
$username = 'user_' . $phone;
$user_id = wp_create_user( $username, wp_generate_password( 16 ), $phone . '@phone.matreshka.local' );
if ( is_wp_error( $user_id ) ) {
wp_send_json_error( [ 'message' => 'Ошибка создания аккаунта' ] );
}
$user = new WP_User( $user_id );
$user->set_role( 'customer' );
update_user_meta( $user_id, '_matreshka_phone', $phone );
update_user_meta( $user_id, '_matreshka_reg_step', 2 );
update_user_meta( $user_id, 'billing_phone', '+' . $phone );
wp_set_current_user( $user_id );
wp_set_auth_cookie( $user_id );
wp_send_json_success( [ 'step' => 2, 'nonce' => wp_create_nonce( 'matreshka_reg' ) ] );
}
// === AJAX: SAVE PROFILE (Step 2 → 3) ===
add_action( 'wp_ajax_matreshka_reg_save_profile', 'matreshka_reg_save_profile' );
function matreshka_reg_save_profile() {
check_ajax_referer( 'matreshka_reg' );
if ( ! is_user_logged_in() ) wp_send_json_error( [ 'message' => 'Не авторизован' ] );
$user_id = get_current_user_id();
$name = sanitize_text_field( $_POST['name'] ?? '' );
$email = sanitize_email( $_POST['email'] ?? '' );
$birthday = sanitize_text_field( $_POST['birthday'] ?? '' );
// Validate birthday format (YYYY-MM-DD)
if ( $birthday && ! preg_match( '/^\d{4}-\d{2}-\d{2}$/', $birthday ) ) {
wp_send_json_error( [ 'message' => 'Некорректная дата рождения' ] );
}
if ( ! $name || ! $email ) {
wp_send_json_error( [ 'message' => 'Заполните имя и email' ] );
}
if ( empty( $_POST['consent_personal_data'] ) ) {
wp_send_json_error( [ 'message' => 'Необходимо согласие на обработку данных' ] );
}
// Check unique email
$email_user = get_user_by( 'email', $email );
if ( $email_user && $email_user->ID !== $user_id ) {
wp_send_json_error( [ 'message' => 'Этот email уже используется' ] );
}
wp_update_user( [
'ID' => $user_id,
'user_email' => $email,
'first_name' => $name,
'display_name' => $name,
] );
update_user_meta( $user_id, '_matreshka_birthday', $birthday );
update_user_meta( $user_id, '_matreshka_want_receipts', ! empty( $_POST['want_receipts'] ) ? 1 : 0 );
update_user_meta( $user_id, '_matreshka_consent_personal_data', 1 );
update_user_meta( $user_id, '_matreshka_want_promotions', ! empty( $_POST['want_promotions'] ) ? 1 : 0 );
update_user_meta( $user_id, 'billing_first_name', $name );
update_user_meta( $user_id, 'billing_email', $email );
// Send verification email
matreshka_send_verification_email( $user_id, $email );
update_user_meta( $user_id, '_matreshka_reg_step', 3 );
wp_send_json_success( [ 'step' => 3, 'email' => $email ] );
}
// === AJAX: RESEND EMAIL ===
add_action( 'wp_ajax_matreshka_reg_resend_email', 'matreshka_reg_resend_email' );
function matreshka_reg_resend_email() {
check_ajax_referer( 'matreshka_reg' );
if ( ! is_user_logged_in() ) wp_send_json_error( [ 'message' => 'Не авторизован' ] );
$user_id = get_current_user_id();
// Rate limit: 1 resend per 60 seconds
$last_sent = get_user_meta( $user_id, '_matreshka_email_sent_at', true );
if ( $last_sent && ( time() - (int) $last_sent ) < 60 ) {
$wait = 60 - ( time() - (int) $last_sent );
wp_send_json_error( [ 'message' => "Подождите {$wait} сек." ] );
}
$user = get_userdata( $user_id );
matreshka_send_verification_email( $user_id, $user->user_email );
update_user_meta( $user_id, '_matreshka_email_sent_at', time() );
wp_send_json_success( [ 'message' => 'Письмо отправлено повторно' ] );
}
// === AJAX: CONTINUE (Step 3 → 4) ===
add_action( 'wp_ajax_matreshka_reg_continue', 'matreshka_reg_continue' );
function matreshka_reg_continue() {
check_ajax_referer( 'matreshka_reg' );
if ( ! is_user_logged_in() ) wp_send_json_error( [ 'message' => 'Не авторизован' ] );
update_user_meta( get_current_user_id(), '_matreshka_reg_step', 4 );
wp_send_json_success( [ 'step' => 4 ] );
}
// === AJAX: SAVE ADDRESS (Step 4 → complete) ===
add_action( 'wp_ajax_matreshka_reg_save_address', 'matreshka_reg_save_address' );
function matreshka_reg_save_address() {
check_ajax_referer( 'matreshka_reg' );
if ( ! is_user_logged_in() ) wp_send_json_error( [ 'message' => 'Не авторизован' ] );
$user_id = get_current_user_id();
$address = sanitize_text_field( $_POST['address'] ?? '' );
if ( ! $address ) {
wp_send_json_error( [ 'message' => 'Введите адрес доставки' ] );
}
update_user_meta( $user_id, 'shipping_address_1', $address );
update_user_meta( $user_id, 'billing_address_1', $address );
update_user_meta( $user_id, '_matreshka_entrance', sanitize_text_field( $_POST['entrance'] ?? '' ) );
update_user_meta( $user_id, '_matreshka_intercom', sanitize_text_field( $_POST['intercom'] ?? '' ) );
update_user_meta( $user_id, '_matreshka_floor', sanitize_text_field( $_POST['floor'] ?? '' ) );
update_user_meta( $user_id, '_matreshka_apartment', sanitize_text_field( $_POST['apartment'] ?? '' ) );
update_user_meta( $user_id, '_matreshka_courier_comment', sanitize_text_field( $_POST['courier_comment'] ?? '' ) );
update_user_meta( $user_id, '_matreshka_reg_step', 'complete' );
wp_send_json_success( [ 'step' => 'complete', 'redirect' => home_url() ] );
}
// === AJAX: SKIP ADDRESS ===
add_action( 'wp_ajax_matreshka_reg_skip_address', 'matreshka_reg_skip_address' );
function matreshka_reg_skip_address() {
check_ajax_referer( 'matreshka_reg' );
if ( ! is_user_logged_in() ) wp_send_json_error( [ 'message' => 'Не авторизован' ] );
update_user_meta( get_current_user_id(), '_matreshka_reg_step', 'complete' );
wp_send_json_success( [ 'step' => 'complete', 'redirect' => home_url() ] );
}