Resolve conflicts between 2.0 and payment-request

This commit is contained in:
Grzegorz Sadowski 2024-10-07 11:21:26 +02:00
commit 34208c1900
No known key found for this signature in database
GPG key ID: EF87FF4E6E3BD364
378 changed files with 9296 additions and 710 deletions

2
.env
View file

@ -36,6 +36,8 @@ SYLIUS_MESSENGER_TRANSPORT_MAIN_DSN=doctrine://default
SYLIUS_MESSENGER_TRANSPORT_MAIN_FAILED_DSN=doctrine://default?queue_name=main_failed
SYLIUS_MESSENGER_TRANSPORT_CATALOG_PROMOTION_REMOVAL_DSN=doctrine://default?queue_name=catalog_promotion_removal
SYLIUS_MESSENGER_TRANSPORT_CATALOG_PROMOTION_REMOVAL_FAILED_DSN=doctrine://default?queue_name=catalog_promotion_removal_failed
SYLIUS_MESSENGER_TRANSPORT_PAYMENT_REQUEST_DSN=doctrine://default?queue_name=payment_request
SYLIUS_MESSENGER_TRANSPORT_PAYMENT_REQUEST_FAILED_DSN=doctrine://default?queue_name=payment_request_failed
###< symfony/messenger ###
###> symfony/mailer ###

View file

@ -13,6 +13,8 @@ SYLIUS_MESSENGER_TRANSPORT_MAIN_DSN=sync://
SYLIUS_MESSENGER_TRANSPORT_MAIN_FAILED_DSN=sync://
SYLIUS_MESSENGER_TRANSPORT_CATALOG_PROMOTION_REMOVAL_DSN=sync://
SYLIUS_MESSENGER_TRANSPORT_CATALOG_PROMOTION_REMOVAL_FAILED_DSN=sync://
SYLIUS_MESSENGER_TRANSPORT_PAYMENT_REQUEST_DSN=sync://
SYLIUS_MESSENGER_TRANSPORT_PAYMENT_REQUEST_FAILED_DSN=sync://
###< symfony/messenger ###
MAILER_DSN=null://null

View file

@ -14,6 +14,8 @@ SYLIUS_MESSENGER_TRANSPORT_MAIN_DSN=sync://
SYLIUS_MESSENGER_TRANSPORT_MAIN_FAILED_DSN=sync://
SYLIUS_MESSENGER_TRANSPORT_CATALOG_PROMOTION_REMOVAL_DSN=sync://
SYLIUS_MESSENGER_TRANSPORT_CATALOG_PROMOTION_REMOVAL_FAILED_DSN=sync://
SYLIUS_MESSENGER_TRANSPORT_PAYMENT_REQUEST_DSN=sync://
SYLIUS_MESSENGER_TRANSPORT_PAYMENT_REQUEST_FAILED_DSN=sync://
###< symfony/messenger ###
MAILER_DSN=null://null

21
.env.test_cached_payum Normal file
View file

@ -0,0 +1,21 @@
APP_DEBUG=0
APP_SECRET='s$cretf0rt3st'
KERNEL_CLASS='App\Kernel'
###> lexik/jwt-authentication-bundle ###
JWT_SECRET_KEY=%kernel.project_dir%/config/jwt/private-test.pem
JWT_PUBLIC_KEY=%kernel.project_dir%/config/jwt/public-test.pem
JWT_PASSPHRASE=ALL_THAT_IS_GOLD_DOES_NOT_GLITTER_NOT_ALL_THOSE_WHO_WANDER_ARE_LOST
###< lexik/jwt-authentication-bundle ###
###> symfony/messenger ###
SYLIUS_MESSENGER_TRANSPORT_MAIN_DSN=sync://
SYLIUS_MESSENGER_TRANSPORT_MAIN_FAILED_DSN=sync://
SYLIUS_MESSENGER_TRANSPORT_CATALOG_PROMOTION_REMOVAL_DSN=sync://
SYLIUS_MESSENGER_TRANSPORT_CATALOG_PROMOTION_REMOVAL_FAILED_DSN=sync://
SYLIUS_MESSENGER_TRANSPORT_PAYMENT_REQUEST_DSN=sync://
SYLIUS_MESSENGER_TRANSPORT_PAYMENT_REQUEST_FAILED_DSN=sync://
###< symfony/messenger ###
MAILER_DSN=null://null

View file

@ -44,14 +44,14 @@ jobs:
behat-no-js:
needs: get-matrix
runs-on: ubuntu-latest
name: "Non-JS, PHP ${{ matrix.php }}, Symfony ${{ matrix.symfony }}, MySQL ${{ matrix.mysql }}, Twig ${{ matrix.twig }}, API Platform ${{ matrix.api-platform }}"
name: "Non-JS, PHP ${{ matrix.php }}, Symfony ${{ matrix.symfony }} (${{ matrix.env }}), MySQL ${{ matrix.mysql }}, Twig ${{ matrix.twig }}, API Platform ${{ matrix.api-platform }}"
timeout-minutes: 45
strategy:
fail-fast: false
matrix: ${{ fromJson(needs.get-matrix.outputs.matrix) }}
env:
APP_ENV: test_cached
APP_ENV: ${{ matrix.env }}
DATABASE_URL: "mysql://root:root@127.0.0.1/sylius?charset=utf8mb4&serverVersion=${{ matrix.mysql }}"
steps:
@ -132,14 +132,14 @@ jobs:
behat-ui-js-chromedriver:
needs: get-matrix
runs-on: ubuntu-latest
name: "JS with Chromedriver, PHP ${{ matrix.php }}, Symfony ${{ matrix.symfony }}, MySQL ${{ matrix.mysql }}, Twig ${{ matrix.twig }}, API Platform ${{ matrix.api-platform }}"
name: "JS with Chromedriver, PHP ${{ matrix.php }}, Symfony ${{ matrix.symfony }} (${{ matrix.env }}), MySQL ${{ matrix.mysql }}, Twig ${{ matrix.twig }}, API Platform ${{ matrix.api-platform }}"
timeout-minutes: 45
strategy:
fail-fast: false
matrix: ${{ fromJson(needs.get-matrix.outputs.matrix) }}
env:
APP_ENV: test_cached
APP_ENV: ${{ matrix.env }}
DATABASE_URL: "mysql://root:root@127.0.0.1/sylius?charset=utf8mb4&serverVersion=${{ matrix.mysql }}"
steps:
@ -210,14 +210,14 @@ jobs:
behat-ui-js-panther:
needs: get-matrix
runs-on: ubuntu-latest
name: "JS with Panther, PHP ${{ matrix.php }}, Symfony ${{ matrix.symfony }}, MySQL ${{ matrix.mysql }}, Twig ${{ matrix.twig }}, API Platform ${{ matrix.api-platform }}"
name: "JS with Panther, PHP ${{ matrix.php }}, Symfony ${{ matrix.symfony }} (${{ matrix.env }}), MySQL ${{ matrix.mysql }}, Twig ${{ matrix.twig }}, API Platform ${{ matrix.api-platform }}"
timeout-minutes: 45
strategy:
fail-fast: false
matrix: ${{ fromJson(needs.get-matrix.outputs.matrix) }}
env:
APP_ENV: test_cached
APP_ENV: ${{ matrix.env }}
DATABASE_URL: "mysql://root:root@127.0.0.1/sylius?charset=utf8mb4&serverVersion=${{ matrix.mysql }}"
steps:

View file

