Download the PHP package belisoful/prado-webhooks without Composer

On this page you can find all versions of the php package belisoful/prado-webhooks. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.

FAQ

After the download, you have to make one include require_once('vendor/autoload.php');. After that you have to import the classes with use statements.

Example:
If you use only one package a project is not needed. But if you use more then one package, without a project it is not possible to import the classes with use statements.

In general, it is recommended to use always a project to download your libraries. In an application normally there is more than one library needed.
Some PHP packages are not free to download and because of that hosted in private repositories. In this case some credentials are needed to access such packages. Please use the auth.json textarea to insert credentials, if a package is coming from a private repository. You can look here for more information.

  • Some hosting areas are not accessible by a terminal or SSH. Then it is not possible to use Composer.
  • To use Composer is sometimes complicated. Especially for beginners.
  • Composer needs much resources. Sometimes they are not available on a simple webspace.
  • If you are using private repositories you don't need to share your credentials. You can set up everything on our site and then you provide a simple download link to your team member.
  • Simplify your Composer build process. Use our own command line tool to download the vendor folder as binary. This makes your build process faster and you don't need to expose your credentials for private repositories.
Please rate this library. Is it a good library?

Informations about the package prado-webhooks

prado-webhooks

Extension

Inbound and outbound webhooks for PRADO 4.4: a signature-verifying receiver service, and a sender that signs and retries.

The package does not own your subscriptions. Which URLs a user has subscribed, to what, and under whose secret is your application's question; you hand the answer to send() as an array, and the package delivers it.

This is a 0.x package. Everything here is tested -- the signature schemes against OpenSSL's own output, the queue against SQLite, MySQL and PostgreSQL -- but the version is the honest signal that the interfaces are not frozen. Expect them to move between minor versions until 1.0, and pin accordingly.

Receiving

Add the service. Each <endpoint> is one provider, reachable at index.php?webhook=<id>.

Attach handlers from a module, once the services exist:

$param carries the raw Body, the decoded Payload (and array access to it), the Headers, the Event name, the whole Request, and the response: StatusCode, ResponseBody, ResponseContentType. Do not throw out of a handler — a provider shown a PRADO error page gets a 500, keeps retrying, and learns about your application. Set a status instead.

The endpoint refuses a delivery before any handler sees it when the method is not allowed (405), the body is over MaxBodySize (413), the signature does not verify (401), or the body is not JSON and RequireJson is on (400). The checks run in that order, so an unauthenticated caller cannot make the endpoint hash a large body, and nothing reads the payload before the signature has been checked — the body is not even decoded until then, and a request whose Content-Length already exceeds MaxBodySize is refused before it is read. A refused delivery reaches the service's onRefused event, never onWebhook, with its payload undecoded; a handler there may count or rate limit, and may swap the status for another refusal but not for a success.

An endpoint with no <signature> child accepts everything, which is only right behind something that already authenticates the request. A misspelled child element is refused at boot rather than ignored, and RequireVerifier="true" makes a missing verifier a configuration error instead of an open door.

Sending

Configure the module — the <signature> child is the fallback for targets that bring no secret of their own:

Then send whatever list of subscribers your application keeps:

A target may be a TWebhookTarget, a URL string, or an array of its properties, in which secret is shorthand for an HMAC signature keyed with it. Targets that are disabled, or that subscribe to other events, are skipped.

Retries. A 2xx is delivered and a 4xx is refused — the receiver understood the request and will not like it better next time. Transport failures and the statuses in RetryStatusCodes (500, 502, 503, 504, plus 408, 425 and 429) are retried, with the delay doubling from RetryDelay up to MaxRetryDelay, and a Retry-After — seconds or an HTTP date — overriding it, capped the same way. A queued delivery honors Retry-After too, capped at QueueMaxRetryDelay.

Deliveries are sent inside the request that triggered them, so keep Timeout × MaxAttempts to something a page can afford. onSending, onDelivered and onFailed are raised on the sender for every delivery, and a handler of the first may add headers, rewrite the body, or cancel the delivery outright — which is where an application that outgrows sending inline puts its queue.

Guaranteed delivery

send() delivers inside the request that called it, so a delivery dies with the process. For deliveries that must not be lost, queue() writes them down and a cron run sends them.

with PRADO's cron run once a minute from the system crontab:

Then queue instead of sending:

Both calls stay available — queue() is a different call, not a mode the module is in.

The queue works on SQLite, MySQL and PostgreSQL; all three are exercised by CI.

The guarantee is at-least-once. A drain takes a lease on each delivery rather than a lock, so a runner that is killed mid-attempt has its work picked up when the lease expires — which also means a delivery can go out twice if the runner dies after the receiver accepted it. That is what the delivery id is for, and it stays the same across every attempt. A runner whose lease has expired can no longer write its result back: the write names the lease, so a late finisher cannot disturb whoever holds the delivery now.

A delivery that cannot be attempted at all — a stored target that will not rebuild, a signer with no key — does not take the rest of the batch with it. It uses up its attempts like any other failure and settles as a failed row with the reason on it.

