← Blog
Developer playbook · 16 мин чтения · Published апрель 2026 · Author Serhat Dogan

Хаб миграции SMS-Activate 2026: Контрольный список для разработчиков, сопоставление API и сравнение возврата средств

Хаб миграции SMS-Activate 2026: Контрольный список для разработчиков, сопоставление API и сравнение возврата средств

If you are still carrying handler_api.php calls in your repo pointed at sms-activate.org, this is the hub you open at 9pm on a Friday and close with a working integration before midnight. Real endpoint mapping, real code diffs, refund policy comparison, and the gotchas that will bite you between the line you change and the alert that wakes you up on Monday morning.

Почему существует это руководство

Большинство публикаций типа «SMS-Activate мертв, вот список альтернатив» упускают ту часть, которая на самом деле сжигает часы разработчиков: код. Регистрация у нового поставщика занимает пять минут. Переписывание интеграции, которая незаметно работает в продакшене годами, с непроверенными крайними случаями и прикрепленным к ней трекером расходов, занимает больше времени, чем вы думаете.

После того как мы выпустили наш слой совместимости в январе 2026 года, мы начали получать одни и те же три вопроса от каждой команды, которая его опробовала:

  1. Какие конечные точки отображаются чисто, а какие требуют ручных изменений?
  2. Как сохранить отслеживание расходов и поток возврата средств без переписывания?
  3. Что ломается незаметно и проявляется через неделю как сюрприз в счетах?

Это руководство отвечает на эти три вопроса по порядку и предоставляет вам готовый код, который вы можете проверить перед запуском.

Итоги 48 часов: что на самом деле произошло

SMS-Activate отключился 29 декабря 2025 года. Не было баннера о плановом обслуживании, инструмента миграции или публичного уведомления. Пользователи, пытавшиеся войти в систему, видели одну страницу с сообщением о том, что сервис закрыт навсегда. API возвращал сбросы соединений по всем конечным точкам в течение нескольких часов.

Три вещи произошли быстро:

Последствия все еще разворачиваются. По состоянию на апрель 2026 года в России ведутся дела о мелких исках, и как минимум два скоординированных общественных иска пытаются вернуть замороженные балансы. Ничто из этого не поможет вашему коду, поэтому мы сосредоточимся на той части, которую вы действительно можете исправить.

Часть 1: Карта устаревания API

SMS-Activate предоставлял один публичный конечный точка по адресу https://sms-activate.org/stubs/handler_api.php. Каждое действие было параметром строки запроса по этому URL. Таблица ниже сопоставляет каждое основное действие с его эквивалентом в VerifySMS. Слой совместимости по адресу https://api.verifysms.app/compat/handler_api.php принимает ту же форму строки запроса.

Действие SMS-ActivateНазначениеСлой совместимости VerifySMSНативный API VerifySMS
getBalanceВозвращает баланс в USD в виде текстаРаботает без изменений. Возвращает ACCESS_BALANCE:X.YYGET /v1/balance возвращает JSON
getNumbersStatusДоступность по странамРаботает. Возвращает устаревший формат картыGET /v1/countries/availability
getNumberАрендовать номер для сервисаРаботает. Возвращает ACCESS_NUMBER:id:+phonePOST /v1/rentals
setStatusПодтвердить или отменить арендуРаботает. Коды статуса 1/3/6/8 ведут себя одинаковоPOST /v1/rentals/{id}/status
getStatusОпрос на получение SMSРаботает. Возвращает STATUS_WAIT_CODE, STATUS_OK:CODE, STATUS_WAIT_RETRYGET /v1/rentals/{id}
getPricesПолучить таблицу ценРаботает. Возвращает цены VerifySMS в устаревшей форме JSONGET /v1/prices
getCountriesКарта кодов странРаботает. Возвращает как устаревшие числовые идентификаторы, так и коды ISO-3166GET /v1/countries
getTopCountriesByServiceЛучшие страны по сервисуВозвращает данные VerifySMS в реальном времени вместо кэшированных рейтингов SMS-ActivateGET /v1/services/{id}/top-countries

