Feature/pre 3550 - #308
Open
jhoaraupp wants to merge 22 commits into
Open
Conversation
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…same payment method Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
… pending PRE-3551 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
A merchant must be able to offer Integrated Payment and Hosted Fields side by side, which requires two PaymentMethod entities sharing factoryName=payplug. canBeCreated() now bypasses the duplicate check for that factory only; every other PayPlug-family factory (Oney, Bancontact, Amex, Apple Pay, Scalapay, Wero) keeps the one-payment-method-per-factory rule. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
handleHostedFieldsToken() used to process any request carrying a non-empty hostedfields_token, so a crafted POST could complete checkout through that path for any payment method. It now verifies the payment method's gateway config actually has hostedFields=true before delegating to the processor. Also adds coverage for alterRequestConfigurationForInlineCardCapture(). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…ected Three related frontend fixes: - the wrapping div now carries data-payment-inline-submit="true", so the generic checkout "next step" button is disabled while Hosted Fields is selected (clicking it submitted an empty hostedfields_token, bypassing tokenization); - connect() no longer calls dalenys.hostedFields(...).load() unconditionally. The cross-origin iframes were mounted into a container that is still hidden at connect time (see shop/select_payment/choice.html.twig). The controller now mirrors integrated-payment: container target, idempotent openFields()/ closeFields() and handleShow()/handleHide(), loading on selection only; - the saved-card radios now pipe handleHide/handleShow to the hosted-fields controller alongside integrated-payment, so picking a saved card hides the Hosted Fields form when both oneClick and hostedFields are enabled. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…scenario The Hosted Fields shop template calls is_payplug_test_mode_enabled(), which performs a real PayPlug account lookup; the fixture's secretKey 'test' is not a valid credential. The scenario now uses the existing "This secret Key is valid" step, whose context had to be registered in the shop suite that runs it. Also bundles three small fixes: - HostedFieldsPaymentProcessorInterface is now an alias instead of a second definition, so it resolves to the auto-registered service and keeps its @monolog.logger.payplug binding; - fixes the "Paiement Integré" -> "Paiement Intégré" typo in validators.fr.yml; - refreshes the two stale constraint-count docblocks in PaymentMethodValidatorTest. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
A Hosted Fields payment carries a Dalenys hfToken and no PayPlug payment_id until PRE-3551 lands, so routing it to sylius_shop_order_pay made StatusAction markNew(), Payum rebuild the details through Convert and CaptureAction issue a real createPayment() API call - which the temporary stub must never cause, even indirectly. The redirect override cannot simply be dropped: Sylius's CheckoutRedirectListener listens to the same sylius.order.post_payment event and bails out only when _sylius['redirect'] is set. Without it, it resolves a route for the `completed` checkout state, which has no entry in sylius_shop.checkout_resolver.route_map, and the request dies with a RouteNotFoundException. Hosted Fields is therefore redirected to sylius_shop_order_show instead (same token-based, guest-accessible route, no Payum involved). Integrated Payment keeps sylius_shop_order_pay. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The redirect ternary picked sylius_shop_order_pay whenever hasToken() was true, but handle() checks hasHostedFieldsToken() first. A request carrying both token fields was therefore processed as Hosted Fields - never writing a payment_id - while still being redirected to sylius_shop_order_pay, reopening the StatusAction -> Convert -> CaptureAction::createPayment() chain this redirect exists to prevent. The ternary now checks hasHostedFieldsToken() first, mirroring handle(). Tests pin the invariant on both sides so the two cannot drift apart again. Also uses self::UPDATE_ORDER_PAYMENT_ROUTE instead of repeating its literal value. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…t renders The old hostedFields flag branch in _payplug.html.twig included the card-iframe markup, but that flag was removed when Hosted Fields moved to its own payplug_uhf gateway factory, leaving the shop checkout with no include site for templates/shop/hosted_fields/index.html.twig at all. Add the missing #payplug_uhf twig-hook entry (following the same pattern as the other factory-keyed hooks in shop.yaml) with a dedicated _payplug_uhf.html.twig partial, and drop the now-dead hostedFields branch from _payplug.html.twig, which is exclusively for the payplug (Integrated Payment) factory.
… script
Twig's HTML autoescaping does not escape a bare apostrophe, so any of the
translated/dynamic values interpolated into the single-quoted JS string
literals in the hosted_fields inline <script> block (companyId, payment
method code, translated error messages) would silently break the script and
prevent the fields from mounting if the value ever contained one. Apply the
|e('js') filter to those four values.
The exemption let a merchant create two PaymentMethod entities on the payplug factory, to support the old flag-based Integrated Payment / Hosted Fields split. That design is gone: Hosted Fields is now its own payplug_uhf factory, so the exemption just permanently relaxes the one-payment-method-per-factory rule on the main card-payment gateway, allowing e.g. two integratedPayment=true payplug methods and the duplicate-ID breakage that would cause in templates/shop/integrated/index.html.twig. Remove the exemption and its test.
…cenario The scenario's only assertion was the generic "I should be able to select" step, which just checks the radio input exists and would still pass even with the Hosted Fields include site missing entirely (see the payplug_uhf twig-hook fix). Add a reusable "I should see the :selector element on the page" step to CheckoutContext (extending RawMinkContext for Mink session access) and use it, after selecting the payment method, to assert #card-container from hosted_fields/index.html.twig is present.
There was a problem hiding this comment.
Claude Code Review
Claude Code Review is paused for this repository. To reconnect it, an admin of this repository's GitHub organization (or the account owner, for personal repositories) who can also manage your Claude organization's Code Review settings needs to re-link GitHub in Code Review settings. This is a one-time step.
Tip: disable this comment in your organization's Code Review settings.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Adds Unified Hosted Fields (UHF) card tokenization at Sylius checkout: a new, distinct payment method (built on PRE-3553's
payplug_uhfgateway factory) coexisting with the existing "Integrated Payment" method. The customer enters card details into hosted iframe fields provided by PayPlug/Dalenys' Hosted Fields SDK; the plugin tokenizes client-side (createToken()), then relays the resultinghfToken+selectedBrand(+ save-card opt-in) toward payment processing through a small interface with a temporary no-op stub — swappable later without touching the frontend or the subscriber, once PRE-3551 lands the real UPC-side processing.Shop side: hosted fields (brand, card, expiry, cvv) rendered as their own checkout choice, styled to match Integrated Payment, with client-side tokenization error handling and an unsupported-card-brand fallback message. Admin side:
oneClick(save-card) support added toUhfGatewayConfigurationType, with the same account-permission checkpayplugitself uses whenoneClickis enabled.Note for reviewers: this branch started as a flag on the
paypluggateway, then was migrated ontopayplug_uhfmid-way once it became clear PRE-3550 was meant to build on top of PRE-3553 rather than invent a parallel config — see the later commits for that migration and its own review pass. The Hosted Fields SDK is currently loaded from a staging Dalenys CDN URL (no production URL identified yet) — do not enable this payment method on a production channel until that's confirmed.Motivation:
PRE-3550 — deliver card tokenization at Sylius checkout via PayPlug's Hosted Fields SDK, decoupled from PRE-3551's real backend processing so it can ship and be tested independently.
Related issue(s): PRE-3550 (continuation of PRE-3553)
Type of Change
Checklist
Code Quality
Testing
sonarcloudCI jobSecurity & Ops