One attempt is made per drain: the queue owns the retry cadence, doubling from QueueRetryDelay up to QueueMaxRetryDelay, for up to QueueMaxAttempts. The retry policy is the sender's: a status outside RetryStatusCodes -- a 4xx -- settles the delivery as failed on the spot rather than sending it again for hours. Deliveries that run out of attempts are kept as failed rows to be looked at and replayed, until the prune task removes them; accepted ones are deleted unless KeepDelivered is on.

Secrets and the queue table. A target holds a signer, and a signer holds a key, which is not something to write into a table. Two ways, and pick one deliberately:

Passing an already-built TWebhookTarget that carries a signature is refused rather than quietly queueing a delivery that would go out unsigned.

Sizing: one run sends up to BatchSize deliveries and holds them for LeaseSeconds. The worst case of a run is the sum of the batch's timeouts — each target's own Timeout where it sets one, the sender's otherwise — so keep that inside the cron interval, or accept overlapping runs, which is safe only while the lease outlasts a whole batch. Handlers that throw during a run are recorded on the delivery rather than turning an accepted delivery into a retry. TWebhookPruneCronTask takes a BatchSize too, to bound one prune. Target URLs are the application's; TWebhookTarget::setUrlValidator() is where it refuses private or link-local ones before a subscriber-supplied URL reaches send() or queue().

Signature schemes

The schemes are general and configured, not one class per provider.

Class The shape it covers
THmacWebhookSignature a keyed hash in a value of its own
TFieldedWebhookSignature a keyed hash packed into one value with named fields, t=…,v1=…
TPublicKeyWebhookSignature an asymmetric signature, PKCS#1 v1.5 or PSS, with a configured key or a certificate the delivery names
TJwtWebhookSignature a JSON Web Token: HS256/384/512, RS256/384/512, ES256/384/512, with claim checks
THttpMessageWebhookSignature HTTP Message Signatures, RFC 9421 — the message lists what it covers
TTokenWebhookSignature a shared secret presented as it is, in a header, the query string, or a posted form field
TIpWebhookVerifier an address allow list, with CIDR and a chain of trusted proxies
TSnsWebhookVerifier Amazon SNS, whose signed string is a canonicalization of the body's own fields
TAnyWebhookSignature / TAllWebhookSignature several of the above, for a secret rotation or a layered check

Everything above implements both IWebhookVerifier and IWebhookSigner except the last three: an address is not something a sender can put in a header, and SNS carries its signature in the body. For the rest, what one PRADO application signs, another verifies with an identically configured object.

What gets signed

PayloadFormat is the part providers disagree about most, so it is a template:

Token Expands to
{body} the raw request body, byte for byte
{method} {url} the HTTP method, the absolute request URL
{timestamp} {id} the timestamp and delivery id the signature is bound to
{crc32} the CRC32 of the body, as a decimal string
{header:Name} {param:name} {query:name} one header, posted form field, or query parameter
{const:NAME} one entry of Constants, for values that come from your own configuration
{params} every posted form field, sorted by name, as name and value concatenated

A form field is a scalar entry of the posted body as PHP parses it: $_POST when receiving, the encoded payload when sending. The query string is never among them — every inbound URL carries ?webhook=<id> there, which no provider signed — and is reached through {url}, {query:name} and Source="query" instead.

A scheme whose payload leaves out {body} is not bound to the body at all unless something else ties them together. BodyHashName is that something: it names where the request presents a digest of its own body — which the signature then covers — and requires it to match. BodyHashSource, BodyHashAlgorithm and BodyHashEncoding say where and how.

Which turns the schemes in the wild into configuration:

Worked examples

Notes on the harder ones

Anything a provider does that none of this expresses is an IWebhookVerifier of your own: the endpoint takes any implementation.

Development

composer fulltest runs the full check: compile, code style, static analysis, unit tests.

composer integration installs the package into a throwaway consumer project and verifies the extra.prado wiring against a real composer require. It needs a PRADO checkout to install the framework from, at ../prado.master unless another path is given: tests/integration/run-install-test.sh /path/to/prado.

Some tests shell out to the openssl command line to check this package's signatures against OpenSSL's own rather than against itself; they skip where the binary is missing.

The queue suite runs against SQLite always, and against MySQL and PostgreSQL when you point it at a server. Both are worth running: each is strict where SQLite is relaxed, in ways this package has to respect — MySQL has no IF NOT EXISTS on CREATE INDEX, PostgreSQL rejects AUTO_INCREMENT — and CI fails the build if either leg quietly skips.

See AGENTS.md for the conventions this package holds to.


All versions of prado-webhooks with dependencies

PHP Build Version
Package Version
Requires php Version >=8.1.0
composer-runtime-api Version ^2.0
pradosoft/prado Version ^4.4@dev
Composer command for our command line client (download client) This client runs in each environment. You don't need a specific PHP version etc. The first 20 API calls are free. Standard composer command

The package belisoful/prado-webhooks contains the following files

Loading the files please wait ...