diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..07229a68 --- /dev/null +++ b/.gitignore @@ -0,0 +1,11 @@ +/vendor/ +/node_modules/ +/composer.lock + +/etc/build/* +!/etc/build/.gitignore + +/tests/Application/yarn.lock + +/behat.yml +/phpspec.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000..aee0a0f2 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,77 @@ +language: php + +dist: trusty + +sudo: false + +php: + - 7.2 + - 7.3 + +cache: + yarn: true + directories: + - ~/.composer/cache/files + - $SYLIUS_CACHE_DIR + +env: + global: + - APP_ENV=test + - SYLIUS_CACHE_DIR=$HOME/.sylius-cache + - SYLIUS_BUILD_DIR=etc/build + +before_install: + - phpenv config-rm xdebug.ini + - echo "memory_limit=4096M" >> ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/travis.ini + - mkdir -p "${SYLIUS_CACHE_DIR}" + - mkdir -p tests/Application/web/media/image + +install: + - composer install --no-interaction --prefer-dist + - (cd tests/Application && yarn install) + +before_script: + - (cd tests/Application && bin/console doctrine:database:create -vvv) + - (cd tests/Application && bin/console doctrine:schema:create -vvv) + - (cd tests/Application && bin/console assets:install public -vvv) + - (cd tests/Application && bin/console cache:warmup -vvv) + - (cd tests/Application && yarn build) + + # Configure display + - /sbin/start-stop-daemon --start --quiet --pidfile /tmp/xvfb_99.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :99 -ac -screen 0 1680x1050x16 + - export DISPLAY=:99 + + # Download and configure ChromeDriver + - | + if [ ! -f $SYLIUS_CACHE_DIR/chromedriver ] || [ "$($SYLIUS_CACHE_DIR/chromedriver --version | grep -c 2.34)" = "0" ]; then + curl http://chromedriver.storage.googleapis.com/2.34/chromedriver_linux64.zip > chromedriver.zip + unzip chromedriver.zip + chmod +x chromedriver + mv chromedriver $SYLIUS_CACHE_DIR + fi + + # Run ChromeDriver + - $SYLIUS_CACHE_DIR/chromedriver > /dev/null 2>&1 & + + # Download and configure Selenium + - | + if [ ! -f $SYLIUS_CACHE_DIR/selenium.jar ] || [ "$(java -jar $SYLIUS_CACHE_DIR/selenium.jar --version | grep -c 3.4.0)" = "0" ]; then + curl http://selenium-release.storage.googleapis.com/3.4/selenium-server-standalone-3.4.0.jar > selenium.jar + mv selenium.jar $SYLIUS_CACHE_DIR + fi + + # Run Selenium + - java -Dwebdriver.chrome.driver=$SYLIUS_CACHE_DIR/chromedriver -jar $SYLIUS_CACHE_DIR/selenium.jar > /dev/null 2>&1 & + + # Run webserver + - (cd tests/Application && bin/console server:run 127.0.0.1:8080 --quiet > /dev/null 2>&1 &) + +script: + - composer validate --strict + + - vendor/bin/phpunit + - vendor/bin/phpspec run + - vendor/bin/behat --strict -vvv --no-interaction || vendor/bin/behat --strict -vvv --no-interaction --rerun + +after_failure: + - vendor/lakion/mink-debug-extension/travis/tools/upload-textfiles "${SYLIUS_BUILD_DIR}/*.log" diff --git a/README.md b/README.md new file mode 100644 index 00000000..2736810d --- /dev/null +++ b/README.md @@ -0,0 +1,109 @@ +## Overview + +This plugin allows you to integrate PayPlug payment with Sylius platform app. + +## Installation + +Add a package to your private repository and add the repository to `composer.json`: + +```json +{ + "minimum-stability": "dev", + "repositories": [ + { + "type": "vcs", + "url": "" + } + ] +} +``` + +1. Require plugin with composer: + + ```bash + composer require payplug/payplug-plugin + ``` + +2. Import configuration in your `config/packages/_sylius.yaml` file: + + ```yaml + imports: + - { resource: "@PayPlugSyliusPayPlugPlugin/Resources/config/config.yml" } + ``` + +3. Add plugin class to your `config/bundles.php` file: + + ```php + $bundles = [ + PayPlug\SyliusPayPlugPlugin\PayPlugSyliusPayPlugPlugin::class => ['all' => true], + ]; + ``` + +4. Clear cache: + + ```bash + bin/console cache:clear + ``` + +## Requirements + +In the channel settings, the base currency must be set to EUR because the payment gateway only works in this currency. The plugin in the local environment will not work properly because you will not be notified of the status of payments from the payment gateway + +## Cron job + +In the case when the IPN is blocked, you can set cron job every minute that updates the payment status. + +For example: + +```bash +* * * * * bin/console payplug:update-payment-state +``` + +## 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]`: + + ```yaml + monolog: + handlers: + ... + + payum: + level: debug + type: stream + path: "%kernel.logs_dir%/%kernel.environment%.log" +``` + +## IPN testing on the local machine + +In the configuration of the payment gateway in the admin panel, set your url (eg from [ngrok](https://ngrok.com/)) to notifications in the field `Notification url for environment dev`. This url will only work in the dev environment. + +## Customization + +### Available services you can [decorate](https://symfony.com/doc/current/service_container/service_decoration.html) and forms you can [extend](http://symfony.com/doc/current/form/create_form_type_extension.html) + +Run the below command to see what Symfony services are shared with this plugin: + +```bash +$ bin/console debug:container payplug_sylius_payplug_plugin +``` + +## Testing + +```bash +$ composer install +$ cd tests/Application +$ yarn install +$ yarn run gulp +$ bin/console assets:install web -e test +$ bin/console doctrine:database:create -e test +$ bin/console doctrine:schema:create -e test +$ bin/console server:run 127.0.0.1:8080 -d web -e test +$ open http://localhost:8080 +$ bin/behat +$ bin/phpspec run +``` + +## Contribution + +Learn more about our contribution workflow on http://docs.sylius.org/en/latest/contributing/. diff --git a/UPGRADE-1.3.md b/UPGRADE-1.3.md new file mode 100644 index 00000000..9fd41411 --- /dev/null +++ b/UPGRADE-1.3.md @@ -0,0 +1,28 @@ +# UPGRADE FROM `v1.2.X` TO `v1.3.0` + +## Application + +* Run `composer require sylius/sylius:~1.3.0 --no-update` + +* Add the following code in your `behat.yml(.dist)` file: + + ```yaml + default: + extensions: + FriendsOfBehat\SymfonyExtension: + env_file: ~ + ``` + +* Incorporate changes from the following files into plugin's test application: + + * [`tests/Application/package.json`](https://github.com/Sylius/PluginSkeleton/blob/1.3/tests/Application/package.json) ([see diff](https://github.com/Sylius/PluginSkeleton/pull/134/files#diff-726e1353c14df7d91379c0dea6b30eef)) + * [`tests/Application/.babelrc`](https://github.com/Sylius/PluginSkeleton/blob/1.3/tests/Application/.babelrc) ([see diff](https://github.com/Sylius/PluginSkeleton/pull/134/files#diff-a2527d9d8ad55460b2272274762c9386)) + * [`tests/Application/.eslintrc.js`](https://github.com/Sylius/PluginSkeleton/blob/1.3/tests/Application/.eslintrc.js) ([see diff](https://github.com/Sylius/PluginSkeleton/pull/134/files#diff-396c8c412b119deaa7dd84ae28ae04ca)) + +* Update PHP and JS dependencies by running `composer update` and `(cd tests/Application && yarn upgrade)` + +* Clear cache by running `(cd tests/Application && bin/console cache:clear)` + +* Install assets by `(cd tests/Application && bin/console assets:install web)` and `(cd tests/Application && yarn build)` + +* optionally, remove the build for PHP 7.1. in `.travis.yml` diff --git a/UPGRADE-1.4.md b/UPGRADE-1.4.md new file mode 100644 index 00000000..dc84cad6 --- /dev/null +++ b/UPGRADE-1.4.md @@ -0,0 +1,86 @@ +# UPGRADE FROM `v1.3.X` TO `v1.4.0` + +First step is upgrading Sylius with composer + +- `composer require sylius/sylius:~1.4.0` + +### Test application database + +#### Migrations + +If you provide migrations with your plugin, take a look at following changes: + +* Change base `AbstractMigration` namespace to `Doctrine\Migrations\AbstractMigration` +* Add `: void` return types to both `up` and `down` functions + +#### Schema update + +If you don't use migrations, just run `(cd tests/Application && bin/console doctrine:schema:update --force)` to update the test application's database schema. + +### Dotenv + +* `composer require symfony/dotenv:^4.2 --dev` +* Follow [Symfony dotenv update guide](https://symfony.com/doc/current/configuration/dot-env-changes.html) to incorporate required changes in `.env` files structure. Remember - they should be done on `tests/Application/` level! Optionally, you can take a look at [corresponding PR](https://github.com/Sylius/PluginSkeleton/pull/156/) introducing these changes in **PluginSkeleton** (this PR also includes changes with Behat - see below) + +Don't forget to clear the cache (`tests/Application/bin/console cache:clear`) to be 100% everything is loaded properly. + +--- + +### Behat + +If you're using Behat and want to be up-to-date with our configuration + +* Update required extensions with `composer require friends-of-behat/symfony-extension:^2.0 friends-of-behat/page-object-extension:^0.3 --dev` +* Remove extensions that are not needed yet with `composer remove friends-of-behat/context-service-extension friends-of-behat/cross-container-extension friends-of-behat/service-container-extension --dev` +* Update your `behat.yml` - look at the diff [here](https://github.com/Sylius/Sylius-Standard/pull/322/files#diff-7bde54db60a6e933518d8b61b929edce) +* Add `SymfonyExtensionBundle` to your `tests/Application/config/bundles.php`: + ```php + return [ + //... + FriendsOfBehat\SymfonyExtension\Bundle\FriendsOfBehatSymfonyExtensionBundle::class => ['test' => true, 'test_cached' => true], + ]; + ``` +* If you use our Travis CI configuration, follow [these changes](https://github.com/Sylius/PluginSkeleton/pull/156/files#diff-354f30a63fb0907d4ad57269548329e3) introduced in `.travis.yml` file +* Create `tests/Application/config/services_test.yaml` file with the following code and add these your own Behat services as well: + ```yaml + imports: + - { resource: "../../../vendor/sylius/sylius/src/Sylius/Behat/Resources/config/services.xml" } + ``` +* Remove all `__symfony__` prefixes in your Behat services +* Remove all `` tags from your Behat services +* Make your Behat services public by default with `` +* Change `contexts_services ` in your suite definitions to `contexts` +* Take a look at [SymfonyExtension UPGRADE guide](https://github.com/FriendsOfBehat/SymfonyExtension/blob/master/UPGRADE-2.0.md) if you have any more problems + +### Phpstan + +* Fix the container XML path parameter in the `phpstan.neon` file as done [here](https://github.com/Sylius/PluginSkeleton/commit/37fa614dbbcf8eb31b89eaf202b4bd4d89a5c7b3) + +# UPGRADE FROM `v1.2.X` TO `v1.4.0` + +Firstly, check out the [PluginSkeleton 1.3 upgrade guide](https://github.com/Sylius/PluginSkeleton/blob/1.4/UPGRADE-1.3.md) to update Sylius version step by step. +To upgrade to Sylius 1.4 follow instructions from [the previous section](https://github.com/Sylius/PluginSkeleton/blob/1.4/UPGRADE-1.4.md#upgrade-from-v13x-to-v140) with following changes: + +### Doctrine migrations + +* Change namespaces of copied migrations to `Sylius\Migrations` + +### Dotenv + +* These changes are not required, but can be done as well, if you've changed application directory structure in `1.2.x` to `1.3` update + +### Behat + +* Add `\FriendsOfBehat\SymfonyExtension\Bundle\FriendsOfBehatSymfonyExtensionBundle()` to your bundles lists in `tests/Application/AppKernel.php` (preferably only in `test` environment) +* Import Sylius Behat services in `tests/Application/config/config_test.yml` and your own Behat services as well: + ```yaml + imports: + - { resource: "../../../../vendor/sylius/sylius/src/Sylius/Behat/Resources/config/services.xml" } + ``` +* Specify test application's kernel path in `behat.yml`: + ```yaml + FriendsOfBehat\SymfonyExtension: + kernel: + class: AppKernel + path: tests/Application/app/AppKernel.php + ``` diff --git a/behat.yml.dist b/behat.yml.dist new file mode 100644 index 00000000..7d2dc095 --- /dev/null +++ b/behat.yml.dist @@ -0,0 +1,47 @@ +imports: + - vendor/sylius/sylius/src/Sylius/Behat/Resources/config/suites.yml + - tests/Behat/Resources/suites.yml + +default: + extensions: + Lakion\Behat\MinkDebugExtension: + directory: etc/build + clean_start: false + screenshot: true + + Behat\MinkExtension: + files_path: "%paths.base%/vendor/sylius/sylius/src/Sylius/Behat/Resources/fixtures/" + base_url: "http://localhost:8080/" + default_session: symfony + javascript_session: chrome + sessions: + symfony: + symfony: ~ + chrome: + selenium2: + browser: chrome + capabilities: + browserName: chrome + browser: chrome + version: "" + marionette: null # https://github.com/Behat/MinkExtension/pull/311 + chrome: + switches: + - "start-fullscreen" + - "start-maximized" + - "no-sandbox" + firefox: + selenium2: + browser: firefox + show_auto: false + + FriendsOfBehat\SymfonyExtension: + bootstrap: tests/Application/config/bootstrap.php + kernel: + class: Tests\PayPlug\SyliusPayPlugPlugin\Application\Kernel + + FriendsOfBehat\VariadicExtension: ~ + + FriendsOfBehat\SuiteSettingsExtension: + paths: + - "features" diff --git a/composer.json b/composer.json new file mode 100644 index 00000000..d5758752 --- /dev/null +++ b/composer.json @@ -0,0 +1,61 @@ +{ + "name": "payplug/payplug-plugin", + "type": "sylius-plugin", + "keywords": ["sylius", "sylius-plugin"], + "description": "PayPlug payment plugin for Sylius applications.", + "license": "OSL-3.0", + "require": { + "php": "^7.2", + + "sylius/sylius": "~1.4.0", + "payplug/payplug-php": "^2.6" + }, + "require-dev": { + "behat/behat": "^3.4", + "behat/mink": "^1.7@dev", + "behat/mink-browserkit-driver": "^1.3", + "behat/mink-extension": "^2.2", + "behat/mink-selenium2-driver": "^1.3", + "friends-of-behat/page-object-extension": "^0.3", + "friends-of-behat/suite-settings-extension": "^1.0", + "friends-of-behat/symfony-extension": "^2.0", + "friends-of-behat/variadic-extension": "^1.1", + "lakion/mink-debug-extension": "^1.2.3", + "phpspec/phpspec": "^5.0", + "phpstan/phpstan-doctrine": "^0.10", + "phpstan/phpstan-shim": "^0.10", + "phpstan/phpstan-symfony": "^0.10", + "phpstan/phpstan-webmozart-assert": "^0.10", + "phpunit/phpunit": "^6.5", + "sensiolabs/security-checker": "^5.0", + "sylius-labs/coding-standard": "^2.0", + "symfony/browser-kit": "^3.4|^4.1", + "symfony/debug-bundle": "^3.4|^4.1", + "symfony/dotenv": "^4.2", + "symfony/intl": "^3.4|^4.1", + "symfony/web-profiler-bundle": "^3.4|^4.1", + "symfony/web-server-bundle": "^3.4|^4.1" + }, + "conflict": { + "symfony/symfony": "4.1.8", + "symfony/browser-kit": "4.1.8", + "symfony/dependency-injection": "4.1.8", + "symfony/dom-crawler": "4.1.8", + "symfony/routing": "4.1.8" + }, + "prefer-stable": true, + "autoload": { + "psr-4": { + "PayPlug\\SyliusPayPlugPlugin\\": "src/", + "Tests\\PayPlug\\SyliusPayPlugPlugin\\": "tests/" + } + }, + "autoload-dev": { + "classmap": ["tests/Application/Kernel.php"] + }, + "extra": { + "branch-alias": { + "dev-master": "1.4-dev" + } + } +} diff --git a/easy-coding-standard.yml b/easy-coding-standard.yml new file mode 100644 index 00000000..24035995 --- /dev/null +++ b/easy-coding-standard.yml @@ -0,0 +1,2 @@ +imports: + - { resource: 'vendor/sylius-labs/coding-standard/easy-coding-standard.yml' } diff --git a/etc/build/.gitignore b/etc/build/.gitignore new file mode 100644 index 00000000..e69de29b diff --git a/features/admin/managing_payplug_payment_method.feature b/features/admin/managing_payplug_payment_method.feature new file mode 100644 index 00000000..bc2d2289 --- /dev/null +++ b/features/admin/managing_payplug_payment_method.feature @@ -0,0 +1,20 @@ +@managing_payplug_payment_method +Feature: Adding a new payplug payment method + In order to pay for orders in different ways + As an Administrator + I want to add a new payment method to the registry + + Background: + Given the store operates on a channel named "Web-USD" in "USD" currency + And I am logged in as an administrator + + @ui + Scenario: Adding a new payplug payment method + Given I want to create a new PayPlug payment method + When I name it "PayPlug" in "English (United States)" + And I specify its code as "payplug_test" + And I fill the Secret key with "test" + And make it available in channel "Web-USD" + And I add it + Then I should be notified that it has been successfully created + And the payment method "PayPlug" should appear in the registry diff --git a/features/admin/payplug_payment_method_validation.feature b/features/admin/payplug_payment_method_validation.feature new file mode 100644 index 00000000..cd29ebfd --- /dev/null +++ b/features/admin/payplug_payment_method_validation.feature @@ -0,0 +1,17 @@ +@managing_payplug_payment_method +Feature: PayPlug payment method validation + In order to avoid making mistakes when managing a payment method + As an Administrator + I want to be prevented from adding it without specifying required fields + + Background: + Given the store operates on a channel named "Web-RUB" in "RUB" currency + And the store has a payment method "Offline" with a code "offline" + And I am logged in as an administrator + + @ui + Scenario: Trying to add a new payplug payment method without specifying required configuration + Given I want to create a new PayPlug payment method + When I name it "PayPlug" in "English (United States)" + And I add it + Then I should be notified that "Secret key" fields cannot be blank diff --git a/features/admin/refunding_payplug_payment.feature b/features/admin/refunding_payplug_payment.feature new file mode 100644 index 00000000..ef41b3fd --- /dev/null +++ b/features/admin/refunding_payplug_payment.feature @@ -0,0 +1,29 @@ +@refunding_payplug_payment +Feature: Refunding order's PayPlug payment + In order to refund order's payment + As an Administrator + I want to be able to mark order's payment as refunded + + Background: + Given the store operates on a single channel in "United States" + And the store has a product "Green Arrow" + And the store ships everywhere for free + And the store has a payment method "PayPlug" with a code "payplug" and PayPlug payment gateway + And there is a customer "oliver@teamarrow.com" that placed an order "#00000001" + And the customer bought a single "Green Arrow" + And the customer chose "Free" shipping method to "United States" with "PayPlug" payment + And this order with payplug payment is already paid + And I am logged in as an administrator + And I am viewing the summary of this order + + @ui + Scenario: Marking order's payment as refunded + When I mark this order's payplug payment as refunded + Then I should be notified that the order's payment has been successfully refunded + And it should have payment with state refunded + + @ui + Scenario: Marking an order as refunded after refunding all its payments + When I mark this order's payplug payment as refunded + Then it should have payment with state refunded + And it's payment state should be refunded diff --git a/features/shop/paying_with_payplug_during_checkout.feature b/features/shop/paying_with_payplug_during_checkout.feature new file mode 100644 index 00000000..15b48fa6 --- /dev/null +++ b/features/shop/paying_with_payplug_during_checkout.feature @@ -0,0 +1,52 @@ +@paying_with_payplug_for_order +Feature: Paying with PayPlug during checkout + In order to buy products + As a Customer + I want to be able to pay with PayPlug + + Background: + Given the store operates on a single channel in "United States" + And there is a user "john@bitbag.pl" identified by "password123" + And the store has a payment method "PayPlug" with a code "payplug" and PayPlug payment gateway + And the store has a product "PHP T-Shirt" priced at "€100.00" + And the store ships everywhere for free + And I am logged in as "john@bitbag.pl" + + @ui + Scenario: Successful payment + Given I added product "PHP T-Shirt" to the cart + And I have proceeded selecting "PayPlug" payment method + When I confirm my order with PayPlug payment + And I sign in to PayPlug and pay successfully + Then I should be notified that my payment has been completed + + @ui + Scenario: Cancelling the payment + Given I added product "PHP T-Shirt" to the cart + And I have proceeded selecting "PayPlug" payment method + When I confirm my order with PayPlug payment + And I cancel my PayPlug payment + Then I should be notified that my payment has been cancelled + And I should be able to pay again + + @ui + Scenario: Retrying the payment with success + Given I added product "PHP T-Shirt" to the cart + And I have proceeded selecting "PayPlug" payment method + And I have confirmed my order with PayPlug payment + But I have cancelled PayPlug payment + When I try to pay again PayPlug payment + And I sign in to PayPlug and pay successfully + Then I should be notified that my payment has been completed + And I should see the thank you page + + @ui + Scenario: Retrying the payment and failing + Given I added product "PHP T-Shirt" to the cart + And I have proceeded selecting "PayPlug" payment method + And I have confirmed my order with PayPlug payment + But I have failed PayPlug payment + When I try to pay again PayPlug payment + And I cancel my PayPlug payment + Then I should be notified that my payment has been cancelled + And I should be able to pay again diff --git a/node_modules b/node_modules new file mode 120000 index 00000000..9270531f --- /dev/null +++ b/node_modules @@ -0,0 +1 @@ +tests/Application/node_modules \ No newline at end of file diff --git a/phpspec.yml.dist b/phpspec.yml.dist new file mode 100644 index 00000000..cdbf5061 --- /dev/null +++ b/phpspec.yml.dist @@ -0,0 +1,4 @@ +suites: + main: + namespace: PayPlug\SyliusPayPlugPlugin + psr4_prefix: PayPlug\SyliusPayPlugPlugin diff --git a/phpstan.neon b/phpstan.neon new file mode 100644 index 00000000..49adfa1d --- /dev/null +++ b/phpstan.neon @@ -0,0 +1,21 @@ +includes: + - vendor/phpstan/phpstan-doctrine/extension.neon + - vendor/phpstan/phpstan-symfony/extension.neon + - vendor/phpstan/phpstan-webmozart-assert/extension.neon + +parameters: + reportUnmatchedIgnoredErrors: false + + symfony: + container_xml_path: tests/Application/var/cache/test/ApplicationTests_BitBag_SyliusPayPlugPlugin_Application_KernelTestDebugContainer.xml + + excludes_analyse: + # Makes PHPStan crash + - 'src/DependencyInjection/Configuration.php' + + # Test dependencies + - 'tests/Application/app/**.php' + - 'tests/Application/src/**.php' + + ignoreErrors: + - '/Parameter #1 $configuration of method Symfony\Component\DependencyInjection\Extension\Extension::processConfiguration() expects Symfony\Component\Config\Definition\ConfigurationInterface, Symfony\Component\Config\Definition\ConfigurationInterface|null given./' diff --git a/phpunit.xml.dist b/phpunit.xml.dist new file mode 100644 index 00000000..3762a139 --- /dev/null +++ b/phpunit.xml.dist @@ -0,0 +1,20 @@ + + + + + + tests + + + + + + + + + + diff --git a/spec/Action/CaptureActionSpec.php b/spec/Action/CaptureActionSpec.php new file mode 100644 index 00000000..16f441ee --- /dev/null +++ b/spec/Action/CaptureActionSpec.php @@ -0,0 +1,102 @@ +beConstructedWith($logger); + } + + function it_is_initializable(): void + { + $this->shouldHaveType(CaptureAction::class); + } + + function it_implements_action_interface(): void + { + $this->shouldHaveType(ActionInterface::class); + } + + function it_implements_api_aware_interface(): void + { + $this->shouldHaveType(ApiAwareInterface::class); + } + + function it_implements_gateway_aware_interface(): void + { + $this->shouldHaveType(GatewayAwareInterface::class); + } + + function it_executes( + Capture $request, + ArrayObject $arrayObject, + TokenInterface $token, + GatewayInterface $gateway, + PayPlugApiClientInterface $payPlugApiClient, + GenericTokenFactory $genericTokenFactory, + TokenInterface $notifyToken + ): void { + $payment = \Mockery::mock('payment', Payment::class); + + $payment->id = 1; + $payment->hosted_payment = (object) [ + 'payment_url' => 'test', + ]; + + $this->setGateway($gateway); + $this->setApi($payPlugApiClient); + $this->setGenericTokenFactory($genericTokenFactory); + + $arrayObject->getArrayCopy()->willReturn([]); + $request->getModel()->willReturn($arrayObject); + $request->getFirstModel()->willReturn($payment); + $request->getToken()->willReturn($token); + $token->getTargetUrl()->willReturn('url'); + $token->getAfterUrl()->willReturn('url'); + $token->getGatewayName()->willReturn('test'); + $token->getDetails()->willReturn([]); + $genericTokenFactory->createNotifyToken('test', [])->willReturn($notifyToken); + $notifyToken->getTargetUrl()->willReturn('url'); + $notifyToken->getHash()->willReturn('test'); + $payPlugApiClient->createPayment([])->willReturn($payment); + $arrayObject->offsetGet('order_number')->willReturn('000001'); + + $arrayObject->offsetExists('status')->shouldBeCalled(); + $arrayObject->offsetSet('hosted_payment', ['return_url' => 'url', 'cancel_url' => 'url?&status=canceled'])->shouldBeCalled(); + $arrayObject->offsetSet('notification_url', 'url')->shouldBeCalled(); + $arrayObject->offsetSet('payment_id', 1)->shouldBeCalled(); + $arrayObject->offsetSet('status', PayPlugApiClientInterface::STATUS_CREATED)->shouldBeCalled(); + + $this + ->shouldThrow(HttpRedirect::class) + ->during('execute', [$request]) + ; + } + + function it_supports_only_capture_request_and_array_access( + Capture $request, + \ArrayAccess $arrayAccess + ): void { + $request->getModel()->willReturn($arrayAccess); + $this->supports($request)->shouldReturn(true); + } +} diff --git a/spec/Action/ConvertPaymentActionSpec.php b/spec/Action/ConvertPaymentActionSpec.php new file mode 100644 index 00000000..fbe385e1 --- /dev/null +++ b/spec/Action/ConvertPaymentActionSpec.php @@ -0,0 +1,86 @@ +shouldHaveType(ConvertPaymentAction::class); + } + + function it_implements_action_interface(): void + { + $this->shouldHaveType(ActionInterface::class); + } + + function it_executes( + Convert $request, + PaymentInterface $payment, + GatewayInterface $gateway, + OrderInterface $order, + CustomerInterface $customer, + AddressInterface $address + ): void { + $this->setGateway($gateway); + + $customer->getEmail()->willReturn('test@test.pl'); + $customer->getId()->willReturn(1); + $address->getFirstName()->willReturn('John'); + $address->getLastName()->willReturn('Doe'); + $address->getPostcode()->willReturn('97980'); + $address->getStreet()->willReturn('test'); + $address->getCity()->willReturn('City'); + $address->getCountryCode()->willReturn('US'); + $order->getCustomer()->willReturn($customer); + $order->getBillingAddress()->willReturn($address); + $order->getNumber()->willReturn('000000001'); + $payment->getOrder()->willReturn($order); + $request->getSource()->willReturn($payment); + $request->getTo()->willReturn('array'); + $payment->getDetails()->willReturn([]); + $payment->getAmount()->willReturn(100); + $payment->getCurrencyCode()->willReturn('EUR'); + + $request->setResult([ + 'amount' => 100, + 'currency' => 'EUR', + 'customer' => [ + 'email' => 'test@test.pl', + 'first_name' => 'John', + 'last_name' => 'Doe', + 'address1' => 'test', + 'city' => 'City', + 'country' => 'US', + 'postcode' => '97980', + ], + 'metadata' => [ + 'customer_id' => 1, + 'order_number' => '000000001', + ], + ])->shouldBeCalled(); + + $this->execute($request); + } + + function it_supports_only_convert_request_payment_source_and_array_to( + Convert $request, + PaymentInterface $payment + ): void { + $request->getSource()->willReturn($payment); + $request->getTo()->willReturn('array'); + $this->supports($request)->shouldReturn(true); + } +} diff --git a/spec/Action/NotifyActionSpec.php b/spec/Action/NotifyActionSpec.php new file mode 100644 index 00000000..17712345 --- /dev/null +++ b/spec/Action/NotifyActionSpec.php @@ -0,0 +1,71 @@ +beConstructedWith($logger); + } + + function it_is_initializable(): void + { + $this->shouldHaveType(NotifyAction::class); + } + + function it_implements_action_interface(): void + { + $this->shouldHaveType(ActionInterface::class); + } + + function it_implements_api_aware_interface(): void + { + $this->shouldHaveType(ApiAwareInterface::class); + } + + function it_implements_gateway_aware_interface(): void + { + $this->shouldHaveType(GatewayAwareInterface::class); + } + + function it_executes( + Notify $request, + \ArrayObject $arrayObject, + GatewayInterface $gateway, + PayPlugApiClient $payPlugApiClient + ): void { + $payment = \Mockery::mock('payment', Payment::class); + + $payment->is_paid = true; + + $this->setGateway($gateway); + $this->setApi($payPlugApiClient); + + $request->getModel()->willReturn($arrayObject); + $payPlugApiClient->treat('')->willReturn($payment); + + $this->execute($request); + } + + function it_supports_only_notify_request_and_array_access( + Notify $request, + \ArrayAccess $arrayAccess + ): void { + $request->getModel()->willReturn($arrayAccess); + $this->supports($request)->shouldReturn(true); + } +} diff --git a/spec/Action/StatusActionSpec.php b/spec/Action/StatusActionSpec.php new file mode 100644 index 00000000..7ad8ecf6 --- /dev/null +++ b/spec/Action/StatusActionSpec.php @@ -0,0 +1,54 @@ +shouldHaveType(StatusAction::class); + } + + function it_implements_action_interface(): void + { + $this->shouldHaveType(ActionInterface::class); + } + + function it_implements_gateway_aware_interface(): void + { + $this->shouldHaveType(GatewayAwareInterface::class); + } + + function it_executes( + GetStatusInterface $request, + PaymentInterface $payment, + GatewayInterface $gateway + ): void { + $this->setGateway($gateway); + + $payment->getDetails()->willReturn([]); + $request->getModel()->willReturn($payment); + + $request->markNew()->shouldBeCalled(); + + $this->execute($request); + } + + function it_supports_only_get_status_request_and_array_access( + GetStatusInterface $request, + PaymentInterface $payment + ): void { + $request->getModel()->willReturn($payment); + $this->supports($request)->shouldReturn(true); + } +} diff --git a/spec/PaymentProcessing/RefundPaymentProcessorSpec.php b/spec/PaymentProcessing/RefundPaymentProcessorSpec.php new file mode 100644 index 00000000..cfc95943 --- /dev/null +++ b/spec/PaymentProcessing/RefundPaymentProcessorSpec.php @@ -0,0 +1,57 @@ +beConstructedWith($session, $payPlugApiClient, $logger); + } + + function it_is_initializable(): void + { + $this->shouldHaveType(RefundPaymentProcessor::class); + } + + function it_implements_payment_processor_interface(): void + { + $this->shouldHaveType(PaymentProcessorInterface::class); + } + + function it_processes( + PaymentInterface $payment, + PaymentMethodInterface $paymentMethod, + GatewayConfigInterface $gatewayConfig, + PayPlugApiClientInterface $payPlugApiClient + ): void { + $gatewayConfig->getFactoryName()->willReturn(PayPlugGatewayFactory::FACTORY_NAME); + $gatewayConfig->getConfig()->willReturn([ + 'secretKey' => 'test' + ]); + $paymentMethod->getGatewayConfig()->willReturn($gatewayConfig); + $paymentMethod->getGatewayConfig()->willReturn($gatewayConfig); + $payment->getMethod()->willReturn($paymentMethod); + $payment->getDetails()->willReturn([ + 'payment_id' => 'test' + ]); + + $payPlugApiClient->refundPayment('test')->shouldBeCalled(); + $payPlugApiClient->initialise('test')->shouldBeCalled(); + + $this->process($payment); + } +} diff --git a/src/Action/Api/ApiAwareTrait.php b/src/Action/Api/ApiAwareTrait.php new file mode 100644 index 00000000..ea21b614 --- /dev/null +++ b/src/Action/Api/ApiAwareTrait.php @@ -0,0 +1,22 @@ +payPlugApiClient = $payPlugApiClient; + } +} diff --git a/src/Action/CaptureAction.php b/src/Action/CaptureAction.php new file mode 100644 index 00000000..c624d3ee --- /dev/null +++ b/src/Action/CaptureAction.php @@ -0,0 +1,118 @@ +logger = $logger; + } + + public function setGenericTokenFactory(GenericTokenFactoryInterface $genericTokenFactory = null): void + { + $this->tokenFactory = $genericTokenFactory; + } + + public function execute($request): void + { + RequestNotSupportedException::assertSupports($this, $request); + + $details = ArrayObject::ensureArrayObject($request->getModel()); + + if (isset($details['status']) && isset($details['payment_id'])) { + if (PayPlugApiClientInterface::STATUS_CREATED !== $details['status']) { + return; + } + + $times = 0; + + do { + $payment = $this->payPlugApiClient->retrieve($details['payment_id']); + + if ($payment->is_paid) { + $details['status'] = PayPlugApiClientInterface::STATUS_CAPTURED; + + return; + } + + sleep(1); + + ++$times; + } while ($times < 3); + + return; + } + + /** @var TokenInterface $token */ + $token = $request->getToken(); + + if (null === $this->tokenFactory) { + throw new RuntimeException(); + } + + $notifyToken = $this->tokenFactory->createNotifyToken($token->getGatewayName(), $token->getDetails()); + + $notificationUrl = $notifyToken->getTargetUrl(); + + $details['hosted_payment'] = [ + 'return_url' => $token->getAfterUrl(), + 'cancel_url' => $token->getTargetUrl() . '?&' . http_build_query(['status' => PayPlugApiClientInterface::STATUS_CANCELED]), + ]; + + if (isset($_ENV['APP_ENV']) && 'dev' === $_ENV['APP_ENV'] && !empty($this->payPlugApiClient->getNotificationUrlDev())) { + $notificationUrl = sprintf( + '%s%s', + rtrim($this->payPlugApiClient->getNotificationUrlDev(),"/"), + parse_url($notificationUrl, PHP_URL_PATH) + ); + } + + $details['notification_url'] = $notificationUrl; + + $payment = $this->payPlugApiClient->createPayment($details->getArrayCopy()); + + $details['payment_id'] = $payment->id; + $details['status'] = PayPlugApiClientInterface::STATUS_CREATED; + + $this->logger->debug('[PayPlug] Create payment', [ + 'payment_id' => $payment->id, + ]); + + throw new HttpRedirect($payment->hosted_payment->payment_url); + } + + public function supports($request): bool + { + return + $request instanceof Capture && + $request->getModel() instanceof \ArrayAccess + ; + } +} diff --git a/src/Action/ConvertPaymentAction.php b/src/Action/ConvertPaymentAction.php new file mode 100644 index 00000000..ca29c1b8 --- /dev/null +++ b/src/Action/ConvertPaymentAction.php @@ -0,0 +1,62 @@ +getSource(); + + /** @var OrderInterface $order */ + $order = $payment->getOrder(); + + $customer = $order->getCustomer(); + $address = $order->getBillingAddress(); + + $details = ArrayObject::ensureArrayObject($payment->getDetails()); + + $details['amount'] = $payment->getAmount(); + $details['currency'] = $payment->getCurrencyCode(); + $details['customer'] = [ + 'email' => $customer->getEmail(), + 'first_name' => $address->getFirstName(), + 'last_name' => $address->getLastName(), + 'address1' => $address->getStreet(), + 'city' => $address->getCity(), + 'country' => $address->getCountryCode(), + 'postcode' => $address->getPostcode(), + ]; + $details['metadata'] = [ + 'customer_id' => $customer->getId(), + 'order_number' => $order->getNumber(), + ]; + + $request->setResult((array) $details); + } + + public function supports($request): bool + { + return + $request instanceof Convert && + $request->getSource() instanceof PaymentInterface && + $request->getTo() == 'array' + ; + } +} diff --git a/src/Action/NotifyAction.php b/src/Action/NotifyAction.php new file mode 100644 index 00000000..308c6129 --- /dev/null +++ b/src/Action/NotifyAction.php @@ -0,0 +1,67 @@ +logger = $logger; + } + + public function execute($request): void + { + $details = ArrayObject::ensureArrayObject($request->getModel()); + + $input = file_get_contents('php://input'); + + try { + $resource = $this->payPlugApiClient->treat($input); + + if ($resource instanceof \Payplug\Resource\Payment && $resource->is_paid) { + $details['status'] = PayPlugApiClientInterface::STATUS_CAPTURED; + + return; + } + + if ($resource instanceof \Payplug\Resource\Refund && $resource->is_refunded) { + $details['status'] = PayPlugApiClientInterface::REFUNDED; + + return; + } + + $this->logger->info('[PayPlug] Notify action', ['failure' => $resource->failure]); + + $details['status'] = PayPlugApiClientInterface::FAILED; + } catch (\Payplug\Exception\PayplugException $exception) { + $details['status'] = PayPlugApiClientInterface::FAILED; + + $this->logger->error('[PayPlug] Notify action', ['error' => $exception->getMessage()]); + } + } + + public function supports($request): bool + { + return + $request instanceof Notify && + $request->getModel() instanceof \ArrayAccess + ; + } +} diff --git a/src/Action/StatusAction.php b/src/Action/StatusAction.php new file mode 100644 index 00000000..58d3ed32 --- /dev/null +++ b/src/Action/StatusAction.php @@ -0,0 +1,72 @@ +getModel(); + + $details = $payment->getDetails(); + + if (!isset($details['status']) || !isset($details['payment_id'])) { + $request->markNew(); + + return; + } + + $this->gateway->execute($httpRequest = new GetHttpRequest()); + + if (isset($httpRequest->query['status']) && PayPlugApiClientInterface::STATUS_CANCELED === $httpRequest->query['status']) { + $details['status'] = PayPlugApiClientInterface::STATUS_CANCELED; + + $payment->setDetails($details); + } + + switch ($details['status']) { + case PayPlugApiClientInterface::STATUS_CANCELED: + $request->markCanceled(); + break; + case PayPlugApiClientInterface::STATUS_CREATED: + $request->markPending(); + break; + case PayPlugApiClientInterface::STATUS_CAPTURED: + $request->markCaptured(); + break; + case PayPlugApiClientInterface::FAILED: + $request->markFailed(); + break; + case PayPlugApiClientInterface::REFUNDED: + $request->markRefunded(); + break; + default: + $request->markUnknown(); + break; + } + } + + public function supports($request): bool + { + return + $request instanceof GetStatusInterface && + $request->getModel() instanceof PaymentInterface + ; + } +} diff --git a/src/ApiClient/PayPlugApiClient.php b/src/ApiClient/PayPlugApiClient.php new file mode 100644 index 00000000..fff60855 --- /dev/null +++ b/src/ApiClient/PayPlugApiClient.php @@ -0,0 +1,46 @@ +notificationUrlDev = $notificationUrlDev; + } + + public function createPayment(array $data): Payment + { + return \Payplug\Payment::create($data); + } + + public function refundPayment(string $paymentId): Refund + { + return \Payplug\Refund::create($paymentId); + } + + public function treat($input) + { + return \Payplug\Notification::treat($input); + } + + public function retrieve(string $paymentId): Payment + { + return \Payplug\Payment::retrieve($paymentId); + } + + public function getNotificationUrlDev(): ?string + { + return $this->notificationUrlDev; + } +} diff --git a/src/ApiClient/PayPlugApiClientInterface.php b/src/ApiClient/PayPlugApiClientInterface.php new file mode 100644 index 00000000..623c4e62 --- /dev/null +++ b/src/ApiClient/PayPlugApiClientInterface.php @@ -0,0 +1,29 @@ +paymentRepository = $paymentRepository; + $this->paymentStateResolver = $paymentStateResolver; + $this->logger = $logger; + } + + protected function configure(): void + { + $this + ->setName('payplug:update-payment-state') + ->setDescription('Updates the payments state.') + ->setHelp('This command allows you to update the payments state for PayPlug gateway.') + ; + } + + protected function execute(InputInterface $input, OutputInterface $output): void + { + /** @var PaymentInterface[] $payments */ + $payments = $this->paymentRepository->findAllActiveByGatewayFactoryName(PayPlugGatewayFactory::FACTORY_NAME); + + $updatesCount = 0; + + foreach ($payments as $payment) { + $oldState = $payment->getState(); + $orderNumber = $payment->getOrder()->getNumber(); + + try { + $this->paymentStateResolver->resolve($payment); + } catch (\Exception $exception) { + $message = sprintf('An error occurred for the order #%s: %s', $orderNumber, $exception->getMessage()); + $this->logger->error($message); + $output->writeln($message); + continue; + } + + if ($oldState !== $payment->getState()) { + ++$updatesCount; + $output->writeln(sprintf('Update payment state for order #%s: %s -> %s', $orderNumber, $oldState, $payment->getState())); + } + } + + $output->writeln(''); + $output->writeln(sprintf('Updated: %d', $updatesCount)); + } +} diff --git a/src/DependencyInjection/PayPlugSyliusPayPlugExtension.php b/src/DependencyInjection/PayPlugSyliusPayPlugExtension.php new file mode 100644 index 00000000..e66f9e6c --- /dev/null +++ b/src/DependencyInjection/PayPlugSyliusPayPlugExtension.php @@ -0,0 +1,20 @@ +load('services.xml'); + } +} diff --git a/src/Form/Type/PayPlugGatewayConfigurationType.php b/src/Form/Type/PayPlugGatewayConfigurationType.php new file mode 100644 index 00000000..a3b76832 --- /dev/null +++ b/src/Form/Type/PayPlugGatewayConfigurationType.php @@ -0,0 +1,41 @@ +add('secretKey', TextType::class, [ + 'label' => 'payplug_sylius_payplug_plugin.ui.secret_key', + 'constraints' => [ + new NotBlank([ + 'message' => 'payplug_sylius_payplug_plugin.secret_key.not_blank', + 'groups' => 'sylius', + ]), + ], + ]) + ->add('notificationUrlDev', TextType::class, [ + 'label' => 'payplug_sylius_payplug_plugin.ui.notification_url_for_env_dev', + 'required' => false, + ]) + ->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) { + $data = $event->getData(); + + $data['payum.http_client'] = '@payplug_sylius_payplug_plugin.api_client.payplug'; + + $event->setData($data); + }) + ; + } +} diff --git a/src/PayPlugGatewayFactory.php b/src/PayPlugGatewayFactory.php new file mode 100644 index 00000000..173c115a --- /dev/null +++ b/src/PayPlugGatewayFactory.php @@ -0,0 +1,47 @@ +defaults([ + 'payum.factory_name' => self::FACTORY_NAME, + 'payum.factory_title' => 'PayPlug', + 'payum.http_client' => '@payplug_sylius_payplug_plugin.api_client.payplug', + ]); + + if (false === (bool) $config['payum.api']) { + $config['payum.default_options'] = [ + 'secretKey' => null, + 'notificationUrlDev' => null, + ]; + + $config->defaults($config['payum.default_options']); + + $config['payum.required_options'] = [ + 'secretKey', + ]; + + $config['payum.api'] = function (ArrayObject $config) { + $config->validateNotEmpty($config['payum.required_options']); + + /** @var PayPlugApiClientInterface $payPlugApiClient */ + $payPlugApiClient = $config['payum.http_client']; + + $payPlugApiClient->initialise($config['secretKey'], $config['notificationUrlDev']); + + return $payPlugApiClient; + }; + } + } +} diff --git a/src/PayPlugSyliusPayPlugPlugin.php b/src/PayPlugSyliusPayPlugPlugin.php new file mode 100644 index 00000000..4cdf069d --- /dev/null +++ b/src/PayPlugSyliusPayPlugPlugin.php @@ -0,0 +1,13 @@ +session = $session; + $this->payPlugApiClient = $payPlugApiClient; + $this->logger = $logger; + } + + public function process(PaymentInterface $payment): void + { + /** @var PaymentMethodInterface $paymentMethod */ + $paymentMethod = $payment->getMethod(); + + $details = $payment->getDetails(); + + if ( + PayPlugGatewayFactory::FACTORY_NAME !== $paymentMethod->getGatewayConfig()->getFactoryName() || + (isset($details['status']) && PayPlugApiClientInterface::REFUNDED === $details['status']) + ) { + return; + } + + if (!isset($details['payment_id'])) { + $this->session->getFlashBag()->add('info', 'The payment refund was made only locally.'); + + return; + } + + $this->logger->info('[PayPlug] Start refund payment', ['payment_id' => $details['payment_id']]); + + $gatewayConfig = $paymentMethod->getGatewayConfig()->getConfig(); + + $this->payPlugApiClient->initialise($gatewayConfig['secretKey']); + + try { + $this->payPlugApiClient->refundPayment($details['payment_id']); + } catch (\Exception $exception) { + $message = $exception->getMessage(); + + $this->session->getFlashBag()->add('error', $message); + + $this->logger->error('[PayPlug] Refund Payment', ['error' => $message]); + + throw new UpdateHandlingException(); + } + } +} diff --git a/src/Repository/PaymentRepository.php b/src/Repository/PaymentRepository.php new file mode 100644 index 00000000..f431fbbb --- /dev/null +++ b/src/Repository/PaymentRepository.php @@ -0,0 +1,26 @@ +createQueryBuilder('o') + ->innerJoin('o.method', 'method') + ->innerJoin('method.gatewayConfig', 'gatewayConfig') + ->where('gatewayConfig.factoryName = :gatewayFactoryName') + ->andWhere('o.state = :stateNew OR o.state = :stateProcessing') + ->setParameter('gatewayFactoryName', $gatewayFactoryName) + ->setParameter('stateNew', PaymentInterface::STATE_NEW) + ->setParameter('stateProcessing', PaymentInterface::STATE_PROCESSING) + ->getQuery() + ->getResult() + ; + } +} diff --git a/src/Repository/PaymentRepositoryInterface.php b/src/Repository/PaymentRepositoryInterface.php new file mode 100644 index 00000000..d7ff6410 --- /dev/null +++ b/src/Repository/PaymentRepositoryInterface.php @@ -0,0 +1,12 @@ +stateMachineFactory = $stateMachineFactory; + $this->payPlugApiClient = $payPlugApiClient; + $this->paymentEntityManager = $paymentEntityManager; + } + + public function resolve(PaymentInterface $payment): void + { + /** @var PaymentMethodInterface $paymentMethod */ + $paymentMethod = $payment->getMethod(); + + if (PayPlugGatewayFactory::FACTORY_NAME !== $paymentMethod->getGatewayConfig()->getFactoryName()) { + return; + } + + $details = $payment->getDetails(); + + if (!isset($details['payment_id'])) { + return; + } + + $gatewayConfig = $paymentMethod->getGatewayConfig()->getConfig(); + + $this->payPlugApiClient->initialise($gatewayConfig['secretKey']); + + $paymentStateMachine = $this->stateMachineFactory->get($payment, PaymentTransitions::GRAPH); + + $payment = $this->payPlugApiClient->retrieve($details['payment_id']); + + switch (true) { + case $payment->is_paid: + $this->applyTransition($paymentStateMachine, PaymentTransitions::TRANSITION_COMPLETE); + break; + case null !== $payment->failure: + $this->applyTransition($paymentStateMachine, PaymentTransitions::TRANSITION_FAIL); + break; + default: + $this->applyTransition($paymentStateMachine, PaymentTransitions::TRANSITION_PROCESS); + } + + $this->paymentEntityManager->flush(); + } + + private function applyTransition(StateMachineInterface $paymentStateMachine, string $transition): void + { + if ($paymentStateMachine->can($transition)) { + $paymentStateMachine->apply($transition); + } + } +} diff --git a/src/Resolver/PaymentStateResolverInterface.php b/src/Resolver/PaymentStateResolverInterface.php new file mode 100644 index 00000000..a55b39b2 --- /dev/null +++ b/src/Resolver/PaymentStateResolverInterface.php @@ -0,0 +1,12 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Resources/config/services/action.xml b/src/Resources/config/services/action.xml new file mode 100644 index 00000000..9a59e8ed --- /dev/null +++ b/src/Resources/config/services/action.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Resources/config/services/form.xml b/src/Resources/config/services/form.xml new file mode 100644 index 00000000..81812e87 --- /dev/null +++ b/src/Resources/config/services/form.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/src/Resources/config/services/gateway_factory.xml b/src/Resources/config/services/gateway_factory.xml new file mode 100644 index 00000000..e06cfb42 --- /dev/null +++ b/src/Resources/config/services/gateway_factory.xml @@ -0,0 +1,10 @@ + + + + + + PayPlug\SyliusPayPlugPlugin\PayPlugGatewayFactory + + + + diff --git a/src/Resources/config/state_machine.yml b/src/Resources/config/state_machine.yml new file mode 100644 index 00000000..2c2fa2b9 --- /dev/null +++ b/src/Resources/config/state_machine.yml @@ -0,0 +1,2 @@ +imports: + - { resource: "state_machine/sylius_payment.yml" } diff --git a/src/Resources/config/state_machine/sylius_payment.yml b/src/Resources/config/state_machine/sylius_payment.yml new file mode 100644 index 00000000..89b339f4 --- /dev/null +++ b/src/Resources/config/state_machine/sylius_payment.yml @@ -0,0 +1,8 @@ +winzou_state_machine: + sylius_payment: + callbacks: + before: + payplug_sylius_payplug_plugin_refund_process: + on: ["refund"] + do: ["@payplug_sylius_payplug_plugin.payment_processing.refund", "process"] + args: ["object"] diff --git a/src/Resources/translations/messages.en.yml b/src/Resources/translations/messages.en.yml new file mode 100644 index 00000000..cf82de37 --- /dev/null +++ b/src/Resources/translations/messages.en.yml @@ -0,0 +1,5 @@ +payplug_sylius_payplug_plugin: + ui: + secret_key: Secret key + payplug_gateway_label: PayPlug + notification_url_for_env_dev: Notification url for environment dev diff --git a/src/Resources/translations/validators.en.yml b/src/Resources/translations/validators.en.yml new file mode 100644 index 00000000..a6bc4b44 --- /dev/null +++ b/src/Resources/translations/validators.en.yml @@ -0,0 +1,3 @@ +payplug_sylius_payplug_plugin: + secret_key: + not_blank: Secret key cannot be blank. diff --git a/tests/Application/.babelrc b/tests/Application/.babelrc new file mode 100644 index 00000000..e563a62e --- /dev/null +++ b/tests/Application/.babelrc @@ -0,0 +1,15 @@ +{ + "presets": [ + ["env", { + "targets": { + "node": "6" + }, + "useBuiltIns": true + }] + ], + "plugins": [ + ["transform-object-rest-spread", { + "useBuiltIns": true + }] + ] +} diff --git a/tests/Application/.env b/tests/Application/.env new file mode 100644 index 00000000..177d4f48 --- /dev/null +++ b/tests/Application/.env @@ -0,0 +1,23 @@ +# This file is a "template" of which env vars needs to be defined in your configuration or in an .env file +# Set variables here that may be different on each deployment target of the app, e.g. development, staging, production. +# https://symfony.com/doc/current/best_practices/configuration.html#infrastructure-related-configuration + +###> symfony/framework-bundle ### +APP_ENV=dev +APP_DEBUG=1 +APP_SECRET=EDITME +###< symfony/framework-bundle ### + +###> doctrine/doctrine-bundle ### +# Format described at http://docs.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/configuration.html#connecting-using-a-url +# For a sqlite database, use: "sqlite:///%kernel.project_dir%/var/data.db" +# Set "serverVersion" to your server version to avoid edge-case exceptions and extra database calls +DATABASE_URL=sqlite:///%kernel.project_dir%/var/data_%kernel.environment%.db +###< doctrine/doctrine-bundle ### + +###> symfony/swiftmailer-bundle ### +# For Gmail as a transport, use: "gmail://username:password@localhost" +# For a generic SMTP server, use: "smtp://localhost:25?encryption=&auth_mode=" +# Delivery is disabled by default via "null://localhost" +MAILER_URL=smtp://localhost +###< symfony/swiftmailer-bundle ### diff --git a/tests/Application/.env.test b/tests/Application/.env.test new file mode 100644 index 00000000..ed2fe6bf --- /dev/null +++ b/tests/Application/.env.test @@ -0,0 +1,3 @@ +APP_SECRET='ch4mb3r0f5ecr3ts' + +KERNEL_CLASS='Tests\PayPlug\SyliusPayPlugPlugin\Application\Kernel' diff --git a/tests/Application/.eslintrc.js b/tests/Application/.eslintrc.js new file mode 100644 index 00000000..92c4cee3 --- /dev/null +++ b/tests/Application/.eslintrc.js @@ -0,0 +1,20 @@ +module.exports = { + extends: 'airbnb-base', + env: { + node: true, + }, + rules: { + 'object-shorthand': ['error', 'always', { + avoidQuotes: true, + avoidExplicitReturnArrows: true, + }], + 'function-paren-newline': ['error', 'consistent'], + 'max-len': ['warn', 120, 2, { + ignoreUrls: true, + ignoreComments: false, + ignoreRegExpLiterals: true, + ignoreStrings: true, + ignoreTemplateLiterals: true, + }], + }, +}; diff --git a/tests/Application/.gitignore b/tests/Application/.gitignore new file mode 100644 index 00000000..8ad1225e --- /dev/null +++ b/tests/Application/.gitignore @@ -0,0 +1,22 @@ +/public/assets +/public/css +/public/js +/public/media/* +!/public/media/image/ +/public/media/image/* +!/public/media/image/.gitignore + +/node_modules + +###> symfony/framework-bundle ### +/.env.*.local +/.env.local +/.env.local.php +/public/bundles +/var/ +/vendor/ +###< symfony/framework-bundle ### + +###> symfony/web-server-bundle ### +/.web-server-pid +###< symfony/web-server-bundle ### diff --git a/tests/Application/Kernel.php b/tests/Application/Kernel.php new file mode 100644 index 00000000..981c6d0f --- /dev/null +++ b/tests/Application/Kernel.php @@ -0,0 +1,106 @@ +getProjectDir() . '/var/cache/' . $this->environment; + } + + public function getLogDir(): string + { + return $this->getProjectDir() . '/var/log'; + } + + public function registerBundles(): iterable + { + $contents = require $this->getProjectDir() . '/config/bundles.php'; + foreach ($contents as $class => $envs) { + if (isset($envs['all']) || isset($envs[$this->environment])) { + yield new $class(); + } + } + } + + protected function configureContainer(ContainerBuilder $container, LoaderInterface $loader): void + { + $container->addResource(new FileResource($this->getProjectDir() . '/config/bundles.php')); + $container->setParameter('container.dumper.inline_class_loader', true); + $confDir = $this->getProjectDir() . '/config'; + + $loader->load($confDir . '/{packages}/*' . self::CONFIG_EXTS, 'glob'); + $loader->load($confDir . '/{packages}/' . $this->environment . '/**/*' . self::CONFIG_EXTS, 'glob'); + $loader->load($confDir . '/{services}' . self::CONFIG_EXTS, 'glob'); + $loader->load($confDir . '/{services}_' . $this->environment . self::CONFIG_EXTS, 'glob'); + } + + protected function configureRoutes(RouteCollectionBuilder $routes): void + { + $confDir = $this->getProjectDir() . '/config'; + + $routes->import($confDir . '/{routes}/*' . self::CONFIG_EXTS, '/', 'glob'); + $routes->import($confDir . '/{routes}/' . $this->environment . '/**/*' . self::CONFIG_EXTS, '/', 'glob'); + $routes->import($confDir . '/{routes}' . self::CONFIG_EXTS, '/', 'glob'); + } + + protected function getContainerBaseClass(): string + { + if ($this->isTestEnvironment()) { + return MockerContainer::class; + } + + return parent::getContainerBaseClass(); + } + + protected function getContainerLoader(ContainerInterface $container): LoaderInterface + { + /** @var ContainerBuilder $container */ + Assert::isInstanceOf($container, ContainerBuilder::class); + + $locator = new FileLocator($this, $this->getRootDir() . '/Resources'); + $resolver = new LoaderResolver(array( + new XmlFileLoader($container, $locator), + new YamlFileLoader($container, $locator), + new IniFileLoader($container, $locator), + new PhpFileLoader($container, $locator), + new GlobFileLoader($container, $locator), + new DirectoryLoader($container, $locator), + new ClosureLoader($container), + )); + + return new DelegatingLoader($resolver); + } + + private function isTestEnvironment(): bool + { + return 0 === strpos($this->getEnvironment(), 'test'); + } +} diff --git a/tests/Application/bin/console b/tests/Application/bin/console new file mode 100755 index 00000000..80effa45 --- /dev/null +++ b/tests/Application/bin/console @@ -0,0 +1,38 @@ +#!/usr/bin/env php +getParameterOption(['--env', '-e'], null, true)) { + putenv('APP_ENV='.$_SERVER['APP_ENV'] = $_ENV['APP_ENV'] = $env); +} + +if ($input->hasParameterOption('--no-debug', true)) { + putenv('APP_DEBUG='.$_SERVER['APP_DEBUG'] = $_ENV['APP_DEBUG'] = '0'); +} + +require dirname(__DIR__).'/config/bootstrap.php'; + +if ($_SERVER['APP_DEBUG']) { + umask(0000); + + if (class_exists(Debug::class)) { + Debug::enable(); + } +} + +$kernel = new Kernel($_SERVER['APP_ENV'], (bool) $_SERVER['APP_DEBUG']); +$application = new Application($kernel); +$application->run($input); diff --git a/tests/Application/composer.json b/tests/Application/composer.json new file mode 100644 index 00000000..326735f5 --- /dev/null +++ b/tests/Application/composer.json @@ -0,0 +1,5 @@ +{ + "name": "sylius/plugin-skeleton-test-application", + "description": "Sylius application for plugin testing purposes (composer.json needed for project dir resolving)", + "license": "MIT" +} diff --git a/tests/Application/config/bootstrap.php b/tests/Application/config/bootstrap.php new file mode 100644 index 00000000..6bb0207a --- /dev/null +++ b/tests/Application/config/bootstrap.php @@ -0,0 +1,21 @@ +=1.2) +if (is_array($env = @include dirname(__DIR__).'/.env.local.php')) { + $_SERVER += $env; + $_ENV += $env; +} elseif (!class_exists(Dotenv::class)) { + throw new RuntimeException('Please run "composer require symfony/dotenv" to load the ".env" files configuring the application.'); +} else { + // load all the .env files + (new Dotenv())->loadEnv(dirname(__DIR__).'/.env'); +} + +$_SERVER['APP_ENV'] = $_ENV['APP_ENV'] = ($_SERVER['APP_ENV'] ?? $_ENV['APP_ENV'] ?? null) ?: 'dev'; +$_SERVER['APP_DEBUG'] = $_SERVER['APP_DEBUG'] ?? $_ENV['APP_DEBUG'] ?? 'prod' !== $_SERVER['APP_ENV']; +$_SERVER['APP_DEBUG'] = $_ENV['APP_DEBUG'] = (int) $_SERVER['APP_DEBUG'] || filter_var($_SERVER['APP_DEBUG'], FILTER_VALIDATE_BOOLEAN) ? '1' : '0'; diff --git a/tests/Application/config/bundles.php b/tests/Application/config/bundles.php new file mode 100644 index 00000000..818fb929 --- /dev/null +++ b/tests/Application/config/bundles.php @@ -0,0 +1,60 @@ + ['all' => true], + Symfony\Bundle\MonologBundle\MonologBundle::class => ['all' => true], + Symfony\Bundle\SecurityBundle\SecurityBundle::class => ['all' => true], + Symfony\Bundle\SwiftmailerBundle\SwiftmailerBundle::class => ['all' => true], + Symfony\Bundle\TwigBundle\TwigBundle::class => ['all' => true], + Doctrine\Bundle\DoctrineBundle\DoctrineBundle::class => ['all' => true], + Doctrine\Bundle\DoctrineCacheBundle\DoctrineCacheBundle::class => ['all' => true], + Sylius\Bundle\OrderBundle\SyliusOrderBundle::class => ['all' => true], + Sylius\Bundle\MoneyBundle\SyliusMoneyBundle::class => ['all' => true], + Sylius\Bundle\CurrencyBundle\SyliusCurrencyBundle::class => ['all' => true], + Sylius\Bundle\LocaleBundle\SyliusLocaleBundle::class => ['all' => true], + Sylius\Bundle\ProductBundle\SyliusProductBundle::class => ['all' => true], + Sylius\Bundle\ChannelBundle\SyliusChannelBundle::class => ['all' => true], + Sylius\Bundle\AttributeBundle\SyliusAttributeBundle::class => ['all' => true], + Sylius\Bundle\TaxationBundle\SyliusTaxationBundle::class => ['all' => true], + Sylius\Bundle\ShippingBundle\SyliusShippingBundle::class => ['all' => true], + Sylius\Bundle\PaymentBundle\SyliusPaymentBundle::class => ['all' => true], + Sylius\Bundle\MailerBundle\SyliusMailerBundle::class => ['all' => true], + Sylius\Bundle\PromotionBundle\SyliusPromotionBundle::class => ['all' => true], + Sylius\Bundle\AddressingBundle\SyliusAddressingBundle::class => ['all' => true], + Sylius\Bundle\InventoryBundle\SyliusInventoryBundle::class => ['all' => true], + Sylius\Bundle\TaxonomyBundle\SyliusTaxonomyBundle::class => ['all' => true], + Sylius\Bundle\UserBundle\SyliusUserBundle::class => ['all' => true], + Sylius\Bundle\CustomerBundle\SyliusCustomerBundle::class => ['all' => true], + Sylius\Bundle\UiBundle\SyliusUiBundle::class => ['all' => true], + Sylius\Bundle\ReviewBundle\SyliusReviewBundle::class => ['all' => true], + Sylius\Bundle\CoreBundle\SyliusCoreBundle::class => ['all' => true], + Sylius\Bundle\ResourceBundle\SyliusResourceBundle::class => ['all' => true], + Sylius\Bundle\GridBundle\SyliusGridBundle::class => ['all' => true], + winzou\Bundle\StateMachineBundle\winzouStateMachineBundle::class => ['all' => true], + Sonata\CoreBundle\SonataCoreBundle::class => ['all' => true], + Sonata\BlockBundle\SonataBlockBundle::class => ['all' => true], + Sonata\IntlBundle\SonataIntlBundle::class => ['all' => true], + Bazinga\Bundle\HateoasBundle\BazingaHateoasBundle::class => ['all' => true], + JMS\SerializerBundle\JMSSerializerBundle::class => ['all' => true], + FOS\RestBundle\FOSRestBundle::class => ['all' => true], + Knp\Bundle\GaufretteBundle\KnpGaufretteBundle::class => ['all' => true], + Knp\Bundle\MenuBundle\KnpMenuBundle::class => ['all' => true], + Liip\ImagineBundle\LiipImagineBundle::class => ['all' => true], + Payum\Bundle\PayumBundle\PayumBundle::class => ['all' => true], + Stof\DoctrineExtensionsBundle\StofDoctrineExtensionsBundle::class => ['all' => true], + WhiteOctober\PagerfantaBundle\WhiteOctoberPagerfantaBundle::class => ['all' => true], + Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle::class => ['all' => true], + Doctrine\Bundle\FixturesBundle\DoctrineFixturesBundle::class => ['all' => true], + Sylius\Bundle\FixturesBundle\SyliusFixturesBundle::class => ['all' => true], + Sylius\Bundle\PayumBundle\SyliusPayumBundle::class => ['all' => true], + Sylius\Bundle\ThemeBundle\SyliusThemeBundle::class => ['all' => true], + Symfony\Bundle\WebServerBundle\WebServerBundle::class => ['all' => true], + Sylius\Bundle\AdminBundle\SyliusAdminBundle::class => ['all' => true], + Sylius\Bundle\ShopBundle\SyliusShopBundle::class => ['all' => true], + FOS\OAuthServerBundle\FOSOAuthServerBundle::class => ['all' => true], + Sylius\Bundle\AdminApiBundle\SyliusAdminApiBundle::class => ['all' => true], + PayPlug\SyliusPayPlugPlugin\PayPlugSyliusPayPlugPlugin::class => ['all' => true], + Symfony\Bundle\DebugBundle\DebugBundle::class => ['dev' => true, 'test' => true, 'test_cached' => true], + Symfony\Bundle\WebProfilerBundle\WebProfilerBundle::class => ['dev' => true, 'test' => true, 'test_cached' => true], + FriendsOfBehat\SymfonyExtension\Bundle\FriendsOfBehatSymfonyExtensionBundle::class => ['test' => true, 'test_cached' => true], +]; diff --git a/tests/Application/config/packages/_sylius.yaml b/tests/Application/config/packages/_sylius.yaml new file mode 100644 index 00000000..89674acc --- /dev/null +++ b/tests/Application/config/packages/_sylius.yaml @@ -0,0 +1,14 @@ +imports: + - { resource: "@SyliusCoreBundle/Resources/config/app/config.yml" } + + - { resource: "@SyliusAdminBundle/Resources/config/app/config.yml" } + - { resource: "@SyliusAdminApiBundle/Resources/config/app/config.yml" } + + - { resource: "@SyliusShopBundle/Resources/config/app/config.yml" } + +parameters: + sylius_core.public_dir: '%kernel.project_dir%/public' + +sylius_shop: + product_grid: + include_all_descendants: true diff --git a/tests/Application/config/packages/dev/framework.yaml b/tests/Application/config/packages/dev/framework.yaml new file mode 100644 index 00000000..4b116def --- /dev/null +++ b/tests/Application/config/packages/dev/framework.yaml @@ -0,0 +1,2 @@ +framework: + profiler: { only_exceptions: false } diff --git a/tests/Application/config/packages/dev/jms_serializer.yaml b/tests/Application/config/packages/dev/jms_serializer.yaml new file mode 100644 index 00000000..353e4602 --- /dev/null +++ b/tests/Application/config/packages/dev/jms_serializer.yaml @@ -0,0 +1,7 @@ +jms_serializer: + visitors: + json: + options: + - JSON_PRETTY_PRINT + - JSON_UNESCAPED_SLASHES + - JSON_PRESERVE_ZERO_FRACTION diff --git a/tests/Application/config/packages/dev/monolog.yaml b/tests/Application/config/packages/dev/monolog.yaml new file mode 100644 index 00000000..da2b092d --- /dev/null +++ b/tests/Application/config/packages/dev/monolog.yaml @@ -0,0 +1,9 @@ +monolog: + handlers: + main: + type: stream + path: "%kernel.logs_dir%/%kernel.environment%.log" + level: debug + firephp: + type: firephp + level: info diff --git a/tests/Application/config/packages/dev/routing.yaml b/tests/Application/config/packages/dev/routing.yaml new file mode 100644 index 00000000..4116679a --- /dev/null +++ b/tests/Application/config/packages/dev/routing.yaml @@ -0,0 +1,3 @@ +framework: + router: + strict_requirements: true diff --git a/tests/Application/config/packages/dev/swiftmailer.yaml b/tests/Application/config/packages/dev/swiftmailer.yaml new file mode 100644 index 00000000..f4380780 --- /dev/null +++ b/tests/Application/config/packages/dev/swiftmailer.yaml @@ -0,0 +1,2 @@ +swiftmailer: + disable_delivery: true diff --git a/tests/Application/config/packages/dev/web_profiler.yaml b/tests/Application/config/packages/dev/web_profiler.yaml new file mode 100644 index 00000000..1f1cb2bb --- /dev/null +++ b/tests/Application/config/packages/dev/web_profiler.yaml @@ -0,0 +1,3 @@ +web_profiler: + toolbar: true + intercept_redirects: false diff --git a/tests/Application/config/packages/doctrine.yaml b/tests/Application/config/packages/doctrine.yaml new file mode 100644 index 00000000..f51ba5a2 --- /dev/null +++ b/tests/Application/config/packages/doctrine.yaml @@ -0,0 +1,14 @@ +parameters: + # Adds a fallback DATABASE_URL if the env var is not set. + # This allows you to run cache:warmup even if your + # environment variables are not available yet. + # You should not need to change this value. + env(DATABASE_URL): '' + +doctrine: + dbal: + driver: 'pdo_mysql' + server_version: '5.7' + charset: UTF8 + + url: '%env(resolve:DATABASE_URL)%' diff --git a/tests/Application/config/packages/doctrine_migrations.yaml b/tests/Application/config/packages/doctrine_migrations.yaml new file mode 100644 index 00000000..c0a12026 --- /dev/null +++ b/tests/Application/config/packages/doctrine_migrations.yaml @@ -0,0 +1,5 @@ +doctrine_migrations: + dir_name: "%kernel.project_dir%/src/Migrations" + + # Namespace is arbitrary but should be different from App\Migrations as migrations classes should NOT be autoloaded + namespace: DoctrineMigrations diff --git a/tests/Application/config/packages/fos_rest.yaml b/tests/Application/config/packages/fos_rest.yaml new file mode 100644 index 00000000..a72eef7c --- /dev/null +++ b/tests/Application/config/packages/fos_rest.yaml @@ -0,0 +1,11 @@ +fos_rest: + exception: true + view: + formats: + json: true + xml: true + empty_content: 204 + format_listener: + rules: + - { path: '^/api/.*', priorities: ['json', 'xml'], fallback_format: json, prefer_extension: true } + - { path: '^/', stop: true } diff --git a/tests/Application/config/packages/framework.yaml b/tests/Application/config/packages/framework.yaml new file mode 100644 index 00000000..e74ed811 --- /dev/null +++ b/tests/Application/config/packages/framework.yaml @@ -0,0 +1,7 @@ +framework: + secret: '%env(APP_SECRET)%' + form: true + csrf_protection: true + templating: { engines: ["twig"] } + session: + handler_id: ~ diff --git a/tests/Application/config/packages/jms_serializer.yaml b/tests/Application/config/packages/jms_serializer.yaml new file mode 100644 index 00000000..64dd8d10 --- /dev/null +++ b/tests/Application/config/packages/jms_serializer.yaml @@ -0,0 +1,4 @@ +jms_serializer: + visitors: + xml: + format_output: '%kernel.debug%' diff --git a/tests/Application/config/packages/liip_imagine.yaml b/tests/Application/config/packages/liip_imagine.yaml new file mode 100644 index 00000000..bb2e7ceb --- /dev/null +++ b/tests/Application/config/packages/liip_imagine.yaml @@ -0,0 +1,6 @@ +liip_imagine: + resolvers: + default: + web_path: + web_root: "%kernel.project_dir%/public" + cache_prefix: "media/cache" diff --git a/tests/Application/config/packages/payplug_sylius_payplug_plugin.yaml b/tests/Application/config/packages/payplug_sylius_payplug_plugin.yaml new file mode 100644 index 00000000..5a3c90d6 --- /dev/null +++ b/tests/Application/config/packages/payplug_sylius_payplug_plugin.yaml @@ -0,0 +1,2 @@ +imports: + - { resource: "@PayPlugSyliusPayPlugPlugin/Resources/config/config.yml" } diff --git a/tests/Application/config/packages/prod/doctrine.yaml b/tests/Application/config/packages/prod/doctrine.yaml new file mode 100644 index 00000000..2f16f0fd --- /dev/null +++ b/tests/Application/config/packages/prod/doctrine.yaml @@ -0,0 +1,31 @@ +doctrine: + orm: + metadata_cache_driver: + type: service + id: doctrine.system_cache_provider + query_cache_driver: + type: service + id: doctrine.system_cache_provider + result_cache_driver: + type: service + id: doctrine.result_cache_provider + +services: + doctrine.result_cache_provider: + class: Symfony\Component\Cache\DoctrineProvider + public: false + arguments: + - '@doctrine.result_cache_pool' + doctrine.system_cache_provider: + class: Symfony\Component\Cache\DoctrineProvider + public: false + arguments: + - '@doctrine.system_cache_pool' + +framework: + cache: + pools: + doctrine.result_cache_pool: + adapter: cache.app + doctrine.system_cache_pool: + adapter: cache.system diff --git a/tests/Application/config/packages/prod/jms_serializer.yaml b/tests/Application/config/packages/prod/jms_serializer.yaml new file mode 100644 index 00000000..bc97faf1 --- /dev/null +++ b/tests/Application/config/packages/prod/jms_serializer.yaml @@ -0,0 +1,6 @@ +jms_serializer: + visitors: + json: + options: + - JSON_UNESCAPED_SLASHES + - JSON_PRESERVE_ZERO_FRACTION diff --git a/tests/Application/config/packages/prod/monolog.yaml b/tests/Application/config/packages/prod/monolog.yaml new file mode 100644 index 00000000..adc2fd40 --- /dev/null +++ b/tests/Application/config/packages/prod/monolog.yaml @@ -0,0 +1,14 @@ +monolog: + handlers: + main: + type: fingers_crossed + action_level: error + handler: nested + nested: + type: stream + path: "%kernel.logs_dir%/%kernel.environment%.log" + level: debug + payum: + level: debug + type: stream + path: "%kernel.logs_dir%/%kernel.environment%.log" diff --git a/tests/Application/config/packages/routing.yaml b/tests/Application/config/packages/routing.yaml new file mode 100644 index 00000000..368bc7f4 --- /dev/null +++ b/tests/Application/config/packages/routing.yaml @@ -0,0 +1,3 @@ +framework: + router: + strict_requirements: ~ diff --git a/tests/Application/config/packages/security.yaml b/tests/Application/config/packages/security.yaml new file mode 100644 index 00000000..830b03df --- /dev/null +++ b/tests/Application/config/packages/security.yaml @@ -0,0 +1,102 @@ +parameters: + sylius.security.admin_regex: "^/admin" + sylius.security.api_regex: "^/api" + sylius.security.shop_regex: "^/(?!admin|api/.*|api$|media/.*)[^/]++" + +security: + providers: + sylius_admin_user_provider: + id: sylius.admin_user_provider.email_or_name_based + sylius_shop_user_provider: + id: sylius.shop_user_provider.email_or_name_based + encoders: + Sylius\Component\User\Model\UserInterface: sha512 + firewalls: + admin: + switch_user: true + context: admin + pattern: "%sylius.security.admin_regex%" + provider: sylius_admin_user_provider + form_login: + provider: sylius_admin_user_provider + login_path: sylius_admin_login + check_path: sylius_admin_login_check + failure_path: sylius_admin_login + default_target_path: sylius_admin_dashboard + use_forward: false + use_referer: true + csrf_token_generator: security.csrf.token_manager + csrf_parameter: _csrf_admin_security_token + csrf_token_id: admin_authenticate + remember_me: + secret: "%env(APP_SECRET)%" + path: /admin + name: APP_ADMIN_REMEMBER_ME + lifetime: 31536000 + remember_me_parameter: _remember_me + logout: + path: sylius_admin_logout + target: sylius_admin_login + anonymous: true + + oauth_token: + pattern: "%sylius.security.api_regex%/oauth/v2/token" + security: false + + api: + pattern: "%sylius.security.api_regex%/.*" + provider: sylius_admin_user_provider + fos_oauth: true + stateless: true + anonymous: true + + shop: + switch_user: { role: ROLE_ALLOWED_TO_SWITCH } + context: shop + pattern: "%sylius.security.shop_regex%" + provider: sylius_shop_user_provider + form_login: + success_handler: sylius.authentication.success_handler + failure_handler: sylius.authentication.failure_handler + provider: sylius_shop_user_provider + login_path: sylius_shop_login + check_path: sylius_shop_login_check + failure_path: sylius_shop_login + default_target_path: sylius_shop_homepage + use_forward: false + use_referer: true + csrf_token_generator: security.csrf.token_manager + csrf_parameter: _csrf_shop_security_token + csrf_token_id: shop_authenticate + remember_me: + secret: "%env(APP_SECRET)%" + name: APP_SHOP_REMEMBER_ME + lifetime: 31536000 + remember_me_parameter: _remember_me + logout: + path: sylius_shop_logout + target: sylius_shop_login + invalidate_session: false + success_handler: sylius.handler.shop_user_logout + anonymous: true + + dev: + pattern: ^/(_(profiler|wdt)|css|images|js)/ + security: false + + access_control: + - { path: "%sylius.security.admin_regex%/_partial", role: IS_AUTHENTICATED_ANONYMOUSLY, ips: [127.0.0.1, ::1] } + - { path: "%sylius.security.admin_regex%/_partial", role: ROLE_NO_ACCESS } + - { path: "%sylius.security.shop_regex%/_partial", role: IS_AUTHENTICATED_ANONYMOUSLY, ips: [127.0.0.1, ::1] } + - { path: "%sylius.security.shop_regex%/_partial", role: ROLE_NO_ACCESS } + + - { path: "%sylius.security.admin_regex%/login", role: IS_AUTHENTICATED_ANONYMOUSLY } + - { path: "%sylius.security.api_regex%/login", role: IS_AUTHENTICATED_ANONYMOUSLY } + - { path: "%sylius.security.shop_regex%/login", role: IS_AUTHENTICATED_ANONYMOUSLY } + + - { path: "%sylius.security.shop_regex%/register", role: IS_AUTHENTICATED_ANONYMOUSLY } + - { path: "%sylius.security.shop_regex%/verify", role: IS_AUTHENTICATED_ANONYMOUSLY } + + - { path: "%sylius.security.admin_regex%", role: ROLE_ADMINISTRATION_ACCESS } + - { path: "%sylius.security.api_regex%/.*", role: ROLE_API_ACCESS } + - { path: "%sylius.security.shop_regex%/account", role: ROLE_USER } diff --git a/tests/Application/config/packages/security_checker.yaml b/tests/Application/config/packages/security_checker.yaml new file mode 100644 index 00000000..0f9cf00f --- /dev/null +++ b/tests/Application/config/packages/security_checker.yaml @@ -0,0 +1,9 @@ +services: + SensioLabs\Security\SecurityChecker: + public: false + + SensioLabs\Security\Command\SecurityCheckerCommand: + arguments: ['@SensioLabs\Security\SecurityChecker'] + public: false + tags: + - { name: console.command, command: 'security:check' } diff --git a/tests/Application/config/packages/sonata_core.yaml b/tests/Application/config/packages/sonata_core.yaml new file mode 100644 index 00000000..e9a6e895 --- /dev/null +++ b/tests/Application/config/packages/sonata_core.yaml @@ -0,0 +1,4 @@ +sonata_core: + form: + mapping: + enabled: false diff --git a/tests/Application/config/packages/staging/monolog.yaml b/tests/Application/config/packages/staging/monolog.yaml new file mode 100644 index 00000000..64612114 --- /dev/null +++ b/tests/Application/config/packages/staging/monolog.yaml @@ -0,0 +1,10 @@ +monolog: + handlers: + main: + type: fingers_crossed + action_level: error + handler: nested + nested: + type: stream + path: "%kernel.logs_dir%/%kernel.environment%.log" + level: debug diff --git a/tests/Application/config/packages/staging/swiftmailer.yaml b/tests/Application/config/packages/staging/swiftmailer.yaml new file mode 100644 index 00000000..f4380780 --- /dev/null +++ b/tests/Application/config/packages/staging/swiftmailer.yaml @@ -0,0 +1,2 @@ +swiftmailer: + disable_delivery: true diff --git a/tests/Application/config/packages/stof_doctrine_extensions.yaml b/tests/Application/config/packages/stof_doctrine_extensions.yaml new file mode 100644 index 00000000..7770f74e --- /dev/null +++ b/tests/Application/config/packages/stof_doctrine_extensions.yaml @@ -0,0 +1,4 @@ +# Read the documentation: https://symfony.com/doc/current/bundles/StofDoctrineExtensionsBundle/index.html +# See the official DoctrineExtensions documentation for more details: https://github.com/Atlantic18/DoctrineExtensions/tree/master/doc/ +stof_doctrine_extensions: + default_locale: '%locale%' diff --git a/tests/Application/config/packages/swiftmailer.yaml b/tests/Application/config/packages/swiftmailer.yaml new file mode 100644 index 00000000..3bab0d32 --- /dev/null +++ b/tests/Application/config/packages/swiftmailer.yaml @@ -0,0 +1,2 @@ +swiftmailer: + url: '%env(MAILER_URL)%' diff --git a/tests/Application/config/packages/test/framework.yaml b/tests/Application/config/packages/test/framework.yaml new file mode 100644 index 00000000..76d7e5e1 --- /dev/null +++ b/tests/Application/config/packages/test/framework.yaml @@ -0,0 +1,4 @@ +framework: + test: ~ + session: + storage_id: session.storage.mock_file diff --git a/tests/Application/config/packages/test/monolog.yaml b/tests/Application/config/packages/test/monolog.yaml new file mode 100644 index 00000000..7e2b9e3a --- /dev/null +++ b/tests/Application/config/packages/test/monolog.yaml @@ -0,0 +1,6 @@ +monolog: + handlers: + main: + type: stream + path: "%kernel.logs_dir%/%kernel.environment%.log" + level: error diff --git a/tests/Application/config/packages/test/swiftmailer.yaml b/tests/Application/config/packages/test/swiftmailer.yaml new file mode 100644 index 00000000..c438f4b2 --- /dev/null +++ b/tests/Application/config/packages/test/swiftmailer.yaml @@ -0,0 +1,6 @@ +swiftmailer: + disable_delivery: true + logging: true + spool: + type: file + path: "%kernel.cache_dir%/spool" diff --git a/tests/Application/config/packages/test/sylius_theme.yaml b/tests/Application/config/packages/test/sylius_theme.yaml new file mode 100644 index 00000000..4d34199f --- /dev/null +++ b/tests/Application/config/packages/test/sylius_theme.yaml @@ -0,0 +1,3 @@ +sylius_theme: + sources: + test: ~ diff --git a/tests/Application/config/packages/test/web_profiler.yaml b/tests/Application/config/packages/test/web_profiler.yaml new file mode 100644 index 00000000..03752de2 --- /dev/null +++ b/tests/Application/config/packages/test/web_profiler.yaml @@ -0,0 +1,6 @@ +web_profiler: + toolbar: false + intercept_redirects: false + +framework: + profiler: { collect: false } diff --git a/tests/Application/config/packages/test_cached/doctrine.yaml b/tests/Application/config/packages/test_cached/doctrine.yaml new file mode 100644 index 00000000..49528606 --- /dev/null +++ b/tests/Application/config/packages/test_cached/doctrine.yaml @@ -0,0 +1,16 @@ +doctrine: + orm: + entity_managers: + default: + result_cache_driver: + type: memcached + host: localhost + port: 11211 + query_cache_driver: + type: memcached + host: localhost + port: 11211 + metadata_cache_driver: + type: memcached + host: localhost + port: 11211 diff --git a/tests/Application/config/packages/test_cached/fos_rest.yaml b/tests/Application/config/packages/test_cached/fos_rest.yaml new file mode 100644 index 00000000..2b4189da --- /dev/null +++ b/tests/Application/config/packages/test_cached/fos_rest.yaml @@ -0,0 +1,3 @@ +fos_rest: + exception: + debug: true diff --git a/tests/Application/config/packages/test_cached/framework.yaml b/tests/Application/config/packages/test_cached/framework.yaml new file mode 100644 index 00000000..76d7e5e1 --- /dev/null +++ b/tests/Application/config/packages/test_cached/framework.yaml @@ -0,0 +1,4 @@ +framework: + test: ~ + session: + storage_id: session.storage.mock_file diff --git a/tests/Application/config/packages/test_cached/monolog.yaml b/tests/Application/config/packages/test_cached/monolog.yaml new file mode 100644 index 00000000..7e2b9e3a --- /dev/null +++ b/tests/Application/config/packages/test_cached/monolog.yaml @@ -0,0 +1,6 @@ +monolog: + handlers: + main: + type: stream + path: "%kernel.logs_dir%/%kernel.environment%.log" + level: error diff --git a/tests/Application/config/packages/test_cached/swiftmailer.yaml b/tests/Application/config/packages/test_cached/swiftmailer.yaml new file mode 100644 index 00000000..c438f4b2 --- /dev/null +++ b/tests/Application/config/packages/test_cached/swiftmailer.yaml @@ -0,0 +1,6 @@ +swiftmailer: + disable_delivery: true + logging: true + spool: + type: file + path: "%kernel.cache_dir%/spool" diff --git a/tests/Application/config/packages/test_cached/sylius_channel.yaml b/tests/Application/config/packages/test_cached/sylius_channel.yaml new file mode 100644 index 00000000..bab83ef2 --- /dev/null +++ b/tests/Application/config/packages/test_cached/sylius_channel.yaml @@ -0,0 +1,2 @@ +sylius_channel: + debug: true diff --git a/tests/Application/config/packages/test_cached/sylius_theme.yaml b/tests/Application/config/packages/test_cached/sylius_theme.yaml new file mode 100644 index 00000000..4d34199f --- /dev/null +++ b/tests/Application/config/packages/test_cached/sylius_theme.yaml @@ -0,0 +1,3 @@ +sylius_theme: + sources: + test: ~ diff --git a/tests/Application/config/packages/test_cached/twig.yaml b/tests/Application/config/packages/test_cached/twig.yaml new file mode 100644 index 00000000..8c6e0b40 --- /dev/null +++ b/tests/Application/config/packages/test_cached/twig.yaml @@ -0,0 +1,2 @@ +twig: + strict_variables: true diff --git a/tests/Application/config/packages/translation.yaml b/tests/Application/config/packages/translation.yaml new file mode 100644 index 00000000..1f4f9664 --- /dev/null +++ b/tests/Application/config/packages/translation.yaml @@ -0,0 +1,8 @@ +framework: + default_locale: '%locale%' + translator: + paths: + - '%kernel.project_dir%/translations' + fallbacks: + - '%locale%' + - 'en' diff --git a/tests/Application/config/packages/twig.yaml b/tests/Application/config/packages/twig.yaml new file mode 100644 index 00000000..3b315dcc --- /dev/null +++ b/tests/Application/config/packages/twig.yaml @@ -0,0 +1,4 @@ +twig: + paths: ['%kernel.project_dir%/templates'] + debug: '%kernel.debug%' + strict_variables: '%kernel.debug%' diff --git a/tests/Application/config/packages/twig_extensions.yaml b/tests/Application/config/packages/twig_extensions.yaml new file mode 100644 index 00000000..0881cc95 --- /dev/null +++ b/tests/Application/config/packages/twig_extensions.yaml @@ -0,0 +1,11 @@ +services: + _defaults: + public: false + autowire: true + autoconfigure: true + + # Uncomment any lines below to activate that Twig extension + #Twig\Extensions\ArrayExtension: ~ + #Twig\Extensions\DateExtension: ~ + #Twig\Extensions\IntlExtension: ~ + #Twig\Extensions\TextExtension: ~ diff --git a/tests/Application/config/packages/validator.yaml b/tests/Application/config/packages/validator.yaml new file mode 100644 index 00000000..61807db6 --- /dev/null +++ b/tests/Application/config/packages/validator.yaml @@ -0,0 +1,3 @@ +framework: + validation: + enable_annotations: true diff --git a/tests/Application/config/routes.yaml b/tests/Application/config/routes.yaml new file mode 100644 index 00000000..e69de29b diff --git a/tests/Application/config/routes/dev/twig.yaml b/tests/Application/config/routes/dev/twig.yaml new file mode 100644 index 00000000..f4ee8396 --- /dev/null +++ b/tests/Application/config/routes/dev/twig.yaml @@ -0,0 +1,3 @@ +_errors: + resource: '@TwigBundle/Resources/config/routing/errors.xml' + prefix: /_error diff --git a/tests/Application/config/routes/dev/web_profiler.yaml b/tests/Application/config/routes/dev/web_profiler.yaml new file mode 100644 index 00000000..3e79dc21 --- /dev/null +++ b/tests/Application/config/routes/dev/web_profiler.yaml @@ -0,0 +1,7 @@ +_wdt: + resource: "@WebProfilerBundle/Resources/config/routing/wdt.xml" + prefix: /_wdt + +_profiler: + resource: "@WebProfilerBundle/Resources/config/routing/profiler.xml" + prefix: /_profiler diff --git a/tests/Application/config/routes/liip_imagine.yaml b/tests/Application/config/routes/liip_imagine.yaml new file mode 100644 index 00000000..201cbd5d --- /dev/null +++ b/tests/Application/config/routes/liip_imagine.yaml @@ -0,0 +1,2 @@ +_liip_imagine: + resource: "@LiipImagineBundle/Resources/config/routing.yaml" diff --git a/tests/Application/config/routes/sylius_admin.yaml b/tests/Application/config/routes/sylius_admin.yaml new file mode 100644 index 00000000..1ba48d6c --- /dev/null +++ b/tests/Application/config/routes/sylius_admin.yaml @@ -0,0 +1,3 @@ +sylius_admin: + resource: "@SyliusAdminBundle/Resources/config/routing.yml" + prefix: /admin diff --git a/tests/Application/config/routes/sylius_admin_api.yaml b/tests/Application/config/routes/sylius_admin_api.yaml new file mode 100644 index 00000000..80aed457 --- /dev/null +++ b/tests/Application/config/routes/sylius_admin_api.yaml @@ -0,0 +1,3 @@ +sylius_admin_api: + resource: "@SyliusAdminApiBundle/Resources/config/routing.yml" + prefix: /api diff --git a/tests/Application/config/routes/sylius_shop.yaml b/tests/Application/config/routes/sylius_shop.yaml new file mode 100644 index 00000000..8818568b --- /dev/null +++ b/tests/Application/config/routes/sylius_shop.yaml @@ -0,0 +1,14 @@ +sylius_shop: + resource: "@SyliusShopBundle/Resources/config/routing.yml" + prefix: /{_locale} + requirements: + _locale: ^[a-z]{2}(?:_[A-Z]{2})?$ + +sylius_shop_payum: + resource: "@SyliusShopBundle/Resources/config/routing/payum.yml" + +sylius_shop_default_locale: + path: / + methods: [GET] + defaults: + _controller: sylius.controller.shop.locale_switch:switchAction diff --git a/tests/Application/config/services.yaml b/tests/Application/config/services.yaml new file mode 100644 index 00000000..615506eb --- /dev/null +++ b/tests/Application/config/services.yaml @@ -0,0 +1,4 @@ +# Put parameters here that don't need to change on each machine where the app is deployed +# https://symfony.com/doc/current/best_practices/configuration.html#application-related-configuration +parameters: + locale: en_US diff --git a/tests/Application/config/services_test.yaml b/tests/Application/config/services_test.yaml new file mode 100644 index 00000000..e71bfa1c --- /dev/null +++ b/tests/Application/config/services_test.yaml @@ -0,0 +1,10 @@ +imports: + - { resource: "../../Behat/Resources/services.xml" } + - { resource: "../../../vendor/sylius/sylius/src/Sylius/Behat/Resources/config/services.xml" } + +services: + payplug_sylius_payplug_plugin.api_client.payplug: + class: Tests\PayPlug\SyliusPayPlugPlugin\Behat\Mocker\PayPlugApiClient + public: true + arguments: + - "@service_container" diff --git a/tests/Application/gulpfile.babel.js b/tests/Application/gulpfile.babel.js new file mode 100644 index 00000000..5920316f --- /dev/null +++ b/tests/Application/gulpfile.babel.js @@ -0,0 +1,60 @@ +import chug from 'gulp-chug'; +import gulp from 'gulp'; +import yargs from 'yargs'; + +const { argv } = yargs + .options({ + rootPath: { + description: ' path to public assets directory', + type: 'string', + requiresArg: true, + required: false, + }, + nodeModulesPath: { + description: ' path to node_modules directory', + type: 'string', + requiresArg: true, + required: false, + }, + }); + +const config = [ + '--rootPath', + argv.rootPath || '../../../../../../../tests/Application/public/assets', + '--nodeModulesPath', + argv.nodeModulesPath || '../../../../../../../tests/Application/node_modules', +]; + +export const buildAdmin = function buildAdmin() { + return gulp.src('../../vendor/sylius/sylius/src/Sylius/Bundle/AdminBundle/gulpfile.babel.js', { read: false }) + .pipe(chug({ args: config, tasks: 'build' })); +}; +buildAdmin.description = 'Build admin assets.'; + +export const watchAdmin = function watchAdmin() { + return gulp.src('../../vendor/sylius/sylius/src/Sylius/Bundle/AdminBundle/gulpfile.babel.js', { read: false }) + .pipe(chug({ args: config, tasks: 'watch' })); +}; +watchAdmin.description = 'Watch admin asset sources and rebuild on changes.'; + +export const buildShop = function buildShop() { + return gulp.src('../../vendor/sylius/sylius/src/Sylius/Bundle/ShopBundle/gulpfile.babel.js', { read: false }) + .pipe(chug({ args: config, tasks: 'build' })); +}; +buildShop.description = 'Build shop assets.'; + +export const watchShop = function watchShop() { + return gulp.src('../../vendor/sylius/sylius/src/Sylius/Bundle/ShopBundle/gulpfile.babel.js', { read: false }) + .pipe(chug({ args: config, tasks: 'watch' })); +}; +watchShop.description = 'Watch shop asset sources and rebuild on changes.'; + +export const build = gulp.parallel(buildAdmin, buildShop); +build.description = 'Build assets.'; + +gulp.task('admin', buildAdmin); +gulp.task('admin-watch', watchAdmin); +gulp.task('shop', buildShop); +gulp.task('shop-watch', watchShop); + +export default build; diff --git a/tests/Application/package.json b/tests/Application/package.json new file mode 100644 index 00000000..14072b2b --- /dev/null +++ b/tests/Application/package.json @@ -0,0 +1,53 @@ +{ + "dependencies": { + "babel-polyfill": "^6.26.0", + "jquery": "^3.2.0", + "lightbox2": "^2.9.0", + "semantic-ui-css": "^2.2.0" + }, + "devDependencies": { + "babel-core": "^6.26.3", + "babel-plugin-external-helpers": "^6.22.0", + "babel-plugin-module-resolver": "^3.1.1", + "babel-plugin-transform-object-rest-spread": "^6.26.0", + "babel-preset-env": "^1.7.0", + "babel-register": "^6.26.0", + "dedent": "^0.7.0", + "eslint": "^4.19.1", + "eslint-config-airbnb-base": "^12.1.0", + "eslint-import-resolver-babel-module": "^4.0.0", + "eslint-plugin-import": "^2.12.0", + "fast-async": "^6.3.7", + "gulp": "^4.0.0", + "gulp-chug": "^0.5", + "gulp-concat": "^2.6.0", + "gulp-debug": "^2.1.2", + "gulp-if": "^2.0.0", + "gulp-livereload": "^3.8.1", + "gulp-order": "^1.1.1", + "gulp-sass": "^4.0.1", + "gulp-sourcemaps": "^1.6.0", + "gulp-uglifycss": "^1.0.5", + "merge-stream": "^1.0.0", + "rollup": "^0.60.7", + "rollup-plugin-babel": "^3.0.4", + "rollup-plugin-commonjs": "^9.1.3", + "rollup-plugin-inject": "^2.0.0", + "rollup-plugin-node-resolve": "^3.3.0", + "rollup-plugin-uglify": "^4.0.0", + "upath": "^1.1.0", + "yargs": "^6.4.0" + }, + "scripts": { + "build": "gulp build", + "gulp": "gulp build", + "lint": "yarn lint:js", + "lint:js": "eslint gulpfile.babel.js" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/Sylius/Sylius.git" + }, + "author": "Paweł Jędrzejewski", + "license": "MIT" +} diff --git a/tests/Application/public/.htaccess b/tests/Application/public/.htaccess new file mode 100644 index 00000000..99ed00df --- /dev/null +++ b/tests/Application/public/.htaccess @@ -0,0 +1,25 @@ +DirectoryIndex app.php + + + RewriteEngine On + + RewriteCond %{HTTP:Authorization} ^(.*) + RewriteRule .* - [e=HTTP_AUTHORIZATION:%1] + + RewriteCond %{REQUEST_URI}::$1 ^(/.+)/(.*)::\2$ + RewriteRule ^(.*) - [E=BASE:%1] + + RewriteCond %{ENV:REDIRECT_STATUS} ^$ + RewriteRule ^index\.php(/(.*)|$) %{ENV:BASE}/$2 [R=301,L] + + RewriteCond %{REQUEST_FILENAME} -f + RewriteRule .? - [L] + + RewriteRule .? %{ENV:BASE}/index.php [L] + + + + + RedirectMatch 302 ^/$ /index.php/ + + diff --git a/tests/Application/public/favicon.ico b/tests/Application/public/favicon.ico new file mode 100644 index 00000000..592f7a8e Binary files /dev/null and b/tests/Application/public/favicon.ico differ diff --git a/tests/Application/public/index.php b/tests/Application/public/index.php new file mode 100644 index 00000000..7a40c4c2 --- /dev/null +++ b/tests/Application/public/index.php @@ -0,0 +1,27 @@ +handle($request); +$response->send(); +$kernel->terminate($request, $response); diff --git a/tests/Application/public/media/image/.gitignore b/tests/Application/public/media/image/.gitignore new file mode 100644 index 00000000..e69de29b diff --git a/tests/Application/public/robots.txt b/tests/Application/public/robots.txt new file mode 100644 index 00000000..214e4119 --- /dev/null +++ b/tests/Application/public/robots.txt @@ -0,0 +1,4 @@ +# www.robotstxt.org/ +# www.google.com/support/webmasters/bin/answer.py?hl=en&answer=156449 + +User-agent: * diff --git a/tests/Application/templates/.gitignore b/tests/Application/templates/.gitignore new file mode 100644 index 00000000..e69de29b diff --git a/tests/Application/translations/.gitignore b/tests/Application/translations/.gitignore new file mode 100644 index 00000000..e69de29b diff --git a/tests/Behat/Context/Setup/OrderContext.php b/tests/Behat/Context/Setup/OrderContext.php new file mode 100644 index 00000000..260c120c --- /dev/null +++ b/tests/Behat/Context/Setup/OrderContext.php @@ -0,0 +1,62 @@ +objectManager = $objectManager; + $this->stateMachineFactory = $stateMachineFactory; + $this->payum = $payum; + } + + /** + * @Given /^(this order) with payplug payment is already paid$/ + */ + public function thisOrderWithPayPlugPaymentIsAlreadyPaid(OrderInterface $order): void + { + $this->applyPayPlugPaymentTransitionOnOrder($order, PaymentTransitions::TRANSITION_COMPLETE); + + $this->objectManager->flush(); + } + + private function applyPayPlugPaymentTransitionOnOrder(OrderInterface $order, $transition): void + { + foreach ($order->getPayments() as $payment) { + /** @var PaymentMethodInterface $paymentMethod */ + $paymentMethod = $payment->getMethod(); + + if (PayPlugGatewayFactory::FACTORY_NAME === $paymentMethod->getGatewayConfig()->getFactoryName()) { + $model['payment_id'] = 'test'; + + $payment->setDetails($model); + } + + $this->stateMachineFactory->get($payment, PaymentTransitions::GRAPH)->apply($transition); + } + } +} diff --git a/tests/Behat/Context/Setup/PayPlugContext.php b/tests/Behat/Context/Setup/PayPlugContext.php new file mode 100644 index 00000000..f6abb6e1 --- /dev/null +++ b/tests/Behat/Context/Setup/PayPlugContext.php @@ -0,0 +1,96 @@ +sharedStorage = $sharedStorage; + $this->paymentMethodRepository = $paymentMethodRepository; + $this->paymentMethodExampleFactory = $paymentMethodExampleFactory; + $this->paymentMethodTranslationFactory = $paymentMethodTranslationFactory; + $this->paymentMethodManager = $paymentMethodManager; + } + + /** + * @Given the store has a payment method :paymentMethodName with a code :paymentMethodCode and PayPlug payment gateway + */ + public function theStoreHasAPaymentMethodWithACodeAndPayPlugPaymentGateway(string $paymentMethodName, string $paymentMethodCode): void + { + $paymentMethod = $this->createPaymentMethodPayPlug( + $paymentMethodName, + $paymentMethodCode, + PayPlugGatewayFactory::FACTORY_NAME, + 'PayPlug' + ); + + $paymentMethod->getGatewayConfig()->setConfig([ + 'secretKey' => 'test', + 'notificationUrlDev' => 'http://test', + 'payum.http_client' => '@payplug_sylius_payplug_plugin.api_client.payplug', + ]); + + $this->paymentMethodManager->flush(); + } + + private function createPaymentMethodPayPlug( + string $name, + string $code, + string $factoryName, + string $description = '', + bool $addForCurrentChannel = true, + int $position = null + ): PaymentMethodInterface { + /** @var PaymentMethodInterface $paymentMethod */ + $paymentMethod = $this->paymentMethodExampleFactory->create([ + 'name' => ucfirst($name), + 'code' => $code, + 'description' => $description, + 'gatewayName' => $factoryName, + 'gatewayFactory' => $factoryName, + 'enabled' => true, + 'channels' => ($addForCurrentChannel && $this->sharedStorage->has('channel')) ? [$this->sharedStorage->get('channel')] : [], + ]); + + if (null !== $position) { + $paymentMethod->setPosition($position); + } + + $this->sharedStorage->set('payment_method', $paymentMethod); + $this->paymentMethodRepository->add($paymentMethod); + + return $paymentMethod; + } +} diff --git a/tests/Behat/Context/Ui/Admin/ManagingPaymentMethodContext.php b/tests/Behat/Context/Ui/Admin/ManagingPaymentMethodContext.php new file mode 100644 index 00000000..31530cc7 --- /dev/null +++ b/tests/Behat/Context/Ui/Admin/ManagingPaymentMethodContext.php @@ -0,0 +1,59 @@ +createPage = $createPage; + } + + /** + * @Given I want to create a new PayPlug payment method + */ + public function iWantToCreateANewPayPlugPaymentMethod(): void + { + $this->createPage->open(['factory' => 'payplug']); + } + + /** + * @Then I should be notified that :fields fields cannot be blank + */ + public function iShouldBeNotifiedThatCannotBeBlank(string $fields): void + { + $fields = explode(',', $fields); + + foreach ($fields as $field) { + Assert::true($this->createPage->containsErrorWithMessage(sprintf( + '%s cannot be blank.', + trim($field) + ))); + } + } + + /** + * @Then I should be notified that :message + */ + public function iShouldBeNotifiedThat(string $message): void + { + Assert::true($this->createPage->containsErrorWithMessage($message)); + } + + /** + * @When I fill the Secret key with :secretKey + */ + public function iFillTheSecretKeyWith(string $secretKey): void + { + $this->createPage->setSecretKey($secretKey); + } +} diff --git a/tests/Behat/Context/Ui/Admin/RefundContext.php b/tests/Behat/Context/Ui/Admin/RefundContext.php new file mode 100644 index 00000000..6cab246c --- /dev/null +++ b/tests/Behat/Context/Ui/Admin/RefundContext.php @@ -0,0 +1,37 @@ +payPlugApiMocker = $payPlugApiMocker; + $this->managingOrdersContext = $managingOrdersContext; + } + + /** + * @When /^I mark (this order)'s payplug payment as refunded$/ + */ + public function iMarkThisOrdersPayPlugPaymentAsRefunded(OrderInterface $order): void + { + $this->payPlugApiMocker->mockApiRefundedPayment(function () use ($order) { + $this->managingOrdersContext->iMarkThisOrderSPaymentAsRefunded($order); + }); + } +} diff --git a/tests/Behat/Context/Ui/Shop/CheckoutContext.php b/tests/Behat/Context/Ui/Shop/CheckoutContext.php new file mode 100644 index 00000000..3fe2f9ec --- /dev/null +++ b/tests/Behat/Context/Ui/Shop/CheckoutContext.php @@ -0,0 +1,93 @@ +summaryPage = $summaryPage; + $this->orderDetails = $orderDetails; + $this->payPlugApiMocker = $payPlugApiMocker; + $this->paymentPage = $paymentPage; + } + + /** + * @When I confirm my order with PayPlug payment + * @Given I have confirmed my order with PayPlug payment + */ + public function iConfirmMyOrderWithPayPlugPayment(): void + { + $this->payPlugApiMocker->mockApiCreatePayment(function () { + $this->summaryPage->confirmOrder(); + }); + } + + /** + * @When I sign in to PayPlug and pay successfully + */ + public function iSignInToPayPlugAndPaySuccessfully(): void + { + $this->payPlugApiMocker->mockApiSuccessfulPayment(function () { + $this->paymentPage->notify(['id' => 1]); + $this->paymentPage->capture(); + }); + } + + /** + * @Given I have failed PayPlug payment + */ + public function iHaveFailedPayPlugPayment() + { + $this->payPlugApiMocker->mockApiFailedPayment(function () { + $this->paymentPage->notify(['id' => 1]); + $this->paymentPage->capture(); + }); + } + + /** + * @When I cancel my PayPlug payment + * @Given I have cancelled PayPlug payment + */ + public function iCancelMyPayPlugPayment(): void + { + $this->payPlugApiMocker->mockApiCancelledPayment(function () { + $this->paymentPage->capture(['status' => PayPlugApiClientInterface::STATUS_CANCELED]); + }); + } + + /** + * @When I try to pay again PayPlug payment + */ + public function iTryToPayAgainPayPlugPayment(): void + { + $this->payPlugApiMocker->mockApiCreatePayment(function () { + $this->orderDetails->pay(); + }); + } +} diff --git a/tests/Behat/Mocker/PayPlugApiClient.php b/tests/Behat/Mocker/PayPlugApiClient.php new file mode 100644 index 00000000..912518d2 --- /dev/null +++ b/tests/Behat/Mocker/PayPlugApiClient.php @@ -0,0 +1,51 @@ +container = $container; + } + + public function initialise(string $secretKey, ?string $notificationUrlDev = null): void + { + $this->container->get('payplug_sylius_payplug_plugin.api_client.payplug')->initialise($secretKey, $notificationUrlDev); + } + + public function createPayment(array $data): Payment + { + return $this->container->get('payplug_sylius_payplug_plugin.api_client.payplug')->createPayment($data); + } + + public function refundPayment(string $paymentId): Refund + { + return $this->container->get('payplug_sylius_payplug_plugin.api_client.payplug')->refundPayment($paymentId); + } + + public function treat($input) + { + return $this->container->get('payplug_sylius_payplug_plugin.api_client.payplug')->treat($input); + } + + public function retrieve(string $paymentId): Payment + { + return $this->container->get('payplug_sylius_payplug_plugin.api_client.payplug')->retrieve($paymentId); + } + + public function getNotificationUrlDev(): ?string + { + return $this->container->get('payplug_sylius_payplug_plugin.api_client.payplug')->getNotificationUrlDev(); + } +} diff --git a/tests/Behat/Mocker/PayPlugApiMocker.php b/tests/Behat/Mocker/PayPlugApiMocker.php new file mode 100644 index 00000000..88839e69 --- /dev/null +++ b/tests/Behat/Mocker/PayPlugApiMocker.php @@ -0,0 +1,121 @@ +mocker = $mocker; + } + + public function mockApiRefundedPayment(callable $action): void + { + $mock = $this->mocker->mockService('payplug_sylius_payplug_plugin.api_client.payplug', PayPlugApiClientInterface::class); + + $mock + ->shouldReceive('initialise') + ; + + $mock + ->shouldReceive('refundPayment') + ->andReturn(\Mockery::mock('refund', Refund::class)) + ; + + $action(); + + $this->mocker->unmockAll(); + } + + public function mockApiCreatePayment(callable $action): void + { + $mock = $this->mocker->mockService('payplug_sylius_payplug_plugin.api_client.payplug', PayPlugApiClientInterface::class); + + $mock + ->shouldReceive('initialise') + ; + + $payment = \Mockery::mock('payment', Payment::class); + + $payment->id = 1; + $payment->hosted_payment = (object) [ + 'payment_url' => 'test', + ]; + + $mock + ->shouldReceive('createPayment') + ->andReturn($payment) + ; + + $action(); + + $this->mocker->unmockAll(); + } + + public function mockApiSuccessfulPayment(callable $action): void + { + $mock = $this->mocker->mockService('payplug_sylius_payplug_plugin.api_client.payplug', PayPlugApiClientInterface::class); + + $mock + ->shouldReceive('initialise') + ; + + $payment = \Mockery::mock('payment', Payment::class); + + $payment->is_paid = true; + + $mock + ->shouldReceive('treat') + ->andReturn($payment) + ; + + $action(); + + $this->mocker->unmockAll(); + } + + public function mockApiFailedPayment(callable $action): void + { + $mock = $this->mocker->mockService('payplug_sylius_payplug_plugin.api_client.payplug', PayPlugApiClientInterface::class); + + $mock + ->shouldReceive('initialise') + ; + + $payment = \Mockery::mock('payment', Payment::class); + + $payment->is_paid = false; + + $mock + ->shouldReceive('treat') + ->andReturn($payment) + ; + + $action(); + + $this->mocker->unmockAll(); + } + + public function mockApiCancelledPayment(callable $action): void + { + $mock = $this->mocker->mockService('payplug_sylius_payplug_plugin.api_client.payplug', PayPlugApiClientInterface::class); + + $mock + ->shouldReceive('initialise') + ; + + $action(); + + $this->mocker->unmockAll(); + } +} diff --git a/tests/Behat/Page/Admin/PaymentMethod/CreatePage.php b/tests/Behat/Page/Admin/PaymentMethod/CreatePage.php new file mode 100644 index 00000000..0842a83d --- /dev/null +++ b/tests/Behat/Page/Admin/PaymentMethod/CreatePage.php @@ -0,0 +1,35 @@ +getDocument()->fillField('Secret key', $secretKey); + } + + public function containsErrorWithMessage(string $message, bool $strict = true): bool + { + $validationMessageElements = $this->getDocument()->findAll('css', '.sylius-validation-error'); + $result = false; + + /** @var NodeElement $validationMessageElement */ + foreach ($validationMessageElements as $validationMessageElement) { + if (true === $strict && $message === $validationMessageElement->getText()) { + return true; + } + + if (false === $strict && strstr($validationMessageElement->getText(), $message)) { + return true; + } + } + + return $result; + } +} diff --git a/tests/Behat/Page/Admin/PaymentMethod/CreatePageInterface.php b/tests/Behat/Page/Admin/PaymentMethod/CreatePageInterface.php new file mode 100644 index 00000000..9ce9e821 --- /dev/null +++ b/tests/Behat/Page/Admin/PaymentMethod/CreatePageInterface.php @@ -0,0 +1,14 @@ +securityTokenRepository = $securityTokenRepository; + $this->client = $client; + } + + public function capture(array $parameters = []): void + { + $captureToken = $this->findToken(); + + $url = $captureToken->getTargetUrl(); + + if (count($parameters) > 0) { + $url .= '?' . http_build_query($parameters); + } + + $this->getDriver()->visit($url); + } + + public function notify(array $postData): void + { + $notifyToken = $this->findToken('notify'); + + $this->client->request('POST', $notifyToken->getTargetUrl(), $postData); + } + + protected function getUrl(array $urlParameters = []): string + { + return ''; + } + + private function findToken(string $type = 'capture'): TokenInterface + { + $tokens = []; + /** @var TokenInterface $token */ + foreach ($this->securityTokenRepository->findAll() as $token) { + if (strpos($token->getTargetUrl(), $type)) { + $tokens[] = $token; + } + } + if (count($tokens) > 0) { + return end($tokens); + } + throw new \RuntimeException('Cannot find capture token, check if you are after proper checkout steps'); + } +} diff --git a/tests/Behat/Page/Shop/Payum/PaymentPageInterface.php b/tests/Behat/Page/Shop/Payum/PaymentPageInterface.php new file mode 100644 index 00000000..b57516a3 --- /dev/null +++ b/tests/Behat/Page/Shop/Payum/PaymentPageInterface.php @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + + + diff --git a/tests/Behat/Resources/services/contexts.xml b/tests/Behat/Resources/services/contexts.xml new file mode 100644 index 00000000..dfce38c9 --- /dev/null +++ b/tests/Behat/Resources/services/contexts.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/tests/Behat/Resources/services/contexts/setup.xml b/tests/Behat/Resources/services/contexts/setup.xml new file mode 100644 index 00000000..d135a3d3 --- /dev/null +++ b/tests/Behat/Resources/services/contexts/setup.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/Behat/Resources/services/contexts/ui.xml b/tests/Behat/Resources/services/contexts/ui.xml new file mode 100644 index 00000000..52d1ca12 --- /dev/null +++ b/tests/Behat/Resources/services/contexts/ui.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/Behat/Resources/services/pages.xml b/tests/Behat/Resources/services/pages.xml new file mode 100644 index 00000000..269c0457 --- /dev/null +++ b/tests/Behat/Resources/services/pages.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/tests/Behat/Resources/services/pages/admin.xml b/tests/Behat/Resources/services/pages/admin.xml new file mode 100644 index 00000000..80fa0f1e --- /dev/null +++ b/tests/Behat/Resources/services/pages/admin.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/tests/Behat/Resources/services/pages/admin/payment_method.xml b/tests/Behat/Resources/services/pages/admin/payment_method.xml new file mode 100644 index 00000000..a2326fdc --- /dev/null +++ b/tests/Behat/Resources/services/pages/admin/payment_method.xml @@ -0,0 +1,9 @@ + + + + + + sylius_admin_payment_method_create + + + diff --git a/tests/Behat/Resources/services/pages/shop.xml b/tests/Behat/Resources/services/pages/shop.xml new file mode 100644 index 00000000..fb188b28 --- /dev/null +++ b/tests/Behat/Resources/services/pages/shop.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/tests/Behat/Resources/services/pages/shop/payum.xml b/tests/Behat/Resources/services/pages/shop/payum.xml new file mode 100644 index 00000000..e467f7b0 --- /dev/null +++ b/tests/Behat/Resources/services/pages/shop/payum.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/tests/Behat/Resources/suites.yml b/tests/Behat/Resources/suites.yml new file mode 100644 index 00000000..23b7245c --- /dev/null +++ b/tests/Behat/Resources/suites.yml @@ -0,0 +1,4 @@ +imports: + - suites/ui/refunding_payplug_payment.yml + - suites/ui/managing_payment_method_payplug.yml + - suites/ui/paying_with_payplug_for_order.yml diff --git a/tests/Behat/Resources/suites/ui/managing_payment_method_payplug.yml b/tests/Behat/Resources/suites/ui/managing_payment_method_payplug.yml new file mode 100644 index 00000000..fbcc3d8a --- /dev/null +++ b/tests/Behat/Resources/suites/ui/managing_payment_method_payplug.yml @@ -0,0 +1,32 @@ +default: + suites: + ui_managing_payment_method_payplug: + contexts: + - sylius.behat.context.hook.doctrine_orm + + - sylius.behat.context.transform.address + - sylius.behat.context.transform.customer + - sylius.behat.context.transform.locale + - sylius.behat.context.transform.payment + - sylius.behat.context.transform.product + - sylius.behat.context.transform.shared_storage + - sylius.behat.context.transform.shipping_method + + - sylius.behat.context.setup.channel + - sylius.behat.context.setup.currency + - sylius.behat.context.setup.locale + - sylius.behat.context.setup.order + - sylius.behat.context.setup.payment + - sylius.behat.context.setup.product + - sylius.behat.context.setup.admin_security + - sylius.behat.context.setup.shipping + - sylius.behat.context.setup.user + - sylius.behat.context.setup.zone + + - sylius.behat.context.ui.admin.managing_payment_methods + - sylius.behat.context.ui.admin.notification + - sylius.behat.context.ui.shop.locale + + - payplug_sylius_payplug_plugin.behat.context.ui.admin.managing_payment_method_payplug + filters: + tags: "@managing_payplug_payment_method && @ui" diff --git a/tests/Behat/Resources/suites/ui/paying_with_payplug_for_order.yml b/tests/Behat/Resources/suites/ui/paying_with_payplug_for_order.yml new file mode 100644 index 00000000..98448466 --- /dev/null +++ b/tests/Behat/Resources/suites/ui/paying_with_payplug_for_order.yml @@ -0,0 +1,46 @@ +default: + suites: + ui_paying_with_payplug_for_order: + contexts: + - sylius.behat.context.hook.doctrine_orm + + - sylius.behat.context.transform.address + - sylius.behat.context.transform.customer + - sylius.behat.context.transform.lexical + - sylius.behat.context.transform.locale + - sylius.behat.context.transform.order + - sylius.behat.context.transform.payment + - sylius.behat.context.transform.product + - sylius.behat.context.transform.shared_storage + - sylius.behat.context.transform.shipping_method + - sylius.behat.context.transform.tax_category + - sylius.behat.context.transform.tax_rate + - sylius.behat.context.transform.zone + + - sylius.behat.context.setup.channel + - sylius.behat.context.setup.currency + - sylius.behat.context.setup.geographical + - sylius.behat.context.setup.locale + - sylius.behat.context.setup.order + - sylius.behat.context.setup.payment + - sylius.behat.context.setup.product + - sylius.behat.context.setup.shipping + - sylius.behat.context.setup.shop_security + - sylius.behat.context.setup.taxation + - sylius.behat.context.setup.user + + - payplug_sylius_payplug_plugin.behat.context.setup.payplug + + - sylius.behat.context.ui.paypal + - sylius.behat.context.ui.shop.cart + - sylius.behat.context.ui.shop.checkout + - sylius.behat.context.ui.shop.checkout.addressing + - sylius.behat.context.ui.shop.checkout.complete + - sylius.behat.context.ui.shop.checkout.order_details + - sylius.behat.context.ui.shop.checkout.payment + - sylius.behat.context.ui.shop.checkout.shipping + - sylius.behat.context.ui.shop.checkout.thank_you + + - payplug_sylius_payplug_plugin.behat.context.ui.shop.checkout + filters: + tags: "@paying_with_payplug_for_order && @ui" diff --git a/tests/Behat/Resources/suites/ui/refunding_payplug_payment.yml b/tests/Behat/Resources/suites/ui/refunding_payplug_payment.yml new file mode 100644 index 00000000..39603892 --- /dev/null +++ b/tests/Behat/Resources/suites/ui/refunding_payplug_payment.yml @@ -0,0 +1,58 @@ +default: + suites: + ui_refunding_payplug_payment: + contexts: + - sylius.behat.context.hook.doctrine_orm + - sylius.behat.context.hook.email_spool + + - sylius.behat.context.setup.channel + - sylius.behat.context.setup.currency + - sylius.behat.context.setup.customer + - sylius.behat.context.setup.geographical + - sylius.behat.context.setup.order + - sylius.behat.context.setup.payment + - sylius.behat.context.setup.product + - sylius.behat.context.setup.product_taxon + - sylius.behat.context.setup.promotion + - sylius.behat.context.setup.admin_security + - sylius.behat.context.setup.shop_security + - sylius.behat.context.setup.shipping + - sylius.behat.context.setup.taxation + - sylius.behat.context.setup.taxonomy + - sylius.behat.context.setup.zone + - sylius.behat.context.setup.admin_user + - sylius.behat.context.setup.user + + - payplug_sylius_payplug_plugin.behat.context.setup.payplug + - payplug_sylius_payplug_plugin.behat.context.setup.order + + - sylius.behat.context.transform.address + - sylius.behat.context.transform.channel + - sylius.behat.context.transform.country + - sylius.behat.context.transform.currency + - sylius.behat.context.transform.customer + - sylius.behat.context.transform.lexical + - sylius.behat.context.transform.order + - sylius.behat.context.transform.payment + - sylius.behat.context.transform.product + - sylius.behat.context.transform.product_variant + - sylius.behat.context.transform.promotion + - sylius.behat.context.transform.shipping_method + - sylius.behat.context.transform.tax_category + - sylius.behat.context.transform.taxon + - sylius.behat.context.transform.zone + - sylius.behat.context.transform.shared_storage + + - sylius.behat.context.ui.admin.managing_orders + - sylius.behat.context.ui.admin.notification + - sylius.behat.context.ui.channel + - sylius.behat.context.ui.email + - sylius.behat.context.ui.shop.cart + - sylius.behat.context.ui.shop.checkout + - sylius.behat.context.ui.shop.checkout.addressing + - sylius.behat.context.ui.shop.checkout.complete + - sylius.behat.context.ui.shop.currency + + - payplug_sylius_payplug_plugin.behat.context.ui.admin.refund + filters: + tags: "@refunding_payplug_payment && @ui" diff --git a/yarn-error.log b/yarn-error.log new file mode 100644 index 00000000..6281d300 --- /dev/null +++ b/yarn-error.log @@ -0,0 +1,26 @@ +Arguments: + /usr/local/Cellar/node/10.10.0/bin/node /usr/local/Cellar/yarn/1.9.4/libexec/bin/yarn.js install + +PATH: + /usr/local/opt/mysql@5.7/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin + +Yarn version: + 1.9.4 + +Node version: + 10.10.0 + +Platform: + darwin x64 + +Trace: + Error: EEXIST: file already exists, mkdir '/Users/patryk/Documents/www/SyliusPayPlugPlugin/node_modules' + +npm manifest: + No manifest + +yarn manifest: + No manifest + +Lockfile: + No lockfile