Skip to content

Reference

phil/standard-webhooks

Every public type of the library, by topic. All live in Phil\StandardWebhooks, the exceptions in Phil\StandardWebhooks\Exception. Value objects are immutable (final readonly), with private constructors behind named constructors unless noted.

Receiving

TypeRoleState
WebhookReceiverreceive(headers, rawBody, now): timestamp, signature under cached keys, one reload, audience. Public constructor (VerificationKeyProvider, Tolerance, ?string $audience = null, Verifier = new Verifier()).Implemented and tested
VerificationKeyProviderPort: keys() and refresh(), the receiver's trusted keys.Port
FixedKeysVerificationKeyProvider for keys known in advance: fromStrings(), a public constructor; refresh() gives them back.Implemented and tested

Signing and verifying

TypeRoleState
SchemeString-backed enum: V1 (v1, HMAC-SHA256, 32-byte signatures) and V1a (v1a, Ed25519, 64-byte signatures); signatureLength().Implemented and tested
SignedContentof(id, timestamp, rawBody): the bytes both schemes sign, {id}.{timestamp}.{body}.Implemented and tested
Signersign(id, timestamp, rawBody, key, ...moreKeys): the WebhookHeaders of one attempt.Implemented and tested
Verifierverify() (timestamp first, then signatures) and signatureVerifies().Implemented and tested
SigningKeyInterface: scheme(), sign().Interface, implemented by Ed25519SecretKey and HmacSecret
VerificationKeyInterface: scheme(), verifies().Interface, implemented by Ed25519PublicKey and HmacSecret
Base64Strict padded base64: constant-time encodeSecret()/decodeSecret(), native encodePublic()/decodePublic().Implemented and tested

Values

TypeRoleState
WebhookHeadersThe three headers: fromStrings(), toArray(), constants ID, TIMESTAMP, SIGNATURE; public constructor.Implemented and tested
WebhookIdwebhook-id: printable ASCII without space or full stop, at most 255 bytes; fromString(), generate(), equals().Implemented and tested
WebhookTimestampwebhook-timestamp: strict Unix seconds; fromHeader(), fromDateTime(), skewFrom(), isWithin(), toHeader().Implemented and tested
TolerancePositive number of seconds, either direction; ofSeconds().Implemented and tested
SignatureOne {version},{base64} entry: of(), tryFromHeaderEntry(), toHeaderEntry().Implemented and tested
SignatureListwebhook-signature, at most eight entries: of(), fromHeader(), toHeader().Implemented and tested
Ed25519SecretKeywhsk_ key, signs v1a: generate(), fromString(), fromBytes() (seed or 64 bytes), toString(), publicKey().Implemented and tested against RFC 8032
Ed25519PublicKeywhpk_ key, verifies v1a: fromString(), fromBytes(), toString(), equals().Implemented and tested against RFC 8032
Keyssigning() and verification(): a key read by its prefix.Implemented and tested
HmacSecretwhsec_ secret of 24 to 64 bytes, signs and verifies v1: generate(), fromString(), toString().Implemented and tested against the Standard Webhooks v1 vector
EnvelopeThe JSON body: type (required), timestamp, data, audience; fromJson(), assertAddressedTo().Implemented and tested
VerifiedWebhookA delivery whose timestamp and signature verified: id, timestamp, rawBody, envelope(); public constructor.Implemented and tested

De-duplication

TypeRoleState
ProcessedWebhookRepositoryPort: atomic claim(id, processedAt) inside the processing transaction.Port

Exception

TypeRoleState
WebhookExceptionMarker interface extending Throwable: one catch at the HTTP boundary.Implemented
InvalidWebhookValueInvalidArgumentException: malformed input, typically 400; because(string).Implemented and tested
WebhookRejectedRuntimeException: a well-formed delivery refused; public RejectionReason $reason; timestampOutsideTolerance(), noValidSignature(), audienceMismatch().Implemented and tested
RejectionReasonString-backed enum: TimestampOutsideTolerance, NoValidSignature, AudienceMismatch.Implemented and tested

Not in the library

  • An HTTP layer: a controller, a request parser, an outbound client, a retry schedule or a dead-letter queue.
  • A storage for the processed ids.

phil/standard-webhooks-bundle brings the Symfony wiring, the controller argument, a sender over Symfony's HTTP client and a Doctrine table.