Skip to content

PRE-3632: add a logout action per gateway config - #330

Merged
adumont-payplug merged 1 commit into
feature/PRE-3440_multi_shop_configurationfrom
feature/PRE-3632_logout_per_gateway_config
Sep 17, 2026
Merged

adumont-payplug merged 1 commit into
feature/PRE-3440_multi_shop_configurationfrom
feature/PRE-3632_logout_per_gateway_config

Conversation

@adumont-payplug

@adumont-payplug adumont-payplug commented Sep 17, 2026

Copy link
Copy Markdown
Collaborator

Description

Adds a Disconnect this account action to each PayPlug gateway's admin screen. It clears the
credentials stored for that one payment method and disables it, leaving every other gateway —
including others of the same factory, on other channels — authenticated and untouched.

Distinct from the existing renew_oauth checkbox, which immediately mints new credentials.
Logout ends with none.

What gets cleared. The ticket assumed the OAuth state was written through
SyliusUpcConfigurationRepository::set(). That method has zero callers, here and in UPC alike
(WebhookNotificationHelper is the only consumer of IConfigurationRepository, and it only
reads). The state is actually:

Where What
UnifiedAuthenticationController::oauthCallback() live_client, test_client, account_email on the gateway config
TokenManagerSyliusTokenCache upc_oauth_token:<client_id>, one per environment
PayPlugApiClient payplug_account_<…> — keyed by secret key, so inert once credentials are gone

All of the first two are cleared. hfIdentifier goes too, but only when
PayPlugGatewayFactory::isHostedFieldsConfig() holds — a CB gateway with Hosted Fields selected,
where it names the account being disconnected. live, oneClick, deferredCapture, the
display-mode flags and fees_for are merchant settings and are left alone.

Three decisions worth a reviewer's eye:

  • TokenManager::CACHE_KEY_PREFIX is private with no accessor, so the literal is mirrored in
    GatewayConnectionRevoker and pinned by a test. The alternative was leaving a usable access
    token at rest for the remainder of its TTL after the merchant asked to disconnect. A forget()
    on UPC would remove the duplication.
  • The action is GET. Its button renders inside the Sylius payment-method <form>, where a
    nested <form> is invalid HTML, so the CSRF token travels in the query string — the same shape
    as Sylius's own sylius_admin_shipment_resend_confirmation_email.
  • security.csrf.token_manager is injected with @?. It only exists while CSRF protection is
    enabled, and a hard reference would break container compilation for an app that turns it off;
    the check is then skipped, matching Sylius's sylius_csrf_protection_enabled() guards.

Two consequences of the auto-disable, both intended but worth confirming: PRE-3629's
per-channel uniqueness rule counts only enabled gateways, so logging out releases this gateway's
channels for another config to claim; and PaymentMethodValidator::process() only ever disables,
never re-enables, so after reconnecting the merchant must tick Enabled by hand.

Motivation: a merchant on a multi-shop setup has no way to sever one gateway's connection —
renew_oauth only ever swaps one connection for another, and there is no path to "disconnect and
stop taking payments here".

Related issue(s): PRE-3632


Type of Change

  • ✨ New feature (non-breaking change that adds functionality)

Checklist

Code Quality

  • Code is linted and formatted
  • No unnecessary commented-out code or debug logs
  • No hardcoded values (use env variables or config) — one deliberate exception, the mirrored
    UPC cache-key prefix, documented above and in the class docblock

Testing

  • Unit tests added / updated — 17 cases across GatewayConnectionRevokerTest and
    UnifiedLogoutControllerTest, including that a failed CSRF check mutates nothing
  • New/changed code is covered by tests — SonarCloud Quality Gate (coverage on new code) passes
    on the sonarcloud CI job — not yet observed; tick once CI is green

Security & Ops

  • No sensitive data or secrets introduced
  • Logging and error handling are appropriate — revocation failures are logged to the payplug
    channel and surfaced as a flash rather than a 500

@adumont-payplug
adumont-payplug merged commit c37495d into feature/PRE-3440_multi_shop_configuration Sep 17, 2026
7 checks passed
@adumont-payplug
adumont-payplug deleted the feature/PRE-3632_logout_per_gateway_config branch September 17, 2026 09:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant