Feature/pre 3628 gateway uniq per channel - #321
Merged
adumont-payplug merged 5 commits intoSep 14, 2026
Merged
adumont-payplug merged 5 commits into
adumont-payplug merged 5 commits into
Conversation
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.
adumont-payplug
changed the base branch from
develop
to
feature/PRE-3440_multi_shop_configuration
September 14, 2026 07:40
- de-dup CB base-currency form errors, not just flashes - flash() no longer throws with no request/session - drop 8 dead gatewayFactoryName property declarations - suppress PHPMD unused-param on shouldValidateBaseCurrency() - assert PaymentMethodTypeExtension::getExtendedTypes() - tighten PaymentMethodRepository docblocks to list<>
adumont-payplug
force-pushed
the
feature/PRE-3628_gateway_uniq_per_channel
branch
from
September 14, 2026 08:09
b6e5334 to
293e773
Compare
jhoaraupp
approved these changes
Sep 14, 2026
adumont-payplug
merged commit Sep 14, 2026
9c7458f
into
feature/PRE-3440_multi_shop_configuration
6 checks passed
adumont-payplug
deleted the
feature/PRE-3628_gateway_uniq_per_channel
branch
September 14, 2026 10:00
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
Scopes the gateway uniqueness rule per channel instead of per instance, so a merchant running several shops on one Sylius instance can connect a distinct PayPlug account per shop.
Before:
AbstractGatewayConfigurationType::canBeCreated()did a globalfindOneBy(['factoryName' => …])and refused any second gateway config of the same factory anywhere on the instance. It was also creation-only —checkCreationRequirements()returned early whenever thePaymentMethodalready had an id, so edits were never validated.After: a channel may be linked to at most one enabled gateway config per factory type. Two gateways of the same factory ("CB 1" and "CB 2") may coexist and both be enabled as long as their channel sets are disjoint. Different factory types never conflict. The rule runs on creation and edit, and the error names the conflicting channel and the payment method already holding it instead of "only one gateway allowed".
Motivation: first story of the Multiboutique epic — a multi-enseigne retail merchant currently cannot give each shop its own PayPlug account.
Related issue(s): PRE-3628 — epic PRE-3440
Why the validation moved forms
Both validations lived in
PRE_SUBMITon the nestedpaymentMethod.gatewayConfig.configform. Sylius addschannelsto the payment-method form fromCoreBundle's own type extension — i.e. aftergatewayConfig— and form children are submitted in insertion order. So that listener runs beforeenabledandchannelshave been submitted and can only ever see persisted data.The per-channel rule needs the submitted channel set and the submitted
enabledflag, so it cannot live there.POST_SUBMITon the rootPaymentMethodform is the first point where all three exist.Two pre-existing defects fixed in passing
The EUR-only base-currency check sat in that same listener and was broken by the same root cause:
$formChannels->getData()— the persisted channels. On creation the entity has none, so it validated nothing at all; on edit it validated the stale set rather than the one being submitted.Collection(offsets0,1, …) and then called$formChannels->get((string) $key).ChannelChoiceTypesetschoice_value => 'code', so the expanded choice's children are named by channel code.Form::get()throwsOutOfBoundsExceptionon an unknown child — so a genuine EUR violation produced a 500, not a form error.Both are fixed by the relocation; errors now attach to the
channelsfield itself.How it's built
src/Checker/GatewayChannelConflictChecker.php— the rule, form-free and DB-free, so it unit-tests without a form tree. Channels are matched by code, not object identity.src/Gateway/Form/Extension/PaymentMethodTypeExtension.php—POST_SUBMITon the root form, running the conflict check and the base-currency check.PaymentMethodRepository::findEnabledByGatewayName()— enabled methods of one factory, withchannelsfetch-joined to avoid N+1.canBeCreated()/checkCreationRequirements()and the wholePRE_SUBMITlistener are gone; the base type's constructor loses two now-unused dependencies, and two PHPStan baseline entries are deleted rather than carried."Is this a PayPlug gateway?" is decided by
instanceof AbstractGatewayConfigurationTypeon theconfigchild's inner form type, not by a hardcoded factory-name list — exact by construction across all seven gateways, and one less list to update when an eighth is added.Behaviour changes worth a reviewer's attention
Testing
phpunit tests/PHPUnit— 531 passing, 976 assertionsphpstan(level max) — clean;ecs— clean;phpmd— 43, one below the pre-existing 44Type of Change
Checklist
Code Quality
Testing
sonarcloudCI jobSecurity & Ops
Out of scope — follow-ups
This branch makes multi-gateway configuration possible, but several sites still assume one gateway config per factory type and will resolve an arbitrary one. None are touched here; they are why the epic sequences PRE-3630 (credential-resolution spike) after this story:
src/Controller/IpnAction.php:103$paymentMethodin hand, then callscreate($factoryName)— webhooks verified against the wrong account's secret key. Highest consequence.src/Controller/IntegratedPaymentController.php:87src/Provider/Payment/ApplePayPaymentProvider.php:52,209findOneByGatewayName()with no channel filter — a shopper on one channel can be assigned another channel's payment method. Not credential resolution, so arguably outside PRE-3630's current scope.src/Provider/OneySupportedPaymentChoiceProvider.php:42fees_foroff an arbitrary Oney gateway.src/Upc/SyliusUpcConfigurationRepository.php:84findOneBy.src/Twig/OneyExtension.php:35findOneBy.This targets the epic branch
feature/PRE-3440_multi_shop_configuration, notdevelop, so none of the above reaches merchants as a result of this merge.Also deliberately deferred: the channel-selector UX showing already-taken channels (PRE-3629); any database-level uniqueness constraint (the rule is an admin-form guard — fixtures, the API and direct SQL bypass it); Behat coverage.