Несколько менее используемых действий SMS-Activate не имеют прямого соответствия. getRentServicesAndCountries и API аренды на длительный срок были специфичны для SMS-Activate и не имеют слоя совместимости. Если ваша интеграция использовала их, вам следует перейти на нативный конечный точка VerifySMS для длительной аренды по адресу POST /v1/rentals/long, который документирован отдельно.

Часть 2: Пошаговые руководства по миграции кода

Следующие фрагменты имеют ту же форму, которую я тестировал в нашей собственной среде staging в январе. Я намеренно оставил их простыми, чтобы вы могли прочитать их в сравнении с вашим собственным кодом без переключения контекста.

Python (requests)

Единственное необходимое изменение — это базовый URL. Если вы уже обернули API в небольшой клиентский модуль, разница будет всего в одной строке.

import os
import requests

# ДО
# BASE_URL = "https://sms-activate.org/stubs/handler_api.php"
# ПОСЛЕ
BASE_URL = "https://api.verifysms.app/compat/handler_api.php"
API_KEY = os.environ["SMS_API_KEY"]

def get_number(service: str, country: int) -> tuple[str, str]:
    resp = requests.get(BASE_URL, params={
        "api_key": API_KEY,
        "action": "getNumber",
        "service": service,
        "country": country,
    }, timeout=30)
    resp.raise_for_status()
    # ACCESS_NUMBER:12345:+441234567890
    status, rental_id, phone = resp.text.split(":", 2)
    if status != "ACCESS_NUMBER":
        raise RuntimeError(f"unexpected response: {resp.text}")
    return rental_id, phone

def wait_for_code(rental_id: str, deadline_seconds: int = 180) -> str:
    import time
    start = time.monotonic()
    while time.monotonic() - start < deadline_seconds:
        resp = requests.get(BASE_URL, params={
            "api_key": API_KEY,
            "action": "getStatus",
            "id": rental_id,
        }, timeout=15).text
        if resp.startswith("STATUS_OK:"):
            return resp.split(":", 1)[1]
        time.sleep(4)
    # Отметить как неиспользованный для получения возврата средств
    requests.get(BASE_URL, params={
        "api_key": API_KEY,
        "action": "setStatus",
        "status": 8,
        "id": rental_id,
    }, timeout=15)
    raise TimeoutError(f"no code after {deadline_seconds}s")

Node.js (axios)

import axios from "axios";

// ДО
// const BASE_URL = "https://sms-activate.org/stubs/handler_api.php";
// ПОСЛЕ
const BASE_URL = "https://api.verifysms.app/compat/handler_api.php";
const API_KEY = process.env.SMS_API_KEY;

export async function getNumber(service, country) {
  const { data } = await axios.get(BASE_URL, {
    params: { api_key: API_KEY, action: "getNumber", service, country },
    timeout: 30_000,
  });
  const [status, rentalId, phone] = data.split(":");
  if (status !== "ACCESS_NUMBER") {
    throw new Error(`unexpected response: ${data}`);
  }
  return { rentalId, phone };
}

export async function waitForCode(rentalId, deadlineMs = 180_000) {
  const start = Date.now();
  while (Date.now() - start < deadlineMs) {
    const { data } = await axios.get(BASE_URL, {
      params: { api_key: API_KEY, action: "getStatus", id: rentalId },
      timeout: 15_000,
    });
    if (data.startsWith("STATUS_OK:")) return data.split(":")[1];
    await new Promise((r) => setTimeout(r, 4000));
  }
  await axios.get(BASE_URL, {
    params: { api_key: API_KEY, action: "setStatus", status: 8, id: rentalId },
    timeout: 15_000,
  });
  throw new Error(`no code after ${deadlineMs}ms`);
}

PHP (curl)

<?php
// ДО
// const BASE_URL = "https://sms-activate.org/stubs/handler_api.php";
// ПОСЛЕ
const BASE_URL = "https://api.verifysms.app/compat/handler_api.php";

