Enterprise 版本
Webhook
NextPDF Enterprise 通过 HTTP POST 将作业事件投递到按租户划分的 webhook 端点,用一个 HMAC-SHA256 签名为每个负载签名,以指数退避重试,并将永久失败的投递路由到一个死信队列以供检查与重放。本页描述可观察的 webhook 行为与公开契约。
可用性与授权
标题为“可用性与授权”的章节此能力随 NextPDF Enterprise(nextpdf/enterprise)交付,并通过一个 Enterprise 层级的授权信封激活。没有该授权的部署不会加载此能力的类。对比各版本并获取授权。
webhook 面是一项 Enterprise 基础能力,只要安装了 Enterprise 包即可使用;没有单独的按功能划分的标志。
概念概览
标题为“概念概览”的章节一个租户注册一个回调 URL、一个签名密钥,以及一个可选的事件类型列表。空事件列表意味着“订阅全部事件”。注册严格按租户划分:一个租户只能看到并管理它自己的注册,且以一个不匹配的租户进行注册会被拒绝。注销会停用该注册而非删除它,因此历史被保留;只有活跃注册会收到投递。
当为某个租户投递一个作业事件时,每条订阅了该事件类型的活跃注册都会收到一次投递。负载是一个标准化的 JSON 文档——一个唯一的投递标识符、作业标识符、事件类型、事件数据、一个 RFC 3339 时间戳,以及租户标识符。投递是一个携带 JSON body 和四个 header 的 HTTP POST:一个 HMAC-SHA256 签名、一个 unix 秒时间戳、投递标识符,以及事件类型。签名是用注册密钥对规范基串 {timestamp}.{body} 计算的,因此时间戳 header 在密码学上被绑定到 body。接收方在同一基串上重新计算 HMAC,并拒绝时间戳落在可接受新鲜度窗口之外的投递,从而限制重放。
投递使用指数退避。一个 2xx 响应即为成功。一个非 429 的 4xx 响应被视为永久拒绝且不重试。其他失败——5xx、429 或一个连接错误——会以一个翻倍延迟(封顶于某个最大值)重试到策略规定的尝试次数。当所有尝试耗尽时,该投递会被记录到一个内存中的死信队列,连同原始负载、尝试计数、最后一条错误,以及最后的 HTTP 状态;一条死信条目可被标记为已重放。随附两种重试策略——一个 default(5 次尝试,1s 基础,5min 上限)和一个 aggressive(10 次尝试,2s 基础,10min 上限)。
为何如此设计
标题为“为何如此设计”的章节投递被当作一个运维面来对待,而非一次即发即忘的调用。失败按意图分类。一个非 429 的 4xx 是接收方的真实拒绝,因此立即停止。一个 5xx、一个 429 或一个连接错误是瞬时的,因此赢得一次封顶的、逐步退避的重试。耗尽每次尝试的投递永远不会被静默丢弃;它们落入一个可检查的死信队列,且可被重放。签名把一个时间戳绑定进其基串,且每个目的地都要通过一道出口闸,因此对每个租户而言,真实性与抗重放性都是天然成立的。
设计背景:在生产环境中运营 NextPDF。
公开 API 面
标题为“公开 API 面”的章节composer require nextpdf/enterprise:^3受支持的集成点是 webhook 管理器(register、unregister、activeRegistrations、dispatch)、注册值对象(subscribesTo、deactivate)、负载(fromJobEvent、toJson、toArray、sign、signedTimestamp)、投递引擎(deliver、deadLetters、clearDeadLetters)、重试策略(delayForAttempt、shouldRetry、default、aggressive),以及死信条目(markReplayed)。
代码示例 — 快速开始
标题为“代码示例 — 快速开始”的章节use NextPDF\Enterprise\Webhook\WebhookManager;use NextPDF\Enterprise\Webhook\WebhookRegistration;
$manager->register($tenant, new WebhookRegistration( id: $id, tenantId: $tenant->tenantId, url: 'https://customer.example.com/hooks/nextpdf', events: [], // empty = subscribe to all event types secret: $signingSecret,));
$delivered = $manager->dispatch($tenant, $jobEvent); // count of successes接收方侧的验证:
$ts = (int) $request->header('X-NextPDF-Timestamp');if (abs(time() - $ts) > 300) { return new Response(401); // stale timestamp: reject to bound replay}$expected = 'sha256=' . hash_hmac('sha256', $ts . '.' . $rawBody, $sharedSecret);if (! hash_equals($expected, $request->header('X-NextPDF-Signature'))) { return new Response(401);}代码示例 — 生产
标题为“代码示例 — 生产”的章节use NextPDF\Enterprise\Webhook\WebhookDelivery;use NextPDF\Enterprise\Webhook\WebhookRetryPolicy;
$delivery = new WebhookDelivery( $httpClient, $requestFactory, $streamFactory, retryPolicy: WebhookRetryPolicy::aggressive(), // 10 attempts, 2s base, 10min cap logger: $logger,);
$manager = new WebhookManager($delivery, $logger);$manager->dispatch($tenant, $jobEvent);
foreach ($delivery->deadLetters() as $dead) { $this->scheduleReplay($dead); // inspect last error + last HTTP status}边界情形与坑
标题为“边界情形与坑”的章节- 空事件列表订阅全部。 一条没有事件类型的注册会收到每一个事件;传入一个显式列表以限定其范围。
- 租户隔离被强制执行。 以一个与上下文租户不同的租户 ID 进行注册会被拒绝;投递只遍历调用方租户的活跃注册。
- 4xx(除 429 外)是终态。 一个非 429 的 4xx 不重试——它被视为永久的接收方拒绝并进入死信队列。
- 注销是软的。 注销会停用;记录持久存在并被排除在投递之外。
- 死信队列在内存中。 它用于在进程生命周期内进行检查与重放;如果你需要跨重启的持久重放,请自行持久化条目。
投递代价与该租户中订阅了该事件的活跃注册数量成正比。每次投递是对已签名基串计算的一次 HMAC-SHA256 加上一次 HTTP 往返;重试会增加受限的指数退避延迟。签名是 O(负载大小)。
安全说明
标题为“安全说明”的章节每个负载都用一个以注册密钥为键的 HMAC-SHA256 签名认证,并以 sha256=<hex> 的形式在 X-NextPDF-Signature header 中发送。签名覆盖 {timestamp}.{body} 基串,且时间戳随 X-NextPDF-Timestamp header 传输;接收方用恒定时间比较来验证,并拒绝新鲜度窗口之外的投递以限制重放。目的地 URL 在每次发送前都要通过一道中央出口闸:要求 HTTPS,且解析为私有、环回、链路本地或云元数据地址的主机会被拒绝而不发出请求,并被路由到死信队列。签名密钥是逐注册的;请把它当作凭据对待。该签名认证负载的完整性与来源;它不是一个加密层——不要把接收方不应看到的机密放入事件数据。
符合性
标题为“符合性”的章节- 负载认证使用带 SHA-256 的 HMAC,即 FIPS PUB 198-1 的带密钥哈希消息认证码;OWASP ASVS 5.0 将 HMAC-SHA-256 列入其批准的消息认证算法之一。
- 负载时间戳是 RFC 3339 日期时间字符串。注意:本页未从 RAG 语料库检索 RFC 3339;该格式是代码声明(RFC 3339 扩展),标注为代码声明而非 RAG 已验证。
行为契约
标题为“行为契约”的章节- 注册严格按租户划分;以一个不匹配的租户进行注册会被拒绝,且注销是一次保留历史的软停用。
- 空事件列表订阅全部事件;只有订阅了该事件类型的活跃注册才会收到一次投递。
- 每次投递是一个带 JSON body 加上一个 HMAC-SHA256 签名 header(对
{timestamp}.{body}基串计算)、一个 unix 秒时间戳 header、投递标识符与事件类型的 HTTP POST。 - 2xx 即为成功;非 429 的 4xx 是永久拒绝(不重试);5xx、429 或一个连接错误会以封顶的翻倍退避重试到策略规定的尝试次数。
- 耗尽的尝试会把该投递记录到一个内存中的死信队列(负载、尝试计数、最后一条错误、最后状态);一条死信条目可被标记为已重放。
发布边界
标题为“发布边界”的章节本页仅描述外部可观察行为与受支持的公开 API 面。内部命名空间路径、辅助类、机制表、runbook 文件名与工单前缀不在范围之内。
Core 回退
标题为“Core 回退”的章节NextPDF Core(Apache-2.0)没有任何 webhook 注册或投递面——完全没有;此能力在 Core 层级没有对应物。
Pro 回退
标题为“Pro 回退”的章节NextPDF Pro 没有任何 webhook 注册或投递面——完全没有;此能力在 Pro 层级没有对应物。webhook 管理器、注册、负载、投递引擎与重试策略仅在 nextpdf/enterprise 包中提供。
Enterprise 边界说明
标题为“Enterprise 边界说明”的章节重试策略、退避计划与死信处理在行为层面被描述。死信队列在内存中,用于在进程生命周期内进行检查与重放;跨重启的持久持久化以及任何内部投递内部机制不在公开面之内。
部署边界
标题为“部署边界”的章节运营方负责回调端点、逐注册的签名密钥(按凭据对待)、在需要跨重启重放时对死信条目的持久化,以及接收方 URL 的 HTTPS 态势。NextPDF Enterprise 进行签名与投递,但其本身不在进程生命周期之外持久化注册或死信。
法律合规边界
标题为“法律合规边界”的章节webhook 面不适用任何出口管制限制。HMAC 签名认证负载的完整性与来源;它不是一个加密层——运营方不得将接收方不应看到的机密放入事件数据。本文档不是法律意见;请咨询你自己的合规与法律顾问。