Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
a382258
PRE-3563: Add SyliusOAuthHttpClient adapter
adumont-payplug Jul 17, 2026
81a7fee
Add SyliusTokenCache adapter for UPC's ITokenCache
adumont-payplug Jul 17, 2026
71818e4
PRE-3563: add OAuth2/PKCE throw UPC
adumont-payplug Jul 20, 2026
fb944d5
PRE-3563: adding coverage check on CI and review fixes
adumont-payplug Jul 22, 2026
23de379
PRE-3563: review fixes
adumont-payplug Jul 22, 2026
e81f280
PRE-3553 feat: Create admin payment method Unify Hosted Fields (#307)
hdelaforce-payplug Aug 5, 2026
fa301c1
Feature/pre 3550 Adds Unified Hosted Fields (UHF) card tokenization a…
jhoaraupp Aug 10, 2026
4ecf166
PRE-3553 refactor: Set UHF as standard payment option (#310)
hdelaforce-payplug Aug 12, 2026
3eb4c4e
PRE-3551: create payment with Hosted Fields
adumont-payplug Aug 18, 2026
3bc282f
Feature/pre 3614 handing ipn notification 3ds
adumont-payplug Aug 21, 2026
de8d075
PRE-3585: Set alias with Unified hosted field (#315)
hdelaforce-payplug Sep 2, 2026
6a30301
PRE-3552: Add UHF refund
jhoaraupp Aug 28, 2026
87831c9
PRE-3646: fixing Multicurrency HF and removing SEIF field
adumont-payplug Sep 7, 2026
4bdc487
PRE-3646: adding failsafe and UI improvements on checkout
adumont-payplug Sep 9, 2026
50b4b95
PRE-3603: Overload scalapay min/max amounts from API
adumont-payplug Sep 1, 2026
461a7a2
PRE-3628: add per-channel gateway conflict checker
adumont-payplug Sep 11, 2026
824c590
PRE-3629: disable already-claimed chans in the gateway conf
adumont-payplug Sep 14, 2026
0f0e9d5
PRE-3683: resolve the /account payload per gateway config
adumont-payplug Sep 14, 2026
eb07149
PRE-3682: scope API client creation to the payment method
adumont-payplug Sep 14, 2026
746576b
PRE-3685: scope the UPC confRepo to a gateway instance
adumont-payplug Sep 15, 2026
897ef33
PRE-3631: show the connected PayPlug account per gateway
adumont-payplug Sep 15, 2026
d06eaab
PRE-3632: add a logout action per gateway config
adumont-payplug Sep 17, 2026
1b84cc6
PRE-3440: fixing Sonar issues
adumont-payplug Sep 17, 2026
4243a4d
PRE-3684: Deprecate Payum
adumont-payplug Sep 22, 2026
76b8f03
PRE-3709: fix co-branded network selector in Hosted Fields
adumont-payplug Sep 29, 2026
b9a181b
PRE-3673: Capture and cancel deferred Hosted Fields payments from the…
ilajili Oct 9, 2026
6c972d7
PRE-3722: bump UPC for the new Unified API contract (#335)
adumont-payplug Oct 9, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@

### Testing
- [ ] Unit tests added / updated
- [ ] New/changed code is covered by tests — SonarCloud Quality Gate (coverage on new code) passes on the `sonarcloud` CI job

### Security & Ops
- [ ] No sensitive data or secrets introduced
Expand Down
73 changes: 71 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,82 @@ jobs:
with:
symfony-versions: '["7.3"]'

# -----------------------------------------------------------------------
# COVERAGE — generates a Clover coverage report (PCOV) for SonarCloud to
# ingest. Kept separate from the reusable sylius_phpunit matrix (which
# runs with coverage: none) so coverage generation stays this repo's
# own concern, same as payplug/unified-plugin-core's ci.yml.
# -----------------------------------------------------------------------
coverage:
name: Coverage
if: github.base_ref == 'develop'
runs-on: ubuntu-latest
env:
APP_ENV: test
services:
mariadb:
image: 'mariadb:10.4.11'
ports:
- '3306:3306'
env:
MYSQL_ALLOW_EMPTY_PASSWORD: true
options: '--health-cmd="mysqladmin ping" --health-interval=5s --health-timeout=2s --health-retries=3'
steps:
-
uses: actions/checkout@v4
-
name: 'Setup PHP 8.2'
uses: shivammathur/setup-php@v2
with:
php-version: '8.2'
ini-values: date.timezone=UTC
extensions: intl
tools: symfony
coverage: pcov
-
name: 'Setup Node 20.x'
uses: actions/setup-node@v4
with:
node-version: '20.x'
-
name: 'Composer - Get Cache Directory'
id: composer-cache
run: 'echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT'
-
name: 'Composer - Set cache'
uses: actions/cache@v4
with:
path: '${{ steps.composer-cache.outputs.dir }}'
key: 'php-8.2-sylius-2.1.0-symfony-7.3-coverage-composer-${{ hashFiles(''**/composer.json'') }}'
restore-keys: 'php-8.2-sylius-2.1.0-symfony-7.3-coverage-composer-'
-
name: 'Composer - Github Auth'
run: 'composer config -g github-oauth.github.com ${{ github.token }}'
-
name: 'Install Sylius-Standard and Plugin'
run: 'make install -e SYLIUS_VERSION=2.1.0 SYMFONY_VERSION=7.3'
id: end-of-setup-sylius
-
name: 'Run tests with coverage'
run: 'composer test-coverage'
if: 'always() && steps.end-of-setup-sylius.outcome == ''success'''
-
name: 'Upload coverage report'
uses: actions/upload-artifact@v4
with:
name: clover-coverage
path: build/logs/clover.xml
retention-days: 1

sonarcloud:
if: always() && !failure() && !cancelled() && github.base_ref == 'develop'
needs: [sylius-matrix]
uses: payplug/template-ci/.github/workflows/sonarcloud.yml@main
needs: [sylius-matrix, coverage]
uses: payplug/template-ci/.github/workflows/sonarcloud-coverage.yml@main
with:
project-name: 'github-payplug-payplug-syliuspayplugplugin'
src-folder: 'src/'
coverage-report-artifact: 'clover-coverage'
enforce-quality-gate: true
secrets:
sonar-orga: ${{ secrets.SONAR_ORGA }}
sonar-token: ${{ secrets.SONAR_TOKEN }}
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,5 @@ CLAUDE.md
.claude
.review
.phpunit.result.cache
docs/
build/
57 changes: 57 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,76 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- PHP 8.2+ compatibility
- Use Payment Request API from Sylius
- New Unified Authentication System (OAuth2)
- **Multi-shop support**: several gateway configurations of the same type may now coexist, each
connected to its own PayPlug account and scoped to its own channels
- The connected PayPlug account is displayed on each gateway's update screen
- "Disconnect this account" per gateway, clearing that gateway's credentials without touching others
- Channels already claimed by another enabled gateway of the same type are rendered unselectable
- **Hosted Fields deferred capture**: with deferred capture enabled, Hosted Fields payments are
created as authorizations and captured (in full, partially, several times) or cancelled (in full
or partially) from the admin order screen, with the remaining amount and capture deadline shown
- The Hosted Fields JS SDK URL is configurable through `PAYPLUG_HOSTED_FIELDS_SDK_URL` and defaults
to the production SDK (`v2.2.0`): QA, staging and local setups must set it to their own SDK URL

> [!IMPORTANT]
> Merchants will need to contact support to switch to the new authentication method.

### Changed
- Plugin structure has been changed to follow the new Symfony bundle structure
- Front assets have been migrated to use Stimulus
- Gateway uniqueness is now enforced **per channel** instead of per installation: two gateways of
the same type may both be enabled as long as their channel sets are disjoint
- Credentials are resolved from the payment method rather than from the gateway factory name, so a
request for one channel can no longer be signed with another channel's account

### Removed
- Drop Payum support
- Drop Sylius 1.x support
- Drop usage of Secret key - Use OAuth2 instead

### Fixed
- Integrated Payment no longer accepts a payment method id that does not belong to the order's
channel, or a disabled one — previously a shopper could have the payment created on another
merchant's PayPlug account
- Oney instalment options, the Oney availability check and Apple Pay now resolve the gateway serving
the current channel instead of an arbitrary one
- Hosted Fields payments, with a new or a saved card, are accepted again by the Unified API, which
now reads the card token from `paymentMethod.hfToken` and the saved card from
`paymentMethod.storedId`
- Cards the shopper asked to save are stored again, including after a 3DS challenge (the alias is
read from `paymentMethod.storedId`)
- Refunding a Hosted Fields payment that PayPlug created without a payment reference is refused
with a clear back-office message, in addition to Sylius's own generic error message, and nothing
is sent to PayPlug; the payment itself is processed normally
- Hosted Fields deferred capture and cancel keep working with `payplug/unified-plugin-core` 1.2.1:
the plugin now passes the payment currency to UPC's `AmountHelper`, which requires it since
PRE-3724

### Breaking changes for anyone extending the plugin

| Removed / changed | Replacement |
| --- | --- |
| `PayPlugApiClientFactoryInterface::create(string $factoryName)` | `createForPaymentMethod(PaymentMethodInterface $pm)` |
| `UnifiedApiPaymentCreatorInterface::createPayment($dto)` | `createPayment($dto, PaymentMethodInterface $method)` |
| `OperationStatusFetcherInterface::getOperation($id)` | `getOperation($id, PaymentMethodInterface $method)` |
| `AbstractGatewayConfigurationType::__construct()` — `$gatewayConfigRepository` and `$requestStack` dropped | translator only |
| `shouldValidateBaseCurrency()` / `baseCurrencyViolationMessage()` — `protected` → `public`, now take the **mapped** config | same hooks, new visibility/shape |
| `$gatewayFactoryName` property on the 8 configuration types | no longer read; the factory name comes off the gateway config |
| Translation key `form.only_one_gateway_allowed` | `form.gateway_channel_conflict` (`%channel%`, `%payment_method%`) |
| Injecting `PayplugUnifiedCore\Contracts\IConfigurationRepository` (its service alias is gone) | `ScopedConfigurationRepositoryInterface`, scoped per payment method |
| `PaymentMethodRepositoryInterface::findOneByGatewayName()` — **deprecated**, returns an arbitrary config when several share a factory name | `findOneEnabledByGatewayNameAndChannel($factoryName, $channel)` |
| `OneyExtension::__construct()` — `$gatewayConfigRepository` dropped, `$paymentMethodRepository` is now the plugin's `PaymentMethodRepositoryInterface` | inject the plugin repository |
| `OneySupportedPaymentChoiceProvider::__construct()` | now also takes a `ChannelContextInterface` |
| `PayPlugExtension::__construct()` | now also takes `string $hostedFieldsSdkUrl` (bound to `%payplug.hosted_fields_sdk_url%`) |

Requires `payplug/unified-plugin-core ^1.2.1` (for the nullable `TokenOutput::$idToken` and the
Unified API payment contract: `paymentMethod.hfToken`, `paymentMethod.storedId`).

> [!NOTE]
> A gateway connected before this release shows a "re-authenticate" placeholder instead of the
> account email until the merchant reconnects — the address is only available from the interactive
> OAuth `id_token`, which is minted at login.

Please refer to [github releases](https://github.com/payplug/SyliusPayPlugPlugin/releases) for historical release information.

---
Expand Down
38 changes: 38 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
FROM composer:2 AS composer

FROM php:8.2-cli

RUN apt-get update && apt-get install -y --no-install-recommends \
git \
unzip \
libicu-dev \
libzip-dev \
libonig-dev \
libxml2-dev \
libpng-dev \
libjpeg-dev \
libfreetype6-dev \
libsodium-dev \
&& docker-php-ext-configure gd --with-freetype --with-jpeg \
&& docker-php-ext-install -j$(nproc) \
intl \
gd \
sodium \
pdo_mysql \
mbstring \
xml \
dom \
simplexml \
xmlwriter \
zip \
&& pecl install pcov \
&& docker-php-ext-enable pcov \
&& rm -rf /var/lib/apt/lists/*

COPY --from=composer /usr/bin/composer /usr/local/bin/composer

RUN useradd --create-home --uid 1000 appuser

WORKDIR /app

USER appuser
21 changes: 18 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@ SYLIUS_VERSION=2.1.0
SYMFONY_VERSION=6.4
PLUGIN_NAME=payplug/sylius-payplug-plugin

# Coverage runs inside Docker (PHP 8.2 + PCOV) instead of the host PHP, since the host's default
# `php`/`composer` may resolve to an unrelated version with no coverage driver installed. Assumes
# `vendor/` (and the Sylius test-application) is already installed on the host via `make install`.
IMAGE_DEV := sylius-payplug-plugin-dev
DOCKER_RUN := docker run --rm -v $(CURDIR):/app -w /app -u "$$(id -u):$$(id -g)" -e COMPOSER_HOME=/tmp/composer $(IMAGE_DEV)

###
### DEVELOPMENT
### ¯¯¯¯¯¯¯¯¯¯¯
Expand All @@ -23,6 +29,14 @@ phpunit: ## Run PHPUnit tests
./vendor/bin/phpunit
.PHONY: phpunit

build-dev: ## Build the Docker image used to run coverage
docker build -t $(IMAGE_DEV) .
.PHONY: build-dev

coverage: build-dev ## Run PHPUnit tests with a Clover coverage report (build/logs/clover.xml), via Docker
$(DOCKER_RUN) composer test-coverage
.PHONY: coverage

###
### OTHER
### ¯¯¯¯¯¯
Expand All @@ -33,11 +47,12 @@ install-sylius:
@echo "Installing Sylius ${SYLIUS_VERSION} using TestApplication"
${COMPOSER} config extra.symfony.require "^${SYMFONY_VERSION}"
${COMPOSER} install
${COMPOSER} require --dev sylius/test-application:"^${SYLIUS_VERSION}@alpha" -n -W # TODO: Remove alpha when stable
@# Capped below 2.3: Sylius 2.3 (PHP 8.3+, so only resolved on the PHP 8.4 cells) drops knp-gaufrette-bundle,
@# which sylius/refund-plugin's config still loads, so test-application:install fails. Lift once RefundPlugin follows.
@# sylius/sylius is capped explicitly: older test-application releases (the 2.0.0 cell) do not constrain it and let it float to 2.3.
${COMPOSER} require --dev sylius/test-application:">=${SYLIUS_VERSION},<2.3@alpha" sylius/sylius:"<2.3" -n -W # TODO: Remove alpha when stable
${COMPOSER} test-application:install



behat-configure: ## Configure Behat
(cd ${TEST_DIRECTORY} && cp behat.yml.dist behat.yml)
(cd ${TEST_DIRECTORY} && sed -i "s#vendor/sylius/sylius/src/Sylius/Behat/Resources/config/suites.yml#vendor/${PLUGIN_NAME}/tests/Behat/Resources/suites.yml#g" behat.yml)
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=github-payplug-payplug-syliuspayplugplugin&metric=alert_status&token=af29f9f3fbb3a74caff4e4a4d168bddab858f4dc)](https://sonarcloud.io/summary/new_code?id=github-payplug-payplug-syliuspayplugplugin)
[![Duplicated Lines (%)](https://sonarcloud.io/api/project_badges/measure?project=github-payplug-payplug-syliuspayplugplugin&metric=duplicated_lines_density&token=af29f9f3fbb3a74caff4e4a4d168bddab858f4dc)](https://sonarcloud.io/summary/new_code?id=github-payplug-payplug-syliuspayplugplugin)
[![Code Smells](https://sonarcloud.io/api/project_badges/measure?project=github-payplug-payplug-syliuspayplugplugin&metric=code_smells&token=af29f9f3fbb3a74caff4e4a4d168bddab858f4dc)](https://sonarcloud.io/summary/new_code?id=github-payplug-payplug-syliuspayplugplugin)
[![Coverage](https://sonarcloud.io/api/project_badges/measure?project=github-payplug-payplug-syliuspayplugplugin&metric=coverage&token=af29f9f3fbb3a74caff4e4a4d168bddab858f4dc)](https://sonarcloud.io/summary/new_code?id=github-payplug-payplug-syliuspayplugplugin)
[![Version](https://img.shields.io/packagist/v/payplug/sylius-payplug-plugin.svg)](https://packagist.org/packages/payplug/sylius-payplug-plugin)
[![Total Downloads](https://poser.pugx.org/payplug/sylius-payplug-plugin/downloads)](https://packagist.org/packages/payplug/sylius-payplug-plugin)

Expand Down
32 changes: 32 additions & 0 deletions UPGRADE.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,35 @@
# Deprecations

## The Payum wiring — removal planned for early 2027

The plugin no longer uses Payum. The live payment flow is Sylius 2.x command/response providers,
declared in `config/services.yaml` and implemented in `src/Command/Handler/`.

The Payum code left in the tree is inert: `PayPlugSyliusPayPlugExtension::load()` does not load
`config/services/gateway.xml`, which holds the only `payum.gateway_factory_builder` tags, so no
Payum gateway is ever registered for the `payplug*` factory names. The actions in `src/Action/` are
still tagged `payum.action` in the compiled container (via `#[AutoconfigureTag]` attributes on the
classes), but with no gateway to attach to they are never executed.

The following are deprecated and **will be removed permanently in early 2027**:

- `PayPlug\SyliusPayPlugPlugin\Action\CaptureAction`
- `PayPlug\SyliusPayPlugPlugin\Action\StatusAction`
- `PayPlug\SyliusPayPlugPlugin\Action\NotifyAction`
- `PayPlug\SyliusPayPlugPlugin\Action\ConvertPaymentAction`
- `PayPlug\SyliusPayPlugPlugin\Action\Api\ApiAwareTrait::setApi()` (the trait's `$payPlugApiClient`
property is **not** deprecated — it is still used by `Controller\OneClickAction`)
- `PayPlug\SyliusPayPlugPlugin\Gateway\AbstractGatewayFactory::populateConfig()` (the class itself
is **not** deprecated — it remains the home of `FACTORY_NAME`, `FACTORY_TITLE` and
`BASE_CURRENCY_CODE`)
- `config/services/gateway.xml`

**If you do not import `config/services/gateway.xml` in your own application, this affects you in no
way** — none of the above runs today, and there is nothing to migrate.

If you *do* import it to keep using Payum, that escape hatch stops working at the removal date. Move
to the command handlers in `src/Command/Handler/` before then.

# Upgrading from 1.0.0

1. Skip the faulty migration
Expand Down
7 changes: 7 additions & 0 deletions assets/controllers.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,13 @@
"@payplug/sylius-payplug-plugin/shop/dist/payment/integrated.css": true
}
},
"hosted-fields": {
"enabled": true,
"fetch": "lazy",
"autoimport": {
"@payplug/sylius-payplug-plugin/shop/dist/payment/hosted-fields.css": true
}
},
"oney-payment": {
"enabled": true,
"fetch": "lazy"
Expand Down
9 changes: 9 additions & 0 deletions assets/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,15 @@
"@payplug/sylius-payplug-plugin/shop/dist/payment/integrated.css": true
}
},
"hosted-fields": {
"main": "shop/controllers/hosted-fields_controller.js",
"webpackMode": "lazy",
"fetch": "lazy",
"enabled": true,
"autoimport": {
"@payplug/sylius-payplug-plugin/shop/dist/payment/hosted-fields.css": true
}
},
"oney-payment": {
"main": "shop/controllers/oney-payment_controller.js",
"webpackMode": "lazy",
Expand Down
Loading
Loading