function sms_call(array $params): string {
    $params["api_key"] = getenv("SMS_API_KEY");
    $url = BASE_URL . "?" . http_build_query($params);
    $ch  = curl_init($url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_TIMEOUT, 30);
    $body = curl_exec($ch);
    curl_close($ch);
    return $body;
}

function get_number(string $service, int $country): array {
    $resp = sms_call(["action" => "getNumber", "service" => $service, "country" => $country]);
    [$status, $id, $phone] = explode(":", $resp, 3);
    if ($status !== "ACCESS_NUMBER") {
        throw new RuntimeException("unexpected: $resp");
    }
    return ["id" => $id, "phone" => $phone];
}

Go (net/http)

package sms

import (
    "errors"
    "fmt"
    "io"
    "net/http"
    "net/url"
    "os"
    "strings"
    "time"
)

// ДО
// const baseURL = "https://sms-activate.org/stubs/handler_api.php"
// ПОСЛЕ
const baseURL = "https://api.verifysms.app/compat/handler_api.php"

func call(params url.Values) (string, error) {
    params.Set("api_key", os.Getenv("SMS_API_KEY"))
    req, _ := http.NewRequest("GET", baseURL+"?"+params.Encode(), nil)
    client := &http.Client{Timeout: 30 * time.Second}
    resp, err := client.Do(req)
    if err != nil {
        return "", err
    }
    defer resp.Body.Close()
    body, _ := io.ReadAll(resp.Body)
    return string(body), nil
}

func GetNumber(service string, country int) (id, phone string, err error) {
    body, err := call(url.Values{
        "action":  {"getNumber"},
        "service": {service},
        "country": {fmt.Sprint(country)},
    })
    if err != nil {
        return "", "", err
    }
    parts := strings.SplitN(body, ":", 3)
    if len(parts) != 3 || parts[0] != "ACCESS_NUMBER" {
        return "", "", errors.New("unexpected: " + body)
    }
    return parts[1], parts[2], nil
}

Пример на Go намеренно написан без сторонних клиентов, чтобы вы могли вставить его в минимальный сервис без добавления зависимостей. Тот же шаблон применим и в других языках: замените URL, оставьте остальное и позвольте вашей существующей обработке ошибок переноситься.

Часть 3: Подводные камни, которые вас подведут

Вот места, где слой совместимости верен причудам SMS-Activate, но сами причуды удивят вас, если вы давно не касались этого кода.

Идентификаторы стран — это не ISO-коды

SMS-Activate нумеровал страны в своем собственном порядке: Россия была 0, США — 187, Индонезия — 6 и так далее. Если в вашей интеграции эти магические числа жестко закодированы, они по-прежнему работают в слое совместимости. Если вы пишете новый код, предпочтите форму ISO-3166 alpha-2 (RU, US, ID), которую также принимает слой совместимости. Не смешивайте оба стиля в одном месте вызова, потому что последующая отладка будет болезненной.

Код статуса 3 против 6

Код действия 3 в setStatus означал «запросить еще одно SMS» в мире SMS-Activate, а код 6 означал «принять код как действительный». Эти два кода легко перепутать в спешке, и они имеют противоположные последствия для биллинга: 3 продолжает взимать плату, 6 подтверждает успешную верификацию. Слой совместимости ведет себя так же. Найдите в своем коде setStatus и убедитесь, что ветка, принимающая код 6, выполняется только после того, как вы уверены, что верификация прошла успешно.

Тайм-ауты и автоматические выключатели

SMS-Activate под нагрузкой иногда возвращал 200 с пустым телом вместо ошибки HTTP. Защищенные клиенты оборачивают вызов в тайм-аут и рассматривают пустое тело как сигнал повторной попытки. VerifySMS никогда не возвращает пустое тело в слое совместимости. Если ваш клиент по-прежнему рассматривает пустое тело как сигнал повторной попытки, он будет тратить бюджет на нестабильную сеть, потому что повторная попытка попадет на другой идентификатор аренды. Более безопасный шаблон — проверять известные префиксы ответов (ACCESS_, STATUS_, BAD_) и рассматривать все остальное как жесткий сбой, а не временный.

