1. Install and publish config
composer require <vendor>/<package>
php artisan vendor:publish --tag=<package>-config
2. Configure your PSPs
The package is built around a named set of Payment Service Providers (PSPs). Each entry in config/<package>.php holds credentials plus, as of v1.1.0, an optional endpoint key:
'psps' => [
'stripe' => [
'driver' => 'stripe',
'key' => env('STRIPE_KEY'),
'secret' => env('STRIPE_SECRET'),
'endpoint' => env('STRIPE_ENDPOINT'), // nullable -> driver default
],
'adyen' => [
'driver' => 'adyen',
'api_key' => env('ADYEN_API_KEY'),
'endpoint' => env('ADYEN_ENDPOINT'),
],
],
3. First use case: sandbox one PSP, leave the rest alone
The most common first move after this release is pointing a single PSP at a sandbox/mock host without disturbing the others. Set only that PSP's endpoint env var, leave the rest unset, and confirm requests land on the sandbox by tailing logs or hitting a local trap server (nc -l 8000, Http::fake(), or a WireMock container).
Where to look first
psps array — this is the extension point v1.1.0 touched.Manager or Factory class) — this is where endpoint precedence is decided.endpoint as a base URI or a full URL; the answer differs per PSP.Environment-scoped endpoints. Keep endpoints in env vars alongside credentials, not hardcoded in config. APP_ENV=local + STRIPE_ENDPOINT=http://localhost:8080 gives you a fully mocked payment flow with zero code changes.
Per-tenant / per-store endpoints. If you run a multi-tenant app where each tenant has its own PSP account or region, resolve config at runtime before the client is built:
config()->set("payment.psps.{$tenant->psp}.endpoint", $tenant->psp_endpoint);
$gateway = app(PaymentManager::class)->psp($tenant->psp);
Do this in a middleware or a tenant-aware service provider that runs before anything resolves the gateway.
Test overrides. In feature tests, override endpoints to a local fake rather than mocking the HTTP client, so you exercise the real driver serialization:
protected function setUp(): void
{
parent::setUp();
config()->set('payment.psps.stripe.endpoint', 'https://fake-psp.test');
Http::fake(['fake-psp.test/*' => Http::response(['id' => 'pi_123'])]);
}
Regional failover / A-B endpoints. Because endpoints are now per-PSP, you can model primary and fallback PSPs as two configured entries pointing at different hosts, then select between them in a service class rather than reaching into the driver.
Endpoint resolution as a first-class concern. If you need dynamic routing (e.g. EU vs US acquiring banks), wrap resolution in a small EndpointResolver class and feed it into the config before the manager resolves the driver. Keep the logic out of controllers.
config:cache will silently break env-based endpoints. env() returns null outside config files once cached. Always read endpoints through config(), and always php artisan config:clear after changing endpoint env vars locally. In deploy pipelines, make sure the endpoint env vars are set before config:cache runs.
The gateway may be a singleton. Most Laravel PSP packages register the manager as a singleton and memoize resolved drivers. If you flip an endpoint at runtime after the driver has been resolved, the change won't apply. Resolve the gateway after your config mutation, or use app()->forgetInstance().
Trailing slashes and double slashes. Drivers typically concatenate endpoint . '/payments'. An endpoint ending in / produces //payments, which some PSPs 404 and others (Cloudflare-fronted) redirect — turning POSTs into GETs or dropping bodies. Normalize with rtrim($endpoint, '/').
Base URI vs full URL. Some drivers expect https://api.stripe.com/v1, others expect the full resource URL. Check the driver before overriding; a "correct-looking" endpoint that omits the version path will 404 with an unhelpful error.
Overriding an endpoint does not re-register webhooks. The PSP still sends callbacks to whatever URL is registered in its dashboard. If you point at a sandbox endpoint, you also need a tunnel (ngrok/expose) and a matching webhook registration, or your confirmation flow will never complete — and the failure is silent.
Never let user or tenant input set an endpoint verbatim. This is an SSRF vector: a compromised tenant record would let an attacker point your server at internal metadata endpoints. Allowlist schemes and hosts, and store endpoints in config you control.
Signature verification is endpoint-sensitive. Some PSPs sign the payload including the request URL. If your override changes the URL, verification can fail even though the request succeeded. Log the resolved endpoint alongside the webhook event ID when debugging.
Log resolved endpoints at debug level. Add the effective endpoint to your PSP request/response log context. When a request fails in staging, "which endpoint did we actually hit?" is the first question, and the answer is invisible by default.
v1.1.0 is additive and non-breaking. Endpoint overrides are optional; unset means driver defaults. Still, pin the version ("^1.1") and run your payment integration tests after upgrading, since a driver's default resolution may have been refactored to support the override.
How can I help you explore Laravel packages today?