@ -39,14 +39,24 @@
"symfony": "^6.4",
"api-platform": "^3.3",
"mysql": "8.0",
"twig": "^3.3"
"twig": "^3.3",
"env": "test_cached"
},
{
"php": "8.3",
"symfony": "^7.1",
"api-platform": "^3.3",
"mysql": "8.4",
"twig": "^3.3"
"twig": "^3.3",
"env": "test_cached"
},
{
"php": "8.3",
"symfony": "^6.4",
"api-platform": "^3.3",
"mysql": "8.4",
"twig": "^3.3",
"env": "test_cached_payum"
}
]
},
@ -101,7 +111,18 @@
"symfony": ["^6.4", "^7.1"],
"api-platform": ["^3.3"],
"mysql": ["8.0", "8.4"],
"twig": ["^3.3"]
"twig": ["^3.3"],
"env": "test_cached",
"include": [
{
"php": "8.3",
"symfony": "^6.4",
"api-platform": "^2.7.10",
"mysql": "8.4",
"twig": "^3.3",
"env": "test_cached_payum"
}
]
},
"e2e-pgsql": {
"php": ["8.2", "8.3"],

View file

@ -6,6 +6,9 @@
* The `sylius_default.bus` and `sylius_event.bus` configuration options were removed. Use `sylius.command_bus` and
`sylius.event_bus` for commands and events respectively.
* SyliusStateMachineAbstraction:
* The `sylius_state_machine_abstraction.default_adapter` option has been changed from `winzou_state_machine` to `symfony_workflow`.
## Codebase
* Doctrine MongoDB and PHPCR is no longer supported in ResourceBundle and GridBundle:
@ -369,6 +372,11 @@ https://symfony.com/doc/current/security/passwords.html#configure-a-new-hasher-u
Note:
If your app never changed the hasher name configuration, you don't need to configure this "migrate_from" configuration.
## Directory structure
* The Winzou state machine configuration file `state_machine.yml` has been moved from `@SyliusPaymentBundle/Resources/config/app` to `@SyliusPaymentBundle/Resources/config/app/state_machine` and renamed to `sylius_payment.yaml`.
* The Symfony workflow configuration file `state_machine.yaml` has been moved from `@SyliusPaymentBundle/Resources/config/workflow` to `@SyliusPaymentBundle/Resources/config/app/workflow` and renamed to `sylius_payment.yaml`.
## Frontend
* `use_webpack` option was removed from the `sylius_ui` configuration, and the Webpack has become the only module
@ -385,3 +393,13 @@ If your app never changed the hasher name configuration, you don't need to confi
if you're running your shop in headless mode.
* Channel's `themeName` form field existence is made optional and depends on `ShopBundle` presence.
* The `Sylius\Bundle\CoreBundle\Theme\ChannelBasedThemeContext` has been moved to the `Sylius\Bundle\ShopBundle\Theme\ChannelBasedThemeContext`.
## Routing
* Routing for the `sylius_shop_payum` has been moved from the `SyliusShopBundle` to the `SyliusPayumBundle`:
```yaml
sylius_shop_payum:
- resource: "@SyliusShopBundle/Resources/config/routing/payum.yml"
+ resource: "@SyliusPayumBundle/Resources/config/routing/integrations/sylius_shop.yaml"
```

View file

@ -66,6 +66,7 @@
"Payum\\Core\\Model\\GatewayConfig",
"Payum\\Core\\Model\\GatewayConfigInterface",
"Payum\\Core\\Model\\Identity",
"Payum\\Core\\Model\\ModelAggregateInterface",
"Payum\\Core\\Model\\Payment",
"Payum\\Core\\Model\\PaymentInterface",
"Payum\\Core\\Payum",
@ -74,11 +75,15 @@
"Payum\\Core\\Request\\Capture",
"Payum\\Core\\Request\\Convert",
"Payum\\Core\\Request\\Generic",
"Payum\\Core\\Request\\GetHttpRequest",
"Payum\\Core\\Request\\GetStatusInterface",
"Payum\\Core\\Request\\Notify",
"Payum\\Core\\Request\\RenderTemplate",
"Payum\\Core\\Security\\CryptedInterface",
"Payum\\Core\\Security\\CypherInterface",
"Payum\\Core\\Security\\GenericTokenFactoryInterface",
"Payum\\Core\\Security\\HttpRequestVerifierInterface",
"Payum\\Core\\Security\\TokenAggregateInterface",
"Payum\\Core\\Security\\TokenInterface",
"Payum\\Core\\Security\\Util\\Random",
"Payum\\Core\\Storage\\AbstractStorage",

View file

@ -128,6 +128,7 @@
"symfony/translation": "^6.4.0 || ^7.1",
"symfony/translation-contracts": "^3.3",
"symfony/twig-bundle": "^6.4.0 || ^7.1",
"symfony/uid": "^6.4.0 || ^7.1",
"symfony/ux-autocomplete": "^2.17",
"symfony/ux-live-component": "^2.17",
"symfony/ux-twig-component": "^2.17",

View file

@ -47,12 +47,12 @@ return [
Sylius\Bundle\ShopBundle\SyliusShopBundle::class => ['all' => true],
ApiPlatform\Symfony\Bundle\ApiPlatformBundle::class => ['all' => true],
Sylius\Bundle\ApiBundle\SyliusApiBundle::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],
Fidry\AliceDataFixtures\Bridge\Symfony\FidryAliceDataFixturesBundle::class => ['dev' => true, 'test' => true, 'test_cached' => true],
Nelmio\Alice\Bridge\Symfony\NelmioAliceBundle::class => ['dev' => true, 'test' => true, 'test_cached' => true],
FriendsOfBehat\SymfonyExtension\Bundle\FriendsOfBehatSymfonyExtensionBundle::class => ['test' => true, 'test_cached' => true],
Sylius\Behat\Application\SyliusTestPlugin\SyliusTestPlugin::class => ['test' => true, 'test_cached' => true],
Symfony\Bundle\DebugBundle\DebugBundle::class => ['dev' => true, 'test' => true, 'test_cached' => true, 'test_cached_payum' => true],
Symfony\Bundle\WebProfilerBundle\WebProfilerBundle::class => ['dev' => true, 'test' => true, 'test_cached' => true, 'test_cached_payum' => true],
Fidry\AliceDataFixtures\Bridge\Symfony\FidryAliceDataFixturesBundle::class => ['dev' => true, 'test' => true, 'test_cached' => true, 'test_cached_payum' => true],
Nelmio\Alice\Bridge\Symfony\NelmioAliceBundle::class => ['dev' => true, 'test' => true, 'test_cached' => true, 'test_cached_payum' => true],
FriendsOfBehat\SymfonyExtension\Bundle\FriendsOfBehatSymfonyExtensionBundle::class => ['test' => true, 'test_cached' => true, 'test_cached_payum' => true],
Sylius\Behat\Application\SyliusTestPlugin\SyliusTestPlugin::class => ['test' => true, 'test_cached' => true, 'test_cached_payum' => true],
Lexik\Bundle\JWTAuthenticationBundle\LexikJWTAuthenticationBundle::class => ['all' => true],
SyliusLabs\DoctrineMigrationsExtraBundle\SyliusLabsDoctrineMigrationsExtraBundle::class => ['all' => true],
Symfony\WebpackEncoreBundle\WebpackEncoreBundle::class => ['all' => true],

View file

@ -1,12 +1,9 @@
imports:
- { resource: "@SyliusCoreBundle/Resources/config/app/config.yml" }
- { resource: "@SyliusPayumBundle/Resources/config/app/config.yaml" }
- { resource: "@SyliusShopBundle/Resources/config/app/config.yml" }
- { resource: "@SyliusApiBundle/Resources/config/app/config.yaml" }
- { resource: "@SyliusAdminBundle/Resources/config/app/config.yml" }
- { resource: "../parameters.yaml" }
parameters:

View file

@ -13,5 +13,6 @@ sylius_state_machine_abstraction:
sylius_order_payment: '%test_sylius_state_machine_adapter%'
sylius_order_shipping: '%test_sylius_state_machine_adapter%'
sylius_payment: '%test_sylius_state_machine_adapter%'
sylius_payment_request: '%test_sylius_state_machine_adapter%'
sylius_product_review: '%test_sylius_state_machine_adapter%'
sylius_shipment: '%test_sylius_state_machine_adapter%'

View file

@ -0,0 +1,2 @@
imports:
- { resource: "../test_cached/_sylius.yaml" }

View file

@ -0,0 +1,2 @@
imports:
- { resource: "../test_cached/doctrine.yaml" }

View file

@ -0,0 +1,2 @@
imports:
- { resource: "../test_cached/fidry_alice_data_fixtures.yaml" }

View file

@ -0,0 +1,2 @@
imports:
- { resource: "../test_cached/framework.yaml" }

View file

@ -0,0 +1,2 @@
imports:
- { resource: "../test_cached/mailer.yaml" }

View file

@ -0,0 +1,2 @@
imports:
- { resource: "../test_cached/monolog.yaml" }

View file

@ -0,0 +1,2 @@
imports:
- { resource: ../dev/nelmio_alice.yaml }

View file

@ -0,0 +1,2 @@
imports:
- { resource: "../test_cached/security.yaml" }

View file

@ -0,0 +1,2 @@
imports:
- { resource: "../test_cached/sylius_channel.yaml" }

View file

@ -0,0 +1,2 @@
imports:
- { resource: "../test_cached/sylius_theme.yaml" }

View file

@ -0,0 +1,2 @@
imports:
- { resource: "../test_cached/sylius_uploader.yaml" }

View file

@ -0,0 +1,2 @@
imports:
- { resource: "../test_cached/twig.yaml" }

View file

@ -0,0 +1,2 @@
imports:
- { resource: "../test_cached/web_profiler.yaml" }

View file

@ -0,0 +1,2 @@
imports:
- { resource: "../test_cached/webpack_encore.yaml" }

4
config/packages/uid.yaml Normal file
View file

@ -0,0 +1,4 @@
framework:
uid:
default_uuid_version: 6
time_based_uuid_version: 6

View file

@ -5,7 +5,7 @@ sylius_shop:
_locale: ^[A-Za-z]{2,4}(_([A-Za-z]{4}|[0-9]{3}))?(_([A-Za-z]{2}|[0-9]{3}))?$
sylius_shop_payum:
resource: "@SyliusShopBundle/Resources/config/routing/payum.yml"
resource: "@SyliusPayumBundle/Resources/config/routing/integrations/sylius_shop.yaml"
sylius_shop_default_locale:
path: /

View file

@ -0,0 +1,5 @@
sylius_test_plugin_main:
path: /test/main
controller: Symfony\Bundle\FrameworkBundle\Controller\TemplateController::templateAction
defaults:
template: "@SyliusTestPlugin/main.html.twig"

View file

@ -0,0 +1,14 @@
imports:
- { resource: "services_test_cached.yaml" }
services:
##
# Prioritize Payum by tagging services with a higher priority (or lower for tagged_iterator).
##
'sylius.payum.payment_request.command_provider.offline':
class: Sylius\Bundle\PaymentBundle\CommandProvider\ActionsCommandProvider
arguments:
- !tagged_locator { tag: 'sylius.payum.payment_request.command_provider.offline', index_by: 'action' }
tags:
- { name: 'sylius.payment_request.command_provider', gateway-factory: 'offline', priority: -100 }

View file

@ -0,0 +1,25 @@
@managing_payments
Feature: Browsing payment requests
In order to have an overview of all payment requests of a specific payment
As an Administrator
I want to browse all payment requests of a payment
Background:
Given the store operates on a single channel in "United States"
And the store has a product "PHP T-Shirt"
And the store ships everywhere for Free
And the store allows paying with "Cash on Delivery"
And there is an "#00000001" order with "PHP T-Shirt" product
And the payment request action "authorize" has been executed for order "#00000001" with the payment method "Cash on Delivery"
And the payment request action "capture" has been executed for order "#00000001" with the payment method "Cash on Delivery"
And the payment request action "sync" has been executed for order "#00000001" with the payment method "Cash on Delivery"
And I am logged in as an administrator
@api @ui
Scenario: Browsing payment requests of a payment
When I browse payments
And I want to view the payment requests of the first payment
Then there should be 3 payment requests on the list
And it should be the payment request with action "authorize"
And it should be the payment request with action "capture"
And it should be the payment request with action "sync"

View file

@ -0,0 +1,46 @@
@managing_payments
Feature: Filtering payment requests
In order to see specific payment requests
As an Administrator
I want to be able to filter payment requests on the list
Background:
Given the store operates on a single channel in "United States"
And the store has a product "PHP T-Shirt"
And the store ships everywhere for Free
And the store allows paying with "Cash on Delivery"
And the store allows paying with "Credit Card"
And there is an "#00000001" order with "PHP T-Shirt" product
And there is an "#00000002" order with "PHP T-Shirt" product
And the payment request action "authorize" has been executed for order "#00000001" with the payment method "Credit Card"
And the payment request action "authorize" has been executed for order "#00000001" with the payment method "Cash on Delivery"
And the payment request action "capture" has been executed for order "#00000001" with the payment method "Cash on Delivery"
And the payment request action "sync" has been executed for order "#00000001" with the payment method "Cash on Delivery"
And the payment request action "capture" has been executed for order "#00000002" with the payment method "Cash on Delivery"
And I am logged in as an administrator
@api @ui
Scenario: Filtering payment requests by action
When I browse payment requests of an order "#00000001"
And I filter by the "capture" action
Then there should be 1 payment request on the list
And it should be the payment request with action "capture"
@api @ui @mink:chromedriver
Scenario: Filtering payment requests by payment method
When I browse payment requests of an order "#00000001"
And I filter by the "Credit Card" payment method
Then there should be 1 payment request on the list
And it should be the payment request with payment method "Credit Card"
@api @ui
Scenario: Filtering payment requests by state "New"
When I browse payment requests of an order "#00000001"
And I filter by the "new" state
Then there should be 4 payment requests on the list
@api @ui
Scenario: Filtering payment requests by state "Completed"
When I browse payment requests of an order "#00000001"
And I filter by the "completed" state
Then there should be 0 payment request on the list

View file

@ -0,0 +1,23 @@
@managing_payments
Feature: Seeing payment request's details
In order to have an overview of one of payment requests
As an Administrator
I want to be able to see its details
Background:
Given the store operates on a single channel in "United States"
And the store has a product "PHP T-Shirt"
And the store ships everywhere for Free
And the store allows paying with "Cash on Delivery"
And there is an "#00000001" order with "PHP T-Shirt" product
And the payment request action "authorize" has been executed for order "#00000001" with the payment method "Cash on Delivery"
And I am logged in as an administrator
@api @ui
Scenario: Seeing payment request's details
When I view details of the payment request for the "#00000001" order
Then its method should be "Cash on Delivery"
And its action should be "Authorize"
And its state should be "New"
And its payload should has empty value
And its response data should has empty value

View file

@ -13,7 +13,7 @@ Feature: Checking taxons of a product
And the product "T-Shirt" belongs to taxon "T-Shirts"
And I am logged in as an administrator
@ui @no-api @mink:chromedriver
@no-api @ui @mink:chromedriver
Scenario: Checking all taxons
When I want to modify the "Shirt" product
And I check all taxons
@ -21,7 +21,7 @@ Feature: Checking taxons of a product
Then the product "Shirt" should have the "Clothes" taxon
And the product "Shirt" should have the "T-Shirts" taxon
@ui @no-api @mink:chromedriver
@no-api @ui @mink:chromedriver
Scenario: Unchecking all taxons
When I want to modify the "T-Shirt" product
And I uncheck all taxons
@ -29,7 +29,7 @@ Feature: Checking taxons of a product
Then the product "Shirt" should not have the "Clothes" taxon
And the product "Shirt" should not have the "T-Shirts" taxon
@ui @no-api @mink:chromedriver
@no-api @ui @mink:chromedriver
Scenario: Checking all filtered taxons
When I want to modify the "Shirt" product
And I filter taxons by "T-Shirts"
@ -38,7 +38,7 @@ Feature: Checking taxons of a product
Then the product "Shirt" should have the "T-Shirts" taxon
But the product "Shirt" should not have the "Clothes" taxon
@ui @no-api @mink:chromedriver
@no-api @ui @mink:chromedriver
Scenario: Unchecking all filtered taxons
When I want to modify the "T-Shirt" product
And I filter taxons by "T-Shirts"

View file

@ -10,7 +10,7 @@ Feature: Filtering product taxons
And the store has a "Shirt" configurable product
And I am logged in as an administrator
@ui @no-api @mink:chromedriver
@no-api @ui @mink:chromedriver
Scenario: Filtering product taxons
When I want to modify the "Shirt" product
And I filter taxons by "T-Shirts"

View file

@ -80,7 +80,7 @@ Feature: Sorting listed products from a taxon by position
And the 7th product on this page should be named "Pug Master"
And this product should be at position 16
@no-api @ui @mink:chromedriver
@no-api @tod-ui @mink:chromedriver
Scenario: Setting two products to the positions overflowing the max available position on the non-last page
When I am browsing the 1st page of products from "Soft Toys" taxon
And I set the position of "Old pug" to "25"

View file

@ -0,0 +1,29 @@
@paying_for_order
Feature: Cancelling payment request when payment method is changed
In order to pay with the correct payment method
As an Administrator
I want the customer's payment requests with the previous payment method to be cancelled
Background:
Given the store operates on a single channel in "United States"
And the store has a product "PHP T-Shirt"
And the store ships everywhere for Free
And the store allows paying with "Cash on Delivery"
And the store also allows paying with "Bank Transfer"
And I am a logged in customer
And I placed an order "#00000001"
And I bought a single "PHP T-Shirt"
And I addressed it to "Lucifer Morningstar", "Seaside Fwy", "90802" "Los Angeles" in the "United States" with identical billing address
And I chose "Free" shipping method with "Cash on Delivery" payment
And there is a "new" "authorize" payment request for order "#00000001" using the "Cash on Delivery" payment method
And there is also a "processing" "status" payment request for order "#00000001" using the "Cash on Delivery" payment method
And there is also a "completed" "capture" payment request for order "#00000001" using the "Cash on Delivery" payment method
And there is an administrator "sylius@example.com" identified by "sylius"
@api @ui
Scenario: Cancelling only not finalized payment requests when the payment method has changed
When I browse my orders
And I change my payment method to "Bank Transfer"
Then the administrator should see the payment request with action "authorize" for "Cash on Delivery" payment method and state "cancelled"
And the administrator should see the payment request with action "status" for "Cash on Delivery" payment method and state "cancelled"
And the administrator should see the payment request with action "capture" for "Cash on Delivery" payment method and state "completed"

View file

@ -0,0 +1,28 @@
@paying_for_order
Feature: Cancelling payment request when payment method is changed
In order to pay with the correct payment method
As a Customer
I want my payment requests with the previous payment method to be cancelled
Background:
Given the store operates on a single channel in "United States"
And the store has a product "PHP T-Shirt"
And the store ships everywhere for Free
And the store allows paying with "Cash on Delivery"
And the store also allows paying with "Bank Transfer"
And I am a logged in customer
And I placed an order "#00000001"
And I bought a single "PHP T-Shirt"
And I addressed it to "Lucifer Morningstar", "Seaside Fwy", "90802" "Los Angeles" in the "United States" with identical billing address
And I chose "Free" shipping method with "Cash on Delivery" payment
And there is a "new" "authorize" payment request for order "#00000001" using the "Cash on Delivery" payment method
And there is also a "processing" "status" payment request for order "#00000001" using the "Cash on Delivery" payment method
And there is also a "completed" "capture" payment request for order "#00000001" using the "Cash on Delivery" payment method
@api @no-ui
Scenario: Cancelling only not finalized payment requests when the payment method has changed
When I view the summary of my order "#00000001"
And I change payment method to "Bank Transfer" after checkout
Then my payment request with action "authorize" for payment method "Cash on Delivery" should have state "cancelled"
And my payment request with action "status" for payment method "Cash on Delivery" should have state "cancelled"
And my payment request with action "capture" for payment method "Cash on Delivery" should have state "completed"

View file

@ -21,7 +21,7 @@ Feature: Changing the offline payment method after order confirmation
And I retry the payment with "Offline" payment method
Then I should have chosen "Offline" payment method
@no-api @ui @javascript
@api @ui @javascript
Scenario: Retrying the payment with different Offline payment works correctly together with inventory
Given there is 1 unit of product "PHP T-Shirt" available in the inventory
And this product is tracked by the inventory
@ -33,7 +33,7 @@ Feature: Changing the offline payment method after order confirmation
And I retry the payment with "Offline" payment method
Then I should have chosen "Offline" payment method
@no-api @ui @javascript
@todo-api @ui @javascript
Scenario: Being unable to pay for my order if my chosen payment method gets disabled
Given I added product "PHP T-Shirt" to the cart
When I complete addressing step with email "john@example.com" and "United States" based billing address
@ -44,7 +44,7 @@ Feature: Changing the offline payment method after order confirmation
And I try to pay for my order
Then I should be notified to choose a payment method
@no-api @ui @javascript
@api @ui @javascript
Scenario: Retrying the payment with different payment method after order confirmation when the original is disabled
Given I added product "PHP T-Shirt" to the cart
When I complete addressing step with email "john@example.com" and "United States" based billing address
@ -55,7 +55,7 @@ Feature: Changing the offline payment method after order confirmation
And I retry the payment with "Offline" payment method
Then I should have chosen "Offline" payment method
@no-api @ui @javascript
@todo-api @ui @javascript
Scenario: Being unable to pay for my order if no methods are available
Given I added product "PHP T-Shirt" to the cart
When I complete addressing step with email "john@example.com" and "United States" based billing address

View file

@ -13,7 +13,7 @@ Feature: Preventing to pay for the cancelled order
And the store allows paying Offline
And there is a customer "sylius@example.com" that placed an order "#00000022"
@no-api @ui
@api @ui
Scenario: Not being able to pay for cancelled order
Given the customer bought 3 "Iron Maiden T-Shirt" products
And the customer chose "Free" shipping method to "United States" with "Offline" payment

View file

@ -6,8 +6,8 @@ use Arkitect\ClassSet;
use Arkitect\CLI\Config;
use Arkitect\Expression\ForClasses\Extend;
use Arkitect\Expression\ForClasses\HaveNameMatching;
use Arkitect\Expression\ForClasses\IsNotAbstract;
use Arkitect\Expression\ForClasses\IsFinal;
use Arkitect\Expression\ForClasses\IsNotAbstract;
use Arkitect\Expression\ForClasses\NotDependsOnTheseNamespaces;
use Arkitect\Expression\ForClasses\ResideInOneOfTheseNamespaces;
use Arkitect\Rules\Rule;
@ -75,6 +75,7 @@ return static function (Config $config): void
'Sylius\Bundle\AdminBundle',
'Sylius\Bundle\ApiBundle',
'Sylius\Bundle\CoreBundle',
'Sylius\Bundle\PayumBundle',
'Sylius\Bundle\ShopBundle',
)
->that(new ResideInOneOfTheseNamespaces('Sylius\Bundle'))

