paymentsuite/redsys-bundle
A PHP bundle for integrating the Redsys payment gateway into Symfony applications, with helpers for signed payment requests, merchant notifications, and response handling to simplify Redsys checkout flows.
Reality check before you composer require. This is a Symfony bundle from the PaymentSuite family, not a Laravel package. There is no service provider, no config file to publish, no facade. On a Laravel codebase it will not drop in — the bundle is a payment method that plugs into paymentsuite/payment-core-bundle, which expects Symfony's container, event dispatcher, and PaymentBridge/PaymentMethodInterface contracts. So for a Laravel developer, the first decision is which of three modes you are in:
app/Services/Redsys/, cut the PaymentSuite dependency, adapt to Laravel's container. Roughly 2–4 days. This is the honest "just use it" path.Where to look first, in order:
DependencyInjection/Configuration.php — the authoritative list of config keys (merchant_code, secret_key, terminal, environment, timeout, OK/KO URLs). The README is thinner than the code.Ds_MerchantParameters is built and signed. This answers the only question that matters: HMAC_SHA256 or SHA1? Redsys enforced SHA256 in 2018 and deprecated SHA1; if this bundle still emits SHA1 it is unusable in production without a fork.composer.json — the Symfony and paymentsuite/core constraints. Run composer why-not against your target PHP/Symfony version; expect old Symfony 2/3-era pins.First use case: a sandbox redirect payment in Sis-Test (sis-t.redsys.es). Configure test credentials, build a signed form for a small fixed amount (in integer cents), POST to the Redsys endpoint, let it redirect back to your callback, and confirm you validate the signature. If that works end-to-end you have validated the only thing this package is worth.
Wrap everything behind your own interface. Whatever mode you pick, define PaymentGateway with createPayment(Order): RedirectResponse and verifyNotification(Request): PaymentResult. Bind it in AppServiceProvider. This is what keeps Redsys from leaking into checkout controllers and lets you swap to Omnipay or a hosted PSP later without touching call sites.
Fork the signing logic, don't reimplement Redsys blind. The Redsys signature is fiddly: base64-decode the merchant secret, derive a per-transaction key via 3DES using Ds_Order (zero-padded), then HMAC-SHA256 the base64 Ds_MerchantParameters, base64 the result into Ds_Signature with Ds_SignatureVersion = HMAC_SHA256_V1. If the bundle already does this correctly, copy the class verbatim rather than writing it from the spec — the encoding quirks (URL-safe base64, trailing padding) bite everyone.
Flow pattern (redirect + callback):
POST /checkout/redsys → build Ds_MerchantParameters (order, amount in cents,
currency EUR, merchant code, terminal, URLs)
→ sign, persist Payment row as "pending"
→ render auto-submitting HTML form to Redsys
POST /redsys/notify → decode Ds_MerchantParameters
→ recompute signature with stored secret
→ compare order reference, amount, currency
→ idempotent state transition (pending → paid/failed)
→ respond "OK" (Redsys retries otherwise)
Idempotency first. Redsys sends notifications more than once. Key the transition on Ds_Order plus your internal payment id, and make the state machine reject a second paid transition as a no-op that still returns OK. Duplicate notifications without idempotency = double fulfilment or double refund, and it is the failure mode that actually costs money.
Config through .env, not committed files. REDSYS_MERCHANT_CODE, REDSYS_TERMINAL, REDSYS_SECRET_KEY, REDSYS_ENVIRONMENT=test|prod. Keep the endpoint and secret selection driven off one environment switch so test keys can never reach production, and refuse to boot in production if the secret looks like a Sis-Test key.
Reconciliation job for abandoned redirects. Customers close the tab. Orders stay pending forever. Schedule a nightly job that re-queries Redsys (or just ages out stale pending payments and alerts) rather than leaving the order stuck.
Logging hygiene. Log the order reference and the signature verdict, never Ds_MerchantParameters in full and never the secret. Redact at the logging boundary, not at the source, so an exception handler can't smuggle the key into a stack trace.
composer require paymentsuite/redsys-bundle, watching it pull PaymentSuite Core and a dozen symfony/* packages, then discovering there is no way to resolve the container from a Laravel app. Decide mode 1/2/3 before touching composer.19.99 becomes 1999. Decimal rounding here is customer-visible and a classic bug; format in one place (a Money value object or a single helper) and test it.OK response.How can I help you explore Laravel packages today?