Enterprise phiên bản
Webhook — tài liệu tham chiếu chuyên sâu
Tổng quan nhanh
Phần tiêu đề “Tổng quan nhanh”Namespace NextPDF\Enterprise\Webhook cung cấp việc gửi webhook theo phạm vi thuê bao cho các sự kiện công việc. Bề mặt công khai gồm sáu ký hiệu: WebhookManager, WebhookRegistration, WebhookPayload, WebhookDelivery, WebhookRetryPolicy, và DeadLetterEntry. Manager đăng ký các endpoint theo từng thuê bao và gửi các sự kiện công việc tới những đăng ký có theo dõi. Engine gửi thực hiện POST một payload JSON đã ký HMAC-SHA256, kiểm tra mọi đích đến qua cổng chặn ra SSRF của Core, thử lại với backoff theo cấp số nhân, và ghi các thất bại vĩnh viễn vào một hàng đợi dead-letter trong bộ nhớ. Kể từ 3.1.0, chữ ký ràng buộc header X-NextPDF-Timestamp vào chuỗi cơ sở của MAC, nên bên nhận xác minh độ tươi mới và tính toàn vẹn cùng lúc. Để xem hướng dẫn ở mức quy trình, hãy xem Webhook.
Tính khả dụng và cấp phép
Phần tiêu đề “Tính khả dụng và cấp phép”Năng lực này đi kèm trong NextPDF Enterprise (nextpdf/enterprise) và kích hoạt bằng một license envelope bậc Enterprise. Một triển khai không có quyền đó sẽ không nạp các lớp của năng lực này. So sánh các phiên bản và lấy giấy phép.
Bề mặt webhook là một năng lực Enterprise cơ sở, sẵn dùng ngay khi gói Enterprise được cài đặt; không có cờ riêng theo từng tính năng. NextPDF Core (Apache-2.0) và NextPDF Pro không có bề mặt đăng ký hay gửi webhook nào; manager, đăng ký, payload, engine gửi, chính sách thử lại, và mục dead-letter chỉ đi kèm trong nextpdf/enterprise.
Bề mặt API công khai
Phần tiêu đề “Bề mặt API công khai”| Ký hiệu | Tham số | Hành vi mặc định | Trả về | Ném hoặc thất bại với | Ghi chú |
|---|---|---|---|---|---|
WebhookManager::__construct | WebhookDelivery $delivery, ?LoggerInterface $logger = null | Tạo một manager với chỉ mục đăng ký trong bộ nhớ rỗng | WebhookManager mới | Không ném | Các đăng ký được lập chỉ mục theo từng thuê bao |
WebhookManager::register | TenantContext $tenant, WebhookRegistration $registration | Thêm đăng ký vào chỉ mục của thuê bao đang gọi | void | InvalidArgumentException khi thuê bao của đăng ký không khớp thuê bao của ngữ cảnh | Đăng ký chéo thuê bao bị từ chối trước khi lưu |
WebhookManager::unregister | TenantContext $tenant, string $registrationId | Thay thế đăng ký khớp bằng một bản sao đã vô hiệu hóa | bool | Không ném; trả về false khi không tìm thấy id | Vô hiệu hóa mềm; lịch sử được giữ lại |
WebhookManager::activeRegistrations | TenantContext $tenant | Lọc các đăng ký của thuê bao xuống những đăng ký đang hoạt động | list<WebhookRegistration> | Không ném | Chỉ các đăng ký của thuê bao đang gọi mới hiển thị |
WebhookManager::dispatch | TenantContext $tenant, JobEvent $event | Gửi sự kiện tới mọi đăng ký đang hoạt động có theo dõi loại sự kiện đó | int (số lần gửi thành công) | Truyền tiếp JsonException khi dữ liệu sự kiện không thể mã hóa JSON; các thất bại khi gửi không ném | Một id lần gửi 32-hex mới được sinh cho mỗi lần gửi tới đăng ký |
WebhookRegistration::__construct | string $id, string $tenantId, string $url, array $events, string $secret, bool $active = true, ?string $description = null | Lưu nguyên văn các giá trị được cung cấp | WebhookRegistration mới | Không khai báo @throws; PHP phát sinh TypeError khi kiểu đối số không khớp dưới strict_types | final readonly; $events rỗng nghĩa là theo dõi tất cả |
WebhookRegistration::subscribesTo | JobEventType $eventType | true khi $events rỗng hoặc chứa loại đó | bool | Không ném | So sánh danh tính nghiêm ngặt |
WebhookRegistration::deactivate | — | Trả về một bản sao không hoạt động | self | Không ném | Thực thể gốc không thay đổi |
WebhookPayload::fromJobEvent | JobEvent $event, string $tenantId, string $deliveryId | Sao chép id công việc, loại sự kiện, dữ liệu, và dấu thời gian từ sự kiện | self | Không ném | Factory tĩnh được dispatch dùng |
WebhookPayload::toJson | — | Tuần tự hóa thân sáu trường với các dấu gạch chéo không thoát | non-empty-string | JsonException khi dữ liệu sự kiện không thể mã hóa JSON | JSON_THROW_ON_ERROR | JSON_UNESCAPED_SLASHES |
WebhookPayload::toArray | — | Trả về thân dưới dạng mảng kết hợp | array<string, mixed> | Không ném | Dấu thời gian định dạng RFC 3339 mở rộng |
WebhookPayload::signedTimestamp | — | Thời gian sự kiện theo giây Unix, kẹp về không hoặc lớn hơn | int<0, max> | Không ném | Phát ra dưới dạng X-NextPDF-Timestamp và ràng buộc vào MAC |
WebhookPayload::sign | string $secret | HMAC-SHA256 trên chuỗi cơ sở {signedTimestamp}.{jsonBody} | non-empty-string (hex) | JsonException qua toJson() khi thân không thể mã hóa | Ràng buộc header dấu thời gian vào thân bằng mật mã |
WebhookDelivery::__construct | ClientInterface $httpClient, RequestFactoryInterface $requestFactory, StreamFactoryInterface $streamFactory, WebhookRetryPolicy $retryPolicy = new WebhookRetryPolicy(), ?LoggerInterface $logger = null | Engine gửi PSR-18/PSR-17 với hàng đợi dead-letter rỗng | WebhookDelivery mới | Không ném | Chính sách mặc định: 5 lần thử, cơ sở 1 s, giới hạn 300 s |
WebhookDelivery::deliver | WebhookRegistration $registration, WebhookPayload $payload | POST payload đã ký với việc kiểm tra chặn ra SSRF theo từng lần thử và backoff theo cấp số nhân | bool | JsonException trước lần thử đầu tiên khi thân không thể mã hóa; ngược lại không ném — false nghĩa là payload đã được định tuyến tới hàng đợi dead-letter | true chỉ khi có phản hồi 2xx |
WebhookDelivery::deadLetters | — | Trả về mọi mục đã ghi | list<DeadLetterEntry> | Không ném | Trong bộ nhớ, phạm vi tiến trình |
WebhookDelivery::clearDeadLetters | — | Làm rỗng hàng đợi dead-letter | void | Không ném | Không thể hoàn tác; hãy xuất các mục trước nếu cần phát lại |
WebhookRetryPolicy::__construct | int $maxRetries = 5, int $baseDelaySeconds = 1, int $maxDelaySeconds = 300 | Lưu các giá trị của chính sách | WebhookRetryPolicy mới | Không khai báo @throws; các tham số được ghi là positive-int | $maxRetries đếm tổng số lần thử |
WebhookRetryPolicy::delayForAttempt | int $attempt | baseDelaySeconds × 2^(attempt − 1), giới hạn ở maxDelaySeconds | positive-int | Không ném | Số thứ tự lần thử tính từ 1 |
WebhookRetryPolicy::shouldRetry | int $currentAttempt | true khi lần thử hiện tại còn dưới mức tối đa | bool | Không ném | Phần chờ được bỏ qua sau lần thử cuối cùng |
WebhookRetryPolicy::default | — | 5 lần thử, cơ sở 1 s, giới hạn 300 s | self | Không ném | Factory tĩnh; mặc định cho sản phẩm |
WebhookRetryPolicy::aggressive | — | 10 lần thử, cơ sở 2 s, giới hạn 600 s | self | Không ném | Factory tĩnh cho các endpoint quan trọng |
DeadLetterEntry::__construct | string $id, string $registrationId, WebhookPayload $payload, int $attempts, string $lastError, ?int $lastHttpStatus, DateTimeImmutable $failedAt, bool $replayed = false | Lưu nguyên văn bản ghi thất bại | DeadLetterEntry mới | Không khai báo @throws; TypeError dưới strict_types | final readonly; $lastHttpStatus null nghĩa là thất bại truyền tải |
DeadLetterEntry::markReplayed | — | Trả về một bản sao với replayed = true | self | Không ném | Cùng id; mục gốc không thay đổi |
public function __construct( private readonly WebhookDelivery $delivery, private readonly ?LoggerInterface $logger = null,) {}
public function register(TenantContext $tenant, WebhookRegistration $registration): void
public function unregister(TenantContext $tenant, string $registrationId): bool
public function activeRegistrations(TenantContext $tenant): array
public function dispatch(TenantContext $tenant, JobEvent $event): intpublic function __construct( public string $id, public string $tenantId, public string $url, public array $events, public string $secret, public bool $active = true, public ?string $description = null,) {}
public function subscribesTo(JobEventType $eventType): bool
public function deactivate(): selfpublic static function fromJobEvent( JobEvent $event, string $tenantId, string $deliveryId,): self
public function toJson(): string
public function toArray(): array
public function signedTimestamp(): int
public function sign(string $secret): stringpublic function __construct( private readonly ClientInterface $httpClient, private readonly RequestFactoryInterface $requestFactory, private readonly StreamFactoryInterface $streamFactory, private readonly WebhookRetryPolicy $retryPolicy = new WebhookRetryPolicy(), private readonly ?LoggerInterface $logger = null,) {}
public function deliver(WebhookRegistration $registration, WebhookPayload $payload): bool
public function deadLetters(): array
public function clearDeadLetters(): voidpublic function __construct( public int $maxRetries = 5, public int $baseDelaySeconds = 1, public int $maxDelaySeconds = 300,) {}
public function delayForAttempt(int $attempt): int
public function shouldRetry(int $currentAttempt): bool
public static function default(): self
public static function aggressive(): selfpublic function __construct( public string $id, public string $registrationId, public WebhookPayload $payload, public int $attempts, public string $lastError, public ?int $lastHttpStatus, public DateTimeImmutable $failedAt, public bool $replayed = false,) {}
public function markReplayed(): selfHợp đồng hành vi
Phần tiêu đề “Hợp đồng hành vi”- Các đăng ký được lập chỉ mục theo từng thuê bao.
register()từ chối một đăng ký có mã định danh thuê bao không khớp với ngữ cảnh đang gọi.unregister()là một thao tác vô hiệu hóa mềm: đăng ký được thay bằng một bản sao không hoạt động, giữ lại lịch sử trong khi loại nó khỏi các lần gửi tương lai. dispatch()chỉ duyệt qua các đăng ký đang hoạt động của thuê bao đang gọi có theo dõi loại sự kiện được gửi. Một danh sách sự kiện theo dõi rỗng nghĩa là theo dõi tất cả. Giá trị trả về đếm số lần gửi thành công.- Mỗi lần gửi là một HTTP POST với một thân JSON và năm header:
Content-Type: application/json,X-NextPDF-Signature(sha256=<hex>),X-NextPDF-Timestamp(giây unix),X-NextPDF-Delivery-Id, vàX-NextPDF-Event. - Các trường của thân JSON là
delivery_id,job_id,event_type,data,timestamp(RFC 3339 mở rộng), vàtenant_id, được tuần tự hóa với các dấu gạch chéo không thoát. Các giá trị loại sự kiện đến từJobEventTypetrongnextpdf/core:progress,completed,failed,cancelled. - Sơ đồ chữ ký (thay đổi trong 3.1.0, phá vỡ tương thích). Chuỗi cơ sở HMAC-SHA256 là
{signedTimestamp}.{jsonBody}, được lập khóa bằng secret của đăng ký — không phải chỉ riêng thân. Giá trịX-NextPDF-Timestamplà thành phần dấu thời gian của MAC, nên một header dấu thời gian bị giả mạo hoặc phát lại sẽ làm chữ ký không hợp lệ. - Xác minh ở bên nhận: đọc header
X-NextPDF-TimestampT; từ chối khiTnằm ngoài một cửa sổ độ tươi mới chấp nhận được (ví dụ 300 s); tính lạihash_hmac('sha256', T . '.' . rawBody, secret)trên các byte thô nhận được; so sánh theo thời gian hằng số với giá trị header sau khi loại bỏ tiền tốsha256=. - Thân, chữ ký, và id lần gửi được tính một lần cho mỗi lần gửi và giữ nguyên qua các lần thử lại.
- Cổng chặn ra SSRF. Trước mỗi lần thử, URL đích đi qua cổng
UrlValidator::validateExternalUrl()của Core: chỉ scheme HTTPS; các dải loopback, riêng tư, dành riêng, carrier-grade-NAT, cloud-metadata, và các dải chuyển tiếp IPv6 nhúng IPv4 đều bị chặn; tên host được phân giải DNS (A và AAAA) và các host không phân giải được bị từ chối theo kiểu fail-closed. Một URL bị chặn sẽ không bao giờ được gửi: vòng lặp thử bị hủy và payload được định tuyến thẳng tới hàng đợi dead-letter với lỗi cuối cùngBlocked SSRF destination:và trạng thái HTTP null. - Phân loại kết quả theo từng lần thử: 2xx là thành công và trả về ngay lập tức; một 4xx khác 429 là cuối cùng và chuyển thẳng tới dead-letter; mọi kết quả khác — 3xx, 429, 5xx, hoặc một ngoại lệ truyền tải — đều có thể thử lại đến tổng số lần thử của chính sách.
- Backoff theo cấp số nhân: thời gian chờ trước lần thử kế tiếp là
baseDelaySeconds × 2^(attempt − 1), giới hạn ởmaxDelaySeconds. Phần chờ được bỏ qua sau lần thử cuối cùng. - Khi không lần thử nào thành công, một
DeadLetterEntryghi lại một id duy nhất, id đăng ký, payload gốc, số lần thử (kẹp ở mức tối đa của chính sách), thông báo lỗi cuối cùng, trạng thái HTTP cuối cùng (null khi thất bại truyền tải hoặc bị chặn SSRF), và dấu thời gian thất bại. - Hàng đợi dead-letter nằm trong bộ nhớ và bị giới hạn trong vòng đời tiến trình.
markReplayed()tạo ra một bản sao được gắn cờ; nó không gửi lại, và hàng đợi giữ lại mục gốc.
Trường hợp biên và các chế độ thất bại
Phần tiêu đề “Trường hợp biên và các chế độ thất bại”- Danh sách sự kiện rỗng. Đăng ký nhận mọi loại sự kiện. Hãy giới hạn danh sách một cách tường minh khi bên nhận không được thấy tất cả sự kiện.
- 4xx cuối cùng so với thất bại truyền tải. Một từ chối 4xx ghi một
lastHttpStatuscó giá trị; một thất bại kết nối ghi null. Hãy dùng null để phân biệt từ chối của bên nhận với thất bại truyền tải. - Đích bị chặn SSRF. Một đăng ký trỏ tới một địa chỉ HTTP, riêng tư, loopback, hoặc metadata sẽ vào dead-letter ngay lần thử đầu tiên với lỗi
Blocked SSRF destination:và trạng thái null. Không có yêu cầu đi ra nào được thực hiện. Hãy sửa URL và đăng ký lại. - Bên nhận cũ sau khi nâng cấp. Một bên nhận vẫn xác minh HMAC chỉ-trên-thân trước 3.1.0 sẽ fail closed đối với các lần gửi 3.1.0. Hãy chuyển bên nhận sang chuỗi cơ sở
{timestamp}.{body}và tiêu thụX-NextPDF-Timestamp. - Dữ liệu sự kiện không thể mã hóa.
toJson()vàsign()némJsonException, ngoại lệ này truyền ra khỏideliver()vàdispatch()trước khi thực hiện bất kỳ lần thử nào. - Chặn đồng bộ.
deliver()ngủ nội tuyến giữa các lần thử. Backoff tích lũy đạt 15 s dưới chính sách mặc định và khoảng 17 phút dưới chính sách aggressive. Hãy gửi từ một queue worker khi độ trễ của bên nhận không đáng tin. - Kẹp số lần thử. Số lần thử được ghi không bao giờ vượt quá mức tối đa của chính sách, ngay cả khi bộ đếm vòng lặp nội bộ tiến vượt quá nó khi cạn kiệt.
- Tăng trưởng hàng đợi và tính bền vững. Hàng đợi dead-letter tăng trưởng không giới hạn trong tiến trình và biến mất khi khởi động lại. Hãy xuất các mục qua
deadLetters()và lưu bền vững chúng ra bên ngoài trước khi gọiclearDeadLetters()khi cần phát lại bền vững. - Phát lại do người vận hành điều khiển. Gửi lại nghĩa là gọi
deliver()lần nữa với payload của mục;markReplayed()chỉ ghi lại sự kiện đó trên một bản sao. - Rủi ro tồn dư DNS-rebinding. URL được kiểm tra lại ở mỗi lần thử, điều này thu hẹp nhưng không đóng hoàn toàn cửa sổ rebinding: lớp trừu tượng PSR-18 không thể ghim kết nối vào IP đã được xác thực. Hãy thêm các kiểm soát chặn ra ở tầng mạng khi rủi ro tồn dư này quan trọng.
- Xử lý secret. Secret của đăng ký là một thông tin xác thực. HMAC chỉ xác thực tính toàn vẹn và nguồn gốc — nó không phải là tính bảo mật. Đừng đặt vào payload sự kiện dữ liệu mà bên nhận không được thấy.
Hành vi ở chế độ FIPS
Phần tiêu đề “Hành vi ở chế độ FIPS”Việc ký payload là HMAC-SHA256 qua hash_hmac() của PHP, nên nó dựa vào nhà cung cấp mã hóa của host. Trong một bản build bị ràng buộc FIPS, một nguyên hàm không được chấp thuận sẽ thất bại tại ranh giới mã hóa thay vì hạ cấp. Lớp webhook không thêm chính sách mã hóa riêng nào.
Tính phù hợp
Phần tiêu đề “Tính phù hợp”- Xác thực payload triển khai HMAC, mã xác thực thông điệp băm-có-khóa của FIPS PUB 198-1 §1, được hiện thực hóa với SHA-256.
- Bảo vệ chống phát lại tuân theo hướng dẫn bảo mật webhook của OWASP Cheat Sheet Series: dấu thời gian sự kiện đi trong một header riêng và được gieo vào phép tính chữ ký, nên một dấu thời gian bị giả mạo sẽ không qua được xác minh.
- Các dấu thời gian trong thân dùng định dạng date-time RFC 3339 mở rộng. Khai báo trong mã: RFC 3339 không được truy xuất từ kho RAG cho trang này.
- Đây là các phát biểu về năng lực dựa trên mã nguồn sản phẩm và các điều khoản được trích dẫn. NextPDF không đưa ra tuyên bố phù hợp hay chứng nhận nào cho bề mặt này.
Ghi chú phát triển
Phần tiêu đề “Ghi chú phát triển”- Mọi lớp khai báo
strict_types=1và làfinal;WebhookRegistration,WebhookPayload,WebhookRetryPolicy, vàDeadLetterEntrylàfinal readonlyvới các thuộc tính công khai được promoted. - Module mang một chú thích
@sincelà2.2.0; sơ đồ chữ ký ràng buộc dấu thời gian là một thay đổi phá vỡ tương thích được ghi nhận trong 3.1.0. - Engine gửi nhận các lớp trừu tượng PSR-18/PSR-17, nên một mock HTTP client có thể chạy toàn bộ đường gửi, thử lại, và dead-letter ngoại tuyến. Logger mặc định là null; hãy tiêm một logger PSR-3 ở sản phẩm nếu không các thất bại chỉ hiện ra qua giá trị trả về.
- Các triển khai bên nhận nên dùng
hash_equals()để so sánh chữ ký và thực thi một cửa sổ độ tươi mới trênX-NextPDF-Timestamp. - Các test biên được khuyến nghị: đăng ký lệch thuê bao, fan-out với danh sách sự kiện rỗng, 4xx cuối cùng, cạn kiệt số lần thử, URL bị chặn SSRF, từ chối chữ ký khi dấu thời gian bị giả mạo so với một vector cố định, và kẹp số lần thử của dead-letter.
Ranh giới xuất bản
Phần tiêu đề “Ranh giới xuất bản”Trang này chỉ tài liệu hóa hành vi có thể quan sát được từ bên ngoài và bề mặt API công khai được hỗ trợ. Các đường dẫn namespace nội bộ, các lớp trợ giúp, các bảng cơ chế, các tên tệp runbook, và các tiền tố ticket đều nằm ngoài phạm vi.
Xem thêm
Phần tiêu đề “Xem thêm”- Webhook — NextPDF Enterprise — trang năng lực: quy trình, cấu hình, và các ví dụ đăng ký có lời giải.
- SaaS — Tài liệu tham chiếu chuyên sâu — danh tính thuê bao, API key, và hạn ngạch; nguồn của
TenantContext. - Metering — Tài liệu tham chiếu chuyên sâu — fan-out đo lường sử dụng với cùng kỷ luật gửi PSR-18.