Лимиты скорости перемещаются с ключа на IP-адрес

Лимиты скорости SMS-Activate были привязаны к токену API. Лимиты скорости VerifySMS привязаны к комбинации токена API и исходного IP-адреса, потому что мы видим много злоупотреблений со стороны скрейпинговых скриптов, использующих один ключ на ботнет. Для обычного производственного трафика с одного сервера или пула с балансировкой нагрузки это незаметно. Если вы запускаете распределенные задания CI, которые используют один ключ staging, вы можете увидеть 429 при первом одновременном прогреве всего флота. Решение — позволить канареечному запуску работать с одного узла в течение дня, прежде чем распространять.

Время возврата средств ощущается мгновенным, потому что оно действительно мгновенное

Это не ловушка, а приятный сюрприз. Там, где возвраты SMS-Activate появлялись на вашем балансе через несколько часов, возвраты VerifySMS появляются в течение 60 секунд. Если ваш трекер расходов считывает баланс по расписанию, он зарегистрирует возврат как кредит, который старая система пропустила бы. Дашборды сверки иногда помечают это как аномалию в первый день.

Часть 4: Реалистичная оценка сравнения цен

До отключения SMS-Activate был дном рынка. Российские номера стоили от 0,03 до 0,05 доллара за верификацию, а покупатели больших объемов платили еще меньше. Этого дна больше нет. Вот где находятся оставшиеся поставщики в апреле 2026 года для наиболее распространенных услуг, взятые со страницы публичных цен каждого поставщика на :

Сервис5simTextVerifiedSMSPVASMS-MANVerifySMS
WhatsApp / Россия$0.014$0.05$0.035$0.10
WhatsApp / США$0.27$0.25$0.28$0.22$0.18
Telegram / Россия$0.016$0.05$0.04$0.10
Telegram / США$0.35$0.40$0.38$0.30$0.20
Google / Индонезия$0.07$0.08$0.06$0.10

Закономерность проста: 5sim и SMS-MAN выигрывают по минимальным российским ценам, TextVerified — это премиум-сегмент США, а VerifySMS находится посередине с фиксированной базовой ставкой 0,10 доллара за все, кроме самых дорогих американских номеров не VoIP. Если ваш бюджет был настроен на минимальные цены SMS-Activate, ожидайте платить в два-пять раз больше за верификацию, независимо от того, какого поставщика вы выберете.

Две заметки к этой таблице. Во-первых, каждый поставщик (включая VerifySMS) повышает и понижает цены на отдельные страны в ответ на затраты операторов, поэтому подтвердите текущую цену в своей панели управления перед выделением бюджета. Во-вторых, *эффективная* цена за успешную верификацию зависит от коэффициента возврата средств. Поставщик со стоимостью 0,08 доллара и 70% успешных верификаций обойдется вам дороже за успех, чем поставщик за 0,10 доллара с автоматическими возвратами и 90% успешных верификаций.

Часть 5: Чек-лист миграции из 10 шагов

