diff --git a/.gitattributes b/.gitattributes
index 33c243c..787d7b1 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -6,7 +6,7 @@
/AGENTS.md export-ignore
/CLAUDE.md export-ignore
/Makefile export-ignore
-/behat.yml.dist export-ignore
+/behat.dist.php export-ignore
/compose.override.dist.yml export-ignore
/compose.yml export-ignore
/ecs.php export-ignore
diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml
index c03bcde..1ab5f14 100644
--- a/.github/workflows/build.yaml
+++ b/.github/workflows/build.yaml
@@ -28,9 +28,15 @@ concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
+permissions:
+ contents: read
+
jobs:
tests:
- runs-on: ubuntu-latest
+ # Pinned rather than `ubuntu-latest`: the build action is only known to work on this image.
+ runs-on: ubuntu-24.04
+ # A stuck browser would otherwise hold the runner for the 6-hour default.
+ timeout-minutes: 30
name: "Sylius ${{ matrix.sylius }}, PHP ${{ matrix.php }}, Symfony ${{ matrix.symfony }}, ${{ matrix.database }}"
@@ -42,27 +48,44 @@ jobs:
# dependency rather than on our code, move the line to `continue-on-error` with a
# comment - do not drop it from the matrix.
php: ["8.2", "8.3", "8.4", "8.5"]
- symfony: ["^6.4", "^7.4"]
+ symfony: ["^6.4", "^7.4", "^8.0"]
# The floor is 2.1 rather than 2.0 for two reasons lying with Sylius itself. Its
# Behat cart context did not restore the security token before 2.1, so a scenario
# logging the customer in before filling their cart cannot pass on 2.0. And 2.0
# pins Behat versions predating PHP 8.5, which Composer therefore cannot install
# together.
- sylius: ["~2.1.0", "~2.2.0"]
+ sylius: ["~2.1.0", "~2.2.0", "~2.3.0"]
# The plugin creates no table and writes no query: a single database is enough on
# the main matrix. The cycles saved cover 4 PHP versions.
database: ["mysql:8.4"]
node: ["22.x"]
+ # Sylius 2.3 needs PHP 8.3 and is the only one to support Symfony 8, which needs
+ # PHP 8.4. Symfony 6.4 is covered by Sylius 2.1 and 2.2: running it on 2.3 as well
+ # would cost three jobs for a combination no new project picks.
+ exclude:
+ - sylius: "~2.1.0"
+ symfony: "^8.0"
+ - sylius: "~2.2.0"
+ symfony: "^8.0"
+ - sylius: "~2.3.0"
+ symfony: "^6.4"
+ - sylius: "~2.3.0"
+ php: "8.2"
+ - symfony: "^8.0"
+ php: "8.3"
include:
+ # The build action declares MariaDB with a bare version, which DBAL reads as
+ # MySQL: that is what the Sylius 2.3 migrations need, they skip a database
+ # declared as MariaDB.
- php: "8.4"
symfony: "^7.4"
- sylius: "~2.2.0"
+ sylius: "~2.3.0"
database: "mariadb:11.4"
node: "22.x"
- php: "8.4"
symfony: "^7.4"
- sylius: "~2.2.0"
+ sylius: "~2.3.0"
database: "postgres:17"
node: "22.x"
@@ -73,9 +96,22 @@ jobs:
-
uses: actions/checkout@v7
+ # Sylius 2.1 / 2.2 declare their Behat steps in annotations, which Behat 4 no longer reads;
+ # Composer would still pick Behat 4 for them.
+ -
+ name: Keep Behat 3 before Sylius 2.3
+ if: matrix.sylius != '~2.3.0'
+ run: composer require --dev --no-update --no-interaction "behat/behat:^3.22" "friends-of-behat/mink-extension:^2.7"
+
+ # Sylius 2.1 mappings fail ORM 3.7 validation (ShipmentUnit#shipment vs Shipment#units).
+ -
+ name: Keep Doctrine ORM below 3.7 before Sylius 2.3
+ if: matrix.sylius != '~2.3.0'
+ run: composer require --no-update --no-interaction "doctrine/orm:^2.20 || >=3.6 <3.7"
+
-
name: Build Sylius Test Application
- uses: SyliusLabs/BuildTestAppAction@v4
+ uses: SyliusLabs/BuildTestAppAction@fbdfc158a99f540fc3bbe554b46120d14444f2f4 # v4.3.0
with:
# The Behat scenarios open no browser - `symfony` session, no `@javascript`
# tag - but the admin pages render the Webpack Encore entries: without a
@@ -86,6 +122,9 @@ jobs:
database: "${{ matrix.database }}"
node_version: "${{ matrix.node }}"
php_version: "${{ matrix.php }}"
+ # The PHP-FPM worker behind `symfony server:start` segfaulted at random under OPcache
+ # (Behat 502, "child exited on signal 11"): Behat does not need it.
+ opcache_enable: "no"
sylius_version: "${{ matrix.sylius }}"
symfony_version: "${{ matrix.symfony }}"
@@ -103,7 +142,7 @@ jobs:
# most frequent class of bug.
-
name: Validate container
- run: vendor/bin/console lint:container
+ run: vendor/bin/console lint:container --env=test
# Sylius encrypts the gateway configuration: without this key, every test writing to
# the database fails on "Cannot read keyfile", an error that does not say its name.
@@ -111,12 +150,16 @@ jobs:
name: Generate the payment encryption key
run: vendor/bin/console sylius:payment:generate-key
+ # Static analysis and the coding standard do not depend on the database nor on the PHP
+ # version line under test, so they only run once, on the primary job.
-
name: Run PHPStan
+ if: matrix.php == '8.4' && matrix.symfony == '^7.4' && matrix.sylius == '~2.3.0' && matrix.database == 'mysql:8.4'
run: vendor/bin/phpstan analyse --ansi --no-progress
-
name: Run ECS
+ if: matrix.php == '8.4' && matrix.symfony == '^7.4' && matrix.sylius == '~2.3.0' && matrix.database == 'mysql:8.4'
run: vendor/bin/ecs check --ansi --no-progress-bar
-
@@ -148,7 +191,7 @@ jobs:
# required check counts as satisfied - the rule would let through exactly what it guards
# against.
build:
- runs-on: ubuntu-latest
+ runs-on: ubuntu-24.04
name: "Build complete"
needs: tests
if: always()
diff --git a/.github/workflows/security.yaml b/.github/workflows/security.yaml
index 9d61239..4762ca4 100644
--- a/.github/workflows/security.yaml
+++ b/.github/workflows/security.yaml
@@ -10,9 +10,13 @@ on:
cron: "0 7 * * 1" # Every Monday at 7am
workflow_dispatch: ~
+permissions:
+ contents: read
+
jobs:
audit:
- runs-on: ubuntu-latest
+ runs-on: ubuntu-24.04
+ timeout-minutes: 15
name: "Composer audit"
@@ -22,7 +26,7 @@ jobs:
-
name: Setup PHP
- uses: shivammathur/setup-php@v2
+ uses: shivammathur/setup-php@f3e473d116dcccaddc5834248c87452386958240 # 2.37.2
with:
php-version: "8.4"
coverage: none
diff --git a/.gitignore b/.gitignore
index d584fdb..a8d5f32 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,7 +7,7 @@
/etc/build/*
!/etc/build/.gitignore
-/behat.yml
+/behat.php
/phpunit.xml
/compose.override.yml
diff --git a/CLAUDE.md b/CLAUDE.md
index 131824e..f465583 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -48,7 +48,7 @@ docker run --rm -v "$PWD":/app -w /app/vendor/sylius/test-application \
### A file added under `config/services/` is not seen by `cache:clear`
-`config/services.xml` imports `services/**`, and the `GlobResource` keeps the list frozen. You need
+`config/services.php` imports `services/*.php`, and the `GlobResource` keeps the list frozen. You need
`rm -rf var/cache/*`. Symptom: the services of the new file do not exist, without the slightest
error.
@@ -64,7 +64,9 @@ first payment in the database appears to work.
Sylius encrypts the gateway configuration (`sylius_payment.encryption.enabled` defaults to true).
**Without an encryption key, every write fails on `Cannot read keyfile`.** The key lives in
`vendor/sylius/test-application/config/encryption/` and therefore disappears on every
-`composer install`.
+`composer install` or `update` that replaces the test application. The gateway configurations
+already in a database were encrypted with the lost key: the payment methods grid then answers 500
+on a decryption error. Recreate those payment methods, or reload the fixtures.
```bash
APP_ENV=test vendor/bin/console doctrine:schema:update --force --complete -n
@@ -86,6 +88,12 @@ APP_ENV=test vendor/bin/console sylius:payment:generate-key
the matching Sylius foundation.
- **`PasswordType` fields require `always_empty: false`** - otherwise saving an existing
`PaymentMethod` again overwrites the keys with empty strings.
+- **Configuration stays in PHP.** Symfony 8 no longer loads XML service definitions, and Behat 4
+ (the one Symfony 8 requires) neither YAML configuration nor step annotations: services in
+ `config/services/*.php`, Behat in `behat.dist.php`, steps as `#[Given]` attributes.
+- **Sylius 2.1 to 2.3 with one code base.** Where Sylius changed a signature, widen the parameter
+ rather than branch on the version - `AxeptaHttpResponseProvider` takes `object $request` because
+ 2.1 / 2.2 pass a `RequestConfiguration` and 2.3 a `Request`.
- **New configuration keys must tolerate their own absence**: a gateway configuration written by an
earlier version does not carry them.
- The public contract - protocol classes, substitutable interfaces, hookable names, service tags,
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index bad7b31..9a9137a 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -37,7 +37,7 @@ Two checks have to be green before a pull request can be merged:
| Check | What it covers |
|---|---|
-| **`Build complete`** | the 18 matrix jobs, aggregated: PHP 8.2 to 8.5, Symfony 6.4 and 7.4, Sylius 2.1 and 2.2 |
+| **`Build complete`** | the 23 matrix jobs, aggregated: PHP 8.2 to 8.5, Symfony 6.4, 7.4 and 8, Sylius 2.1, 2.2 and 2.3, within what each Sylius supports |
| **`Composer audit`** | known vulnerabilities in the dependencies |
If this is your first contribution, the workflows will not start until a maintainer approves the
diff --git a/README.md b/README.md
index 822a75f..804aa78 100644
--- a/README.md
+++ b/README.md
@@ -17,9 +17,9 @@ Integration of the **Axepta BNP Paribas** payment gateway for **Sylius 2.1+**.
| Component | Versions |
|---|---|
-| PHP | `^8.2` |
-| Sylius | `^2.1` |
-| Symfony | `^6.4` or `^7.4` |
+| PHP | `^8.2` (Sylius 2.3: `^8.3`, Symfony 8: `^8.4`) |
+| Sylius | `2.1`, `2.2`, `2.3` |
+| Symfony | `^6.4`, `^7.4`, or `^8.0` with Sylius 2.3 |
## What this plugin does
@@ -124,8 +124,9 @@ notification, accented description in ISO-8859-1, and twelve-character merchant
nominal cycles were replayed on the code as it stands; the remaining cases come from an earlier
campaign, on a slightly older revision.
-**The continuous integration matrix is green** across the eighteen advertised combinations: PHP 8.2
-to 8.5, Symfony 6.4 and 7.4, Sylius 2.1 and 2.2, and MySQL 8.4, MariaDB 11.4 and PostgreSQL 17. The
+**The continuous integration matrix is green** across the twenty-three advertised combinations:
+PHP 8.2 to 8.5, Symfony 6.4, 7.4 and 8, Sylius 2.1, 2.2 and 2.3 - within what each Sylius version
+supports - and MySQL 8.4, MariaDB 11.4 and PostgreSQL 17. The
run against the real platform was carried out on **PHP 8.4 / Sylius 2.2 / Symfony 7.4** - the only
combination on which a payment was actually taken.
diff --git a/UPGRADE.md b/UPGRADE.md
index 845b746..2c8cd75 100644
--- a/UPGRADE.md
+++ b/UPGRADE.md
@@ -6,4 +6,9 @@ plugin.
A payment plugin is upgraded in production, on a site that takes money: every compatibility break
must be written here before it is released, together with the exact manoeuvre to carry out.
-`v1.0.0` is the first release: there is nothing to upgrade from yet.
+## From 1.0 to 1.1
+
+Nothing to change in the project: 1.1 adds Sylius 2.3 and Symfony 8 support and breaks nothing.
+
+The plugin now defines its services in PHP, Symfony 8 no longer reading XML. Service ids, tags and
+arguments are unchanged: a project that decorates or overrides them by id keeps working.
diff --git a/behat.dist.php b/behat.dist.php
new file mode 100644
index 0000000..0d9cd56
--- /dev/null
+++ b/behat.dist.php
@@ -0,0 +1,64 @@
+ [
+ 'formatters' => [
+ 'pretty' => [
+ 'verbose' => true,
+ 'paths' => false,
+ 'snippets' => false,
+ ],
+ ],
+ 'extensions' => [
+ ChromeExtension::class => null,
+ MinkDebugExtension::class => [
+ 'directory' => 'etc/build',
+ 'clean_start' => false,
+ 'screenshot' => true,
+ ],
+ MinkExtension::class => [
+ 'files_path' => '%paths.base%/vendor/sylius/sylius/src/Sylius/Behat/Resources/fixtures/',
+ 'base_url' => '%env(BEHAT_BASE_URL)%',
+ 'default_session' => 'symfony',
+ 'javascript_session' => 'chrome',
+ 'sessions' => [
+ 'symfony' => [
+ 'symfony' => null,
+ ],
+ 'chrome' => [
+ 'chrome' => [
+ 'api_url' => 'http://127.0.0.1:9222',
+ 'validate_certificate' => false,
+ ],
+ ],
+ ],
+ 'show_auto' => false,
+ ],
+ SymfonyExtension::class => [
+ 'bootstrap' => 'vendor/sylius/test-application/config/bootstrap.php',
+ 'kernel' => [
+ 'class' => 'Sylius\TestApplication\Kernel',
+ 'environment' => 'test',
+ ],
+ ],
+ VariadicExtension::class => null,
+ SuiteSettingsExtension::class => [
+ 'paths' => ['features'],
+ ],
+ SuiteTagsExtension::class => null,
+ ],
+ ],
+]))->import('tests/Behat/Resources/suites.php');
diff --git a/behat.yml.dist b/behat.yml.dist
deleted file mode 100644
index 1dd2b92..0000000
--- a/behat.yml.dist
+++ /dev/null
@@ -1,46 +0,0 @@
-imports:
- - vendor/sylius/sylius/src/Sylius/Behat/Resources/config/suites.yml
- - tests/Behat/Resources/suites.yml
-
-default:
- formatters:
- pretty:
- verbose: true
- paths: false
- snippets: false
-
- extensions:
- DMore\ChromeExtension\Behat\ServiceContainer\ChromeExtension: ~
-
- FriendsOfBehat\MinkDebugExtension:
- directory: etc/build
- clean_start: false
- screenshot: true
-
- Behat\MinkExtension:
- files_path: "%paths.base%/vendor/sylius/sylius/src/Sylius/Behat/Resources/fixtures/"
- base_url: "%env(BEHAT_BASE_URL)%"
- default_session: symfony
- javascript_session: chrome
- sessions:
- symfony:
- symfony: ~
- chrome:
- chrome:
- api_url: http://127.0.0.1:9222
- validate_certificate: false
- show_auto: false
-
- FriendsOfBehat\SymfonyExtension:
- bootstrap: vendor/sylius/test-application/config/bootstrap.php
- kernel:
- class: Sylius\TestApplication\Kernel
- environment: test
-
- FriendsOfBehat\VariadicExtension: ~
-
- FriendsOfBehat\SuiteSettingsExtension:
- paths:
- - "features"
-
- SyliusLabs\SuiteTagsExtension: ~
diff --git a/compose.override.dist.yml b/compose.override.dist.yml
index f8fccb6..d9e685e 100644
--- a/compose.override.dist.yml
+++ b/compose.override.dist.yml
@@ -1,6 +1,6 @@
services:
php:
- image: ghcr.io/sylius/sylius-php:8.3-fixuid-xdebug-alpine
+ image: ghcr.io/sylius/sylius-php:8.4-fixuid-xdebug-alpine
user: ${DOCKER_USER:-1000:1000}
depends_on:
mysql:
@@ -51,7 +51,7 @@ services:
ports:
- "80:80"
nodejs:
- image: node:${NODE_VERSION:-20}-alpine
+ image: node:${NODE_VERSION:-22}-alpine
user: ${DOCKER_USER:-1000:1000}
working_dir: /srv/sylius
entrypoint: [ "/bin/sh","-c" ]
diff --git a/compose.yml b/compose.yml
index 067908b..67fe013 100644
--- a/compose.yml
+++ b/compose.yml
@@ -1,6 +1,6 @@
services:
php:
- image: ghcr.io/sylius/sylius-php:8.3-alpine
+ image: ghcr.io/sylius/sylius-php:8.4-alpine
mysql:
image: mysql:8.4
platform: linux/amd64
diff --git a/composer.json b/composer.json
index af5aa53..cc70cdf 100644
--- a/composer.json
+++ b/composer.json
@@ -11,6 +11,18 @@
"payment-gateway"
],
"license": "MIT",
+ "homepage": "https://github.com/CylleneDigital/SyliusAxeptaPlugin",
+ "authors": [
+ {
+ "name": "Cyllene",
+ "homepage": "https://github.com/CylleneDigital"
+ }
+ ],
+ "support": {
+ "issues": "https://github.com/CylleneDigital/SyliusAxeptaPlugin/issues",
+ "source": "https://github.com/CylleneDigital/SyliusAxeptaPlugin",
+ "security": "https://github.com/CylleneDigital/SyliusAxeptaPlugin/security/policy"
+ },
"require": {
"php": "^8.2",
"ext-hash": "*",
@@ -18,28 +30,27 @@
"payum/core": "^1.7",
"psr/log": "^3.0",
"sylius/sylius": "^2.1",
- "symfony/config": "^6.4 || ^7.4",
- "symfony/dependency-injection": "^6.4 || ^7.4",
- "symfony/form": "^6.4 || ^7.4",
- "symfony/http-foundation": "^6.4 || ^7.4",
- "symfony/http-kernel": "^6.4 || ^7.4",
- "symfony/messenger": "^6.4 || ^7.4",
- "symfony/routing": "^6.4 || ^7.4",
+ "symfony/config": "^6.4 || ^7.4 || ^8.0",
+ "symfony/dependency-injection": "^6.4 || ^7.4 || ^8.0",
+ "symfony/form": "^6.4 || ^7.4 || ^8.0",
+ "symfony/http-foundation": "^6.4 || ^7.4 || ^8.0",
+ "symfony/http-kernel": "^6.4 || ^7.4 || ^8.0",
+ "symfony/messenger": "^6.4 || ^7.4 || ^8.0",
+ "symfony/routing": "^6.4 || ^7.4 || ^8.0",
"symfony/service-contracts": "^3.0",
- "symfony/validator": "^6.4 || ^7.4",
- "symfony/yaml": "^6.4 || ^7.4",
+ "symfony/validator": "^6.4 || ^7.4 || ^8.0",
+ "symfony/yaml": "^6.4 || ^7.4 || ^8.0",
"twig/twig": "^3.10"
},
"require-dev": {
- "behat/behat": "^3.16",
+ "behat/behat": "^3.16 || ^4.0",
+ "behat/mink": "^1.11",
+ "behat/mink-browserkit-driver": "^2.2",
"dbrekelmans/bdi": "^1.4",
- "dmore/behat-chrome-extension": "^1.4",
"dmore/chrome-mink-driver": "^2.9",
- "friends-of-behat/mink": "^1.11",
- "friends-of-behat/mink-browserkit-driver": "^1.6",
"friends-of-behat/mink-debug-extension": "^2.1",
- "friends-of-behat/mink-extension": "^2.7",
- "friends-of-behat/page-object-extension": "^0.3",
+ "friends-of-behat/mink-extension": "^2.7 || ^3.0@alpha",
+ "friends-of-behat/page-object-extension": "^0.3 || ^0.4",
"friends-of-behat/suite-settings-extension": "^1.1",
"friends-of-behat/symfony-extension": "^2.6",
"friends-of-behat/variadic-extension": "^1.6",
@@ -48,16 +59,17 @@
"phpstan/phpstan-doctrine": "^2.0",
"phpstan/phpstan-webmozart-assert": "^2.0",
"phpunit/phpunit": "^10.5",
+ "sylius-labs/behat-chrome-extension": "^1.5",
"sylius-labs/coding-standard": "^4.4",
"sylius-labs/suite-tags-extension": "~0.2",
- "sylius/test-application": "^2.0.0@alpha",
- "symfony/browser-kit": "^6.4 || ^7.4",
- "symfony/debug-bundle": "^6.4 || ^7.4",
- "symfony/dotenv": "^6.4 || ^7.4",
- "symfony/http-client": "^6.4 || ^7.4",
- "symfony/intl": "^6.4 || ^7.4",
- "symfony/runtime": "^6.4 || ^7.4",
- "symfony/web-profiler-bundle": "^6.4 || ^7.4",
+ "sylius/test-application": "~2.1.0@alpha || ~2.2.0@alpha || ~2.3.0@alpha",
+ "symfony/browser-kit": "^6.4 || ^7.4 || ^8.0",
+ "symfony/debug-bundle": "^6.4 || ^7.4 || ^8.0",
+ "symfony/dotenv": "^6.4 || ^7.4 || ^8.0",
+ "symfony/http-client": "^6.4 || ^7.4 || ^8.0",
+ "symfony/intl": "^6.4 || ^7.4 || ^8.0",
+ "symfony/runtime": "^6.4 || ^7.4 || ^8.0",
+ "symfony/web-profiler-bundle": "^6.4 || ^7.4 || ^8.0",
"symfony/webpack-encore-bundle": "^2.2"
},
"config": {
@@ -75,7 +87,7 @@
"dev-main": "1.0.x-dev"
},
"symfony": {
- "require": "^7.4"
+ "require": "^7.4 || ^8.0"
},
"public-dir": "vendor/sylius/test-application/public"
},
diff --git a/config/services.php b/config/services.php
new file mode 100644
index 0000000..3bd582e
--- /dev/null
+++ b/config/services.php
@@ -0,0 +1,9 @@
+import('services/*.php');
+};
diff --git a/config/services.xml b/config/services.xml
deleted file mode 100644
index 3a4a0ff..0000000
--- a/config/services.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
-
-
-
-
-
-
-
diff --git a/config/services/controller.php b/config/services/controller.php
new file mode 100644
index 0000000..9ade5b8
--- /dev/null
+++ b/config/services/controller.php
@@ -0,0 +1,27 @@
+services();
+
+ $services->set('cyllene_digital_sylius_axepta.provider.return_url', ReturnUrlProvider::class)
+ ->args([
+ service('router'),
+ param('kernel.secret'),
+ ]);
+
+ $services->set(ReturnFromPaymentPageAction::class, ReturnFromPaymentPageAction::class)
+ ->public()
+ ->args([
+ service('sylius.repository.payment'),
+ service('cyllene_digital_sylius_axepta.provider.return_url'),
+ service('router'),
+ ])
+ ->tag('controller.service_arguments');
+};
diff --git a/config/services/controller.xml b/config/services/controller.xml
deleted file mode 100644
index ad8c2f1..0000000
--- a/config/services/controller.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
-
-
-
-
- %kernel.secret%
-
-
-
-
-
-
-
-
-
-
diff --git a/config/services/form.php b/config/services/form.php
new file mode 100644
index 0000000..ae63062
--- /dev/null
+++ b/config/services/form.php
@@ -0,0 +1,17 @@
+services();
+
+ // The `type` attribute is an invariant: it selects this form for payment methods whose gateway
+ // is `axepta`, including those already in the database.
+ $services->set('cyllene_digital_sylius_axepta.form.type.gateway_configuration', AxeptaGatewayConfigurationType::class)
+ ->tag('sylius.gateway_configuration_type', ['type' => 'axepta', 'label' => 'Axepta - BNP Paribas'])
+ ->tag('form.type');
+};
diff --git a/config/services/form.xml b/config/services/form.xml
deleted file mode 100644
index 7bbb92c..0000000
--- a/config/services/form.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/config/services/payment_request.php b/config/services/payment_request.php
new file mode 100644
index 0000000..b864b1f
--- /dev/null
+++ b/config/services/payment_request.php
@@ -0,0 +1,101 @@
+services();
+
+ $services->set('cyllene_digital_sylius_axepta.command_provider.payment_request', AxeptaActionsCommandProvider::class)
+ ->args([tagged_locator('cyllene_digital_sylius_axepta.command_provider.payment_request', indexAttribute: 'action')])
+ ->tag('sylius.payment_request.command_provider', ['gateway_factory' => 'axepta']);
+
+ $services->set('cyllene_digital_sylius_axepta.command_provider.payment_request.capture', CaptureCommandProvider::class)
+ ->tag('cyllene_digital_sylius_axepta.command_provider.payment_request', ['action' => 'capture']);
+
+ $services->set('cyllene_digital_sylius_axepta.command_provider.payment_request.notify', NotifyCommandProvider::class)
+ ->tag('cyllene_digital_sylius_axepta.command_provider.payment_request', ['action' => 'notify']);
+
+ $services->set('cyllene_digital_sylius_axepta.command_provider.payment_request.status', StatusCommandProvider::class)
+ ->tag('cyllene_digital_sylius_axepta.command_provider.payment_request', ['action' => 'status']);
+
+ $services->set('cyllene_digital_sylius_axepta.command_handler.payment_request.capture', CaptureAxeptaPaymentRequestHandler::class)
+ ->args([
+ service('sylius.provider.payment_request'),
+ service('cyllene_digital_sylius_axepta.provider.credentials'),
+ service('cyllene_digital_sylius_axepta.protocol.payment_page_request_builder'),
+ service(PaymentDescriptionProviderInterface::class),
+ service('sylius_abstraction.state_machine'),
+ service('router'),
+ service('cyllene_digital_sylius_axepta.provider.return_url'),
+ service('logger'),
+ ])
+ ->tag('messenger.message_handler', ['bus' => 'sylius.payment_request.command_bus'])
+ ->tag('monolog.logger', ['channel' => '%cyllene_digital_sylius_axepta.logger_channel%']);
+
+ $services->set('cyllene_digital_sylius_axepta.command_handler.payment_request.notify', NotifyAxeptaPaymentRequestHandler::class)
+ ->args([
+ service('sylius.provider.payment_request'),
+ service('cyllene_digital_sylius_axepta.provider.credentials'),
+ service('cyllene_digital_sylius_axepta.protocol.notification_verifier'),
+ service('sylius_abstraction.state_machine'),
+ service('sylius_payum.resolver.payment_request.doctrine_proxy_object'),
+ service('logger'),
+ ])
+ ->tag('messenger.message_handler', ['bus' => 'sylius.payment_request.command_bus'])
+ ->tag('monolog.logger', ['channel' => '%cyllene_digital_sylius_axepta.logger_channel%']);
+
+ $services->set('cyllene_digital_sylius_axepta.command_handler.payment_request.status', StatusAxeptaPaymentRequestHandler::class)
+ ->args([
+ service('sylius.provider.payment_request'),
+ service('sylius_abstraction.state_machine'),
+ ])
+ ->tag('messenger.message_handler', ['bus' => 'sylius.payment_request.command_bus']);
+
+ $services->set('cyllene_digital_sylius_axepta.provider.payment_request.http_response', AxeptaHttpResponseProvider::class)
+ ->args([service('cyllene_digital_sylius_axepta.renderer.auto_submit_form')])
+ ->tag('sylius.payment_request.provider.http_response', ['gateway_factory' => 'axepta']);
+
+ // Explicit priority: in the test environment Sylius registers a `DummyNotifyPaymentProvider`
+ // whose `supports()` answers true to everything and which returns the oldest payment in the
+ // database. Without a priority it captures our notifications and ties them to the wrong payment -
+ // silently, since the response stays 200.
+ $services->set('cyllene_digital_sylius_axepta.provider.payment_request.notify_payment', AxeptaNotifyPaymentProvider::class)
+ ->args([
+ service('sylius.repository.payment'),
+ service('cyllene_digital_sylius_axepta.provider.credentials'),
+ service('cyllene_digital_sylius_axepta.protocol.notification_verifier'),
+ service(TransactionIdGeneratorInterface::class),
+ service('sylius.provider.payment_request.gateway_factory_name'),
+ 'axepta',
+ ])
+ ->tag('sylius.payment_request.payment_notify_provider', ['priority' => 10]);
+
+ // Decoration: the other gateways of the host application keep their response.
+ $services->set('cyllene_digital_sylius_axepta.provider.payment_request.notify_response', AxeptaNotifyResponseProvider::class)
+ ->decorate('sylius.provider.payment_request.notify_response')
+ ->args([
+ service('.inner'),
+ service('sylius.provider.payment_request.gateway_factory_name'),
+ 'axepta',
+ ]);
+};
diff --git a/config/services/payment_request.xml b/config/services/payment_request.xml
deleted file mode 100644
index d0425ac..0000000
--- a/config/services/payment_request.xml
+++ /dev/null
@@ -1,104 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- axepta
-
-
-
-
-
-
-
-
- axepta
-
-
-
diff --git a/config/services/payum.php b/config/services/payum.php
new file mode 100644
index 0000000..a72940b
--- /dev/null
+++ b/config/services/payum.php
@@ -0,0 +1,55 @@
+get()`: they must
+ // be public. A private action does not break at boot, it breaks on the first payment.
+ $services = $container->services()->defaults()->public();
+
+ $services->set('cyllene_digital_sylius_axepta.payum.gateway_factory_builder', AxeptaGatewayFactoryBuilder::class)
+ ->args([service('cyllene_digital_sylius_axepta.provider.credentials')])
+ ->tag('payum.gateway_factory_builder', ['factory' => 'axepta']);
+
+ $services->set('cyllene_digital_sylius_axepta.payum.action.convert_payment', ConvertPaymentAction::class)
+ ->tag('payum.action', ['factory' => 'axepta', 'alias' => 'cyllene_digital_sylius_axepta.convert_payment']);
+
+ $services->set('cyllene_digital_sylius_axepta.payum.action.capture', CaptureAction::class)
+ ->args([
+ service('payum'),
+ service('cyllene_digital_sylius_axepta.provider.return_url'),
+ service('cyllene_digital_sylius_axepta.protocol.payment_page_request_builder'),
+ service(PaymentDescriptionProviderInterface::class),
+ service('cyllene_digital_sylius_axepta.renderer.auto_submit_form'),
+ service('logger'),
+ ])
+ ->tag('payum.action', ['factory' => 'axepta', 'alias' => 'cyllene_digital_sylius_axepta.capture'])
+ ->tag('monolog.logger', ['channel' => '%cyllene_digital_sylius_axepta.logger_channel%']);
+
+ $services->set('cyllene_digital_sylius_axepta.payum.action.notify', NotifyAction::class)
+ ->args([
+ service('request_stack'),
+ service(StateMachineInterface::class),
+ service('cyllene_digital_sylius_axepta.protocol.notification_verifier'),
+ service('logger'),
+ ])
+ ->tag('payum.action', ['factory' => 'axepta', 'alias' => 'cyllene_digital_sylius_axepta.notify'])
+ ->tag('monolog.logger', ['channel' => '%cyllene_digital_sylius_axepta.logger_channel%']);
+
+ $services->set('cyllene_digital_sylius_axepta.payum.action.status', StatusAction::class)
+ ->tag('payum.action', ['factory' => 'axepta', 'alias' => 'cyllene_digital_sylius_axepta.status']);
+
+ $services->set('cyllene_digital_sylius_axepta.payum.action.resolve_next_route', ResolveNextRouteAction::class)
+ ->tag('payum.action', ['factory' => 'axepta', 'alias' => 'cyllene_digital_sylius_axepta.resolve_next_route']);
+};
diff --git a/config/services/payum.xml b/config/services/payum.xml
deleted file mode 100644
index 27b6b96..0000000
--- a/config/services/payum.xml
+++ /dev/null
@@ -1,59 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/config/services/protocol.php b/config/services/protocol.php
new file mode 100644
index 0000000..b3c9423
--- /dev/null
+++ b/config/services/protocol.php
@@ -0,0 +1,43 @@
+services();
+
+ $services->set('cyllene_digital_sylius_axepta.crypto.blowfish_factory', BlowfishEcbFactory::class);
+ $services->alias(BlowfishEcbFactoryInterface::class, 'cyllene_digital_sylius_axepta.crypto.blowfish_factory');
+
+ $services->set('cyllene_digital_sylius_axepta.protocol.transaction_id_generator', PrefixedTransactionIdGenerator::class);
+ $services->alias(TransactionIdGeneratorInterface::class, 'cyllene_digital_sylius_axepta.protocol.transaction_id_generator');
+
+ $services->set('cyllene_digital_sylius_axepta.protocol.reference_provider', PaddedReferenceProvider::class);
+ $services->alias(ReferenceProviderInterface::class, 'cyllene_digital_sylius_axepta.protocol.reference_provider');
+
+ $services->set('cyllene_digital_sylius_axepta.protocol.payment_page_request_builder', PaymentPageRequestBuilder::class)
+ ->args([
+ service(TransactionIdGeneratorInterface::class),
+ service(ReferenceProviderInterface::class),
+ ]);
+
+ $services->set('cyllene_digital_sylius_axepta.protocol.notification_verifier', NotificationVerifier::class);
+
+ $services->set('cyllene_digital_sylius_axepta.provider.credentials', CredentialsProvider::class)
+ ->args([
+ service(BlowfishEcbFactoryInterface::class),
+ param('cyllene_digital_sylius_axepta.payment_page_url'),
+ ]);
+};
diff --git a/config/services/protocol.xml b/config/services/protocol.xml
deleted file mode 100644
index d9b2548..0000000
--- a/config/services/protocol.xml
+++ /dev/null
@@ -1,41 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- %cyllene_digital_sylius_axepta.payment_page_url%
-
-
-
diff --git a/config/services/renderer.php b/config/services/renderer.php
new file mode 100644
index 0000000..e9f7c4e
--- /dev/null
+++ b/config/services/renderer.php
@@ -0,0 +1,13 @@
+services()
+ ->set('cyllene_digital_sylius_axepta.renderer.auto_submit_form', AutoSubmitFormRenderer::class)
+ ->args([service('twig')]);
+};
diff --git a/config/services/renderer.xml b/config/services/renderer.xml
deleted file mode 100644
index 826b84c..0000000
--- a/config/services/renderer.xml
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
-
-
-
-
-
-
-
diff --git a/docs/images/banner.svg b/docs/images/banner.svg
index eec5536..8825827 100644
--- a/docs/images/banner.svg
+++ b/docs/images/banner.svg
@@ -1,53 +1,36 @@
-
\ No newline at end of file
diff --git a/docs/installation.md b/docs/installation.md
index f730a43..52c4db0 100644
--- a/docs/installation.md
+++ b/docs/installation.md
@@ -4,8 +4,9 @@
| | |
|---|---|
-| PHP | `^8.2`, with `hash` and `mbstring` |
-| Sylius | `^2.1` |
+| PHP | `^8.2` (Sylius 2.3: `^8.3`), with `hash` and `mbstring` |
+| Sylius | `2.1`, `2.2` or `2.3` |
+| Symfony | `^6.4`, `^7.4`, or `^8.0` with Sylius 2.3 |
| An Axepta contract | Merchant identifier and keys, supplied by BNP Paribas |
No exotic extension: the Blowfish encryption is pure PHP, precisely so that nothing is required of
diff --git a/src/DependencyInjection/CylleneDigitalSyliusAxeptaExtension.php b/src/DependencyInjection/CylleneDigitalSyliusAxeptaExtension.php
index e1cd3de..89b8035 100644
--- a/src/DependencyInjection/CylleneDigitalSyliusAxeptaExtension.php
+++ b/src/DependencyInjection/CylleneDigitalSyliusAxeptaExtension.php
@@ -8,7 +8,7 @@
use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface;
-use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
+use Symfony\Component\DependencyInjection\Loader\PhpFileLoader;
use Symfony\Component\Yaml\Yaml;
/**
@@ -29,9 +29,9 @@ public function load(array $configs, ContainerBuilder $container): void
$container->setParameter('cyllene_digital_sylius_axepta.payment_page_url', $config['payment_page_url']);
$container->setParameter('cyllene_digital_sylius_axepta.logger_channel', $config['logger_channel']);
- $loader = new XmlFileLoader($container, new FileLocator(__DIR__ . '/../../config'));
+ $loader = new PhpFileLoader($container, new FileLocator(__DIR__ . '/../../config'));
- $loader->load('services.xml');
+ $loader->load('services.php');
}
/**
diff --git a/src/PaymentRequest/Provider/AxeptaHttpResponseProvider.php b/src/PaymentRequest/Provider/AxeptaHttpResponseProvider.php
index 3b3a3b5..9487a88 100644
--- a/src/PaymentRequest/Provider/AxeptaHttpResponseProvider.php
+++ b/src/PaymentRequest/Provider/AxeptaHttpResponseProvider.php
@@ -7,7 +7,6 @@
use CylleneDigital\SyliusAxeptaPlugin\Axepta\Protocol\PaymentPageRequest;
use CylleneDigital\SyliusAxeptaPlugin\Renderer\AutoSubmitFormRenderer;
use Sylius\Bundle\PaymentBundle\Provider\HttpResponseProviderInterface;
-use Sylius\Bundle\ResourceBundle\Controller\RequestConfiguration;
use Sylius\Component\Payment\Model\PaymentRequestInterface;
use Symfony\Component\HttpFoundation\Response;
@@ -17,6 +16,9 @@
* It is the same template as on the Payum path: the customer sees the same page whichever
* mechanism the shop settled on.
*
+ * The first argument is an `object` on purpose: Sylius 2.1 and 2.2 pass a `RequestConfiguration`,
+ * Sylius 2.3 a `Request`. Neither is read here, and the wider type satisfies both interfaces.
+ *
* @experimental
*
* @internal
@@ -27,13 +29,13 @@ public function __construct(private AutoSubmitFormRenderer $formRenderer)
{
}
- public function supports(RequestConfiguration $requestConfiguration, PaymentRequestInterface $paymentRequest): bool
+ public function supports(object $request, PaymentRequestInterface $paymentRequest): bool
{
return PaymentRequestInterface::ACTION_CAPTURE === $paymentRequest->getAction() &&
[] !== $paymentRequest->getResponseData();
}
- public function getResponse(RequestConfiguration $requestConfiguration, PaymentRequestInterface $paymentRequest): Response
+ public function getResponse(object $request, PaymentRequestInterface $paymentRequest): Response
{
$responseData = $paymentRequest->getResponseData();
diff --git a/templates/shop/payment/axepta_redirect.html.twig b/templates/shop/payment/axepta_redirect.html.twig
index fad0b8d..e30974a 100644
--- a/templates/shop/payment/axepta_redirect.html.twig
+++ b/templates/shop/payment/axepta_redirect.html.twig
@@ -1,7 +1,7 @@
{#
Transition page between the shop and the hosted payment page.
- Surchargeable : `templates/bundles/CylleneDigitalSyliusAxeptaPlugin/shop/payment/axepta_redirect.html.twig`.
+ Overridable: `templates/bundles/CylleneDigitalSyliusAxeptaPlugin/shop/payment/axepta_redirect.html.twig`.
Must stay usable without JavaScript - the `noscript` button is not decorative, it is the only
way to pay for a hardened browser or a screen reader.
@@ -11,6 +11,8 @@
+ {# Unstyled page: without it, a dark-mode browser flashes it white before the bank's page. #}
+
{{ 'cyllene_digital_sylius_axepta.ui.redirecting'|trans }}
diff --git a/tests/Behat/Context/Setup/AxeptaContext.php b/tests/Behat/Context/Setup/AxeptaContext.php
index 04c4fec..e7b1517 100644
--- a/tests/Behat/Context/Setup/AxeptaContext.php
+++ b/tests/Behat/Context/Setup/AxeptaContext.php
@@ -5,6 +5,7 @@
namespace Tests\CylleneDigital\SyliusAxeptaPlugin\Behat\Context\Setup;
use Behat\Behat\Context\Context;
+use Behat\Step\Given;
use CylleneDigital\SyliusAxeptaPlugin\Payum\AxeptaGatewayFactory;
use Sylius\Behat\Service\SharedStorageInterface;
use Sylius\Bundle\PayumBundle\Model\GatewayConfigInterface;
@@ -39,18 +40,14 @@ public function __construct(
) {
}
- /**
- * @Given the store has a payment method :name with a code :code and Axepta gateway
- * @Given /^the store has a payment method "([^"]+)" with a code "([^"]+)" and Axepta gateway in (test mode)$/
- */
+ #[Given('the store has a payment method :name with a code :code and Axepta gateway')]
+ #[Given('/^the store has a payment method "([^"]+)" with a code "([^"]+)" and Axepta gateway in (test mode)$/')]
public function theStoreHasAnAxeptaPaymentMethod(string $name, string $code, ?string $testMode = null): void
{
$this->createPaymentMethod($name, $code, null !== $testMode, usePayum: true);
}
- /**
- * @Given the store has a payment method :name with a code :code and Axepta gateway on the PaymentRequest path
- */
+ #[Given('the store has a payment method :name with a code :code and Axepta gateway on the PaymentRequest path')]
public function theStoreHasAnAxeptaPaymentMethodOnThePaymentRequestPath(string $name, string $code): void
{
$this->createPaymentMethod($name, $code, false, usePayum: false);
diff --git a/tests/Behat/Context/Ui/Admin/ManagingAxeptaPaymentMethodContext.php b/tests/Behat/Context/Ui/Admin/ManagingAxeptaPaymentMethodContext.php
index a49856a..11c613c 100644
--- a/tests/Behat/Context/Ui/Admin/ManagingAxeptaPaymentMethodContext.php
+++ b/tests/Behat/Context/Ui/Admin/ManagingAxeptaPaymentMethodContext.php
@@ -5,6 +5,8 @@
namespace Tests\CylleneDigital\SyliusAxeptaPlugin\Behat\Context\Ui\Admin;
use Behat\Behat\Context\Context;
+use Behat\Step\Then;
+use Behat\Step\When;
use Sylius\Behat\Service\Resolver\CurrentPageResolverInterface;
use Sylius\Behat\Service\SharedStorageInterface;
use Sylius\Component\Core\Model\PaymentMethodInterface;
@@ -26,9 +28,7 @@ public function __construct(
) {
}
- /**
- * @When I configure it with merchant id :merchantId, hmac key :hmacKey and blowfish key :blowfishKey
- */
+ #[When('I configure it with merchant id :merchantId, hmac key :hmacKey and blowfish key :blowfishKey')]
public function iConfigureItWithCredentials(string $merchantId, string $hmacKey, string $blowfishKey): void
{
$page = $this->currentPage();
@@ -39,17 +39,13 @@ public function iConfigureItWithCredentials(string $merchantId, string $hmacKey,
$page->specifyBlowfishKey($blowfishKey);
}
- /**
- * @When I enable its test mode
- */
+ #[When('I enable its test mode')]
public function iEnableItsTestMode(): void
{
$this->updatePage->enableTestMode();
}
- /**
- * @Then this payment method should be in test mode
- */
+ #[Then('this payment method should be in test mode')]
public function thisPaymentMethodShouldBeInTestMode(): void
{
Assert::true(
@@ -64,9 +60,8 @@ public function thisPaymentMethodShouldBeInTestMode(): void
* `PasswordType` never redisplays its value: both key fields are blank on screen, and that is
* expected. What matters is that saving without retyping them **does not erase them**, which is
* what this step checks, by reading the stored configuration back.
- *
- * @Then this payment method should still have its Axepta keys
*/
+ #[Then('this payment method should still have its Axepta keys')]
public function thisPaymentMethodShouldStillHaveItsKeys(): void
{
Assert::true(
@@ -89,25 +84,19 @@ public function thisPaymentMethodShouldStillHaveItsKeys(): void
}
}
- /**
- * @Then I should be notified that the merchant id is required
- */
+ #[Then('I should be notified that the merchant id is required')]
public function iShouldBeNotifiedThatTheMerchantIdIsRequired(): void
{
$this->assertValidationMessage('merchant ID');
}
- /**
- * @Then I should be notified that the hmac key is required
- */
+ #[Then('I should be notified that the hmac key is required')]
public function iShouldBeNotifiedThatTheHmacKeyIsRequired(): void
{
$this->assertValidationMessage('HMAC key');
}
- /**
- * @Then I should be notified that the blowfish key is required
- */
+ #[Then('I should be notified that the blowfish key is required')]
public function iShouldBeNotifiedThatTheBlowfishKeyIsRequired(): void
{
$this->assertValidationMessage('Blowfish key');
diff --git a/tests/Behat/Context/Ui/Shop/AxeptaPaymentContext.php b/tests/Behat/Context/Ui/Shop/AxeptaPaymentContext.php
index 7b8e33b..a9a7680 100644
--- a/tests/Behat/Context/Ui/Shop/AxeptaPaymentContext.php
+++ b/tests/Behat/Context/Ui/Shop/AxeptaPaymentContext.php
@@ -5,6 +5,9 @@
namespace Tests\CylleneDigital\SyliusAxeptaPlugin\Behat\Context\Ui\Shop;
use Behat\Behat\Context\Context;
+use Behat\Step\Given;
+use Behat\Step\Then;
+use Behat\Step\When;
use CylleneDigital\SyliusAxeptaPlugin\Axepta\Protocol\AxeptaCredentials;
use Doctrine\ORM\EntityManagerInterface;
use Sylius\Behat\Service\SharedStorageInterface;
@@ -47,9 +50,8 @@ public function __construct(
*
* The step is explicit rather than inferred from the checkout: what we want to exercise here is
* the notification, not walking the checkout, which Sylius already covers.
- *
- * @Given the shop has sent the payment request to the bank
*/
+ #[Given('the shop has sent the payment request to the bank')]
public function theShopHasSentThePaymentRequestToTheBank(): void
{
$order = $this->order();
@@ -67,9 +69,7 @@ public function theShopHasSentThePaymentRequestToTheBank(): void
$this->paymentRequestAnnouncer->dispatchPaymentRequestCommand($paymentRequest);
}
- /**
- * @Then the payment request should target the Axepta payment page
- */
+ #[Then('the payment request should target the Axepta payment page')]
public function thePaymentRequestShouldTargetTheAxeptaPaymentPage(): void
{
Assert::same($this->captureResponseData()['url'] ?? null, AxeptaCredentials::DEFAULT_PAYMENT_PAGE_URL);
@@ -78,9 +78,8 @@ public function thePaymentRequestShouldTargetTheAxeptaPaymentPage(): void
/**
* The fields go out as such in the self-submitting form; their rendering is covered by the
* integration tests.
- *
- * @Then it should carry a signed payload
*/
+ #[Then('it should carry a signed payload')]
public function itShouldCarryASignedPayload(): void
{
$fields = $this->captureResponseData()['fields'] ?? [];
@@ -100,9 +99,7 @@ private function captureResponseData(): array
return $this->capture()->getResponseData();
}
- /**
- * @When /^the bank notifies the shop that the payment (succeeded|was refused)$/
- */
+ #[When('/^the bank notifies the shop that the payment (succeeded|was refused)$/')]
public function theBankNotifiesTheShop(string $outcome): void
{
$notification = $this->mocker->notification(
@@ -116,9 +113,7 @@ public function theBankNotifiesTheShop(string $outcome): void
$this->sendNotification($notification);
}
- /**
- * @When the bank sends the same notification again
- */
+ #[When('the bank sends the same notification again')]
public function theBankSendsTheSameNotificationAgain(): void
{
/** @var array $notification */
@@ -127,17 +122,13 @@ public function theBankSendsTheSameNotificationAgain(): void
$this->sendNotification($notification);
}
- /**
- * @When someone sends a notification with an invalid signature
- */
+ #[When('someone sends a notification with an invalid signature')]
public function someoneSendsAForgedNotification(): void
{
$this->sendNotification($this->mocker->forgedNotification($this->paymentMethod(), $this->transactionId()));
}
- /**
- * @Then the shop should have answered the bank without an error
- */
+ #[Then('the shop should have answered the bank without an error')]
public function theShopShouldHaveAnsweredWithoutError(): void
{
$status = $this->sharedStorage->get('axepta_notification_status');
@@ -148,25 +139,19 @@ public function theShopShouldHaveAnsweredWithoutError(): void
));
}
- /**
- * @Then my order should be paid
- */
+ #[Then('my order should be paid')]
public function myOrderShouldBePaid(): void
{
Assert::same($this->reloadPaymentState(), OrderPaymentStates::STATE_PAID);
}
- /**
- * @Then my order should not be paid
- */
+ #[Then('my order should not be paid')]
public function myOrderShouldNotBePaid(): void
{
Assert::notSame($this->reloadPaymentState(), OrderPaymentStates::STATE_PAID);
}
- /**
- * @Then I should be able to pay for my order again
- */
+ #[Then('I should be able to pay for my order again')]
public function iShouldBeAbleToPayForMyOrderAgain(): void
{
Assert::notSame(
diff --git a/tests/Behat/Resources/services.php b/tests/Behat/Resources/services.php
new file mode 100644
index 0000000..f98fe7c
--- /dev/null
+++ b/tests/Behat/Resources/services.php
@@ -0,0 +1,72 @@
+services()->defaults()->public();
+
+ // Pages: they only add the Axepta configuration fields to the Sylius form.
+ $services->set('cyllene_digital_sylius_axepta.behat.page.admin.payment_method.create', AxeptaCreatePage::class)
+ ->parent('sylius.behat.page.admin.crud.create')
+ ->args(['sylius_admin_payment_method_create']);
+
+ $services->set('cyllene_digital_sylius_axepta.behat.page.admin.payment_method.update', AxeptaUpdatePage::class)
+ ->parent('sylius.behat.page.admin.crud.update')
+ ->args(['sylius_admin_payment_method_update']);
+
+ $services->set('cyllene_digital_sylius_axepta.behat.mocker.payment_page', AxeptaPaymentPageMocker::class)
+ ->args([service('cyllene_digital_sylius_axepta.provider.credentials')]);
+
+ $services->set('cyllene_digital_sylius_axepta.behat.context.setup.axepta', AxeptaContext::class)
+ ->args([
+ service('sylius.behat.shared_storage'),
+ service('sylius.factory.payment_method'),
+ service('sylius.factory.gateway_config'),
+ service('sylius.repository.payment_method'),
+ ]);
+
+ // The Sylius context receives a hard-coded collection of factories, holding only `offline`: it
+ // therefore cannot build the creation URL for ours, and opens "/admin/payment-methods/new/" with
+ // no parameter - a 404. We redeclare it with the extended collection rather than writing our own
+ // creation steps.
+ $services->set('cyllene_digital_sylius_axepta.behat.context.ui.admin.managing_payment_methods', ManagingPaymentMethodsContext::class)
+ ->args([
+ service('cyllene_digital_sylius_axepta.behat.page.admin.payment_method.create'),
+ service('sylius.behat.page.admin.payment_method.index'),
+ service('cyllene_digital_sylius_axepta.behat.page.admin.payment_method.update'),
+ service('sylius.behat.current_page_resolver'),
+ [
+ 'offline' => 'Offline',
+ 'axepta' => 'Axepta - BNP Paribas',
+ ],
+ ]);
+
+ $services->set('cyllene_digital_sylius_axepta.behat.context.ui.admin.managing_payment_method', ManagingAxeptaPaymentMethodContext::class)
+ ->args([
+ service('cyllene_digital_sylius_axepta.behat.page.admin.payment_method.create'),
+ service('cyllene_digital_sylius_axepta.behat.page.admin.payment_method.update'),
+ service('sylius.behat.current_page_resolver'),
+ service('sylius.behat.shared_storage'),
+ ]);
+
+ $services->set('cyllene_digital_sylius_axepta.behat.context.ui.shop.payment', AxeptaPaymentContext::class)
+ ->args([
+ service('sylius.behat.shared_storage'),
+ service('cyllene_digital_sylius_axepta.behat.mocker.payment_page'),
+ service('kernel'),
+ service('sylius.repository.payment_request'),
+ service('doctrine.orm.entity_manager'),
+ service('sylius.factory.payment_request'),
+ service('sylius.announcer.payment_request'),
+ ]);
+};
diff --git a/tests/Behat/Resources/services.xml b/tests/Behat/Resources/services.xml
deleted file mode 100644
index 34a4f68..0000000
--- a/tests/Behat/Resources/services.xml
+++ /dev/null
@@ -1,70 +0,0 @@
-
-
-
-
-
-
-
-
- sylius_admin_payment_method_create
-
-
-
- sylius_admin_payment_method_update
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Offline
- Axepta - BNP Paribas
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/tests/Behat/Resources/suites.php b/tests/Behat/Resources/suites.php
new file mode 100644
index 0000000..2145e01
--- /dev/null
+++ b/tests/Behat/Resources/suites.php
@@ -0,0 +1,92 @@
+ [
+ 'suites' => [
+ 'ui_managing_axepta_payment_method' => [
+ 'contexts' => [
+ 'sylius.behat.context.hook.bad_gateway',
+ 'sylius.behat.context.hook.doctrine_orm',
+ 'sylius.behat.context.hook.session',
+
+ 'sylius.behat.context.transform.lexical',
+ 'sylius.behat.context.transform.locale',
+ 'sylius.behat.context.transform.payment',
+ 'sylius.behat.context.transform.shared_storage',
+
+ 'sylius.behat.context.setup.admin_security',
+ 'sylius.behat.context.setup.channel',
+ 'sylius.behat.context.setup.currency',
+ 'sylius.behat.context.setup.locale',
+ 'sylius.behat.context.setup.payment',
+ 'cyllene_digital_sylius_axepta.behat.context.setup.axepta',
+
+ 'cyllene_digital_sylius_axepta.behat.context.ui.admin.managing_payment_methods',
+ 'sylius.behat.context.ui.admin.notification',
+ 'sylius.behat.context.ui.save',
+ 'cyllene_digital_sylius_axepta.behat.context.ui.admin.managing_payment_method',
+ ],
+ 'filters' => [
+ 'tags' => '@managing_axepta_payment_method&&@ui',
+ ],
+ ],
+ 'ui_paying_with_axepta' => [
+ 'contexts' => [
+ 'sylius.behat.context.hook.bad_gateway',
+ 'sylius.behat.context.hook.doctrine_orm',
+ 'sylius.behat.context.hook.guest_cart',
+ 'sylius.behat.context.hook.session',
+
+ 'sylius.behat.context.transform.address',
+ 'sylius.behat.context.transform.channel',
+ 'sylius.behat.context.transform.country',
+ 'sylius.behat.context.transform.customer',
+ 'sylius.behat.context.transform.lexical',
+ 'sylius.behat.context.transform.locale',
+ 'sylius.behat.context.transform.order',
+ 'sylius.behat.context.transform.payment',
+ 'sylius.behat.context.transform.product',
+ 'sylius.behat.context.transform.product_variant',
+ 'sylius.behat.context.transform.shared_storage',
+ 'sylius.behat.context.transform.shipping_method',
+ 'sylius.behat.context.transform.tax_category',
+ 'sylius.behat.context.transform.user',
+ 'sylius.behat.context.transform.zone',
+
+ 'sylius.behat.context.setup.cart',
+ 'sylius.behat.context.setup.channel',
+ 'sylius.behat.context.setup.checkout',
+ 'sylius.behat.context.setup.checkout.address',
+ 'sylius.behat.context.setup.currency',
+ 'sylius.behat.context.setup.geographical',
+ 'sylius.behat.context.setup.locale',
+ 'sylius.behat.context.setup.order',
+ 'sylius.behat.context.setup.payment',
+ 'sylius.behat.context.setup.product',
+ 'sylius.behat.context.setup.shipping',
+ 'sylius.behat.context.setup.shop_security',
+ 'sylius.behat.context.setup.user',
+ 'sylius.behat.context.setup.zone',
+ 'cyllene_digital_sylius_axepta.behat.context.setup.axepta',
+
+ 'sylius.behat.context.ui.shop.cart',
+ 'sylius.behat.context.ui.shop.checkout',
+ 'sylius.behat.context.ui.shop.checkout.addressing',
+ 'sylius.behat.context.ui.shop.checkout.complete',
+ 'sylius.behat.context.ui.shop.checkout.order_details',
+ 'sylius.behat.context.ui.shop.checkout.payment',
+ 'sylius.behat.context.ui.shop.checkout.shipping',
+ 'sylius.behat.context.ui.shop.checkout.thank_you',
+ 'cyllene_digital_sylius_axepta.behat.context.ui.shop.payment',
+ ],
+ 'filters' => [
+ 'tags' => '@paying_with_axepta&&@ui',
+ ],
+ ],
+ ],
+ ],
+]);
diff --git a/tests/Behat/Resources/suites.yml b/tests/Behat/Resources/suites.yml
deleted file mode 100644
index 3b870a4..0000000
--- a/tests/Behat/Resources/suites.yml
+++ /dev/null
@@ -1,79 +0,0 @@
-default:
- suites:
- ui_managing_axepta_payment_method:
- contexts:
- - sylius.behat.context.hook.bad_gateway
- - sylius.behat.context.hook.doctrine_orm
- - sylius.behat.context.hook.session
-
- - sylius.behat.context.transform.lexical
- - sylius.behat.context.transform.locale
- - sylius.behat.context.transform.payment
- - sylius.behat.context.transform.shared_storage
-
- - sylius.behat.context.setup.admin_security
- - sylius.behat.context.setup.channel
- - sylius.behat.context.setup.currency
- - sylius.behat.context.setup.locale
- - sylius.behat.context.setup.payment
- - cyllene_digital_sylius_axepta.behat.context.setup.axepta
-
- - cyllene_digital_sylius_axepta.behat.context.ui.admin.managing_payment_methods
- - sylius.behat.context.ui.admin.notification
- - sylius.behat.context.ui.save
- - cyllene_digital_sylius_axepta.behat.context.ui.admin.managing_payment_method
-
- filters:
- tags: "@managing_axepta_payment_method&&@ui"
-
- ui_paying_with_axepta:
- contexts:
- - sylius.behat.context.hook.bad_gateway
- - sylius.behat.context.hook.doctrine_orm
- - sylius.behat.context.hook.guest_cart
- - sylius.behat.context.hook.session
-
- - sylius.behat.context.transform.address
- - sylius.behat.context.transform.channel
- - sylius.behat.context.transform.country
- - sylius.behat.context.transform.customer
- - sylius.behat.context.transform.lexical
- - sylius.behat.context.transform.locale
- - sylius.behat.context.transform.order
- - sylius.behat.context.transform.payment
- - sylius.behat.context.transform.product
- - sylius.behat.context.transform.product_variant
- - sylius.behat.context.transform.shared_storage
- - sylius.behat.context.transform.shipping_method
- - sylius.behat.context.transform.tax_category
- - sylius.behat.context.transform.user
- - sylius.behat.context.transform.zone
-
- - sylius.behat.context.setup.cart
- - sylius.behat.context.setup.channel
- - sylius.behat.context.setup.checkout
- - sylius.behat.context.setup.checkout.address
- - sylius.behat.context.setup.currency
- - sylius.behat.context.setup.geographical
- - sylius.behat.context.setup.locale
- - sylius.behat.context.setup.order
- - sylius.behat.context.setup.payment
- - sylius.behat.context.setup.product
- - sylius.behat.context.setup.shipping
- - sylius.behat.context.setup.shop_security
- - sylius.behat.context.setup.user
- - sylius.behat.context.setup.zone
- - cyllene_digital_sylius_axepta.behat.context.setup.axepta
-
- - sylius.behat.context.ui.shop.cart
- - sylius.behat.context.ui.shop.checkout
- - sylius.behat.context.ui.shop.checkout.addressing
- - sylius.behat.context.ui.shop.checkout.complete
- - sylius.behat.context.ui.shop.checkout.order_details
- - sylius.behat.context.ui.shop.checkout.payment
- - sylius.behat.context.ui.shop.checkout.shipping
- - sylius.behat.context.ui.shop.checkout.thank_you
- - cyllene_digital_sylius_axepta.behat.context.ui.shop.payment
-
- filters:
- tags: "@paying_with_axepta&&@ui"
diff --git a/tests/TestApplication/config/services_test.php b/tests/TestApplication/config/services_test.php
index a6d4814..88138be 100644
--- a/tests/TestApplication/config/services_test.php
+++ b/tests/TestApplication/config/services_test.php
@@ -6,7 +6,9 @@
return function (ContainerConfigurator $container) {
if (str_starts_with($container->env(), 'test')) {
- $container->import('../../../vendor/sylius/sylius/src/Sylius/Behat/Resources/config/services.xml');
- $container->import('@CylleneDigitalSyliusAxeptaPlugin/tests/Behat/Resources/services.xml');
+ // Sylius 2.3 ships its Behat services in PHP; 2.1 and 2.2 only in XML, which Symfony 8 cannot load.
+ $syliusBehatServices = dirname(__DIR__, 3) . '/vendor/sylius/sylius/src/Sylius/Behat/Resources/config/services';
+ $container->import(is_file($syliusBehatServices . '.php') ? $syliusBehatServices . '.php' : $syliusBehatServices . '.xml');
+ $container->import('@CylleneDigitalSyliusAxeptaPlugin/tests/Behat/Resources/services.php');
}
};