View file

@ -1100,6 +1100,26 @@ parameters:
count: 1
path: src/Sylius/Bundle/CoreBundle/Order/Checker/OrderPromotionsIntegrityChecker.php
-
message: "#^Call to method toBinary\\(\\) on an unknown class Symfony\\\\Component\\\\Uid\\\\Uuid\\.$#"
count: 1
path: src/Sylius/Bundle/CoreBundle/PaymentRequest/CommandProvider/Offline/CapturePaymentRequestCommandProvider.php
-
message: "#^Call to method toBinary\\(\\) on an unknown class Symfony\\\\Component\\\\Uid\\\\Uuid\\.$#"
count: 1
path: src/Sylius/Bundle/CoreBundle/PaymentRequest/CommandProvider/Payum/AuthorizeCommandProvider.php
-
message: "#^Call to method toBinary\\(\\) on an unknown class Symfony\\\\Component\\\\Uid\\\\Uuid\\.$#"
count: 1
path: src/Sylius/Bundle/CoreBundle/PaymentRequest/CommandProvider/Payum/CaptureCommandProvider.php
-
message: "#^Call to method toBinary\\(\\) on an unknown class Symfony\\\\Component\\\\Uid\\\\Uuid\\.$#"
count: 1
path: src/Sylius/Bundle/CoreBundle/PaymentRequest/CommandProvider/Payum/StatusCommandProvider.php
-
message: "#^Method Sylius\\\\Bundle\\\\CoreBundle\\\\PriceHistory\\\\Command\\\\ApplyLowestPriceOnChannelPricings\\:\\:__construct\\(\\) has parameter \\$channelPricingIds with no value type specified in iterable type array\\.$#"
count: 1
@ -1330,41 +1350,6 @@ parameters:
count: 1
path: src/Sylius/Bundle/PaymentBundle/Form/Type/PaymentGatewayChoiceType.php
-
message: "#^Method Sylius\\\\Bundle\\\\PayumBundle\\\\Controller\\\\PayumController\\:\\:__construct\\(\\) has parameter \\$orderRepository with generic interface Sylius\\\\Component\\\\Order\\\\Repository\\\\OrderRepositoryInterface but does not specify its types\\: T$#"
count: 1
path: src/Sylius/Bundle/PayumBundle/Controller/PayumController.php
-
message: "#^Method Sylius\\\\Bundle\\\\PayumBundle\\\\Controller\\\\PayumController\\:\\:prepareCaptureAction\\(\\) has parameter \\$tokenValue with no type specified\\.$#"
count: 1
path: src/Sylius/Bundle/PayumBundle/Controller/PayumController.php
-
message: "#^Method Sylius\\\\Bundle\\\\PayumBundle\\\\Controller\\\\PayumController\\:\\:provideTokenBasedOnPayment\\(\\) has parameter \\$redirectOptions with no value type specified in iterable type array\\.$#"
count: 1
path: src/Sylius/Bundle/PayumBundle/Controller/PayumController.php
-
message: "#^Method Sylius\\\\Bundle\\\\PayumBundle\\\\Factory\\\\GetStatusFactory\\:\\:createNewWithModel\\(\\) has parameter \\$model with no type specified\\.$#"
count: 1
path: src/Sylius/Bundle/PayumBundle/Factory/GetStatusFactory.php
-
message: "#^Method Sylius\\\\Bundle\\\\PayumBundle\\\\Factory\\\\GetStatusFactoryInterface\\:\\:createNewWithModel\\(\\) has parameter \\$model with no type specified\\.$#"
count: 1
path: src/Sylius/Bundle/PayumBundle/Factory/GetStatusFactoryInterface.php
-
message: "#^Method Sylius\\\\Bundle\\\\PayumBundle\\\\Factory\\\\ResolveNextRouteFactory\\:\\:createNewWithModel\\(\\) has parameter \\$model with no type specified\\.$#"
count: 1
path: src/Sylius/Bundle/PayumBundle/Factory/ResolveNextRouteFactory.php
-
message: "#^Method Sylius\\\\Bundle\\\\PayumBundle\\\\Factory\\\\ResolveNextRouteFactoryInterface\\:\\:createNewWithModel\\(\\) has parameter \\$model with no type specified\\.$#"
count: 1
path: src/Sylius/Bundle/PayumBundle/Factory/ResolveNextRouteFactoryInterface.php
-
message: "#^Method Sylius\\\\Bundle\\\\PayumBundle\\\\Request\\\\ResolveNextRoute\\:\\:getRouteParameters\\(\\) return type has no value type specified in iterable type array\\.$#"
count: 1
@ -1675,6 +1660,11 @@ parameters:
count: 1
path: src/Sylius/Bundle/ShopBundle/DependencyInjection/SyliusShopExtension.php
-
message: "#^Method Sylius\\\\Bundle\\\\ShopBundle\\\\DependencyInjection\\\\SyliusShopExtension\\:\\:configureOrderPay\\(\\) has parameter \\$config with no value type specified in iterable type array\\.$#"
count: 1
path: src/Sylius/Bundle/ShopBundle/DependencyInjection/SyliusShopExtension.php
-
message: "#^Method Sylius\\\\Bundle\\\\ShopBundle\\\\DependencyInjection\\\\SyliusShopExtension\\:\\:registerCheckoutRedirectListener\\(\\) has parameter \\$config with no value type specified in iterable type array\\.$#"
count: 1
@ -2910,6 +2900,26 @@ parameters:
count: 1
path: src/Sylius/Component/Payment/Model/PaymentInterface.php
-
message: "#^Call to method toBinary\\(\\) on an unknown class Symfony\\\\Component\\\\Uid\\\\Uuid\\.$#"
count: 1
path: src/Sylius/Component/Payment/Model/PaymentRequest.php
-
message: "#^Method Sylius\\\\Component\\\\Payment\\\\Model\\\\PaymentRequest\\:\\:getHash\\(\\) has invalid return type Symfony\\\\Component\\\\Uid\\\\Uuid\\.$#"
count: 1
path: src/Sylius/Component/Payment/Model/PaymentRequest.php
-
message: "#^Property Sylius\\\\Component\\\\Payment\\\\Model\\\\PaymentRequest\\:\\:\\$hash has unknown class Symfony\\\\Component\\\\Uid\\\\Uuid as its type\\.$#"
count: 1
path: src/Sylius/Component/Payment/Model/PaymentRequest.php
-
message: "#^Method Sylius\\\\Component\\\\Payment\\\\Model\\\\PaymentRequestInterface\\:\\:getHash\\(\\) has invalid return type Symfony\\\\Component\\\\Uid\\\\Uuid\\.$#"
count: 1
path: src/Sylius/Component/Payment/Model/PaymentRequestInterface.php
-
message: "#^Method Sylius\\\\Component\\\\Payment\\\\Resolver\\\\DefaultPaymentMethodResolver\\:\\:__construct\\(\\) has parameter \\$paymentMethodRepository with generic interface Sylius\\\\Component\\\\Payment\\\\Repository\\\\PaymentMethodRepositoryInterface but does not specify its types\\: T$#"
count: 1

