Все статьи
События вебхука подписки и лицензии, проходящие через HTTPS-туннель в проверенный локальный обработчик разработки.
Lemon Squeezywebhookssubscriptionslocal testing

Тестирование вебхуков Lemon Squeezy на localhost

Чтобы тестировать вебхуки Lemon Squeezy на локальном хосте, запустите ваше приложение, откройте его маршрут вебхука через публичный HTTPS-туннель, создайте вебхук в режиме тестирования в Настройки → Вебхуки и проверьте исходное тело запроса. X-Signature заголовок. Используйте тестовую проверку для реальных данных заказа и управляющие элементы имитации подписки Lemon Squeezy для событий жизненного цикла, на которые трудно дождаться.

Как выглядит правильная локальная настройка

Lemon Squeezy отправляет HTTP POST от его инфраструктуры, поэтому он не может достичь localhost на вашем компьютере. Туннель завершает общедоступный TLS и пересылает неизменённый метод, тело и заголовки на локальный маршрут, такой как http://localhost:3000/api/webhooks/lemonsqueezy.

Полный тест состоит из четырех частей: режим теста Lemon Squeezy, публичный URL для обратного вызова, тот же секрет подписи в панели управления и локальной среде, а также обработчик, который проверяет данные перед изменением состояния подписки или лицензии. Это более полезно, чем публикация скопированного JSON с curl, потому что реальная доставка проверяет как маршрутизацию, так и подпись, созданную провайдером.

Создайте вебхук и отправьте тестовое событие

  1. Запустите ваше приложение на порту 3000 и убедитесь, что маршрут принимается POST.
  2. Начните туннель с npx portpreview 3000.
  3. Переключите панель управления Lemon Squeezy в тестовый режим.
  4. Откройте Настройки → Вебхуки и добавьте https://your-subdomain.portpreview.dev/api/webhooks/lemonsqueezy.
  5. Сгенерируйте сильный случайный секрет для подписи и сохраните его как LEMONSQUEEZY_WEBHOOK_SECRETи выберите только те события, которые использует ваше приложение.
  6. Создайте тестовую покупку или используйте доступное действие имитации подписки в режиме тестирования, затем проверьте журнал доставки и ваш локальный ответ.

Официальный Документация вебхуков Lemon Squeezy позволяет вам просматривать последние полезные нагрузки и повторно отправлять записанные вебхуки со страницы настроек. Повторная отправка полезна для регрессионного тестирования, но ваше приложение должно рассматривать это как дубликат, а не как второе бизнес-событие.

Проверить X-Signature перед разбором JSON

Lemon Squeezy вычисляет HMAC-SHA256 дайджест для полезной нагрузки запроса, используя секрет подписи, и отправляет шестнадцатеричный дайджест в X-Signature. Вычислите контрольную сумму по точно исходным байтам тела и сравните буферы одинаковой длины с помощью функции постоянного времени. Основное документация по подписанию запросов включает примеры для Node, PHP, Python и Ruby.

// app/api/webhooks/lemonsqueezy/route.ts
import crypto from 'node:crypto';
import { NextRequest, NextResponse } from 'next/server';

export async function POST(request: NextRequest) {
  const secret = process.env.LEMONSQUEEZY_WEBHOOK_SECRET;
  if (!secret) return new NextResponse('server misconfigured', { status: 500 });

  const rawBody = await request.text();
  const suppliedHex = request.headers.get('x-signature') ?? '';

  if (!/^[0-9a-f]{64}$/i.test(suppliedHex)) {
    return new NextResponse('invalid signature', { status: 401 });
  }

  const expected = crypto
    .createHmac('sha256', secret)
    .update(rawBody)
    .digest();
  const supplied = Buffer.from(suppliedHex, 'hex');

  if (supplied.length !== expected.length ||
      !crypto.timingSafeEqual(supplied, expected)) {
    return new NextResponse('invalid signature', { status: 401 });
  }

  const event = JSON.parse(rawBody);
  await acceptEvent(event);
  return NextResponse.json({ received: true });
}

Не звоните request.json() во-первых. Повторная сериализация полученного объекта может изменить, казалось бы, незначительные байты и нарушить HMAC. Проверка длины также является важной, потому что Node's timingSafeEqual выбрасывает исключение, когда длины буферов различаются. Официальный Lemon Squeezy Учебник по вебхукам Next.js следует той же последовательности сырого тела.

Понимайте полезную нагрузку перед обновлением вашей базы данных

Тело является объектом ресурса JSON:API. meta.event_name идентифицирует событие, meta.custom_data несёт данные, переданные через корзину, и data содержит ресурс заказа, подписки или лицензионного ключа. Lemon Squeezy также отправляет X-Event-Name; рассматривайте подписанное тело как авторитетный ввод и используйте заголовок для диагностики маршрутизации.

Заказы и идентификация клиентов

Для order_created, сопоставьте покупку с внутренним пользователем, используя идентификатор, сгенерированный сервером, в пользовательских данных оформления заказа. Не доверяйте идентификатору пользователя, предоставленному клиентом, без проверки права собственности. Сохраните идентификатор ресурса и идентификатор варианта Lemon Squeezy, чтобы позднее события по подписке или возврату можно было согласовать.

