ruudk/payment-adyen-bundle
A Symfony bundle that integrates the Adyen payment provider into the ruudk/payment library, letting applications create payments, handle notifications, and process Adyen transactions through a consistent payment abstraction.
The bundle is a thin Symfony2 bridge between the Adyen API and JMSPaymentCoreBundle. It does not implement its own payment state machine — it registers an Adyen plugin with JMSPaymentCore, plus a controller for Adyen's server-to-server notification (webhook). Budget your onboarding around JMSPaymentCore, not around this bundle.
Prerequisites and install
composer require ruudk/payment-adyen-bundle
You also need jms/payment-core-bundle. Register both in app/AppKernel.php:
new JMS\Payment\CoreBundle\JMSPaymentCoreBundle(),
new Ruudk\PaymentAdyenBundle\RuudkPaymentAdyenBundle(),
Then configure a merchant account in app/config/config.yml (verify exact key names against DependencyInjection/Configuration.php — they vary between releases):
ruudk_payment_adyen:
merchant_account: "%adyen_merchant_account%"
skin_code: "%adyen_skin_code%"
shared_secret: "%adyen_shared_secret%"
test: true
Keep the credentials in parameters.yml so test: false in production is the only diff.
Where to look first
DependencyInjection/Configuration.php — the authoritative list of options; the README is incomplete.Resources/config/services.xml — the service ids you will override for the notification controller and the plugin.PluginInterface) — this is where the Adyen call is actually made and where you extend behaviour.First use case
Create a PaymentInstruction through JMSPaymentCore, open it with the Adyen plugin, and redirect the user to the hosted payment page Adyen returns. Do this in a controller action, not in a service, so the redirect URL is easy to hand back to the browser. Confirm it works against the Adyen test environment before touching production credentials.
Redirect flow. The canonical shape is: build PaymentInstruction → $instruction->setAmount(...) → $instruction->setCurrency(...) → open with the plugin → RedirectResponse to the returned URL. Always persist the instruction's id and stash it in the session, so the returnUrl action can reload it and reconcile.
Treat the return URL as cosmetic. The user-controlled return URL is only a UX signal. The authoritative status arrives via Adyen's notification callback. Any code that fulfils an order on the return URL alone is a fraud vector.
Notification handling. The bundle's controller verifies the HMAC signature and translates the notification into JMSPaymentCore state transitions. Two things you will almost always add on top:
pspReference and short-circuit on duplicates before touching your order.Answer with the literal body [accepted]. Adyen treats anything else as a failure and will retry. If your override of the controller throws, catch it, log it, and still return [accepted] — returning a 500 will trigger a retry storm.
Multi-merchant / multi-skin. Define more than one plugin instance in the container (one per merchant account or skin) and select at runtime. This is cleaner than mutating config per request, which fights the container and makes testing painful.
Testing. Adyen's dashboard has a "Test notification" button per notification config; use it to validate signature handling and your idempotency logic without moving money. Assert both the accept path and the reject path (bad signature, wrong merchant account) in a functional test.
This package is archived and Symfony2-only (last release 2015). It will not install cleanly on Symfony 4+ without forking. Before adopting it, ask whether a direct Adyen SDK integration is cheaper than maintaining a fork of a dead bundle plus a dead parent bundle (JMSPaymentCoreBundle). In most greenfield cases it is. If you must use it, pin the versions in composer.lock and expect PHP 7.x-era code.
HMAC signature verification is where bugs live. Adyen's signature is computed over a specific field ordering with \ and : escaped inside values. A single mis-escaped character makes every signature fail while looking superficially correct. Reproduce Adyen's own test vector against the bundle's signature code before debugging anything else.
Amounts are minor units. Adyen expects integer cents (or the currency's smallest unit, which is not always 100 — see JPY). Formatting a float with number_format somewhere upstream will silently send the wrong amount. Keep the conversion in one place.
Don't trust config caching. Symfony2 caches the compiled container. After editing the ruudk_payment_adyen block, run cache:clear; a stale cache is the most common "my key isn't being used" cause.
API drift is a real risk. Adyen has deprecated several endpoints and field names since 2015. The bundle may be calling something that still responds but behaves differently. Read the actual HTTP call in the plugin and compare it against current Adyen documentation before going live.
Extension points. Override the notification controller service in your app config rather than editing vendor code; subclass the plugin if you need pre/post hooks around the authorization call. Both are supported by the container and survive composer update far better than a patch to the bundle.
Debugging checklist when a payment silently fails: raw notification body logged? signature verification result logged? pspReference recorded? instruction id present in the session at return time? container cache cleared? Nine times out of ten it's one of those five.
How can I help you explore Laravel packages today?