View file

@ -28,7 +28,7 @@ final class Configuration implements ConfigurationInterface
$rootNode
->addDefaultsIfNotSet()
->children()
->scalarNode('default_adapter')->defaultValue('winzou_state_machine')->end()
->scalarNode('default_adapter')->defaultValue('symfony_workflow')->end()
->arrayNode('graphs_to_adapters_mapping')
->useAttributeAsKey('graph_name')
->scalarPrototype()->end()

View file

@ -51,7 +51,7 @@ final class ConfigurationTest extends TestCase
],
],
[
'default_adapter' => 'winzou_state_machine',
'default_adapter' => 'symfony_workflow',
'graphs_to_adapters_mapping' => [
'order' => 'symfony_workflow',
'payment' => 'winzou_state_machine',

View file

@ -55,10 +55,10 @@ final class ApiPlatformClient implements ApiClientInterface
/** @param array<string, string> $queryParameters */
public function subResourceIndex(string $resource, string $subResource, string $id, array $queryParameters = [], bool $forgetResponse = false): Response
{
$request = $this->requestFactory->subResourceIndex($this->section, $resource, $id, $subResource, $queryParameters);
$request->authorize($this->getToken(), $this->authorizationHeader);
$this->request = $this->requestFactory->subResourceIndex($this->section, $resource, $id, $subResource, $queryParameters);
$this->request->authorize($this->getToken(), $this->authorizationHeader);
return $this->request($request, $forgetResponse);
return $this->request($this->request, $forgetResponse);
}
public function show(string $resource, string $id, bool $forgetResponse = false): Response

View file

@ -0,0 +1,188 @@
<?php
/*
* This file is part of the Sylius package.
*
* (c) Sylius Sp. z o.o.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Sylius\Behat\Context\Api\Admin;
use Behat\Behat\Context\Context;
use Sylius\Behat\Client\ApiClientInterface;
use Sylius\Behat\Client\RequestFactoryInterface;
use Sylius\Behat\Client\ResponseCheckerInterface;
use Sylius\Behat\Context\Api\Resources;
use Sylius\Behat\Service\Converter\IriConverterInterface;
use Sylius\Behat\Service\SharedSecurityServiceInterface;
use Sylius\Behat\Service\SharedStorageInterface;
use Sylius\Component\Core\Model\OrderInterface;
use Sylius\Component\Core\Model\PaymentMethodInterface;
use Sylius\Component\Payment\Repository\PaymentRequestRepositoryInterface;
use Symfony\Component\HttpFoundation\Request as HTTPRequest;
use Webmozart\Assert\Assert;
final readonly class ManagingPaymentRequestsContext implements Context
{
public function __construct(
private ApiClientInterface $client,
private ResponseCheckerInterface $responseChecker,
private IriConverterInterface $iriConverter,
private PaymentRequestRepositoryInterface $paymentRequestRepository,
private RequestFactoryInterface $requestFactory,
private SharedSecurityServiceInterface $sharedSecurityService,
private SharedStorageInterface $sharedStorage,
) {
}
/**
* @When I browse payment requests of an order :order
*/
public function iBrowseOrdersOfACustomer(OrderInterface $order): void
{
$this->client->subResourceIndex(
Resources::PAYMENTS,
Resources::PAYMENT_REQUESTS,
(string) $order->getLastPayment()->getId(),
);
}
/**
* @When I view details of the payment request for the :order order
*/
public function iViewDetailsOfThePaymentRequestForTheOrder(OrderInterface $order): void
{
$paymentRequest = $this->paymentRequestRepository->findOneBy(['payment' => $order->getLastPayment()]);
$this->client->show(Resources::PAYMENT_REQUESTS, (string) $paymentRequest->getHash());
}
/**
* @When I filter by the :action action
*/
public function iFilterByTheAction(string $action): void
{
$this->client->addFilter('action', $action);
$this->client->filter();
}
/**
* @When I filter by the :paymentMethod payment method
*/
public function iFilterByThePaymentMethod(PaymentMethodInterface $paymentMethod): void
{
$this->client->addFilter('method.code', $paymentMethod->getCode());
$this->client->filter();
}
/**
* @When I filter by the :state state
*/
public function iFilterByTheState(string $state): void
{
$this->client->addFilter('state', $state);
$this->client->filter();
}
/**
* @Then /^there should be (\d+) payment requests? on the list$/
*/
public function thereShouldBeProductVariantsOnTheList(int $count): void
{
Assert::same($this->responseChecker->countCollectionItems($this->client->getLastResponse()), $count);
}
/**
* @Then it should be the payment request with action :action
*/
public function itShouldBeThePaymentRequestWithAction(string $action): void
{
Assert::true($this->responseChecker->hasItemWithValue($this->client->getLastResponse(), 'action', $action));
}
/**
* @Then it should be the payment request with payment method :paymentMethod
*/
public function itShouldBeThePaymentRequestWithPaymentMethod(PaymentMethodInterface $paymentMethod): void
{
Assert::true(
$this->responseChecker->hasItemWithValue(
$this->client->getLastResponse(),
'method',
$this->iriConverter->getIriFromResourceInSection($paymentMethod, 'admin'),
),
);
}
/**
* @Then its method should be :paymentMethod
*/
public function itsMethodShouldBe(PaymentMethodInterface $paymentMethod): void
{
Assert::true(
$this->responseChecker->hasValue(
$this->client->getLastResponse(),
'method',
$this->iriConverter->getIriFromResourceInSection($paymentMethod, 'admin'),
),
);
}
/**
* @Then /^its (action|state) should be "([^"]+)"$/
*/
public function itsActionStateShouldBe(string $field, string $value): void
{
Assert::true($this->responseChecker->hasValue($this->client->getLastResponse(), $field, strtolower($value)));
}
/**
* @Then its payload should has empty value
*/
public function itsPayloadShouldHasEmptyValue(): void
{
Assert::isEmpty($this->responseChecker->getValue($this->client->getLastResponse(), 'payload'));
}
/**
* @Then its response data should has empty value
*/
public function itsResponseDataShouldHasEmptyValue(): void
{
Assert::isEmpty($this->responseChecker->getValue($this->client->getLastResponse(), 'responseData'));
}
/**
* @Then the administrator should see the payment request with action :action for :paymentMethod payment method and state :state
*/
public function administratorShouldSeeThePaymentRequestWithActionAndState(string $action, PaymentMethodInterface $paymentMethod, string $state): void
{
$adminUser = $this->sharedStorage->get('administrator');
/** @var OrderInterface $order */
$this->sharedSecurityService->performActionAsAdminUser($adminUser, function () {
$order = $this->sharedStorage->get('order');
$request = $this->requestFactory->custom('/api/v2/admin/payments/' . $order->getLastPayment()->getId() . '/payment-requests', HTTPRequest::METHOD_GET, [], $this->client->getToken());
$this->client->executeCustomRequest($request);
});
Assert::true(
$this->responseChecker->hasItemWithValue($this->client->getLastResponse(), 'action', $action),
sprintf('Payment request should have action %s', $action),
);
Assert::true(
$this->responseChecker->hasItemWithValue($this->client->getLastResponse(), 'method', $this->iriConverter->getIriFromResourceInSection($paymentMethod, 'admin')),
sprintf('Payment request should have payment method %s', $paymentMethod->getCode()),
);
Assert::true(
$this->responseChecker->hasItemWithValue($this->client->getLastResponse(), 'state', $state),
sprintf('Payment request should have state %s', $state),
);
}
}

View file

@ -58,6 +58,20 @@ final readonly class ManagingPaymentsContext implements Context
$this->client->customItemAction(Resources::ORDERS, $order->getTokenValue(), HttpRequest::METHOD_GET, 'payments');
}
/**
* @When I want to view the payment requests of the first payment
*/
public function iWantToViewThePaymentRequestsOfTheFirstPayment(): void
{
$response = $this->client->getLastResponse();
$this->client->subResourceIndex(
Resources::PAYMENTS,
Resources::PAYMENT_REQUESTS,
(string) $this->responseChecker->getCollection($response)[0]['id'],
);
}
/**
* @Then I should see the details of order :order
*/

View file

@ -55,6 +55,8 @@ final class Resources
public const PAYMENTS = 'payments';
public const PAYMENT_REQUESTS = 'payment-requests';
public const PRODUCT_ASSOCIATION_TYPES = 'product-association-types';
public const PRODUCT_ASSOCIATIONS = 'product-associations';

View file

