diff --git a/.github/workflows/analysis.yaml b/.github/workflows/analysis.yaml
new file mode 100644
index 00000000..8411f1b8
--- /dev/null
+++ b/.github/workflows/analysis.yaml
@@ -0,0 +1,86 @@
+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/ci.yaml b/.github/workflows/ci.yaml
deleted file mode 100644
index e63065b4..00000000
--- a/.github/workflows/ci.yaml
+++ /dev/null
@@ -1,205 +0,0 @@
-name: CI
-
-on:
- push:
- branches: [develop, qa, master]
- paths-ignore:
- - README.md
- pull_request:
- paths-ignore:
- - README.md
-
-jobs:
- php:
- name: PHP ${{ matrix.php }} Symfony ${{ matrix.symfony }}
- runs-on: ubuntu-18.04
-
- strategy:
- fail-fast: false
- matrix:
- php: [7.4, 8.0]
- symfony: [4.4.*, 5.2.*]
-
- 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@v2
- 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-
-
- - 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
-
-# Disable in cause of error occurs symplify/symplify#2873
-# - name: ECS - Run
-# run: if [ -f rulesets/ecs.php ]; then vendor/bin/ecs check src/ tests/Behat/ --no-progress-bar -c rulesets/ecs.php ; else echo Ecs ruleset file does not exist, skipping step ; fi
-# if: always() && steps.end-of-setup.outcome == 'success'
-
- - name: PHPStan - Run
- run: if [ -f rulesets/phpstan.neon ]; then vendor/bin/phpstan analyse -c rulesets/phpstan.neon src/ ; else echo PHPStan rulesets file does not exist, skipping step ; fi
- if: always() && steps.end-of-setup.outcome == 'success'
-
- - name: PHPSpec - Run
- run: if [ -f phpspec.yml.dist ]; then vendor/bin/phpspec run ; else echo PHPSpec config file does not exist, skipping step ; fi
- if: always() && steps.end-of-setup.outcome == 'success'
-
- - name: Checks security issues - Run
- run: symfony security:check
- if: always() && steps.end-of-setup.outcome == 'success'
-
- sylius:
- name: PHPUnit-Behat (PHP ${{ matrix.php }} Sylius ${{ matrix.sylius }} Symfony ${{ matrix.symfony }})
- runs-on: ubuntu-18.04
- strategy:
- fail-fast: false
- matrix:
- php: [7.4, 8.0]
- sylius: [1.9.0, 1.10.0]
- symfony: [4.4, 5.2]
- node: [10.x]
- exclude:
- - sylius: 1.9.0
- php: 8.0
- - sylius: 1.10.0
- symfony: 4.4
-
- env:
- APP_ENV: test
- package-name: payplug/sylius-payplug-plugin
-
- steps:
- - name: Setup PHP
- uses: shivammathur/setup-php@v2
- with:
- php-version: "${{ matrix.php }}"
- ini-values: date.timezone=UTC
- extensions: intl
- tools: symfony
- coverage: none
-
- - name: Setup Node
- uses: actions/setup-node@v1
- with:
- node-version: "${{ matrix.node }}"
-
- - name: Wkhtmltopdf - Install
- run: |
- sudo apt-get update
- sudo apt-get install xvfb libfontconfig wkhtmltopdf
- printf '#!/bin/bash\nxvfb-run -a --server-args="-screen 0, 1024x768x24" /usr/bin/wkhtmltopdf -q $*' | sudo tee /usr/bin/wkhtmltopdf.sh
- sudo chmod a+x /usr/bin/wkhtmltopdf.sh
- sudo ln -s /usr/bin/wkhtmltopdf.sh /usr/local/bin/wkhtmltopdf
-
- - uses: actions/checkout@v2
-
- - 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@v2
- id: cache-composer
- with:
- path: ${{ steps.composer-cache.outputs.dir }}
- key: php-${{ matrix.php }}-sylius-${{ matrix.sylius }}-symfony-${{ matrix.symfony }}-composer-${{ hashFiles('**/composer.json') }}
- restore-keys: php-${{ matrix.php }}-sylius-${{ matrix.sylius }}-symfony-${{ matrix.symfony }}-composer-
-
- - name: Composer - Create cache directory
- run: mkdir -p /home/runner/.composer/cache
- if: steps.cache-composer.outputs.cache-hit != 'true'
-
- - name: Composer - Github Auth
- run: composer config -g github-oauth.github.com ${{ github.token }}
-
- - name: Yarn - Get cache directory
- id: yarn-cache
- run: echo "::set-output name=dir::$(yarn cache dir)"
-
- - name: Yarn - Set Cache
- uses: actions/cache@v2
- with:
- path: ${{ steps.yarn-cache.outputs.dir }}
- key: node-${{ matrix.node }}-yarn-${{ hashFiles('**/package.json **/yarn.lock') }}
- restore-keys: |
- node-${{ matrix.node }}-yarn-
-
- - name: Install Sylius-Standard and Plugin
- run: make install -e SYLIUS_VERSION=${{ matrix.sylius }} SYMFONY_VERSION=${{ matrix.symfony }}
-
- - name: Output PHP version for Symfony CLI
- working-directory: ./tests/Application
- run: php -v | head -n 1 | awk '{ print $2 }' > .php-version
-
- - name: Install certificates
- working-directory: ./tests/Application
- run: symfony server:ca:install
-
- - name: Run Chrome headless
- working-directory: ./tests/Application
- run: google-chrome-stable --enable-automation --disable-background-networking --no-default-browser-check --no-first-run --disable-popup-blocking --disable-default-apps --allow-insecure-localhost --disable-translate --disable-extensions --no-sandbox --enable-features=Metal --headless --remote-debugging-port=9222 --window-size=2880,1800 --proxy-server='direct://' --proxy-bypass-list='*' https://127.0.0.1 > /dev/null 2>&1 &
-
- - name: Run webserver
- working-directory: ./tests/Application
- run: symfony server:start --port=8080 --dir=public --daemon
- id: end-of-setup-sylius
-
- - name: Doctrine Schema Validate - Run
- working-directory: ./tests/Application
- run: php bin/console doctrine:schema:validate --skip-sync
- if: always() && steps.end-of-setup-sylius.outcome == 'success'
-
- - name: Run PHPUnit
- run: make phpunit
- if: always() && steps.end-of-setup-sylius.outcome == 'success'
-
- - name: Configure Behat
- run: make behat-configure
- if: always() && steps.end-of-setup-sylius.outcome == 'success'
-
- - name: Run behat
- working-directory: ./tests/Application
- run: vendor/bin/behat --strict --no-interaction -f progress || vendor/bin/behat --strict -vvv --no-interaction --rerun
- if: always() && steps.end-of-setup-sylius.outcome == 'success'
-
- - uses: actions/upload-artifact@v2.1.4
- if: failure()
- with:
- name: logs
- path: ./tests/Application/etc/build
-
- services:
- mariadb:
- image: mariadb:10.4.11
- ports:
- - 3306:3306
- env:
- MYSQL_ALLOW_EMPTY_PASSWORD: true
- options: --health-cmd="mysqladmin ping" --health-interval=5s --health-timeout=2s --health-retries=3
diff --git a/.github/workflows/jira.yml b/.github/workflows/jira.yml
new file mode 100644
index 00000000..45e4e4e8
--- /dev/null
+++ b/.github/workflows/jira.yml
@@ -0,0 +1,32 @@
+name: CREATE JIRA ISSUE
+
+on:
+ issues:
+ types: [opened, edited]
+
+jobs:
+ jira:
+ name: Create Jira issue
+ runs-on: ubuntu-latest
+ steps:
+ - name: Jira Login
+ uses: atlassian/gajira-login@master
+ env:
+ JIRA_BASE_URL: ${{ secrets.JIRA_BASE_URL }}
+ JIRA_USER_EMAIL: ${{ secrets.JIRA_USER_EMAIL }}
+ JIRA_API_TOKEN: ${{ secrets.JIRA_API_TOKEN }}
+
+ - name: Create
+ id: create
+ uses: atlassian/gajira-create@master
+ env:
+ TITLE_ISSUE: ${{ github.event.issue.title }}
+ with:
+ project: SMP
+ issuetype: Sylius Technical Support
+ summary: "${{ env.TITLE_ISSUE }} #${{ github.event.issue.number }}"
+ # Valeur par default "from github" en MVP
+ fields: '{"customfield_10150": "${{ github.event.issue.html_url }} \n\nCreated from GitHub Action", "customfield_10106": "from github","customfield_10105": "https://www.from-github.fr","customfield_10108": "from github", "customfield_10145": "from github"}'
+
+ - name: Log
+ run: echo "Created issue ${{ steps.create.outputs.issue }}"
diff --git a/.github/workflows/pull_request_template.md b/.github/workflows/pull_request_template.md
new file mode 100644
index 00000000..c90cc2c8
--- /dev/null
+++ b/.github/workflows/pull_request_template.md
@@ -0,0 +1,5 @@
+# ⚠️ Requirements
+Reviewer, please take a look at those requirements:
+
+- [ ] Check that plugin version has been upgrated and are identical in both `composer.json` and `src/PayPlugSyliusPayPlugPlugin.php` files
+
diff --git a/.github/workflows/sylius.yaml b/.github/workflows/sylius.yaml
new file mode 100644
index 00000000..33dc6c31
--- /dev/null
+++ b/.github/workflows/sylius.yaml
@@ -0,0 +1,105 @@
+name: Sylius
+'on':
+ push:
+ branches:
+ - develop
+ - qa
+ - master
+ paths-ignore:
+ - README.md
+ pull_request:
+ paths-ignore:
+ - README.md
+jobs:
+ sylius:
+ name: 'PHPUnit-Behat (PHP ${{ matrix.php }} Sylius ${{ matrix.sylius }} Symfony ${{ matrix.symfony }})'
+ runs-on: ubuntu-latest
+ strategy:
+ fail-fast: false
+ matrix:
+ php:
+ - 8.2
+ - 8.4
+ sylius:
+ - 2.1.0
+ - 2.0.0
+ symfony:
+ - 6.4
+ - 7.3
+ node:
+ - 20.x
+ env:
+ APP_ENV: test
+ package-name: payplug/sylius-payplug-plugin
+ steps:
+ -
+ name: 'Setup PHP'
+ uses: shivammathur/setup-php@v2
+ with:
+ php-version: '${{ matrix.php }}'
+ ini-values: date.timezone=UTC
+ extensions: intl
+ tools: symfony
+ coverage: none
+ -
+ name: 'Setup Node'
+ uses: actions/setup-node@v3
+ with:
+ node-version: '${{ matrix.node }}'
+ -
+ uses: actions/checkout@v3
+ -
+ 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
+ id: cache-composer
+ with:
+ path: '${{ steps.composer-cache.outputs.dir }}'
+ key: 'php-${{ matrix.php }}-sylius-${{ matrix.sylius }}-symfony-${{ matrix.symfony }}-composer-${{ hashFiles(''**/composer.json'') }}'
+ restore-keys: 'php-${{ matrix.php }}-sylius-${{ matrix.sylius }}-symfony-${{ matrix.symfony }}-composer-'
+ -
+ name: 'Composer - Create cache directory'
+ run: 'mkdir -p /home/runner/.composer/cache'
+ if: 'steps.cache-composer.outputs.cache-hit != ''true'''
+ -
+ name: 'Composer - Github Auth'
+ run: 'composer config -g github-oauth.github.com ${{ github.token }}'
+ -
+ name: 'Yarn - Get cache directory'
+ id: yarn-cache
+ run: 'echo "dir=$(yarn cache dir)" >> $GITHUB_OUTPUT'
+ -
+ name: 'Yarn - Set Cache'
+ uses: actions/cache@v4
+ with:
+ path: '${{ steps.yarn-cache.outputs.dir }}'
+ key: 'node-${{ matrix.node }}-yarn-${{ hashFiles(''**/package.json **/yarn.lock'') }}'
+ restore-keys: "node-${{ matrix.node }}-yarn-\n"
+ -
+ name: 'Install Sylius-Standard and Plugin'
+ run: 'make install -e SYLIUS_VERSION=${{ matrix.sylius }} SYMFONY_VERSION=${{ matrix.symfony }}'
+ id: end-of-setup-sylius
+ -
+ name: 'Doctrine Schema Validate - Run'
+ run: 'vendor/bin/console doctrine:schema:validate --skip-sync'
+ -
+ name: 'Run PHPUnit'
+ run: 'make phpunit'
+ if: 'always() && steps.end-of-setup-sylius.outcome == ''success'''
+ -
+ uses: actions/upload-artifact@v4
+ if: failure()
+ with:
+ name: logs
+ path: ./tests/Application/etc/build
+ services:
+ mariadb:
+ image: 'mariadb:10.4.11'
+ ports:
+ - '3306:3306'
+ env:
+ MYSQL_ALLOW_EMPTY_PASSWORD: true
+ options: '--health-cmd="mysqladmin ping" --health-interval=5s --health-timeout=2s --health-retries=3'
diff --git a/.gitignore b/.gitignore
index f3101a24..cde5dab0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,7 +5,7 @@
/etc/build/*
!/etc/build/.gitignore
-/tests/Application/yarn.lock
+/tests/Application/*
/behat.yml
/phpspec.yml
@@ -13,3 +13,14 @@
/.idea
*.map
+
+/tests/TestApplication/.env.local
+/tests/TestApplication/.env.*.local
+/var/
+
+
+Audit.md
+CLAUDE.md
+.DS_Store
+.claude
+.review
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 00000000..7be32a5b
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,33 @@
+# Changelog
+
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
+and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+
+## [2.0.0] - Unreleased
+
+### Added
+- Support for Sylius v2.0+
+- PHP 8.2+ compatibility
+- Use Payment Request API from Sylius
+- New Unified Authentication System (OAuth2)
+
+> [!IMPORTANT]
+> Merchants will need to contact support to switch to the new authentication method.
+
+### Changed
+- Plugin structure has been changed to follow the new Symfony bundle structure
+- Front assets have been migrated to use Stimulus
+
+### Removed
+- Drop Payum support
+- Drop Sylius 1.x support
+- Drop usage of Secret key - Use OAuth2 instead
+
+Please refer to [github releases](https://github.com/payplug/SyliusPayPlugPlugin/releases) for historical release information.
+
+---
+
+For migration guides and upgrade instructions, see [UPGRADE.md](UPGRADE.md).
+For contributing guidelines, see [CONTRIBUTING.md](CONTRIBUTING.md).
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 59b089dc..8c9b7307 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -3,10 +3,10 @@
From the plugin root directory, run the following commands:
```bash
-$ make install -e SYLIUS_VERSION=XX SYMFONY_VERSION=YY
+$ make install -e SYLIUS_VERSION=XX SYMFONY_VERSION=YY PHP_VERSION=ZZ
```
-Default values : XX=1.9.0 and YY=5.2
+Default values : XX=1.14.0 and YY=6.4 and ZZ=8.2
To be able to setup the plugin database, remember to configure you database credentials
in `install/Application/.env.local` and `install/Application/.env.test.local`.
@@ -22,7 +22,7 @@ $ make reset
- GrumPHP (see configuration [grumphp.yml](grumphp.yml).)
- GrumPHP is executed by the Git pre-commit hook, but you can launch it manualy with :
+ GrumPHP is executed by the Git pre-commit hook, but you can launch it manually with :
```bash
$ make grumphp
diff --git a/Makefile b/Makefile
index 3ba136cd..b3007cc4 100644
--- a/Makefile
+++ b/Makefile
@@ -1,100 +1,58 @@
.DEFAULT_GOAL := help
SHELL=/bin/bash
COMPOSER_ROOT=composer
-TEST_DIRECTORY=tests/Application
-CONSOLE=cd tests/Application && php bin/console -e test
-COMPOSER=cd tests/Application && composer
-YARN=cd tests/Application && yarn
-
-SYLIUS_VERSION=1.9.0
-SYMFONY_VERSION=4.4
+TEST_DIRECTORY=tests/TestApplication
+CONSOLE=vendor/bin/console
+COMPOSER=composer
+SYLIUS_VERSION=2.1.0
+SYMFONY_VERSION=6.4
PLUGIN_NAME=payplug/sylius-payplug-plugin
###
### DEVELOPMENT
### ¯¯¯¯¯¯¯¯¯¯¯
-
-install: sylius ## Install Plugin on Sylius [SyliusVersion=1.9] [SymfonyVersion=5.2]
+install: sylius ## Install all dependencies with [SYLIUS_VERSION=2.1.0] [SYMFONY_VERSION=6.4]
.PHONY: install
reset: ## Remove dependencies
- rm -rf tests/Application
+ ${CONSOLE} doctrine:database:drop --force --if-exists || true
+ rm -rf vendor
.PHONY: reset
-phpunit: phpunit-configure phpunit-run ## Run PHPUnit
+phpunit: ## Run PHPUnit tests
+ ./vendor/bin/phpunit
.PHONY: phpunit
###
### OTHER
### ¯¯¯¯¯¯
-
-sylius: sylius-standard update-dependencies install-plugin install-sylius
+sylius: install-sylius
.PHONY: sylius
-sylius-standard:
- ${COMPOSER_ROOT} create-project sylius/sylius-standard ${TEST_DIRECTORY} "~${SYLIUS_VERSION}"
-
-update-dependencies:
- ${COMPOSER} config extra.symfony.require "^${SYMFONY_VERSION}"
- ${COMPOSER} require --dev donatj/mock-webserver:^2.1 --no-scripts --no-update
-# FIX since https://github.com/Sylius/Sylius/pull/13215 is not merged
- ${COMPOSER} require doctrine/dbal:"^2.6" doctrine/orm:"^2.9" --no-scripts --no-update
-ifeq ($(SYMFONY_VERSION), 4.4)
- ${COMPOSER} require sylius/admin-api-bundle --no-scripts --no-update
-endif
-ifeq ($(SYLIUS_VERSION), 1.8.0)
- ${COMPOSER} update --no-progress --no-scripts --prefer-dist -n
-endif
- ${COMPOSER} update --no-progress -n
-
-install-plugin:
- ${COMPOSER} config repositories.plugin '{"type": "path", "url": "../../"}'
- ${COMPOSER} config extra.symfony.allow-contrib true
- ${COMPOSER} config minimum-stability "dev"
- ${COMPOSER} config prefer-stable true
- ${COMPOSER} req ${PLUGIN_NAME}:* --prefer-source --no-scripts
- ${COMPOSER} symfony:recipes:install "${PLUGIN_NAME}" --force
-
- cp -r install/Application tests
- sed -i "4a \ \ \ \ form_themes: ['form/form_gateway_config_row.html.twig']" ${TEST_DIRECTORY}/config/packages/twig.yaml
- mkdir -p ${TEST_DIRECTORY}/templates/form/
- cp -R src/Resources/views/form/* ${TEST_DIRECTORY}/templates/form/
- mkdir -p ${TEST_DIRECTORY}/templates/bundles/SyliusAdminBundle/
- cp -R src/Resources/views/SyliusAdminBundle/* ${TEST_DIRECTORY}/templates/bundles/SyliusAdminBundle/
-
- # For Refund Plugin
- cp -R ${TEST_DIRECTORY}/vendor/sylius/refund-plugin/src/Resources/views/SyliusAdminBundle/* ${TEST_DIRECTORY}/templates/bundles/SyliusAdminBundle/
-
install-sylius:
- ${CONSOLE} sylius:install -n -s default
- ${YARN} install
- ${YARN} build
- ${CONSOLE} translation:update en PayPlugSyliusPayPlugPlugin --dump-messages
- ${CONSOLE} translation:update fr PayPlugSyliusPayPlugPlugin --dump-messages
- ${CONSOLE} cache:clear
+ @echo "Installing Sylius ${SYLIUS_VERSION} using TestApplication"
+ ${COMPOSER} config extra.symfony.require "^${SYMFONY_VERSION}"
+ ${COMPOSER} install
+ ${COMPOSER} require --dev sylius/test-application:"^${SYLIUS_VERSION}@alpha" -n -W # TODO: Remove alpha when stable
+ ${COMPOSER} test-application:install
-phpunit-configure:
- cp phpunit.xml.dist ${TEST_DIRECTORY}/phpunit.xml
- echo -e "\nMOCK_SERVER_HOST=localhost\nMOCK_SERVER_PORT=8987\n" >> ${TEST_DIRECTORY}/.env.test.local
-phpunit-run:
- cd ${TEST_DIRECTORY} && ./vendor/bin/phpunit
behat-configure: ## Configure Behat
(cd ${TEST_DIRECTORY} && cp behat.yml.dist behat.yml)
(cd ${TEST_DIRECTORY} && sed -i "s#vendor/sylius/sylius/src/Sylius/Behat/Resources/config/suites.yml#vendor/${PLUGIN_NAME}/tests/Behat/Resources/suites.yml#g" behat.yml)
(cd ${TEST_DIRECTORY} && sed -i "s#vendor/sylius/sylius/features#vendor/${PLUGIN_NAME}/features#g" behat.yml)
- (cd ${TEST_DIRECTORY} && echo ' - { resource: "../vendor/${PLUGIN_NAME}/tests/Behat/Resources/services.xml" }' >> config/services_test.yaml)
- (cd ${TEST_DIRECTORY} && echo ' - { resource: "../vendor/${PLUGIN_NAME}/src/Resources/config/services.xml" }' >> config/services_test.yaml)
- (cd ${TEST_DIRECTORY} && echo ' - { resource: "../vendor/sylius/refund-plugin/src/Resources/config/services.xml" }' >> config/services_test.yaml)
- (cd ${TEST_DIRECTORY} && echo ' - { resource: "../vendor/sylius/refund-plugin/tests/Behat/Resources/services.xml" }' >> config/services_test.yaml)
- (cd ${TEST_DIRECTORY} && echo ' - { resource: "services_payplug.yaml" }' >> config/services_test.yaml)
+ (cd ${TEST_DIRECTORY} && sed -i '2i \ \ \ \ - { resource: "../vendor/${PLUGIN_NAME}/tests/Behat/Resources/services.xml\" }' config/services_test.yaml)
+ (cd ${TEST_DIRECTORY} && sed -i '3i \ \ \ \ - { resource: "../vendor/${PLUGIN_NAME}/src/Resources/config/services.xml" }' config/services_test.yaml)
+ (cd ${TEST_DIRECTORY} && sed -i '4i \ \ \ \ - { resource: "../vendor/sylius/refund-plugin/src/Resources/config/services.xml" }' config/services_test.yaml)
+ (cd ${TEST_DIRECTORY} && sed -i '5i \ \ \ \ - { resource: "../vendor/sylius/refund-plugin/tests/Behat/Resources/services.xml" }' config/services_test.yaml)
+ (cd ${TEST_DIRECTORY} && sed -i '6i \ \ \ \ - { resource: "services_payplug.yaml" }' config/services_test.yaml)
grumphp:
vendor/bin/grumphp run
help: SHELL=/bin/bash
-help: ## Dislay this help
+help: ## Display this help
@IFS=$$'\n'; for line in `grep -h -E '^[a-zA-Z_#-]+:?.*?##.*$$' $(MAKEFILE_LIST)`; do if [ "$${line:0:2}" = "##" ]; then \
echo $$line | awk 'BEGIN {FS = "## "}; {printf "\033[33m %s\033[0m\n", $$2}'; else \
echo $$line | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m%s\n", $$1, $$2}'; fi; \
diff --git a/README.md b/README.md
index ef06deb7..610a38d8 100644
--- a/README.md
+++ b/README.md
@@ -1,17 +1,22 @@
-[](https://github.com/payplug/SyliusPayPlugPlugin/blob/master/LICENSE)
-
-[](https://packagist.org/packages/payplug/payplug-sylius)
-[](https://packagist.org/packages/payplug/payplug-sylius)
+[](https://github.com/payplug/SyliusPayPlugPlugin/blob/master/LICENSE)
+[](https://github.com/payplug/SyliusPayPlugPlugin/actions/workflows/analysis.yaml)
+[](https://github.com/payplug/SyliusPayPlugPlugin/actions/workflows/sylius.yaml)
+[](https://packagist.org/packages/payplug/sylius-payplug-plugin)
+[](https://packagist.org/packages/payplug/sylius-payplug-plugin)
-
+
+
+
+
+
-PayPlug payment plugin for Sylius
+Payplug payment plugin for Sylius
-This plugin allows you to integrate PayPlug payment with Sylius platform app including payment features and refunding orders.
+This plugin allows you to integrate Payplug payment with Sylius platform app including payment features and refunding orders.
## Requirements
@@ -26,122 +31,152 @@ In local environment, the plugin will not work properly because you will not be
## Compatibility
-| | Version |
-| :--- | :--- |
-| PHP | 7.4, 8.0 |
-| Sylius | 1.9, 1.10 |
+| | Version |
+|:-------|:--------|
+| PHP | ^8.2 |
+| Sylius | ^2.0 |
-## Installation
-1. Require the **payplug/sylius-payplug-plugin** :
+### With Symfony Flex
- ```bash
- composer config extra.symfony.allow-contrib true
- composer require payplug/sylius-payplug-plugin
- ```
+#### 1. Allow contrib recipes and require the plugin
-2. Apply migrations to your database:
+```bash
+composer config extra.symfony.allow-contrib true
+composer require payplug/sylius-payplug-plugin
+```
- ```shell
- bin/console doctrine:migrations:migrate
- ```
+#### 2. Install the Flex recipe
-3. Copy templates that are overridden by Sylius into `templates/bundles/SyliusAdminBundle`
-
- ```shell
- mkdir -p templates/bundles/SyliusAdminBundle/
- cp -R vendor/payplug/sylius-payplug-plugin/src/Resources/views/SyliusAdminBundle/* templates/bundles/SyliusAdminBundle/
- ```
+```bash
+composer recipes:install payplug/sylius-payplug-plugin --force
+```
-4. Add PayPlug to refundable payment method for Sylius Refund Plugin in `config/services.yaml`
+This automatically registers the bundle, copies configuration files, and sets up assets (on Sylius 2.1+).
- ```yaml
- parameters:
- sylius_refund.supported_gateways:
- - payplug
- - payplug_oney
- ```
+#### 3. Apply migrations to your database
-5. Add PayPlug routes in `config/routes.yaml`
+```shell
+bin/console doctrine:migrations:migrate
+```
- ```yaml
- sylius_payplug:
- resource: "@PayPlugSyliusPayPlugPlugin/Resources/config/routing.yaml"
- ```
+#### 4. Add Payplug to refundable payment methods for Sylius Refund Plugin in `config/services.yaml`
+
+```yaml
+parameters:
+ locale: fr_FR
+ sylius_refund.supported_gateways:
+ - payplug
+ - payplug_oney
+ - payplug_bancontact
+ - payplug_apple_pay
+ - payplug_american_express
+```
-8. Add Traits for Customer and PaymentMethod entities
+#### 5. Add Traits for Customer and PaymentMethod entities
* App\Entity\Customer\Customer
- ```php
- Payment methods`, then click on `Create` and choose "**PayPlug**".
+In your back-office, go to `Configuration > Payment methods`, then click on `Create` and choose "**Payplug**".
## Logs
-If you want to follow the logs in the production environment, you need to add the configuration in `config/packages/prod/monolog.yaml`, logs should be in `var/log/prod.log` which can be searched after the phrase `[Payum]` or `[PayPlug]`:
+If you want to follow the logs in the production environment, you need to add the configuration in `config/packages/prod/monolog.yaml`, logs should be in `var/log/prod.log` which can be searched after the phrase `[Payum]` or `[Payplug]`:
```yaml
monolog:
@@ -164,22 +199,6 @@ Run the below command to see what Symfony services are shared with this plugin:
$ bin/console debug:container payplug_sylius_payplug_plugin
```
-### Template overriding
-
-This plugin override some sylius templates.
-If you plan override them also, you should retrieve them in your application.
-
-Copy Sylius templates overridden in plugin to your templates directory (e.g templates/bundles/)
-
- ```shell
- mkdir -p templates/bundles/SyliusAdminBundle/
- mkdir -p templates/bundles/SyliusShopBundle/
- mkdir -p templates/bundles/SyliusUiBundle/
- cp -R vendor/payplug/sylius-payplug-plugin/src/Resources/views/SyliusAdminBundle/* templates/bundles/SyliusAdminBundle/
- cp -R vendor/payplug/sylius-payplug-plugin/src/Resources/views/SyliusShopBundle/* templates/bundles/SyliusShopBundle/
- cp -R vendor/payplug/sylius-payplug-plugin/src/Resources/views/SyliusUiBundle/* templates/bundles/SyliusUiBundle/
- ```
-
## Development
See [How to contribute](CONTRIBUTING.md).
@@ -192,6 +211,10 @@ This library is under the MIT license.
For better Oney integration, you can check the [Oney enhancement documentation](doc/oney_enhancement.md).
+## Authorized Payment
+
+Since 1.11.0, the plugin supports the authorized payment feature. You can check the [Authorized Payment documentation](doc/authorized_payment.md).
+
## Doc
- [Development](doc/development.md)
- [Release Process](RELEASE.md)
diff --git a/RELEASE.md b/RELEASE.md
index 9773def5..6cc12383 100644
--- a/RELEASE.md
+++ b/RELEASE.md
@@ -2,3 +2,5 @@
Upon releasing a new version there are checks and updates to be made:
* Update plugin's version inside `src/PayPlugSyliusPayPlugPlugin.php`
+* Ensure that the `CHANGELOG.md` is up to date with the changes made
+* Ensure that the date of the release version is updated in `CHANGELOG.md`
diff --git a/src/Resources/dev/.eslintignore b/assets/.eslintignore
similarity index 100%
rename from src/Resources/dev/.eslintignore
rename to assets/.eslintignore
diff --git a/src/Resources/dev/.eslintrc b/assets/.eslintrc
similarity index 100%
rename from src/Resources/dev/.eslintrc
rename to assets/.eslintrc
diff --git a/src/Resources/dev/.gitignore b/assets/.gitignore
similarity index 59%
rename from src/Resources/dev/.gitignore
rename to assets/.gitignore
index 5dde5fca..7b6786d1 100644
--- a/src/Resources/dev/.gitignore
+++ b/assets/.gitignore
@@ -1,2 +1,3 @@
node_modules/
.*cache
+yarn-error.log
diff --git a/assets/admin/entrypoint.js b/assets/admin/entrypoint.js
new file mode 100644
index 00000000..9ccfccbe
--- /dev/null
+++ b/assets/admin/entrypoint.js
@@ -0,0 +1 @@
+// Mandatory by test application
diff --git a/assets/controllers.json b/assets/controllers.json
new file mode 100644
index 00000000..5582d953
--- /dev/null
+++ b/assets/controllers.json
@@ -0,0 +1,41 @@
+{
+ "controllers": {
+ "@payplug/sylius-payplug-plugin": {
+ "oney-popin": {
+ "enabled": true,
+ "fetch": "lazy",
+ "autoimport": {
+ "@payplug/sylius-payplug-plugin/shop/dist/oney_common/index.css": true,
+ "@payplug/sylius-payplug-plugin/shop/dist/oney_popin/index.css": true
+ }
+ },
+ "integrated-payment": {
+ "enabled": true,
+ "fetch": "lazy",
+ "autoimport": {
+ "@payplug/sylius-payplug-plugin/shop/dist/payment/integrated.css": true
+ }
+ },
+ "oney-payment": {
+ "enabled": true,
+ "fetch": "lazy"
+ },
+ "payment-logo": {
+ "enabled": true,
+ "fetch": "lazy"
+ },
+ "checkout-select-payment": {
+ "enabled": true,
+ "fetch": "lazy",
+ "autoimport": {
+ "@payplug/sylius-payplug-plugin/shop/dist/payment/index.css": true
+ }
+ },
+ "apple-pay": {
+ "enabled": true,
+ "fetch": "lazy"
+ }
+ }
+ },
+ "entrypoints": []
+}
diff --git a/assets/images/integrated/account.svg b/assets/images/integrated/account.svg
new file mode 100644
index 00000000..297efca7
--- /dev/null
+++ b/assets/images/integrated/account.svg
@@ -0,0 +1,9 @@
+
+
\ No newline at end of file
diff --git a/assets/images/integrated/calendar.svg b/assets/images/integrated/calendar.svg
new file mode 100644
index 00000000..82fb3686
--- /dev/null
+++ b/assets/images/integrated/calendar.svg
@@ -0,0 +1,13 @@
+
+
\ No newline at end of file
diff --git a/assets/images/integrated/card.svg b/assets/images/integrated/card.svg
new file mode 100644
index 00000000..5c08a102
--- /dev/null
+++ b/assets/images/integrated/card.svg
@@ -0,0 +1,10 @@
+
+
\ No newline at end of file
diff --git a/assets/images/integrated/cb-dark.svg b/assets/images/integrated/cb-dark.svg
new file mode 100644
index 00000000..5281a653
--- /dev/null
+++ b/assets/images/integrated/cb-dark.svg
@@ -0,0 +1,33 @@
+
+
\ No newline at end of file
diff --git a/assets/images/integrated/cb.svg b/assets/images/integrated/cb.svg
new file mode 100644
index 00000000..51c1cad5
--- /dev/null
+++ b/assets/images/integrated/cb.svg
@@ -0,0 +1,33 @@
+
+
\ No newline at end of file
diff --git a/assets/images/integrated/lock.svg b/assets/images/integrated/lock.svg
new file mode 100644
index 00000000..316e1f5e
--- /dev/null
+++ b/assets/images/integrated/lock.svg
@@ -0,0 +1,9 @@
+
+
\ No newline at end of file
diff --git a/assets/images/integrated/logo-payplug.png b/assets/images/integrated/logo-payplug.png
new file mode 100644
index 00000000..b67d7cce
Binary files /dev/null and b/assets/images/integrated/logo-payplug.png differ
diff --git a/assets/images/integrated/mastercard-dark.svg b/assets/images/integrated/mastercard-dark.svg
new file mode 100644
index 00000000..652fb0ae
--- /dev/null
+++ b/assets/images/integrated/mastercard-dark.svg
@@ -0,0 +1,19 @@
+
+
\ No newline at end of file
diff --git a/assets/images/integrated/mastercard.svg b/assets/images/integrated/mastercard.svg
new file mode 100644
index 00000000..337ff9fb
--- /dev/null
+++ b/assets/images/integrated/mastercard.svg
@@ -0,0 +1,18 @@
+
+
\ No newline at end of file
diff --git a/assets/images/integrated/visa-dark.svg b/assets/images/integrated/visa-dark.svg
new file mode 100644
index 00000000..9de9cde7
--- /dev/null
+++ b/assets/images/integrated/visa-dark.svg
@@ -0,0 +1,19 @@
+
+
\ No newline at end of file
diff --git a/assets/images/integrated/visa.svg b/assets/images/integrated/visa.svg
new file mode 100644
index 00000000..5c488458
--- /dev/null
+++ b/assets/images/integrated/visa.svg
@@ -0,0 +1,16 @@
+
+
\ No newline at end of file
diff --git a/assets/package.json b/assets/package.json
new file mode 100644
index 00000000..52494447
--- /dev/null
+++ b/assets/package.json
@@ -0,0 +1,76 @@
+{
+ "name": "@payplug/sylius-payplug-plugin",
+ "license": "MIT",
+ "version": "2.0.0",
+ "keywords": [
+ "symfony-ux"
+ ],
+ "description": "Sylius Payplug Plugin",
+ "type": "module",
+ "files": [
+ "dist"
+ ],
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "symfony": {
+ "controllers": {
+ "oney-popin": {
+ "main": "shop/controllers/oney-popin_controller.js",
+ "enabled": true,
+ "webpackMode": "lazy",
+ "fetch": "lazy",
+ "autoimport": {
+ "@payplug/sylius-payplug-plugin/shop/dist/oney_common/index.css": true,
+ "@payplug/sylius-payplug-plugin/shop/dist/oney_popin/index.css": true
+ }
+ },
+ "integrated-payment": {
+ "main": "shop/controllers/integrated-payment_controller.js",
+ "webpackMode": "lazy",
+ "fetch": "lazy",
+ "enabled": true,
+ "autoimport": {
+ "@payplug/sylius-payplug-plugin/shop/dist/payment/integrated.css": true
+ }
+ },
+ "oney-payment": {
+ "main": "shop/controllers/oney-payment_controller.js",
+ "webpackMode": "lazy",
+ "fetch": "lazy",
+ "enabled": true
+ },
+ "payment-logo": {
+ "main": "shop/controllers/payment-logo_controller.js",
+ "webpackMode": "lazy",
+ "fetch": "lazy",
+ "enabled": true
+ },
+ "checkout-select-payment": {
+ "main": "shop/controllers/checkout-select-payment_controller.js",
+ "autoimport": {
+ "@payplug/sylius-payplug-plugin/shop/dist/payment/index.css": true
+ },
+ "webpackMode": "lazy",
+ "fetch": "lazy",
+ "enabled": true
+ },
+ "apple-pay": {
+ "main": "shop/controllers/apple-pay_controller.js",
+ "webpackMode": "lazy",
+ "fetch": "lazy",
+ "enabled": true
+ }
+ }
+ },
+ "peerDependencies": {
+ "@hotwired/stimulus": "^3.0.0"
+ },
+ "devDependencies": {
+ "@hotwired/stimulus": "^3.0.0"
+ },
+ "dependencies": {
+ "jquery": "^3.5.1",
+ "webfontloader": "^1.6.28"
+ }
+}
diff --git a/assets/shop/controllers/apple-pay_controller.js b/assets/shop/controllers/apple-pay_controller.js
new file mode 100644
index 00000000..fe977eb6
--- /dev/null
+++ b/assets/shop/controllers/apple-pay_controller.js
@@ -0,0 +1,181 @@
+import { Controller } from '@hotwired/stimulus';
+
+/* stimulusFetch: 'lazy' */
+export default class extends Controller {
+ static values = {
+ paymentInputId: String,
+ settings: Object,
+ notice: String,
+ }
+
+ connect() {
+ this.applePayButton = this.element.querySelector('apple-pay-button');
+ this.onApplePayButtonClick = this.onApplePayButtonClick.bind(this);
+
+ if (this.applePayButton) {
+ this.applePayButton.addEventListener('click', this.onApplePayButtonClick);
+ }
+
+ this.retryCount = 0;
+ this.initApplePay();
+ }
+
+ initApplePay() {
+ if (typeof window.ApplePaySession !== 'undefined') {
+ this.checkSupport();
+ return;
+ }
+
+ if (this.retryCount < 20) { // Try for 2 seconds
+ this.retryCount++;
+ setTimeout(() => this.initApplePay(), 100);
+ } else {
+ this.disablePaymentMethod();
+ }
+ }
+
+ checkSupport() {
+ try {
+ const isSupported = window.ApplePaySession.canMakePayments();
+
+ if (isSupported && this.applePayButton) {
+ this.applePayButton.classList.add('enabled');
+ } else {
+ this.disablePaymentMethod();
+ }
+ } catch (e) {
+ this.disablePaymentMethod();
+ }
+ }
+
+ disablePaymentMethod() {
+ if (!this.paymentInputIdValue) return;
+
+ const input = document.getElementById(this.paymentInputIdValue);
+ if (!input) return;
+
+ const container = input.closest('.card, .item, .form-check, .field');
+ if (container) {
+ container.style.opacity = '0.5';
+ container.style.pointerEvents = 'none';
+ container.classList.add('apple-pay-ineligible');
+
+ // Add a small info message if not already present
+ if (!container.querySelector('.apple-pay-notice')) {
+ const notice = document.createElement('div');
+ notice.className = 'apple-pay-notice small text-muted mt-2';
+ notice.style.padding = '0 1rem 1rem';
+ notice.innerText = this.noticeValue || 'Apple Pay is not available on this browser or device.';
+ container.appendChild(notice);
+ }
+
+ if (input.checked) {
+ input.checked = false;
+ input.dispatchEvent(new Event('change', { bubbles: true }));
+ }
+ }
+
+ input.disabled = true;
+
+ if (this.element) {
+ this.element.style.display = 'none';
+ }
+ }
+
+ async onApplePayButtonClick(event) {
+ const applePayButton = event.currentTarget;
+
+ if (!this.settingsValue) {
+ console.error('Invalid Apple Pay settings!');
+ return;
+ }
+
+ if (typeof window.ApplePaySession === 'undefined') {
+ return;
+ }
+
+ // Prepare settings
+ const settings = { ...this.settingsValue };
+ if (settings.applePayDomain) {
+ settings.applicationData = btoa(JSON.stringify({
+ 'apple_pay_domain': settings.applePayDomain
+ }));
+ delete settings.applePayDomain;
+ }
+
+ try {
+ const version = window.ApplePaySession.supportsVersion(14) ? 14 : 3;
+ const session = new window.ApplePaySession(version, settings);
+
+ session.onvalidatemerchant = async (event) => {
+ try {
+ const response = await fetch(applePayButton.dataset.validateMerchantRoute, {
+ method: 'POST',
+ headers: {
+ 'X-Requested-With': 'XMLHttpRequest',
+ },
+ });
+ const authorization = await response.json();
+
+ if (authorization.success === true) {
+ session.completeMerchantValidation(authorization.merchant_session);
+ } else {
+ session.abort();
+ }
+ } catch (error) {
+ console.error(error);
+ session.abort();
+ window.location.reload();
+ }
+ };
+
+ session.onpaymentauthorized = async (event) => {
+ try {
+ const response = await fetch(applePayButton.dataset.paymentAuthorizedRoute, {
+ method: 'POST',
+ headers: {
+ 'X-Requested-With': 'XMLHttpRequest',
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify({
+ token: event.payment.token,
+ }),
+ });
+ const authorization = await response.json();
+
+ let applePaySessionStatus = window.ApplePaySession.STATUS_SUCCESS;
+
+ if (authorization.data.responseToApple.status !== 1) {
+ applePaySessionStatus = window.ApplePaySession.STATUS_FAILURE;
+ }
+
+ session.completePayment({ "status": applePaySessionStatus });
+ window.location.href = authorization.data.returnUrl;
+ } catch (error) {
+ console.error(error);
+ window.location.reload();
+ }
+ };
+
+ session.oncancel = async (event) => {
+ try {
+ const response = await fetch(applePayButton.dataset.sessionCancelRoute, {
+ method: 'POST',
+ headers: {
+ 'X-Requested-With': 'XMLHttpRequest',
+ },
+ });
+ const authorization = await response.json();
+ window.location.href = authorization.data.returnUrl;
+ } catch (error) {
+ console.error(error);
+ window.location.reload();
+ }
+ };
+
+ session.begin();
+ } catch (e) {
+ console.error('Failed to create Apple Pay session:', e);
+ }
+ }
+}
diff --git a/assets/shop/controllers/checkout-select-payment_controller.js b/assets/shop/controllers/checkout-select-payment_controller.js
new file mode 100644
index 00000000..ead033d4
--- /dev/null
+++ b/assets/shop/controllers/checkout-select-payment_controller.js
@@ -0,0 +1,129 @@
+import { Controller } from '@hotwired/stimulus';
+import $ from 'jquery';
+
+/* stimulusFetch: 'lazy' */
+export default class extends Controller {
+ static targets = ['trigger'];
+ form = null;
+
+ connect() {
+ this.form = this.element.closest('form') || document.querySelector('form[name*="checkout_select_payment"]');
+ this.findNextStepButton();
+
+ this.handleStateChange = this.handleStateChange.bind(this);
+ this.element.addEventListener('payment-method-state-change', this.handleStateChange);
+
+ this.toggleGateway();
+
+ if (this.form) {
+ this.form.addEventListener('submit', (event) => {
+ this.handleForm();
+ });
+ }
+ }
+
+ disconnect() {
+ this.element.removeEventListener('payment-method-state-change', this.handleStateChange);
+ }
+
+ handleStateChange(event) {
+ this.updateNextStepButtonVisibility(event.target);
+ }
+
+ findNextStepButton() {
+ if (!this.form) return;
+ this.nextStepButton = this.form.querySelector('button[type="submit"], button:not([type])');
+ }
+
+ toggleGateway() {
+ const $inputs = $(this.element).find('input[id*="checkout_select_payment_payments"]');
+ const checkedInput = $inputs.filter(':checked')[0];
+
+ // Hide all initially
+ this.triggerTargets.forEach(target => $(target).hide());
+
+ if (checkedInput) {
+ const currentTarget = this.triggerTargets.find(target => target.dataset.paymentInputId === checkedInput.id);
+ if (currentTarget) {
+ $(currentTarget).show();
+ this.updateNextStepButtonVisibility(currentTarget);
+ } else {
+ this.toggleNextStepButton(true);
+ }
+ }
+
+ $inputs.on('change', (event) => {
+ const clickedPaymentMethodId = event.currentTarget.id;
+
+ // Hide all choices
+ this.triggerTargets.forEach(target => $(target).slideUp());
+
+ const currentTarget = this.triggerTargets.find(target => target.dataset.paymentInputId === clickedPaymentMethodId);
+ if (currentTarget) {
+ $(currentTarget).slideDown(() => {
+ this.updateNextStepButtonVisibility(currentTarget);
+ });
+ } else {
+ this.toggleNextStepButton(true);
+ }
+ });
+ }
+
+ updateNextStepButtonVisibility(container) {
+ let targetContainer = container;
+
+ if (!targetContainer || !targetContainer.dataset.paymentInputId) {
+ // Fallback to currently checked input
+ const checkedInput = this.element.querySelector('input[id*="checkout_select_payment_payments"]:checked');
+ if (checkedInput) {
+ targetContainer = this.triggerTargets.find(target => target.dataset.paymentInputId === checkedInput.id);
+ }
+ }
+
+ if (!targetContainer) {
+ this.toggleNextStepButton(true);
+ return;
+ }
+
+ const handlesSubmit = targetContainer.dataset.paymentInlineSubmit === 'true' ||
+ targetContainer.querySelector('[data-payment-inline-submit="true"]') !== null;
+
+ this.toggleNextStepButton(!handlesSubmit);
+ }
+
+ toggleNextStepButton(show) {
+ if (!this.nextStepButton) {
+ this.findNextStepButton();
+ }
+ if (!this.nextStepButton) return;
+
+ if (show) {
+ this.nextStepButton.classList.remove('disabled');
+ this.nextStepButton.disabled = false;
+ } else {
+ this.nextStepButton.classList.add('disabled');
+ this.nextStepButton.disabled = true;
+ }
+ }
+
+ enableNextStepButton() {
+ this.toggleNextStepButton(true);
+ }
+
+ disableNextStepButton() {
+ this.toggleNextStepButton(false);
+ }
+
+ handleForm() {
+ if ($('.checkbox-oney :radio:checked').length) {
+ $('.checkbox-payplug').closest('.oney-payment-choice__item, .payplug-payment-choice__item, .form-check, .payment-item').find('.payment-choice__input:checked').prop('checked', false);
+ } else if ($('.checkbox-payplug :radio:checked').length) {
+ $('.checkbox-oney').closest('.oney-payment-choice__item, .payplug-payment-choice__item, .form-check, .payment-item').find('.payment-choice__input:checked').prop('checked', false);
+ }
+
+ const otherCardOther = document.querySelector('input#payplug_choice_card_other');
+ if (otherCardOther) {
+ otherCardOther.disabled = true;
+ }
+ }
+}
diff --git a/assets/shop/controllers/integrated-payment_controller.js b/assets/shop/controllers/integrated-payment_controller.js
new file mode 100644
index 00000000..5c85c16e
--- /dev/null
+++ b/assets/shop/controllers/integrated-payment_controller.js
@@ -0,0 +1,259 @@
+import { Controller } from '@hotwired/stimulus';
+import WebFont from 'webfontloader';
+
+/* stimulusFetch: 'lazy' */
+export default class extends Controller {
+ static targets = ['container']
+ static values = {
+ code: String,
+ factoryName: String,
+ }
+ initialize() {
+ WebFont.load({
+ google: {
+ families: ["Poppins:400,600"],
+ },
+ });
+ }
+ connect() {
+ this.options = {
+ api: null,
+ cartId: null,
+ form: {},
+ fieldsValid: {
+ cardHolder: false,
+ pan: false,
+ cvv: false,
+ exp: false,
+ },
+ fieldsEmpty: {
+ cardHolder: true,
+ pan: true,
+ cvv: true,
+ exp: true,
+ },
+ inputStyle: {
+ default: {
+ color: '#2B343D',
+ fontFamily: 'Poppins, Arial, sans-serif',
+ fontSize: '14px',
+ textAlign: 'left',
+ '::placeholder': {
+ color: '#969a9f',
+ },
+ ':focus': {
+ color: '#2B343D',
+ }
+ },
+ invalid: {
+ color: '#E91932'
+ }
+ },
+ save_card: false,
+ schemes: null,
+ scheme: null,
+ };
+
+ // Verify if we have required data
+ // TODO: Pass as param
+ if (typeof payplug_integrated_payment_params === 'undefined') {
+ return;
+ }
+
+ this.form = this.element.closest('form');
+
+ if (payplug_integrated_payment_params.has_saved_cards) {
+ const otherCardRadio = this.element.querySelector('#payplug_choice_card_other');
+ const payplugRadio = document.querySelector(`[id*="checkout_select_payment_payments"][value="${payplug_integrated_payment_params.payment_method_code}"]`);
+
+ // Initial check
+ if (
+ (otherCardRadio && otherCardRadio.checked && payplugRadio && payplugRadio.checked) ||
+ (otherCardRadio && otherCardRadio.checked && !payplugRadio && document.querySelector('.payplug-payment-choice__input:checked'))
+ ) {
+ this.openFields();
+ }
+
+ this.element.querySelectorAll('.payment-choice__input, [id*="checkout_select_payment_payments"]').forEach((element) => {
+ element.addEventListener('change', (e) => {
+ const isOtherCardChecked = this.element.querySelector('#payplug_choice_card_other')?.checked;
+ const isPayplugSelected = document.querySelector(`[id*="checkout_select_payment_payments"][value="${payplug_integrated_payment_params.payment_method_code}"]`)?.checked;
+
+ if (isOtherCardChecked && isPayplugSelected) {
+ this.openFields();
+ }
+ })
+ })
+ return;
+ }
+
+ const isChecked = this.getPaymentMethodSelectors({ methodCode: payplug_integrated_payment_params.payment_method_code, checked: true });
+ if (isChecked.length) {
+ this.openFields();
+ }
+
+ const selectPaymentMethodsField = this.getPaymentMethodSelectors();
+ selectPaymentMethodsField.forEach((element) => {
+ // On payment method select, open fields if payplug selected
+ element.addEventListener('change', (e) => {
+ if (payplug_integrated_payment_params.payment_method_code === e.currentTarget.value && e.currentTarget.checked) {
+ this.openFields();
+ }
+ })
+ });
+ }
+
+ handleShow(event) {
+ if (this.hasContainerTarget) {
+ import('jquery').then(({ default: $ }) => {
+ $(this.containerTarget).slideDown();
+ });
+ this.openFields();
+ this.containerTarget.dataset.paymentInlineSubmit = "true";
+ this.element.dispatchEvent(new CustomEvent('payment-method-state-change', { bubbles: true }));
+ }
+ }
+
+ handleHide(event) {
+ if (this.hasContainerTarget) {
+ import('jquery').then(({ default: $ }) => {
+ $(this.containerTarget).slideUp();
+ });
+ this.closeFields();
+ this.containerTarget.dataset.paymentInlineSubmit = "false";
+ this.element.dispatchEvent(new CustomEvent('payment-method-state-change', { bubbles: true }));
+ }
+ }
+
+ getPaymentMethodSelectors({ methodCode, checked } = {}) {
+ const baseSelector = '[id*=checkout_select_payment_payments]';
+
+ if (methodCode) {
+ if (checked) {
+ return document.querySelectorAll(`${baseSelector}[value=${methodCode}]:checked`);
+ }
+ return document.querySelectorAll(`${baseSelector}[value=${methodCode}]`);
+ }
+ return document.querySelectorAll(baseSelector);
+ }
+
+ openFields() {
+ this.containerTarget.classList.add('payplugIntegratedPayment--loaded');
+ if (null === this.options.api) {
+ this.load();
+ }
+ }
+
+ closeFields() {
+ this.containerTarget.classList.remove('payplugIntegratedPayment--loaded');
+ }
+
+ load() {
+ this.options.api = new window.Payplug.IntegratedPayment(payplug_integrated_payment_params.is_test_mode);
+ this.options.api.setDisplayMode3ds(window.Payplug.DisplayMode3ds.LIGHTBOX);
+
+ const container = this.hasContainerTarget ? this.containerTarget : this.element;
+
+ this.options.form.cardHolder = this.options.api.cardHolder(
+ container.querySelector('.cardHolder-input-container'),
+ {
+ default: this.options.inputStyle.default,
+ placeholder: payplug_integrated_payment_params.cardholder
+ }
+ );
+ this.options.form.pan = this.options.api.cardNumber(
+ container.querySelector('.pan-input-container'),
+ {
+ default: this.options.inputStyle.default,
+ placeholder: payplug_integrated_payment_params.pan
+ }
+ );
+ this.options.form.cvv = this.options.api.cvv(
+ container.querySelector('.cvv-input-container'),
+ {
+ default: this.options.inputStyle.default,
+ placeholder: payplug_integrated_payment_params.cvv
+ }
+ );
+ this.options.form.exp = this.options.api.expiration(
+ container.querySelector('.exp-input-container'),
+ {
+ default: this.options.inputStyle.default,
+ placeholder: payplug_integrated_payment_params.exp
+ }
+ );
+ this.options.schemes = this.options.api.getSupportedSchemes();
+ this.bindEvents();
+ this.fieldValidation();
+ }
+ bindEvents() {
+ const container = this.hasContainerTarget ? this.containerTarget : this.element;
+ const paidButton = container.querySelector('#paid');
+ if (paidButton) {
+ paidButton.addEventListener('click', (event) => {
+ event.preventDefault();
+ this.options.api.validateForm();
+ });
+ }
+
+ this.options.api.onValidateForm(async ({ isFormValid }) => {
+ if (isFormValid) {
+ this.toggleLoader();
+ const saveCardElement = this.element.querySelector('#savecard');
+ if (null !== saveCardElement) {
+ this.options.save_card = saveCardElement.checked;
+ }
+ const chosenScheme = this.element.querySelector('input.schemeOptions:checked');
+ this.options.scheme = window.Payplug.Scheme.AUTO;
+ if (null !== chosenScheme) {
+ this.options.scheme = chosenScheme.value;
+ }
+ if (payplug_integrated_payment_params.payment_id !== undefined) {
+ this.options.api.pay(payplug_integrated_payment_params.payment_id, this.options.scheme, {save_card: this.options.save_card});
+ return;
+ }
+ const response = await fetch(payplug_integrated_payment_params.routes.init_payment, {method: 'POST'});
+ const data = await response.json();
+ this.options.api.pay(data.payment_id, this.options.scheme, {save_card: this.options.save_card});
+ }
+ });
+ this.options.api.onCompleted((event) => {
+ if (event.error) {
+ console.error(event.error);
+ return;
+ }
+ document.querySelector('input[name=payplug_integrated_payment_token]').value = event.token;
+ this.form.submit();
+ });
+ }
+ fieldValidation () {
+ const container = this.hasContainerTarget ? this.containerTarget : this.element;
+ Object.keys(this.options.form).forEach((key) => {
+ const field = this.options.form[key];
+ field.onChange((err) => {
+ if (err.error) {
+ container.querySelector(`.payplugIntegratedPayment__error--${key}`).classList.remove('payplugIntegratedPayment__error--hide');
+ container.querySelector(`.${key}-input-container`).classList.add('payplugIntegratedPayment__container--invalid');
+ if (err.error.name === "FIELD_EMPTY") {
+ container.querySelector(`.payplugIntegratedPayment__error--${key}`).querySelector(".emptyField").classList.remove('payplugIntegratedPayment__error--hide');
+ container.querySelector(`.payplugIntegratedPayment__error--${key}`).querySelector(".invalidField").classList.add('payplugIntegratedPayment__error--hide');
+ } else {
+ container.querySelector(`.payplugIntegratedPayment__error--${key}`).querySelector(".invalidField").classList.remove('payplugIntegratedPayment__error--hide');
+ container.querySelector(`.payplugIntegratedPayment__error--${key}`).querySelector(".emptyField").classList.add('payplugIntegratedPayment__error--hide');
+ }
+ } else {
+ container.querySelector(`.payplugIntegratedPayment__error--${key}`).classList.add('payplugIntegratedPayment__error--hide');
+ container.querySelector(`.${key}-input-container`).classList.remove('payplugIntegratedPayment__container--invalid');
+ container.querySelector(`.payplugIntegratedPayment__error--${key}`).querySelector(".invalidField").classList.add('payplugIntegratedPayment__error--hide');
+ container.querySelector(`.payplugIntegratedPayment__error--${key}`).querySelector(".emptyField").classList.add('payplugIntegratedPayment__error--hide');
+ this.options.fieldsValid[key] = true;
+ this.options.fieldsEmpty[key] = false;
+ }
+ });
+ })
+ }
+ toggleLoader() {
+ const container = this.hasContainerTarget ? this.containerTarget : this.element;
+ container.querySelector('.sylius-shop-loader').classList.toggle('d-none');
+ }
+}
diff --git a/assets/shop/controllers/oney-payment_controller.js b/assets/shop/controllers/oney-payment_controller.js
new file mode 100644
index 00000000..bdf7d63e
--- /dev/null
+++ b/assets/shop/controllers/oney-payment_controller.js
@@ -0,0 +1,95 @@
+import { Controller } from '@hotwired/stimulus';
+import $ from 'jquery';
+
+/* stimulusFetch: 'lazy' */
+export default class extends Controller {
+ static values = {
+ modal: String,
+ area: String,
+ route: String,
+ }
+ connect() {
+ this.tabs();
+ window.addEventListener('resize', () => {
+ setTimeout(this.tabs, 100);
+ });
+ this.tabsHandler();
+ }
+ tabs() {
+ if (window.innerWidth <= 991) {
+ $('.oney-payment-choice__item').hide();
+ setTimeout(() => {
+ $.each($('.oney-payment-choice__input'), (k, el) => {
+ if ($(el).is(':checked')) {
+ $(el).parent().show();
+ $(`a.tablink[data-id=${$(el).val()}]`).addClass('active');
+ }
+ });
+ }, 1);
+ } else {
+ $('.oney-payment-choice__item').show();
+ $('a.tablink').removeClass('active');
+ }
+ }
+ tabsHandler() {
+ const $tabLinks = $('a.tablink');
+ $.each($tabLinks, (k, el) => {
+ $(el).click(function (evt) {
+ $('a.tablink').removeClass('active');
+ $(this).addClass('active');
+ $('.oney-payment-choice__item').hide();
+ $(`#${$(this).data('id')}`).show();
+ $(`input[value=${$(this).data('id')}`).prop('checked', true);
+ });
+ });
+ }
+ routeValueChanged() {
+ if (!this.hasRouteValue) {
+ return;
+ }
+ this.modalAppear();
+ }
+ modalAppear() {
+ let path = this.routeValue;
+ $.get(path).then((data) => {
+ const modalTpl = document.querySelector('.modal');
+ this.modal = new window.bootstrap.Modal(modalTpl);
+ $(modalTpl).find('.modal-body').html(data);
+ this.modal.show();
+ this.bindModalEvents();
+ });
+ }
+ modalSubmit(e) {
+ if (!this.hasRouteValue) {
+ return;
+ }
+ e.preventDefault();
+ $('.sylius-shop-loader').toggleClass("d-none");
+ $.ajax({
+ method: 'post',
+ url: this.routeValue,
+ data: $(e.currentTarget).serialize(),
+ success: (res) => {
+ if (Array.isArray(res)) {
+ $(`${this.modalValue}__content`).fadeOut(() => {
+ $(`${this.modalValue}__success`).show();
+ });
+ setTimeout(() => {
+ this.modal.hide();
+ window.location.reload();
+ }, 2500);
+ }
+ },
+ error: (res) => {
+ $(this.modalValue).html(res.responseText);
+ this.bindModalEvents();
+ },
+ complete: () => {
+ $('.sylius-shop-loader').toggleClass("d-none");
+ }
+ });
+ }
+ bindModalEvents() {
+ $('form[name=form]').on('submit', (e) => this.modalSubmit(e));
+ }
+}
diff --git a/assets/shop/controllers/oney-popin_controller.js b/assets/shop/controllers/oney-popin_controller.js
new file mode 100644
index 00000000..e8cc4554
--- /dev/null
+++ b/assets/shop/controllers/oney-popin_controller.js
@@ -0,0 +1,167 @@
+import { Controller } from '@hotwired/stimulus';
+import $ from 'jquery';
+import WebFont from 'webfontloader';
+
+/* stimulusFetch: 'lazy' */
+export default class extends Controller {
+ static targets = ['popin', 'variantCodes'];
+ static values = {
+ eligibleUrl: String,
+ popinUrl: String,
+ productMeta: Object,
+ imagesMap: Object,
+ translations: Object,
+ }
+ inputs = {
+ option: "cartItem_variant",
+ quantity: "cartItem_quantity",
+ }
+ storage = []
+ initialize() {
+ WebFont.load({
+ google: {
+ families: ["Poppins:400,600"],
+ },
+ });
+ }
+ connect() {
+ if (this.hasProductMetaValue) {
+ this.watch();
+ }
+ this.fade();
+ this.closeHandler();
+ }
+ watch() {
+ for (const prop in this.inputs) {
+ const $selectors = $(`[id*=${this.inputs[prop]}`);
+ if ($selectors.length > 0) {
+ this.handleProductOptionsChange($selectors, prop);
+ }
+ this.productMetaValue = {
+ ...this.productMetaValue,
+ [prop]: $selectors.val(),
+ };
+ $selectors.on(
+ "input",
+ this.debounce((e) => {
+ e.preventDefault();
+ if ($selectors.length > 0) {
+ this.handleProductOptionsChange($selectors, prop);
+ }
+ this.productMetaValue = {
+ ...this.productMetaValue,
+ [prop]: $(e.currentTarget).val(),
+ };
+ this.check();
+ }, 500)
+ );
+ }
+ }
+ handleProductOptionsChange($selectors, prop) {
+ if (prop === "option") {
+ let selector = "";
+ $selectors.each((k, v) => {
+ const select = $(v);
+ const option = select.find("option:selected").val();
+ selector += `[data-${select.attr("data-option")}="${option}"]`;
+ });
+ return (this.productMetaValue = {
+ ...this.productMetaValue,
+ product_variant_code: $(this.variantCodesTarget).find(selector).attr("data-value")
+ });
+ }
+ }
+ check() {
+ this.storage = [];
+ this.toggleLoader();
+ $.ajax({
+ url: this.eligibleUrlValue,
+ data: this.productMetaValue,
+ success: (res) => {
+ $(this.element).find(".oney-logo").attr("src", this.imagesMapValue[res.isEligible ? 'enabled' : 'disabled']);
+ res.isEligible
+ ? $(this.popinTarget).removeClass("disabled").addClass("enabled")
+ : $(this.popinTarget).removeClass("enabled").addClass("disabled");
+ },
+ complete: () => {
+ this.toggleLoader();
+ }
+ });
+ }
+ /**
+ * https://davidwalsh.name/javascript-debounce-function
+ */
+ debounce(func, wait) {
+ let timeout;
+ return function executedFunction(...args) {
+ const later = () => {
+ clearTimeout(timeout);
+ func(...args);
+ };
+ clearTimeout(timeout);
+ timeout = setTimeout(later, wait);
+ };
+ }
+ fade() {
+ $(this.element).on("click", (e) => {
+ e.preventDefault();
+ e.stopPropagation();
+ if (this.hasEligibleUrlValue) {
+ if (!$(this.popinTarget).is(":empty") && $(this.popinTarget).text().trim() === this.storage) {
+ $(this.popinTarget).fadeIn();
+ return;
+ }
+ }
+ // content isn't loaded yet or cart route
+ this.toggleLoader();
+ this.load();
+ });
+ }
+ load() {
+ $.ajax({
+ url: this.popinUrlValue,
+ data: this.productMetaValue,
+ success: (res) => {
+ if (res.includes(this.translationsValue.reason)) {
+ $(this.popinTarget).removeClass("enabled").addClass("disabled");
+ }
+ this.storage = $(res).text().trim();
+ $(this.popinTarget).html(res);
+ },
+ error: ()=> {
+ $(this.popinTarget).removeClass("enabled").addClass("disabled").html(`
+
+
+
${this.translationsValue.reason}
+
+ `);
+ },
+ complete: ()=> {
+ this.toggleLoader();
+ $(this.popinTarget).fadeIn();
+ this.closeHandler();
+ },
+ });
+ }
+ toggleLoader() {
+ $(this.element).find('.sylius-shop-loader').toggleClass("d-none");
+ }
+ closeHandler() {
+ $("html")
+ .not(this.popinTarget)
+ .on("click", (e) => {
+ e.stopPropagation();
+ $(this.popinTarget).fadeOut();
+ });
+ $(this.popinTarget)
+ .find("a.close")
+ .on("click", (e) => {
+ e.stopPropagation();
+ $(this.popinTarget).fadeOut();
+ });
+ }
+}
diff --git a/assets/shop/controllers/payment-logo_controller.js b/assets/shop/controllers/payment-logo_controller.js
new file mode 100644
index 00000000..486cb375
--- /dev/null
+++ b/assets/shop/controllers/payment-logo_controller.js
@@ -0,0 +1,20 @@
+import { Controller } from '@hotwired/stimulus';
+import $ from 'jquery';
+
+/* stimulusFetch: 'lazy' */
+export default class extends Controller {
+ static values = {
+ inputId: String,
+ logoUrl: String,
+ className: String,
+ };
+ connect() {
+ if (!this.inputIdValue || !this.logoUrlValue) {
+ return;
+ }
+
+ const label = document.querySelector(`[for="${this.inputIdValue}"]`);
+ label.style.setProperty('--logo', `url(${this.logoUrlValue})`);
+ label.classList.add('payment-label-with-image', this.classNameValue);
+ }
+}
diff --git a/assets/shop/dist/oney_common/index.css b/assets/shop/dist/oney_common/index.css
new file mode 100644
index 00000000..84b84648
--- /dev/null
+++ b/assets/shop/dist/oney_common/index.css
@@ -0,0 +1 @@
+[class*=oney] *{line-height:1.25;font-family:Poppins,Arial,sans-serif!important}[class*=oney] * p{color:#66727f;margin:0}[class*=oney] * small{font-size:90%}.oney-info{margin:1em auto .5em;display:inline-block;position:relative}.oney-info span{vertical-align:text-bottom;text-transform:uppercase;font-size:16px}.oney-info>img{cursor:pointer;vertical-align:middle;margin:0 4px}.oney-info.loading{pointer-events:none}.oney-logo[src*=without-fees]{max-width:190px}@media screen and (max-width:768px){.oney-logo{max-width:230px}}
\ No newline at end of file
diff --git a/assets/shop/dist/oney_popin/index.css b/assets/shop/dist/oney_popin/index.css
new file mode 100644
index 00000000..ecaf7bff
--- /dev/null
+++ b/assets/shop/dist/oney_popin/index.css
@@ -0,0 +1 @@
+.oney-popin{z-index:99;max-width:20em;background-color:#fff;padding:10px;display:none;position:absolute;top:0;left:calc(-20em - 32px);transform:translateY(calc(10px - 33.3333%))}.oney-popin:after{content:"";height:0;width:0;border-top:20px solid #0000;border-bottom:20px solid #0000;display:inline-block;position:absolute;top:calc(33.3333% - 10px);right:-20px}.oney-popin.enabled{border:1px solid #81bc00}.oney-popin.enabled hr{border-color:#81bc00}.oney-popin.enabled:after{border-left:20px solid #81bc00}.oney-popin.enabled a.close>span{background:#81bc00}.oney-popin.enabled .oney-popin__content>p{color:#81bc00}.oney-popin.disabled{border:1px solid #ccc}.oney-popin.disabled hr{border-color:#ccc}.oney-popin.disabled:after{border-left:20px solid #ccc}.oney-popin.disabled a.close>span{background:#ccc}.oney-popin.disabled .oney-popin__content>p{color:#66727f}.oney-popin__header{text-align:right}.oney-popin__header a.close{width:40px;height:40px;position:absolute;top:0;right:0}.oney-popin__header a.close>span{width:15px;height:2px;border-radius:0;margin:0;position:absolute;top:1em;right:.5em}.oney-popin__header a.close>span:first-of-type{transform:rotate(55deg)}.oney-popin__header a.close>span:last-of-type{transform:rotate(-55deg)}.oney-popin__content>p:not(.reasons){text-transform:uppercase;margin-bottom:0;font-size:16px}.oney-popin__content>p:not(.reasons):last-of-type{margin-bottom:1em}.oney-popin__content>p.reasons{max-width:95%}.oney-popin__footer{margin-top:1em}.oney-popin__footer>p{text-align:justify}.oney-popin section{align-items:flex-start;display:flex}.oney-popin img{height:auto;margin-right:.5em}.oney-popin hr{border-style:solid none none;border-width:1px 0 0;border-bottom-color:currentColor;border-left-color:currentColor;border-right-color:currentColor;margin:1.25em 0}@media screen and (max-width:768px){.oney-popin{top:60px;left:0;transform:none}.oney-popin__header a.close{padding:10px 50px}.oney-popin:after{top:-30px;right:calc(50% - 10px);transform:rotate(-90deg)}}
\ No newline at end of file
diff --git a/assets/shop/dist/payment/account.039342ad.svg b/assets/shop/dist/payment/account.039342ad.svg
new file mode 100644
index 00000000..429c051d
--- /dev/null
+++ b/assets/shop/dist/payment/account.039342ad.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/assets/shop/dist/payment/calendar.3c23bb16.svg b/assets/shop/dist/payment/calendar.3c23bb16.svg
new file mode 100644
index 00000000..170acc49
--- /dev/null
+++ b/assets/shop/dist/payment/calendar.3c23bb16.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/assets/shop/dist/payment/card.0d2bd9bc.svg b/assets/shop/dist/payment/card.0d2bd9bc.svg
new file mode 100644
index 00000000..5806d291
--- /dev/null
+++ b/assets/shop/dist/payment/card.0d2bd9bc.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/assets/shop/dist/payment/cb-dark.888aec45.svg b/assets/shop/dist/payment/cb-dark.888aec45.svg
new file mode 100644
index 00000000..eb75a35d
--- /dev/null
+++ b/assets/shop/dist/payment/cb-dark.888aec45.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/assets/shop/dist/payment/cb.ccd964e9.svg b/assets/shop/dist/payment/cb.ccd964e9.svg
new file mode 100644
index 00000000..2fa204f2
--- /dev/null
+++ b/assets/shop/dist/payment/cb.ccd964e9.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/assets/shop/dist/payment/index.css b/assets/shop/dist/payment/index.css
new file mode 100644
index 00000000..95e0bf4b
--- /dev/null
+++ b/assets/shop/dist/payment/index.css
@@ -0,0 +1,232 @@
+.oney-payment-choice__item label, .payplug-payment-choice__item label {
+ margin-top: 0 !important;
+ padding: 1em !important;
+ display: flex !important
+}
+
+.oney-payment-choice__container, .payplug-payment-choice__container {
+ margin: 1rem 0;
+ display: flex
+}
+
+.oney-payment-choice__header p, .payplug-payment-choice__header p {
+ color: #000;
+ margin: .5em 0 !important
+}
+
+.oney-payment-choice__item, .payplug-payment-choice__item {
+ flex: auto
+}
+
+.oney-payment-choice__item--oney_x3_with_fees, .oney-payment-choice__item--oney_x3_without_fees, .payplug-payment-choice__item--oney_x3_with_fees, .payplug-payment-choice__item--oney_x3_without_fees {
+ margin-right: 1em
+}
+
+.oney-payment-choice__item input, .payplug-payment-choice__item input {
+ display: none
+}
+
+.oney-payment-choice__item input:checked + label, .payplug-payment-choice__item input:checked + label {
+ background-color: #81bc0021;
+ border-color: #81bc00
+}
+
+.oney-payment-choice__item input:checked + label.payplug-payment-choice__label, .payplug-payment-choice__item input:checked + label.payplug-payment-choice__label {
+ background-color: #8fd2b821;
+ border-color: #8fd2b8
+}
+
+.oney-payment-choice__item label, .payplug-payment-choice__item label {
+ width: 100%;
+ height: 100%;
+ cursor: pointer;
+ border: 1px solid #ccc;
+ border-radius: .285714rem;
+ flex-direction: column;
+ transition: border-color, background-color .3s ease-in-out;
+ position: relative;
+ box-shadow: 0 1px 2px #22242626
+}
+
+.oney-payment-choice__item label img, .payplug-payment-choice__item label img {
+ vertical-align: text-bottom
+}
+
+.oney-payment-choice__content, .payplug-payment-choice__content {
+ display: contents
+}
+
+.oney-payment-choice__content p, .payplug-payment-choice__content p {
+ color: #000;
+ border-bottom: 1px solid #ccc;
+ flex-wrap: wrap;
+ justify-content: space-between;
+ display: flex;
+ margin: 0 !important;
+ padding: .75rem 0 !important
+}
+
+.oney-payment-choice__content p:nth-last-of-type(2), .payplug-payment-choice__content p:nth-last-of-type(2) {
+ margin-bottom: 1rem !important
+}
+
+.oney-payment-choice__content p:last-of-type, .payplug-payment-choice__content p:last-of-type {
+ border: none;
+ margin-top: auto !important;
+ margin-bottom: 0 !important
+}
+
+.oney-payment-choice__content p.oney-without-fees-financing, .payplug-payment-choice__content p.oney-without-fees-financing {
+ display: inline-block
+}
+
+.oney-payment-choice__content small, .payplug-payment-choice__content small {
+ margin-top: .5rem;
+ font-size: 80%
+}
+
+.oney-payment-choice__tab {
+ display: none
+}
+
+.oney-payment-choice__header {
+ text-align: center;
+ margin: 0 auto
+}
+
+[data-gateway=oney] {
+ margin-top: 10px !important;
+ padding: 0 !important
+}
+
+.oney-logo[src*=without-fees] {
+ max-width: 200px
+}
+
+apple-pay-button {
+ --apple-pay-button-width: 222px;
+ --apple-pay-button-height: 40px;
+ --apple-pay-button-border-radius: 4px;
+ --apple-pay-button-padding: 4px 4px;
+ --apple-pay-button-box-sizing: border-box;
+ min-width: 140px;
+ max-width: 100%;
+}
+
+label {
+ cursor: pointer
+}
+
+.payplug-payment-choice__container {
+ flex-direction: column
+}
+
+.payplug-payment-choice__item {
+ margin-bottom: 1em
+}
+
+.payplug-payment-choice__header {
+ justify-content: space-between;
+ align-items: center;
+ display: flex
+}
+
+.payplug-payment-choice__header .card-expiry span {
+ font-weight: 700
+}
+
+.oney-complete-info-popin {
+ max-width: 480px;
+ margin: auto;
+}
+
+.oney-complete-info-popin__content ul {
+ padding: 0;
+ list-style: none
+}
+
+.oney-complete-info-popin__success {
+ display: none
+}
+
+.payment-label-with-image::after {
+ content: "";
+ background-image: var(--logo);
+ display: inline-block;
+ width: 45px;
+ height: 40px;
+ margin-top: -6px;
+ margin-left: 10px;
+ background-repeat: no-repeat;
+ background-position: center;
+ background-size: contain;
+ float: right;
+}
+.payment-label-with-image.oney-label::after {
+ width: 130px;
+}
+
+@media screen and (max-width: 991px) {
+ .oney-payment-choice__container, .payplug-payment-choice__container {
+ display: block
+ }
+
+ .oney-payment-choice__content small, .payplug-payment-choice__content small {
+ width: 100%
+ }
+
+ .oney-payment-choice__tab {
+ justify-content: space-between;
+ display: flex
+ }
+
+ .oney-payment-choice__tab .tablink {
+ text-align: center;
+ border: 1px solid #ccc;
+ border-bottom: 0;
+ flex: 1;
+ padding: 1rem 0
+ }
+
+ .oney-payment-choice__tab .tablink.active {
+ border-bottom: 5px solid #81bc00
+ }
+
+ .oney-payment-choice__tab .tablink p {
+ color: #000
+ }
+
+ .oney-payment-choice__tab .tablink:first-of-type {
+ border-right: 0
+ }
+
+ .oney-payment-choice__tab .tablink > .oney-payment__image {
+ max-width: 120px
+ }
+
+ .oney-payment-choice__header, .oney-payment-choice__item {
+ display: none
+ }
+
+ .oney-payment-choice__item input:checked + label {
+ background-color: #fff;
+ border-color: #ccc
+ }
+
+ .oney-payment-choice__item label {
+ border-top-left-radius: 0;
+ border-top-right-radius: 0
+ }
+
+ .oney-payment-choice__item--oney_x3_with_fees, .oney-payment-choice__item--oney_x3_without_fees {
+ margin-bottom: 1rem;
+ margin-right: 0;
+ display: block
+ }
+}
+
+@media screen and (max-width: 480px) {
+ .payplug-payment-choice__header, .payplug-payment-choice__header .card-type, .payplug-payment-choice__header .card-expiry {
+ display: block
+ }
+}
diff --git a/assets/shop/dist/payment/integrated.css b/assets/shop/dist/payment/integrated.css
new file mode 100644
index 00000000..2bf34d51
--- /dev/null
+++ b/assets/shop/dist/payment/integrated.css
@@ -0,0 +1,301 @@
+.payplugIntegratedPayment {
+ justify-self: center;
+ display: none
+}
+
+.payplugIntegratedPayment * {
+ font-family: Poppins, Arial, sans-serif !important
+}
+
+.payplugIntegratedPayment--loaded {
+ width: 100%;
+ max-width: 400px;
+ flex-wrap: wrap;
+ justify-content: space-between;
+ margin: 20px auto 0;
+ display: flex;
+ position: relative
+}
+
+.payplugIntegratedPayment__select {
+ height: 36px;
+ width: 100%;
+ border: 1px solid #ccc;
+ border-radius: 5px;
+ margin: 0 0 10px;
+ padding: 0 8px
+}
+
+.payplugIntegratedPayment__container {
+ width: 100%;
+ margin: 0 0 10px;
+ padding: 0;
+ display: flex;
+ position: relative
+}
+
+.payplugIntegratedPayment__container--cardHolder,
+.payplugIntegratedPayment__container--pan,
+.payplugIntegratedPayment__container--exp,
+.payplugIntegratedPayment__container--cvv {
+ height: 40px;
+ cursor: text;
+ border: 1px solid #d5d6d8;
+ border-radius: 2px;
+ padding: 0 16px 0 50px;
+ line-height: 40px
+}
+
+.payplugIntegratedPayment__container--cardHolder:before,
+.payplugIntegratedPayment__container--pan:before,
+.payplugIntegratedPayment__container--exp:before,
+.payplugIntegratedPayment__container--cvv:before {
+ content: "";
+ width: 24px;
+ height: 24px;
+ background: #95999e 50%/100% no-repeat;
+ position: absolute;
+ top: 20%;
+ left: 16px
+}
+
+.payplugIntegratedPayment__container--cardHolder:focus,
+.payplugIntegratedPayment__container--pan:focus,
+.payplugIntegratedPayment__container--exp:focus,
+.payplugIntegratedPayment__container--cvv:focus {
+ border-color: #2b343d
+}
+
+.payplugIntegratedPayment__container--cardHolder--invalid,
+.payplugIntegratedPayment__container--pan--invalid,
+.payplugIntegratedPayment__container--exp--invalid,
+.payplugIntegratedPayment__container--cvv--invalid {
+ border-color: #e91932
+}
+
+.payplugIntegratedPayment__container--cardHolder:before {
+ -webkit-mask-image: url(account.039342ad.svg);
+ mask-image: url(account.039342ad.svg)
+}
+
+.payplugIntegratedPayment__container--pan:before {
+ -webkit-mask-image: url(card.0d2bd9bc.svg);
+ mask-image: url(card.0d2bd9bc.svg)
+}
+
+.payplugIntegratedPayment__container--exp:before {
+ -webkit-mask-image: url(calendar.3c23bb16.svg);
+ mask-image: url(calendar.3c23bb16.svg)
+}
+
+.payplugIntegratedPayment__container--cvv:before {
+ -webkit-mask-image: url(lock.fe8a73cd.svg);
+ mask-image: url(lock.fe8a73cd.svg)
+}
+
+.payplugIntegratedPayment__container--exp,
+.payplugIntegratedPayment__container--cvv {
+ max-width: calc(50% - 2px);
+ display: inline-block
+}
+
+.payplugIntegratedPayment__container--scheme {
+ text-transform: uppercase;
+ height: 22px;
+ justify-content: space-between;
+ align-items: center;
+ margin: 10px 0;
+ font-size: 14px;
+ font-weight: 700
+}
+
+.payplugIntegratedPayment__container--saveCard {
+ height: auto;
+ align-items: center;
+ padding: 10px 0 0;
+ display: flex
+}
+
+.payplugIntegratedPayment__container--saveCard input {
+ display: none
+}
+
+.payplugIntegratedPayment__container--saveCard input:checked+label span:before {
+ opacity: 1
+}
+
+.payplugIntegratedPayment__container--saveCard label {
+ cursor: pointer;
+ color: #918f8f;
+ margin: 0 !important;
+ font-size: 12px !important
+}
+
+.payplugIntegratedPayment__container--saveCard label span {
+ cursor: pointer;
+ height: 16px;
+ -o-transition: border .4s;
+ width: 16px;
+ border: 1px solid #d5d6d8;
+ border-radius: 2px;
+ margin: 0 10px -3px 0;
+ transition: border .4s;
+ display: inline-block;
+ position: relative
+}
+
+.payplugIntegratedPayment__container--saveCard label span:before {
+ content: "";
+ height: 5px;
+ opacity: 0;
+ width: 10px;
+ border-top: none;
+ border-bottom: 2.5px solid #2b343d;
+ border-left: 2.5px solid #2b343d;
+ border-right: none;
+ border-radius: 1px;
+ transition: opacity .4s;
+ display: block;
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -55%)rotate(-48deg)
+}
+
+.payplugIntegratedPayment__container--saveCard label:hover {
+ color: #2b343d;
+ transition: all .1s
+}
+
+.payplugIntegratedPayment__container--saveCard label:hover span {
+ border-color: #2b343d;
+ transition: all .1s
+}
+
+.payplugIntegratedPayment__container--transaction {
+ align-items: center;
+ margin-top: 10px
+}
+
+.payplugIntegratedPayment__container--transaction .transaction-label {
+ vertical-align: super;
+ margin-left: 5px;
+ font-size: 12px
+}
+
+.payplugIntegratedPayment__container img.lock-icon {
+ width: 18px;
+ float: left !important
+}
+
+.payplugIntegratedPayment__container img.payplug-logo {
+ width: 80px;
+ height: auto;
+ vertical-align: text-top;
+ margin-left: 6px;
+ display: inline-block;
+ float: inherit !important
+}
+
+.payplugIntegratedPayment__container--privacy-policy {
+ text-align: center;
+ display: inline-block
+}
+
+.payplugIntegratedPayment__container--privacy-policy a {
+ color: #918f8f;
+ font-size: 14px
+}
+
+.payplugIntegratedPayment__schemes {
+ width: 115px;
+ flex-wrap: wrap;
+ justify-content: space-between;
+ align-items: center;
+ display: flex
+}
+
+.payplugIntegratedPayment__schemes label {
+ display: table-cell
+}
+
+.payplugIntegratedPayment__scheme {
+ margin: 0
+}
+
+.payplugIntegratedPayment__scheme span {
+ cursor: pointer;
+ width: 33px;
+ height: 22px;
+ background: 50%/100% no-repeat;
+ display: block
+}
+
+.payplugIntegratedPayment__scheme span:before {
+ width: 100%;
+ height: 100%;
+ content: "";
+ opacity: 0;
+ background: 50%/100% no-repeat;
+ display: block
+}
+
+.payplugIntegratedPayment__scheme input {
+ display: none
+}
+
+.payplugIntegratedPayment__scheme input:checked+span:before {
+ opacity: 1
+}
+
+.payplugIntegratedPayment__scheme--visa span {
+ background-image: url(visa-dark.87c34e0f.svg)
+}
+
+.payplugIntegratedPayment__scheme--visa span:before {
+ background-image: url(visa.d11a46f6.svg)
+}
+
+.payplugIntegratedPayment__scheme--mastercard span {
+ background-image: url(mastercard-dark.8977e440.svg)
+}
+
+.payplugIntegratedPayment__scheme--mastercard span:before {
+ background-image: url(mastercard.7dd4ce0b.svg)
+}
+
+.payplugIntegratedPayment__scheme--cb span {
+ background-image: url(./cb-dark.888aec45.svg)
+}
+
+.payplugIntegratedPayment__scheme--cb span:before {
+ background-image: url(./cb.ccd964e9.svg)
+}
+
+.payplugIntegratedPayment__error {
+ color: #e91932;
+ width: 100%;
+ margin: -10px 0 10px;
+ padding-left: 4px;
+ font-size: 12px;
+ line-height: 18px
+}
+
+.payplugIntegratedPayment__error--cardHolder {
+ margin: -10px 0 0
+}
+
+.payplugIntegratedPayment__error--cvv {
+ justify-self: flex-end;
+ margin: -10px 0 10px auto
+}
+
+.payplugIntegratedPayment__error--exp,
+.payplugIntegratedPayment__error--cvv {
+ width: 100%;
+ max-width: 49%
+}
+
+.payplugIntegratedPayment__error--hide {
+ display: none
+}
diff --git a/assets/shop/dist/payment/lock.fe8a73cd.svg b/assets/shop/dist/payment/lock.fe8a73cd.svg
new file mode 100644
index 00000000..96076615
--- /dev/null
+++ b/assets/shop/dist/payment/lock.fe8a73cd.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/assets/shop/dist/payment/mastercard-dark.8977e440.svg b/assets/shop/dist/payment/mastercard-dark.8977e440.svg
new file mode 100644
index 00000000..371c9023
--- /dev/null
+++ b/assets/shop/dist/payment/mastercard-dark.8977e440.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/assets/shop/dist/payment/mastercard.7dd4ce0b.svg b/assets/shop/dist/payment/mastercard.7dd4ce0b.svg
new file mode 100644
index 00000000..4a6736e6
--- /dev/null
+++ b/assets/shop/dist/payment/mastercard.7dd4ce0b.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/assets/shop/dist/payment/visa-dark.87c34e0f.svg b/assets/shop/dist/payment/visa-dark.87c34e0f.svg
new file mode 100644
index 00000000..40891108
--- /dev/null
+++ b/assets/shop/dist/payment/visa-dark.87c34e0f.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/assets/shop/dist/payment/visa.d11a46f6.svg b/assets/shop/dist/payment/visa.d11a46f6.svg
new file mode 100644
index 00000000..09454b38
--- /dev/null
+++ b/assets/shop/dist/payment/visa.d11a46f6.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/assets/shop/entrypoint.js b/assets/shop/entrypoint.js
new file mode 100644
index 00000000..9ccfccbe
--- /dev/null
+++ b/assets/shop/entrypoint.js
@@ -0,0 +1 @@
+// Mandatory by test application
diff --git a/assets/yarn.lock b/assets/yarn.lock
new file mode 100644
index 00000000..961087b2
--- /dev/null
+++ b/assets/yarn.lock
@@ -0,0 +1,18 @@
+# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
+# yarn lockfile v1
+
+
+"@hotwired/stimulus@^3.0.0":
+ version "3.2.2"
+ resolved "https://registry.yarnpkg.com/@hotwired/stimulus/-/stimulus-3.2.2.tgz#071aab59c600fed95b97939e605ff261a4251608"
+ integrity sha512-eGeIqNOQpXoPAIP7tC1+1Yc1yl1xnwYqg+3mzqxyrbE5pg5YFBZcA6YoTiByJB6DKAEsiWtl6tjTJS4IYtbB7A==
+
+jquery@^3.5.1:
+ version "3.7.1"
+ resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.7.1.tgz#083ef98927c9a6a74d05a6af02806566d16274de"
+ integrity sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg==
+
+webfontloader@^1.6.28:
+ version "1.6.28"
+ resolved "https://registry.yarnpkg.com/webfontloader/-/webfontloader-1.6.28.tgz#db786129253cb6e8eae54c2fb05f870af6675bae"
+ integrity sha512-Egb0oFEga6f+nSgasH3E0M405Pzn6y3/9tOVanv/DLfa1YBIgcv90L18YyWnvXkRbIM17v5Kv6IT2N6g1x5tvQ==
diff --git a/composer.json b/composer.json
old mode 100644
new mode 100755
index 0402c1c1..f7d338e1
--- a/composer.json
+++ b/composer.json
@@ -1,61 +1,83 @@
{
"name": "payplug/sylius-payplug-plugin",
"type": "sylius-plugin",
- "keywords": ["sylius", "sylius-plugin", "payplug"],
+ "keywords": [
+ "sylius",
+ "sylius-plugin",
+ "payplug",
+ "symfony-ux"
+ ],
"description": "PayPlug payment plugin for Sylius applications.",
"license": "MIT",
"require": {
- "php": "^7.4 || ^8.0",
+ "php": "^8.2",
+ "ext-json": "*",
"giggsey/libphonenumber-for-php": "^8.12",
- "payplug/payplug-php": "^3.1",
- "sylius/refund-plugin": "^1.0.0",
- "sylius/sylius": "^1.9.0 || ^1.10.0",
- "symfony/lock": "^4.3|^5.0",
- "symfony/validator": "^4.3|^5.0"
+ "payplug/payplug-php": "^4.0",
+ "php-http/message-factory": "^1.1",
+ "sylius/refund-plugin": "^2.0",
+ "sylius/sylius": "^2.0",
+ "symfony/asset": "^6.4|| ^7.2",
+ "symfony/lock": "^6.4|| ^7.2",
+ "symfony/validator": "^6.4|| ^7.2"
},
"require-dev": {
- "behat/behat": "^3.7",
- "behat/mink-selenium2-driver": "^1.4",
- "dmore/behat-chrome-extension": "^1.3",
- "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/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",
- "friendsoftwig/twigcs": "^5.0",
- "j13k/yaml-lint": "^1.1",
- "php-parallel-lint/php-parallel-lint": "^1.0",
- "lakion/mink-debug-extension": "^2.0",
- "phpmd/phpmd": "^2.8",
- "phpspec/phpspec": "^6.1",
- "phpstan/extension-installer": "^1.0",
- "phpstan/phpstan": "0.12.91",
- "phpstan/phpstan-doctrine": "0.12.41",
- "phpstan/phpstan-strict-rules": "^0.12.10",
- "phpstan/phpstan-webmozart-assert": "0.12.14",
- "phpunit/phpunit": "^9.0",
- "slevomat/coding-standard": "^6.3.2",
- "sylius-labs/coding-standard": "^4.0",
- "symfony/browser-kit": "^5.1",
- "symfony/debug-bundle": "^5.1",
- "symfony/dotenv": "^5.1",
- "symfony/intl": "^5.1",
- "symfony/web-profiler-bundle": "^5.1",
+ "behat/behat": "3.20.0",
+ "behat/mink-selenium2-driver": "1.7.0",
+ "dmore/behat-chrome-extension": "1.4.0",
+ "dmore/chrome-mink-driver": "2.9.3",
+ "friends-of-behat/mink": "1.11.0",
+ "friends-of-behat/mink-browserkit-driver": "1.6.2",
+ "friends-of-behat/mink-debug-extension": "2.1.0",
+ "friends-of-behat/mink-extension": "2.7.5",
+ "friends-of-behat/page-object-extension": "0.3.2",
+ "friends-of-behat/suite-settings-extension": "1.1.0",
+ "friends-of-behat/symfony-extension": "2.6.0",
+ "friends-of-behat/variadic-extension": "1.6.0",
+ "friendsoftwig/twigcs": "6.5.0",
+ "lakion/mink-debug-extension": "2.0.0",
+ "mockery/mockery": "1.6.12",
+ "php-parallel-lint/php-parallel-lint": "1.4.0",
+ "phpmd/phpmd": "^2.15.0",
+ "phpro/grumphp": "^2.12",
+ "phpstan/extension-installer": "1.4.3",
+ "phpstan/phpstan": "2.0.4",
+ "phpstan/phpstan-doctrine": "2.0.1",
+ "phpstan/phpstan-strict-rules": "2.0.1",
+ "phpstan/phpstan-webmozart-assert": "2.0.0",
+ "phpunit/phpunit": "^9.6",
+ "rector/rector": "2.0.4",
+ "sylius-labs/coding-standard": "4.4.0",
+ "sylius/test-application": "^2.1.0@alpha",
+ "symfony/apache-pack": "*",
+ "symfony/browser-kit": "^6.4|| ^7.2",
+ "symfony/debug-bundle": "^6.4|| ^7.2",
+ "symfony/dotenv": "^6.4|| ^7.2",
+ "symfony/web-profiler-bundle": "^6.4|| ^7.2",
"webmozart/assert": "^1.8"
},
"prefer-stable": true,
"autoload": {
"psr-4": {
- "PayPlug\\SyliusPayPlugPlugin\\": "src/",
- "Tests\\PayPlug\\SyliusPayPlugPlugin\\": "tests/"
+ "PayPlug\\SyliusPayPlugPlugin\\": "src/"
+ }
+ },
+ "autoload-dev": {
+ "psr-4": {
+ "Tests\\PayPlug\\SyliusPayPlugPlugin\\": ["tests/", "tests/TestApplication/src/"]
}
},
"config": {
- "sort-packages": true
+ "sort-packages": true,
+ "allow-plugins": {
+ "dealerdirect/phpcodesniffer-composer-installer": true,
+ "php-http/discovery": true,
+ "phpro/grumphp": true,
+ "phpstan/extension-installer": true,
+ "symfony/flex": true,
+ "symfony/runtime": true,
+ "symfony/thanks": true
+ }
},
"scripts": {
"ecs": "ecs check -c rulesets/ecs.php --ansi --clear-cache .",
@@ -68,6 +90,26 @@
"@phpmd",
"@phpstan",
"@phpunit"
+ ],
+ "test-application:install": [
+ "vendor/bin/console doctrine:database:drop --force --if-exists -n",
+ "vendor/bin/console doctrine:database:create -n",
+ "vendor/bin/console doctrine:migration:migrate -n",
+ "vendor/bin/console sylius:payment:generate-key -n",
+ "vendor/bin/console sylius:fixtures:load -n",
+ "yarn --cwd vendor/sylius/test-application install",
+ "yarn --cwd vendor/sylius/test-application build",
+ "vendor/bin/console assets:install --symlink -n"
+ ],
+ "test-application:front": [
+ "yarn --cwd vendor/sylius/test-application install --force",
+ "yarn --cwd vendor/sylius/test-application build"
]
+ },
+ "extra": {
+ "public-dir": "vendor/sylius/test-application/public",
+ "symfony": {
+ "require": "^6.4"
+ }
}
}
diff --git a/src/Resources/config/config.yml b/config/config.yml
similarity index 68%
rename from src/Resources/config/config.yml
rename to config/config.yml
index c476d4c2..fb713c19 100644
--- a/src/Resources/config/config.yml
+++ b/config/config.yml
@@ -1,4 +1,4 @@
imports:
- { resource: "state_machine.yml" }
- { resource: "resources.yaml" }
- - { resource: "ui.yaml" }
+ - { resource: "twig_hooks/*.yaml" }
diff --git a/src/Resources/config/resources.yaml b/config/resources.yaml
similarity index 100%
rename from src/Resources/config/resources.yaml
rename to config/resources.yaml
diff --git a/config/routing.yaml b/config/routing.yaml
new file mode 100644
index 00000000..84c98416
--- /dev/null
+++ b/config/routing.yaml
@@ -0,0 +1,6 @@
+payplug_sylius_admin:
+ resource: "@PayPlugSyliusPayPlugPlugin/config/routing/admin.yaml"
+ prefix: '/%sylius_admin.path_name%'
+
+payplug_sylius_shop:
+ resource: "@PayPlugSyliusPayPlugPlugin/config/routing/shop.yaml"
diff --git a/config/routing/admin.yaml b/config/routing/admin.yaml
new file mode 100644
index 00000000..4800b8e9
--- /dev/null
+++ b/config/routing/admin.yaml
@@ -0,0 +1,5 @@
+controllers:
+ resource:
+ path: '../../src/Action/Admin/'
+ namespace: 'PayPlug\SyliusPayPlugPlugin\Action\Admin'
+ type: attribute
\ No newline at end of file
diff --git a/config/routing/shop.yaml b/config/routing/shop.yaml
new file mode 100644
index 00000000..4f34982e
--- /dev/null
+++ b/config/routing/shop.yaml
@@ -0,0 +1,5 @@
+controllers:
+ resource:
+ path: '../../src/Controller/'
+ namespace: 'PayPlug\SyliusPayPlugPlugin\Controller'
+ type: attribute
\ No newline at end of file
diff --git a/config/services.yaml b/config/services.yaml
new file mode 100644
index 00000000..8b5dac42
--- /dev/null
+++ b/config/services.yaml
@@ -0,0 +1,140 @@
+services:
+ _defaults:
+ autowire: true
+ autoconfigure: true
+ public: false
+
+ PayPlug\SyliusPayPlugPlugin\:
+ resource: '../src/*'
+ exclude: '../src/{ApiClient,DependencyInjection,Entity,Exception,Model,Repository,PayPlugSyliusPayPlugPlugin.php}'
+ bind:
+ Psr\Log\LoggerInterface: '@monolog.logger.payplug'
+
+ PayPlug\SyliusPayPlugPlugin\Repository\PaymentRepositoryInterface:
+ class: PayPlug\SyliusPayPlugPlugin\Repository\PaymentRepository
+ parent: sylius.repository.payment
+
+ PayPlug\SyliusPayPlugPlugin\Repository\PaymentMethodRepositoryInterface:
+ class: PayPlug\SyliusPayPlugPlugin\Repository\PaymentMethodRepository
+ parent: sylius.repository.payment_method
+
+ PayPlug\SyliusPayPlugPlugin\Controller\OrderController:
+ parent: sylius.controller.order
+ autowire: true
+
+ PayPlug\SyliusPayPlugPlugin\Provider\OneySimulation\OneySimulationDataProviderInterface:
+ class: PayPlug\SyliusPayPlugPlugin\Provider\OneySimulation\OneySimulationDataProvider
+
+ payplug_sylius_payplug_plugin.action.capture:
+ class: PayPlug\SyliusPayPlugPlugin\Action\CaptureAction
+
+ payplug_sylius_payplug_plugin.action.status:
+ class: PayPlug\SyliusPayPlugPlugin\Action\StatusAction
+
+ payplug_sylius_payplug_plugin.action.convert_payment:
+ class: PayPlug\SyliusPayPlugPlugin\Action\ConvertPaymentAction
+
+ payplug_sylius_payplug_plugin.action.notify:
+ class: PayPlug\SyliusPayPlugPlugin\Action\NotifyAction
+
+ ## Default Payplug Gateway ##
+ payplug_sylius_payplug_plugin.command_provider.payplug:
+ class: Sylius\Bundle\PaymentBundle\CommandProvider\ActionsCommandProvider
+ arguments:
+ - !tagged_locator
+ tag: payplug_sylius_payplug_plugin.command_provider.payplug
+ index_by: 'action'
+ tags:
+ - name: sylius.payment_request.command_provider
+ gateway_factory: !php/const PayPlug\SyliusPayPlugPlugin\Gateway\PayPlugGatewayFactory::FACTORY_NAME
+ payplug_sylius_payplug_plugin.provider.order_pay.http_response.payplug:
+ class: Sylius\Bundle\PaymentBundle\Provider\ActionsHttpResponseProvider
+ arguments:
+ - !tagged_locator
+ tag: payplug_sylius_payplug_plugin.http_response_provider.payplug
+ index_by: action
+ tags:
+ - name: sylius.payment_request.provider.http_response
+ gateway_factory: !php/const PayPlug\SyliusPayPlugPlugin\Gateway\PayPlugGatewayFactory::FACTORY_NAME
+
+ ## Oney Payplug Gateway ##
+ payplug_sylius_payplug_plugin.command_provider.payplug_oney:
+ class: Sylius\Bundle\PaymentBundle\CommandProvider\ActionsCommandProvider
+ arguments:
+ - !tagged_locator
+ tag: payplug_sylius_payplug_plugin.command_provider.payplug_oney
+ index_by: 'action'
+ tags:
+ - name: sylius.payment_request.command_provider
+ gateway_factory: !php/const PayPlug\SyliusPayPlugPlugin\Gateway\OneyGatewayFactory::FACTORY_NAME
+ payplug_sylius_payplug_plugin.provider.order_pay.http_response.payplug_oney:
+ class: Sylius\Bundle\PaymentBundle\Provider\ActionsHttpResponseProvider
+ arguments:
+ - !tagged_locator
+ tag: payplug_sylius_payplug_plugin.http_response_provider.payplug_oney
+ index_by: action
+ tags:
+ - name: sylius.payment_request.provider.http_response
+ gateway_factory: !php/const PayPlug\SyliusPayPlugPlugin\Gateway\OneyGatewayFactory::FACTORY_NAME
+
+
+ ## Bancontact Payplug Gateway ##
+ payplug_sylius_payplug_plugin.command_provider.payplug_bancontact:
+ class: Sylius\Bundle\PaymentBundle\CommandProvider\ActionsCommandProvider
+ arguments:
+ - !tagged_locator
+ tag: payplug_sylius_payplug_plugin.command_provider.payplug_bancontact
+ index_by: 'action'
+ tags:
+ - name: sylius.payment_request.command_provider
+ gateway_factory: !php/const PayPlug\SyliusPayPlugPlugin\Gateway\BancontactGatewayFactory::FACTORY_NAME
+ payplug_sylius_payplug_plugin.provider.order_pay.http_response.payplug_bancontact:
+ class: Sylius\Bundle\PaymentBundle\Provider\ActionsHttpResponseProvider
+ arguments:
+ - !tagged_locator
+ tag: payplug_sylius_payplug_plugin.http_response_provider.payplug_bancontact
+ index_by: action
+ tags:
+ - name: sylius.payment_request.provider.http_response
+ gateway_factory: !php/const PayPlug\SyliusPayPlugPlugin\Gateway\BancontactGatewayFactory::FACTORY_NAME
+
+
+ ## Amex Payplug Gateway ##
+ payplug_sylius_payplug_plugin.command_provider.payplug_american_express:
+ class: Sylius\Bundle\PaymentBundle\CommandProvider\ActionsCommandProvider
+ arguments:
+ - !tagged_locator
+ tag: payplug_sylius_payplug_plugin.command_provider.payplug_american_express
+ index_by: 'action'
+ tags:
+ - name: sylius.payment_request.command_provider
+ gateway_factory: !php/const PayPlug\SyliusPayPlugPlugin\Gateway\AmericanExpressGatewayFactory::FACTORY_NAME
+ payplug_sylius_payplug_plugin.provider.order_pay.http_response.payplug_american_express:
+ class: Sylius\Bundle\PaymentBundle\Provider\ActionsHttpResponseProvider
+ arguments:
+ - !tagged_locator
+ tag: payplug_sylius_payplug_plugin.http_response_provider.payplug_american_express
+ index_by: action
+ tags:
+ - name: sylius.payment_request.provider.http_response
+ gateway_factory: !php/const PayPlug\SyliusPayPlugPlugin\Gateway\AmericanExpressGatewayFactory::FACTORY_NAME
+
+ ## Apple Pay Payplug Gateway ##
+ payplug_sylius_payplug_plugin.command_provider.payplug_apple_pay:
+ class: Sylius\Bundle\PaymentBundle\CommandProvider\ActionsCommandProvider
+ arguments:
+ - !tagged_locator
+ tag: payplug_sylius_payplug_plugin.command_provider.payplug_apple_pay
+ index_by: 'action'
+ tags:
+ - name: sylius.payment_request.command_provider
+ gateway_factory: !php/const PayPlug\SyliusPayPlugPlugin\Gateway\ApplePayGatewayFactory::FACTORY_NAME
+ payplug_sylius_payplug_plugin.provider.order_pay.http_response.payplug_apple_pay:
+ class: Sylius\Bundle\PaymentBundle\Provider\ActionsHttpResponseProvider
+ arguments:
+ - !tagged_locator
+ tag: payplug_sylius_payplug_plugin.http_response_provider.payplug_apple_pay
+ index_by: action
+ tags:
+ - name: sylius.payment_request.provider.http_response
+ gateway_factory: !php/const PayPlug\SyliusPayPlugPlugin\Gateway\ApplePayGatewayFactory::FACTORY_NAME
diff --git a/config/services/client.xml b/config/services/client.xml
new file mode 100644
index 00000000..0ca32b00
--- /dev/null
+++ b/config/services/client.xml
@@ -0,0 +1,57 @@
+
+
+
+
+
+
+
+
+
+
+
+ payplug
+
+
+
+
+ payplug_oney
+
+
+
+
+ payplug_bancontact
+
+
+
+
+ payplug_apple_pay
+
+
+
+
+ american_express
+
+
+
diff --git a/config/services/gateway.xml b/config/services/gateway.xml
new file mode 100644
index 00000000..eeda89e7
--- /dev/null
+++ b/config/services/gateway.xml
@@ -0,0 +1,51 @@
+
+
+
+
+
+
+
+ PayPlug\SyliusPayPlugPlugin\Gateway\PayPlugGatewayFactory
+
+
+
+
+
+ PayPlug\SyliusPayPlugPlugin\Gateway\OneyGatewayFactory
+
+
+
+
+
+ PayPlug\SyliusPayPlugPlugin\Gateway\BancontactGatewayFactory
+
+
+
+
+
+ PayPlug\SyliusPayPlugPlugin\Gateway\ApplePayGatewayFactory
+
+
+
+
+
+ PayPlug\SyliusPayPlugPlugin\Gateway\AmericanExpressGatewayFactory
+
+
+
+
diff --git a/config/state_machine.yml b/config/state_machine.yml
new file mode 100644
index 00000000..2781aef2
--- /dev/null
+++ b/config/state_machine.yml
@@ -0,0 +1,10 @@
+framework:
+ workflows:
+ !php/const Sylius\Component\Core\OrderPaymentTransitions::GRAPH:
+ transitions:
+ oney_request_payment: # Allow to request payment from authorized (for Oney)
+ from:
+ - cart
+ - authorized
+ to:
+ - awaiting_payment
diff --git a/config/twig_hooks/admin.yaml b/config/twig_hooks/admin.yaml
new file mode 100644
index 00000000..931ecf69
--- /dev/null
+++ b/config/twig_hooks/admin.yaml
@@ -0,0 +1,49 @@
+sylius_twig_hooks:
+ hooks:
+ sylius_admin.payment_method.update.content:
+ payplug_flashes:
+ template: '@PayPlugSyliusPayPlugPlugin/admin/shared/flashes.html.twig'
+ priority: 350
+ 'sylius_admin.payment_method.create.content.form.sections.gateway_configuration.payplug': &payplugGateway
+ live_checkbox: &liveCheckbox
+ template: '@PayPlugSyliusPayPlugPlugin/admin/payment_method/form/live_checkbox.html.twig'
+ priority: 0
+ one_click:
+ template: '@PayPlugSyliusPayPlugPlugin/admin/payment_method/form/one_click.html.twig'
+ priority: 0
+ integrated_payment:
+ template: '@PayPlugSyliusPayPlugPlugin/admin/payment_method/form/integrated_payment.html.twig'
+ priority: 0
+ deferred_capture:
+ template: '@PayPlugSyliusPayPlugPlugin/admin/payment_method/form/deferred_capture.html.twig'
+ priority: 0
+ 'sylius_admin.payment_method.create.content.form.sections.gateway_configuration.payplug_oney': &oneyGateway
+ live_checkbox: *liveCheckbox
+ fees_for:
+ template: '@PayPlugSyliusPayPlugPlugin/admin/payment_method/form/fees_for.html.twig'
+ priority: 0
+ 'sylius_admin.payment_method.create.content.form.sections.gateway_configuration.payplug_bancontact': &bancontactGateway
+ live_checkbox: *liveCheckbox
+ 'sylius_admin.payment_method.create.content.form.sections.gateway_configuration.payplug_apple_pay': &applePayGateway
+ live_checkbox: *liveCheckbox
+
+ 'sylius_admin.payment_method.create.content.form.sections.gateway_configuration.payplug_american_express': &amexGateway
+ live_checkbox: *liveCheckbox
+
+ 'sylius_admin.payment_method.update.content.form.sections.gateway_configuration.payplug':
+ <<: *payplugGateway
+ renew_oauth: &renewOAuth
+ template: '@PayPlugSyliusPayPlugPlugin/admin/payment_method/form/renew_oauth.html.twig'
+ priority: -5
+ 'sylius_admin.payment_method.update.content.form.sections.gateway_configuration.payplug_oney':
+ <<: *oneyGateway
+ renew_oauth: *renewOAuth
+ 'sylius_admin.payment_method.update.content.form.sections.gateway_configuration.payplug_bancontact':
+ <<: *bancontactGateway
+ renew_oauth: *renewOAuth
+ 'sylius_admin.payment_method.update.content.form.sections.gateway_configuration.payplug_apple_pay':
+ <<: *applePayGateway
+ renew_oauth: *renewOAuth
+ 'sylius_admin.payment_method.update.content.form.sections.gateway_configuration.payplug_american_express':
+ <<: *amexGateway
+ renew_oauth: *renewOAuth
diff --git a/config/twig_hooks/shop.yaml b/config/twig_hooks/shop.yaml
new file mode 100644
index 00000000..ed250d5f
--- /dev/null
+++ b/config/twig_hooks/shop.yaml
@@ -0,0 +1,46 @@
+sylius_twig_hooks:
+ hooks:
+ 'sylius_shop.product.show.content.info.summary':
+ pay_with_oney:
+ template: '@PayPlugSyliusPayPlugPlugin/shop/oney/product/pay_with_oney.html.twig'
+ priority: 101
+ 'sylius_shop.cart.index.content.form.sections.general#right':
+ pay_with_oney:
+ template: '@PayPlugSyliusPayPlugPlugin/shop/oney/cart/pay_with_oney.html.twig'
+ priority: 1
+ 'sylius.shop.account.saved_cards.index.subcontent':
+ header:
+ template: '@PayPlugSyliusPayPlugPlugin/shop/saved_cards/index/_header.html.twig'
+ priority: 20
+ subcontent:
+ template: "@PayPlugSyliusPayPlugPlugin/shop/saved_cards/index/_subcontent.html.twig"
+ priority: 10
+
+ 'sylius_shop.checkout.select_payment':
+ modal:
+ template: '@PayPlugSyliusPayPlugPlugin/shared/modal/index.html.twig'
+ priority: 0
+
+ 'sylius_shop.shared.form.select_payment.payment':
+ choice:
+ template: '@PayPlugSyliusPayPlugPlugin/shared/form/select_payment/payment/choice.html.twig'
+ priority: 0
+
+ 'sylius_shop.shared.form.select_payment.payment.choice.details':
+ choice:
+ template: '@PayPlugSyliusPayPlugPlugin/shop/select_payment/choice.html.twig'
+ 'sylius_shop.shared.form.select_payment.payment.choice.details#payplug':
+ payplug:
+ template: '@PayPlugSyliusPayPlugPlugin/shop/select_payment/_payplug.html.twig'
+ 'sylius_shop.shared.form.select_payment.payment.choice.details#payplug_oney':
+ oney:
+ template: '@PayPlugSyliusPayPlugPlugin/shop/select_payment/_oney.html.twig'
+ 'sylius_shop.shared.form.select_payment.payment.choice.details#payplug_bancontact':
+ bancontact:
+ template: '@PayPlugSyliusPayPlugPlugin/shop/select_payment/_bancontact.html.twig'
+ 'sylius_shop.shared.form.select_payment.payment.choice.details#payplug_apple_pay':
+ apple_pay:
+ template: '@PayPlugSyliusPayPlugPlugin/shop/select_payment/_apple_pay.html.twig'
+ 'sylius_shop.shared.form.select_payment.payment.choice.details#payplug_american_express':
+ american_express:
+ template: '@PayPlugSyliusPayPlugPlugin/shop/select_payment/_american_express.html.twig'
diff --git a/doc/authorized_payment.md b/doc/authorized_payment.md
new file mode 100644
index 00000000..83332706
--- /dev/null
+++ b/doc/authorized_payment.md
@@ -0,0 +1,155 @@
+# Authorized Payment
+
+This feature allow merchant to deferred the capture of the payment.
+The payment is authorized and the capture can be done later.
+
+> [!IMPORTANT]
+> The authorized payment feature is only available for the "PayPlug" payment gateway.
+
+## Activation
+
+On the payment method configuration, you can enable the deferred capture feature.
+
+
+
+## Trigger the capture
+
+### Periodically
+
+An authorized payment is valid for 7 days.
+You can trigger the capture of the authorized payment by running the following command:
+
+```bash
+$ bin/console payplug:capture-authorized-payments --days=6
+```
+
+It will capture all authorized payments that are older than 6 days.
+
+> [!TIP]
+> You can add this command to a cron job to automate the capture of the authorized payments.
+
+### Programmatically
+
+An authorized payment is in state `AUTHORIZED`.
+A capture trigger is placed on the complete transition for such payments.
+
+```yaml
+winzou_state_machine:
+ sylius_payment:
+ callbacks:
+ before:
+ payplug_sylius_payplug_plugin_complete:
+ on: ["complete"]
+ do: ["@payplug_sylius_payplug_plugin.payment_processing.capture", "process"]
+ args: ["object"]
+```
+> [!NOTE]
+> This configuration is already added by the plugin.
+
+### With Winzou State Machine
+
+For example, if you want to trigger the capture when an order is shipped, you can create a callback on the `sylius_order_shipping` state machine.
+
+File: `config/packages/winzou_state_machine.yaml`
+
+```yaml
+winzou_state_machine:
+ sylius_order_shipping:
+ callbacks:
+ before:
+ app_ensure_capture_payment:
+ on: ["ship"]
+ do: ['@App\StateMachine\CaptureOrderProcessor', "process"]
+ args: ["object"]
+```
+
+File : `src/StateMachine/CaptureOrderProcessor.php`
+
+```php
+getLastPayment(PaymentInterface::STATE_AUTHORIZED);
+ if (null === $payment) {
+ // No payment in authorized state, nothing to do here
+ return;
+ }
+
+ $this->stateMachineFactory
+ ->get($payment, PaymentTransitions::GRAPH)
+ ->apply(PaymentTransitions::TRANSITION_COMPLETE);
+
+ if (PaymentInterface::STATE_COMPLETED !== $payment->getState()) {
+ throw new \LogicException('Oh no! Payment capture failed 💸');
+ }
+ }
+}
+```
+
+### With Symfony Workflow (default in Sylius 2)
+
+If you are using Symfony Workflow, you can create a custom action to capture the payment, with a transition listener.
+
+File: `src/StateMachine/CaptureOrderProcessor.php`
+
+```php
+getSubject();
+ if (!$order instanceof OrderInterface) {
+ throw new \LogicException('Expected an instance of OrderInterface');
+ }
+
+ $payment = $order->getLastPayment(PaymentInterface::STATE_AUTHORIZED);
+ if (null === $payment) {
+ // No payment in authorized state, nothing to do here
+ return;
+ }
+
+ $this->stateMachine->apply($payment, PaymentTransitions::GRAPH, PaymentTransitions::TRANSITION_COMPLETE);
+ if (PaymentInterface::STATE_COMPLETED !== $payment->getState()) {
+ throw new \LogicException('Oh no! Payment capture failed 💸');
+ }
+ }
+}
+```
diff --git a/doc/development.md b/doc/development.md
index 49ab58b7..ab362af8 100644
--- a/doc/development.md
+++ b/doc/development.md
@@ -3,13 +3,11 @@
## Assets
For the sake of quickness, the usage of a tool like [Parcel](https://github.com/parcel-bundler/parcel/tree/1.x) shows that its efficiency is indeed undeniable.
-
-So, if you want to edit assets (js, scss, ...) you'll likely go into `src/Resources/dev` and run `yarn install`.
-
+So, if you want to edit assets (js, scss, ...) you'll likely go into `assets` and run `yarn install`.
Then, you'll find a list of commands inside `package.json` which are :
```bash
-$ (cd src/Resources/dev && yarn build)
+$ (cd assets && yarn build)
```
Or, if you prefer the dev mode; a `watch` command that compile in real time, then run:
@@ -18,9 +16,10 @@ Or, if you prefer the dev mode; a `watch` command that compile in real time, the
$ (cd src/Resources/dev && yarn dev)
```
-You can add any resources as far as Parcel can go, but those have to be located in `/pages` otherwize they won't be compiled.
+You can add any resources as far as Parcel can go,
+but those have to be located in `/pages` otherwise they won't be compiled.
-Assets can be found in `src/Resources/public/assets/oney` so you'll have to install them in your application by running:
+Assets can be found in `public/assets/oney` so you'll have to install them in your application by running:
```bash
$ bin/console assets:install --symlink
@@ -28,7 +27,8 @@ $ bin/console assets:install --symlink
$ bin/console sylius:theme:assets:install --symlink # e.g if bootstrapTheme is enabled
```
-To make it fully compatible with [Sylius Bootstrap Theme](https://github.com/Sylius/BootstrapTheme), some lines have to be added to ̀the main entrypoint (such as `app.js`) of the theme:
+To make it fully compatible with [Sylius Bootstrap Theme](https://github.com/Sylius/BootstrapTheme),
+some lines have to be added to the main entrypoint (such as `app.js`) of the theme:
```js
const $ = require('jquery');
diff --git a/doc/images/admin_deferred_capture_feature.png b/doc/images/admin_deferred_capture_feature.png
new file mode 100644
index 00000000..0244aeba
Binary files /dev/null and b/doc/images/admin_deferred_capture_feature.png differ
diff --git a/grumphp.yml b/grumphp.yml
new file mode 100644
index 00000000..1ae601d8
--- /dev/null
+++ b/grumphp.yml
@@ -0,0 +1,32 @@
+grumphp:
+ ascii:
+ failed: ~
+ succeeded: ~
+ tasks:
+ composer:
+ no_check_all: true
+ jsonlint:
+ detect_key_conflicts: true
+ phplint:
+ exclude: ['vendor', 'tests/Application/*']
+ triggered_by: ['php']
+ phpstan:
+ level: ~
+ configuration: 'ruleset/phpstan.neon'
+ use_grumphp_paths: false
+ securitychecker_symfony: ~
+ yamllint:
+ parse_custom_tags: true
+ ecs:
+ config: 'ruleset/ecs.php'
+ no-progress-bar: true
+ twigcs:
+ path: 'src/'
+ severity: error
+ phpcs:
+ standard: "ruleset"
+ warning_severity: 0
+ whitelist_patterns:
+ - 'src'
+ exclude:
+ - 'PSR12.Files.FileHeader'
\ No newline at end of file
diff --git a/install/Application/.gitignore b/install/Application/.gitignore
deleted file mode 100644
index 83c72082..00000000
--- a/install/Application/.gitignore
+++ /dev/null
@@ -1,3 +0,0 @@
-.env.*.local
-.env.local
-.env.local.php
diff --git a/install/Application/config/packages/payplug.yaml b/install/Application/config/packages/payplug.yaml
deleted file mode 100644
index 1d41d8b4..00000000
--- a/install/Application/config/packages/payplug.yaml
+++ /dev/null
@@ -1,30 +0,0 @@
-parameters:
- sylius_refund.supported_gateways:
- - payplug
- - payplug_oney
- - cash_on_delivery
-
-services:
- payplug_sylius_payplug_plugin.api_client.payplug:
- class: Tests\PayPlug\SyliusPayPlugPlugin\Behat\Mocker\PayPlugApiClient
- public: true
- arguments:
- - "@service_container"
- - 'payplug_sylius_payplug_plugin.api_client.payplug'
-
- payplug_sylius_payplug_plugin.api_client.oney:
- class: Tests\PayPlug\SyliusPayPlugPlugin\Behat\Mocker\PayPlugApiClient
- public: true
- arguments:
- - "@service_container"
- - 'payplug_sylius_payplug_plugin.api_client.oney'
-
- Tests\Sylius\RefundPlugin\Behat\Services\Generator\FailedCreditMemoGenerator:
- decorates: 'Sylius\RefundPlugin\Generator\CreditMemoGeneratorInterface'
- arguments:
- - '@Tests\Sylius\RefundPlugin\Behat\Services\Generator\FailedCreditMemoGenerator.inner'
-
- Tests\Sylius\RefundPlugin\Behat\Services\Factory\FailedRefundPaymentFactory:
- decorates: 'sylius_refund.factory.refund_payment'
- arguments:
- - '@Tests\Sylius\RefundPlugin\Behat\Services\Factory\FailedRefundPaymentFactory.inner'
diff --git a/install/Application/config/packages/sylius_payplug.yaml b/install/Application/config/packages/sylius_payplug.yaml
deleted file mode 100644
index 82462ec0..00000000
--- a/install/Application/config/packages/sylius_payplug.yaml
+++ /dev/null
@@ -1,3 +0,0 @@
-imports:
- - { resource: "@PayPlugSyliusPayPlugPlugin/Resources/config/config.yml" }
- - { resource: "@PayPlugSyliusPayPlugPlugin/Resources/config/services.xml" }
diff --git a/install/Application/config/packages/sylius_refund.yaml b/install/Application/config/packages/sylius_refund.yaml
deleted file mode 100644
index ce7ff8ae..00000000
--- a/install/Application/config/packages/sylius_refund.yaml
+++ /dev/null
@@ -1,2 +0,0 @@
-imports:
- - { resource: "@SyliusRefundPlugin/Resources/config/app/config.yml" }
diff --git a/install/Application/config/routes/payplug.yaml b/install/Application/config/routes/payplug.yaml
deleted file mode 100644
index 778bf549..00000000
--- a/install/Application/config/routes/payplug.yaml
+++ /dev/null
@@ -1,2 +0,0 @@
-sylius_payplug:
- resource: "@PayPlugSyliusPayPlugPlugin/Resources/config/routing.yaml"
diff --git a/install/Application/config/services_payplug.yaml b/install/Application/config/services_payplug.yaml
deleted file mode 100644
index 1d41d8b4..00000000
--- a/install/Application/config/services_payplug.yaml
+++ /dev/null
@@ -1,30 +0,0 @@
-parameters:
- sylius_refund.supported_gateways:
- - payplug
- - payplug_oney
- - cash_on_delivery
-
-services:
- payplug_sylius_payplug_plugin.api_client.payplug:
- class: Tests\PayPlug\SyliusPayPlugPlugin\Behat\Mocker\PayPlugApiClient
- public: true
- arguments:
- - "@service_container"
- - 'payplug_sylius_payplug_plugin.api_client.payplug'
-
- payplug_sylius_payplug_plugin.api_client.oney:
- class: Tests\PayPlug\SyliusPayPlugPlugin\Behat\Mocker\PayPlugApiClient
- public: true
- arguments:
- - "@service_container"
- - 'payplug_sylius_payplug_plugin.api_client.oney'
-
- Tests\Sylius\RefundPlugin\Behat\Services\Generator\FailedCreditMemoGenerator:
- decorates: 'Sylius\RefundPlugin\Generator\CreditMemoGeneratorInterface'
- arguments:
- - '@Tests\Sylius\RefundPlugin\Behat\Services\Generator\FailedCreditMemoGenerator.inner'
-
- Tests\Sylius\RefundPlugin\Behat\Services\Factory\FailedRefundPaymentFactory:
- decorates: 'sylius_refund.factory.refund_payment'
- arguments:
- - '@Tests\Sylius\RefundPlugin\Behat\Services\Factory\FailedRefundPaymentFactory.inner'
diff --git a/install/Application/src/Entity/Customer/Customer.php b/install/Application/src/Entity/Customer/Customer.php
deleted file mode 100644
index 805fdd5d..00000000
--- a/install/Application/src/Entity/Customer/Customer.php
+++ /dev/null
@@ -1,25 +0,0 @@
-cards = new ArrayCollection();
- }
-}
diff --git a/src/Migrations/Version20210410143918.php b/migrations/Version20210410143918.php
similarity index 100%
rename from src/Migrations/Version20210410143918.php
rename to migrations/Version20210410143918.php
diff --git a/src/Migrations/Version20210823123914.php b/migrations/Version20210823123914.php
similarity index 100%
rename from src/Migrations/Version20210823123914.php
rename to migrations/Version20210823123914.php
diff --git a/phpspec.yml.dist b/phpspec.yml.dist
deleted file mode 100644
index cdbf5061..00000000
--- a/phpspec.yml.dist
+++ /dev/null
@@ -1,4 +0,0 @@
-suites:
- main:
- namespace: PayPlug\SyliusPayPlugPlugin
- psr4_prefix: PayPlug\SyliusPayPlugPlugin
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
index 8e5a0339..b01264ac 100644
--- a/phpunit.xml.dist
+++ b/phpunit.xml.dist
@@ -1,19 +1,18 @@
+ bootstrap="vendor/sylius/test-application/config/bootstrap.php">
- vendor/payplug/sylius-payplug-plugin/tests/PHPUnit
+ tests/PHPUnit
-
-
+
diff --git a/public/assets/american-express/logo.svg b/public/assets/american-express/logo.svg
new file mode 100644
index 00000000..b6dfc5b8
--- /dev/null
+++ b/public/assets/american-express/logo.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/public/assets/apple-pay/logo.svg b/public/assets/apple-pay/logo.svg
new file mode 100644
index 00000000..5a86310e
--- /dev/null
+++ b/public/assets/apple-pay/logo.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/public/assets/bancontact/logo.svg b/public/assets/bancontact/logo.svg
new file mode 100644
index 00000000..1ab4d0f9
--- /dev/null
+++ b/public/assets/bancontact/logo.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/Resources/public/assets/oney/3x.svg b/public/assets/oney/3x.svg
similarity index 100%
rename from src/Resources/public/assets/oney/3x.svg
rename to public/assets/oney/3x.svg
diff --git a/src/Resources/public/assets/oney/3x4x-alt.svg b/public/assets/oney/3x4x-alt.svg
similarity index 100%
rename from src/Resources/public/assets/oney/3x4x-alt.svg
rename to public/assets/oney/3x4x-alt.svg
diff --git a/public/assets/oney/3x4x-without-fees-alt.svg b/public/assets/oney/3x4x-without-fees-alt.svg
new file mode 100644
index 00000000..5c783bbb
--- /dev/null
+++ b/public/assets/oney/3x4x-without-fees-alt.svg
@@ -0,0 +1,8 @@
+
diff --git a/public/assets/oney/3x4x-without-fees.svg b/public/assets/oney/3x4x-without-fees.svg
new file mode 100644
index 00000000..a029d51b
--- /dev/null
+++ b/public/assets/oney/3x4x-without-fees.svg
@@ -0,0 +1,8 @@
+
diff --git a/src/Resources/public/assets/oney/3x4x.svg b/public/assets/oney/3x4x.svg
similarity index 100%
rename from src/Resources/public/assets/oney/3x4x.svg
rename to public/assets/oney/3x4x.svg
diff --git a/src/Resources/public/assets/oney/4x.svg b/public/assets/oney/4x.svg
similarity index 100%
rename from src/Resources/public/assets/oney/4x.svg
rename to public/assets/oney/4x.svg
diff --git a/src/Resources/public/assets/oney/logo.svg b/public/assets/oney/logo.svg
similarity index 100%
rename from src/Resources/public/assets/oney/logo.svg
rename to public/assets/oney/logo.svg
diff --git a/public/assets/oney/oney_x3_with_fees.svg b/public/assets/oney/oney_x3_with_fees.svg
new file mode 100644
index 00000000..ecd9c79d
--- /dev/null
+++ b/public/assets/oney/oney_x3_with_fees.svg
@@ -0,0 +1,10 @@
+
diff --git a/public/assets/oney/oney_x3_without_fees.svg b/public/assets/oney/oney_x3_without_fees.svg
new file mode 100644
index 00000000..20c5dbc1
--- /dev/null
+++ b/public/assets/oney/oney_x3_without_fees.svg
@@ -0,0 +1,23 @@
+
diff --git a/public/assets/oney/oney_x4_with_fees.svg b/public/assets/oney/oney_x4_with_fees.svg
new file mode 100644
index 00000000..7ec13955
--- /dev/null
+++ b/public/assets/oney/oney_x4_with_fees.svg
@@ -0,0 +1,10 @@
+
diff --git a/public/assets/oney/oney_x4_without_fees.svg b/public/assets/oney/oney_x4_without_fees.svg
new file mode 100644
index 00000000..362e95ee
--- /dev/null
+++ b/public/assets/oney/oney_x4_without_fees.svg
@@ -0,0 +1,23 @@
+
diff --git a/public/assets/shop/images/integrated/lock.svg b/public/assets/shop/images/integrated/lock.svg
new file mode 100644
index 00000000..316e1f5e
--- /dev/null
+++ b/public/assets/shop/images/integrated/lock.svg
@@ -0,0 +1,9 @@
+
+
\ No newline at end of file
diff --git a/public/assets/shop/images/integrated/logo-payplug.png b/public/assets/shop/images/integrated/logo-payplug.png
new file mode 100644
index 00000000..b67d7cce
Binary files /dev/null and b/public/assets/shop/images/integrated/logo-payplug.png differ
diff --git a/rulesets/.php_md.xml b/ruleset/.php_md.xml
similarity index 100%
rename from rulesets/.php_md.xml
rename to ruleset/.php_md.xml
diff --git a/ruleset/ecs.php b/ruleset/ecs.php
new file mode 100644
index 00000000..7de9449a
--- /dev/null
+++ b/ruleset/ecs.php
@@ -0,0 +1,19 @@
+import(dirname(__DIR__) . '/vendor/sylius-labs/coding-standard/ecs.php');
+
+ $ecsConfig->paths([
+ dirname(__DIR__, 1) . '/src',
+ dirname(__DIR__, 1) . '/tests/Behat',
+ dirname(__DIR__, 1) . '/tests/PHPUnit',
+ ]);
+
+ /** @phpstan-ignore-next-line */
+ $ecsConfig->rule(\SlevomatCodingStandard\Sniffs\Classes\RequireMultiLineMethodSignatureSniff::class);
+ $ecsConfig->skip([\PhpCsFixer\Fixer\Basic\BracesFixer::class]);
+};
diff --git a/ruleset/phpstan-baseline.neon b/ruleset/phpstan-baseline.neon
new file mode 100644
index 00000000..1d16fed3
--- /dev/null
+++ b/ruleset/phpstan-baseline.neon
@@ -0,0 +1,1463 @@
+parameters:
+ ignoreErrors:
+ -
+ message: '#^Cannot call method add\(\) on mixed\.$#'
+ identifier: method.nonObject
+ count: 2
+ path: ../src/Action/Admin/CompleteRefundPaymentAction.php
+
+ -
+ message: '#^Parameter \#1 \$id of class Sylius\\RefundPlugin\\Event\\RefundPaymentGenerated constructor expects int, mixed given\.$#'
+ identifier: argument.type
+ count: 1
+ path: ../src/Action/Admin/CompleteRefundPaymentAction.php
+
+ -
+ message: '#^Parameter \#5 \$paymentMethodId of class Sylius\\RefundPlugin\\Event\\RefundPaymentGenerated constructor expects int, mixed given\.$#'
+ identifier: argument.type
+ count: 1
+ path: ../src/Action/Admin/CompleteRefundPaymentAction.php
+
+ -
+ message: '#^Cannot call method add\(\) on mixed\.$#'
+ identifier: method.nonObject
+ count: 3
+ path: ../src/Action/Admin/RefundUnitsAction.php
+
+ -
+ message: '#^Cannot access offset ''billing'' on mixed\.$#'
+ identifier: offsetAccess.nonOffsetAccessible
+ count: 2
+ path: ../src/Action/CaptureAction.php
+
+ -
+ message: '#^Cannot access offset ''can_save_cards'' on mixed\.$#'
+ identifier: offsetAccess.nonOffsetAccessible
+ count: 1
+ path: ../src/Action/CaptureAction.php
+
+ -
+ message: '#^Cannot access offset ''cancel_url'' on mixed\.$#'
+ identifier: offsetAccess.nonOffsetAccessible
+ count: 2
+ path: ../src/Action/CaptureAction.php
+
+ -
+ message: '#^Cannot access offset ''postcode'' on mixed\.$#'
+ identifier: offsetAccess.nonOffsetAccessible
+ count: 6
+ path: ../src/Action/CaptureAction.php
+
+ -
+ message: '#^Cannot access offset ''shipping'' on mixed\.$#'
+ identifier: offsetAccess.nonOffsetAccessible
+ count: 2
+ path: ../src/Action/CaptureAction.php
+
+ -
+ message: '#^Cannot access property \$payment_url on mixed\.$#'
+ identifier: property.nonObject
+ count: 2
+ path: ../src/Action/CaptureAction.php
+
+ -
+ message: '#^Cannot call method add\(\) on mixed\.$#'
+ identifier: method.nonObject
+ count: 6
+ path: ../src/Action/CaptureAction.php
+
+ -
+ message: '#^Cannot call method getFirstModel\(\) on mixed\.$#'
+ identifier: method.nonObject
+ count: 1
+ path: ../src/Action/CaptureAction.php
+
+ -
+ message: '#^Cannot call method getModel\(\) on mixed\.$#'
+ identifier: method.nonObject
+ count: 1
+ path: ../src/Action/CaptureAction.php
+
+ -
+ message: '#^Cannot call method getToken\(\) on mixed\.$#'
+ identifier: method.nonObject
+ count: 1
+ path: ../src/Action/CaptureAction.php
+
+ -
+ message: '#^Parameter \#1 \$timestamp of method DateTimeImmutable\:\:setTimestamp\(\) expects int, mixed given\.$#'
+ identifier: argument.type
+ count: 1
+ path: ../src/Action/CaptureAction.php
+
+ -
+ message: '#^Parameter \#1 \$url of class Payum\\Core\\Reply\\HttpRedirect constructor expects string, mixed given\.$#'
+ identifier: argument.type
+ count: 3
+ path: ../src/Action/CaptureAction.php
+
+ -
+ message: '#^Cannot call method getSource\(\) on mixed\.$#'
+ identifier: method.nonObject
+ count: 1
+ path: ../src/Action/ConvertPaymentAction.php
+
+ -
+ message: '#^Cannot call method setResult\(\) on mixed\.$#'
+ identifier: method.nonObject
+ count: 1
+ path: ../src/Action/ConvertPaymentAction.php
+
+ -
+ message: '#^Cannot call method getFirstModel\(\) on mixed\.$#'
+ identifier: method.nonObject
+ count: 2
+ path: ../src/Action/NotifyAction.php
+
+ -
+ message: '#^Cannot call method getModel\(\) on mixed\.$#'
+ identifier: method.nonObject
+ count: 1
+ path: ../src/Action/NotifyAction.php
+
+ -
+ message: '#^Parameter \#1 \$payment of method PayPlug\\SyliusPayPlugPlugin\\Handler\\PaymentNotificationHandler\:\:treat\(\) expects Sylius\\Component\\Core\\Model\\PaymentInterface, mixed given\.$#'
+ identifier: argument.type
+ count: 1
+ path: ../src/Action/NotifyAction.php
+
+ -
+ message: '#^Parameter \#1 \$payment of method PayPlug\\SyliusPayPlugPlugin\\Handler\\RefundNotificationHandler\:\:treat\(\) expects Sylius\\Component\\Core\\Model\\PaymentInterface, mixed given\.$#'
+ identifier: argument.type
+ count: 1
+ path: ../src/Action/NotifyAction.php
+
+ -
+ message: '#^Access to an undefined property PayPlug\\SyliusPayPlugPlugin\\Action\\StatusAction\:\:\$stateMachineFactory\.$#'
+ identifier: property.notFound
+ count: 2
+ path: ../src/Action/StatusAction.php
+
+ -
+ message: '#^Cannot call method add\(\) on mixed\.$#'
+ identifier: method.nonObject
+ count: 1
+ path: ../src/Action/StatusAction.php
+
+ -
+ message: '#^Cannot call method apply\(\) on mixed\.$#'
+ identifier: method.nonObject
+ count: 1
+ path: ../src/Action/StatusAction.php
+
+ -
+ message: '#^Cannot call method can\(\) on mixed\.$#'
+ identifier: method.nonObject
+ count: 1
+ path: ../src/Action/StatusAction.php
+
+ -
+ message: '#^Cannot call method get\(\) on mixed\.$#'
+ identifier: method.nonObject
+ count: 2
+ path: ../src/Action/StatusAction.php
+
+ -
+ message: '#^Cannot call method getFirstModel\(\) on mixed\.$#'
+ identifier: method.nonObject
+ count: 3
+ path: ../src/Action/StatusAction.php
+
+ -
+ message: '#^Cannot call method getModel\(\) on mixed\.$#'
+ identifier: method.nonObject
+ count: 2
+ path: ../src/Action/StatusAction.php
+
+ -
+ message: '#^Cannot call method markNew\(\) on mixed\.$#'
+ identifier: method.nonObject
+ count: 1
+ path: ../src/Action/StatusAction.php
+
+ -
+ message: '#^Parameter \#1 \$payment of method PayPlug\\SyliusPayPlugPlugin\\Handler\\PaymentNotificationHandler\:\:treat\(\) expects Sylius\\Component\\Core\\Model\\PaymentInterface, mixed given\.$#'
+ identifier: argument.type
+ count: 3
+ path: ../src/Action/StatusAction.php
+
+ -
+ message: '#^Parameter \#1 \$payment of method PayPlug\\SyliusPayPlugPlugin\\PaymentProcessing\\RefundPaymentHandlerInterface\:\:updatePaymentStatus\(\) expects Sylius\\Component\\Core\\Model\\PaymentInterface, mixed given\.$#'
+ identifier: argument.type
+ count: 1
+ path: ../src/Action/StatusAction.php
+
+ -
+ message: '#^Parameter \#1 \$paymentId of method PayPlug\\SyliusPayPlugPlugin\\ApiClient\\PayPlugApiClientInterface\:\:retrieve\(\) expects string, mixed given\.$#'
+ identifier: argument.type
+ count: 3
+ path: ../src/Action/StatusAction.php
+
+ -
+ message: '#^Parameter \#1 \$status of method PayPlug\\SyliusPayPlugPlugin\\Action\\StatusAction\:\:markRequestAs\(\) expects string, mixed given\.$#'
+ identifier: argument.type
+ count: 1
+ path: ../src/Action/StatusAction.php
+
+ -
+ message: '#^Parameter \#2 \$request of method PayPlug\\SyliusPayPlugPlugin\\Action\\StatusAction\:\:markRequestAs\(\) expects Payum\\Core\\Request\\GetStatusInterface, mixed given\.$#'
+ identifier: argument.type
+ count: 1
+ path: ../src/Action/StatusAction.php
+
+ -
+ message: '#^Access to constant VERSION on an unknown class Sylius\\Bundle\\CoreBundle\\Application\\Kernel\.$#'
+ identifier: class.notFound
+ count: 2
+ path: ../src/ApiClient/PayPlugApiClient.php
+
+ -
+ message: '#^Anonymous function should return array but returns mixed\.$#'
+ identifier: return.type
+ count: 1
+ path: ../src/ApiClient/PayPlugApiClient.php
+
+ -
+ message: '#^Binary operation "\." between ''Sylius/'' and mixed results in an error\.$#'
+ identifier: binaryOp.invalid
+ count: 2
+ path: ../src/ApiClient/PayPlugApiClient.php
+
+ -
+ message: '#^PHPDoc tag @var with type Payplug\\Resource\\Refund\|null is not subtype of type Payplug\\Refund\|null\.$#'
+ identifier: varTag.type
+ count: 2
+ path: ../src/ApiClient/PayPlugApiClient.php
+
+ -
+ message: '#^PHPDoc tag @var with type Payum\\Core\\Model\\GatewayConfig\|null is not subtype of type Sylius\\Resource\\Model\\ResourceInterface\|null\.$#'
+ identifier: varTag.type
+ count: 1
+ path: ../src/ApiClient/PayPlugApiClientFactory.php
+
+ -
+ message: '#^Parameter \#1 \$secretKey of class PayPlug\\SyliusPayPlugPlugin\\ApiClient\\PayPlugApiClient constructor expects string, mixed given\.$#'
+ identifier: argument.type
+ count: 2
+ path: ../src/ApiClient/PayPlugApiClientFactory.php
+
+ -
+ message: '#^Argument of an invalid type mixed supplied for foreach, only iterables are supported\.$#'
+ identifier: foreach.nonIterable
+ count: 1
+ path: ../src/Checker/CanSavePayplugPaymentMethodChecker.php
+
+ -
+ message: '#^Cannot access offset ''enabled'' on mixed\.$#'
+ identifier: offsetAccess.nonOffsetAccessible
+ count: 1
+ path: ../src/Checker/CanSavePayplugPaymentMethodChecker.php
+
+ -
+ message: '#^Cannot call method getHostname\(\) on mixed\.$#'
+ identifier: method.nonObject
+ count: 1
+ path: ../src/Checker/CanSavePayplugPaymentMethodChecker.php
+
+ -
+ message: '#^Method PayPlug\\SyliusPayPlugPlugin\\Checker\\CanSavePayplugPaymentMethodChecker\:\:isEnabled\(\) should return bool but returns mixed\.$#'
+ identifier: return.type
+ count: 1
+ path: ../src/Checker/CanSavePayplugPaymentMethodChecker.php
+
+ -
+ message: '#^Parameter \#1 \$method of method PayPlug\\SyliusPayPlugPlugin\\Checker\\CanSavePayplugPaymentMethodChecker\:\:isAllowedDomainNames\(\) expects array, mixed given\.$#'
+ identifier: argument.type
+ count: 1
+ path: ../src/Checker/CanSavePayplugPaymentMethodChecker.php
+
+ -
+ message: '#^Parameter \#2 \$haystack of function in_array expects array, mixed given\.$#'
+ identifier: argument.type
+ count: 1
+ path: ../src/Checker/CanSavePayplugPaymentMethodChecker.php
+
+ -
+ message: '#^Cannot access offset ''allowed_countries'' on mixed\.$#'
+ identifier: offsetAccess.nonOffsetAccessible
+ count: 1
+ path: ../src/Checker/OneyChecker.php
+
+ -
+ message: '#^Cannot access offset ''max_amounts'' on mixed\.$#'
+ identifier: offsetAccess.nonOffsetAccessible
+ count: 1
+ path: ../src/Checker/OneyChecker.php
+
+ -
+ message: '#^Cannot access offset ''min_amounts'' on mixed\.$#'
+ identifier: offsetAccess.nonOffsetAccessible
+ count: 1
+ path: ../src/Checker/OneyChecker.php
+
+ -
+ message: '#^Cannot access offset ''oney'' on mixed\.$#'
+ identifier: offsetAccess.nonOffsetAccessible
+ count: 3
+ path: ../src/Checker/OneyChecker.php
+
+ -
+ message: '#^Cannot access offset string on mixed\.$#'
+ identifier: offsetAccess.nonOffsetAccessible
+ count: 2
+ path: ../src/Checker/OneyChecker.php
+
+ -
+ message: '#^Parameter \#2 \$haystack of function in_array expects array, mixed given\.$#'
+ identifier: argument.type
+ count: 1
+ path: ../src/Checker/OneyChecker.php
+
+ -
+ message: '#^Cannot call method apply\(\) on mixed\.$#'
+ identifier: method.nonObject
+ count: 1
+ path: ../src/Command/CaptureAuthorizedPaymentCommand.php
+
+ -
+ message: '#^Cannot call method get\(\) on mixed\.$#'
+ identifier: method.nonObject
+ count: 1
+ path: ../src/Command/CaptureAuthorizedPaymentCommand.php
+
+ -
+ message: '#^Parameter \#2 \.\.\.\$values of function sprintf expects bool\|float\|int\|string\|null, mixed given\.$#'
+ identifier: argument.type
+ count: 1
+ path: ../src/Command/CaptureAuthorizedPaymentCommand.php
+
+ -
+ message: '#^Property PayPlug\\SyliusPayPlugPlugin\\Command\\CaptureAuthorizedPaymentCommand\:\:\$stateMachineFactory has no type specified\.$#'
+ identifier: missingType.property
+ count: 1
+ path: ../src/Command/CaptureAuthorizedPaymentCommand.php
+
+ -
+ message: '#^Cannot call method add\(\) on mixed\.$#'
+ identifier: method.nonObject
+ count: 2
+ path: ../src/Controller/CardController.php
+
+ -
+ message: '#^Parameter \#2 \$type of method Symfony\\Component\\Form\\FormBuilderInterface\:\:add\(\) expects string\|null, mixed given\.$#'
+ identifier: argument.type
+ count: 1
+ path: ../src/Controller/CompleteInfoController.php
+
+ -
+ message: '#^PHPDoc tag @param for parameter \$paymentMethodRepository contains generic type Sylius\\Component\\Resource\\Repository\\RepositoryInterface\ but interface Sylius\\Component\\Resource\\Repository\\RepositoryInterface is not generic\.$#'
+ identifier: generics.notGeneric
+ count: 1
+ path: ../src/Controller/IntegratedPaymentController.php
+
+ -
+ message: '#^PHPDoc type for property PayPlug\\SyliusPayPlugPlugin\\Controller\\IntegratedPaymentController\:\:\$paymentMethodRepository contains generic type Sylius\\Component\\Resource\\Repository\\RepositoryInterface\ but interface Sylius\\Component\\Resource\\Repository\\RepositoryInterface is not generic\.$#'
+ identifier: generics.notGeneric
+ count: 1
+ path: ../src/Controller/IntegratedPaymentController.php
+
+ -
+ message: '#^Call to function is_string\(\) with string will always evaluate to true\.$#'
+ identifier: function.alreadyNarrowedType
+ count: 1
+ path: ../src/Controller/IpnAction.php
+
+ -
+ message: '#^Parameter \#1 \$payplugPaymentId of method PayPlug\\SyliusPayPlugPlugin\\Repository\\PaymentRepositoryInterface\:\:findOneByPayPlugPaymentId\(\) expects string, mixed given\.$#'
+ identifier: argument.type
+ count: 1
+ path: ../src/Controller/IpnAction.php
+
+ -
+ message: '#^Parameter \#1 \$secretKey of method PayPlug\\SyliusPayPlugPlugin\\ApiClient\\PayPlugApiClientInterface\:\:initialise\(\) expects string, mixed given\.$#'
+ identifier: argument.type
+ count: 1
+ path: ../src/Controller/IpnAction.php
+
+ -
+ message: '#^Parameter \#2 \$key of method PayPlug\\SyliusPayPlugPlugin\\ApiClient\\PayPlugApiClientFactoryInterface\:\:create\(\) expects string\|null, mixed given\.$#'
+ identifier: argument.type
+ count: 1
+ path: ../src/Controller/IpnAction.php
+
+ -
+ message: '#^Cannot access offset ''payment_url'' on mixed\.$#'
+ identifier: offsetAccess.nonOffsetAccessible
+ count: 1
+ path: ../src/Controller/OneClickAction.php
+
+ -
+ message: '#^Cannot access offset ''return_url'' on mixed\.$#'
+ identifier: offsetAccess.nonOffsetAccessible
+ count: 1
+ path: ../src/Controller/OneClickAction.php
+
+ -
+ message: '#^Cannot cast mixed to string\.$#'
+ identifier: cast.string
+ count: 1
+ path: ../src/Controller/OneClickAction.php
+
+ -
+ message: '#^PHPDoc tag @var with type Payum\\Core\\Model\\GatewayConfigInterface is not subtype of type Sylius\\Component\\Payment\\Model\\GatewayConfigInterface\|null\.$#'
+ identifier: varTag.type
+ count: 1
+ path: ../src/Controller/OneClickAction.php
+
+ -
+ message: '#^Parameter \#1 \$url of class Symfony\\Component\\HttpFoundation\\RedirectResponse constructor expects string, mixed given\.$#'
+ identifier: argument.type
+ count: 1
+ path: ../src/Controller/OneClickAction.php
+
+ -
+ message: '#^Parameter \#2 \$key of method PayPlug\\SyliusPayPlugPlugin\\ApiClient\\PayPlugApiClientFactory\:\:create\(\) expects string\|null, mixed given\.$#'
+ identifier: argument.type
+ count: 1
+ path: ../src/Controller/OneClickAction.php
+
+ -
+ message: '#^Cannot cast mixed to int\.$#'
+ identifier: cast.int
+ count: 1
+ path: ../src/Controller/OneyIsProductEligible.php
+
+ -
+ message: '#^PHPDoc tag @var with type int\|null is not subtype of native type int\.$#'
+ identifier: varTag.nativeType
+ count: 1
+ path: ../src/Controller/OneyIsProductEligible.php
+
+ -
+ message: '#^Parameter \#2 \$productVariantCode of method PayPlug\\SyliusPayPlugPlugin\\Controller\\OneyIsProductEligible\:\:isProductEligible\(\) expects string, mixed given\.$#'
+ identifier: argument.type
+ count: 1
+ path: ../src/Controller/OneyIsProductEligible.php
+
+ -
+ message: '#^Cannot cast mixed to int\.$#'
+ identifier: cast.int
+ count: 1
+ path: ../src/Controller/OneySimulationPopin.php
+
+ -
+ message: '#^PHPDoc tag @var with type int\|null is not subtype of native type int\.$#'
+ identifier: varTag.nativeType
+ count: 1
+ path: ../src/Controller/OneySimulationPopin.php
+
+ -
+ message: '#^Binary operation "\." between ''apple_pay_cancel'' and mixed results in an error\.$#'
+ identifier: binaryOp.invalid
+ count: 1
+ path: ../src/Controller/OrderController.php
+
+ # Session Specific
+ -
+ message: '#^Cannot call method add\(\) on mixed\.$#'
+ identifier: method.nonObject
+ count: 4
+ path: ../src/Controller/OrderController.php
+ -
+ message: '#^Cannot call method add\(\) on mixed\.$#'
+ identifier: method.nonObject
+ count: 2
+ path: ../src/Action/Admin/Auth/UnifiedAuthenticationController.php
+ -
+ message: '#^Cannot call method add\(\) on mixed\.$#'
+ identifier: method.nonObject
+ count: 2
+ path: ../src/Validator/PaymentMethodValidator.php
+ -
+ message: '#^Cannot call method add\(\) on mixed\.$#'
+ identifier: method.nonObject
+ count: 1
+ path: ../src/EventListener/PostSavePaymentMethodEventListener.php
+
+ -
+ message: '#^Cannot call method apply\(\) on mixed\.$#'
+ identifier: method.nonObject
+ count: 3
+ path: ../src/Controller/OrderController.php
+
+ -
+ message: '#^Cannot call method can\(\) on mixed\.$#'
+ identifier: method.nonObject
+ count: 3
+ path: ../src/Controller/OrderController.php
+
+ -
+ message: '#^Cannot call method get\(\) on mixed\.$#'
+ identifier: method.nonObject
+ count: 3
+ path: ../src/Controller/OrderController.php
+
+ -
+ message: '#^Property PayPlug\\SyliusPayPlugPlugin\\Controller\\OrderController\:\:\$stateMachineFactory has no type specified\.$#'
+ identifier: missingType.property
+ count: 1
+ path: ../src/Controller/OrderController.php
+
+ -
+ message: '#^Cannot access offset ''company_name'' on mixed\.$#'
+ identifier: offsetAccess.nonOffsetAccessible
+ count: 2
+ path: ../src/Creator/PayPlugPaymentDataCreator.php
+
+ -
+ message: '#^Cannot access offset ''first_name'' on mixed\.$#'
+ identifier: offsetAccess.nonOffsetAccessible
+ count: 2
+ path: ../src/Creator/PayPlugPaymentDataCreator.php
+
+ -
+ message: '#^Cannot access offset ''last_name'' on mixed\.$#'
+ identifier: offsetAccess.nonOffsetAccessible
+ count: 2
+ path: ../src/Creator/PayPlugPaymentDataCreator.php
+
+ -
+ message: '#^PHPDoc tag @SuppressWarnings has invalid value \(\(PHPMD\.UnusedFormalParameter\)\)\: Unexpected token "\.UnusedFormalParameter\)", expected ''\)'' at offset 34 on line 2$#'
+ identifier: phpDoc.parseError
+ count: 1
+ path: ../src/Creator/PayPlugPaymentDataCreator.php
+
+ -
+ message: '#^Parameter \#2 \.\.\.\$values of function sprintf expects bool\|float\|int\|string\|null, mixed given\.$#'
+ identifier: argument.type
+ count: 2
+ path: ../src/Creator/PayPlugPaymentDataCreator.php
+
+ -
+ message: '#^Parameter \#3 \.\.\.\$values of function sprintf expects bool\|float\|int\|string\|null, mixed given\.$#'
+ identifier: argument.type
+ count: 2
+ path: ../src/Creator/PayPlugPaymentDataCreator.php
+
+ -
+ message: '#^Parameter &\$landingPhone by\-ref type of method PayPlug\\SyliusPayPlugPlugin\\Creator\\PayPlugPaymentDataCreator\:\:loadPhoneNumbers\(\) expects string\|null, mixed given\.$#'
+ identifier: parameterByRef.type
+ count: 1
+ path: ../src/Creator/PayPlugPaymentDataCreator.php
+
+ -
+ message: '#^Parameter &\$mobilePhone by\-ref type of method PayPlug\\SyliusPayPlugPlugin\\Creator\\PayPlugPaymentDataCreator\:\:loadPhoneNumbers\(\) expects string\|null, mixed given\.$#'
+ identifier: parameterByRef.type
+ count: 1
+ path: ../src/Creator/PayPlugPaymentDataCreator.php
+
+ -
+ message: '#^Right side of && is always true\.$#'
+ identifier: booleanAnd.rightAlwaysTrue
+ count: 1
+ path: ../src/Creator/PayPlugPaymentDataCreator.php
+
+ -
+ message: '#^PHPDoc tag @var with type Payum\\Core\\Model\\GatewayConfigInterface is not subtype of type Sylius\\Component\\Payment\\Model\\GatewayConfigInterface\|null\.$#'
+ identifier: varTag.type
+ count: 1
+ path: ../src/Creator/RefundUnitsCommandCreatorDecorator.php
+
+ -
+ message: '#^Parameter \#1 \$paymentId of method PayPlug\\SyliusPayPlugPlugin\\ApiClient\\PayPlugApiClientInterface\:\:retrieve\(\) expects string, mixed given\.$#'
+ identifier: argument.type
+ count: 1
+ path: ../src/Creator/RefundUnitsCommandCreatorDecorator.php
+
+ -
+ message: '#^Parameter \#1 \$unit of method PayPlug\\SyliusPayPlugPlugin\\Creator\\RefundUnitsCommandCreatorDecorator\:\:getAmount\(\) expects Sylius\\RefundPlugin\\Model\\UnitRefundInterface, mixed given\.$#'
+ identifier: argument.type
+ count: 1
+ path: ../src/Creator/RefundUnitsCommandCreatorDecorator.php
+
+ -
+ message: '#^Parameter \#1 \$units of method Sylius\\RefundPlugin\\Converter\\RefundUnitsConverterInterface\:\:convert\(\) expects array, mixed given\.$#'
+ identifier: argument.type
+ count: 2
+ path: ../src/Creator/RefundUnitsCommandCreatorDecorator.php
+
+ -
+ message: '#^Property PayPlug\\SyliusPayPlugPlugin\\Entity\\RefundHistory\:\:\$value type mapping mismatch\: database can contain int\|null but property expects int\.$#'
+ identifier: doctrine.columnType
+ count: 1
+ path: ../src/Entity/RefundHistory.php
+
+ -
+ message: '#^Trait PayPlug\\SyliusPayPlugPlugin\\Entity\\Traits\\CustomerTrait is used zero times and is not analysed\.$#'
+ identifier: trait.unused
+ count: 1
+ path: ../src/Entity/Traits/CustomerTrait.php
+
+ -
+ message: '#^Trait PayPlug\\SyliusPayPlugPlugin\\Entity\\Traits\\PaymentMethodTrait is used zero times and is not analysed\.$#'
+ identifier: trait.unused
+ count: 1
+ path: ../src/Entity/Traits/PaymentMethodTrait.php
+
+ -
+ message: '#^Trait PayPlug\\SyliusPayPlugPlugin\\Entity\\Traits\\PaymentTrait is used zero times and is not analysed\.$#'
+ identifier: trait.unused
+ count: 1
+ path: ../src/Entity/Traits/PaymentTrait.php
+
+ -
+ message: '#^Cannot call method add\(\) on mixed\.$#'
+ identifier: method.nonObject
+ count: 1
+ path: ../src/EventSubscriber/DisplayOneyGatewayFormEventSubscriber.php
+
+ -
+ message: '#^Cannot call method apply\(\) on mixed\.$#'
+ identifier: method.nonObject
+ count: 1
+ path: ../src/EventSubscriber/PostPaymentSelectEventSubscriber.php
+
+ -
+ message: '#^Cannot call method can\(\) on mixed\.$#'
+ identifier: method.nonObject
+ count: 1
+ path: ../src/EventSubscriber/PostPaymentSelectEventSubscriber.php
+
+ -
+ message: '#^Cannot call method get\(\) on mixed\.$#'
+ identifier: method.nonObject
+ count: 1
+ path: ../src/EventSubscriber/PostPaymentSelectEventSubscriber.php
+
+ -
+ message: '#^Property PayPlug\\SyliusPayPlugPlugin\\EventSubscriber\\PostPaymentSelectEventSubscriber\:\:\$stateMachineFactory has no type specified\.$#'
+ identifier: missingType.property
+ count: 1
+ path: ../src/EventSubscriber/PostPaymentSelectEventSubscriber.php
+
+ -
+ message: '#^Parameter \#1 \$input of method Payum\\Core\\Bridge\\Spl\\ArrayObject\:\:defaults\(\) expects array\|Traversable, mixed given\.$#'
+ identifier: argument.type
+ count: 1
+ path: ../src/Gateway/AbstractGatewayFactory.php
+
+ -
+ message: '#^Parameter \#1 \$required of method Payum\\Core\\Bridge\\Spl\\ArrayObject\:\:validateNotEmpty\(\) expects array, mixed given\.$#'
+ identifier: argument.type
+ count: 1
+ path: ../src/Gateway/AbstractGatewayFactory.php
+
+ -
+ message: '#^Parameter \#1 \$secretKey of class PayPlug\\SyliusPayPlugPlugin\\ApiClient\\PayPlugApiClient constructor expects string, mixed given\.$#'
+ identifier: argument.type
+ count: 1
+ path: ../src/Gateway/AbstractGatewayFactory.php
+
+ -
+ message: '#^Parameter \#2 \$factoryName of class PayPlug\\SyliusPayPlugPlugin\\ApiClient\\PayPlugApiClient constructor expects string\|null, mixed given\.$#'
+ identifier: argument.type
+ count: 1
+ path: ../src/Gateway/AbstractGatewayFactory.php
+
+ -
+ message: '#^Cannot call method add\(\) on mixed\.$#'
+ identifier: method.nonObject
+ count: 1
+ path: ../src/Gateway/Form/Type/AbstractGatewayConfigurationType.php
+
+ -
+ message: '#^Cannot call method getId\(\) on mixed\.$#'
+ identifier: method.nonObject
+ count: 1
+ path: ../src/Gateway/Form/Type/AbstractGatewayConfigurationType.php
+
+ -
+ message: '#^Cannot access offset ''secretKey'' on mixed\.$#'
+ identifier: offsetAccess.nonOffsetAccessible
+ count: 1
+ path: ../src/Gateway/Validator/Constraints/IsCanSaveCardsValidator.php
+
+ -
+ message: '#^Cannot call method getConfig\(\) on mixed\.$#'
+ identifier: method.nonObject
+ count: 1
+ path: ../src/Gateway/Validator/Constraints/IsCanSaveCardsValidator.php
+
+ -
+ message: '#^Cannot call method getData\(\) on mixed\.$#'
+ identifier: method.nonObject
+ count: 1
+ path: ../src/Gateway/Validator/Constraints/IsCanSaveCardsValidator.php
+
+ -
+ message: '#^Cannot call method getGatewayConfig\(\) on mixed\.$#'
+ identifier: method.nonObject
+ count: 1
+ path: ../src/Gateway/Validator/Constraints/IsCanSaveCardsValidator.php
+
+ -
+ message: '#^Method PayPlug\\SyliusPayPlugPlugin\\Gateway\\Validator\\Constraints\\IsCanSaveCardsValidator\:\:validate\(\) has parameter \$value with no type specified\.$#'
+ identifier: missingType.parameter
+ count: 1
+ path: ../src/Gateway/Validator/Constraints/IsCanSaveCardsValidator.php
+
+ -
+ message: '#^Parameter \#2 \$key of method PayPlug\\SyliusPayPlugPlugin\\ApiClient\\PayPlugApiClientFactory\:\:create\(\) expects string\|null, mixed given\.$#'
+ identifier: argument.type
+ count: 1
+ path: ../src/Gateway/Validator/Constraints/IsCanSaveCardsValidator.php
+
+ -
+ message: '#^Cannot call method getChannels\(\) on mixed\.$#'
+ identifier: method.nonObject
+ count: 1
+ path: ../src/Gateway/Validator/Constraints/IsCanSavePaymentMethodValidator.php
+
+ -
+ message: '#^Cannot call method getData\(\) on mixed\.$#'
+ identifier: method.nonObject
+ count: 2
+ path: ../src/Gateway/Validator/Constraints/IsCanSavePaymentMethodValidator.php
+
+ -
+ message: '#^Cannot call method getFactoryName\(\) on mixed\.$#'
+ identifier: method.nonObject
+ count: 1
+ path: ../src/Gateway/Validator/Constraints/IsCanSavePaymentMethodValidator.php
+
+ -
+ message: '#^Cannot call method getGatewayConfig\(\) on mixed\.$#'
+ identifier: method.nonObject
+ count: 1
+ path: ../src/Gateway/Validator/Constraints/IsCanSavePaymentMethodValidator.php
+
+ -
+ message: '#^Method PayPlug\\SyliusPayPlugPlugin\\Gateway\\Validator\\Constraints\\IsCanSavePaymentMethodValidator\:\:validate\(\) has parameter \$value with no type specified\.$#'
+ identifier: missingType.parameter
+ count: 1
+ path: ../src/Gateway/Validator/Constraints/IsCanSavePaymentMethodValidator.php
+
+ -
+ message: '#^Parameter \#2 \$channels of method PayPlug\\SyliusPayPlugPlugin\\Checker\\CanSavePayplugPaymentMethodChecker\:\:isEnabled\(\) expects Doctrine\\Common\\Collections\\Collection, mixed given\.$#'
+ identifier: argument.type
+ count: 1
+ path: ../src/Gateway/Validator/Constraints/IsCanSavePaymentMethodValidator.php
+
+ -
+ message: '#^Method PayPlug\\SyliusPayPlugPlugin\\Gateway\\Validator\\Constraints\\IsOneyEnabledValidator\:\:validate\(\) has parameter \$value with no type specified\.$#'
+ identifier: missingType.parameter
+ count: 1
+ path: ../src/Gateway/Validator/Constraints/IsOneyEnabledValidator.php
+
+ -
+ message: '#^Method PayPlug\\SyliusPayPlugPlugin\\Gateway\\Validator\\Constraints\\IsPayPlugSecretKeyValidator\:\:validate\(\) has parameter \$value with no type specified\.$#'
+ identifier: missingType.parameter
+ count: 1
+ path: ../src/Gateway/Validator/Constraints/IsPayPlugSecretKeyValidator.php
+
+ -
+ message: '#^Cannot access offset ''secretKey'' on mixed\.$#'
+ identifier: offsetAccess.nonOffsetAccessible
+ count: 1
+ path: ../src/Gateway/Validator/Constraints/PayplugPermissionValidator.php
+
+ -
+ message: '#^Cannot call method getConfig\(\) on mixed\.$#'
+ identifier: method.nonObject
+ count: 1
+ path: ../src/Gateway/Validator/Constraints/PayplugPermissionValidator.php
+
+ -
+ message: '#^Cannot call method getData\(\) on mixed\.$#'
+ identifier: method.nonObject
+ count: 1
+ path: ../src/Gateway/Validator/Constraints/PayplugPermissionValidator.php
+
+ -
+ message: '#^Cannot call method getGatewayConfig\(\) on mixed\.$#'
+ identifier: method.nonObject
+ count: 1
+ path: ../src/Gateway/Validator/Constraints/PayplugPermissionValidator.php
+
+ -
+ message: '#^Parameter \#2 \$key of method PayPlug\\SyliusPayPlugPlugin\\ApiClient\\PayPlugApiClientFactory\:\:create\(\) expects string\|null, mixed given\.$#'
+ identifier: argument.type
+ count: 1
+ path: ../src/Gateway/Validator/Constraints/PayplugPermissionValidator.php
+
+ -
+ message: '#^Binary operation "\." between ''payment_'' and mixed results in an error\.$#'
+ identifier: binaryOp.invalid
+ count: 1
+ path: ../src/Handler/PaymentNotificationHandler.php
+
+ -
+ message: '#^Cannot access offset ''customer_id'' on mixed\.$#'
+ identifier: offsetAccess.nonOffsetAccessible
+ count: 1
+ path: ../src/Handler/PaymentNotificationHandler.php
+
+ -
+ message: '#^Cannot access property \$brand on mixed\.$#'
+ identifier: property.nonObject
+ count: 1
+ path: ../src/Handler/PaymentNotificationHandler.php
+
+ -
+ message: '#^Cannot access property \$code on mixed\.$#'
+ identifier: property.nonObject
+ count: 2
+ path: ../src/Handler/PaymentNotificationHandler.php
+
+ -
+ message: '#^Cannot access property \$country on mixed\.$#'
+ identifier: property.nonObject
+ count: 1
+ path: ../src/Handler/PaymentNotificationHandler.php
+
+ -
+ message: '#^Cannot access property \$exp_month on mixed\.$#'
+ identifier: property.nonObject
+ count: 1
+ path: ../src/Handler/PaymentNotificationHandler.php
+
+ -
+ message: '#^Cannot access property \$exp_year on mixed\.$#'
+ identifier: property.nonObject
+ count: 1
+ path: ../src/Handler/PaymentNotificationHandler.php
+
+ -
+ message: '#^Cannot access property \$id on mixed\.$#'
+ identifier: property.nonObject
+ count: 4
+ path: ../src/Handler/PaymentNotificationHandler.php
+
+ -
+ message: '#^Cannot access property \$last4 on mixed\.$#'
+ identifier: property.nonObject
+ count: 1
+ path: ../src/Handler/PaymentNotificationHandler.php
+
+ -
+ message: '#^Cannot access property \$message on mixed\.$#'
+ identifier: property.nonObject
+ count: 2
+ path: ../src/Handler/PaymentNotificationHandler.php
+
+ -
+ message: '#^Cannot call method add\(\) on mixed\.$#'
+ identifier: method.nonObject
+ count: 2
+ path: ../src/Handler/PaymentNotificationHandler.php
+
+ -
+ message: '#^Parameter \#1 \$brand of method PayPlug\\SyliusPayPlugPlugin\\Entity\\Card\:\:setBrand\(\) expects string, mixed given\.$#'
+ identifier: argument.type
+ count: 1
+ path: ../src/Handler/PaymentNotificationHandler.php
+
+ -
+ message: '#^Parameter \#1 \$countryCode of method PayPlug\\SyliusPayPlugPlugin\\Entity\\Card\:\:setCountryCode\(\) expects string, mixed given\.$#'
+ identifier: argument.type
+ count: 1
+ path: ../src/Handler/PaymentNotificationHandler.php
+
+ -
+ message: '#^Parameter \#1 \$expirationMonth of method PayPlug\\SyliusPayPlugPlugin\\Entity\\Card\:\:setExpirationMonth\(\) expects int, mixed given\.$#'
+ identifier: argument.type
+ count: 1
+ path: ../src/Handler/PaymentNotificationHandler.php
+
+ -
+ message: '#^Parameter \#1 \$expirationYear of method PayPlug\\SyliusPayPlugPlugin\\Entity\\Card\:\:setExpirationYear\(\) expects int, mixed given\.$#'
+ identifier: argument.type
+ count: 1
+ path: ../src/Handler/PaymentNotificationHandler.php
+
+ -
+ message: '#^Parameter \#1 \$externalId of method PayPlug\\SyliusPayPlugPlugin\\Entity\\Card\:\:setExternalId\(\) expects string, mixed given\.$#'
+ identifier: argument.type
+ count: 1
+ path: ../src/Handler/PaymentNotificationHandler.php
+
+ -
+ message: '#^Parameter \#1 \$isLive of method PayPlug\\SyliusPayPlugPlugin\\Entity\\Card\:\:setIsLive\(\) expects bool, mixed given\.$#'
+ identifier: argument.type
+ count: 1
+ path: ../src/Handler/PaymentNotificationHandler.php
+
+ -
+ message: '#^Parameter \#1 \$last4 of method PayPlug\\SyliusPayPlugPlugin\\Entity\\Card\:\:setLast4\(\) expects string, mixed given\.$#'
+ identifier: argument.type
+ count: 1
+ path: ../src/Handler/PaymentNotificationHandler.php
+
+ -
+ message: '#^Parameter \#1 \$timestamp of method DateTimeImmutable\:\:setTimestamp\(\) expects int, mixed given\.$#'
+ identifier: argument.type
+ count: 1
+ path: ../src/Handler/PaymentNotificationHandler.php
+
+ -
+ message: '#^Parameter \#2 \$array of function array_key_exists expects array, mixed given\.$#'
+ identifier: argument.type
+ count: 2
+ path: ../src/Handler/PaymentNotificationHandler.php
+
+ -
+ message: '#^Cannot access offset ''refund_from_sylius'' on mixed\.$#'
+ identifier: offsetAccess.nonOffsetAccessible
+ count: 1
+ path: ../src/Handler/RefundNotificationHandler.php
+
+ -
+ message: '#^Parameter \#1 \$externalId of method PayPlug\\SyliusPayPlugPlugin\\Entity\\RefundHistory\:\:setExternalId\(\) expects string\|null, mixed given\.$#'
+ identifier: argument.type
+ count: 1
+ path: ../src/Handler/RefundNotificationHandler.php
+
+ -
+ message: '#^Parameter \#1 \$value of method PayPlug\\SyliusPayPlugPlugin\\Entity\\RefundHistory\:\:setValue\(\) expects int, mixed given\.$#'
+ identifier: argument.type
+ count: 1
+ path: ../src/Handler/RefundNotificationHandler.php
+
+ -
+ message: '#^Call to static method Webmozart\\Assert\\Assert\:\:isInstanceOf\(\) with DateTimeInterface and ''DateTimeInterface'' will always evaluate to true\.$#'
+ identifier: staticMethod.alreadyNarrowedType
+ count: 1
+ path: ../src/MessageHandler/RefundPaymentGeneratedHandler.php
+
+ -
+ message: '#^Cannot call method add\(\) on mixed\.$#'
+ identifier: method.nonObject
+ count: 1
+ path: ../src/MessageHandler/RefundPaymentGeneratedHandler.php
+
+ -
+ message: '#^Cannot call method apply\(\) on mixed\.$#'
+ identifier: method.nonObject
+ count: 2
+ path: ../src/MessageHandler/RefundPaymentGeneratedHandler.php
+
+ -
+ message: '#^Cannot call method get\(\) on mixed\.$#'
+ identifier: method.nonObject
+ count: 2
+ path: ../src/MessageHandler/RefundPaymentGeneratedHandler.php
+
+ -
+ message: '#^Property PayPlug\\SyliusPayPlugPlugin\\MessageHandler\\RefundPaymentGeneratedHandler\:\:\$stateMachineFactory has no type specified\.$#'
+ identifier: missingType.property
+ count: 1
+ path: ../src/MessageHandler/RefundPaymentGeneratedHandler.php
+
+ -
+ message: '#^Parameter \#1 \$paymentId of method PayPlug\\SyliusPayPlugPlugin\\ApiClient\\PayPlugApiClientInterface\:\:abortPayment\(\) expects string, mixed given\.$#'
+ identifier: argument.type
+ count: 1
+ path: ../src/PaymentProcessing/AbortPaymentProcessor.php
+
+ -
+ message: '#^Binary operation "\-\=" between float\|int and mixed results in an error\.$#'
+ identifier: assignOp.invalid
+ count: 2
+ path: ../src/PaymentProcessing/RefundPaymentHandler.php
+
+ -
+ message: '#^Cannot cast mixed to float\.$#'
+ identifier: cast.double
+ count: 1
+ path: ../src/PaymentProcessing/RefundPaymentHandler.php
+
+ -
+ message: '#^Class Sylius\\RefundPlugin\\Command\\RefundUnits constructor invoked with 5 parameters, 4 required\.$#'
+ identifier: arguments.count
+ count: 1
+ path: ../src/PaymentProcessing/RefundPaymentHandler.php
+
+ -
+ message: '#^Method PayPlug\\SyliusPayPlugPlugin\\PaymentProcessing\\RefundPaymentHandler\:\:parseIdsToUnitRefunds\(\) should return array\ but returns list\