Доступен — Локальные цифровые продукты
HCA · Studio
TR — Контакты ↗
Заметка 10 · Интеграция

NetGSM REST API и его молчаливые сбои

Самая частая беда SMS-интеграций — не упавший запрос, а запрос, который выглядит успешным, хотя сообщение не отправлено. Вы получаете HTTP 200, код доволен, телефон молчит. Никто не замечает этого, пока кто-нибудь не прочитает код в теле ответа.

Сервис
NetGSM REST v2
Язык
PHP
Симптом
200, но SMS нет
Короткий ответ

Конечная точка NetGSM REST возвращает HTTP 200, сообщая, что запрос получен; принято ли сообщение на самом деле — это код внутри тела ответа. 00 — успех, 30 — неверные учётные данные или ограничение по IP, 40 — неодобренное имя отправителя, 20 — проблема с длиной или символами. Интеграция, проверяющая только HTTP-статус, делает все эти ошибки невидимыми. Кроме того, номер должен быть в форме 5XXXXXXXXX (без ведущего нуля и кода страны), msgheader — одобренным отправителем, а коммерческие сообщения подпадают под турецкий реестр согласий.

Запрос

Конечная точка REST принимает тело JSON и базовую аутентификацию. Минимальный рабочий пример:

<?php
$payload = [
  'msgheader' => 'APPROVED_SENDER',
  'encoding'  => 'TR',
  'messages'  => [
    ['msg' => 'Ваша заявка принята.', 'no' => '5301234567'],
  ],
];

$ch = curl_init('https://api.netgsm.com.tr/sms/rest/v2/send');
curl_setopt_array($ch, [
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_POST           => true,
  CURLOPT_HTTPHEADER     => ['Content-Type: application/json'],
  CURLOPT_USERPWD        => NETGSM_USER . ':' . NETGSM_PASS,
  CURLOPT_POSTFIELDS     => json_encode($payload, JSON_UNESCAPED_UNICODE),
  CURLOPT_TIMEOUT        => 15,
]);
$body = curl_exec($ch);
$http = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

Флаг JSON_UNESCAPED_UNICODE важен: отправка нелатинских символов в виде escape-последовательностей вида \u011f в некоторых конфигурациях вызывает проблемы.

Коды ответа

Ключевой момент: $http почти всегда равен 200. Реальный результат — в теле.

00Успех — сообщение поставлено в очередь
20Некорректный текст или превышен лимит символов
30Неверные учётные данные или доступ к API ограничен по IP
40Имя отправителя не одобрено
50Заблокировано реестром согласий — получатель отказался от коммерческих сообщений
70Неверный параметр; структура тела отличается от ожидаемой

Код 30 отнимает больше всего времени, потому что появляется даже при верных учётных данных. Аккаунт NetGSM может ограничивать доступ к API конкретными IP-адресами; код, работающий с машины разработчика, перестаёт работать сразу после деплоя. Тестировать, не добавив исходящий IP сервера в панели, — пустая трата времени.

Формат номера

Сервис ожидает 5XXXXXXXXX: десять цифр, без ведущего нуля и кода страны. Реальные номера в базе никогда не бывают такими аккуратными — пользователи вводят пробелы, скобки, дефисы, +90 и ведущие нули.

Один шаг нормализации перед отправкой убирает большую часть сбоев:

function netgsm_number(string $raw): ?string {
    $d = preg_replace('/\D+/', '', $raw);        // убрать всё, кроме цифр
    if (str_starts_with($d, '90')) $d = substr($d, 2);
    if (str_starts_with($d, '0'))  $d = substr($d, 1);
    return preg_match('/^5\d{9}$/', $d) ? $d : null;
}

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

Турецкие символы

Здесь есть компромисс. Значение encoding, равное TR, корректно отображает турецкие буквы, но сообщение кодируется как Unicode, и одна SMS сокращается со 160 символов до 70. Длинный текст разбивается на несколько кредитов.

Альтернатива — свести текст к ASCII: ş→s, ğ→g, ı→i, ö→o, ü→u, ç→c. Вы сохраняете 160 символов ценой того, как текст читается. Выбор зависит от содержания: для кодов подтверждения и напоминаний о записи ASCII вполне достаточно, а корпоративные уведомления обычно заслуживают правильного написания.