Это фактическая последовательность, которую мы прошли с нашими пользователями в январе. Она предполагает одного разработчика с доступом к репозиторию, одну производственную службу и среду staging. Масштабируйте процент канареечного трафика, если вы запускаете несколько служб или монорепозиторий.

  1. Инвентаризация каждого места вызова. Запустите git grep -n 'sms-activate\.org\|handler_api\.php\|getNumber\|setStatus' и перечислите все файлы, которые обращаются к старому API. Если вы найдете более дюжины, сначала выберите оберточный модуль и централизуйте вызовы перед миграцией.
  2. Получите API-ключ VerifySMS. Зарегистрируйтесь, добавьте небольшую сумму на баланс и сгенерируйте ключ с ограниченными правами для staging. Не храните производственный ключ в репозитории.
  3. Измените базовый URL. Замените хост SMS-Activate на api.verifysms.app/compat/handler_api.php. Не меняйте строку запроса. Зафиксируйте это изменение отдельно, чтобы diff был чистым.
  4. Запустите существующие тесты. Если тесты обращаются к реальному API, направьте их на staging и следите за несоответствиями формата. Если они мокируют API, запустите их также против живого конечного точка staging, чтобы выявить расхождения в контрактах.
  5. Перепроверьте идентификаторы стран. Просмотрите свой код на наличие констант стран. Если вы используете устаревшие числовые идентификаторы, они по-прежнему работают. Если у вас есть возможность, замените их на коды ISO-3166, потому что следующий разработчик, который будет работать с этим файлом, будет вам благодарен.
  6. Настройте запросы на возврат средств. Убедитесь, что ваш путь обработки тайм-аутов вызывает setStatus со статусом 8. Без этого вы все равно будете получать возвраты (мы автоматически возвращаем средства за истекшие аренды), но ваш трекер расходов будет отставать от реальности.
  7. Обновите свой трекер расходов. Считывайте стоимость из заголовка ответа X-VerifySMS-Cost вместо парсинга из таблицы цен. Это одно изменение сделает ваш финансовый дашборд точным до цента.
  8. Мониторинг. Добавьте оповещения об уровне успешности, p95 задержке и коэффициенте возврата средств на основе вашей существующей базовой линии. Выбирайте пороговые значения, которые вы можете обосновать, а не те, которые, по вашему мнению, будут «нормальными».
  9. Канареечный запуск 5% в течение 24 часов. Направьте небольшую часть производственного трафика через новый конечный точка. Следите за дашбордом, а не только за оповещениями.
  10. Переключите остальное. После того как канареечное окно пройдет чисто, переключите оставшиеся 95% и оставьте старый код клиента закомментированным (не удаленным) на один цикл выпуска, чтобы у вас был быстрый откат.

Удалите старый код в следующем цикле выпуска после этого. Не оставляйте мертвые места вызовов дольше недели, потому что следующий человек, который будет работать с модулем, случайно вставит их обратно в новую интеграцию.

Часто задаваемые вопросы

Является ли слой совместимости SMS-Activate реальным API или просто заглушкой?

Это реальный конечный точка по адресу api.verifysms.app/compat/handler_api.php, который принимает каждое основное действие из публичной документации SMS-Activate: getBalance, getNumber, getStatus, setStatus, getPrices и getCountries. Запросы проксируются к нашему нативному API под капотом, поэтому вы получаете цены, покрытие и поведение возврата средств VerifySMS без каких-либо изменений в коде с вашей стороны.

Будет ли работать мой старый API-ключ?

Нет. API-ключи SMS-Activate перестали аутентифицироваться в день закрытия сервиса. Вам нужен новый ключ от VerifySMS. Зарегистрируйтесь, добавьте небольшую сумму на баланс и сгенерируйте ключ из панели управления. Формат ключа идентичен по длине, поэтому вы можете вставить его в ту же переменную окружения.

Как работают возвраты по сравнению с SMS-Activate?

SMS-Activate требовал вызова setStatus со статусом 8 в течение 20 минут, чтобы пометить номер как неиспользованный, а возвраты обрабатывались вручную в течение нескольких часов. VerifySMS принимает тот же вызов setStatus и возвращает полную сумму на ваш баланс в течение 60 секунд. Если вы забудете вызвать setStatus полностью, наша система все равно автоматически вернет средства за любой номер, который так и не получил SMS после истечения срока аренды.

Какие страны поддерживаются?

VerifySMS охватывает более 200 стран. Каждая страна, которую предлагал SMS-Activate, доступна на VerifySMS, включая Россию, Индонезию, Вьетнам, Нигерию и США. Вы можете сохранить свое существующее сопоставление идентификаторов стран или перейти на коды ISO-3166 alpha-2 в любое время.

Тарифы те же?

Нет. Минимальные рыночные цены SMS-Activate от 0,03 до 0,05 доллара за верификацию для российских номеров ушли с открытого рынка. Текущие рыночные цены варьируются от 0,10 доллара за распространенные услуги до 0,25 доллара за американские номера не VoIP на более строгих платформах. VerifySMS взимает 0,10 доллара в качестве базовой ставки и публикует цены по странам на панели управления.

