Все статьи
Shopifywebhook debugginglocal testinge-commerce

Тестируйте вебхуки Shopify локально без сюрпризов

Большинство багов вебхуков Shopify в локальной разработке сводятся к одной строке: HMAC кодируется в base64, а не hex. Если вы вызываете .digest('hex') и сравниваете с X-Shopify-Hmac-Sha256, проверка никогда не пройдёт — даже с правильным общим секретом. Мы видели, как senior-инженеры теряли полдня из-за этого одного символа.

Ловушка base64

Stripe использует hex. GitHub использует hex (с префиксом sha256=). Shopify использует base64. Три провайдера, три кодировки. Ваша первая версия верификатора почти наверняка скопирует сниппет из другого проекта и тихо провалится.

Вот как должна выглядеть проверка:

const hmac = crypto
  .createHmac('sha256', SHOPIFY_API_SECRET)
  .update(rawBody)         // raw body, not JSON-parsed
  .digest('base64');       // base64, not hex

const valid = crypto.timingSafeEqual(
  Buffer.from(hmac),
  Buffer.from(req.headers['x-shopify-hmac-sha256']),
);

Помимо кодировки важны две вещи. Тело должно быть сырыми, непарсенными байтами. И сравнение должно быть timing-safe — равенство строк утекает информацию по байту за раз.

Почему туннель упрощает это

Можно также использовать команду Shopify shopify app dev, которая поднимает внутренний туннель. Работает. Но она оборачивает ваш dev-сервер в собственный процесс, проглатывает логи определённым образом и не даёт кнопки повтора. Для разработки приложения сложнее «hello world» стабильный публичный URL плюс туннель localhost с захватом запросов экономит больше времени, чем CLI экономит на начальной настройке.

npx portpreview 3000

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

Тестовые магазины: часть, которую документация Shopify пробегает

Две вещи, которые нужно знать, прежде чем что-то подключать:

  • Магазины разработки шлют все события вебхуков. Создание заказа, фулфилмент, инвентарь — всё. Не нужно ничего подделывать; просто установите приложение на dev-магазин и кликайте.
  • Секрет вебхука отличается по приложению и по каналу доставки. Если вы также подписаны на EventBridge или Pub/Sub, поведение HMAC другое. Здесь речь о простой HTTPS-доставке вебхуков.

Пошаговая настройка

  1. Запустите приложение локально на нужном порту (3000 — частый выбор).
  2. Выполните npx portpreview 3000 и скопируйте напечатанный HTTPS-URL.
  3. В дашборде Shopify Partners откройте приложение и перейдите в Configuration → Webhooks.
  4. Установите endpoint вебхука на https://your-tunnel.portpreview.dev/api/webhooks/shopify (или путь, который использует приложение).
  5. Установите приложение на dev-магазин, затем триггерните событие — создайте черновик заказа, выполните товар, измените инвентарь.
  6. Смотрите, как запрос попадает. Инспектируйте заголовки и тело. Повторяйте захваченный запрос после каждого фикса обработчика.

Ошибки, которые мы постоянно видим

Тело распарсено до верификации

Если вы ставите app.use(express.json()) перед маршрутом вебхука, сырые байты пропадают к моменту проверки. Монтируйте парсер сырого тела только на путь вебхука или вытаскивайте тело вручную из потока запроса до любого JSON-парсинга.

Перепутанные секреты

Секрет приложения Partners — не то же, что токен Storefront API. HMAC вебхука использует секрет приложения. Если вы смотрите на shp_xxx в env-файле, вы взяли не тот.

Тестовые события, выглядящие одинаково

Кнопка Shopify «Send test notification» доставляет синтетическое событие с телом-заглушкой. Подпись на этом тестовом событии настоящая, но payload фиксирован. Для реалистичного тестирования формы payload триггерите события из самого dev-магазина.

Повтор не обсуждается для разработки приложений

Shopify повторяет неудачные вебхуки до 48 часов с экспоненциальным backoff. Это щедро, но при итерациях вы не хотите ждать следующего повтора. Захватите первую доставку в истории запросов вашего туннеля и повторяйте по требованию против локального обработчика.

Когда локального становится мало

Потоки регистрации магазина, GDPR-вебхуки и изменения биллинга подписок проще валидировать против развёрнутой среды, потому что они взаимодействуют с собственным состоянием аккаунта Shopify. Для всего остального — парсинг payload, верификация подписи, бизнес-логика — локально быстрее.

По лежащей в основе математике подписи для всех провайдеров прочтите наше руководство по верификации подписи вебхуков. Запишитесь в лист ожидания PortPreview, если хотите туннель со встроенным повтором.

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

Как тестировать вебхуки Shopify на localhost?
Запустите туннель вроде PortPreview, получите публичный HTTPS-URL и зарегистрируйте его как endpoint вебхука в дашборде Shopify Partners. Установите приложение на магазин разработки, триггерите события, и доставки достигают вашего локального обработчика.
Почему моя верификация HMAC Shopify всегда падает?
Чаще всего дело в кодировке. Shopify подписывает через HMAC SHA-256 и кодирует результат в base64. Если ваш код использует .digest('hex') вместо .digest('base64'), любое сравнение падает, даже если секрет верный.
Нужен ли shopify app dev или можно любой туннель?
Любой туннель работает. shopify app dev включает собственный туннель для удобства, но универсальный туннель с захватом и повтором запросов полезнее для продолжительной разработки.