@ -0,0 +1,89 @@
<?php
/*
* This file is part of the Sylius package.
*
* (c) Sylius Sp. z o.o.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Sylius\Behat\Context\Api\Shop\Checkout;
use Behat\Behat\Context\Context;
use Sylius\Behat\Client\ApiClientInterface;
use Sylius\Behat\Client\ResponseCheckerInterface;
use Sylius\Behat\Context\Api\Resources;
use Sylius\Behat\Service\SharedStorageInterface;
use Sylius\Component\Core\Model\OrderInterface;
use Sylius\Component\Core\OrderPaymentStates;
use Sylius\Component\Payment\Model\PaymentInterface;
use Webmozart\Assert\Assert;
final class CheckoutOrderDetailsContext implements Context
{
public function __construct(
private SharedStorageInterface $sharedStorage,
private ApiClientInterface $client,
private ResponseCheckerInterface $responseChecker,
) {
}
/**
* @When /^I want to browse order details for (this order)$/
*/
public function iWantToBrowseOrderDetailsForThisOrder(OrderInterface $order): void
{
$this->sharedStorage->set('cart_token', $order->getTokenValue());
$this->client->show(Resources::ORDERS, $order->getTokenValue());
}
/**
* @Then I should be able to pay (again)
*/
public function iShouldBeAbleToPay(): void
{
$state = $this->getLatestPaymentState();
Assert::eq($state, PaymentInterface::STATE_NEW);
}
/**
* @Then I should not be able to pay (again)
*/
public function iShouldNotBeAbleToPay(): void
{
$state = $this->getLatestPaymentState();
Assert::notEq($state, PaymentInterface::STATE_NEW);
}
/**
* @When I want to pay for my order
* @When I go to the change payment method page
*/
public function iWantToPayForMyOrder(): void
{
$this->client->show(Resources::ORDERS, $this->sharedStorage->get('cart_token'));
}
private function getLatestPaymentState(): ?string
{
$response = $this->client->show(Resources::ORDERS, $this->sharedStorage->get('cart_token'));
Assert::same($this->client->getLastResponse()->getStatusCode(), 200);
// If the payment is canceled we won't be able to retrieve it because only new one are retrievable
if (OrderPaymentStates::STATE_CANCELLED === $this->responseChecker->getValue($response, 'paymentState')) {
return PaymentInterface::STATE_CANCELLED;
}
$payments = $this->responseChecker->getValue($response, 'payments');
$payment = end($payments);
$paymentId = $payment['id'];
$response = $this->client->requestGet(sprintf('/api/v2/shop/orders/%s/payments/%s', $this->sharedStorage->get('cart_token'), $paymentId));
return $this->responseChecker->getValue($response, 'state');
}
}

View file

@ -110,6 +110,8 @@ final readonly class OrderContext implements Context
public function iViewTheSummaryOfMyOrder(OrderInterface $order): void
{
$this->shopClient->show(Resources::ORDERS, $order->getTokenValue());
$this->sharedStorage->set('cart_token', $order->getTokenValue());
}
/**

View file

@ -0,0 +1,122 @@
<?php
/*
* This file is part of the Sylius package.
*
* (c) Sylius Sp. z o.o.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Sylius\Behat\Context\Api\Shop;
use Behat\Behat\Context\Context;
use Sylius\Behat\Client\ApiClientInterface;
use Sylius\Behat\Client\Request;
use Sylius\Behat\Client\RequestFactoryInterface;
use Sylius\Behat\Client\ResponseCheckerInterface;
use Sylius\Behat\Context\Api\Resources;
use Sylius\Behat\Service\SharedStorageInterface;
use Sylius\Component\Core\Model\PaymentMethodInterface;
use Sylius\Component\Payment\Repository\PaymentRequestRepositoryInterface;
use Symfony\Component\HttpFoundation\Request as HTTPRequest;
use Webmozart\Assert\Assert;
final readonly class PaymentRequestContext implements Context
{
public function __construct(
private SharedStorageInterface $sharedStorage,
private ApiClientInterface $client,
private ResponseCheckerInterface $responseChecker,
private RequestFactoryInterface $requestFactory,
private PaymentRequestRepositoryInterface $paymentRequestRepository,
) {
}
/**
* @When I try to pay for my order
*/
public function iTryToPayForMyOrder(array $payload = []): void
{
$this->client->show(Resources::ORDERS, $this->sharedStorage->get('cart_token'));
$payments = $this->responseChecker->getValue($this->client->getLastResponse(), 'payments');
$payment = end($payments);
$this->postPaymentRequest($payment, $payload);
$uri = $this->responseChecker->getValue($this->client->getLastResponse(), '@id');
$this->sharedStorage->set('payment_request_uri', $uri);
}
/**
* @When I try to update my payment request
*/
public function iTryToUpdateMyPaymentRequest(array $payload = []): void
{
$this->putPaymentRequest($this->sharedStorage->get('payment_request_uri'), $payload);
}
private function postPaymentRequest(array $payment, array $payload): void
{
$request = $this->requestFactory->create(
'shop',
Resources::PAYMENT_REQUESTS,
'Authorization',
$this->client->getToken(),
);
$request->setContent([
'paymentId' => $payment['id'],
'paymentMethodCode' => $payment['method'],
'payload' => $payload,
]);
$this->client->executeCustomRequest($request);
}
public function putPaymentRequest(string $paymentRequestUri, array $payload = []): void
{
$request = $this->requestFactory->custom(
$paymentRequestUri,
HttpRequest::METHOD_PUT,
[],
$this->client->getToken(),
);
$request->setContent([
'payload' => $payload,
]);
$this->client->executeCustomRequest($request);
}
/**
* @Then my payment request with action :action for payment method :paymentMethod should have state :state
*/
public function myPaymentRequestShouldBeCancelled(string $action, PaymentMethodInterface $paymentMethod, string $state): void
{
$request = $this->getRequestForPaymentRequestWithAction($action);
Assert::notNull($request, sprintf('Payment request with action %s not found', $action));
$this->client->executeCustomRequest($request);
$response = $this->client->getLastResponse();
Assert::same($this->responseChecker->getValue($response, 'action'), $action, sprintf('Payment request should have action %s', $action));
Assert::true(str_contains($this->responseChecker->getValue($response, 'method'), $paymentMethod->getCode()), sprintf('Payment request should have payment method %s', $paymentMethod->getCode()));
Assert::same($this->responseChecker->getValue($response, 'state'), $state, sprintf('Payment request should have state %s', $state));
}
private function getRequestForPaymentRequestWithAction(string $action): ?Request
{
$orderToken = $this->sharedStorage->get('cart_token');
$order = $this->client->show(Resources::ORDERS, $orderToken);
$payments = $this->responseChecker->getValue($order, 'payments');
$paymentId = end($payments)['id'];
$paymentRequest = $this->paymentRequestRepository->findOneBy(['payment' => $paymentId, 'action' => $action]);
return $paymentRequest ? $this->requestFactory->custom('/api/v2/shop/payment-requests/' . $paymentRequest->getHash(), HttpRequest::METHOD_GET, [], $this->client->getToken()) : null;
}
}

View file