Что бы вы ни выбрали, считайте символы перед отправкой и записывайте, сколько кредитов уйдёт на сообщение. Иначе месячный счёт станет сюрпризом.

Реестр согласий

В Турции отправка коммерческих электронных сообщений требует согласия, зарегистрированного в национальной системе. Информационные сообщения — статус заказа, напоминание о записи, код подтверждения — под это не подпадают; рекламный и кампанейский контент подпадает.

Проведите это разделение и в коде: применяйте проверку согласия к коммерческим отправкам и пропускайте её для транзакционных. Пропуская и те и другие через одну функцию, вы либо напрасно блокируете транзакционные сообщения, либо отправляете коммерческие там, где не должны.

Надёжная интеграция

Минимум, которому должен соответствовать SMS-слой перед выходом в продакшен:

  • Читайте код в теле ответа, никогда не доверяйте HTTP-статусу
  • Записывайте каждую отправку: номер, текст, возвращённый код, отметку времени
  • Ставьте таймаут (CURLOPT_TIMEOUT), чтобы медленный SMS-провайдер не подвесил оформление заказа
  • Отвяжите отправку от запроса пользователя — ставьте в очередь, отправляйте в фоне
  • Нормализуйте и проверяйте номер перед отправкой
  • Не держите учётные данные в коде; используйте переменные окружения

Журналирование важнее всего. Когда приходит жалоба «SMS не пришла», возможность увидеть возвращённый код разделяет случаи за секунды: 00 означает, что сообщение передано оператору и проблема дальше по цепочке; 40 — что одобрение отправителя истекло; отсутствие записи означает, что отправки вообще не было.

Итог

SMS-интеграции выглядят простыми, именно поэтому их пишут небрежно. Привычка, которая имеет значение, укладывается в одно предложение: HTTP-ответ транспорта не сообщает о бизнес-результате. Читайте код в теле, журналируйте каждую отправку, нормализуйте номера и уводите отправку с основного пути запроса. Эти четыре пункта снимают почти всю отладку, связанную с SMS.

Быстрая справка
Точка входа
POST https://api.netgsm.com.tr/sms/rest/v2/send, basic auth
Главное
HTTP 200 приходит всегда — результат в коде тела, 00 это успех
Код 30
Неверные учётные данные или доступ к API ограничен по IP
Код 40
Имя отправителя не одобрено
Номер
5XXXXXXXXX — без ведущего нуля и кода страны
Турецкий
encoding: TR отображает верно, но снижает лимит со 160 до 70
Согласия
Нужны для коммерческих сообщений; транзакционные вне сферы
Устойчивость
Ставьте таймаут, отправляйте из очереди, журналируйте каждую попытку
Частые вопросы

Об интеграции NetGSM.

Запрос возвращает 200, но SMS не отправляется. Почему?

Потому что HTTP-статус говорит лишь о том, что запрос дошёл; принята ли отправка — это код в теле ответа. 00 — успех, 30 — учётные данные или ограничение по IP, 40 — неодобренное имя отправителя, 20 — проблема с сообщением или символами. Если ваша интеграция не читает тело, все эти ошибки остаются невидимыми.

Я получаю код 30, но логин и пароль верные.

Этот код покрывает и ограничение по IP, а не только неверные учётные данные. Аккаунт NetGSM может ограничивать доступ к API конкретными IP-адресами, поэтому код, работающий на машине разработчика, перестаёт работать после деплоя. Добавьте исходящий IP сервера в список разрешённых в панели.

Стоит ли использовать турецкие символы?

Зависит от содержания. Значение encoding, равное TR, отображает буквы корректно, но переводит сообщение в Unicode, снижая лимит одной SMS со 160 до 70 символов; длинный текст разбивается на несколько кредитов. Для коротких транзакционных сообщений вроде кодов подтверждения переход на ASCII заметно снижает стоимость.

Каждому ли сообщению нужна проверка согласия?

Нет. Обязанность распространяется на коммерческие электронные сообщения. Транзакционные — статус заказа, напоминание о записи, код подтверждения — вне этой сферы. Если не разделить их в коде, вы либо напрасно заблокируете транзакционные сообщения, либо отправите коммерческие там, где не должны.

Доступность и смета

Есть идея?
Можно даже одной фразой.