jeroennoten/laravel-psp
A Laravel package that provides a unified payment service provider layer, letting you integrate and switch between payment gateways through a consistent API for creating, handling, and tracking payments in your application.
First — verify this is the package you think it is. "PSP" here is ambiguous. The GitHub metadata describes it as "Laravel Package Service Provider for autoloading dependencies of packages in development", while the description.md included in your sources describes an unrelated payment-gateway abstraction. These are two different pieces of software. Based on the repo name, stars (7), last release (2016), and the v2.0.2 note ("Also support having zero packages"), the real package is the Package Service Provider helper — a pre-Composer-path-repositories utility that auto-loads service providers from a local packages/ directory during development. If you need payment integration, this is not it.
Do you actually need it? The package predates two features that now make it redundant for almost every team:
extra.laravel.providers in the package's composer.json)"type": "path" in repositories)For a fresh project, prefer path repositories. Install this package only if you're maintaining a legacy app that already depends on it.
Minimal steps (legacy install):
composer require --dev jeroennoten/laravel-psp:^2.0.2config/app.php (this package predates auto-discovery, so don't assume it will self-register):
'providers' => [
// ...
JeroenNoten\LaravelPsp\PspServiceProvider::class,
],
php artisan vendor:publish --provider="JeroenNoten\LaravelPsp\PspServiceProvider"packages/ or workbench/).First use case: You're developing a Laravel package in the same repo as your app (e.g. packages/acme/billing/) and want its service provider wired in automatically without hand-editing config/app.php every time you add a package. That's the entire scope. Anything else — payment gateways, general-purpose container tricks — is out of scope.
Development-only registration. Never ship this provider enabled in production. Wrap it behind an environment guard so it can't break non-local boots:
if ($this->app->environment('local', 'development')) {
$this->app->register(\JeroenNoten\LaravelPsp\PspServiceProvider::class);
}
Convention-over-configuration package layout. The package expects a directory convention roughly like:
packages/
└── acme/
└── billing/
├── composer.json # name: acme/billing
└── src/
└── BillingServiceProvider.php
Keep the provider class name and namespace derivable from the folder path; the whole value of this package is that you don't edit config for each new package.
Config-driven enumeration. Where the package exposes a list, drive it from a single config key rather than scattering provider arrays through config/app.php. That way enabling/disabling a local package is one config edit, not two.
Testing pattern. Because the value is "providers load without ceremony", the sanity test is trivial — a feature test that boots the kernel in the local environment and asserts your package's provider is present:
public function test_local_package_services_are_registered(): void
{
$this->assertArrayHasKey(
'Acme\Billing\Contracts\InvoiceGateway',
app()->getLoadedProviders()
);
}
Migration pattern (recommended). If you're on a modern Laravel, replace this package outright:
{
"repositories": [
{ "type": "path", "url": "packages/*", "options": { "symlink": true } }
],
"require-dev": {
"acme/billing": "*"
}
}
Then declare extra.laravel.providers in packages/acme/billing/composer.json and let Laravel's own auto-discovery do the rest. Same outcome, zero third-party surface area, and it participates in composer dump-autoload.
Integration tips.
scripts.post-autoload-dump lifecycle only if you also understand the autoload map — otherwise you'll double-load providers.getLoadedProviders() before register()); Laravel's kernel will happily register the same provider twice.Description mismatch — the biggest gotcha. The description.md in your source bundle describes a payment-service-provider abstraction (driver architecture, unified payment API, extensible contracts). None of that exists in this repository. If an internal assessment or PR cites "driver-based payment abstraction," that content is hallucinated or belongs to a different package. Treat the GitHub description and the v2.0.2 release note as the authoritative signal.
Pin ^2.0.2, not 2.0.0 or 2.0.1. The single change in v2.0.2 is "Also support having zero packages" — an empty-collection fix. Fresh CI containers and Laravel skeletons with no local packages would crash on the earlier builds. The fix may not be uniform across every code path (config resolution, provider iteration, console output), so if the package exposes any artisan command, exercise each one on an empty packages/ directory before trusting it.
Empty-state semantics are undefined in docs. The release note tells you that it handles zero packages, not what it returns. Assert the shape explicitly (empty array vs. silent no-op) in your own tests, and if downstream tooling consumes it, distinguish "no packages found" from "discovery failed." Silent success is the subtle failure mode this release introduces.
Laravel version compatibility is the real risk. Last released 2016-06-27. It targets Laravel 5.x. On Laravel 9/10/11 you may hit:
$this->app->bindShared() or similar pre-5.3 container callsconfig/app.php registration idioms that don't exist in modern skeletonsTest in a clean app before assuming it works. Do not assume composer require success means runtime success.
Debugging checklist when providers don't load:
php artisan tinker → app()->getLoadedProviders() — is the provider even registered?composer dump-autoload -o — autoload map may be stale for newly added local packages.Extension points. The package is thin by design; don't expect a rich plugin surface. If you need custom discovery logic (e.g., scanning a Monorepo apps/*/packages layout), fork and subclass the provider, or — better — write your own 30-line provider that reads a config array and calls $this->app->register() in a loop. This is genuinely all the package does, and reimplementing it removes a 7-star, unmaintained dependency.
Maintenance verdict. Low operational risk only because the surface is tiny, not because it is actively maintained. No release since 2016, no dependents, no security-sensitive role (development-only). Budget an afternoon to replace it with Composer path repositories the next time you touch the app's bootstrap — that's the durable fix, and it's the answer you'd want your assessment to recommend.
How can I help you explore Laravel packages today?