- Mirror the ergonomics and reliability of the TypeScript
@marcohefti/request-network-api-clientin a PHP-first package that WooCommerce (and other PHP runtimes) can consume without a Node bridge. - Lean on shared assets (OpenAPI spec, webhook fixtures) so TypeScript and PHP SDKs stay aligned as the Request REST API evolves.
- Follow modern PHP standards: PSR-4 autoloading, PSR-18-friendly transport adapters, and pluggable retry/logging hooks.
graph TB
A[RequestClient] --> B[Domain Facades]
A --> C[HttpClient]
B --> C
C --> D[RetryPolicy]
C --> E[Interceptors]
C --> F[RuntimeValidation]
C --> G[HttpAdapter]
G --> H[CurlHttpAdapter]
G --> I[Psr18HttpAdapter]
style A fill:#e1f5ff
style C fill:#fff4e1
style G fill:#ffe1f5
sequenceDiagram
participant Client as RequestClient
participant Facade as Domain Facade
participant HTTP as HttpClient
participant Retry as RetryPolicy
participant Adapter as HttpAdapter
participant API as Request Network API
Client->>Facade: requests().create(data)
Facade->>HTTP: request(options)
HTTP->>Retry: execute with retry
loop Retry Attempts
Retry->>Adapter: send request
Adapter->>API: HTTP POST
API-->>Adapter: Response/Error
Adapter-->>Retry: Response
alt Success
Retry-->>HTTP: Response
else Retryable Error
Retry->>Retry: Wait with backoff
end
end
HTTP-->>Facade: Parsed response
Facade-->>Client: Domain data
RequestClient (public entrypoint)
│
├── Config\RequestClientConfig (immutable options, auth headers, telemetry)
├── Http\HttpClient (request lifecycle and retry loop)
│ ├── Http\RequestOptions (caller-supplied HTTP shape)
│ ├── Http\PendingRequest (merges config + options, builds URL/headers)
│ └── Http\HttpAdapter (transport abstraction)
│ └── Http\Adapter\CurlHttpAdapter (placeholder; PSR-18 bridge will follow)
└── Retry\RetryPolicy (retry semantics, defaults to StandardRetryPolicy)
Exceptions bubble up through `RequestApiException` so consumers always receive status, Request error
code, and correlation identifiers when available.
sequenceDiagram
participant Webhook as Incoming Webhook
participant Middleware as WebhookMiddleware
participant Verifier as SignatureVerifier
participant Parser as WebhookParser
participant Dispatcher as WebhookDispatcher
participant Handler as Event Handlers
Webhook->>Middleware: PSR-7 Request
Middleware->>Verifier: Verify signature
alt Invalid Signature
Verifier-->>Middleware: Exception
Middleware-->>Webhook: 401 Unauthorized
else Valid Signature
Verifier-->>Middleware: Verified
Middleware->>Parser: Parse payload
Parser->>Parser: Validate schema
Parser->>Parser: Create typed event
Parser-->>Middleware: ParsedWebhookEvent
Middleware->>Dispatcher: Dispatch event
Dispatcher->>Handler: Invoke listeners
Handler-->>Dispatcher: Complete
Dispatcher-->>Middleware: Success
Middleware-->>Webhook: 200 OK
end
Domain facades (requests, payouts, payments, payer compliance, currencies, client IDs, pay) sit on top
of this pipeline and call HttpClient with schema‑keyed requests. Webhook helpers reuse the same
fixtures already committed to the TypeScript suite.
- Configuration –
RequestClientConfig::fromArray()normalises base URL, credentials, telemetry headers, and optional SDK metadata. Credential headers mirror the TypeScript helper (x-api-key,x-client-id,x-request-origin). - Transport –
HttpClientorchestrates retries through aRetryPolicy. The default policy (StandardRetryPolicy) provides exponential backoff with jitter and retries on network failures, HTTP 408/429, and 5xx responses. Actual HTTP exchange is delegated toHttpAdapterimplementations. The bundledCurlHttpAdapteris the default adapter today. A PSR‑18 bridge can be plugged in alongside it so applications can supply any PSR‑18 client. - Error mapping – transport failures are wrapped in
TransportException(aRequestApiException), and JSON error envelopes are normalised viaRequestApiErrorBuilder::buildRequestApiError(). The builder mirrors the TypeScript logic by extracting status/code/message, retry headers, and nested error details soRequestApiException::toArray()exposes parity metadata. - Runtime validation –
Validation\SchemaRegistry+SchemaValidatorcoordinate JSON Schema (Opis) validation keyed by OpenAPIoperationId. The validator exposesparseWithSchema/parseWithRegistryhelpers (parity with the TSzodhelpers) and honoursRuntimeValidationConfigtoggles for requests/responses/errors. Override fragments (request status, payment routes, payer compliance) live undersrc/Validation/Overrides/and register during bootstrap so parity quirks are handled automatically. - Spec sync/codegen –
composer update:speccopies OpenAPI + webhook assets from the shared contracts package, whilecomposer codegenemitsgenerated/OpenApi/Operations.phpand a schema manifest consumed by the registry. The manifest stores JSON pointers into the synced OpenAPI document so PHP can keep validation data in lock-step with the TypeScript client. - Requests domain (Phase 10) –
RequestClient::requests()exposes a thin facade built onJsonRequestHelper. It reuses the generated manifest for runtime validation, a query builder helper to strip null parameters, and the request-status normaliser that mirrors the TypeScript helper semantics. - Requests v1 (Phase 11) –
RequestClient::requestsV1()keeps the legacy flows alive using the same helper set plus a legacy status normaliser that only returns the paid/pending states required by older merchants. The facade mirrors the TS v1 wrapper (create, payment routes, calldata, status, payment intent, stop recurrence). - Payments domain (Phase 13) –
RequestClient::payments()exposes the v2 search facade. It reuses the shared query builder for filtering and the OpenAPI manifest for runtime validation, matching TS pagination and response shapes. - Pay v1 domain (Phase 14) –
RequestClient::pay()mirrors the TScreatePayApihelper by returning a wrapper aroundPayV1Api. The wrapper forwards/v1/payinitiate/execute calls, injects schema metadata for runtime validation, and exposes alegacyalias so WooCommerce code paths that expect the original facade can keep calling the same methods. - Payer domain (Phase 15) –
RequestClient::payer()exposes the v2 compliance facade covering onboarding, status polling, and payment detail creation. Requests attach schema metadata for runtime validation, and theComplianceStatusFormatterhelper converts compliance responses or API error payloads into user-facing summaries so Woo admin notices can explain blocking KYC states. - Payer v1 domain (Phase 16) – Added
PayerV1Apiplus thePayerApiwrapper so$client->payer()->legacyroutes to/v1/payer/**. Legacy helpers reuse the same schema metadata + formatter utilities, ensuring Woo can fall back to historic compliance flows without losing validation or messaging. - Client IDs domain (Phase 17) –
RequestClient::clientIds()exposes the/v2/client-idslifecycle (list/create/find/update/delete). Each request carries request/response schema keys so runtime validation remains consistent with the TypeScript client, enabling Woo to provision/revoke credentials without custom HTTP glue. - Currencies domain (Phase 18) –
RequestClient::currencies()surfaces the/v2/currencieslist + conversion routes alongside a.legacyv1 facade. Requests include schema metadata for runtime validation while returning plain associative arrays so Woo can render token metadata without manual HTTP plumbing. - Shared utilities & aggregator (Phase 19) –
RequestClientnow lazily instantiates every domain facade (requests, requests v1, payouts, payments, payer, payer v1, pay, pay v1, client IDs, currencies, currencies v1) on demand so they share the sameHttpClient+ retry/logging/validation pipeline. Thesrc/index.phphelper functions (createRequestClient(),createRequestClientFromEnv()) mirror the TypeScriptsrc/index.tsexport names while PHP continues to rely on PSR-4 namespaces instead of wildcard barrels.$client->http()remains exposed for advanced integrations that need the low-level transport. - Webhook signature & header utilities (Phase 20) –
WebhookSignatureVerifiermirrors the TS helper: it accepts raw strings or PSR-7 requests, normalises headers viaWebhookHeaders, supports secret rotation arrays, optional timestamp tolerances, and surfacesRequestWebhookSignatureException(code, header name, signature, timestamp, reason). This keeps Woo’s PHP stack aligned with the TypeScript SDK’s webhook security story ahead of the dispatcher phases. - Webhook events & parser (Phase 21) – Added strongly typed webhook event value
objects (
PaymentConfirmedEvent,PaymentFailedEvent,ComplianceUpdatedEvent, etc.) plusWebhookParserthat verifies (optional) signatures, loads the shared webhook spec into the schema registry, validates payloads, and returns aParsedWebhookEventwith typed metadata + payload accessors. Payment detail and compliance events promote to status-specific subclasses (approved/pending/ rejected/failed) so Woo code can rely oninstanceofchecks without re-reading raw fields, and missing schemas now fail fast to keep parity with the TS validator. This mirrors the TypeScript event helpers and prepares the ground for the dispatcher/middleware layers that follow. - Webhook dispatcher, middleware & testing harness (Phase 22) – Added
WebhookDispatcherfor strongly typed handler registration, PSR-15WebhookMiddlewarethat verifies + parses PSR-7 requests (attaching the parsed event + dispatching to handlers), and aWebhookTestHelperthat mimics the TS testing utilities (signature generation, PSR-7 mock builders, verification bypass toggles). - Logging & telemetry (Phase 23) – Added
Logging\RedactorandLogging\PsrLoggerAdapterso HttpClient + webhook middleware can stream events through PSR-3 loggers without leaking credentials. RequestClient now accepts a PSR-3 logger directly and the middleware emits structured webhook events. - Testing harness & parity guards (Phase 24) – Introduced
Testing\FakeHttpAdapterfor PSR-18-friendly fakes/assertions, locked PHPUnit coverage to ≥80 %, and wired the OpenAPI/webhook parity scripts into the workflow so spec drift fails fast. - Tests – PHPUnit scaffolding lives under
tests/. As behaviour lands we’ll add unit tests for retry policy, transport adapters, and domain facades using the shared fixtures.
- Same default base URL (
https://api.request.network). - Same credential + telemetry header logic.
- Retry defaults align with
DEFAULT_RETRY_CONFIG(max attempts: 4, base delay: 200ms, max delay: 2000ms, jitter 20%). - Architecture keeps a low-level HTTP pipeline and exposes domain-specific
facades on top-matching the TS
createRequestClientstructure.
- PSR-18 adapter bridge + curl implementation (curl stub exists. PSR-18 bridge planned).
- OpenAPI-driven DTO generation (
generated/directory) and domain facades for requests, payouts, payments, payer compliance, and currencies. - Publishing pipeline (composer archive, packagist automation, docs) per
docs/PUBLISHING.md.
- Operation coverage: each facade method will be associated with a canonical
OpenAPI
operationId. We will consolidate these IDs in a small registry undersrc/Validation/Operations.phpso a parity script can compare implemented operations against@marcohefti/request-network-api-contracts/specs/openapi/request-network-openapi.json. - Webhook coverage: event classes under
src/Webhooks/Eventsfollow a deterministic naming scheme based on fixture filenames (e.g.,payment-confirmed.json->PaymentConfirmedEvent). A parity script will compare classes to the fixtures in the contracts package.