nordkit/svea
Modern PHP SDK for Svea Checkout, Payment Admin, webhook subscriptions and inbound webhook verification. Fluent API with typed value objects, retries, idempotency, async task polling, and a robust testing fake. Includes first-class Laravel integration.
src/Laravel/Commands/ — six Artisan commands for managing Svea webhook subscriptions, ported from the original freightseeker-api-v2 codebase:
svea:subscription:add — register a new subscription with optional --url, --events, and --no-verify flagssvea:subscription:list — list all registered subscriptions in a tablesvea:subscription:get {id} — show details of a single subscriptionsvea:subscription:verify {id} — send a verification Ping to a subscription's callback URLsvea:subscription:update {id} — update URL and/or events with optional --verify flagsvea:subscription:remove {id} — remove a subscription with --force to skip confirmationSveaServiceProvider — commands are now registered automatically when running in the consoletests/Unit/Laravel/Commands/SveaSubscriptionCommandsTest.php — 11 Pest tests covering all six commandsdocs/guide/migration-from-official-sdk.md — comprehensive migration guide from sveaekonomi/checkout with side-by-side code examples for checkout, admin, credit, cancel, and webhook flows (community contribution by @looooown2006)docs/.vitepress/config.ts — new "Migration" sidebar section linking to the migration guidedocs/api/checkout.md — "Currencies & locales" section with PHP examples for all four Nordic markets (community contribution by @looooown2006)README.md — "Common Nordic checkout defaults" table (SEK/NOK/DKK/EUR with locales and country codes) (community contribution by @looooown2006)docs/guide/quick-start.md — cross-link to nordkit/svea-example-laravel demo projectdocs/guide/fluent-builders.md — corrected minor-unit documentation: OrderRow and AdminOrderRow both use raw minor-unit values with no SDK-level ×100 conversion (community contribution by @looooown2006)docs/guide/fluent-builders.md — fixed broken internal links (./testing → ./testing.md, ../api/checkout → ../api/checkout.md, ../api/admin → ../api/admin.md)docs/ — VitePress documentation site scaffold for https://nordkit.github.io/svea/:
docs/.vitepress/config.ts — site config (nav, sidebar, sitemap, OG meta, local search, edit-on-GitHub, last-updated, base path /svea/)docs/index.md — home page with branded hero and feature grid (full API coverage, fluent & strict, first-class testing, production transport, framework-agnostic core, end-to-end docs); added new "Two ways to build any request" feature tile highlighting the dual input styledocs/guide/ — 12 narrative guides: getting-started, installation, quick-start, configuration, authentication, laravel, standalone, testing, error-handling, retries-idempotency, middleware, fluent-builders (new "Fluent Builders & Conditionable" page covering both input styles, the full method-to-builder mapping table, a worked Checkout example with loops + conditions, and the complete when() / unless() reference; wired into the sidebar under a new Core Concepts section)docs/api/ — 5 reference pages: checkout, admin, subscriptions, webhooks, response-objectsdocs/api/checkout.md — added "Two ways to build a request" section with full named-constructor and fluent-callback examples side by side, plus a dedicated "Conditional builders — when() / unless()" subsectiondocs/api/admin.md — expanded "Modify order rows" with explicit fluent-callback examples for addOrderRow(), updateOrderRow(), and replaceOrderRows(); added a new "Conditional builders — when() / unless()" subsection covering both AdminOrderRequest and AdminOrderRowdocs/guide/quick-start.md — cross-link to the new fluent builders guidedocs/public/logo.svg — placeholder hero/favicon logo (Nordic blue gradient)docs/package.json — VitePress 1.6, dev/build/preview scripts; docs/.gitignore for node_modules/ and build outputdocs/README.md — local development, structure, deployment, and custom-domain instructions.github/workflows/docs.yml — GitHub Actions workflow that builds and deploys the docs to GitHub Pages on every push to main touching docs/** (Node 20, npm cache, artifact upload + Pages deploy).gitattributes — docs/ export-ignore so the docs source stays in the GitHub repo but ships out of the Composer dist tarballcomposer.json suggest updated; orchestra/testbench constraint widened to ^10.0 || ^11.0 to cover testing on both Laravel 12 and 13composer.json), Laravel 11/12/13, and license badgescomposer.json — added keywords array (svea, svea-checkout, svea-payments, payments, payment-gateway, checkout, ecommerce, laravel, laravel-package, webhooks, sdk, php-sdk) for Packagist discoverability.github/FUNDING.yml — enables the GitHub Sponsors button on the repo (nordkit org).github/ISSUE_TEMPLATE/bug_report.yml — structured bug report form (version, PHP/Laravel, API surface, reproduction, expected/actual).github/ISSUE_TEMPLATE/feature_request.yml — structured feature request form (problem, proposed API, alternatives, API surface, Svea docs link).github/ISSUE_TEMPLATE/config.yml — disables blank issues; routes questions to Discussions, security to private advisories, and Svea-API questions to the official docs.github/PULL_REQUEST_TEMPLATE.md — PR checklist (Pint, Pest, PHPStan, CHANGELOG, README) with type/API-surface tagging8.2, 8.3, 8.4, 8.5; docs/index.md feature tile updated; .github/workflows/tests.yml matrix simplified (8.5 added inline alongside 8.3/8.4, dropping the include: block, experimental: true flag, and continue-on-error guard — failures on 8.5 now block the build like any other version)NAMING.md — file structure was significantly stale: src/Laravel/Facades/Svea.php corrected to src/Laravel/Svea.php (no Facades/ subdirectory); SveaServiceProvider comment no longer references removed Wiretap integration; added missing DeliverResponse.php, CheckoutResponse.php, all Checkout/ support classes, Contracts/ directory, full Subscriptions/ and Webhooks/ listings, and Events/SveaWebhookReceived.php; SubscriptionService comment corrected from register(), list(), get(), delete() to add(), list(), get(), update(), remove(), verify()CONTRIBUTING.md — directory layout diagram updated to show correct src/Laravel/ structure (Svea.php, WebhookService.php, Events/) and added Contracts/ to module list; composer.json skeleton now includes phpstan/phpstan: ^2.1 in require-dev; Roadmap Phase 11 no longer claims tests/Integration/ is scaffolded (directory was removed)README.md — Package Structure section: fixed root path from packages/svea/ (old monorepo path) to src/; removed stale src/SveaServiceProvider.php entry (file lives at src/Laravel/SveaServiceProvider.php); condensed the tree to a high-level overview — full annotated structure lives in CONTRIBUTING.mdphpstan/phpstan ^2.1 added to require-dev; phpstan.neon configured to analyse src/ (excluding src/Laravel/); zero errors at level 6tests.yml CI workflow (runs on PHP 8.2, --memory-limit=512M); automatically included in the Release workflow via workflow_calltests.yml; PHP 8.5 added as an experimental (non-blocking) matrix entrytests/Unit/ reorganised into subdirectories mirroring src/: Admin/, Checkout/, Webhooks/, Laravel/, Testing/, Transport/, Core/ — Subscriptions/ was already structured this wayEventType reference table (all 10 cases with descriptions and Svea API strings), registration workflow with verification step, re-verification note after URL changepending() method documented, production guidance (queued job)phpunit.xml and tests/Pest.php — Integration testsuite pointed at non-existent tests/Integration/; removed from both config files; vendor/bin/pest --compact now exits cleanlyphpunit.xml — Feature testsuite pointed at non-existent tests/Feature/; replaced with Integration testsuite pointing at tests/Integration/SveaResource — added [@implements](https://github.com/implements) ArrayAccess<string, mixed> to satisfy PHPStan generics checkSveaResource::make(), AdminOrderRow::make(), CheckoutOrder::make(), OrderRow::make() — added [@phpstan-ignore](https://github.com/phpstan-ignore) new.static (return type is static, required for the factory-on-subclass pattern)FakeAdminOrderRequest::$idempotencyKey — added [@phpstan-ignore](https://github.com/phpstan-ignore) property.onlyWritten with explanatory comment (key is intentionally stored but not forwarded — no real HTTP in the fake)SveaResource::withLastResponse() — removed stale [@internal](https://github.com/internal) annotation; method is public by design (called by service classes) and must not carry [@internal](https://github.com/internal)LICENSE.md — added copyright year: Copyright (c) Nordkit → Copyright (c) 2024–2026 Nordkit.gitignore — added *.log and .vscode/ entries.gitattributes — added svea-bug-report.md export-ignore safeguard entryCONTRIBUTING.md — Development Roadmap row 11: updated test count (144 → 188); marked Integration directory as ✅ (scaffolded)RELEASING.md — added Section 0 pre-release checklist (pest, pint, phpstan) before the changelog step; updated CI table to reflect PHPStan jobsvea-bug-report.md — deleted scratch file that matched the *-bug-report.md exclusion patterntests/Integration/ — empty directory removed; integration-style tests are covered by the MockHandler approach documented in the READMEFinalized is not a subscription event; it is delivered via the per-order merchant push (pushUri) as {"type": "Finalized"}; recipients must call Svea::admin()->order($orderId)->get() to read the Payment Admin status; corrected earlier note that incorrectly referenced Svea::checkout()->get() and omitted the Finalized push type namecheckout.create() alongside the named-constructor form in the API Reference sectionupdate() now shows a full fluent callback example with its CheckoutResponse return value; cancel() notes it returns voidmerchantData(), partnerKey(), recurring(), requireElectronicIdAuthentication(), and metadata()deliver() was documented as returning TaskResponse (with $task->reference); corrected to DeliverResponse with ->deliveryId() and ->taskReference() in API Reference and Advanced Usage sectionsAdminOrderResponse helpers: deliveries(), delivery(), deliveryRowIds(), hasAction(), hasStatus()addOrderRow(), updateOrderRow(), and replaceOrderRows() with full fluent callback examples (these were entirely absent)AdminOrderRequest.php, AdminOrderResponse.php; added missing DeliverResponse.php entryAdminOrderRequest::cancel() — was sending PUT with {"CancelledAmount": -1}; corrected to PATCH with {"IsCancelled": true} per Svea Admin API specAdminOrderRequest::cancelAmount() — was sending PUT; corrected to PATCH (payload unchanged)AdminOrderRequest::cancelRow() — was sending PUT to /rows/{rowId} with {"IsCancelled": true}; corrected to PATCH /rows/cancelOrderRows/ with {"OrderRowIds": [rowId]} per Svea Admin API specAdminOrderRequestTest — added three new tests asserting correct HTTP method, endpoint, and payload for cancel(), cancelAmount(), and cancelRow()tests/)SignatureVerifierTest — replaced meaningless expect(true)->toBeTrue() assertion with expect(fn() => ...)->not->toThrow(); added two new test cases: empty signature and uppercase (case-sensitive) signature mismatchCheckoutOrderTest — expanded from 1 test to 6: new coverage for all merchant URI setters (confirmationUri, termsUri, checkoutUri), multi-row accumulation, OrderRow minor-unit conversion for quantity and vatPercent, discountPercent conversion, and clientOrderNumber/countryCodeAdminOrderRequestTest — added [@param](https://github.com/param)/[@return](https://github.com/return) docblock to adminConnector() helper; blank line added after declareCreditRequestTest — added [@param](https://github.com/param)/[@return](https://github.com/return) docblock to creditConnector() helperSubscriptionTest — added [@return](https://github.com/return) array<string, mixed> docblock to subscriptionFixture() helper: void return typesCheckoutServiceTest — 5 new tests covering the real CheckoutService with Guzzle MockHandler: create() POST payload and response mapping, getLastResponse() attachment, get() GET to correct path, update() POST payload, cancel() POST to cancel endpointSveaConnectorExceptionMappingTest — dataset-driven tests for HTTP status → exception class mapping (401 → SveaAuthenticationException, 404 → SveaNotFoundException, 429 → SveaRateLimitException, 400/500/503 → SveaApiException); asserts $e->statusCode and $e->getLastResponse() on SveaApiException; dataset of successful statuses (200/201/202/204) that must not throwSveaOrderStatusTest — dataset-driven tests for all 4 SveaOrderStatus enum cases: from() resolution, value string match, tryFrom() null for unknown, count assertionsrc/Laravel/)SveaServiceProvider — removed all Wiretap wiring; the provider no longer references nordkit/wiretap in any way. HTTP tracing is now the responsibility of the consuming application — override the SveaClient singleton and inject a HandlerStack in your own service provider (see README for a Wiretap example)WebhookService (Laravel bridge) — expanded class-level PHPDoc documenting why this bridge layer exists (PSR-7 vs Illuminate\Http\Request), the container binding, and a full controller usage example; fromRequest() [@param](https://github.com/param)/[@throws](https://github.com/throws) annotations addedSvea (Facade) — expanded class-level PHPDoc with usage examples for all four API surfaces and the fake() testing workflow; added static assertion proxy methods (assertCheckoutCreated, assertDelivered, assertCredited, assertCancelledOrder, assertTaskPolled, assertSubscriptionRegistered, assertSubscriptionAdded, assertSubscriptionFetched, assertSubscriptionsListed, assertSubscriptionUpdated, assertSubscriptionRemoved, assertSubscriptionVerified, assertNothingSent) that delegate to the active SveaFakeAssertions — enabling the Svea::assertDelivered(...) pattern without storing the return value of fake(); private resolveAssertions() helper throws RuntimeException when called outside fake() contextSveaWebhookReceived — expanded class-level PHPDoc with dispatch() usage, listener registration example, and handle() pattern with event type matchingsrc/Testing/)SveaFakeAssertions — expanded class-level PHPDoc documenting generic vs domain-specific assertions and the facade shortcut pattern; added three new public assertion methods: assertCalled(string $method), assertNotCalled(string $method), assertCalledTimes(string $method, int $times); improved assertNothingSent() docblockFakeSveaClient — expanded class-level PHPDoc with standalone and facade usage examples, full list of supported fake keys, and links to related classes; webhook() method documented to explain why the real WebhookService is returned (pure HMAC — no I/O to fake)FakeAdminService — expanded class-level PHPDoc listing all operation call keys and cross-linking to FakeAdminOrderRequestFakeAdminOrderRequest — expanded class-level PHPDoc with full list of supported call keysFakeAdminDeliveryRequest — expanded class-level PHPDoc documenting credit() and creditAmount() pathsFakeCreditRequest — expanded class-level PHPDoc noting state-accumulation behaviour matching the real CreditRequestLaravelServiceProviderTest — 11 new tests covering: container singleton binding, WebhookService binding, svea alias, Svea::fake() return type and singleton swap, seeded response passthrough, and all static assertion proxy methods including the RuntimeException guardFakeSveaClientTest — expanded from 8 to 30 tests: assertCalled, assertNotCalled, assertCalledTimes (including failure case), all subscription assertion methods, assertCredited, assertTaskPolled, and webhook() instance checkAdminDeliveryRequest — creditAmount() now uses PACH method instead of POSTSveaConnector - Added patch method to support AdminDeliveryRequest::creditAmount()SveaClient — expanded class-level PHPDoc documenting all supported config keys, usage examples, and property-vs-method access distinction; added [@return](https://github.com/return)/[@throws](https://github.com/throws) annotations to all public methodsSveaResource — expanded class-level PHPDoc; all public methods now carry full [@param](https://github.com/param)/[@return](https://github.com/return)/[@throws](https://github.com/throws) annotations; offsetSet() and offsetUnset() now throw \BadMethodCallException (response objects are read-only)Svea\Contracts\CheckoutServiceInterface — contract for CheckoutService and FakeCheckoutService; declares create(), get(), update(), cancel() with full PHPDocSvea\Contracts\SubscriptionServiceInterface — contract for SubscriptionService and FakeSubscriptionService; declares on(), list(), get(), add(), update(), remove(), verify() with full PHPDocAdminServiceInterface — improved class-level and method PHPDoc; order() return type documented as mixed with rationale for the builder-compatibility patternCheckoutService — now implements CheckoutServiceInterface; added method-level PHPDocSubscriptionService — now implements SubscriptionServiceInterfaceFakeCheckoutService — now implements CheckoutServiceInterface instead of extending CheckoutService; removes the // Skip parent::__construct hack; added full method PHPDocFakeSubscriptionService — now implements SubscriptionServiceInterfaceSveaException — documents the complete exception hierarchy in its docblockSveaApiException — documents $statusCode and $sveaError properties; [@param](https://github.com/param)/[@return](https://github.com/return) on constructor and getLastResponse()SveaInvalidRequestException — documents $errors property and constructor [@param](https://github.com/param)SveaAuthenticationException, SveaNotFoundException, SveaRateLimitException, SveaConnectionException, SignatureVerificationException — expanded docblocks with context and handling guidanceSveaConnector — expanded class-level PHPDoc documenting auth format, redirect behaviour, and config keys; full [@param](https://github.com/param)/[@throws](https://github.com/throws) on all public and private methodsSveaResponse — expanded class-level and property PHPDoc; documented the 302/303-as-success behaviour; [@param](https://github.com/param) on constructor and [@return](https://github.com/return)-equivalent note on successful()RetryMiddleware — expanded class-level PHPDoc with retry conditions, delay formula, and usage example; [@param](https://github.com/param)/[@return](https://github.com/return) on all methodsCheckoutOrder — expanded class-level PHPDoc with full usage example and Conditionable note; [@param](https://github.com/param) on all setter methods and [@return](https://github.com/return) on toArray()CheckoutResponse — expanded class-level PHPDoc listing all named getters; each getter documents what it returns and common valuesOrderRow — expanded class-level PHPDoc with unit conversion table and usage example; [@param](https://github.com/param) on all setter methods; internal × 100 conversions for quantity, vatPercent, and discountPercent documentedCheckoutService — [@param](https://github.com/param) on all methods (added in Folder 2, noted here for completeness)AdminService — expanded class-level PHPDoc with usage example; [@param](https://github.com/param) on both public methodsAdminOrderRequest — expanded class-level PHPDoc listing all operations and Conditionable example; [@param](https://github.com/param)/[@throws](https://github.com/throws) on all public methods; constructor [@param](https://github.com/param) addedAdminOrderResponse — expanded class-level PHPDoc with guard-gate example; full [@param](https://github.com/param)/[@return](https://github.com/return) on all methodsAdminDeliveryRequest — expanded class-level PHPDoc with both credit strategies and examples; constructor and method [@param](https://github.com/param)/[@throws](https://github.com/throws) addedCreditRequest — expanded class-level PHPDoc with all three crediting strategies and examples; [@param](https://github.com/param)/[@throws](https://github.com/throws) on all methods; constructor [@param](https://github.com/param) addedAdminOrderRow — expanded class-level PHPDoc noting no SDK-level unit conversion (unlike Checkout OrderRow); [@param](https://github.com/param) on all settersTaskResponse — expanded class-level PHPDoc with polling pattern example; all methods annotated; pending() constructor documentedDeliverResponse — expanded class-level PHPDoc; both methods annotatedSveaOrderStatus — added class-level PHPDoc with doc link; inline case descriptions addedSubscriptionService — constructor [@param](https://github.com/param) added; all methods were already fully annotatedSubscriptionBuilder — constructor [@param](https://github.com/param) added; all methods were already fully annotatedSubscription, EventType — already had comprehensive docblocks; no changes neededSignatureVerifier — removed stale TODO comment; expanded class-level PHPDoc documenting HMAC-SHA256 algorithm, timing-safe comparison, and direct usage example; [@param](https://github.com/param)/[@throws](https://github.com/throws) added to verify()Webhook — expanded class-level PHPDoc with full Laravel controller usage example and match dispatch pattern; [@param](https://github.com/param)/[@throws](https://github.com/throws) added to constructEvent()WebhookEvent — expanded class-level PHPDoc with Svea payload shape; all methods annotated including type() null-return rationale for unknown future event typesWebhookService — expanded class-level PHPDoc with DI binding and controller usage examples; constructor [@param](https://github.com/param) and fromRequest() [@param](https://github.com/param)/[@throws](https://github.com/throws) addedConditionable — expanded class-level PHPDoc documenting zero-dependency rationale, all classes that consume the trait, and a usage example; [@param](https://github.com/param) descriptions improved for both when() and unless()SubscriptionService::add(), get(), update(), remove(), verify() — full CRUD for webhook subscriptions, cross-validated against Svea Payments API docsFakeSubscriptionService — complete fake mirroring the full SubscriptionService API; added get(), add(), update(), remove(), verify(); fixed delete() → remove() and EventTypes → Events keySveaFakeAssertions — added assertSubscriptionAdded(), assertSubscriptionFetched(), assertSubscriptionsListed(), assertSubscriptionUpdated(), assertSubscriptionRemoved(), assertSubscriptionVerified()SubscriptionBuilderTest — 4 tests covering fluent chain payload, accessor correctness, getLastResponse(), and on() replacement behaviourSubscriptionServiceTest — 11 tests covering all service methods, HTTP verb/path assertions, error handling (401)SubscriptionTest — 15 tests covering all Subscription resource accessors and SveaResource base behaviourSubscriptionService::list() was treating SveaResponse as a plain array; corrected to use $response->jsonEventTypeTest — rewrote with correct enum cases (CheckoutOrderCreated, etc.); previous test referenced non-existent PaymentDelivered/CheckoutCompleted casesAdminOrderRequestTest, AdminDeliveryRequestTest, CreditRequestTest — were passing a GuzzleHttp\Client as the third SveaConnector constructor argument instead of ?HandlerStackFakeSveaClientTest, WebhookTest — were referencing non-existent EventType::PaymentDelivered; updated to EventType::CheckoutOrderCreatedAdminOrderRequestTest — was calling $task->reference() on DeliverResponse which exposes taskReference(), not reference()Subscriptions/ source classes enriched with comprehensive PHPDoc: API endpoint refs, [@param](https://github.com/param)/[@return](https://github.com/return)/[@throws](https://github.com/throws), usage examples (SubscriptionService, SubscriptionBuilder, Subscription, EventType)EventType enum — confirmed all 10 cases against Svea API docs; enriched class-level PHPDoc with doc linkassertSubscriptionRegistered, added all new assertions), fixed stale EventType::PaymentDelivered references in webhook example, added isVerified() to subscription accessor docs, closed resolved open questions for base URLs and EventType listSveaClient — main entry point; lazily resolves CheckoutService, AdminService, SubscriptionService, and WebhookServiceSveaConnector — central HTTP transport with HMAC-SHA512 auth, idempotency key support, configurable retry middleware, and typed exception mapping (SveaAuthenticationException, SveaNotFoundException, SveaRateLimitException, SveaApiException, SveaConnectionException)SveaResource — base class for all API response objects; magic property access, ArrayAccess, and withLastResponse() for raw HTTP debuggingSveaResponse — wraps PSR-7 responses; exposes ->json, ->headers, ->statusCode, and ->successful()CheckoutService — create, get, and update Svea Checkout ordersCheckoutOrder — fluent builder for checkout order payloads; currency(), locale(), countryCode(), clientOrderNumber(), pushUri(), confirmationUri(), termsUri(), checkoutUri(), addRow()OrderRow — fluent builder for checkout order row itemsAdminService — order(string $orderId) returns AdminOrderRequest; task(string $taskUrl) polls async tasksAdminOrderRequest — fluent builder for all order operations: get(), deliver(), cancel(), cancelAmount(), cancelRow(), delivery(), addOrderRow(), updateOrderRow(), replaceOrderRows(); supports withIdempotencyKey()AdminOrderResponse — typed wrapper for order details; status(), actions(), canDeliver(), canCredit(), canCancel(), deliveries(), delivery(), deliveryRowIds(), hasAction(), hasStatus()AdminDeliveryRequest — delivery-scoped operations: credit() (returns CreditRequest), creditAmount(int $amount)CreditRequest — fluent refund builder: rows(array $rowIds, ?array $rowCreditingOptions), newRow(callable), send()AdminOrderRow — fluent builder for order row payloads: name(), quantity(), unitPrice(), vatPercent(), sku(), discountPercent(), unit(), rowType(), temporaryReference(), merchantData()TaskResponse — async task resource; reference(), resource(), completed(), failed(), pending(string $reference) named constructorSveaOrderStatus enum — Open, Delivered, Cancelled, FinalSubscriptionService — register, list, get, and delete webhook subscriptionsWebhookService — inbound webhook signature verification via HMAC-SHA256SveaServiceProvider — Laravel auto-discovered service provider; binds SveaClient as a singleton; publishes config/svea.phpSvea facade — Svea::checkout(), Svea::admin(), Svea::subscriptions(), Svea::webhook()FakeSveaClient — test double with fake() named constructor; fakeCheckout(), fakeAdmin(), fakeSubscriptions()FakeCheckoutService, FakeAdminService, FakeSubscriptionService — record calls, seed responses, and assert interactionsFakeAdminOrderRequest, FakeAdminDeliveryRequest, FakeCreditRequest — mirror the real API for unit testsSveaFakeAssertions — shared assertion helpers: assertCalled(), assertNotCalled(), assertCalledTimes(), preventStrayRequests()Conditionable trait — when() / unless() for inline conditional builder chainsRetryMiddleware — configurable exponential-backoff retry on 429 and 5xx responsesHow can I help you explore Laravel packages today?