Нужно ли менять логику опроса?

Нет. Вызов getStatus возвращает STATUS_WAIT_CODE и STATUS_OK в том же формате, который использовал SMS-Activate. Интервалы опроса от 3 до 5 секунд по-прежнему работают. Единственное новое поведение заключается в том, что VerifySMS также предоставляет URL вебхука на панели управления, поэтому вы можете полностью прекратить опрос, если предпочитаете поток, управляемый событиями.

Что произойдет, если слой совместимости когда-нибудь устареет?

Слой совместимости считается постоянным публичным интерфейсом. Если мы когда-либо изменим его поведение, мы опубликуем окно устаревания минимум на шесть месяцев с полным примечанием о миграции. Нативный JSON API VerifySMS также документирован, поэтому вы можете перейти с слоя совместимости в удобном для вас темпе, когда это будет целесообразно.

Как тестировать, не тратя деньги?

Панель управления VerifySMS предоставляет режим песочницы, который возвращает симулированные номера телефонов и фиксированные коды SMS без списания средств с вашего баланса. Включите флаг песочницы на панели управления или отправьте заголовок X-Sandbox-Mode с любым запросом, чтобы протестировать пути вашего кода перед запуском.

Могу ли я мигрировать и с других сервисов?

Да. Это руководство написано вокруг API SMS-Activate, потому что именно там находится большая часть застрявшего кода, но тот же чек-лист применим к миграциям с 5sim, SMS-MAN или любого другого сервиса, совместимого с handler_api. Слой совместимости распознает параметры handler_api.php независимо от того, с каким сервисом вы ранее обращались.

Сколько времени занимает реальная миграция?

Для интеграции одной службы с несколькими десятками мест вызовов планируйте от двух до четырех часов сосредоточенной работы, плюс 24-часовое канареечное окно перед полным переключением трафика. Более крупные многосервисные миграции с пользовательской обработкой ошибок, аналитикой и повторными попытками могут занять больше времени, но обычно завершаются в течение одного рабочего дня.

Я потеряю свои исторические данные?

История верификаций SMS-Activate стала недоступна при закрытии сервиса и не подлежит восстановлению. VerifySMS ведет полный журнал аудита каждой попытки верификации по вашему счету в течение 12 месяцев, доступный с панели управления и через расширение /compat/handler_api.php?action=getHistory.

Влияет ли это на мою позицию в отношении GDPR или соответствия требованиям?

VerifySMS зарегистрирован в Соединенном Королевстве и соблюдает UK GDPR. Мы публикуем нашу политику хранения данных, субпроцессоров и соглашение о конфиденциальности на странице конфиденциальности. Если ваша предыдущая настройка требовала соглашения о конфиденциальности с SMS-Activate, свяжитесь с нами, и мы подпишем то же соглашение в течение одного рабочего дня.

Следующие шаги

Если вы дочитали до этого места, у вас уже есть необходимые компоненты. Начните с шага инвентаризации, передайте замену базового URL коллеге для проверки и запустите канареечный тест на ночь. Руководство намеренно небольшое; сложная часть — это дисциплина остановиться после канареечного окна, а не переключать все в одном коммите.

Связанное чтение на остальной части сайта:

Готовы завершить миграцию за один вечер?

Создайте API-ключ VerifySMS →

Режим песочницы включен · Гарантия автовозврата · 200+ стран · Слой совместимости SMS-Activate по адресу /compat/handler_api.php

Next steps

If you have read this far, you already have the pieces you need. Start with the inventory step, get the base URL swap in front of a teammate for review, and run the canary overnight. The playbook is small on purpose; the hard part is the discipline to stop after the canary window instead of cutting everything over in one commit.

Related reading on the rest of the site:

Ready to cut the migration to a single evening?

Create a VerifySMS API key →

Sandbox mode included · Auto-refund guarantee · 200+ countries · SMS-Activate compat layer on /compat/handler_api.php