Как определить платежного агрегатора по webhook на едином URL
При разработке платежной интеграции часто возникает задача: у вас есть один URL для приема вебхуков от нескольких платежных агрегаторов (например, Stripe, PayPal, YooKassa). Как понять, от какого именно провайдера пришел запрос? Простая проверка заголовков может быть подделана, а подпись (signature) требует предварительного знания провайдера. В этой статье разберем три надежных способа решения проблемы.
Проблема единого webhook-эндпоинта
Когда вы используете один маршрут, например /api/webhook/payment, для всех платежных систем, вы сталкиваетесь с дилеммой: без идентификации провайдера невозможно выбрать правильный секретный ключ для верификации подписи. Подделка заголовков (например, User-Agent или кастомных X-Provider) - реальная угроза, поэтому полагаться только на них рискованно.
Способ 1: Проверка по подписи (Signature) с предварительной фильтрацией
Самый безопасный метод - использовать подпись, но для этого нужно сначала определить, какой секрет применить. Решение: отправляйте в теле запроса или заголовке идентификатор провайдера (например, provider: stripe), а затем проверяйте подпись по соответствующему ключу. Если подпись не совпала - отбрасывайте запрос. Это исключает подделку, так как злоумышленник не знает ваших секретов.
Способ 2: Использование списка IP-адресов провайдера
Многие крупные агрегаторы (Stripe, PayPal) публикуют официальные диапазоны IP-адресов, с которых приходят вебхуки. Вы можете получить IP из запроса (req.ip или заголовок X-Forwarded-For) и сверить его с заранее загруженным списком. Если IP принадлежит, например, Stripe - используйте соответствующий секрет. Недостаток: не все провайдеры предоставляют такие списки, и они могут меняться.
Способ 3: Анализ структуры тела запроса
Каждый платежный агрегатор формирует уникальную структуру JSON или XML в теле вебхука. Например, у Stripe поле type содержит payment_intent.succeeded, а у PayPal - event_type с CHECKOUT.ORDER.APPROVED. Вы можете написать функцию-детектор, которая анализирует ключи верхнего уровня и определяет провайдера. Это работает даже без подписи, но менее надежно при изменении API.
Рекомендуемая архитектура для продакшена
- Шаг 1: Получите IP-адрес отправителя и проверьте по белому списку провайдеров.
- Шаг 2: Если IP совпал - извлеките идентификатор провайдера из тела запроса (например, по полю
event_type). - Шаг 3: Выберите соответствующий секретный ключ и верифицируйте подпись.
- Шаг 4: Если подпись не прошла - верните ошибку 403. Это защищает от подделки.
Пример кода на Node.js (Express)
app.post('/api/webhook/payment', (req, res) => {
const provider = detectProvider(req.body); // по структуре JSON
const secret = getSecretByProvider(provider);
const signature = req.headers['x-signature'];
if (!verifySignature(req.body, signature, secret)) {
return res.status(403).send('Invalid signature');
}
// обработка платежа
});Заключение
Определение платежного агрегатора на едином URL - задача, решаемая комбинацией методов: проверка IP, анализ тела запроса и верификация подписи. Никогда не используйте только заголовки User-Agent или X-Provider без дополнительной проверки, так как они легко подделываются. Внедрите многоуровневую защиту, и ваш webhook-эндпоинт будет безопасным и надежным.