diff --git a/docs/09-integration-guide.md b/docs/09-integration-guide.md index 0f47189..0a2c28d 100644 --- a/docs/09-integration-guide.md +++ b/docs/09-integration-guide.md @@ -131,10 +131,45 @@ локальная валидация клиента до HTTP; не зарегистрированная тройка → 422; отправка с чужим/отозванным ключом → 401; чужой event_id → 404 (`status`). -## 7. Анти-паттерны (каждое из них уже решено на сервере — не повторять в сервисе) +## 7. Приём s2s-доставки — тот же SDK + +Большинство сервисов-приёмников и шлют события (§3) — поэтому приёмная +половина живёт в **том же SDK**, без второй зависимости. Одна точка входа +`verify_webhook()` проверяет подпись (одна схема на всю экосистему — зеркало +`WebhookSignature.php` gnexus-auth и `app/signature.py`) и распарсивает конверт: + +Python: + +```python +from gnexus_synapse import verify_webhook + +@router.post("/webhooks/synapse") +async def synapse_webhook(request: Request): + env = verify_webhook(await request.body(), request.headers, S2S_SECRET) + # env["event_id"], env["subject"], env["action"], env["payload"], ... +``` + +PHP: + +```php +use GNexus\Synapse\Webhook\WebhookVerifier; +$envelope = WebhookVerifier::verify($r->getContent(), $r->headers->all(), $secret); +``` + +Всё, что не прошло (нет подписи, чужой секрет, replay — тело старше 300 с, +не-JSON), ловится одним `SynapseWebhookError` / `InvalidWebhookException`. +Endpoint обязан возвращать 2xx быстро (первая попытка — сразу при +маршрутизации; провал → ретраи Synapse 30 с → 2 м → 10 м → 30 м) и быть +идемпотентным или не бояться повторов. Секрет per-target — +`S2S_SECRET_` в env приёмника (+ gnexus-creds); в Synapse цель +создаётся с `token_ref` (админка/MCP `target_create`). Заголовки-контекст: +`X-Gnexus-Event-Type` = `..`, `X-Synapse-Source`. +Полная спецификация подписи — docs/05 → «Доставка s2s». + +## 8. Анти-паттерны (каждое из них уже решено на сервере — не повторять в сервисе) - Локальные очереди/ретраи отправки — Synapse отвечает 202 мгновенно и сам - ретраит доставки (backoff 30 с → 2 м → 10 м → 30 с, 5 попыток); клиент + ретраит доставки (backoff 30 с → 2 м → 10 м → 30 м, 5 попыток); клиент ничего этого не делает. - Опрос `status()` «до delivered» в бизнес-коде — события fire-and-forget; нужен мониторинг — это дежурные метрики Synapse и админка, не поллинг.