sparkling/adyen-bundle
A Symfony bundle for integrating the Adyen payment platform, offering configuration and services to handle payments, notifications, and Adyen API interactions within Symfony applications.
First, the reality check. sparkling/adyen-bundle is a Symfony 2 bundle, not a Laravel package. It ships a DependencyInjection extension, a config tree, and service YAML — things Laravel's container has no hook for. composer require sparkling/adyen-bundle on Laravel 10/11 will fail at resolution (Symfony ~2.x components, PHP ^5.3|^7.0 constraints). Do not plan on installing it. Plan on reading it.
Minimal steps that are actually worth doing:
git clone https://github.com/ruudk/AdyenBundle into a scratch directory outside your project. Note the package name (sparkling/adyen-bundle) differs from the upstream repo (ruudk/AdyenBundle) — diff the Packagist dist against upstream before trusting either.Resources/config/services.* — the service IDs and injected constructor args tell you the entire public surface in one file.DependencyInjection/Configuration.php — the config keys (merchant account, API key, test/live mode, skin code, HMAC key) are a ready-made checklist for your own config/adyen.php.Adyen\Client / the payment API — usually a recurring-contract service and a notification/payment service. That's where all the domain logic lives, and it's perhaps 5–10 classes total.RECURRING payment type, recurringDetailReference). Confirm those endpoints are still enabled for your merchant account and get a sunset date. This gate can kill the whole initiative and costs one email.Treat the repo as a specification artifact. The Symfony wiring is worthless to you; the recurring-billing domain shape is the asset. Lift the shape, write the code natively against adyen/php-api-library.
The contract lifecycle, as expressed by the bundle (this is the part worth copying verbatim into your domain model):
recurring.contract = RECURRING → Adyen returns a recurringDetailReference.shopperReference (your user ID, stable) plus the recurringDetailReference — one shopper can have many, so model a payment_methods table with a is_default flag, not a column on users.selectedRecurringDetailReference + shopperReference + shopperInteraction: ContAuth, with no shopper present.Laravel wiring pattern:
// app/Contracts/AdyenGateway.php — the only thing your app code should touch
interface AdyenGateway {
public function authoriseRecurring(AuthoriseRequest $r): AuthoriseResult;
public function chargeStored(ChargeRequest $r): ChargeResult;
public function disableContract(string $shopperRef, string $detailRef): void;
}
Bind it in a AdyenServiceProvider reading config/adyen.php (typed config, env() only inside the config file). Bind a FakeAdyenGateway in testing so no test ever hits the network — the bundle can't give you this, and it's the single highest-value thing you'll add.
Webhook pattern: controller does one thing (verify HMAC over the raw body, return 200 immediately), then dispatches ProcessAdyenNotification on a queue. The job switches on eventCode and is idempotent on pspReference (unique index on a adyen_notifications table — insert-first, bail on duplicate). Adyen retries aggressively; without this you will double-fulfil orders.
Charge ledger pattern: every merchant-initiated charge gets an application-generated idempotency key (subscription_id:period_start) written before the HTTP call. The legacy recurring API plus naive retries is a well-documented path to charging a customer twice.
Interop pattern if a legacy Symfony app is still live: don't port. Dual-write recurringDetailReference into both systems during transition, route charges by a per-account feature flag, and reconcile contract state nightly until drift is zero. Then retire the old path — a strangler, not a rewrite.
sparkling/adyen-bundle vs ruudk/AdyenBundle, zero dependents, archived. If you ever do vendor this code, diff the dist against upstream and pin the commit hash in repositories.parameters.yml puts credentials uncomfortably close to VCS. When you reimplement, keep merchant account, API key, and HMAC key in .env → secrets manager, and never let the HMAC key into a log or an exception payload.$request->getContent() in the controller, not $request->all().pspReference, resultCode, and amounts — never the full card payload or the API key header.adyen/php-api-library + a thin Laravel wrapper of your own, with the lifecycle, ledger, and webhook patterns above. Use this repo for two days of reading, then add nothing to composer.json.How can I help you explore Laravel packages today?