From a22903aaa4434e37c4ee48a8aa4fb3b94eda8e9a Mon Sep 17 00:00:00 2001 From: adumont-payplug Date: Wed, 29 Apr 2026 11:52:37 +0200 Subject: [PATCH] PRE-3278: adding scalapay APM and project configuration --- .github/workflows/analysis.yaml | 86 ---------------- .github/workflows/{sylius.yaml => ci.yml} | 99 ++++++++++++++++--- .github/workflows/pull_request_template.md | 35 ++++++- README.md | 5 +- composer.json | 11 ++- config/services.yaml | 21 ++++ config/services/client.xml | 9 ++ config/services/gateway.xml | 8 ++ config/twig_hooks/admin.yaml | 6 ++ config/twig_hooks/shop.yaml | 3 + ecs.php | 29 ++++++ grumphp.yml | 11 +++ public/assets/scalapay/logo.svg | 23 +++++ ruleset/phpstan-baseline.neon | 6 ++ ruleset/rector.php | 2 +- .../Auth/UnifiedAuthenticationController.php | 4 + src/ApiClient/PayPlugApiClientFactory.php | 13 +-- .../Handler/CapturePaymentRequestHandler.php | 8 +- .../Handler/StatusPaymentRequestHandler.php | 3 +- .../CapturePaymentRequestCommandProvider.php | 4 + .../NotifyPaymentRequestCommandProvider.php | 4 + .../StatusPaymentRequestCommandProvider.php | 4 + src/Controller/OneClickAction.php | 3 +- src/Controller/OrderController.php | 3 +- src/Creator/PayPlugPaymentDataCreator.php | 6 ++ .../RefundUnitsCommandCreatorDecorator.php | 3 +- src/Entity/Traits/CustomerTrait.php | 1 - src/Entity/Traits/PaymentMethodTrait.php | 1 - .../PostSavePaymentMethodEventListener.php | 3 +- src/Form/Extension/PaymentTypeExtension.php | 2 +- .../Type/AbstractGatewayConfigurationType.php | 1 + .../Type/ScalapayGatewayConfigurationType.php | 25 +++++ src/Gateway/ScalapayGatewayFactory.php | 21 ++++ .../Constraints/IsCanSavePaymentMethod.php | 1 + .../Constraints/IsOneyEnabledValidator.php | 2 +- src/Handler/PaymentNotificationHandler.php | 4 +- .../RefundPaymentGeneratedHandler.php | 4 +- .../Provider/CaptureHttpResponseProvider.php | 4 + .../AbortPaymentProcessor.php | 1 + .../RefundPaymentHandler.php | 2 +- .../RefundPaymentProcessor.php | 4 +- ...tSupportedRefundPaymentMethodsProvider.php | 2 +- .../OneySupportedPaymentChoiceProvider.php | 2 +- .../Payment/ApplePayPaymentProvider.php | 4 +- ...dRefundPaymentMethodsProviderDecorator.php | 15 +++ src/Provider/SupportedMethodsProvider.php | 2 +- ...dRefundPaymentMethodsProviderDecorator.php | 2 +- src/Repository/PaymentRepository.php | 6 +- .../OneyPaymentMethodsResolverDecorator.php | 2 +- src/Resolver/PaymentStateResolver.php | 4 +- ...calapayPaymentMethodsResolverDecorator.php | 43 ++++++++ src/Validator/PaymentMethodValidator.php | 17 +++- .../shop/select_payment/_scalapay.html.twig | 7 ++ tests/TestApplication/config/bundles.php | 2 + .../src/Entity/PaymentMethod.php | 2 +- translations/messages.en.yml | 1 + translations/messages.fr.yml | 1 + translations/messages.it.yml | 1 + translations/validators.en.yml | 8 ++ translations/validators.fr.yml | 8 ++ translations/validators.it.yml | 8 ++ 61 files changed, 473 insertions(+), 149 deletions(-) delete mode 100644 .github/workflows/analysis.yaml rename .github/workflows/{sylius.yaml => ci.yml} (52%) create mode 100644 ecs.php create mode 100644 public/assets/scalapay/logo.svg create mode 100644 src/Gateway/Form/Type/ScalapayGatewayConfigurationType.php create mode 100644 src/Gateway/ScalapayGatewayFactory.php create mode 100644 src/Provider/ScalapaySupportedRefundPaymentMethodsProviderDecorator.php create mode 100644 src/Resolver/ScalapayPaymentMethodsResolverDecorator.php create mode 100644 templates/shop/select_payment/_scalapay.html.twig diff --git a/.github/workflows/analysis.yaml b/.github/workflows/analysis.yaml deleted file mode 100644 index 8411f1b8..00000000 --- a/.github/workflows/analysis.yaml +++ /dev/null @@ -1,86 +0,0 @@ -name: Analysis -'on': - push: - branches: - - develop - - qa - - master - paths-ignore: - - README.md - pull_request: - paths-ignore: - - README.md -jobs: - analysis: - name: 'PHP ${{ matrix.php }} Symfony ${{ matrix.symfony }}' - runs-on: ubuntu-latest - strategy: - matrix: - php: - - 8.2 - symfony: - - '6.4.*' - env: - APP_ENV: test - steps: - - - uses: actions/checkout@v2 - - - name: 'Setup PHP' - uses: shivammathur/setup-php@v2 - with: - php-version: '${{ matrix.php }}' - tools: symfony - coverage: none - - - name: 'Composer - Get Cache Directory' - id: composer-cache - run: 'echo "::set-output name=dir::$(composer config cache-files-dir)"' - - - name: 'Composer - Set cache' - uses: actions/cache@v4 - with: - path: '${{ steps.composer-cache.outputs.dir }}' - key: 'php-${{ matrix.php }}-symfony-${{ matrix.symfony }}-composer-${{ hashFiles(''**/composer.json'') }}' - restore-keys: "php-${{ matrix.php }}-symfony-${{ matrix.symfony }}-composer-\n" - - - name: 'Composer - Validate composer.json and composer.lock' - run: 'composer validate --strict' - - - name: 'Composer - Github Auth' - run: 'composer config -g github-oauth.github.com ${{ github.token }}' - - - name: 'Composer - Restrict Symfony version' - run: 'composer config extra.symfony.require "${{ matrix.symfony }}"' - - - name: 'Composer - Update dependencies' - run: 'composer update --no-progress' - id: end-of-setup - - - name: 'PHPStan - Run' - run: 'if [ -f ruleset/phpstan.neon ]; then vendor/bin/phpstan analyse -c ruleset/phpstan.neon src/ ; else echo PHPStan rulesets file does not exist, skipping step ; fi' - if: 'always() && steps.end-of-setup.outcome == ''success''' - # TODO: launch Grumphp - - sonarcloud: - if: github.event.repository.fork != true - runs-on: ubuntu-latest - continue-on-error: true - steps: - - uses: actions/checkout@v3 - with: - # Disabling shallow clone is recommended for improving relevancy of reporting - fetch-depth: 0 - - name: SonarCloud Scan - uses: sonarsource/sonarcloud-github-action@master - env: - SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} - with: - projectBaseDir: . - args: > - -Dsonar.organization=${{ secrets.SONAR_ORGA }} - -Dsonar.projectKey=github-payplug-payplug-syliuspayplugplugin - -Dsonar.sources=src/ - -Dsonar.test.exclusions=tests/** - -Dsonar.tests=tests/ - -Dsonar.verbose=true diff --git a/.github/workflows/sylius.yaml b/.github/workflows/ci.yml similarity index 52% rename from .github/workflows/sylius.yaml rename to .github/workflows/ci.yml index 33dc6c31..a1217745 100644 --- a/.github/workflows/sylius.yaml +++ b/.github/workflows/ci.yml @@ -1,19 +1,76 @@ -name: Sylius +name: CI + 'on': - push: - branches: - - develop - - qa - - master - paths-ignore: - - README.md pull_request: + branches: + - test-develop-ci + - test-master-ci paths-ignore: - README.md + jobs: - sylius: + quality: + name: PHP Quality + runs-on: ubuntu-latest + env: + APP_ENV: test + steps: + - + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - + name: 'Setup PHP' + uses: shivammathur/setup-php@v2 + with: + php-version: '8.2' + ini-values: date.timezone=UTC + extensions: intl + tools: symfony + coverage: none + - + name: 'Composer - Get Cache Directory' + id: composer-cache + run: 'echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT' + - + name: 'Composer - Set cache' + uses: actions/cache@v4 + with: + path: '${{ steps.composer-cache.outputs.dir }}' + key: 'php-8.2-symfony-6.4-composer-${{ hashFiles(''**/composer.json'') }}' + restore-keys: 'php-8.2-symfony-6.4-composer-' + - + name: 'Composer - Github Auth' + run: 'composer config -g github-oauth.github.com ${{ github.token }}' + - + name: 'Composer - Remove version field for validation' + run: | + jq 'del(.version)' composer.json > composer.json.tmp + mv composer.json.tmp composer.json + - + name: 'Composer - Validate' + run: 'composer validate --strict' + - + name: 'Composer - Restrict Symfony version' + run: 'composer config extra.symfony.require "^6.4"' + - + name: 'Composer - Install dependencies' + run: 'composer install --no-progress' + id: end-of-setup + - + name: 'ECS - Coding Standard' + run: 'composer ecs' + if: 'always() && steps.end-of-setup.outcome == ''success''' + - + name: 'PHPStan - Static Analysis' + run: 'composer phpstan' + if: 'always() && steps.end-of-setup.outcome == ''success''' + + sylius-matrix: name: 'PHPUnit-Behat (PHP ${{ matrix.php }} Sylius ${{ matrix.sylius }} Symfony ${{ matrix.symfony }})' runs-on: ubuntu-latest + needs: [quality] + if: github.base_ref == 'test-develop-ci' strategy: fail-fast: false matrix: @@ -43,11 +100,11 @@ jobs: coverage: none - name: 'Setup Node' - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: '${{ matrix.node }}' - - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: 'Composer - Get Cache Directory' id: composer-cache @@ -83,7 +140,7 @@ jobs: run: 'make install -e SYLIUS_VERSION=${{ matrix.sylius }} SYMFONY_VERSION=${{ matrix.symfony }}' id: end-of-setup-sylius - - name: 'Doctrine Schema Validate - Run' + name: 'Doctrine Schema Validate' run: 'vendor/bin/console doctrine:schema:validate --skip-sync' - name: 'Run PHPUnit' @@ -93,8 +150,8 @@ jobs: uses: actions/upload-artifact@v4 if: failure() with: - name: logs - path: ./tests/Application/etc/build + name: 'logs-php${{ matrix.php }}-sylius${{ matrix.sylius }}-sf${{ matrix.symfony }}' + path: ./tests/TestApplication/etc/build services: mariadb: image: 'mariadb:10.4.11' @@ -103,3 +160,17 @@ jobs: env: MYSQL_ALLOW_EMPTY_PASSWORD: true options: '--health-cmd="mysqladmin ping" --health-interval=5s --health-timeout=2s --health-retries=3' + + # ----------------------------------------------------------------------- + # 4. SONARCLOUD — code quality analysis (develop PRs only) + # ----------------------------------------------------------------------- + sonarcloud: + if: always() && !failure() && !cancelled() && github.base_ref == 'test-develop-ci' + needs: sylius-matrix + uses: payplug/template-ci/.github/workflows/sonarcloud.yml@main + with: + project-name: 'github-payplug-payplug-syliuspayplugplugin' + src-folder: 'src/' + secrets: + sonar-orga: ${{ secrets.SONAR_ORGA }} + sonar-token: ${{ secrets.SONAR_TOKEN }} diff --git a/.github/workflows/pull_request_template.md b/.github/workflows/pull_request_template.md index c90cc2c8..6fed9975 100644 --- a/.github/workflows/pull_request_template.md +++ b/.github/workflows/pull_request_template.md @@ -1,5 +1,34 @@ -# ⚠️ Requirements -Reviewer, please take a look at those requirements: +## Description + + -- [ ] Check that plugin version has been upgrated and are identical in both `composer.json` and `src/PayPlugSyliusPayPlugPlugin.php` files +**Motivation:** +**Related issue(s):** Closes # + +--- + +## Type of Change + +- 🐛 Bug fix (non-breaking change that fixes an issue) [ ] +- ✨ New feature (non-breaking change that adds functionality) [ ] +- 💥 Breaking change (fix or feature that causes existing functionality to change and that could impact other libs) [ ] +- 🔧 Refactor (no functional changes, code improvement only) [ ] +- 📦 Dependency update [ ] +- 🔒 Security fix [ ] +- 📝 Documentation update [ ] + +--- + +## Checklist +### Code Quality +- [ ] Code is linted and formatted +- [ ] No unnecessary commented-out code or debug logs +- [ ] No hardcoded values (use env variables or config) + +### Testing +- [ ] Unit tests added / updated + +### Security & Ops +- [ ] No sensitive data or secrets introduced +- [ ] Logging and error handling are appropriate diff --git a/README.md b/README.md index 610a38d8..40b8025e 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ [![License](https://img.shields.io/packagist/l/payplug/sylius-payplug-plugin.svg)](https://github.com/payplug/SyliusPayPlugPlugin/blob/master/LICENSE) -[![CI - Analysis](https://github.com/payplug/SyliusPayPlugPlugin/actions/workflows/analysis.yaml/badge.svg?branch=master)](https://github.com/payplug/SyliusPayPlugPlugin/actions/workflows/analysis.yaml) -[![CI - Sylius](https://github.com/payplug/SyliusPayPlugPlugin/actions/workflows/sylius.yaml/badge.svg?branch=master)](https://github.com/payplug/SyliusPayPlugPlugin/actions/workflows/sylius.yaml) +[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=github-payplug-payplug-syliuspayplugplugin&metric=alert_status&token=af29f9f3fbb3a74caff4e4a4d168bddab858f4dc)](https://sonarcloud.io/summary/new_code?id=github-payplug-payplug-syliuspayplugplugin) +[![Duplicated Lines (%)](https://sonarcloud.io/api/project_badges/measure?project=github-payplug-payplug-syliuspayplugplugin&metric=duplicated_lines_density&token=af29f9f3fbb3a74caff4e4a4d168bddab858f4dc)](https://sonarcloud.io/summary/new_code?id=github-payplug-payplug-syliuspayplugplugin) +[![Code Smells](https://sonarcloud.io/api/project_badges/measure?project=github-payplug-payplug-syliuspayplugplugin&metric=code_smells&token=af29f9f3fbb3a74caff4e4a4d168bddab858f4dc)](https://sonarcloud.io/summary/new_code?id=github-payplug-payplug-syliuspayplugplugin) [![Version](https://img.shields.io/packagist/v/payplug/sylius-payplug-plugin.svg)](https://packagist.org/packages/payplug/sylius-payplug-plugin) [![Total Downloads](https://poser.pugx.org/payplug/sylius-payplug-plugin/downloads)](https://packagist.org/packages/payplug/sylius-payplug-plugin) diff --git a/composer.json b/composer.json index f7d338e1..059fce80 100755 --- a/composer.json +++ b/composer.json @@ -1,6 +1,7 @@ { "name": "payplug/sylius-payplug-plugin", "type": "sylius-plugin", + "version": "2.0.0.", "keywords": [ "sylius", "sylius-plugin", @@ -47,7 +48,7 @@ "phpstan/phpstan-webmozart-assert": "2.0.0", "phpunit/phpunit": "^9.6", "rector/rector": "2.0.4", - "sylius-labs/coding-standard": "4.4.0", + "sylius-labs/coding-standard": "^4.4", "sylius/test-application": "^2.1.0@alpha", "symfony/apache-pack": "*", "symfony/browser-kit": "^6.4|| ^7.2", @@ -80,10 +81,10 @@ } }, "scripts": { - "ecs": "ecs check -c rulesets/ecs.php --ansi --clear-cache .", - "fix-ecs": "@ecs --fix", - "phpmd": "phpmd src ansi rulesets/.php_md.xml", - "phpstan": "phpstan analyse src -c rulesets/phpstan.neon", + "ecs": "ecs check -c ruleset/ecs.php --ansi --clear-cache", + "fix-ecs": "@ecs --fix --memory-limit=4G", + "phpmd": "phpmd src ansi ruleset/.php_md.xml", + "phpstan": "phpstan analyse src -c ruleset/phpstan.neon", "phpunit": "phpunit tests/PHPUnit --colors=always", "tests": [ "@ecs", diff --git a/config/services.yaml b/config/services.yaml index 8b5dac42..116676f0 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -119,6 +119,27 @@ services: - name: sylius.payment_request.provider.http_response gateway_factory: !php/const PayPlug\SyliusPayPlugPlugin\Gateway\AmericanExpressGatewayFactory::FACTORY_NAME + ## Scalapay Payplug Gateway ## + payplug_sylius_payplug_plugin.command_provider.payplug_scalapay: + class: Sylius\Bundle\PaymentBundle\CommandProvider\ActionsCommandProvider + arguments: + - !tagged_locator + tag: payplug_sylius_payplug_plugin.command_provider.payplug_scalapay + index_by: 'action' + tags: + - name: sylius.payment_request.command_provider + gateway_factory: !php/const PayPlug\SyliusPayPlugPlugin\Gateway\ScalapayGatewayFactory::FACTORY_NAME + payplug_sylius_payplug_plugin.provider.order_pay.http_response.payplug_scalapay: + class: Sylius\Bundle\PaymentBundle\Provider\ActionsHttpResponseProvider + arguments: + - !tagged_locator + tag: payplug_sylius_payplug_plugin.http_response_provider.payplug_scalapay + index_by: action + tags: + - name: sylius.payment_request.provider.http_response + gateway_factory: !php/const PayPlug\SyliusPayPlugPlugin\Gateway\ScalapayGatewayFactory::FACTORY_NAME + + ## Apple Pay Payplug Gateway ## payplug_sylius_payplug_plugin.command_provider.payplug_apple_pay: class: Sylius\Bundle\PaymentBundle\CommandProvider\ActionsCommandProvider diff --git a/config/services/client.xml b/config/services/client.xml index 0ca32b00..22917bdf 100644 --- a/config/services/client.xml +++ b/config/services/client.xml @@ -53,5 +53,14 @@ method="create"/> american_express + + + + payplug_scalapay + diff --git a/config/services/gateway.xml b/config/services/gateway.xml index eeda89e7..dd174517 100644 --- a/config/services/gateway.xml +++ b/config/services/gateway.xml @@ -47,5 +47,13 @@ + + + + PayPlug\SyliusPayPlugPlugin\Gateway\ScalapayGatewayFactory + + diff --git a/config/twig_hooks/admin.yaml b/config/twig_hooks/admin.yaml index 931ecf69..ba1e2403 100644 --- a/config/twig_hooks/admin.yaml +++ b/config/twig_hooks/admin.yaml @@ -30,6 +30,9 @@ sylius_twig_hooks: 'sylius_admin.payment_method.create.content.form.sections.gateway_configuration.payplug_american_express': &amexGateway live_checkbox: *liveCheckbox + 'sylius_admin.payment_method.create.content.form.sections.gateway_configuration.payplug_scalapay': &scalapayGateway + live_checkbox: *liveCheckbox + 'sylius_admin.payment_method.update.content.form.sections.gateway_configuration.payplug': <<: *payplugGateway renew_oauth: &renewOAuth @@ -47,3 +50,6 @@ sylius_twig_hooks: 'sylius_admin.payment_method.update.content.form.sections.gateway_configuration.payplug_american_express': <<: *amexGateway renew_oauth: *renewOAuth + 'sylius_admin.payment_method.update.content.form.sections.gateway_configuration.payplug_scalapay': + <<: *scalapayGateway + renew_oauth: *renewOAuth diff --git a/config/twig_hooks/shop.yaml b/config/twig_hooks/shop.yaml index ed250d5f..5ffe2826 100644 --- a/config/twig_hooks/shop.yaml +++ b/config/twig_hooks/shop.yaml @@ -44,3 +44,6 @@ sylius_twig_hooks: 'sylius_shop.shared.form.select_payment.payment.choice.details#payplug_american_express': american_express: template: '@PayPlugSyliusPayPlugPlugin/shop/select_payment/_american_express.html.twig' + 'sylius_shop.shared.form.select_payment.payment.choice.details#payplug_scalapay': + scalapay: + template: '@PayPlugSyliusPayPlugPlugin/shop/select_payment/_scalapay.html.twig' diff --git a/ecs.php b/ecs.php new file mode 100644 index 00000000..50afbd67 --- /dev/null +++ b/ecs.php @@ -0,0 +1,29 @@ +withPaths([ + __DIR__ . '/ruleset', + __DIR__ . '/src', + __DIR__ . '/tests', + ]) + + // add a single rule + ->withRules([ + NoUnusedImportsFixer::class, + ]) + + // add sets - group of rules, from easiest to more complex ones + // uncomment one, apply one, commit, PR, merge and repeat + //->withPreparedSets( + // spaces: true, + // namespaces: true, + // docblocks: true, + // arrays: true, + // comments: true, + //) + ; diff --git a/grumphp.yml b/grumphp.yml index 1ae601d8..e8ea3a8c 100644 --- a/grumphp.yml +++ b/grumphp.yml @@ -3,6 +3,12 @@ grumphp: failed: ~ succeeded: ~ tasks: + git_commit_message: + matchers: + - '/^(PRE|SYL|SMP)-\d+: .+/' + git_branch_name: + whitelist: + - '/^(feature|fix|hotfix|refactor|release)\/(PRE|SYL|SMP)-\d+/' composer: no_check_all: true jsonlint: @@ -14,12 +20,17 @@ grumphp: level: ~ configuration: 'ruleset/phpstan.neon' use_grumphp_paths: false + memory_limit: '-1' securitychecker_symfony: ~ yamllint: parse_custom_tags: true ecs: config: 'ruleset/ecs.php' + clear-cache: true no-progress-bar: true + files_on_pre_commit: false + paths: ['src', 'tests/Behat', 'tests/PHPUnit'] + triggered_by: ['php', 'phtml', 'twig', 'yaml', 'yml', 'xml', 'json', 'neon', 'lock'] twigcs: path: 'src/' severity: error diff --git a/public/assets/scalapay/logo.svg b/public/assets/scalapay/logo.svg new file mode 100644 index 00000000..619520ad --- /dev/null +++ b/public/assets/scalapay/logo.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ruleset/phpstan-baseline.neon b/ruleset/phpstan-baseline.neon index 1d16fed3..2ebc3b8c 100644 --- a/ruleset/phpstan-baseline.neon +++ b/ruleset/phpstan-baseline.neon @@ -1360,6 +1360,12 @@ parameters: count: 1 path: ../src/Resolver/PayPlugPaymentMethodsResolverDecorator.php + - + message: '#^Method PayPlug\\SyliusPayPlugPlugin\\Resolver\\ScalapayPaymentMethodsResolverDecorator\:\:getSupportedMethods\(\) should return array\ but returns array\.$#' + identifier: return.type + count: 1 + path: ../src/Resolver/ScalapayPaymentMethodsResolverDecorator.php + - message: '#^Call to method apply\(\) on an unknown class SM\\StateMachine\\StateMachineInterface\.$#' identifier: class.notFound diff --git a/ruleset/rector.php b/ruleset/rector.php index c68c6f8f..90e1fac4 100644 --- a/ruleset/rector.php +++ b/ruleset/rector.php @@ -38,4 +38,4 @@ SymfonySetList::SYMFONY_72, SetList::CODE_QUALITY, SetList::DEAD_CODE, - ]); \ No newline at end of file + ]); diff --git a/src/Action/Admin/Auth/UnifiedAuthenticationController.php b/src/Action/Admin/Auth/UnifiedAuthenticationController.php index 603ad6aa..2449cc48 100644 --- a/src/Action/Admin/Auth/UnifiedAuthenticationController.php +++ b/src/Action/Admin/Auth/UnifiedAuthenticationController.php @@ -24,6 +24,7 @@ * This controller is used to authenticate the user with PayPlug * * The OAuth process start when creating a new payment method or updated it. + * * @see PayPlug\SyliusPayPlugPlugin\EventListener\PostSavePaymentMethodEventListener */ #[Route('/payplug/auth')] @@ -66,9 +67,11 @@ public function setupRedirection(Request $request): Response return new RedirectResponse(substr($header, 9)); } } + throw new \LogicException('No location header found'); } catch (\Throwable $e) { $this->logger->critical('Error while perform Payplug OAuth Setup redirection', ['message' => $e->getMessage(), 'exception' => $e]); + return $this->handleOAuthError($request); } } @@ -128,6 +131,7 @@ public function oauthCallback(Request $request): Response return new RedirectResponse($this->router->generate('sylius_admin_payment_method_update', ['id' => $paymentMethod->getId()])); } catch (\Throwable $e) { $this->logger->critical('Error while perform Payplug OAuth callback', ['message' => $e->getMessage(), 'exception' => $e]); + return $this->handleOAuthError($request); } } diff --git a/src/ApiClient/PayPlugApiClientFactory.php b/src/ApiClient/PayPlugApiClientFactory.php index 4d12c900..9b71b41e 100644 --- a/src/ApiClient/PayPlugApiClientFactory.php +++ b/src/ApiClient/PayPlugApiClientFactory.php @@ -9,7 +9,6 @@ use Sylius\Component\Payment\Model\GatewayConfigInterface; use Sylius\Component\Payment\Model\PaymentMethodInterface; use Sylius\Component\Resource\Repository\RepositoryInterface; -use Symfony\Config\SyliusPayment\GatewayConfigConfig; use Symfony\Contracts\Cache\CacheInterface; use Symfony\Contracts\Cache\ItemInterface; @@ -48,17 +47,14 @@ public function createForPaymentMethod(PaymentMethodInterface $paymentMethod): P private function getTokenForGatewayConfig(GatewayConfigInterface $gatewayConfig): string { $config = $gatewayConfig->getConfig(); - $clientConfig = $config['live_client']; - if (true !== $config['live']) { // The live mode is not enabled, use client config for test mode - $clientConfig = $config['test_client']; - } - if (!\is_array($clientConfig)) { + $rawClientConfig = true !== $config['live'] ? $config['test_client'] : $config['live_client']; + if (!\is_array($rawClientConfig)) { throw new GatewayConfigurationException('No client config found for ' . $gatewayConfig->getFactoryName() . '. Please renew your credentials in the PayPlug plugin configuration.'); } - + /** @var array $clientConfig */ + $clientConfig = $rawClientConfig; $cacheKey = sprintf('payplug_%s_api_key_%s', $gatewayConfig->getFactoryName(), $config['live'] === true ? 'live' : 'test'); - /** @var array $clientConfig */ return $this->cache->get($cacheKey, function (ItemInterface $item) use ($clientConfig) { $response = Authentication::generateJWT($clientConfig['client_id'] ?? '', $clientConfig['client_secret'] ?? ''); if ([] === $response || !is_array($response['httpResponse'])) { @@ -75,6 +71,7 @@ private function getTokenForGatewayConfig(GatewayConfigInterface $gatewayConfig) } $item->expiresAfter($expiresIn); + return $accessToken; }); } diff --git a/src/Command/Handler/CapturePaymentRequestHandler.php b/src/Command/Handler/CapturePaymentRequestHandler.php index 71bcf9dd..73d11964 100644 --- a/src/Command/Handler/CapturePaymentRequestHandler.php +++ b/src/Command/Handler/CapturePaymentRequestHandler.php @@ -28,6 +28,7 @@ public function __construct( #[Autowire(service: 'sylius_shop.provider.order_pay.after_pay_url')] private UrlProviderInterface $afterPayUrlProvider, private UrlGeneratorInterface $urlGenerator, + private \Psr\Log\LoggerInterface $logger, ) {} public function __invoke(CapturePaymentRequest $capturePaymentRequest): void @@ -41,7 +42,10 @@ public function __invoke(CapturePaymentRequest $capturePaymentRequest): void throw new \LogicException('Payment method is not set for the payment.'); } - if (PayPlugApiClientInterface::STATUS_CREATED === ($payment->getDetails()['status'] ?? null)) { + if ( + PayPlugApiClientInterface::STATUS_CREATED === ($payment->getDetails()['status'] ?? null) && + ($payment->getDetails()['factory_name'] ?? null) === $method->getGatewayConfig()?->getFactoryName() + ) { $paymentRequest->setResponseData([ 'retry' => true, 'message' => 'Payment already created', @@ -76,6 +80,7 @@ public function __invoke(CapturePaymentRequest $capturePaymentRequest): void $payplugPayment = $client->createPayment($data); } catch (HttpException $exception) { $paymentRequest->setResponseData(\json_decode($exception->getHttpResponse(), true)); // @phpstan-ignore-line + $this->logger->error('[PayPlug] Scalapay capture failed', ['response' => $exception->getHttpResponse()]); $this->stateMachine->apply( $paymentRequest, PaymentRequestTransitions::GRAPH, @@ -88,6 +93,7 @@ public function __invoke(CapturePaymentRequest $capturePaymentRequest): void $payment->setDetails([ ...$payment->getDetails(), 'status' => PayPlugApiClientInterface::STATUS_CREATED, + 'factory_name' => $method->getGatewayConfig()?->getFactoryName(), 'payment_id' => $payplugPayment->__get('id'), 'payplug_response' => $arrayPayplugPayment, 'redirect_url' => $payplugPayment->hosted_payment->payment_url, // @phpstan-ignore-line diff --git a/src/Command/Handler/StatusPaymentRequestHandler.php b/src/Command/Handler/StatusPaymentRequestHandler.php index 1f2c47fe..fcd0f67e 100644 --- a/src/Command/Handler/StatusPaymentRequestHandler.php +++ b/src/Command/Handler/StatusPaymentRequestHandler.php @@ -45,12 +45,13 @@ public function __invoke(StatusPaymentRequest $statusPaymentRequest): void // We don't have a forced status, so we retrieve the payment status from PayPlug $client = $this->apiClientFactory->createForPaymentMethod($method); - /** @var null|string $payplugPaymentId */ + /** @var string|null $payplugPaymentId */ $payplugPaymentId = $payment->getDetails()['payment_id'] ?? null; if (null === $payplugPaymentId) { $this->logger->warning('No PayPlug payment ID found in payment details.', ['payment_id' => $payment->getId(), 'order_id' => $payment->getOrder()?->getId()]); $payment->setDetails(['status' => PayPlugApiClientInterface::FAILED]); $this->updatePaymentState($payment); + return; } diff --git a/src/Command/Provider/CapturePaymentRequestCommandProvider.php b/src/Command/Provider/CapturePaymentRequestCommandProvider.php index 1627b281..8d0be621 100644 --- a/src/Command/Provider/CapturePaymentRequestCommandProvider.php +++ b/src/Command/Provider/CapturePaymentRequestCommandProvider.php @@ -31,6 +31,10 @@ 'payplug_sylius_payplug_plugin.command_provider.payplug_apple_pay', ['action' => PaymentRequestInterface::ACTION_CAPTURE], )] +#[AutoconfigureTag( + 'payplug_sylius_payplug_plugin.command_provider.payplug_scalapay', + ['action' => PaymentRequestInterface::ACTION_CAPTURE], +)] final class CapturePaymentRequestCommandProvider implements PaymentRequestCommandProviderInterface { public function supports(PaymentRequestInterface $paymentRequest): bool diff --git a/src/Command/Provider/NotifyPaymentRequestCommandProvider.php b/src/Command/Provider/NotifyPaymentRequestCommandProvider.php index 0311f69b..55f16fb8 100644 --- a/src/Command/Provider/NotifyPaymentRequestCommandProvider.php +++ b/src/Command/Provider/NotifyPaymentRequestCommandProvider.php @@ -29,6 +29,10 @@ 'payplug_sylius_payplug_plugin.command_provider.payplug_apple_pay', ['action' => PaymentRequestInterface::ACTION_NOTIFY], )] +#[AutoconfigureTag( + 'payplug_sylius_payplug_plugin.command_provider.payplug_scalapay', + ['action' => PaymentRequestInterface::ACTION_NOTIFY], +)] final class NotifyPaymentRequestCommandProvider implements PaymentRequestCommandProviderInterface { public function supports(PaymentRequestInterface $paymentRequest): bool diff --git a/src/Command/Provider/StatusPaymentRequestCommandProvider.php b/src/Command/Provider/StatusPaymentRequestCommandProvider.php index e75804b0..a0475641 100644 --- a/src/Command/Provider/StatusPaymentRequestCommandProvider.php +++ b/src/Command/Provider/StatusPaymentRequestCommandProvider.php @@ -30,6 +30,10 @@ 'payplug_sylius_payplug_plugin.command_provider.payplug_apple_pay', ['action' => PaymentRequestInterface::ACTION_STATUS], )] +#[AutoconfigureTag( + 'payplug_sylius_payplug_plugin.command_provider.payplug_scalapay', + ['action' => PaymentRequestInterface::ACTION_STATUS], +)] final class StatusPaymentRequestCommandProvider implements PaymentRequestCommandProviderInterface { public function __construct(private RequestStack $requestStack) diff --git a/src/Controller/OneClickAction.php b/src/Controller/OneClickAction.php index 166fc383..a1e27ab2 100644 --- a/src/Controller/OneClickAction.php +++ b/src/Controller/OneClickAction.php @@ -6,15 +6,14 @@ use PayPlug\SyliusPayPlugPlugin\Action\Api\ApiAwareTrait; use PayPlug\SyliusPayPlugPlugin\ApiClient\PayPlugApiClientFactory; -use PayPlug\SyliusPayPlugPlugin\Gateway\PayPlugGatewayFactory; use Payum\Core\ApiAwareInterface; use Payum\Core\GatewayAwareInterface; use Payum\Core\GatewayAwareTrait; -use Sylius\Component\Payment\Model\GatewayConfigInterface; use Payum\Core\Payum; use Sylius\Component\Core\Model\PaymentInterface; use Sylius\Component\Core\Model\PaymentMethodInterface; use Sylius\Component\Core\Repository\PaymentRepositoryInterface; +use Sylius\Component\Payment\Model\GatewayConfigInterface; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; diff --git a/src/Controller/OrderController.php b/src/Controller/OrderController.php index 7c70250e..0baf45c9 100644 --- a/src/Controller/OrderController.php +++ b/src/Controller/OrderController.php @@ -27,12 +27,12 @@ use Symfony\Component\Routing\Attribute\Route; use Symfony\Component\Security\Core\Exception\AccessDeniedException; use Symfony\Contracts\Service\Attribute\Required; -use Webmozart\Assert\Assert; #[AsController] final class OrderController extends BaseOrderController { private const APPLE_ERROR_RESPONSE_CODE = 0; + private const APPLE_SUCCESS_RESPONSE_CODE = 1; #[Required] @@ -178,6 +178,7 @@ public function confirmApplePayPaymentAction(Request $request): Response if (null !== $eventResponse) { return $eventResponse; } + return new JsonResponse([], Response::HTTP_BAD_REQUEST); } diff --git a/src/Creator/PayPlugPaymentDataCreator.php b/src/Creator/PayPlugPaymentDataCreator.php index e9e32322..7cd87025 100644 --- a/src/Creator/PayPlugPaymentDataCreator.php +++ b/src/Creator/PayPlugPaymentDataCreator.php @@ -19,6 +19,7 @@ use PayPlug\SyliusPayPlugPlugin\Gateway\BancontactGatewayFactory; use PayPlug\SyliusPayPlugPlugin\Gateway\OneyGatewayFactory; use PayPlug\SyliusPayPlugPlugin\Gateway\PayPlugGatewayFactory; +use PayPlug\SyliusPayPlugPlugin\Gateway\ScalapayGatewayFactory; use Sylius\Component\Core\Model\AddressInterface; use Sylius\Component\Core\Model\CustomerInterface; use Sylius\Component\Core\Model\OrderInterface; @@ -97,6 +98,10 @@ public function create( $details->offsetSet('payment_context', $this->getCartContext($order)); } + if (ScalapayGatewayFactory::FACTORY_NAME === $gatewayFactoryName) { + $details->offsetSet('payment_context', $this->getCartContext($order)); + } + $this->addPaymentMethodFieldToDetails($details, $gatewayFactoryName ?? ''); return $details; @@ -344,6 +349,7 @@ private function addPaymentMethodFieldToDetails(ArrayObject $details, string $ga BancontactGatewayFactory::FACTORY_NAME => BancontactGatewayFactory::PAYMENT_METHOD_BANCONTACT, ApplePayGatewayFactory::FACTORY_NAME => ApplePayGatewayFactory::PAYMENT_METHOD_APPLE_PAY, AmericanExpressGatewayFactory::FACTORY_NAME => AmericanExpressGatewayFactory::PAYMENT_METHOD_AMERICAN_EXPRESS, + ScalapayGatewayFactory::FACTORY_NAME => ScalapayGatewayFactory::PAYMENT_METHOD_SCALAPAY, ]; // match function is only supported by php 8. so can not use it here. foreach ($paymentMethods as $name => $method) { diff --git a/src/Creator/RefundUnitsCommandCreatorDecorator.php b/src/Creator/RefundUnitsCommandCreatorDecorator.php index 396ac3ac..1f509dc1 100644 --- a/src/Creator/RefundUnitsCommandCreatorDecorator.php +++ b/src/Creator/RefundUnitsCommandCreatorDecorator.php @@ -28,9 +28,10 @@ class RefundUnitsCommandCreatorDecorator implements RequestCommandCreatorInterface { private const MINIMUM_REFUND_AMOUNT = 10; + private const SUPPORTED_METHODS = [ PayPlugGatewayFactory::FACTORY_NAME, - OneyGatewayFactory::FACTORY_NAME + OneyGatewayFactory::FACTORY_NAME, ]; public function __construct( diff --git a/src/Entity/Traits/CustomerTrait.php b/src/Entity/Traits/CustomerTrait.php index 7d3ae3dd..fca7508d 100644 --- a/src/Entity/Traits/CustomerTrait.php +++ b/src/Entity/Traits/CustomerTrait.php @@ -6,7 +6,6 @@ use Doctrine\Common\Collections\Collection; use Doctrine\ORM\Mapping as ORM; -use PayPlug\SyliusPayPlugPlugin\ApiClient\PayPlugApiClientInterface; use PayPlug\SyliusPayPlugPlugin\Entity\Card; trait CustomerTrait diff --git a/src/Entity/Traits/PaymentMethodTrait.php b/src/Entity/Traits/PaymentMethodTrait.php index a5a6c862..bcd2e66b 100644 --- a/src/Entity/Traits/PaymentMethodTrait.php +++ b/src/Entity/Traits/PaymentMethodTrait.php @@ -6,7 +6,6 @@ use Doctrine\Common\Collections\Collection; use Doctrine\ORM\Mapping as ORM; -use PayPlug\SyliusPayPlugPlugin\ApiClient\PayPlugApiClientInterface; use PayPlug\SyliusPayPlugPlugin\Entity\Card; trait PaymentMethodTrait diff --git a/src/EventListener/PostSavePaymentMethodEventListener.php b/src/EventListener/PostSavePaymentMethodEventListener.php index 59657705..8a9a087c 100644 --- a/src/EventListener/PostSavePaymentMethodEventListener.php +++ b/src/EventListener/PostSavePaymentMethodEventListener.php @@ -13,7 +13,6 @@ use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\Routing\RouterInterface; -use function Symfony\Component\Translation\t; #[AsEventListener(event: 'sylius.payment_method.post_create', method: 'onCreate')] #[AsEventListener(event: 'sylius.payment_method.post_update', method: 'onUpdate')] @@ -76,6 +75,7 @@ private function startOAuth(PaymentMethodInterface $paymentMethod, ResourceContr // Should never happen return; } + try { $request->getSession()->set('payplug_sylius_oauth_payment_method_id', $paymentMethod->getId()); $setupRedirection = $this->router->generate('payplug_sylius_admin_auth_setup_redirection', referenceType: RouterInterface::ABSOLUTE_URL); @@ -83,6 +83,7 @@ private function startOAuth(PaymentMethodInterface $paymentMethod, ResourceContr /** * @var string $payplugRedirectUrl + * * @phpstan-ignore-next-line -- Error of return type in Payplug SDK */ $payplugRedirectUrl = Authentication::getRegisterUrl($setupRedirection, $oauthCallback); diff --git a/src/Form/Extension/PaymentTypeExtension.php b/src/Form/Extension/PaymentTypeExtension.php index 9fce7b3c..dc1889f7 100644 --- a/src/Form/Extension/PaymentTypeExtension.php +++ b/src/Form/Extension/PaymentTypeExtension.php @@ -8,9 +8,9 @@ use PayPlug\SyliusPayPlugPlugin\Gateway\OneyGatewayFactory; use PayPlug\SyliusPayPlugPlugin\Gateway\PayPlugGatewayFactory; use PayPlug\SyliusPayPlugPlugin\Provider\OneySupportedPaymentChoiceProvider; -use Sylius\Component\Payment\Model\GatewayConfigInterface; use Sylius\Bundle\CoreBundle\Form\Type\Checkout\PaymentType; use Sylius\Component\Core\Model\OrderInterface; +use Sylius\Component\Payment\Model\GatewayConfigInterface; use Symfony\Component\Form\AbstractTypeExtension; use Symfony\Component\Form\Extension\Core\Type\ChoiceType; use Symfony\Component\Form\Extension\Core\Type\TextType; diff --git a/src/Gateway/Form/Type/AbstractGatewayConfigurationType.php b/src/Gateway/Form/Type/AbstractGatewayConfigurationType.php index da325b82..80fce24a 100644 --- a/src/Gateway/Form/Type/AbstractGatewayConfigurationType.php +++ b/src/Gateway/Form/Type/AbstractGatewayConfigurationType.php @@ -24,6 +24,7 @@ class AbstractGatewayConfigurationType extends AbstractType public const VALIDATION_GROUPS = ['Default', 'sylius']; protected string $gatewayFactoryTitle = ''; + protected string $gatewayFactoryName = ''; protected string $gatewayBaseCurrencyCode = PayPlugGatewayFactory::BASE_CURRENCY_CODE; diff --git a/src/Gateway/Form/Type/ScalapayGatewayConfigurationType.php b/src/Gateway/Form/Type/ScalapayGatewayConfigurationType.php new file mode 100644 index 00000000..ec89fb96 --- /dev/null +++ b/src/Gateway/Form/Type/ScalapayGatewayConfigurationType.php @@ -0,0 +1,25 @@ + 'payplug_scalapay', + 'label' => 'payplug_sylius_payplug_plugin.ui.scalapay_gateway_label', + 'priority' => 80, + ], +)] +final class ScalapayGatewayConfigurationType extends AbstractGatewayConfigurationType +{ + protected string $gatewayFactoryTitle = ScalapayGatewayFactory::FACTORY_TITLE; + + protected string $gatewayFactoryName = ScalapayGatewayFactory::FACTORY_NAME; + + protected string $gatewayBaseCurrencyCode = ScalapayGatewayFactory::BASE_CURRENCY_CODE; +} diff --git a/src/Gateway/ScalapayGatewayFactory.php b/src/Gateway/ScalapayGatewayFactory.php new file mode 100644 index 00000000..e6a7b92a --- /dev/null +++ b/src/Gateway/ScalapayGatewayFactory.php @@ -0,0 +1,21 @@ + [ + 'min_amount' => 500, + 'max_amount' => 200000, + ], + ]; +} diff --git a/src/Gateway/Validator/Constraints/IsCanSavePaymentMethod.php b/src/Gateway/Validator/Constraints/IsCanSavePaymentMethod.php index 7bd79203..9de50f01 100644 --- a/src/Gateway/Validator/Constraints/IsCanSavePaymentMethod.php +++ b/src/Gateway/Validator/Constraints/IsCanSavePaymentMethod.php @@ -12,6 +12,7 @@ final class IsCanSavePaymentMethod extends Constraint { public string $noTestKeyMessage = 'payplug_sylius_payplug_plugin.%s.can_not_save_method_with_test_key'; + public string $noAccessMessage = 'payplug_sylius_payplug_plugin.%s.can_not_save_method_no_access'; public function validatedBy(): string diff --git a/src/Gateway/Validator/Constraints/IsOneyEnabledValidator.php b/src/Gateway/Validator/Constraints/IsOneyEnabledValidator.php index 86aefd6e..5ef51802 100644 --- a/src/Gateway/Validator/Constraints/IsOneyEnabledValidator.php +++ b/src/Gateway/Validator/Constraints/IsOneyEnabledValidator.php @@ -9,8 +9,8 @@ use PayPlug\SyliusPayPlugPlugin\Checker\OneyChecker; use PayPlug\SyliusPayPlugPlugin\Exception\GatewayConfigurationException; use PayPlug\SyliusPayPlugPlugin\Gateway\OneyGatewayFactory; -use Sylius\Component\Payment\Model\GatewayConfigInterface; use Sylius\Component\Core\Model\PaymentMethodInterface; +use Sylius\Component\Payment\Model\GatewayConfigInterface; use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\ConstraintValidator; use Symfony\Component\Validator\Exception\UnexpectedTypeException; diff --git a/src/Handler/PaymentNotificationHandler.php b/src/Handler/PaymentNotificationHandler.php index afcc8468..7e1c8851 100644 --- a/src/Handler/PaymentNotificationHandler.php +++ b/src/Handler/PaymentNotificationHandler.php @@ -48,7 +48,7 @@ public function treat( $this->entityManager->refresh($payment); - if ($details['status'] === PayPlugApiClientInterface::STATUS_ABORTED) { + if (($details['status'] ?? null) === PayPlugApiClientInterface::STATUS_ABORTED) { $lock->release(); return; @@ -92,7 +92,7 @@ public function treat( 'message' => $paymentResource->failure->message ?? '', ]; - if (PayPlugApiClientInterface::INTERNAL_STATUS_ONE_CLICK === $details['status']) { + if (PayPlugApiClientInterface::INTERNAL_STATUS_ONE_CLICK === ($details['status'] ?? null)) { $this->requestStack->getSession()->getFlashBag()->add('error', 'payplug_sylius_payplug_plugin.error.transaction_failed_1click'); } diff --git a/src/MessageHandler/RefundPaymentGeneratedHandler.php b/src/MessageHandler/RefundPaymentGeneratedHandler.php index 9bd4bc27..79e5b849 100644 --- a/src/MessageHandler/RefundPaymentGeneratedHandler.php +++ b/src/MessageHandler/RefundPaymentGeneratedHandler.php @@ -14,9 +14,9 @@ use PayPlug\SyliusPayPlugPlugin\Gateway\BancontactGatewayFactory; use PayPlug\SyliusPayPlugPlugin\Gateway\OneyGatewayFactory; use PayPlug\SyliusPayPlugPlugin\Gateway\PayPlugGatewayFactory; +use PayPlug\SyliusPayPlugPlugin\Gateway\ScalapayGatewayFactory; use PayPlug\SyliusPayPlugPlugin\PaymentProcessing\RefundPaymentProcessor; use PayPlug\SyliusPayPlugPlugin\Repository\RefundHistoryRepositoryInterface; -use Sylius\Component\Payment\Model\GatewayConfigInterface; use Psr\Log\LoggerInterface; use Sylius\Abstraction\StateMachine\StateMachineInterface; use Sylius\Component\Core\Model\OrderInterface; @@ -24,6 +24,7 @@ use Sylius\Component\Core\Model\PaymentMethodInterface; use Sylius\Component\Core\Repository\OrderRepositoryInterface; use Sylius\Component\Core\Repository\PaymentRepositoryInterface; +use Sylius\Component\Payment\Model\GatewayConfigInterface; use Sylius\Component\Resource\Repository\RepositoryInterface; use Sylius\RefundPlugin\Entity\RefundPayment; use Sylius\RefundPlugin\Event\RefundPaymentGenerated; @@ -72,6 +73,7 @@ public function __invoke(RefundPaymentGenerated $message): void BancontactGatewayFactory::FACTORY_NAME, ApplePayGatewayFactory::FACTORY_NAME, AmericanExpressGatewayFactory::FACTORY_NAME, + ScalapayGatewayFactory::FACTORY_NAME, ], true) ) { return; diff --git a/src/OrderPay/Provider/CaptureHttpResponseProvider.php b/src/OrderPay/Provider/CaptureHttpResponseProvider.php index 6fe83347..e7d7e326 100644 --- a/src/OrderPay/Provider/CaptureHttpResponseProvider.php +++ b/src/OrderPay/Provider/CaptureHttpResponseProvider.php @@ -31,6 +31,10 @@ 'payplug_sylius_payplug_plugin.http_response_provider.payplug_american_express', ['action' => PaymentRequestInterface::ACTION_CAPTURE], )] +#[AutoconfigureTag( + 'payplug_sylius_payplug_plugin.http_response_provider.payplug_scalapay', + ['action' => PaymentRequestInterface::ACTION_CAPTURE], +)] class CaptureHttpResponseProvider implements HttpResponseProviderInterface { public function supports(RequestConfiguration $requestConfiguration, PaymentRequestInterface $paymentRequest): bool diff --git a/src/PaymentProcessing/AbortPaymentProcessor.php b/src/PaymentProcessing/AbortPaymentProcessor.php index a4f85d78..2b805aac 100644 --- a/src/PaymentProcessing/AbortPaymentProcessor.php +++ b/src/PaymentProcessing/AbortPaymentProcessor.php @@ -44,6 +44,7 @@ public function process(PaymentInterface $payment): void return; } $client = $this->payplugApiClientFactory->createForPaymentMethod($method); + try { // When a payment is failed on Sylius, also abort it on PayPlug. // This should prevent the case that if we are already on PayPlug payment page diff --git a/src/PaymentProcessing/RefundPaymentHandler.php b/src/PaymentProcessing/RefundPaymentHandler.php index 70d80e53..c2ebe143 100644 --- a/src/PaymentProcessing/RefundPaymentHandler.php +++ b/src/PaymentProcessing/RefundPaymentHandler.php @@ -56,7 +56,7 @@ public function fromRequest(Refund $refund, PaymentInterface $payment): RefundUn $payment->getOrder()->getNumber(), array_merge( $this->parseIdsToUnitRefunds($items, RefundType::orderItemUnit(), OrderItemUnitRefund::class), - $this->parseIdsToUnitRefunds($shipments, RefundType::shipment(), ShipmentRefund::class) + $this->parseIdsToUnitRefunds($shipments, RefundType::shipment(), ShipmentRefund::class), ), $payment->getMethod()->getId(), // @phpstan-ignore-line '', diff --git a/src/PaymentProcessing/RefundPaymentProcessor.php b/src/PaymentProcessing/RefundPaymentProcessor.php index d9c62382..54fe0d51 100644 --- a/src/PaymentProcessing/RefundPaymentProcessor.php +++ b/src/PaymentProcessing/RefundPaymentProcessor.php @@ -14,11 +14,10 @@ use PayPlug\SyliusPayPlugPlugin\Gateway\OneyGatewayFactory; use PayPlug\SyliusPayPlugPlugin\Gateway\PayPlugGatewayFactory; use PayPlug\SyliusPayPlugPlugin\Repository\RefundHistoryRepositoryInterface; -use Sylius\Component\Payment\Model\GatewayConfigInterface; -use Webmozart\Assert\Assert; use Psr\Log\LoggerInterface; use Sylius\Component\Core\Model\PaymentInterface; use Sylius\Component\Core\Model\PaymentMethodInterface; +use Sylius\Component\Payment\Model\GatewayConfigInterface; use Sylius\Component\Payment\PaymentTransitions; use Sylius\Component\Resource\Exception\UpdateHandlingException; use Sylius\Component\Resource\Repository\RepositoryInterface; @@ -28,6 +27,7 @@ use Symfony\Component\Workflow\Attribute\AsCompletedListener; use Symfony\Component\Workflow\Event\CompletedEvent; use Symfony\Contracts\Translation\TranslatorInterface; +use Webmozart\Assert\Assert; #[Autoconfigure(public: true)] final class RefundPaymentProcessor implements PaymentProcessorInterface diff --git a/src/Provider/AbstractSupportedRefundPaymentMethodsProvider.php b/src/Provider/AbstractSupportedRefundPaymentMethodsProvider.php index 2336059d..42e88191 100644 --- a/src/Provider/AbstractSupportedRefundPaymentMethodsProvider.php +++ b/src/Provider/AbstractSupportedRefundPaymentMethodsProvider.php @@ -4,11 +4,11 @@ namespace PayPlug\SyliusPayPlugPlugin\Provider; -use Sylius\Component\Payment\Model\GatewayConfigInterface; use Sylius\Component\Core\Model\OrderInterface; use Sylius\Component\Core\Model\PaymentInterface; use Sylius\Component\Core\Model\PaymentMethodInterface; use Sylius\Component\Core\Repository\OrderRepositoryInterface; +use Sylius\Component\Payment\Model\GatewayConfigInterface; use Sylius\RefundPlugin\Provider\RefundPaymentMethodsProviderInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\RequestStack; diff --git a/src/Provider/OneySupportedPaymentChoiceProvider.php b/src/Provider/OneySupportedPaymentChoiceProvider.php index 64309253..9635b7b3 100644 --- a/src/Provider/OneySupportedPaymentChoiceProvider.php +++ b/src/Provider/OneySupportedPaymentChoiceProvider.php @@ -6,8 +6,8 @@ use PayPlug\SyliusPayPlugPlugin\Gateway\OneyGatewayFactory; use PayPlug\SyliusPayPlugPlugin\Repository\PaymentMethodRepositoryInterface; -use Sylius\Component\Payment\Model\GatewayConfigInterface; use Sylius\Component\Core\Model\PaymentMethodInterface; +use Sylius\Component\Payment\Model\GatewayConfigInterface; class OneySupportedPaymentChoiceProvider { diff --git a/src/Provider/Payment/ApplePayPaymentProvider.php b/src/Provider/Payment/ApplePayPaymentProvider.php index 74f9b93c..c0b4fb49 100644 --- a/src/Provider/Payment/ApplePayPaymentProvider.php +++ b/src/Provider/Payment/ApplePayPaymentProvider.php @@ -5,7 +5,6 @@ namespace PayPlug\SyliusPayPlugPlugin\Provider\Payment; use DateTimeImmutable; -use Doctrine\ORM\EntityManagerInterface; use LogicException; use Payplug\Resource\IVerifiableAPIResource; use Payplug\Resource\Payment; @@ -109,6 +108,7 @@ public function patch(Request $request, OrderInterface $order): PaymentInterface if (!$lastPayment instanceof PaymentInterface) { $this->logger->error('[Payplug] No new payment found for order', ['order' => $order]); + throw new LogicException(); } @@ -123,6 +123,7 @@ public function patch(Request $request, OrderInterface $order): PaymentInterface $paymentResource = $this->applePayClient->retrieve($lastPayment->getDetails()['payment_id']); $this->logger->notice('[Payplug] ApplePay payment resource', ['payment' => (array) $paymentResource]); + try { $token = $request->request->all('token'); if ([] === $token) { @@ -167,6 +168,7 @@ public function patch(Request $request, OrderInterface $order): PaymentInterface } catch (\Exception $exception) { $this->logger->error('[Payplug] ApplePay payment update failed', ['exception' => $exception, 'message' => $exception->getMessage()]); $this->applyRequiredPaymentTransition($lastPayment, PaymentInterface::STATE_FAILED); + try { $paymentResource->abort($this->applePayClient->getConfiguration()); } catch (\Throwable $throwable) { diff --git a/src/Provider/ScalapaySupportedRefundPaymentMethodsProviderDecorator.php b/src/Provider/ScalapaySupportedRefundPaymentMethodsProviderDecorator.php new file mode 100644 index 00000000..ae4c7ede --- /dev/null +++ b/src/Provider/ScalapaySupportedRefundPaymentMethodsProviderDecorator.php @@ -0,0 +1,15 @@ +createQueryBuilder('o') + /** @var PaymentInterface|null $result */ + $result = $this->createQueryBuilder('o') ->where('o.details LIKE :payplugPaymentId') ->setParameter('payplugPaymentId', '%' . $payplugPaymentId . '%') ->getQuery() ->setMaxResults(1) ->getOneOrNullResult() ; + + return $result; } /** diff --git a/src/Resolver/OneyPaymentMethodsResolverDecorator.php b/src/Resolver/OneyPaymentMethodsResolverDecorator.php index e555f96e..73802220 100644 --- a/src/Resolver/OneyPaymentMethodsResolverDecorator.php +++ b/src/Resolver/OneyPaymentMethodsResolverDecorator.php @@ -6,11 +6,11 @@ use PayPlug\SyliusPayPlugPlugin\Checker\OneyCheckerInterface; use PayPlug\SyliusPayPlugPlugin\Gateway\OneyGatewayFactory; -use Sylius\Component\Payment\Model\GatewayConfigInterface; use Sylius\Component\Core\Model\OrderInterface; use Sylius\Component\Core\Model\Payment; use Sylius\Component\Core\Model\PaymentMethodInterface; use Sylius\Component\Currency\Context\CurrencyContextInterface; +use Sylius\Component\Payment\Model\GatewayConfigInterface; use Sylius\Component\Payment\Model\PaymentInterface as BasePaymentInterface; use Sylius\Component\Payment\Resolver\PaymentMethodsResolverInterface; use Symfony\Component\DependencyInjection\Attribute\AsDecorator; diff --git a/src/Resolver/PaymentStateResolver.php b/src/Resolver/PaymentStateResolver.php index 1b338106..1c8de161 100644 --- a/src/Resolver/PaymentStateResolver.php +++ b/src/Resolver/PaymentStateResolver.php @@ -8,14 +8,12 @@ use Payplug\Resource\Payment; use Payplug\Resource\PaymentAuthorization; use PayPlug\SyliusPayPlugPlugin\ApiClient\PayPlugApiClientFactory; -use PayPlug\SyliusPayPlugPlugin\ApiClient\PayPlugApiClientInterface; use PayPlug\SyliusPayPlugPlugin\Gateway\PayPlugGatewayFactory; -use Sylius\Component\Payment\Model\GatewayConfigInterface; use Sylius\Abstraction\StateMachine\StateMachineInterface; use Sylius\Component\Core\Model\PaymentInterface; use Sylius\Component\Core\Model\PaymentMethodInterface; +use Sylius\Component\Payment\Model\GatewayConfigInterface; use Sylius\Component\Payment\PaymentTransitions; -use Symfony\Component\DependencyInjection\Attribute\Autowire; final class PaymentStateResolver implements PaymentStateResolverInterface { diff --git a/src/Resolver/ScalapayPaymentMethodsResolverDecorator.php b/src/Resolver/ScalapayPaymentMethodsResolverDecorator.php new file mode 100644 index 00000000..7dc3a306 --- /dev/null +++ b/src/Resolver/ScalapayPaymentMethodsResolverDecorator.php @@ -0,0 +1,43 @@ +decorated->getSupportedMethods($subject); + + return $this->supportedMethodsProvider->provide( + $supportedMethods, + ScalapayGatewayFactory::FACTORY_NAME, + ScalapayGatewayFactory::AUTHORIZED_CURRENCIES, + $subject->getAmount() ?? 0, + ); + } + + public function supports(BasePaymentInterface $subject): bool + { + return $this->decorated->supports($subject); + } +} diff --git a/src/Validator/PaymentMethodValidator.php b/src/Validator/PaymentMethodValidator.php index bb88cf29..34b826e2 100644 --- a/src/Validator/PaymentMethodValidator.php +++ b/src/Validator/PaymentMethodValidator.php @@ -1,5 +1,7 @@ $this->processBancontact($paymentMethod), AmericanExpressGatewayFactory::FACTORY_NAME => $this->processAmex($paymentMethod), ApplePayGatewayFactory::FACTORY_NAME => $this->processApplePay($paymentMethod), - default => throw new \InvalidArgumentException("Unsupported payment method"), + ScalapayGatewayFactory::FACTORY_NAME => $this->processScalapay($paymentMethod), + default => throw new \InvalidArgumentException('Unsupported payment method'), }; foreach ($errors as $error) { @@ -78,24 +82,35 @@ private function processPayplug(PaymentMethodInterface $paymentMethod): Constrai private function processOney(PaymentMethodInterface $paymentMethod): ConstraintViolationListInterface { $constraintList = [new IsOneyEnabled()]; + return $this->validator->validate($paymentMethod, $constraintList, self::VALIDATION_GROUPS); } private function processBancontact(PaymentMethodInterface $paymentMethod): ConstraintViolationListInterface { $constraintList = [new IsCanSavePaymentMethod()]; + return $this->validator->validate($paymentMethod, $constraintList, self::VALIDATION_GROUPS); } private function processAmex(PaymentMethodInterface $paymentMethod): ConstraintViolationListInterface { $constraintList = [new IsCanSavePaymentMethod()]; + return $this->validator->validate($paymentMethod, $constraintList, self::VALIDATION_GROUPS); } private function processApplePay(PaymentMethodInterface $paymentMethod): ConstraintViolationListInterface { $constraintList = [new IsCanSavePaymentMethod()]; + + return $this->validator->validate($paymentMethod, $constraintList, self::VALIDATION_GROUPS); + } + + private function processScalapay(PaymentMethodInterface $paymentMethod): ConstraintViolationListInterface + { + $constraintList = [new IsCanSavePaymentMethod()]; + return $this->validator->validate($paymentMethod, $constraintList, self::VALIDATION_GROUPS); } } diff --git a/templates/shop/select_payment/_scalapay.html.twig b/templates/shop/select_payment/_scalapay.html.twig new file mode 100644 index 00000000..d5d83f02 --- /dev/null +++ b/templates/shop/select_payment/_scalapay.html.twig @@ -0,0 +1,7 @@ +{% set form = hookable_metadata.context.form %} + +
diff --git a/tests/TestApplication/config/bundles.php b/tests/TestApplication/config/bundles.php index 48514e52..39254569 100644 --- a/tests/TestApplication/config/bundles.php +++ b/tests/TestApplication/config/bundles.php @@ -1,5 +1,7 @@ ['all' => true], Knp\Bundle\SnappyBundle\KnpSnappyBundle::class => ['all' => true], diff --git a/tests/TestApplication/src/Entity/PaymentMethod.php b/tests/TestApplication/src/Entity/PaymentMethod.php index 7dad0a1c..4aa8c0cc 100644 --- a/tests/TestApplication/src/Entity/PaymentMethod.php +++ b/tests/TestApplication/src/Entity/PaymentMethod.php @@ -8,8 +8,8 @@ use Doctrine\ORM\Mapping as ORM; use PayPlug\SyliusPayPlugPlugin\Entity\Traits\PaymentMethodTrait; use Sylius\Component\Core\Model\PaymentMethod as BasePaymentMethod; -use Sylius\Component\Payment\Model\PaymentMethodTranslationInterface; use Sylius\Component\Payment\Model\PaymentMethodTranslation; +use Sylius\Component\Payment\Model\PaymentMethodTranslationInterface; #[ORM\Entity] #[ORM\Table(name: 'sylius_payment_method')] diff --git a/translations/messages.en.yml b/translations/messages.en.yml index c62ba008..97b5424c 100644 --- a/translations/messages.en.yml +++ b/translations/messages.en.yml @@ -24,6 +24,7 @@ payplug_sylius_payplug_plugin: confirmation_modal: confirm_card_deletion: Are you sure you want to delete this card? bancontact_gateway_label: Bancontact by Payplug + scalapay_gateway_label: Scalapay by PayPlug apple_pay_gateway_label: Apple Pay by Payplug american_express_gateway_label: American Express by Payplug apple_pay_not_available: Apple Pay is not available on this browser or device. diff --git a/translations/messages.fr.yml b/translations/messages.fr.yml index 9a9de68e..42193f9b 100644 --- a/translations/messages.fr.yml +++ b/translations/messages.fr.yml @@ -24,6 +24,7 @@ payplug_sylius_payplug_plugin: confirmation_modal: confirm_card_deletion: Êtes-vous sûr(e) de vouloir supprimer cette carte ? bancontact_gateway_label: Bancontact by Payplug + scalapay_gateway_label: Scalapay by PayPlug apple_pay_gateway_label: Apple Pay by Payplug american_express_gateway_label: American Express by Payplug apple_pay_not_available: Apple Pay n'est pas disponible sur ce navigateur ou appareil. diff --git a/translations/messages.it.yml b/translations/messages.it.yml index cc97e1ed..b9170f12 100644 --- a/translations/messages.it.yml +++ b/translations/messages.it.yml @@ -24,6 +24,7 @@ payplug_sylius_payplug_plugin: confirmation_modal: confirm_card_deletion: Desideri cancellare questa carta? bancontact_gateway_label: Bancontact by Payplug + scalapay_gateway_label: Scalapay by PayPlug apple_pay_gateway_label: Apple Pay by Payplug american_express_gateway_label: American Express by Payplug apple_pay_not_available: Apple Pay non è disponibile su questo browser o dispositivo. diff --git a/translations/validators.en.yml b/translations/validators.en.yml index 8ca73234..a5fc5a83 100644 --- a/translations/validators.en.yml +++ b/translations/validators.en.yml @@ -25,6 +25,14 @@ payplug_sylius_payplug_plugin: To activate Bancontact, please fill in this form and activate the LIVE mode. + payplug_scalapay: + can_not_save_method_with_test_key: | + The Scalapay payment method is not available for the TEST mode. + Please activate the LIVE mode. + can_not_save_method_no_access: | + You don't have access to this feature yet. + To activate Scalapay, please contact us at support@payplug.com + and activate the LIVE mode. payplug_apple_pay: can_not_save_method_with_test_key: | The Apple Pay payment method is not available for the TEST mode. diff --git a/translations/validators.fr.yml b/translations/validators.fr.yml index 4da2b61b..128c6ffb 100644 --- a/translations/validators.fr.yml +++ b/translations/validators.fr.yml @@ -24,6 +24,14 @@ payplug_sylius_payplug_plugin: Pour activer Bancontact, rendez-vous sur ce formulaire et activez le mode LIVE. + payplug_scalapay: + can_not_save_method_with_test_key: | + Le paiement par Scalapay n'est pas disponible en mode TEST. + Veuillez activer le mode LIVE. + can_not_save_method_no_access: | + Vous n'avez pas accès à cette fonctionnalité. + Pour activer Scalapay, contactez-nous à support@payplug.com + et activez le mode LIVE. payplug_apple_pay: can_not_save_method_with_test_key: | Le paiement par Apple Pay n’est pas disponible en mode TEST. diff --git a/translations/validators.it.yml b/translations/validators.it.yml index fdd6fe52..efaf49df 100644 --- a/translations/validators.it.yml +++ b/translations/validators.it.yml @@ -24,6 +24,14 @@ payplug_sylius_payplug_plugin: Per attivare Bancontact, compila questo modulo e attiva la modalità LIVE. + payplug_scalapay: + can_not_save_method_with_test_key: | + Il metodo di pagamento Scalapay non è disponibile in modalità TEST. + Attiva la modalità LIVE. + can_not_save_method_no_access: | + Non puoi ancora accedere a questa funzionalità. + Per attivare Scalapay, contattaci a support@payplug.com + e attiva la modalità LIVE. payplug_american_express: can_not_save_method_with_test_key: | Il pagamento Apple Pay non è disponibile in modalità TEST.