Как определить платежного агрегатора по 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-эндпоинт будет безопасным и надежным.

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