Endpoint (API v2): Ротация секрета подписи вебхуков

ℹ️ EasyPay API v2. Это ручка второго поколения EasyPay API (база https://api.appsign.me), пришедшего на смену legacy-API v1 (https://n8n.thenextgen.store/webhook/*). Отличия v2 от v1: авторизация заголовком X-Partner-Api-Key, плоское тело запроса, идемпотентность через client_token (полный ключ собирает сервер), коды ошибок — в UPPER_CASE.

Зачем это нужно

EasyPay подписывает каждый исходящий вебхук HMAC-секретом (см. гайд по проверке подписи). Секрет вы получаете при регистрации вебхука.

Эта ручка меняет секрет для одной среды: генерит новый и оставляет прежний действительным на короткий grace-период. Нужна когда:

  • есть подозрение на утечку секрета;

  • вы делаете плановую ротацию по расписанию безопасности.

💡 Просто потеряли секрет? Ротация не нужна. Ручка регистрации — get-or-create: повторный вызов возвращает текущий секрет. Ротация нужна именно чтобы сменить секрет, а не прочитать его.

Grace-период — переход без потери событий

Ротация мгновенно генерирует новый секрет (current) и сохраняет прежний как previous до момента previous_valid_until (по умолчанию 7 дней). Пока идёт grace-период, EasyPay подписывает каждый вебхук обоими секретами — в заголовке EasyPay-Signature появляются два значения через пробел: v1,<hex> v1,<hex>. Вы успеваете раскатить новый секрет по всем инстансам, не теряя ни одного события.

Как подключиться

  1. У вас уже должен быть зарегистрирован вебхук для нужной среды.

  2. Тот же API-ключ и то же разрешение webhook_config, что и для регистрации.

Endpoint

POST https://api.appsign.me/rotate-partner-webhook-secret
Content-Type: application/json
X-Partner-Api-Key: ваш-api-ключ

Формат запроса

{
  "environment": "live",
  "client_token": "rotate-2026-07-10-leak"
}

Параметры:

Параметр

Обязательный

Описание

environment

Да

Среда, для которой ротировать секрет: "test" или "live". У test и live разные секреты — ротируйте нужную; другая не затрагивается.

client_token

Да

Идемпотентный токен ротации (см. ниже).

Идемпотентность

Ротация требует client_tokenв отличие от ручек создания, где он опционален. Причина: ротация не идемпотентна по содержимому (каждый вызов обязан выдать новый секрет), поэтому безопасного «дедупа по содержимому» здесь нет.

  • Каждый уникальный client_token = одна ротация.

  • Повтор того же client_token не ротирует повторно и возвращает текущий секрет (rotated: false, secret_status: "returned") — безопасный retry.

  • Чтобы ротировать ещё раз, передайте новый client_token.

Формат ответа

Успешный ответ (200)

{
  "success": true,
  "environment": "live",
  "rotated": true,
  "secret_status": "issued",
  "signing_secret": "whsec_live_044a6b63…",
  "previous_valid_until": "2026-07-17T12:00:00.000Z"
}

Поле

Описание

signing_secret

Новый секрет (whsec_live_… / whsec_test_…). Сохраните немедленно. Присутствует при secret_status "issued" (новый) или "returned" (повтор — текущий).

secret_status

"issued" — новый секрет выдан на этом вызове; "returned" — повтор того же client_token, возвращён текущий секрет.

previous_valid_until

ISO8601 — до этого момента прежний секрет ещё проверяет подпись (grace-период).

rotated

true — ротация произошла; false — идемпотентный повтор (новой ротации не было).

environment

Среда, для которой ротировали.

Ошибки

Все ошибки возвращаются с HTTP 200 + полем success: false и error_code. Проверяйте success, а не HTTP-статус.

error_code

Когда срабатывает

INVALID_INPUT

environment не "test"/"live", или отсутствует client_token.

AUTH_REQUIRED / INVALID_API_KEY

Не передан или невалиден API-ключ.

PERMISSION_DENIED

У ключа нет разрешения webhook_config.

IDEMPOTENCY_KEY_REUSED_WITH_DIFFERENT_PAYLOAD

Тот же client_token использован с другим environment. Новый токен.

PREVIOUS_ATTEMPT_FAILED

Прошлая попытка с этим токеном упала. Новый токен.

OPERATION_IN_PROGRESS

Другой запрос с тем же токеном ещё обрабатывается. Повторите чуть позже.

INTERNAL_ERROR

Внутренний сбой EasyPay. Команда заботы уведомлена.

Как перейти на новый секрет

  1. Вызовите ротацию, получите signing_secret и разверните его рядом со старым секретом.

  2. Принимайте вебхук, если проходит любой из ваших секретов — готовые сниппеты в гайде уже перебирают все значения v1, в заголовке.

  3. Уберите старый секрет до previous_valid_until.

Ограничения

  • Ротация — по одной среде за вызов (test или live отдельно).

  • Нужен уже зарегистрированный вебхук и разрешение webhook_config.

  • client_token обязателен (в отличие от ручек создания).

  • Grace-период (действие старого секрета) — по умолчанию 7 дней.

Пример на Python

import urllib.request
import json
import uuid

url = "https://api.appsign.me/rotate-partner-webhook-secret"
payload = {
    "environment": "live",
    "client_token": f"rotate-{uuid.uuid4().hex[:12]}",
}

req = urllib.request.Request(
    url,
    data=json.dumps(payload).encode("utf-8"),
    headers={"Content-Type": "application/json", "X-Partner-Api-Key": "ваш-api-ключ"},
    method="POST",
)

with urllib.request.urlopen(req) as resp:
    result = json.loads(resp.read().decode("utf-8"))

if result.get("success") and result.get("secret_status") == "issued":
    print("НОВЫЙ секрет (сохраните немедленно!):", result["signing_secret"])
    print("Старый секрет действует до:", result.get("previous_valid_until"))
else:
    print("Ответ:", result.get("error_code") or result.get("secret_status"))

Пример на cURL

curl -X POST "https://api.appsign.me/rotate-partner-webhook-secret" \
  -H "Content-Type: application/json" \
  -H "X-Partner-Api-Key: ваш-api-ключ" \
  -d '{
    "environment": "live",
    "client_token": "rotate-2026-07-10-leak"
  }'

См. также