@ -17,14 +17,13 @@ use Behat\Behat\Context\Context;
use Doctrine\Persistence\ObjectManager;
use Sylius\Behat\Service\SharedStorageInterface;
use Sylius\Bundle\CoreBundle\Fixture\Factory\ExampleFactoryInterface;
use Sylius\Bundle\PayumBundle\Model\GatewayConfigInterface;
use Sylius\Component\Core\Formatter\StringInflector;
use Sylius\Component\Core\Model\ChannelInterface;
use Sylius\Component\Core\Model\OrderInterface;
use Sylius\Component\Core\Model\PaymentMethodInterface;
use Sylius\Component\Payment\Model\PaymentMethodTranslationInterface;
use Sylius\Component\Payment\Repository\PaymentMethodRepositoryInterface;
use Sylius\Resource\Factory\FactoryInterface;
use Webmozart\Assert\Assert;
final readonly class PaymentContext implements Context
{
@ -71,7 +70,7 @@ final readonly class PaymentContext implements Context
/**
* @Given /^the store allows paying (\w+) for (all channels)$/
*/
public function storeAllowsPayingForAllChannels($paymentMethodName, array $channels)
public function storeAllowsPayingForAllChannels($paymentMethodName, array $channels): void
{
$paymentMethod = $this->createPaymentMethod($paymentMethodName, StringInflector::nameToUppercaseCode($paymentMethodName), 'Offline', 'Payment method', false);
@ -91,7 +90,7 @@ final readonly class PaymentContext implements Context
/**
* @Given /^(this payment method) is named "([^"]+)" in the "([^"]+)" locale$/
*/
public function thisPaymentMethodIsNamedIn(PaymentMethodInterface $paymentMethod, $name, $locale)
public function thisPaymentMethodIsNamedIn(PaymentMethodInterface $paymentMethod, $name, $locale): void
{
/** @var PaymentMethodTranslationInterface $translation */
$translation = $this->paymentMethodTranslationFactory->createNew();
@ -108,7 +107,7 @@ final readonly class PaymentContext implements Context
* @Given /^(this payment method) (?:has been|is) disabled$/
* @When the payment method :paymentMethod gets disabled
*/
public function theStoreHasAPaymentMethodDisabled(PaymentMethodInterface $paymentMethod)
public function theStoreHasAPaymentMethodDisabled(PaymentMethodInterface $paymentMethod): void
{
$paymentMethod->disable();
@ -118,7 +117,7 @@ final readonly class PaymentContext implements Context
/**
* @Given /^(it) has instructions "([^"]+)"$/
*/
public function itHasInstructions(PaymentMethodInterface $paymentMethod, $instructions)
public function itHasInstructions(PaymentMethodInterface $paymentMethod, $instructions): void
{
$paymentMethod->setInstructions($instructions);
@ -128,7 +127,7 @@ final readonly class PaymentContext implements Context
/**
* @Given the store has :paymentMethodName payment method not assigned to any channel
*/
public function theStoreHasPaymentMethodNotAssignedToAnyChannel($paymentMethodName)
public function theStoreHasPaymentMethodNotAssignedToAnyChannel(string $paymentMethodName): void
{
$this->createPaymentMethod($paymentMethodName, 'PM_' . $paymentMethodName, 'Offline', 'Payment method', false);
}
@ -136,8 +135,9 @@ final readonly class PaymentContext implements Context
/**
* @Given the payment method :paymentMethod requires authorization before capturing
*/
public function thePaymentMethodRequiresAuthorizationBeforeCapturing(PaymentMethodInterface $paymentMethod)
public function thePaymentMethodRequiresAuthorizationBeforeCapturing(PaymentMethodInterface $paymentMethod): void
{
/** @var GatewayConfigInterface $config */
$config = $paymentMethod->getGatewayConfig();
$config->setConfig(array_merge($config->getConfig(), ['use_authorize' => true]));
$paymentMethod->setGatewayConfig($config);
@ -161,16 +161,6 @@ final readonly class PaymentContext implements Context
$paymentMethod->addChannel($channel);
}
/**
* @Then /^the (latest order) should have a payment with state "([^"]+)"$/
*/
public function theLatestOrderHasAuthorizedPayment(OrderInterface $order, string $state)
{
$payment = $order->getLastPayment();
Assert::eq($payment->getState(), $state);
}
private function createPaymentMethod(
string $name,
string $code,

View file

@ -0,0 +1,86 @@
<?php
/*
* This file is part of the Sylius package.
*
* (c) Sylius Sp. z o.o.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Sylius\Behat\Context\Setup;
use Behat\Behat\Context\Context;
use Sylius\Abstraction\StateMachine\StateMachineInterface;
use Sylius\Bundle\ApiBundle\Command\Payment\AddPaymentRequest;
use Sylius\Component\Core\Model\OrderInterface;
use Sylius\Component\Core\Model\PaymentMethodInterface;
use Sylius\Component\Payment\Factory\PaymentRequestFactoryInterface;
use Sylius\Component\Payment\Model\PaymentRequestInterface;
use Sylius\Component\Payment\PaymentRequestTransitions;
use Sylius\Component\Payment\Repository\PaymentRequestRepositoryInterface;
use Symfony\Component\Messenger\MessageBusInterface;
final readonly class PaymentRequestContext implements Context
{
public function __construct(
private MessageBusInterface $commandBus,
private PaymentRequestRepositoryInterface $paymentRequestRepository,
private PaymentRequestFactoryInterface $paymentRequestFactory,
private StateMachineInterface $stateMachine,
) {
}
/**
* @Given the payment request action :action has been executed for order :order with the payment method :paymentMethod
*/
public function thePaymentRequestActionHasBeenExecutedForOrderWithThePaymentMethod(
string $action,
OrderInterface $order,
PaymentMethodInterface $paymentMethod,
): void {
$addPaymentRequest = new AddPaymentRequest(
paymentId: $order->getLastPayment()->getId(),
paymentMethodCode: $paymentMethod->getCode(),
action: $action,
);
$this->commandBus->dispatch($addPaymentRequest);
}
/**
* @Given there is (also) a :state :action payment request for order :order using the :paymentMethod payment method
*/
public function thePaymentRequestActionHasBeenExecutedForOrderWithThePaymentMethodAndState(
string $state = null,
string $action,
OrderInterface $order,
PaymentMethodInterface $paymentMethod,
): void {
$paymentRequest = $this->paymentRequestFactory->create($order->getLastPayment(), $paymentMethod);
if ($state !== PaymentRequestInterface::STATE_NEW) {
$this->stateMachine->apply(
$paymentRequest,
PaymentRequestTransitions::GRAPH,
$this->getTransitionForState($state),
);
}
$paymentRequest->setAction($action);
$this->paymentRequestRepository->add($paymentRequest);
}
private function getTransitionForState(string $state): string
{
return match ($state) {
'completed' => PaymentRequestTransitions::TRANSITION_COMPLETE,
'processing' => PaymentRequestTransitions::TRANSITION_PROCESS,
default => throw new \InvalidArgumentException(sprintf('Invalid state "%s" provided.', $state)),
};
}
}

View file

@ -0,0 +1,151 @@
<?php
/*
* This file is part of the Sylius package.
*
* (c) Sylius Sp. z o.o.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Sylius\Behat\Context\Ui\Admin;
use Behat\Behat\Context\Context;
use Sylius\Behat\Page\Admin\Payment\PaymentRequest\IndexPageInterface;
use Sylius\Behat\Page\Admin\Payment\PaymentRequest\ShowPageInterface;
use Sylius\Behat\Service\SharedSecurityServiceInterface;
use Sylius\Behat\Service\SharedStorageInterface;
use Sylius\Component\Core\Model\OrderInterface;
use Sylius\Component\Core\Model\PaymentMethodInterface;
use Sylius\Component\Payment\Repository\PaymentRequestRepositoryInterface;
use Webmozart\Assert\Assert;
final readonly class ManagingPaymentRequestsContext implements Context
{
public function __construct(
private IndexPageInterface $indexPage,
private ShowPageInterface $showPage,
private PaymentRequestRepositoryInterface $paymentRequestRepository,
private SharedStorageInterface $sharedStorage,
private SharedSecurityServiceInterface $sharedSecurityService,
) {
}
/**
* @When I browse payment requests of an order :order
*/
public function iBrowsePaymentRequestsOfACustomer(OrderInterface $order): void
{
$this->indexPage->open(['paymentId' => $order->getLastPayment()->getId()]);
}
/**
* @When I view details of the payment request for the :order order
*/
public function iViewDetailsOfThePaymentRequestForTheOrder(OrderInterface $order): void
{
$payment = $order->getLastPayment();
$paymentRequest = $this->paymentRequestRepository->findOneBy(['payment' => $payment]);
$this->showPage->open([
'hash' => $paymentRequest->getHash(),
'paymentId' => $payment->getId(),
]);
}
/**
* @When I filter by the :action action
*/
public function iFilterByTheAction(string $action): void
{
$this->indexPage->chooseActionToFilter($action);
$this->indexPage->filter();
}
/**
* @When I filter by the :paymentMethod payment method
*/
public function iFilterByThePaymentMethod(PaymentMethodInterface $paymentMethod): void
{
$this->indexPage->choosePaymentMethodToFilter($paymentMethod->getName());
$this->indexPage->filter();
}
/**
* @When I filter by the :state state
*/
public function iFilterByTheState(string $state): void
{
$this->indexPage->chooseStateToFilter($state);
$this->indexPage->filter();
}
/**
* @Then /^there should be (\d+) payment requests? on the list$/
*/
public function thereShouldBeProductVariantsOnTheList(int $count): void
{
Assert::same($this->indexPage->countItems(), $count);
}
/**
* @Then it should be the payment request with action :action
*/
public function itShouldBeThePaymentRequestWithAction(string $action): void
{
Assert::true($this->indexPage->isSingleResourceOnPage(['action' => $action]));
}
/**
* @Then it should be the payment request with payment method :paymentMethod
*/
public function itShouldBeThePaymentRequestWithPaymentMethod(PaymentMethodInterface $paymentMethod): void
{
Assert::true($this->indexPage->isSingleResourceOnPage(['method' => $paymentMethod->getName()]));
}
/**
* @Then its :field should be :value
*/
public function itsFieldShouldBe(string $field, string $value): void
{
Assert::same($this->showPage->getFieldText($field), $value);
}
/**
* @Then its payload should has empty value
*/
public function itsPayloadShouldHasEmptyValue(): void
{
Assert::same($this->showPage->getFieldText('payload'), 'null');
}
/**
* @Then its response data should has empty value
*/
public function itsResponseDataShouldBe(): void
{
Assert::same($this->showPage->getFieldText('response_data'), json_encode([]));
}
/**
* @Then the administrator should see the payment request with action :action for :method payment method and state :state
*/
public function administratorShouldSeeThePaymentRequestWithActionAndState(string $action, string $paymentMethod, string $state): void
{
$adminUser = $this->sharedStorage->get('administrator');
$this->sharedSecurityService->performActionAsAdminUser($adminUser, function () {
$this->iBrowsePaymentRequestsOfACustomer($this->sharedStorage->get('order'));
});
Assert::true($this->indexPage->isSingleResourceOnPage([
'action' => $action,
'state' => $state,
'method' => $paymentMethod,
]));
}
}

View file

@ -80,6 +80,14 @@ final class ManagingPaymentsContext implements Context
$this->indexPage->chooseChannelFilter($channelName);
}
/**
* @When I want to view the payment requests of the first payment
*/
public function iWantToViewThePaymentRequestsOfTheFirstPayment(): void
{
$this->indexPage->showPaymentRequestOfNthPayment(1);
}
/**
* @Then I should see :count payments in the list
* @Then I should see a single payment in the list

View file

@ -48,6 +48,17 @@ class IndexPage extends BaseIndexPage implements IndexPageInterface
$this->getOrderLinkForRow($position)->clickLink('#');
}
public function showPaymentRequestOfNthPayment(int $position): void
{
$tableAccessor = $this->getTableAccessor();
$table = $this->getElement('table');
$row = $tableAccessor->getRowsWithFields($table, [])[$position];
$field = $tableAccessor->getFieldFromRow($table, $row, 'actions');
$field->find('css', '[data-test-show-action="List payment requests"]')->click();
}
public function chooseChannelFilter(string $channelName): void
{
$this->getElement('filter_channel')->selectOption($channelName);

View file

@ -27,5 +27,7 @@ interface IndexPageInterface extends BaseIndexPageInterface
public function showOrderPageForNthPayment(int $position): void;
public function showPaymentRequestOfNthPayment(int $position): void;
public function chooseChannelFilter(string $channelName): void;
}

View file

@ -0,0 +1,74 @@
<?php
/*
* This file is part of the Sylius package.
*
* (c) Sylius Sp. z o.o.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Sylius\Behat\Page\Admin\Payment\PaymentRequest;
use Behat\Mink\Element\NodeElement;
use Behat\Mink\Session;
use Sylius\Behat\Page\Admin\Crud\IndexPage as BaseIndexPage;
use Sylius\Behat\Service\Accessor\TableAccessorInterface;
use Sylius\Behat\Service\Helper\AutocompleteHelperInterface;
use Symfony\Component\Routing\RouterInterface;
class IndexPage extends BaseIndexPage implements IndexPageInterface
{
public function __construct(
Session $session,
$minkParameters,
RouterInterface $router,
TableAccessorInterface $tableAccessor,
string $routeName,
private readonly AutocompleteHelperInterface $autocompleteHelper,
) {
parent::__construct($session, $minkParameters, $router, $tableAccessor, $routeName);
}
public function choosePaymentMethodToFilter(string $paymentMethodName): void
{
$this->specifyAutocompleteFilter($this->getElement('filter_payment_method'), $paymentMethodName);
}
public function chooseActionToFilter(string $action): void
{
$this->getElement('filter_action')->selectOption($action);
}
public function chooseStateToFilter(string $state): void
{
$this->getElement('filter_state')->selectOption($state);
}
protected function getDefinedElements(): array
{
return array_merge(parent::getDefinedElements(), [
'filter_action' => '#criteria_action',
'filter_payment_method' => '#criteria_payment_method',
'filter_state' => '#criteria_state',
]);
}
private function specifyAutocompleteFilter(NodeElement $autocomplete, string $value): void
{
if (!$this->areFiltersVisible()) {
$this->toggleFilters();
}
$this->autocompleteHelper->selectByName(
$this->getDriver(),
$autocomplete->getXpath(),
$value,
);
$this->waitForFormUpdate();
}
}

View file

@ -0,0 +1,25 @@
<?php
/*
* This file is part of the Sylius package.
*
* (c) Sylius Sp. z o.o.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Sylius\Behat\Page\Admin\Payment\PaymentRequest;
use Sylius\Behat\Page\Admin\Crud\IndexPageInterface as BaseIndexPageInterface;
interface IndexPageInterface extends BaseIndexPageInterface
{
public function choosePaymentMethodToFilter(string $paymentMethodName): void;
public function chooseActionToFilter(string $action): void;
public function chooseStateToFilter(string $state): void;
}

View file

@ -0,0 +1,40 @@
<?php
/*
* This file is part of the Sylius package.
*
* (c) Sylius Sp. z o.o.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Sylius\Behat\Page\Admin\Payment\PaymentRequest;
use FriendsOfBehat\PageObjectExtension\Page\SymfonyPage;
class ShowPage extends SymfonyPage implements ShowPageInterface
{
public function getRouteName(): string
{
return 'sylius_admin_payment_request_show';
}
public function getFieldText(string $fieldName): string
{
return $this->getElement($fieldName)->getText();
}
protected function getDefinedElements(): array
{
return array_merge(parent::getDefinedElements(), [
'action' => '[data-test-action]',
'method' => '[data-test-method]',
'payload' => '[data-test-payload]',
'response_data' => '[data-test-response-data]',
'state' => '[data-test-state]',
]);
}
}

View file

@ -0,0 +1,21 @@
<?php
/*
* This file is part of the Sylius package.
*
* (c) Sylius Sp. z o.o.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Sylius\Behat\Page\Admin\Payment\PaymentRequest;
use FriendsOfBehat\PageObjectExtension\Page\SymfonyPageInterface;
interface ShowPageInterface extends SymfonyPageInterface
{
public function getFieldText(string $fieldName): string;
}

View file

@ -303,5 +303,15 @@
<argument type="service" id="api_platform.symfony.iri_converter" />
<argument type="service" id="sylius.behat.shared_storage" />
</service>
<service id="sylius.behat.context.api.admin.managing_payment_requests" class="Sylius\Behat\Context\Api\Admin\ManagingPaymentRequestsContext">
<argument type="service" id="sylius.behat.api_platform_client.admin" />
<argument type="service" id="Sylius\Behat\Client\ResponseCheckerInterface" />
<argument type="service" id="api_platform.symfony.iri_converter" />
<argument type="service" id="sylius.repository.payment_request" />
<argument type="service" id="sylius.behat.request_factory" />
<argument type="service" id="sylius.behat.api.shared_security" />
<argument type="service" id="sylius.behat.shared_storage" />
</service>
</services>
</container>

View file

@ -161,6 +161,14 @@
<argument type="service" id="sylius.behat.shared_storage" />
</service>
<service id="sylius.behat.context.api.shop.payment_request" class="Sylius\Behat\Context\Api\Shop\PaymentRequestContext">
<argument type="service" id="sylius.behat.shared_storage" />
<argument type="service" id="sylius.behat.api_platform_client.shop" />
<argument type="service" id="Sylius\Behat\Client\ResponseCheckerInterface" />
<argument type="service" id="sylius.behat.request_factory" />
<argument type="service" id="sylius.repository.payment_request" />
</service>
<service id="sylius.behat.context.api.shop.payment" class="Sylius\Behat\Context\Api\Shop\PaymentContext">
<argument type="service" id="sylius.behat.api_platform_client.shop" />
<argument type="service" id="Sylius\Behat\Client\ResponseCheckerInterface" />
@ -191,6 +199,12 @@
<argument type="service" id="sylius.behat.api_platform_client.shop" />
</service>
<service id="sylius.behat.context.api.shop.checkout.order_details" class="Sylius\Behat\Context\Api\Shop\Checkout\CheckoutOrderDetailsContext">
<argument type="service" id="sylius.behat.shared_storage" />
<argument type="service" id="sylius.behat.api_platform_client.shop" />
<argument type="service" id="Sylius\Behat\Client\ResponseCheckerInterface" />
</service>
<service id="sylius.behat.context.api.shop.taxon" class="Sylius\Behat\Context\Api\Shop\TaxonContext">
<argument type="service" id="sylius.behat.api_platform_client.shop" />
<argument type="service" id="Sylius\Behat\Client\ResponseCheckerInterface" />

View file

@ -142,7 +142,6 @@
<argument type="collection">
<argument key="offline">Offline</argument>
</argument>
</service>
<service id="Sylius\Behat\Context\Setup\PriceHistoryContext">
@ -320,5 +319,12 @@
<argument type="service" id="sylius.event_bus" />
<argument type="service" id="sylius.behat.shared_storage" />
</service>
<service id="sylius.behat.context.setup.payment_request" class="Sylius\Behat\Context\Setup\PaymentRequestContext">
<argument type="service" id="sylius.command_bus" />
<argument type="service" id="sylius.repository.payment_request" />
<argument type="service" id="sylius.factory.payment_request" />
<argument type="service" id="sylius_abstraction.state_machine" />
</service>
</services>
</container>

View file

@ -613,5 +613,13 @@
<service id="sylius.behat.context.ui.admin.search_filter" class="Sylius\Behat\Context\Ui\Admin\SearchFilterContext">
<argument type="service" id="sylius.behat.element.admin.crud.index.search_filter" />
</service>
<service id="sylius.behat.context.ui.admin.managing_payment_requests" class="Sylius\Behat\Context\Ui\Admin\ManagingPaymentRequestsContext">
<argument type="service" id="sylius.behat.page.admin.payment.payment_request.index" />
<argument type="service" id="sylius.behat.page.admin.payment.payment_request.show"/>
<argument type="service" id="sylius.repository.payment_request"/>
<argument type="service" id="sylius.behat.shared_storage" />
<argument type="service" id="sylius.behat.shared_security" />
</service>
</services>
</container>

View file

@ -14,6 +14,8 @@
<container xmlns="http://symfony.com/schema/dic/services" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<parameters>
<parameter key="sylius.behat.page.admin.payment.index.class">Sylius\Behat\Page\Admin\Payment\IndexPage</parameter>
<parameter key="sylius.behat.page.admin.payment.payment_request.index.class">Sylius\Behat\Page\Admin\Payment\PaymentRequest\IndexPage</parameter>
<parameter key="sylius.behat.page.admin.payment.payment_request.show.class">Sylius\Behat\Page\Admin\Payment\PaymentRequest\ShowPage</parameter>
</parameters>
<services>
@ -22,5 +24,21 @@
<service id="sylius.behat.page.admin.payment.index" class="%sylius.behat.page.admin.payment.index.class%" parent="sylius.behat.page.admin.crud.index" public="false">
<argument type="string">sylius_admin_payment_index</argument>
</service>
<service
id="sylius.behat.page.admin.payment.payment_request.index"
class="%sylius.behat.page.admin.payment.payment_request.index.class%"
parent="sylius.behat.page.admin.crud.index"
>
<argument type="string">sylius_admin_payment_request_index</argument>
<argument type="service" id="Sylius\Behat\Service\Helper\AutocompleteHelperInterface" />
</service>
<service
id="sylius.behat.page.admin.payment.payment_request.show"
class="%sylius.behat.page.admin.payment.payment_request.show.class%"
parent="sylius.behat.symfony_page"
>
</service>
</services>
</container>

View file

@ -11,8 +11,10 @@ default:
- sylius.behat.context.transform.cart
- sylius.behat.context.transform.channel
- sylius.behat.context.transform.country
- sylius.behat.context.transform.customer
- sylius.behat.context.transform.lexical
- sylius.behat.context.transform.locale
- sylius.behat.context.transform.order
- sylius.behat.context.transform.payment
- sylius.behat.context.transform.product
- sylius.behat.context.transform.product_variant
@ -23,24 +25,32 @@ default:
- sylius.behat.context.transform.user
- sylius.behat.context.transform.zone
- sylius.behat.context.setup.admin_user
- sylius.behat.context.setup.cart
- sylius.behat.context.setup.channel
- sylius.behat.context.setup.currency
- sylius.behat.context.setup.checkout
- sylius.behat.context.setup.checkout.address
- sylius.behat.context.setup.geographical
- sylius.behat.context.setup.locale
- sylius.behat.context.setup.order
- sylius.behat.context.setup.payment
- sylius.behat.context.setup.payment_request
- sylius.behat.context.setup.product
- sylius.behat.context.setup.shipping
- sylius.behat.context.setup.shop_api_security
- sylius.behat.context.setup.taxation
- sylius.behat.context.setup.user
- sylius.behat.context.setup.zone
- sylius.behat.context.api.admin.managing_payment_requests
- sylius.behat.context.api.shop.cart
- sylius.behat.context.api.shop.checkout
- sylius.behat.context.api.shop.checkout.complete
- sylius.behat.context.api.shop.checkout.order_details
- sylius.behat.context.api.shop.customer
- sylius.behat.context.api.shop.order
- sylius.behat.context.api.shop.payment_request
- sylius.behat.context.api.shop.response
filters:

View file

@ -11,6 +11,7 @@ default:
- 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.shared_storage
@ -18,10 +19,12 @@ default:
- sylius.behat.context.setup.channel
- sylius.behat.context.setup.order
- sylius.behat.context.setup.payment
- sylius.behat.context.setup.payment_request
- sylius.behat.context.setup.product
- sylius.behat.context.setup.shipping
- sylius.behat.context.api.admin.managing_payments
- sylius.behat.context.api.admin.managing_payment_requests
filters:
tags: "@managing_payments&&@api"

View file

@ -24,12 +24,14 @@ default:
- sylius.behat.context.transform.user
- sylius.behat.context.transform.zone
- sylius.behat.context.setup.admin_user
- 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.payment_request
- sylius.behat.context.setup.product
- sylius.behat.context.setup.shipping
- sylius.behat.context.setup.shop_security
@ -37,6 +39,7 @@ default:
- sylius.behat.context.setup.user
- sylius.behat.context.setup.zone
- sylius.behat.context.ui.admin.managing_payment_requests
- sylius.behat.context.ui.shop.account
- sylius.behat.context.ui.shop.cart
- sylius.behat.context.ui.shop.checkout

View file

@ -23,6 +23,7 @@ default:
- sylius.behat.context.setup.geographical
- sylius.behat.context.setup.order
- sylius.behat.context.setup.payment
- sylius.behat.context.setup.payment_request
- sylius.behat.context.setup.product
- sylius.behat.context.setup.shipping
- sylius.behat.context.setup.zone
@ -30,6 +31,7 @@ default:
- sylius.behat.context.ui.email
- sylius.behat.context.ui.admin.managing_payments
- sylius.behat.context.ui.admin.managing_payment_requests
- sylius.behat.context.ui.shop.cart
filters:
tags: "@managing_payments&&@ui"

View file

@ -124,6 +124,8 @@ twig_hooks:
template: '@SyliusAdmin/order/show/content/sections/payments/item/actions.html.twig'
'sylius_admin.order.show.content.sections.payments.item.actions':
list_payment_requests:
template: '@SyliusAdmin/order/show/content/sections/payments/item/actions/list_payment_requests.html.twig'
complete:
template: '@SyliusAdmin/order/show/content/sections/payments/item/actions/complete.html.twig'
refund:

View file

@ -0,0 +1,5 @@
twig_hooks:
hooks:
'sylius_admin.payment_request.index.content.header':
breadcrumbs:
template: '@SyliusAdmin/payment_request/index/content/header/breadcrumbs.html.twig'

View file

@ -0,0 +1,35 @@
twig_hooks:
hooks:
'sylius_admin.payment_request.show.content':
sections:
template: '@SyliusAdmin/payment_request/show/content/sections.html.twig'
'sylius_admin.payment_request.show.content.header':
breadcrumbs:
template: '@SyliusAdmin/payment_request/show/content/header/breadcrumbs.html.twig'
'sylius_admin.payment_request.show.content.sections':
general:
template: '@SyliusAdmin/payment_request/show/content/sections/general.html.twig'
'sylius_admin.payment_request.show.content.sections#left':
payload:
template: '@SyliusAdmin/payment_request/show/content/sections/payload.html.twig'
'sylius_admin.payment_request.show.content.sections#right':
response_data:
template: '@SyliusAdmin/payment_request/show/content/sections/response_data.html.twig'
'sylius_admin.payment_request.show.content.sections.general':
hash:
template: '@SyliusAdmin/payment_request/show/content/sections/general/hash.html.twig'
method:
template: '@SyliusAdmin/payment_request/show/content/sections/general/method.html.twig'
action:
template: '@SyliusAdmin/payment_request/show/content/sections/general/action.html.twig'
state:
template: '@SyliusAdmin/payment_request/show/content/sections/general/state.html.twig'
created_at:
template: '@SyliusAdmin/payment_request/show/content/sections/general/created_at.html.twig'
updated_at:
template: '@SyliusAdmin/payment_request/show/content/sections/general/updated_at.html.twig'

View file

@ -69,6 +69,15 @@ sylius_grid:
class: "%sylius.model.channel.class%"
actions:
item:
list_payment_requests:
type: show
icon: list_letters
label: sylius.ui.list_payment_requests
options:
link:
route: sylius_admin_payment_request_index
parameters:
paymentId: resource.id
complete:
type: apply_transition
label: sylius.ui.complete

View file

@ -0,0 +1,93 @@
sylius_grid:
grids:
sylius_admin_payment_request:
driver:
name: doctrine/orm
options:
class: "%sylius.model.payment_request.class%"
repository:
method: createQueryBuilderForPayment
arguments:
paymentId: $paymentId
sorting:
createdAt: desc
fields:
hash:
type: string
label: sylius.ui.hash
sortable: ~
method:
type: twig
label: sylius.ui.payment_method
path: .
options:
template: "@SyliusAdmin/payment_request/grid/field/method.html.twig"
action:
type: twig
label: sylius.ui.action
options:
template: "@SyliusAdmin/payment_request/grid/field/action.html.twig"
state:
type: twig
label: sylius.ui.state
options:
template: "@SyliusAdmin/payment_request/grid/field/state.html.twig"
createdAt:
type: twig
label: sylius.ui.creation_date
sortable: ~
options:
template: "@SyliusAdmin/shared/grid/field/date.html.twig"
vars:
th_class: "w-1 text-center"
updatedAt:
type: twig
label: sylius.ui.updating_date
sortable: ~
options:
template: "@SyliusAdmin/shared/grid/field/date.html.twig"
vars:
th_class: "w-1 text-center"
filters:
payment_method:
type: ux_translatable_autocomplete
label: sylius.ui.payment_method
form_options:
extra_options:
class: "%sylius.model.payment_method.class%"
translation_fields: [ name ]
choice_label: name
options:
fields: [ method.id ]
action:
type: select
label: sylius.ui.action
form_options:
choices:
sylius.ui.authorize: !php/const Sylius\Component\Payment\Model\PaymentRequestInterface::ACTION_AUTHORIZE
sylius.ui.cancel: !php/const Sylius\Component\Payment\Model\PaymentRequestInterface::ACTION_CANCEL
sylius.ui.capture: !php/const Sylius\Component\Payment\Model\PaymentRequestInterface::ACTION_CAPTURE
sylius.ui.payout: !php/const Sylius\Component\Payment\Model\PaymentRequestInterface::ACTION_PAYOUT
sylius.ui.refund: !php/const Sylius\Component\Payment\Model\PaymentRequestInterface::ACTION_REFUND
sylius.ui.status: !php/const Sylius\Component\Payment\Model\PaymentRequestInterface::ACTION_STATUS
sylius.ui.sync: !php/const Sylius\Component\Payment\Model\PaymentRequestInterface::ACTION_SYNC
state:
type: select
label: sylius.ui.state
form_options:
choices:
sylius.ui.cancelled: !php/const Sylius\Component\Payment\Model\PaymentRequestInterface::STATE_CANCELLED
sylius.ui.completed: !php/const Sylius\Component\Payment\Model\PaymentRequestInterface::STATE_COMPLETED
sylius.ui.failed: !php/const Sylius\Component\Payment\Model\PaymentRequestInterface::STATE_FAILED
sylius.ui.new: !php/const Sylius\Component\Payment\Model\PaymentRequestInterface::STATE_NEW
sylius.ui.processing: !php/const Sylius\Component\Payment\Model\PaymentRequestInterface::STATE_PROCESSING
actions:
item:
show:
type: show
options:
link:
route: sylius_admin_payment_request_show
parameters:
hash: resource.hash
paymentId: resource.payment.id

View file

@ -58,6 +58,10 @@ sylius_admin_payment:
sylius_admin_payment_method:
resource: "@SyliusAdminBundle/Resources/config/routing/payment_method.yml"
sylius_admin_payment_request:
resource: "@SyliusAdminBundle/Resources/config/routing/payment_request.yaml"
prefix: /payments/{paymentId}/payment-requests/
sylius_admin_product:
resource: "@SyliusAdminBundle/Resources/config/routing/product.yml"

View file

@ -0,0 +1,25 @@
sylius_admin_payment_request_index:
path: /
methods: [GET]
defaults:
_controller: sylius.controller.payment_request::indexAction
_sylius:
section: admin
permission: true
template: "@SyliusAdmin/shared/crud/index.html.twig"
grid: sylius_admin_payment_request
sylius_admin_payment_request_show:
path: /{hash}
methods: [GET]
defaults:
_controller: sylius.controller.payment_request::showAction
_sylius:
section: admin
permission: true
template: "@SyliusAdmin/shared/crud/show.html.twig"
repository:
method: findOneByPaymentId
arguments:
hash: $hash
paymentId: $paymentId

View file

@ -34,6 +34,7 @@
"knplabs/knp-menu": "^3.1",
"knplabs/knp-menu-bundle": "^3.0",
"sylius/core-bundle": "^2.0",
"sylius/payum-bundle": "^2.0",
"sylius/ui-bundle": "^2.0",
"sylius/twig-hooks": "~0.3.0",
"symfony/framework-bundle": "^6.4.1 || ^7.1",

View file

@ -1,5 +1,5 @@
<td>
<div class="d-flex justify-content-end">
<div class="d-flex gap-1 justify-content-end">
{% hook 'actions' %}
</div>
</td>

View file

@ -0,0 +1,7 @@
{% from '@SyliusAdmin/shared/helper/icon.html.twig' import icon %}
{% set payment = hookable_metadata.context.payment %}
<a title="details" href="{{ path('sylius_admin_payment_request_index', {'paymentId': payment.id}) }}" class="btn btn-icon" data-bs-toggle="tooltip" data-bs-title="{{ 'sylius.ui.list_payment_requests'|trans }}" {{ sylius_test_html_attribute('list_payment_requests') }}>
{{ icon({ icon: 'list_letters', class: 'icon icon-tabler' }) }}
</a>

View file

@ -0,0 +1,3 @@
<span class="badge bg-red-lt">
{{ 'sylius.ui.cancelled'|trans }}
</span>

View file

@ -0,0 +1,3 @@
<span class="badge bg-green-lt">
{{ 'sylius.ui.completed'|trans }}
</span>

View file

@ -0,0 +1,3 @@
<span class="badge bg-red-lt">
{{ 'sylius.ui.failed'|trans }}
</span>

View file

@ -0,0 +1,3 @@
<span class="badge">
{{ 'sylius.ui.new'|trans }}
</span>

View file

@ -0,0 +1,3 @@
<span class="badge bg-blue-lt">
{{ 'sylius.ui.processing'|trans }}
</span>

View file

@ -0,0 +1 @@
{{ ('sylius.ui.' ~ data)|trans }}

View file

@ -0,0 +1 @@
<a class="fw-medium" href="{{ path('sylius_admin_payment_method_update', {'id' : data.method.id }) }}">{{ data.method.name }}</a>

View file

@ -0,0 +1 @@
{{ include('@SyliusAdmin/payment_request/common/label/state/' ~ data ~ '.html.twig') }}

View file

@ -0,0 +1,10 @@
{% from '@SyliusAdmin/shared/helper/breadcrumbs.html.twig' import breadcrumbs %}
{% set configuration = hookable_metadata.context.resources.requestConfiguration %}
{{ breadcrumbs([
{ name: 'sylius.ui.dashboard', url: path('sylius_admin_dashboard'), active: false },
{ name: 'sylius.ui.payments'|trans, url: path('sylius_admin_payment_index'), active: false },
{ name: configuration.request.get('id'), active: false },
{ name: 'sylius.ui.payment_requests'|trans, active: true },
]) }}

View file

@ -0,0 +1,12 @@
{% from '@SyliusAdmin/shared/helper/breadcrumbs.html.twig' import breadcrumbs %}
{% set payment_request = hookable_metadata.context.resource %}
{% set payment_id = payment_request.payment.id %}
{{ breadcrumbs([
{ name: 'sylius.ui.dashboard', url: path('sylius_admin_dashboard'), active: false },
{ name: 'sylius.ui.payments'|trans, url: path('sylius_admin_payment_index'), active: false },
{ name: payment_id, active: false },
{ name: 'sylius.ui.payment_requests'|trans, url: path('sylius_admin_payment_request_index', {'paymentId': payment_id}), active: false },
{ name: payment_request.hash, active: true },
]) }}

View file

@ -0,0 +1,13 @@
<div class="page-body">
<div class="container-xl">
{% hook 'sections' %}
<div class="row">
<div class="col-12 col-lg-6">
{% hook 'sections#left' %}
</div>
<div class="col-12 col-lg-6">
{% hook 'sections#right' %}
</div>
</div>
</div>
</div>

View file

@ -0,0 +1,12 @@
<div class="card mb-3">
<div class="card-header">
<div class="card-title">
{{ 'sylius.ui.general'|trans }}
</div>
</div>
<div class="card-body">
<div class="divide-y">
{% hook 'general' %}
</div>
</div>
</div>

View file

@ -0,0 +1,6 @@
<div class="row">
<div class="col-12 col-md-4 fw-bold">{{ 'sylius.ui.action'|trans }}</div>
<div class="col-12 col-md-8" {{ sylius_test_html_attribute('action') }}>
{{ ('sylius.ui.' ~ hookable_metadata.context.resource.action)|trans }}
</div>
</div>

View file

@ -0,0 +1,8 @@
{% set date_format = hookable_metadata.configuration.date_format|default('YYYY-MM-dd HH:mm:ss') %}
<div class="row">
<div class="col-12 col-md-4 fw-bold">{{ 'sylius.ui.created_at'|trans }}</div>
<div class="col-12 col-md-8" {{ sylius_test_html_attribute('created-at') }}>
{{ hookable_metadata.context.resource.createdAt|format_datetime(pattern=date_format) }}
</div>
</div>

View file

@ -0,0 +1,4 @@
<div class="row">
<div class="col-12 col-md-4 fw-bold">{{ 'sylius.ui.hash'|trans }}</div>
<div class="col-12 col-md-8" {{ sylius_test_html_attribute('hash') }}>{{ hookable_metadata.context.resource.hash }}</div>
</div>

View file

@ -0,0 +1,8 @@
{% set payment_method = hookable_metadata.context.resource.method %}
<div class="row">
<div class="col-12 col-md-4 fw-bold">{{ 'sylius.ui.payment_method'|trans }}</div>
<div class="col-12 col-md-8" {{ sylius_test_html_attribute('method') }}>
<a href="{{ path('sylius_admin_payment_method_update', {'id' : payment_method.id }) }}">{{ payment_method.name }}</a>
</div>
</div>

View file

@ -0,0 +1,6 @@
<div class="row">
<div class="col-12 col-md-4 fw-bold">{{ 'sylius.ui.state'|trans }}</div>
<div class="col-12 col-md-8" {{ sylius_test_html_attribute('state') }}>
{{ include('@SyliusAdmin/payment_request/common/label/state/' ~ hookable_metadata.context.resource.state ~ '.html.twig') }}
</div>
</div>

View file

@ -0,0 +1,8 @@
{% set date_format = hookable_metadata.configuration.date_format|default('YYYY-MM-dd HH:mm:ss') %}
<div class="row">
<div class="col-12 col-md-4 fw-bold">{{ 'sylius.ui.updated_at'|trans }}</div>
<div class="col-12 col-md-8" {{ sylius_test_html_attribute('updated-at') }}>
{{ hookable_metadata.context.resource.updatedAt|format_datetime(pattern=date_format) }}
</div>
</div>

View file

@ -0,0 +1,10 @@
<div class="card mb-3">
<div class="card-header">
<div class="card-title">
{{ 'sylius.ui.payload'|trans }}
</div>
</div>
<div class="card-body">
<pre {{ sylius_test_html_attribute('payload') }}>{{ hookable_metadata.context.resource.payload|json_encode(constant('JSON_PRETTY_PRINT')) }}</pre>
</div>
</div>

Some files were not shown because too many files have changed in this diff Show more