Самая частая беда SMS-интеграций — не упавший запрос, а запрос, который выглядит успешным, хотя сообщение не отправлено. Вы получаете HTTP 200, код доволен, телефон молчит. Никто не замечает этого, пока кто-нибудь не прочитает код в теле ответа.
Конечная точка 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-слой перед выходом в продакшен:
CURLOPT_TIMEOUT), чтобы медленный SMS-провайдер не подвесил оформление заказаЖурналирование важнее всего. Когда приходит жалоба «SMS не пришла», возможность увидеть возвращённый код разделяет случаи за секунды: 00 означает, что сообщение передано оператору и проблема дальше по цепочке; 40 — что одобрение отправителя истекло; отсутствие записи означает, что отправки вообще не было.
SMS-интеграции выглядят простыми, именно поэтому их пишут небрежно. Привычка, которая имеет значение, укладывается в одно предложение: HTTP-ответ транспорта не сообщает о бизнес-результате. Читайте код в теле, журналируйте каждую отправку, нормализуйте номера и уводите отправку с основного пути запроса. Эти четыре пункта снимают почти всю отладку, связанную с SMS.
POST https://api.netgsm.com.tr/sms/rest/v2/send, basic auth00 это успех5XXXXXXXXX — без ведущего нуля и кода страныencoding: TR отображает верно, но снижает лимит со 160 до 70Потому что HTTP-статус говорит лишь о том, что запрос дошёл; принята ли отправка — это код в теле ответа. 00 — успех, 30 — учётные данные или ограничение по IP, 40 — неодобренное имя отправителя, 20 — проблема с сообщением или символами. Если ваша интеграция не читает тело, все эти ошибки остаются невидимыми.
Этот код покрывает и ограничение по IP, а не только неверные учётные данные. Аккаунт NetGSM может ограничивать доступ к API конкретными IP-адресами, поэтому код, работающий на машине разработчика, перестаёт работать после деплоя. Добавьте исходящий IP сервера в список разрешённых в панели.
Зависит от содержания. Значение encoding, равное TR, отображает буквы корректно, но переводит сообщение в Unicode, снижая лимит одной SMS со 160 до 70 символов; длинный текст разбивается на несколько кредитов. Для коротких транзакционных сообщений вроде кодов подтверждения переход на ASCII заметно снижает стоимость.
Нет. Обязанность распространяется на коммерческие электронные сообщения. Транзакционные — статус заказа, напоминание о записи, код подтверждения — вне этой сферы. Если не разделить их в коде, вы либо напрасно заблокируете транзакционные сообщения, либо отправите коммерческие там, где не должны.