Подписки — это конечный автомат

Ручка subscription_created, subscription_updated, subscription_cancelled, subscription_resumed, и subscription_expired как отдельные переходы. Отмена может означать, что доступ остается действительным до конца оплаченного периода; истечение является более сильным сигналом того, что подписка закончилась. Моделируйте даты и статус, а не переключайте один логический параметр.

Сбои платежей и восстановление

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

Повторные попытки требуют идемпотентной обработки

По данным Lemon Squeezy ссылка на запрос вебхука, а 200 метки ответа фиксируют успех. Другие статусы повторяются до трех раз с экспоненциальными задержками, примерно равными 5, 25 и 125 секундам. Панель управления повторно отправляет запрос, что создает другую причину, по которой одно и то же логическое событие может приходить более одного раза.

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

BEGIN;
INSERT INTO webhook_receipts (fingerprint, received_at)
VALUES ($1, now())
ON CONFLICT DO NOTHING;
-- Continue only if one row was inserted.
UPDATE subscriptions SET provider_status = $2 WHERE provider_id = $3;
COMMIT;

Не делайте доставку электронной почты или медленный вызов стороннего API частью пути подтверждения. Сохраняйте задачу в той же транзакции, отвечайте и позволяйте обработчику справляться с побочными эффектами. Более широко Руководство по повторным попыткам вебхука и идемпотентности охватывает этот шаблон.

Устранение неполадок тестов вебхука Lemon Squeezy на локальном хосте

Ни один запрос не достигает приложения

Проверьте, что туннель все еще работает, что обратный вызов содержит суффикс маршрута, и что в вашем фреймворке есть обработчик POST по этому точному пути. Вызовите публичную конечную точку самостоятельно. Ошибка туннеля 502 указывает на локальный порт; ошибка фреймворка 404 указывает на размещение маршрута.

Каждая подпись недействительна

Подтвердите, что тестовый режим и рабочий режим не используют разные записи вебхуков или секреты. Прочитайте тело один раз как текст или байты, не обрезайте ни тело, ни секрет, и убедитесь, что глобальный JSON middleware не запускается первым. Проверьте, использует ли X-Signature прибыл, но никогда не печатайте секрет подписи.

Доставка продолжает пытаться снова

Лемон Сквизи ожидает 200, а не перенаправление на страницу входа. Исключите путь вебхука из CSRF и промежуточного ПО пользовательской сессии, сохранив при этом проверку подписи. Обрабатывайте ошибки базы данных, держите работу короткой и убедитесь, что на каждом обработанном событии действительно возвращается ответ об успешном выполнении.

Симуляция подписки недоступна

Управление симуляцией требует соответствующих данных о подписке в тестовом режиме. Для некоторых симуляций платежей необходима история продлений. Сначала создайте требуемую тестовую подписку или тестовую покупку, затем используйте меню действий с подпиской, описанное в Lemon Squeezy. руководство разработчика по вебхукам.

Проверочный список по безопасности и запуску

  • Создайте случайный секрет для подписи, сохраните его в конфигурации окружения и смените, если он будет скомпрометирован.
  • Проверьте HMAC над исходным телом перед разбором JSON, записью в базу данных, генерацией лицензии или изменением доступа.
  • Используйте сравнительный метод с защитой от тайминговых атак и отклоняйте отсутствующие или некорректные подписи.
  • Держите секреты и записи тестового режима и живого вебхука отдельно.
  • Разрешать только POST-запросы, устанавливать разумный лимит размера тела, ограничивать частоту недействительных запросов и скрывать данные клиентов из логов.
  • Проверьте дубликаты, события жизненного цикла вне порядка, время простоя базы данных и восстановление после тайм-аута перед заменой туннеля на ваш рабочий URL.

Успешная проверка оформления заказа по счастливому сценарию доказывает лишь то, что одно событие было получено. Интеграция, готовая к производству, подтверждает отклонение подписи, защиту от дубликатов, сходимость жизненного цикла и восстановление после ответа, отличного от 200. Для изучения основных криптографических подводных камней см. руководство по проверке подписи.

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

Как мне протестировать вебхуки Lemon Squeezy на локальном хосте?
Откройте вашу локальную маршрут вебхука через HTTPS-туннель, создайте вебхук, пока панель управления находится в тестовом режиме, используйте тот же секрет подписи локально и вызовите тестовую оплату или имитацию поддерживаемой подписки.
Как проверяется подпись вебхука Lemon Squeezy?
Вычислите HMAC-SHA256 хеш от точного необработанного тела запроса с вашим секретным ключом, декодируйте шестнадцатеричное значение X-Signature и сравните буферы одинаковой длины с помощью функции, безопасной по времени выполнения.
Повторяет ли Lemon Squeezy неудачные вебхуки?
Да. В документации указано, что ответы, отличные от 200, повторяются до трёх дополнительных раз с экспоненциальной задержкой, примерно через 5, 25 и 125 секунд.
Могу ли я симулировать продление подписки Lemon Squeezy в тестовом режиме?
Режим тестирования предоставляет имитацию действий для поддерживаемых событий подписки. Некоторые симуляции, связанные с оплатой, требуют существующей тестовой подписки и данных о продлении, поэтому сначала создайте необходимое тестовое состояние.