diff --git a/CHANGELOG.md b/CHANGELOG.md index 32548821..3cf7bb87 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - **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. @@ -43,6 +45,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 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 @@ -59,8 +72,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 | `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.1.2` (for the nullable `TokenOutput::$idToken`). +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 diff --git a/composer.json b/composer.json index a9069864..3ea65550 100755 --- a/composer.json +++ b/composer.json @@ -14,7 +14,7 @@ "ext-json": "*", "giggsey/libphonenumber-for-php": "^8.12", "payplug/payplug-php": "^4.0", - "payplug/unified-plugin-core": "^1.1.3", + "payplug/unified-plugin-core": "^1.2.1", "php-http/message-factory": "^1.1", "sylius/refund-plugin": "^2.0", "sylius/sylius": "^2.0", diff --git a/composer.lock b/composer.lock index 1d20f3e4..15ccf32d 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "dcc0d25a7d712c2284f331538785b388", + "content-hash": "39a63c300688d00971a9a43114e99ec7", "packages": [ { "name": "alcohol/iso4217", @@ -70,7 +70,7 @@ }, { "name": "api-platform/doctrine-common", - "version": "v4.3.20", + "version": "v4.3.21", "source": { "type": "git", "url": "https://github.com/api-platform/doctrine-common.git", @@ -154,13 +154,13 @@ "rest" ], "support": { - "source": "https://github.com/api-platform/doctrine-common/tree/v4.3.20" + "source": "https://github.com/api-platform/doctrine-common/tree/v4.3.21" }, "time": "2026-09-12T13:19:57+00:00" }, { "name": "api-platform/doctrine-orm", - "version": "v4.3.20", + "version": "v4.3.21", "source": { "type": "git", "url": "https://github.com/api-platform/doctrine-orm.git", @@ -243,13 +243,13 @@ "rest" ], "support": { - "source": "https://github.com/api-platform/doctrine-orm/tree/v4.3.20" + "source": "https://github.com/api-platform/doctrine-orm/tree/v4.3.21" }, "time": "2026-09-09T07:14:18+00:00" }, { "name": "api-platform/documentation", - "version": "v4.3.20", + "version": "v4.3.21", "source": { "type": "git", "url": "https://github.com/api-platform/documentation.git", @@ -312,7 +312,7 @@ }, { "name": "api-platform/http-cache", - "version": "v4.3.20", + "version": "v4.3.21", "source": { "type": "git", "url": "https://github.com/api-platform/http-cache.git", @@ -392,7 +392,7 @@ }, { "name": "api-platform/hydra", - "version": "v4.3.20", + "version": "v4.3.21", "source": { "type": "git", "url": "https://github.com/api-platform/hydra.git", @@ -473,13 +473,13 @@ "rest" ], "support": { - "source": "https://github.com/api-platform/hydra/tree/v4.3.20" + "source": "https://github.com/api-platform/hydra/tree/v4.3.21" }, "time": "2026-08-16T15:12:48+00:00" }, { "name": "api-platform/json-schema", - "version": "v4.3.20", + "version": "v4.3.21", "source": { "type": "git", "url": "https://github.com/api-platform/json-schema.git", @@ -554,13 +554,13 @@ "swagger" ], "support": { - "source": "https://github.com/api-platform/json-schema/tree/v4.3.20" + "source": "https://github.com/api-platform/json-schema/tree/v4.3.21" }, "time": "2026-09-02T08:21:34+00:00" }, { "name": "api-platform/jsonld", - "version": "v4.3.20", + "version": "v4.3.21", "source": { "type": "git", "url": "https://github.com/api-platform/jsonld.git", @@ -634,22 +634,22 @@ "rest" ], "support": { - "source": "https://github.com/api-platform/jsonld/tree/v4.3.20" + "source": "https://github.com/api-platform/jsonld/tree/v4.3.21" }, "time": "2026-06-13T05:11:46+00:00" }, { "name": "api-platform/metadata", - "version": "v4.3.20", + "version": "v4.3.21", "source": { "type": "git", "url": "https://github.com/api-platform/metadata.git", - "reference": "4cbdad3301afbade9f9dc46f75246fad25fe4aeb" + "reference": "6493d73e04649e07cff914db3f2c178abeae3278" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/api-platform/metadata/zipball/4cbdad3301afbade9f9dc46f75246fad25fe4aeb", - "reference": "4cbdad3301afbade9f9dc46f75246fad25fe4aeb", + "url": "https://api.github.com/repos/api-platform/metadata/zipball/6493d73e04649e07cff914db3f2c178abeae3278", + "reference": "6493d73e04649e07cff914db3f2c178abeae3278", "shasum": "" }, "require": { @@ -732,13 +732,13 @@ "swagger" ], "support": { - "source": "https://github.com/api-platform/metadata/tree/v4.3.20" + "source": "https://github.com/api-platform/metadata/tree/v4.3.21" }, - "time": "2026-09-11T12:56:16+00:00" + "time": "2026-09-29T18:57:50+00:00" }, { "name": "api-platform/openapi", - "version": "v4.3.20", + "version": "v4.3.21", "source": { "type": "git", "url": "https://github.com/api-platform/openapi.git", @@ -823,13 +823,13 @@ "swagger" ], "support": { - "source": "https://github.com/api-platform/openapi/tree/v4.3.20" + "source": "https://github.com/api-platform/openapi/tree/v4.3.21" }, "time": "2026-06-16T10:01:53+00:00" }, { "name": "api-platform/serializer", - "version": "v4.3.20", + "version": "v4.3.21", "source": { "type": "git", "url": "https://github.com/api-platform/serializer.git", @@ -917,13 +917,13 @@ "serializer" ], "support": { - "source": "https://github.com/api-platform/serializer/tree/v4.3.20" + "source": "https://github.com/api-platform/serializer/tree/v4.3.21" }, "time": "2026-09-09T07:14:58+00:00" }, { "name": "api-platform/state", - "version": "v4.3.20", + "version": "v4.3.21", "source": { "type": "git", "url": "https://github.com/api-platform/state.git", @@ -1014,22 +1014,22 @@ "swagger" ], "support": { - "source": "https://github.com/api-platform/state/tree/v4.3.20" + "source": "https://github.com/api-platform/state/tree/v4.3.21" }, "time": "2026-09-12T13:42:25+00:00" }, { "name": "api-platform/symfony", - "version": "v4.3.20", + "version": "v4.3.21", "source": { "type": "git", "url": "https://github.com/api-platform/symfony.git", - "reference": "c158ab345142aeee5335050034818da234fd16e9" + "reference": "04aec51ec642c202cce0c476cf93ff0b0ce76ab9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/api-platform/symfony/zipball/c158ab345142aeee5335050034818da234fd16e9", - "reference": "c158ab345142aeee5335050034818da234fd16e9", + "url": "https://api.github.com/repos/api-platform/symfony/zipball/04aec51ec642c202cce0c476cf93ff0b0ce76ab9", + "reference": "04aec51ec642c202cce0c476cf93ff0b0ce76ab9", "shasum": "" }, "require": { @@ -1143,13 +1143,13 @@ "symfony" ], "support": { - "source": "https://github.com/api-platform/symfony/tree/v4.3.20" + "source": "https://github.com/api-platform/symfony/tree/v4.3.21" }, - "time": "2026-09-24T21:51:53+00:00" + "time": "2026-09-29T14:56:10+00:00" }, { "name": "api-platform/validator", - "version": "v4.3.20", + "version": "v4.3.21", "source": { "type": "git", "url": "https://github.com/api-platform/validator.git", @@ -1219,7 +1219,7 @@ "validator" ], "support": { - "source": "https://github.com/api-platform/validator/tree/v4.3.20" + "source": "https://github.com/api-platform/validator/tree/v4.3.21" }, "time": "2026-09-05T06:05:04+00:00" }, @@ -1487,16 +1487,16 @@ }, { "name": "composer/semver", - "version": "3.5.0", + "version": "3.5.1", "source": { "type": "git", "url": "https://github.com/composer/semver.git", - "reference": "f7a296f4c4cf8cb8bb83e35d6951a406bb11afa5" + "reference": "528948210cc46f967a4b98e83b19e76f2b504993" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/semver/zipball/f7a296f4c4cf8cb8bb83e35d6951a406bb11afa5", - "reference": "f7a296f4c4cf8cb8bb83e35d6951a406bb11afa5", + "url": "https://api.github.com/repos/composer/semver/zipball/528948210cc46f967a4b98e83b19e76f2b504993", + "reference": "528948210cc46f967a4b98e83b19e76f2b504993", "shasum": "" }, "require": { @@ -1548,7 +1548,7 @@ "support": { "irc": "ircs://irc.libera.chat:6697/composer", "issues": "https://github.com/composer/semver/issues", - "source": "https://github.com/composer/semver/tree/3.5.0" + "source": "https://github.com/composer/semver/tree/3.5.1" }, "funding": [ { @@ -1560,7 +1560,7 @@ "type": "github" } ], - "time": "2026-09-24T14:38:51+00:00" + "time": "2026-10-05T13:12:12+00:00" }, { "name": "doctrine/collections", @@ -2456,16 +2456,16 @@ }, { "name": "doctrine/lexer", - "version": "3.0.2", + "version": "3.0.3", "source": { "type": "git", "url": "https://github.com/doctrine/lexer.git", - "reference": "e96fe45e92a54233726014a7cc7340abf29bb14c" + "reference": "71d305eaa6480d3d3460c7d053d1da7749fa9cca" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/lexer/zipball/e96fe45e92a54233726014a7cc7340abf29bb14c", - "reference": "e96fe45e92a54233726014a7cc7340abf29bb14c", + "url": "https://api.github.com/repos/doctrine/lexer/zipball/71d305eaa6480d3d3460c7d053d1da7749fa9cca", + "reference": "71d305eaa6480d3d3460c7d053d1da7749fa9cca", "shasum": "" }, "require": { @@ -2511,7 +2511,7 @@ ], "support": { "issues": "https://github.com/doctrine/lexer/issues", - "source": "https://github.com/doctrine/lexer/tree/3.0.2" + "source": "https://github.com/doctrine/lexer/tree/3.0.3" }, "funding": [ { @@ -2527,20 +2527,20 @@ "type": "tidelift" } ], - "time": "2026-06-14T20:44:06+00:00" + "time": "2026-10-01T06:59:29+00:00" }, { "name": "doctrine/migrations", - "version": "3.9.7", + "version": "3.9.8", "source": { "type": "git", "url": "https://github.com/doctrine/migrations.git", - "reference": "96cb2a89b56c9efb0bac38e606dc0b0f13e650ec" + "reference": "eb0b9ce3f999d720fa73037bbd06977ea34b76a0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/migrations/zipball/96cb2a89b56c9efb0bac38e606dc0b0f13e650ec", - "reference": "96cb2a89b56c9efb0bac38e606dc0b0f13e650ec", + "url": "https://api.github.com/repos/doctrine/migrations/zipball/eb0b9ce3f999d720fa73037bbd06977ea34b76a0", + "reference": "eb0b9ce3f999d720fa73037bbd06977ea34b76a0", "shasum": "" }, "require": { @@ -2614,7 +2614,7 @@ ], "support": { "issues": "https://github.com/doctrine/migrations/issues", - "source": "https://github.com/doctrine/migrations/tree/3.9.7" + "source": "https://github.com/doctrine/migrations/tree/3.9.8" }, "funding": [ { @@ -2630,7 +2630,7 @@ "type": "tidelift" } ], - "time": "2026-04-23T19:33:20+00:00" + "time": "2026-10-04T12:35:58+00:00" }, { "name": "doctrine/orm", @@ -3086,20 +3086,20 @@ }, { "name": "gedmo/doctrine-extensions", - "version": "v3.22.1", + "version": "v3.22.2", "source": { "type": "git", "url": "https://github.com/doctrine-extensions/DoctrineExtensions.git", - "reference": "a973a68a990392b96f5ccbd4d713ae33e4bde95f" + "reference": "486719169b3042dd4cdb801f70651f30723aab9e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine-extensions/DoctrineExtensions/zipball/a973a68a990392b96f5ccbd4d713ae33e4bde95f", - "reference": "a973a68a990392b96f5ccbd4d713ae33e4bde95f", + "url": "https://api.github.com/repos/doctrine-extensions/DoctrineExtensions/zipball/486719169b3042dd4cdb801f70651f30723aab9e", + "reference": "486719169b3042dd4cdb801f70651f30723aab9e", "shasum": "" }, "require": { - "doctrine/collections": "^1.2 || ^2.0", + "doctrine/collections": "^1.2 || ^2.0 || ^3.1", "doctrine/deprecations": "^1.0", "doctrine/event-manager": "^1.2 || ^2.0", "doctrine/persistence": "^2.2 || ^3.0 || ^4.0", @@ -3194,7 +3194,7 @@ "support": { "docs": "https://github.com/doctrine-extensions/DoctrineExtensions/tree/main/doc", "issues": "https://github.com/doctrine-extensions/DoctrineExtensions/issues", - "source": "https://github.com/doctrine-extensions/DoctrineExtensions/tree/v3.22.1" + "source": "https://github.com/doctrine-extensions/DoctrineExtensions/tree/v3.22.2" }, "funding": [ { @@ -3214,7 +3214,7 @@ "type": "github" } ], - "time": "2026-08-01T13:57:40+00:00" + "time": "2026-09-29T18:56:23+00:00" }, { "name": "giggsey/libphonenumber-for-php", @@ -4976,22 +4976,22 @@ }, { "name": "liip/imagine-bundle", - "version": "2.17.2", + "version": "2.18.1", "source": { "type": "git", "url": "https://github.com/liip/LiipImagineBundle.git", - "reference": "548846e07d1b928890772d245c2e31aaa45706b5" + "reference": "3c799cf7c56b87166f82c6c48c082cd95977cf6c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/liip/LiipImagineBundle/zipball/548846e07d1b928890772d245c2e31aaa45706b5", - "reference": "548846e07d1b928890772d245c2e31aaa45706b5", + "url": "https://api.github.com/repos/liip/LiipImagineBundle/zipball/3c799cf7c56b87166f82c6c48c082cd95977cf6c", + "reference": "3c799cf7c56b87166f82c6c48c082cd95977cf6c", "shasum": "" }, "require": { "ext-mbstring": "*", "imagine/imagine": "^1.3.2", - "php": "^8.0", + "php": "^8.1", "symfony/dependency-injection": "^5.4|^6.4|^7.4|^8.0", "symfony/deprecation-contracts": "^2.5 || ^3", "symfony/filesystem": "^5.4|^6.4|^7.3|^8.0", @@ -5078,9 +5078,9 @@ ], "support": { "issues": "https://github.com/liip/LiipImagineBundle/issues", - "source": "https://github.com/liip/LiipImagineBundle/tree/2.17.2" + "source": "https://github.com/liip/LiipImagineBundle/tree/2.18.1" }, - "time": "2026-08-05T12:56:26+00:00" + "time": "2026-10-07T20:50:10+00:00" }, { "name": "marcj/topsort", @@ -5135,16 +5135,16 @@ }, { "name": "monolog/monolog", - "version": "3.12.0", + "version": "3.12.1", "source": { "type": "git", "url": "https://github.com/Seldaek/monolog.git", - "reference": "72c534fc0ab181ef52d92a68382318631e301608" + "reference": "d7059e4c691d05ea1222b5124bed0f961be46e15" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Seldaek/monolog/zipball/72c534fc0ab181ef52d92a68382318631e301608", - "reference": "72c534fc0ab181ef52d92a68382318631e301608", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/d7059e4c691d05ea1222b5124bed0f961be46e15", + "reference": "d7059e4c691d05ea1222b5124bed0f961be46e15", "shasum": "" }, "require": { @@ -5224,7 +5224,7 @@ ], "support": { "issues": "https://github.com/Seldaek/monolog/issues", - "source": "https://github.com/Seldaek/monolog/tree/3.12.0" + "source": "https://github.com/Seldaek/monolog/tree/3.12.1" }, "funding": [ { @@ -5236,7 +5236,7 @@ "type": "tidelift" } ], - "time": "2026-09-09T08:34:20+00:00" + "time": "2026-09-29T19:21:12+00:00" }, { "name": "myclabs/php-enum", @@ -5748,16 +5748,16 @@ }, { "name": "payplug/unified-plugin-core", - "version": "1.3.0", + "version": "1.2.1", "source": { "type": "git", "url": "https://github.com/payplug/unified-plugin-core.git", - "reference": "050f14e31c88e3ac3e50a2ec4707776110e1dc7f" + "reference": "05423ee4bae1bef5e1374ba1aeadf127c9bb0dca" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/payplug/unified-plugin-core/zipball/050f14e31c88e3ac3e50a2ec4707776110e1dc7f", - "reference": "050f14e31c88e3ac3e50a2ec4707776110e1dc7f", + "url": "https://api.github.com/repos/payplug/unified-plugin-core/zipball/05423ee4bae1bef5e1374ba1aeadf127c9bb0dca", + "reference": "05423ee4bae1bef5e1374ba1aeadf127c9bb0dca", "shasum": "" }, "require": { @@ -5794,9 +5794,9 @@ "description": "Core foundations shared library for Payplug e-commerce plugins.", "support": { "issues": "https://github.com/payplug/unified-plugin-core/issues", - "source": "https://github.com/payplug/unified-plugin-core/tree/1.3.0" + "source": "https://github.com/payplug/unified-plugin-core/tree/1.2.1" }, - "time": "2026-09-29T08:57:15+00:00" + "time": "2026-10-08T07:20:41+00:00" }, { "name": "payum/core", @@ -7759,19 +7759,21 @@ }, { "name": "sylius/refund-plugin", - "version": "v2.1.0", + "version": "v2.2.1", "source": { "type": "git", "url": "https://github.com/Sylius/RefundPlugin.git", - "reference": "5161e116d02ee7398182858be0e892d7f6a31172" + "reference": "0ca6789ac408f43e05452ee8595c1fe219c3c3d2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Sylius/RefundPlugin/zipball/5161e116d02ee7398182858be0e892d7f6a31172", - "reference": "5161e116d02ee7398182858be0e892d7f6a31172", + "url": "https://api.github.com/repos/Sylius/RefundPlugin/zipball/0ca6789ac408f43e05452ee8595c1fe219c3c3d2", + "reference": "0ca6789ac408f43e05452ee8595c1fe219c3c3d2", "shasum": "" }, "require": { + "knplabs/gaufrette": "^0.11 || ^1.0", + "knplabs/knp-gaufrette-bundle": "^0.9 || ^1.0", "knplabs/knp-snappy-bundle": "^1.10", "myclabs/php-enum": "^1.8", "php": "^8.2", @@ -7780,32 +7782,31 @@ "sylius/resource-bundle": "^1.12", "sylius/sylius": "^2.0", "sylius/telemetry": "^1.0", - "symfony/messenger": "^6.4 || ^7.4" + "symfony/messenger": "^6.4 || ^7.4 || ^8.0" }, "require-dev": { - "behat/behat": "^3.6.1", - "dmore/behat-chrome-extension": "^1.3", + "behat/behat": "^3.22 || 4.0.0-alpha1", + "behat/mink": "^1.10 || ^1.13", "dmore/chrome-mink-driver": "^2.7", - "friends-of-behat/mink": "^1.8", - "friends-of-behat/mink-browserkit-driver": "^1.4", - "friends-of-behat/mink-debug-extension": "^2.0", - "friends-of-behat/mink-extension": "^2.4", + "friends-of-behat/mink-browserkit-driver": "^1.6.3", + "friends-of-behat/mink-debug-extension": "^2.2", + "friends-of-behat/mink-extension": "^2.7 || 3.0.0-alpha1", "friends-of-behat/page-object-extension": "^0.3", - "friends-of-behat/suite-settings-extension": "^1.0", - "friends-of-behat/symfony-extension": "^2.1", - "friends-of-behat/variadic-extension": "^1.3", - "matthiasnoback/symfony-config-test": "^5.1", - "matthiasnoback/symfony-dependency-injection-test": "^5.1", - "phpstan/phpstan": "^1.6", - "phpstan/phpstan-webmozart-assert": "^1.1", + "friends-of-behat/symfony-extension": "^2.6.2", + "friends-of-behat/variadic-extension": "^1.6", + "matthiasnoback/symfony-config-test": "^6.1", + "matthiasnoback/symfony-dependency-injection-test": "^6.2", + "phpstan/phpstan": "^2.2", + "phpstan/phpstan-webmozart-assert": "^2.0", "phpunit/phpunit": "^10.5", + "sylius-labs/behat-chrome-extension": "^1.5", "sylius-labs/coding-standard": "^4.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/intl": "^6.4 || ^7.4", - "symfony/web-profiler-bundle": "^6.4 || ^7.4", + "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/intl": "^6.4 || ^7.4 || ^8.0", + "symfony/web-profiler-bundle": "^6.4 || ^7.4 || ^8.0", "symfony/webpack-encore-bundle": "^2.2" }, "type": "sylius-plugin", @@ -7853,7 +7854,7 @@ ], "support": { "issues": "https://github.com/Sylius/RefundPlugin/issues", - "source": "https://github.com/Sylius/RefundPlugin/tree/v2.1.0" + "source": "https://github.com/Sylius/RefundPlugin/tree/v2.2.1" }, "funding": [ { @@ -7861,7 +7862,7 @@ "type": "github" } ], - "time": "2026-05-06T06:07:12+00:00" + "time": "2026-10-06T09:28:15+00:00" }, { "name": "sylius/registry", @@ -8748,16 +8749,16 @@ }, { "name": "symfony/cache", - "version": "v6.4.46", + "version": "v6.4.47", "source": { "type": "git", "url": "https://github.com/symfony/cache.git", - "reference": "b001ef092224de1358462ceabfbdf4d4c2d4c9d2" + "reference": "7ce0de35c828d29a5602134cdcf82b9c301b198f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/cache/zipball/b001ef092224de1358462ceabfbdf4d4c2d4c9d2", - "reference": "b001ef092224de1358462ceabfbdf4d4c2d4c9d2", + "url": "https://api.github.com/repos/symfony/cache/zipball/7ce0de35c828d29a5602134cdcf82b9c301b198f", + "reference": "7ce0de35c828d29a5602134cdcf82b9c301b198f", "shasum": "" }, "require": { @@ -8824,7 +8825,7 @@ "psr6" ], "support": { - "source": "https://github.com/symfony/cache/tree/v6.4.46" + "source": "https://github.com/symfony/cache/tree/v6.4.47" }, "funding": [ { @@ -8844,7 +8845,7 @@ "type": "tidelift" } ], - "time": "2026-09-08T06:25:25+00:00" + "time": "2026-09-29T12:25:26+00:00" }, { "name": "symfony/cache-contracts", @@ -9085,16 +9086,16 @@ }, { "name": "symfony/console", - "version": "v6.4.46", + "version": "v6.4.47", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "9e118dd9e3b94544ff3cce860457c2951a0a2bd6" + "reference": "0f758dfab4608cac6a342b7e7f80cb84c9cc98ca" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/9e118dd9e3b94544ff3cce860457c2951a0a2bd6", - "reference": "9e118dd9e3b94544ff3cce860457c2951a0a2bd6", + "url": "https://api.github.com/repos/symfony/console/zipball/0f758dfab4608cac6a342b7e7f80cb84c9cc98ca", + "reference": "0f758dfab4608cac6a342b7e7f80cb84c9cc98ca", "shasum": "" }, "require": { @@ -9159,7 +9160,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v6.4.46" + "source": "https://github.com/symfony/console/tree/v6.4.47" }, "funding": [ { @@ -9179,20 +9180,20 @@ "type": "tidelift" } ], - "time": "2026-09-10T10:42:14+00:00" + "time": "2026-09-18T10:23:48+00:00" }, { "name": "symfony/dependency-injection", - "version": "v6.4.44", + "version": "v6.4.47", "source": { "type": "git", "url": "https://github.com/symfony/dependency-injection.git", - "reference": "675b2dfaf70bdca37d13816582023e3bac5fc2d1" + "reference": "c7ffb6da26739b461ae18dca3438bc68c35cd2a0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/675b2dfaf70bdca37d13816582023e3bac5fc2d1", - "reference": "675b2dfaf70bdca37d13816582023e3bac5fc2d1", + "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/c7ffb6da26739b461ae18dca3438bc68c35cd2a0", + "reference": "c7ffb6da26739b461ae18dca3438bc68c35cd2a0", "shasum": "" }, "require": { @@ -9244,7 +9245,7 @@ "description": "Allows you to standardize and centralize the way objects are constructed in your application", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/dependency-injection/tree/v6.4.44" + "source": "https://github.com/symfony/dependency-injection/tree/v6.4.47" }, "funding": [ { @@ -9264,7 +9265,7 @@ "type": "tidelift" } ], - "time": "2026-08-21T15:20:39+00:00" + "time": "2026-09-29T10:29:26+00:00" }, { "name": "symfony/deprecation-contracts", @@ -9339,16 +9340,16 @@ }, { "name": "symfony/doctrine-bridge", - "version": "v6.4.46", + "version": "v6.4.47", "source": { "type": "git", "url": "https://github.com/symfony/doctrine-bridge.git", - "reference": "6194d8f369d8cbe5a02f2b899776ca30ada7d411" + "reference": "1fa8f2328ad79314c036bc61754d2f86112e7360" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/doctrine-bridge/zipball/6194d8f369d8cbe5a02f2b899776ca30ada7d411", - "reference": "6194d8f369d8cbe5a02f2b899776ca30ada7d411", + "url": "https://api.github.com/repos/symfony/doctrine-bridge/zipball/1fa8f2328ad79314c036bc61754d2f86112e7360", + "reference": "1fa8f2328ad79314c036bc61754d2f86112e7360", "shasum": "" }, "require": { @@ -9427,7 +9428,7 @@ "description": "Provides integration for Doctrine with various Symfony components", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/doctrine-bridge/tree/v6.4.46" + "source": "https://github.com/symfony/doctrine-bridge/tree/v6.4.47" }, "funding": [ { @@ -9447,20 +9448,20 @@ "type": "tidelift" } ], - "time": "2026-09-11T10:34:36+00:00" + "time": "2026-09-28T17:32:40+00:00" }, { "name": "symfony/doctrine-messenger", - "version": "v6.4.45", + "version": "v6.4.47", "source": { "type": "git", "url": "https://github.com/symfony/doctrine-messenger.git", - "reference": "21e0d6e210270d1ebff2fa0dace165627c49119f" + "reference": "77d265ac40864f9f0488a0233ac940ad5ff4b0e1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/doctrine-messenger/zipball/21e0d6e210270d1ebff2fa0dace165627c49119f", - "reference": "21e0d6e210270d1ebff2fa0dace165627c49119f", + "url": "https://api.github.com/repos/symfony/doctrine-messenger/zipball/77d265ac40864f9f0488a0233ac940ad5ff4b0e1", + "reference": "77d265ac40864f9f0488a0233ac940ad5ff4b0e1", "shasum": "" }, "require": { @@ -9473,7 +9474,7 @@ "doctrine/persistence": "<1.3" }, "require-dev": { - "doctrine/persistence": "^1.3|^2|^3", + "doctrine/persistence": "^1.3|^2|^3|^4", "symfony/property-access": "^5.4|^6.0|^7.0", "symfony/serializer": "^5.4|^6.0|^7.0" }, @@ -9503,7 +9504,7 @@ "description": "Symfony Doctrine Messenger Bridge", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/doctrine-messenger/tree/v6.4.45" + "source": "https://github.com/symfony/doctrine-messenger/tree/v6.4.47" }, "funding": [ { @@ -9523,20 +9524,20 @@ "type": "tidelift" } ], - "time": "2026-06-16T14:52:47+00:00" + "time": "2026-09-24T07:57:43+00:00" }, { "name": "symfony/error-handler", - "version": "v6.4.44", + "version": "v6.4.47", "source": { "type": "git", "url": "https://github.com/symfony/error-handler.git", - "reference": "8fa59eb915a14b2881993565a122c9dd1b0f8a19" + "reference": "0b7be4e3f15ad53b46941bed1e59863245de8647" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/error-handler/zipball/8fa59eb915a14b2881993565a122c9dd1b0f8a19", - "reference": "8fa59eb915a14b2881993565a122c9dd1b0f8a19", + "url": "https://api.github.com/repos/symfony/error-handler/zipball/0b7be4e3f15ad53b46941bed1e59863245de8647", + "reference": "0b7be4e3f15ad53b46941bed1e59863245de8647", "shasum": "" }, "require": { @@ -9582,7 +9583,7 @@ "description": "Provides tools to manage errors and ease debugging PHP code", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/error-handler/tree/v6.4.44" + "source": "https://github.com/symfony/error-handler/tree/v6.4.47" }, "funding": [ { @@ -9602,7 +9603,7 @@ "type": "tidelift" } ], - "time": "2026-08-20T17:31:15+00:00" + "time": "2026-09-22T18:37:41+00:00" }, { "name": "symfony/event-dispatcher", @@ -9908,16 +9909,16 @@ }, { "name": "symfony/finder", - "version": "v6.4.46", + "version": "v6.4.47", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "04db08efd85558162ef43fba10c299871b60d60b" + "reference": "c8d1b2a09bb820175d1f2f9a3cf30303200b10a7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/04db08efd85558162ef43fba10c299871b60d60b", - "reference": "04db08efd85558162ef43fba10c299871b60d60b", + "url": "https://api.github.com/repos/symfony/finder/zipball/c8d1b2a09bb820175d1f2f9a3cf30303200b10a7", + "reference": "c8d1b2a09bb820175d1f2f9a3cf30303200b10a7", "shasum": "" }, "require": { @@ -9952,7 +9953,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v6.4.46" + "source": "https://github.com/symfony/finder/tree/v6.4.47" }, "funding": [ { @@ -9972,20 +9973,20 @@ "type": "tidelift" } ], - "time": "2026-09-10T14:17:24+00:00" + "time": "2026-09-25T09:03:59+00:00" }, { "name": "symfony/form", - "version": "v6.4.46", + "version": "v6.4.47", "source": { "type": "git", "url": "https://github.com/symfony/form.git", - "reference": "b0f1e86b3800e3cb9b90c0b9ce83cd9a822ef63d" + "reference": "5753fe93e0e83dfea93b697d70e52bcb36a3f3e4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/form/zipball/b0f1e86b3800e3cb9b90c0b9ce83cd9a822ef63d", - "reference": "b0f1e86b3800e3cb9b90c0b9ce83cd9a822ef63d", + "url": "https://api.github.com/repos/symfony/form/zipball/5753fe93e0e83dfea93b697d70e52bcb36a3f3e4", + "reference": "5753fe93e0e83dfea93b697d70e52bcb36a3f3e4", "shasum": "" }, "require": { @@ -10053,7 +10054,7 @@ "description": "Allows to easily create, process and reuse HTML forms", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/form/tree/v6.4.46" + "source": "https://github.com/symfony/form/tree/v6.4.47" }, "funding": [ { @@ -10073,20 +10074,20 @@ "type": "tidelift" } ], - "time": "2026-09-14T16:44:10+00:00" + "time": "2026-09-29T11:58:14+00:00" }, { "name": "symfony/framework-bundle", - "version": "v6.4.46", + "version": "v6.4.47", "source": { "type": "git", "url": "https://github.com/symfony/framework-bundle.git", - "reference": "407e77216969a366fd2bbf6d96a84c6b4a6500a2" + "reference": "a513e4e5d964599331811657c0e2f2c5fb21c293" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/framework-bundle/zipball/407e77216969a366fd2bbf6d96a84c6b4a6500a2", - "reference": "407e77216969a366fd2bbf6d96a84c6b4a6500a2", + "url": "https://api.github.com/repos/symfony/framework-bundle/zipball/a513e4e5d964599331811657c0e2f2c5fb21c293", + "reference": "a513e4e5d964599331811657c0e2f2c5fb21c293", "shasum": "" }, "require": { @@ -10140,7 +10141,7 @@ }, "require-dev": { "doctrine/annotations": "^1.13.1|^2", - "doctrine/persistence": "^1.3|^2|^3", + "doctrine/persistence": "^1.3|^2|^3|^4", "dragonmantank/cron-expression": "^3.1", "phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0", "seld/jsonlint": "^1.10", @@ -10206,7 +10207,7 @@ "description": "Provides a tight integration between Symfony components and the Symfony full-stack framework", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/framework-bundle/tree/v6.4.46" + "source": "https://github.com/symfony/framework-bundle/tree/v6.4.47" }, "funding": [ { @@ -10226,20 +10227,20 @@ "type": "tidelift" } ], - "time": "2026-09-13T09:15:59+00:00" + "time": "2026-09-28T18:03:43+00:00" }, { "name": "symfony/http-client", - "version": "v6.4.46", + "version": "v6.4.47", "source": { "type": "git", "url": "https://github.com/symfony/http-client.git", - "reference": "2027434cec6519eee0bcff54efa42f7aa7e6e182" + "reference": "c064327ae0f64b7d5b27ed16fd8a7fa20350180c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-client/zipball/2027434cec6519eee0bcff54efa42f7aa7e6e182", - "reference": "2027434cec6519eee0bcff54efa42f7aa7e6e182", + "url": "https://api.github.com/repos/symfony/http-client/zipball/c064327ae0f64b7d5b27ed16fd8a7fa20350180c", + "reference": "c064327ae0f64b7d5b27ed16fd8a7fa20350180c", "shasum": "" }, "require": { @@ -10304,7 +10305,7 @@ "http" ], "support": { - "source": "https://github.com/symfony/http-client/tree/v6.4.46" + "source": "https://github.com/symfony/http-client/tree/v6.4.47" }, "funding": [ { @@ -10324,7 +10325,7 @@ "type": "tidelift" } ], - "time": "2026-09-08T13:27:13+00:00" + "time": "2026-09-24T10:58:56+00:00" }, { "name": "symfony/http-client-contracts", @@ -10410,16 +10411,16 @@ }, { "name": "symfony/http-foundation", - "version": "v6.4.46", + "version": "v6.4.47", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "08bbdd5cf09248785442c41929aabe42f8ef5cfd" + "reference": "8c9d7b59c9315ad6801f3ac49234591d92f95634" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/08bbdd5cf09248785442c41929aabe42f8ef5cfd", - "reference": "08bbdd5cf09248785442c41929aabe42f8ef5cfd", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/8c9d7b59c9315ad6801f3ac49234591d92f95634", + "reference": "8c9d7b59c9315ad6801f3ac49234591d92f95634", "shasum": "" }, "require": { @@ -10467,7 +10468,7 @@ "description": "Defines an object-oriented layer for the HTTP specification", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-foundation/tree/v6.4.46" + "source": "https://github.com/symfony/http-foundation/tree/v6.4.47" }, "funding": [ { @@ -10487,20 +10488,20 @@ "type": "tidelift" } ], - "time": "2026-09-14T17:40:49+00:00" + "time": "2026-09-24T07:12:21+00:00" }, { "name": "symfony/http-kernel", - "version": "v6.4.46", + "version": "v6.4.47", "source": { "type": "git", "url": "https://github.com/symfony/http-kernel.git", - "reference": "c662aa16f2a6b078800ef5302444043c38932213" + "reference": "238e4681af2765043b4ed139843b3bded4220eb6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-kernel/zipball/c662aa16f2a6b078800ef5302444043c38932213", - "reference": "c662aa16f2a6b078800ef5302444043c38932213", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/238e4681af2765043b4ed139843b3bded4220eb6", + "reference": "238e4681af2765043b4ed139843b3bded4220eb6", "shasum": "" }, "require": { @@ -10585,7 +10586,7 @@ "description": "Provides a structured process for converting a Request into a Response", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-kernel/tree/v6.4.46" + "source": "https://github.com/symfony/http-kernel/tree/v6.4.47" }, "funding": [ { @@ -10605,7 +10606,7 @@ "type": "tidelift" } ], - "time": "2026-09-15T07:06:31+00:00" + "time": "2026-09-29T14:02:38+00:00" }, { "name": "symfony/intl", @@ -10696,16 +10697,16 @@ }, { "name": "symfony/lock", - "version": "v6.4.45", + "version": "v6.4.47", "source": { "type": "git", "url": "https://github.com/symfony/lock.git", - "reference": "84679f4725d2359f53739ad44e7fa1af9ee9a11d" + "reference": "94ea4a8415f40e5146d8559f74f632895505863b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/lock/zipball/84679f4725d2359f53739ad44e7fa1af9ee9a11d", - "reference": "84679f4725d2359f53739ad44e7fa1af9ee9a11d", + "url": "https://api.github.com/repos/symfony/lock/zipball/94ea4a8415f40e5146d8559f74f632895505863b", + "reference": "94ea4a8415f40e5146d8559f74f632895505863b", "shasum": "" }, "require": { @@ -10755,7 +10756,7 @@ "semaphore" ], "support": { - "source": "https://github.com/symfony/lock/tree/v6.4.45" + "source": "https://github.com/symfony/lock/tree/v6.4.47" }, "funding": [ { @@ -10775,7 +10776,7 @@ "type": "tidelift" } ], - "time": "2026-08-30T20:10:39+00:00" + "time": "2026-09-18T06:15:40+00:00" }, { "name": "symfony/mailer", @@ -10863,16 +10864,16 @@ }, { "name": "symfony/messenger", - "version": "v6.4.46", + "version": "v6.4.47", "source": { "type": "git", "url": "https://github.com/symfony/messenger.git", - "reference": "6aa31b55986cbd00a0aa1f8d8507201fef9beaeb" + "reference": "19a98a82b4452f820034fb88815dac3783975d17" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/messenger/zipball/6aa31b55986cbd00a0aa1f8d8507201fef9beaeb", - "reference": "6aa31b55986cbd00a0aa1f8d8507201fef9beaeb", + "url": "https://api.github.com/repos/symfony/messenger/zipball/19a98a82b4452f820034fb88815dac3783975d17", + "reference": "19a98a82b4452f820034fb88815dac3783975d17", "shasum": "" }, "require": { @@ -10930,7 +10931,7 @@ "description": "Helps applications send and receive messages to/from other applications or via message queues", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/messenger/tree/v6.4.46" + "source": "https://github.com/symfony/messenger/tree/v6.4.47" }, "funding": [ { @@ -10950,7 +10951,7 @@ "type": "tidelift" } ], - "time": "2026-09-13T07:04:28+00:00" + "time": "2026-09-21T15:42:22+00:00" }, { "name": "symfony/mime", @@ -11437,16 +11438,16 @@ }, { "name": "symfony/polyfill-iconv", - "version": "v1.37.0", + "version": "v1.43.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-iconv.git", - "reference": "2c5729fd241b4b22f6e4b436bc3354a4f262df57" + "reference": "254724d95fe01b679cd97d88786e9623ee00bfb7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-iconv/zipball/2c5729fd241b4b22f6e4b436bc3354a4f262df57", - "reference": "2c5729fd241b4b22f6e4b436bc3354a4f262df57", + "url": "https://api.github.com/repos/symfony/polyfill-iconv/zipball/254724d95fe01b679cd97d88786e9623ee00bfb7", + "reference": "254724d95fe01b679cd97d88786e9623ee00bfb7", "shasum": "" }, "require": { @@ -11497,7 +11498,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-iconv/tree/v1.37.0" + "source": "https://github.com/symfony/polyfill-iconv/tree/v1.43.0" }, "funding": [ { @@ -11517,20 +11518,20 @@ "type": "tidelift" } ], - "time": "2026-04-10T16:19:22+00:00" + "time": "2026-09-29T06:25:14+00:00" }, { "name": "symfony/polyfill-intl-grapheme", - "version": "v1.41.0", + "version": "v1.43.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "bb899c1db0aa8127dc3afe8cda4a67eb24915f8d" + "reference": "5cac8f4db97208823aa353f59a01faad2da52c75" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/bb899c1db0aa8127dc3afe8cda4a67eb24915f8d", - "reference": "bb899c1db0aa8127dc3afe8cda4a67eb24915f8d", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/5cac8f4db97208823aa353f59a01faad2da52c75", + "reference": "5cac8f4db97208823aa353f59a01faad2da52c75", "shasum": "" }, "require": { @@ -11579,7 +11580,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.41.0" + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.43.0" }, "funding": [ { @@ -11599,20 +11600,20 @@ "type": "tidelift" } ], - "time": "2026-07-28T08:25:59+00:00" + "time": "2026-09-25T15:46:47+00:00" }, { "name": "symfony/polyfill-intl-icu", - "version": "v1.38.0", + "version": "v1.43.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-icu.git", - "reference": "445c90e341fccda10311019cf82ff73bb7343945" + "reference": "d13cf82f40af144e5e5a867dc54ec3c7b4544b2e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-icu/zipball/445c90e341fccda10311019cf82ff73bb7343945", - "reference": "445c90e341fccda10311019cf82ff73bb7343945", + "url": "https://api.github.com/repos/symfony/polyfill-intl-icu/zipball/d13cf82f40af144e5e5a867dc54ec3c7b4544b2e", + "reference": "d13cf82f40af144e5e5a867dc54ec3c7b4544b2e", "shasum": "" }, "require": { @@ -11667,7 +11668,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-icu/tree/v1.38.0" + "source": "https://github.com/symfony/polyfill-intl-icu/tree/v1.43.0" }, "funding": [ { @@ -11687,20 +11688,20 @@ "type": "tidelift" } ], - "time": "2026-05-25T11:52:53+00:00" + "time": "2026-09-25T15:46:47+00:00" }, { "name": "symfony/polyfill-intl-idn", - "version": "v1.42.0", + "version": "v1.43.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-idn.git", - "reference": "51b5ff5ba85452b31ec6f55490b08148612339d9" + "reference": "533d68cf99c8f8ee2e8893c59ed59192e92cfd74" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/51b5ff5ba85452b31ec6f55490b08148612339d9", - "reference": "51b5ff5ba85452b31ec6f55490b08148612339d9", + "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/533d68cf99c8f8ee2e8893c59ed59192e92cfd74", + "reference": "533d68cf99c8f8ee2e8893c59ed59192e92cfd74", "shasum": "" }, "require": { @@ -11754,7 +11755,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.42.0" + "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.43.0" }, "funding": [ { @@ -11774,20 +11775,20 @@ "type": "tidelift" } ], - "time": "2026-08-24T10:51:20+00:00" + "time": "2026-09-23T14:40:04+00:00" }, { "name": "symfony/polyfill-intl-normalizer", - "version": "v1.42.0", + "version": "v1.43.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-normalizer.git", - "reference": "aa20edea75bd9c48cfecc8360922e5a6e5c44502" + "reference": "ebd57ccc7edb93d632e184efbe7693d2517bab3b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/aa20edea75bd9c48cfecc8360922e5a6e5c44502", - "reference": "aa20edea75bd9c48cfecc8360922e5a6e5c44502", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/ebd57ccc7edb93d632e184efbe7693d2517bab3b", + "reference": "ebd57ccc7edb93d632e184efbe7693d2517bab3b", "shasum": "" }, "require": { @@ -11839,7 +11840,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.42.0" + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.43.0" }, "funding": [ { @@ -11859,20 +11860,20 @@ "type": "tidelift" } ], - "time": "2026-08-07T06:33:24+00:00" + "time": "2026-09-23T22:02:09+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.38.2", + "version": "v1.43.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "d3d318bad5e7a1bfbd026009c8bfb8d8f99ae6b6" + "reference": "4d5d331bf731e0aedbc6b2f5e61135b7b7996a72" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/d3d318bad5e7a1bfbd026009c8bfb8d8f99ae6b6", - "reference": "d3d318bad5e7a1bfbd026009c8bfb8d8f99ae6b6", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/4d5d331bf731e0aedbc6b2f5e61135b7b7996a72", + "reference": "4d5d331bf731e0aedbc6b2f5e61135b7b7996a72", "shasum": "" }, "require": { @@ -11924,7 +11925,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.38.2" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.43.0" }, "funding": [ { @@ -11944,20 +11945,20 @@ "type": "tidelift" } ], - "time": "2026-05-27T06:59:30+00:00" + "time": "2026-09-24T15:19:04+00:00" }, { "name": "symfony/polyfill-php80", - "version": "v1.37.0", + "version": "v1.43.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "dfb55726c3a76ea3b6459fcfda1ec2d80a682411" + "reference": "9c6a5d6b01ca51d486c813e9a9c0ed55f208bb74" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/dfb55726c3a76ea3b6459fcfda1ec2d80a682411", - "reference": "dfb55726c3a76ea3b6459fcfda1ec2d80a682411", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/9c6a5d6b01ca51d486c813e9a9c0ed55f208bb74", + "reference": "9c6a5d6b01ca51d486c813e9a9c0ed55f208bb74", "shasum": "" }, "require": { @@ -12008,7 +12009,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.37.0" + "source": "https://github.com/symfony/polyfill-php80/tree/v1.43.0" }, "funding": [ { @@ -12028,20 +12029,20 @@ "type": "tidelift" } ], - "time": "2026-04-10T16:19:22+00:00" + "time": "2026-09-25T15:50:05+00:00" }, { "name": "symfony/polyfill-php83", - "version": "v1.41.0", + "version": "v1.43.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php83.git", - "reference": "5ea99087fb99c273a9b9236ed4c31e78b16103c6" + "reference": "80ccff923a8d61f73ebe9da0aa94e25232f004ce" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/5ea99087fb99c273a9b9236ed4c31e78b16103c6", - "reference": "5ea99087fb99c273a9b9236ed4c31e78b16103c6", + "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/80ccff923a8d61f73ebe9da0aa94e25232f004ce", + "reference": "80ccff923a8d61f73ebe9da0aa94e25232f004ce", "shasum": "" }, "require": { @@ -12088,7 +12089,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php83/tree/v1.41.0" + "source": "https://github.com/symfony/polyfill-php83/tree/v1.43.0" }, "funding": [ { @@ -12108,20 +12109,20 @@ "type": "tidelift" } ], - "time": "2026-07-01T12:47:55+00:00" + "time": "2026-09-25T15:50:05+00:00" }, { "name": "symfony/polyfill-php84", - "version": "v1.38.1", + "version": "v1.43.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php84.git", - "reference": "f4e1dfaee5b74aba5964fe1fd4dfc7ba5e3085fa" + "reference": "22c6548499cbe5c5bb072fc0c42312dff062a015" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php84/zipball/f4e1dfaee5b74aba5964fe1fd4dfc7ba5e3085fa", - "reference": "f4e1dfaee5b74aba5964fe1fd4dfc7ba5e3085fa", + "url": "https://api.github.com/repos/symfony/polyfill-php84/zipball/22c6548499cbe5c5bb072fc0c42312dff062a015", + "reference": "22c6548499cbe5c5bb072fc0c42312dff062a015", "shasum": "" }, "require": { @@ -12168,7 +12169,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php84/tree/v1.38.1" + "source": "https://github.com/symfony/polyfill-php84/tree/v1.43.0" }, "funding": [ { @@ -12188,20 +12189,20 @@ "type": "tidelift" } ], - "time": "2026-05-26T12:51:13+00:00" + "time": "2026-09-23T22:01:09+00:00" }, { "name": "symfony/polyfill-uuid", - "version": "v1.37.0", + "version": "v1.43.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-uuid.git", - "reference": "26dfec253c4cf3e51b541b52ddf7e42cb0908e94" + "reference": "b23ff8245cc5c4479ea2be3d0132d2e6dd433993" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-uuid/zipball/26dfec253c4cf3e51b541b52ddf7e42cb0908e94", - "reference": "26dfec253c4cf3e51b541b52ddf7e42cb0908e94", + "url": "https://api.github.com/repos/symfony/polyfill-uuid/zipball/b23ff8245cc5c4479ea2be3d0132d2e6dd433993", + "reference": "b23ff8245cc5c4479ea2be3d0132d2e6dd433993", "shasum": "" }, "require": { @@ -12251,7 +12252,7 @@ "uuid" ], "support": { - "source": "https://github.com/symfony/polyfill-uuid/tree/v1.37.0" + "source": "https://github.com/symfony/polyfill-uuid/tree/v1.43.0" }, "funding": [ { @@ -12271,7 +12272,7 @@ "type": "tidelift" } ], - "time": "2026-04-10T16:19:22+00:00" + "time": "2026-09-24T05:20:52+00:00" }, { "name": "symfony/process", @@ -12582,16 +12583,16 @@ }, { "name": "symfony/routing", - "version": "v6.4.45", + "version": "v6.4.47", "source": { "type": "git", "url": "https://github.com/symfony/routing.git", - "reference": "6fe2ed5b356beec41d5720dd73db72887c3f9d6d" + "reference": "641a94df9ad73671abd86bc4303aa301ca555b2b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/routing/zipball/6fe2ed5b356beec41d5720dd73db72887c3f9d6d", - "reference": "6fe2ed5b356beec41d5720dd73db72887c3f9d6d", + "url": "https://api.github.com/repos/symfony/routing/zipball/641a94df9ad73671abd86bc4303aa301ca555b2b", + "reference": "641a94df9ad73671abd86bc4303aa301ca555b2b", "shasum": "" }, "require": { @@ -12645,7 +12646,7 @@ "url" ], "support": { - "source": "https://github.com/symfony/routing/tree/v6.4.45" + "source": "https://github.com/symfony/routing/tree/v6.4.47" }, "funding": [ { @@ -12665,20 +12666,20 @@ "type": "tidelift" } ], - "time": "2026-08-16T17:07:07+00:00" + "time": "2026-09-28T18:59:50+00:00" }, { "name": "symfony/security-bundle", - "version": "v6.4.45", + "version": "v6.4.47", "source": { "type": "git", "url": "https://github.com/symfony/security-bundle.git", - "reference": "de06cd0f756ca8fe4894c7e95bac71d90bb94e64" + "reference": "f3ffaf6d5e6fad7f50c6ae5c021003ba6e374622" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/security-bundle/zipball/de06cd0f756ca8fe4894c7e95bac71d90bb94e64", - "reference": "de06cd0f756ca8fe4894c7e95bac71d90bb94e64", + "url": "https://api.github.com/repos/symfony/security-bundle/zipball/f3ffaf6d5e6fad7f50c6ae5c021003ba6e374622", + "reference": "f3ffaf6d5e6fad7f50c6ae5c021003ba6e374622", "shasum": "" }, "require": { @@ -12761,7 +12762,7 @@ "description": "Provides a tight integration of the Security component into the Symfony full-stack framework", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/security-bundle/tree/v6.4.45" + "source": "https://github.com/symfony/security-bundle/tree/v6.4.47" }, "funding": [ { @@ -12781,20 +12782,20 @@ "type": "tidelift" } ], - "time": "2026-08-30T06:17:28+00:00" + "time": "2026-09-24T08:57:57+00:00" }, { "name": "symfony/security-core", - "version": "v6.4.45", + "version": "v6.4.47", "source": { "type": "git", "url": "https://github.com/symfony/security-core.git", - "reference": "8f74c37460002b97c1318007629c8be37681498a" + "reference": "8479fccf23be745d7a05beb0d31f989ea515529a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/security-core/zipball/8f74c37460002b97c1318007629c8be37681498a", - "reference": "8f74c37460002b97c1318007629c8be37681498a", + "url": "https://api.github.com/repos/symfony/security-core/zipball/8479fccf23be745d7a05beb0d31f989ea515529a", + "reference": "8479fccf23be745d7a05beb0d31f989ea515529a", "shasum": "" }, "require": { @@ -12852,7 +12853,7 @@ "description": "Symfony Security Component - Core Library", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/security-core/tree/v6.4.45" + "source": "https://github.com/symfony/security-core/tree/v6.4.47" }, "funding": [ { @@ -12872,7 +12873,7 @@ "type": "tidelift" } ], - "time": "2026-08-30T00:30:12+00:00" + "time": "2026-09-21T20:17:28+00:00" }, { "name": "symfony/security-csrf", @@ -12948,16 +12949,16 @@ }, { "name": "symfony/security-http", - "version": "v6.4.46", + "version": "v6.4.47", "source": { "type": "git", "url": "https://github.com/symfony/security-http.git", - "reference": "a8ad51e48f20b515e589b5927ee7cde7e1e37453" + "reference": "44c7c1afb0537eedfe4670079eaf89ee179095b1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/security-http/zipball/a8ad51e48f20b515e589b5927ee7cde7e1e37453", - "reference": "a8ad51e48f20b515e589b5927ee7cde7e1e37453", + "url": "https://api.github.com/repos/symfony/security-http/zipball/44c7c1afb0537eedfe4670079eaf89ee179095b1", + "reference": "44c7c1afb0537eedfe4670079eaf89ee179095b1", "shasum": "" }, "require": { @@ -13016,7 +13017,7 @@ "description": "Symfony Security Component - HTTP Integration", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/security-http/tree/v6.4.46" + "source": "https://github.com/symfony/security-http/tree/v6.4.47" }, "funding": [ { @@ -13036,20 +13037,20 @@ "type": "tidelift" } ], - "time": "2026-09-15T05:55:29+00:00" + "time": "2026-09-15T16:35:01+00:00" }, { "name": "symfony/serializer", - "version": "v6.4.46", + "version": "v6.4.47", "source": { "type": "git", "url": "https://github.com/symfony/serializer.git", - "reference": "5febca192427e0d788848abad61be565c68b9645" + "reference": "67e177a3fe8cae15decfb004563f9b4988296be3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/serializer/zipball/5febca192427e0d788848abad61be565c68b9645", - "reference": "5febca192427e0d788848abad61be565c68b9645", + "url": "https://api.github.com/repos/symfony/serializer/zipball/67e177a3fe8cae15decfb004563f9b4988296be3", + "reference": "67e177a3fe8cae15decfb004563f9b4988296be3", "shasum": "" }, "require": { @@ -13118,7 +13119,7 @@ "description": "Handles serializing and deserializing data structures, including object graphs, into array structures or other formats like XML and JSON.", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/serializer/tree/v6.4.46" + "source": "https://github.com/symfony/serializer/tree/v6.4.47" }, "funding": [ { @@ -13138,7 +13139,7 @@ "type": "tidelift" } ], - "time": "2026-09-08T09:57:00+00:00" + "time": "2026-09-28T08:44:55+00:00" }, { "name": "symfony/service-contracts", @@ -13839,16 +13840,16 @@ }, { "name": "symfony/type-info", - "version": "v7.4.17", + "version": "v7.4.20", "source": { "type": "git", "url": "https://github.com/symfony/type-info.git", - "reference": "62d0ad6995630a4f7ea8fd270aadb3e2545c8e31" + "reference": "bbaaf3366400f5c85f66800b955c77b46e8abad2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/type-info/zipball/62d0ad6995630a4f7ea8fd270aadb3e2545c8e31", - "reference": "62d0ad6995630a4f7ea8fd270aadb3e2545c8e31", + "url": "https://api.github.com/repos/symfony/type-info/zipball/bbaaf3366400f5c85f66800b955c77b46e8abad2", + "reference": "bbaaf3366400f5c85f66800b955c77b46e8abad2", "shasum": "" }, "require": { @@ -13898,7 +13899,7 @@ "type" ], "support": { - "source": "https://github.com/symfony/type-info/tree/v7.4.17" + "source": "https://github.com/symfony/type-info/tree/v7.4.20" }, "funding": [ { @@ -13918,20 +13919,20 @@ "type": "tidelift" } ], - "time": "2026-08-21T15:52:48+00:00" + "time": "2026-09-28T12:19:37+00:00" }, { "name": "symfony/uid", - "version": "v6.4.32", + "version": "v6.4.47", "source": { "type": "git", "url": "https://github.com/symfony/uid.git", - "reference": "6b973c385f00341b246f697d82dc01a09107acdd" + "reference": "2266f787b76bbbc339f4fdb40111ca461782f282" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/uid/zipball/6b973c385f00341b246f697d82dc01a09107acdd", - "reference": "6b973c385f00341b246f697d82dc01a09107acdd", + "url": "https://api.github.com/repos/symfony/uid/zipball/2266f787b76bbbc339f4fdb40111ca461782f282", + "reference": "2266f787b76bbbc339f4fdb40111ca461782f282", "shasum": "" }, "require": { @@ -13976,7 +13977,7 @@ "uuid" ], "support": { - "source": "https://github.com/symfony/uid/tree/v6.4.32" + "source": "https://github.com/symfony/uid/tree/v6.4.47" }, "funding": [ { @@ -13996,7 +13997,7 @@ "type": "tidelift" } ], - "time": "2025-12-23T15:07:59+00:00" + "time": "2026-09-23T16:05:37+00:00" }, { "name": "symfony/ux-autocomplete", @@ -14379,16 +14380,16 @@ }, { "name": "symfony/validator", - "version": "v6.4.46", + "version": "v6.4.47", "source": { "type": "git", "url": "https://github.com/symfony/validator.git", - "reference": "496589b7ac1be8ed2da92310f65f05e3989baef4" + "reference": "022729aef78cefd4ac8b745f557abd57dc910204" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/validator/zipball/496589b7ac1be8ed2da92310f65f05e3989baef4", - "reference": "496589b7ac1be8ed2da92310f65f05e3989baef4", + "url": "https://api.github.com/repos/symfony/validator/zipball/022729aef78cefd4ac8b745f557abd57dc910204", + "reference": "022729aef78cefd4ac8b745f557abd57dc910204", "shasum": "" }, "require": { @@ -14456,7 +14457,7 @@ "description": "Provides tools to validate values", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/validator/tree/v6.4.46" + "source": "https://github.com/symfony/validator/tree/v6.4.47" }, "funding": [ { @@ -14476,7 +14477,7 @@ "type": "tidelift" } ], - "time": "2026-09-09T10:57:39+00:00" + "time": "2026-09-29T11:58:14+00:00" }, { "name": "symfony/var-dumper", @@ -14812,16 +14813,16 @@ }, { "name": "symfony/workflow", - "version": "v6.4.37", + "version": "v6.4.47", "source": { "type": "git", "url": "https://github.com/symfony/workflow.git", - "reference": "0624ea5019589fd6a941f0ff11b899c4caca4fc2" + "reference": "f44fcf2b5b712a9a9c734a2d076007e0afec28d4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/workflow/zipball/0624ea5019589fd6a941f0ff11b899c4caca4fc2", - "reference": "0624ea5019589fd6a941f0ff11b899c4caca4fc2", + "url": "https://api.github.com/repos/symfony/workflow/zipball/f44fcf2b5b712a9a9c734a2d076007e0afec28d4", + "reference": "f44fcf2b5b712a9a9c734a2d076007e0afec28d4", "shasum": "" }, "require": { @@ -14880,7 +14881,7 @@ "workflow" ], "support": { - "source": "https://github.com/symfony/workflow/tree/v6.4.37" + "source": "https://github.com/symfony/workflow/tree/v6.4.47" }, "funding": [ { @@ -14900,20 +14901,20 @@ "type": "tidelift" } ], - "time": "2026-04-13T14:11:12+00:00" + "time": "2026-09-21T16:55:19+00:00" }, { "name": "symfony/yaml", - "version": "v6.4.45", + "version": "v6.4.47", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "a778aba2d7130eba6150cc80cb692988a361ee4b" + "reference": "928e3ef5d46756da90cc2f54c3bd4a21bc18ceff" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/a778aba2d7130eba6150cc80cb692988a361ee4b", - "reference": "a778aba2d7130eba6150cc80cb692988a361ee4b", + "url": "https://api.github.com/repos/symfony/yaml/zipball/928e3ef5d46756da90cc2f54c3bd4a21bc18ceff", + "reference": "928e3ef5d46756da90cc2f54c3bd4a21bc18ceff", "shasum": "" }, "require": { @@ -14956,7 +14957,7 @@ "description": "Loads and dumps YAML files", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/yaml/tree/v6.4.45" + "source": "https://github.com/symfony/yaml/tree/v6.4.47" }, "funding": [ { @@ -14976,7 +14977,7 @@ "type": "tidelift" } ], - "time": "2026-08-30T00:27:22+00:00" + "time": "2026-09-26T06:15:25+00:00" }, { "name": "symfonycasts/dynamic-forms", @@ -18551,16 +18552,16 @@ }, { "name": "phpro/grumphp", - "version": "v2.24.0", + "version": "v2.25.0", "source": { "type": "git", "url": "https://github.com/phpro/grumphp.git", - "reference": "8abcadb4eb39a2c0124b9a220de12f8cf268e4c4" + "reference": "8af668884c838382e5114404204e62385ce3cdba" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpro/grumphp/zipball/8abcadb4eb39a2c0124b9a220de12f8cf268e4c4", - "reference": "8abcadb4eb39a2c0124b9a220de12f8cf268e4c4", + "url": "https://api.github.com/repos/phpro/grumphp/zipball/8af668884c838382e5114404204e62385ce3cdba", + "reference": "8af668884c838382e5114404204e62385ce3cdba", "shasum": "" }, "require": { @@ -18666,9 +18667,9 @@ "description": "A composer plugin that enables source code quality checks.", "support": { "issues": "https://github.com/phpro/grumphp/issues", - "source": "https://github.com/phpro/grumphp/tree/v2.24.0" + "source": "https://github.com/phpro/grumphp/tree/v2.25.0" }, - "time": "2026-09-25T08:41:10+00:00" + "time": "2026-10-03T08:58:23+00:00" }, { "name": "phpstan/extension-installer", @@ -19314,16 +19315,16 @@ }, { "name": "phpunit/phpunit", - "version": "9.6.37", + "version": "9.6.38", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "aceaa623912caf8e80bffea22fc4103a0a01565f" + "reference": "8b5f757e8e92071bfdbfa93943f94f84c4e31007" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/aceaa623912caf8e80bffea22fc4103a0a01565f", - "reference": "aceaa623912caf8e80bffea22fc4103a0a01565f", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/8b5f757e8e92071bfdbfa93943f94f84c4e31007", + "reference": "8b5f757e8e92071bfdbfa93943f94f84c4e31007", "shasum": "" }, "require": { @@ -19397,7 +19398,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.37" + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.38" }, "funding": [ { @@ -19405,7 +19406,7 @@ "type": "other" } ], - "time": "2026-09-23T09:59:59+00:00" + "time": "2026-10-05T05:44:26+00:00" }, { "name": "phrity/net-stream", @@ -21634,16 +21635,16 @@ }, { "name": "symfony/web-profiler-bundle", - "version": "v6.4.46", + "version": "v6.4.47", "source": { "type": "git", "url": "https://github.com/symfony/web-profiler-bundle.git", - "reference": "bab4798b3d9084431b589cb95772671c66cc6c32" + "reference": "df8f3c9f05c08cd7aeab029b4c6af456265c3666" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/web-profiler-bundle/zipball/bab4798b3d9084431b589cb95772671c66cc6c32", - "reference": "bab4798b3d9084431b589cb95772671c66cc6c32", + "url": "https://api.github.com/repos/symfony/web-profiler-bundle/zipball/df8f3c9f05c08cd7aeab029b4c6af456265c3666", + "reference": "df8f3c9f05c08cd7aeab029b4c6af456265c3666", "shasum": "" }, "require": { @@ -21696,7 +21697,7 @@ "dev" ], "support": { - "source": "https://github.com/symfony/web-profiler-bundle/tree/v6.4.46" + "source": "https://github.com/symfony/web-profiler-bundle/tree/v6.4.47" }, "funding": [ { @@ -21716,7 +21717,7 @@ "type": "tidelift" } ], - "time": "2026-09-02T17:01:51+00:00" + "time": "2026-09-28T16:09:37+00:00" }, { "name": "symplify/easy-coding-standard", diff --git a/config/services.yaml b/config/services.yaml index ac50d0bd..328a2c40 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -13,6 +13,11 @@ parameters: # explicitly opt out for its own known-internal host, not as a general "skip TLS" switch. payplug.unified_api_verify_tls.default: true payplug.unified_api_verify_tls: '%env(bool:default:payplug.unified_api_verify_tls.default:PAYPLUG_UNIFIED_API_VERIFY_TLS)%' + # Hosted Fields JS SDK loaded by the checkout: one full https URL, because the host, the path + # and the pinned version all differ per environment. Defaults to the production SDK; QA and + # staging setups set PAYPLUG_HOSTED_FIELDS_SDK_URL to their own SDK URL. + payplug.hosted_fields_sdk_url.default: 'https://js.dalenys.com/hosted-fields/v2.2.0/hosted-fields.min.js' + payplug.hosted_fields_sdk_url: '%env(default:payplug.hosted_fields_sdk_url.default:PAYPLUG_HOSTED_FIELDS_SDK_URL)%' services: _defaults: @@ -29,6 +34,7 @@ services: $payplugOauthAudience: '%payplug.oauth_audience%' $unifiedApiBaseUrl: '%payplug.unified_api_base_url%' $unifiedApiVerifyTls: '%payplug.unified_api_verify_tls%' + $hostedFieldsSdkUrl: '%payplug.hosted_fields_sdk_url%' # `security.csrf.token_manager` is only registered while CSRF protection is enabled, so it is # injected with `@?`: a hard reference would break container compilation for an application diff --git a/src/Action/Admin/AuthorizedPaymentController.php b/src/Action/Admin/AuthorizedPaymentController.php index 09509d38..4714c713 100644 --- a/src/Action/Admin/AuthorizedPaymentController.php +++ b/src/Action/Admin/AuthorizedPaymentController.php @@ -94,8 +94,9 @@ private function handle( $version = self::parseVersion($request); try { + $amount = $this->parseAmountFor($payment, $request->request->getString('amount'), $action); // The processor flushes while it still holds its lock, see there. - $operation($payment, self::parseAmount($request->request->getString('amount')), $version); + $operation($payment, $amount, $version); $this->addFlashMessage($request, 'success', self::FLASH_PREFIX . $action . '_success'); } catch (AuthorizationOperationException $exception) { $this->addFlashMessage($request, 'error', $exception->getTranslationKey(), $exception->getTranslationParameters()); @@ -121,8 +122,11 @@ private function handle( * digits before the decimal point, which no real capture reaches and which would overflow — * is sent on as an invalid amount (0) for the processor to refuse with its own explicit * message. + * + * @throws AuthorizationOperationException when an amount is typed in a currency that + * AuthorizedPaymentOperationProcessor::supportsTypedAmounts() refuses */ - public static function parseAmount(string $raw): ?int + public static function parseAmount(string $raw, string $currencyCode): ?int { $raw = \str_replace([' ', "\u{00A0}", ','], ['', '', '.'], \trim($raw)); if ('' === $raw) { @@ -133,7 +137,31 @@ public static function parseAmount(string $raw): ?int return 0; } - return AmountHelper::toCents((float) $raw); + if (!AuthorizedPaymentOperationProcessor::supportsTypedAmounts($currencyCode)) { + throw new AuthorizationOperationException(self::FLASH_PREFIX . 'error.unsupported_currency', ['%currency%' => $currencyCode]); + } + + return AmountHelper::toCents((float) $raw, $currencyCode); + } + + /** + * @throws AuthorizationOperationException + */ + private function parseAmountFor(PaymentInterface $payment, string $raw, string $action): ?int + { + $currencyCode = (string) $payment->getCurrencyCode(); + + try { + return self::parseAmount($raw, $currencyCode); + } catch (AuthorizationOperationException $exception) { + $this->logger->warning('[PayPlug][UPC] Typed amount refused: its currency cannot be converted to the stored amounts.', [ + 'sylius_payment_id' => $payment->getId(), + 'action' => $action, + 'currency' => $currencyCode, + ]); + + throw $exception; + } } /** diff --git a/src/Command/Handler/CaptureAliasPaymentRequestHandler.php b/src/Command/Handler/CaptureAliasPaymentRequestHandler.php index 93cf61c7..192b5dc1 100644 --- a/src/Command/Handler/CaptureAliasPaymentRequestHandler.php +++ b/src/Command/Handler/CaptureAliasPaymentRequestHandler.php @@ -69,14 +69,16 @@ public function __invoke(CaptureAliasPaymentRequest $captureAliasPaymentRequest) return; } + $hostedFieldsIds = $this->contextBuilder->resolveHostedFieldsIds($output->body); $createdDetails = [ ...$payment->getDetails(), 'alias_id' => $card->getExternalId(), 'alias_payment_created_at' => (new \DateTimeImmutable())->format(\DateTimeInterface::ATOM), - ...$this->contextBuilder->resolveHostedFieldsIds($output->body), + ...$hostedFieldsIds, ]; // Before applyOutcome() below — see CaptureHostedPaymentRequestHandler's own comment. $payment->setDetails($common->capture ? $createdDetails : AuthorizationDetails::open($createdDetails, $output, $amount)); + $this->outcomeApplier->warnWhenPaymentIdMissing($payment, PaymentCaptureFlow::Alias, $hostedFieldsIds); $this->outcomeApplier->applyOutcome($paymentRequest, $payment, $output); diff --git a/src/Command/Handler/CaptureHostedPaymentRequestHandler.php b/src/Command/Handler/CaptureHostedPaymentRequestHandler.php index bf75760f..d7c6f1d8 100644 --- a/src/Command/Handler/CaptureHostedPaymentRequestHandler.php +++ b/src/Command/Handler/CaptureHostedPaymentRequestHandler.php @@ -82,6 +82,7 @@ public function __invoke(CaptureHostedPaymentRequest $captureHostedPaymentReques // Before applyOutcome() below, which reads this back to apply "authorize" rather than // "complete" for an authorization-only payment. $payment->setDetails($dto->common->capture ? $createdDetails : AuthorizationDetails::open($createdDetails, $output, $amountAndCurrency[0])); + $this->outcomeApplier->warnWhenPaymentIdMissing($payment, PaymentCaptureFlow::Hosted, $hostedFieldsIds); $this->outcomeApplier->applyOutcome($paymentRequest, $payment, $output); diff --git a/src/Command/PaymentCaptureFlow.php b/src/Command/PaymentCaptureFlow.php index 074790fd..641c9641 100644 --- a/src/Command/PaymentCaptureFlow.php +++ b/src/Command/PaymentCaptureFlow.php @@ -5,9 +5,9 @@ namespace PayPlug\SyliusPayPlugPlugin\Command; /** - * Identifies which capture path a PaymentCaptureOutcomeApplier::failPaymentRequest() call - * came from, so its log message stays distinguishable between the two handlers without each - * caller passing a free-text string. + * Identifies which capture path a PaymentCaptureOutcomeApplier call came from, so its log + * messages stay distinguishable between the two handlers without each caller passing a + * free-text string. */ enum PaymentCaptureFlow: string { diff --git a/src/Handler/HostedFieldsWebhookNotificationHandler.php b/src/Handler/HostedFieldsWebhookNotificationHandler.php index 3cb8df07..df02fd72 100644 --- a/src/Handler/HostedFieldsWebhookNotificationHandler.php +++ b/src/Handler/HostedFieldsWebhookNotificationHandler.php @@ -300,9 +300,9 @@ private function applyLocked( // A 3DS-challenge capture never gets an alias back synchronously (CaptureHostedPaymentRequestHandler // only sees one on a direct, frictionless success) — this webhook, fired once the challenge is // validated, is the only place a 3DS payment's card ever gets saved. The alias/card metadata - // itself is already in $rawBody: confirmed the same paymentMethod.{id, card, details} shape as - // the operation resource CaptureHostedPaymentRequestHandler fetches separately, so no extra API - // call is needed here. + // itself is already in $rawBody, as paymentMethod.{storedId, card, details}, so no extra API call + // is needed here. The operation resource CaptureHostedPaymentRequestHandler fetches separately + // carries the alias as paymentMethod.id; CardDataFromPaymentMethodExtractor reads both. private function maybeSaveCard(PaymentInterface $payment, string $rawBody): void { $details = $payment->getDetails(); diff --git a/src/PaymentProcessing/AuthorizedPaymentOperationProcessor.php b/src/PaymentProcessing/AuthorizedPaymentOperationProcessor.php index 64f21e29..80b119b2 100644 --- a/src/PaymentProcessing/AuthorizedPaymentOperationProcessor.php +++ b/src/PaymentProcessing/AuthorizedPaymentOperationProcessor.php @@ -17,6 +17,7 @@ use PayplugUnifiedCore\Exceptions\CancellationAmountException; use PayplugUnifiedCore\Exceptions\CaptureAmountException; use PayplugUnifiedCore\Exceptions\CardOperationException; +use PayplugUnifiedCore\Exceptions\InvalidCurrencyException; use PayplugUnifiedCore\Exceptions\MultipleCaptureNotAllowedException; use PayplugUnifiedCore\Exceptions\OperationConflictException; use PayplugUnifiedCore\Exceptions\PartialCancellationNotAllowedException; @@ -65,7 +66,9 @@ * refunds that captured amount. See doc/authorized_payment.md. * * Amounts are integers in the minor unit Sylius stores everywhere (two decimals, whatever the - * currency), converted for display and input with UPC's AmountHelper. + * currency), converted for display and input with UPC's AmountHelper. The helper converts with the + * currency's own minor unit, which matches Sylius's two decimals only for a 2-decimal currency, so + * an amount typed by the merchant is accepted in those currencies only (supportsTypedAmounts()). */ final class AuthorizedPaymentOperationProcessor { @@ -101,6 +104,25 @@ public static function supports(PaymentInterface $payment): bool AuthorizationDetails::fromDetails($payment->getDetails())->isDeferred(); } + /** + * Whether an amount in $currencyCode can be converted between the decimal notation the + * merchant types and Sylius's two-decimal minor unit: only when UPC's AmountHelper converts it + * with that same factor of 100. An empty, malformed or 3-decimal code is rejected by the + * helper; a zero-decimal one (JPY, XPF...) converts with a factor of 1. + */ + public static function supportsTypedAmounts(?string $currencyCode): bool + { + if (null === $currencyCode) { + return false; + } + + try { + return 100 === AmountHelper::toCents(1.0, $currencyCode); + } catch (InvalidCurrencyException) { + return false; + } + } + public function canCapture(PaymentInterface $payment): bool { $authorization = AuthorizationDetails::fromDetails($payment->getDetails()); @@ -272,6 +294,13 @@ private function assertOperable(PaymentInterface $payment, ?int $expectedVersion */ private function resolveAmount(PaymentInterface $payment, ?int $amount, int $remaining): int { + $currencyCode = $payment->getCurrencyCode(); + if (null !== $amount && !self::supportsTypedAmounts($currencyCode)) { + throw new AuthorizationOperationException(self::ERROR_KEY_PREFIX . 'unsupported_currency', [ + '%currency%' => $currencyCode ?? '', + ]); + } + $amount ??= $remaining; if ($amount <= 0) { @@ -280,7 +309,7 @@ private function resolveAmount(PaymentInterface $payment, ?int $amount, int $rem if ($amount > $remaining) { throw new AuthorizationOperationException(self::ERROR_KEY_PREFIX . 'amount_exceeds_remaining', [ - '%remaining%' => self::formatAmount($remaining, $payment->getCurrencyCode()), + '%remaining%' => self::formatAmount($remaining, (string) $currencyCode), ]); } @@ -428,8 +457,12 @@ private static function extractExecCode(string $body): int|string|null return \is_int($execCode) || \is_string($execCode) ? $execCode : null; } - private static function formatAmount(int $amount, ?string $currencyCode): string + /** + * Only reached for an explicit amount, so resolveAmount() has already checked that + * $currencyCode is one supportsTypedAmounts() accepts: a 2-decimal currency. + */ + private static function formatAmount(int $amount, string $currencyCode): string { - return \trim(\number_format(AmountHelper::fromCents($amount), 2, '.', ' ') . ' ' . ($currencyCode ?? '')); + return \trim(\number_format(AmountHelper::fromCents($amount, $currencyCode), 2, '.', ' ') . ' ' . $currencyCode); } } diff --git a/src/PaymentProcessing/RefundPaymentProcessor.php b/src/PaymentProcessing/RefundPaymentProcessor.php index 275c8fe1..1085f0b5 100644 --- a/src/PaymentProcessing/RefundPaymentProcessor.php +++ b/src/PaymentProcessing/RefundPaymentProcessor.php @@ -31,6 +31,7 @@ use Sylius\RefundPlugin\Entity\RefundPayment; use Symfony\Component\DependencyInjection\Attribute\Autoconfigure; use Symfony\Component\HttpFoundation\RequestStack; +use Symfony\Component\HttpFoundation\Session\FlashBagAwareSessionInterface; use Symfony\Component\Workflow\Attribute\AsCompletedListener; use Symfony\Component\Workflow\Event\CompletedEvent; use Symfony\Contracts\Translation\TranslatorInterface; @@ -161,10 +162,7 @@ public function processWithAmount(PaymentInterface $payment, int $amount, int $r */ private function processHostedFields(PaymentInterface $payment): void { - // Deliberately outside the try/catch below, mirroring the legacy path's own - // Assert::string($details['payment_id']) — a payment reaching here without this detail - // set raises a raw InvalidArgumentException rather than UpdateHandlingException. - Assert::string($payment->getDetails()['hosted_fields_payment_id']); + $hostedFieldsPaymentId = $this->resolveHostedFieldsPaymentId($payment); $originalAmount = $payment->getAmount(); if (null === $originalAmount) { throw new \LogicException('Payment amount is not set.'); @@ -174,7 +172,7 @@ private function processHostedFields(PaymentInterface $payment): void $method = $payment->getMethod(); $lockKey = RefundDetailsLockKey::forPaymentId($payment->getId()); - $this->runLockedRefund($lockKey, ['sylius_payment_id' => $payment->getId()], function () use ($payment, $method, $originalAmount): void { + $this->runLockedRefund($lockKey, ['sylius_payment_id' => $payment->getId()], function () use ($payment, $method, $originalAmount, $hostedFieldsPaymentId): void { // Re-read now that the lock is held, not a snapshot taken before it: the lock is what // actually keeps this read-modify-write from racing // HostedFieldsWebhookNotificationHandler::markMatchedRefundAsFailed()'s own — see this @@ -182,7 +180,7 @@ private function processHostedFields(PaymentInterface $payment): void $details = $payment->getDetails(); $externalId = $this->createRefundOperation( $method, - $details['hosted_fields_payment_id'], + $hostedFieldsPaymentId, PaymentOrderIdResolver::resolve($payment->getOrder(), $payment->getId()), null, $payment->getCurrencyCode(), @@ -367,8 +365,7 @@ private static function sumRecordedRefunds(array $refunds): int */ private function processHostedFieldsWithAmount(PaymentInterface $payment, int $amount, int $refundId): void { - // Deliberately outside the try/catch below — see processHostedFields()'s own comment. - Assert::string($payment->getDetails()['hosted_fields_payment_id']); + $hostedFieldsPaymentId = $this->resolveHostedFieldsPaymentId($payment); /** @var PaymentMethodInterface $method */ $method = $payment->getMethod(); @@ -377,7 +374,7 @@ private function processHostedFieldsWithAmount(PaymentInterface $payment, int $a $this->runLockedRefund( $lockKey, ['sylius_payment_id' => $payment->getId(), 'refund_id' => $refundId], - function () use ($payment, $method, $amount, $refundId): void { + function () use ($payment, $method, $amount, $refundId, $hostedFieldsPaymentId): void { /** @var RefundPayment $refundPayment */ $refundPayment = $this->refundPaymentRepository->findOneBy(['id' => $refundId]); @@ -392,7 +389,7 @@ function () use ($payment, $method, $amount, $refundId): void { $details = $payment->getDetails(); $externalId = $this->createRefundOperation( $method, - $details['hosted_fields_payment_id'], + $hostedFieldsPaymentId, PaymentOrderIdResolver::resolve($payment->getOrder(), $payment->getId()), $amount, $payment->getCurrencyCode(), @@ -414,6 +411,53 @@ function () use ($payment, $method, $amount, $refundId): void { ); } + /** + * Refunds are keyed by the payment's own id; the refund route refuses the operation id ("The + * reference transaction has not been found."), so there is nothing to fall back on. Refuses + * before the lock, the API call and any bookkeeping, tells the admin and logs ids only. A + * stored id is never changed afterwards, so the value read here is the one used under the lock. + */ + private function resolveHostedFieldsPaymentId(PaymentInterface $payment): string + { + $details = $payment->getDetails(); + $paymentId = $details['hosted_fields_payment_id'] ?? null; + if (\is_string($paymentId) && '' !== $paymentId) { + return $paymentId; + } + + $orderNumber = $payment->getOrder()?->getNumber(); + $this->logger->error('[PayPlug][UPC] Refund refused: the payment has no hosted_fields_payment_id.', [ + 'sylius_payment_id' => $payment->getId(), + 'order_number' => $orderNumber, + 'hosted_fields_operation_id' => $details['hosted_fields_operation_id'] ?? null, + ]); + $this->notifyAdminRefundUnavailable($orderNumber); + + throw new UpdateHandlingException(); + } + + /** + * No main request or no session (CLI, worker) means there is no admin to tell; the log entry + * and the exception still record the refusal. + */ + private function notifyAdminRefundUnavailable(?string $orderNumber): void + { + $request = $this->requestStack->getMainRequest(); + if (null === $request || !$request->hasSession()) { + return; + } + + $session = $request->getSession(); + if (!$session instanceof FlashBagAwareSessionInterface) { + return; + } + + $session->getFlashBag()->add('error', $this->translator->trans( + 'payplug_sylius_payplug_plugin.ui.payment_refund_missing_payment_id', + ['%order%' => $orderNumber ?? ''], + )); + } + private static function extractFirstOperationId(string $body): ?string { $decoded = \json_decode($body, true); diff --git a/src/Twig/AuthorizationExtension.php b/src/Twig/AuthorizationExtension.php index 77d2d21a..ff67c6bf 100644 --- a/src/Twig/AuthorizationExtension.php +++ b/src/Twig/AuthorizationExtension.php @@ -62,8 +62,7 @@ public function describe(PaymentInterface $payment): ?array 'captured_amount' => $authorization->capturedAmount(), 'cancelled_amount' => $authorization->cancelledAmount(), 'remaining_amount' => $authorization->remainingAmount(), - // What the amount fields' placeholder shows: the same decimal notation they accept. - 'remaining_amount_input' => \number_format(AmountHelper::fromCents($authorization->remainingAmount()), 2, '.', ''), + 'remaining_amount_input' => self::remainingAmountInput($authorization->remainingAmount(), $payment->getCurrencyCode()), 'max_capture_date' => $authorization->maxCaptureDate(), 'expired' => $authorization->isExpired($now), 'expiring_soon' => $authorization->isExpiringSoon($now), @@ -72,4 +71,18 @@ public function describe(PaymentInterface $payment): ?array 'version' => $authorization->version(), ]; } + + /** + * What the amount fields' placeholder shows: the same decimal notation they accept. Empty for a + * currency in which a typed amount would be refused (see + * AuthorizedPaymentOperationProcessor::supportsTypedAmounts()), which is 2-decimal otherwise. + */ + private static function remainingAmountInput(int $remainingAmount, ?string $currencyCode): string + { + if (null === $currencyCode || !AuthorizedPaymentOperationProcessor::supportsTypedAmounts($currencyCode)) { + return ''; + } + + return \number_format(AmountHelper::fromCents($remainingAmount, $currencyCode), 2, '.', ''); + } } diff --git a/src/Twig/PayPlugExtension.php b/src/Twig/PayPlugExtension.php index 06a62756..446d75b6 100644 --- a/src/Twig/PayPlugExtension.php +++ b/src/Twig/PayPlugExtension.php @@ -16,6 +16,7 @@ final class PayPlugExtension extends AbstractExtension public function __construct( private CanSaveCardCheckerInterface $canSaveCardChecker, private PayPlugApiClientFactoryInterface $apiClientFactory, + private string $hostedFieldsSdkUrl, ) { } @@ -25,10 +26,16 @@ public function getFunctions(): array new TwigFunction('is_save_card_enabled', $this->isSaveCardAllowed(...)), new TwigFunction('is_payplug_test_mode_enabled', $this->isTest(...)), new TwigFunction('payplug_hosted_fields_company_id', $this->hostedFieldsCompanyId(...)), + new TwigFunction('payplug_hosted_fields_sdk_url', $this->hostedFieldsSdkUrl(...)), new TwigFunction('payplug_display_mode', $this->displayMode(...)), ]; } + public function hostedFieldsSdkUrl(): string + { + return $this->hostedFieldsSdkUrl; + } + /** * @param array $config */ diff --git a/src/Upc/CardDataFromPaymentMethodExtractor.php b/src/Upc/CardDataFromPaymentMethodExtractor.php index 99e11af5..7269e568 100644 --- a/src/Upc/CardDataFromPaymentMethodExtractor.php +++ b/src/Upc/CardDataFromPaymentMethodExtractor.php @@ -9,10 +9,11 @@ * `paymentMethod` object — shared by CaptureHostedPaymentRequestHandler (parsing the operation * resource fetched via OperationStatusFetcherInterface) and HostedFieldsWebhookNotificationHandler * (parsing the webhook's own raw body directly — confirmed the same shape as the operation - * resource, so no separate API call is needed there). Confirmed against a real staging response: - * paymentMethod.id ("card_xxx", the alias), paymentMethod.card.{network, code6x4} ("VISA", a - * masked PAN "424242XXXXXX4242") and paymentMethod.details.{selectedBrand, validityDate} ("VISA", - * "2027-12" — YYYY-MM). last4 isn't its own field — it's derived from code6x4's last 4 characters. + * resource, so no separate API call is needed there). The alias is paymentMethod.storedId, with + * paymentMethod.id read as a fallback, as UPC's UnifiedApiPaymentService::extractAliasId() does; + * paymentMethod.card.{network, code6x4} ("VISA", a masked PAN "424242XXXXXX4242") and + * paymentMethod.details.{selectedBrand, validityDate} ("VISA", "2027-12" — YYYY-MM). last4 isn't + * its own field — it's derived from code6x4's last 4 characters. */ final class CardDataFromPaymentMethodExtractor { @@ -56,9 +57,14 @@ public static function extractFromDecoded(mixed $decoded): array */ private static function extractAliasId(array $paymentMethod): array { - $aliasId = $paymentMethod['id'] ?? null; + foreach (['storedId', 'id'] as $key) { + $aliasId = $paymentMethod[$key] ?? null; + if (\is_string($aliasId) && '' !== $aliasId) { + return ['aliasId' => $aliasId]; + } + } - return \is_string($aliasId) && '' !== $aliasId ? ['aliasId' => $aliasId] : []; + return []; } /** @return array{brand?: string, last4?: string} */ diff --git a/src/Upc/PaymentCaptureOutcomeApplier.php b/src/Upc/PaymentCaptureOutcomeApplier.php index c688a950..d1825d45 100644 --- a/src/Upc/PaymentCaptureOutcomeApplier.php +++ b/src/Upc/PaymentCaptureOutcomeApplier.php @@ -82,6 +82,29 @@ private function notifyShopper(): void $session->getFlashBag()->add('error', self::SHOPPER_ERROR_FLASH_KEY); } + /** + * Refunds are keyed by the payment's top-level id, so a creation response without one leaves + * the payment unrefundable from the back office. Ids only: the response body carries customer + * data. + * + * @param array{hosted_fields_payment_id?: string, hosted_fields_operation_id?: string} $hostedFieldsIds + */ + public function warnWhenPaymentIdMissing( + PaymentInterface $payment, + PaymentCaptureFlow $flow, + array $hostedFieldsIds, + ): void { + if (isset($hostedFieldsIds['hosted_fields_payment_id'])) { + return; + } + + $this->logger->warning('[PayPlug][UPC] Payment created but the response carried no payment id; it cannot be refunded from the back office until PayPlug returns a payment id.', [ + 'sylius_payment_id' => $payment->getId(), + 'flow' => $flow->value, + 'hosted_fields_operation_id' => $hostedFieldsIds['hosted_fields_operation_id'] ?? null, + ]); + } + public function applyOutcome( PaymentRequestInterface $paymentRequest, PaymentInterface $payment, diff --git a/templates/shop/hosted_fields/index.html.twig b/templates/shop/hosted_fields/index.html.twig index da0dfcfe..8dc16b9f 100644 --- a/templates/shop/hosted_fields/index.html.twig +++ b/templates/shop/hosted_fields/index.html.twig @@ -1,4 +1,4 @@ - +