Skip to content

Commit 4c6d53c

Browse files
author
shopwareBot
committed
[create-pull-request] automated change
1 parent 3049f90 commit 4c6d53c

8 files changed

+539
-7
lines changed
Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
---
2+
title: Checkout gateway
3+
date: 2024-04-01
4+
area: checkout
5+
tags: [checkout, app, payment, shipping, cart]
6+
---
7+
8+
# Checkout gateway
9+
10+
::: info
11+
This document represents an architecture decision record (ADR) and has been mirrored from the ADR section in our Shopware 6 repository.
12+
You can find the original version [here](https://github.com/shopware/shopware/blob/trunk/adr/2024-04-01-checkout-gateway.md)
13+
:::
14+
15+
# ADR: Enhanced Checkout Gateway Feature
16+
## Context
17+
In response to the evolving landscape of checkout decision-making, we propose the introduction of a centralized and opinionated solution.
18+
This solution aims to facilitate informed decisions during the checkout process based on both the cart contents and the current sales channel context.
19+
The app-system, in particular, stands to benefit significantly, enabling seamless communication with the app server.
20+
Presently, achieving such functionality is constrained to app scripts, limiting the capacity for making nuanced decisions during checkout based on app server logic.
21+
22+
Moreover, payment and shipping providers necessitate specific criteria for determining the availability of their respective methods.
23+
These criteria include considerations such as risk assessment related to the current customer and cart, unavailability criteria,
24+
merchant connection status validation (e.g., checking for correct credentials), and service availability testing (e.g., detecting provider outages).
25+
Additionally, these providers require the ability to block carts during checkout based on risk assessment decisions.
26+
27+
While this ADR focuses on the aforementioned features, the implementation is designed to allow for seamless future extensions.
28+
29+
## Decision
30+
### CheckoutGatewayInterface
31+
To address the outlined challenges, we propose the introduction of the CheckoutGatewayInterface.
32+
This interface will be invoked during the checkout process to determine a response tailored to the current cart and sales channel context.
33+
34+
```php
35+
<?php declare(strict_types=1);
36+
37+
namespace Shopware\Core\Checkout\Gateway;
38+
39+
use Shopware\Core\Checkout\Gateway\Command\Struct\CheckoutGatewayPayloadStruct;
40+
use Shopware\Core\Framework\Log\Package;
41+
42+
#[Package('checkout')]
43+
interface CheckoutGatewayInterface
44+
{
45+
/**
46+
* The input struct consists of the cart, sales channel context and currently available payment and shipping methods.
47+
*/
48+
public function process(CheckoutGatewayPayloadStruct): CheckoutGatewayResponse;
49+
}
50+
```
51+
52+
Plugin developers are encouraged to create custom implementations of the `CheckoutGatewayInterface` for their specific checkout logic based on decisions from external systems (e.g., ERP, PIM).
53+
54+
The `CheckoutGatewayResponse` will include an `EntityCollection` of payment and shipping methods suitable for the current context, along with a collection of `CartErrors`.
55+
The input struct and the response is designed for future extension, allowing for more intricate decision-making during checkout.
56+
57+
#### Store-API
58+
A new store API route, `CheckoutGatewayRoute` '/store-api/checkout/gateway', will be introduced.
59+
This route will call a `CheckoutGatewayInterface` implementation and respond accordingly,
60+
and is integral to `CartOrderRoute` requests, ensuring the cart's validity for checkout during the order process.
61+
62+
#### Storefront
63+
The default invocation of the `CheckoutGatewayRoute` will occur during the checkout-confirm page and edit-order page (so-called "after order").
64+
Any changes to the context (e.g., language, currency) will trigger a reload of the payment method selection, calling the app server again.
65+
66+
#### Checkout Gateway Commands
67+
For streamlined response manipulation by plugins and app servers alike, we propose an executable chain of `CheckoutGatewayCommands`.
68+
The implementation of the app-system will heavily rely on the command structure.
69+
However, it is encouraged, but not mandatory for a custom implementation plugin-system implementation of the `CheckoutGatewayInterface` to follow the command structure.
70+
71+
These commands, chosen from a predefined set, can be responded with by plugins and app servers.
72+
The initial release will include the following commands: `add-payment-method`, `remove-payment-method`, `add-shipping-method`, `remove-shipping-method`, and `add-cart-error`.
73+
Depending on the command, the payload may differ, necessitating updates to the documentation.
74+
We propose the use of a handler pattern, to facilitate the execution of these commands.
75+
Commands will be executed in the order provided in the response.
76+
77+
### App-System
78+
For the initial release, Shopware will support a single implementation of the `CheckoutGatewayInterface`, provided by the app-system.
79+
The `AppCheckoutGateway` will sequentially call active apps, but only if the app has a defined `checkout-gateway-url` in its manifest.xml file.
80+
81+
#### App Manifest
82+
To address challenges for apps, a new app endpoint can be defined in the manifest.xml.
83+
A new key `gateways` will be added to the manifest file, with a sub-key `checkout` to define the endpoint.
84+
The `gateways` key signalizes possible future similar endpoints for different purposes.
85+
The checkout gateway endpoint is configured using a new element called `checkout`.
86+
87+
```xml
88+
<?xml version="1.0" encoding="UTF-8"?>
89+
<manifest>
90+
<!-- ... -->
91+
92+
<gateways>
93+
<checkout>https://example.com/checkout/gateway</checkout>
94+
</gateways>
95+
</manifest>
96+
```
97+
98+
#### Checkout Gateway App Payload
99+
The app server will receive the current `SalesChannelContext`, `Cart`, and available payment and shipping methods as part of the payload.
100+
The `AppCheckoutGateway` will call the app server with this payload.
101+
102+
```json
103+
{
104+
"salesChannelContext": SalesChannelContextObject,
105+
"cart": CartObject,
106+
"paymentMethods": [
107+
"payment-method-technical-name-1",
108+
"payment-method-technical-name-2",
109+
"payment-method-technical-name-3",
110+
...
111+
],
112+
"shippingMethods": [
113+
"shipping-method-technical-name-1",
114+
"shipping-method-technical-name-2",
115+
"shipping-method-technical-name-3",
116+
...
117+
]
118+
}
119+
```
120+
121+
Note that the paymentMethods and shippingMethods arrays will only contain the technical names of the methods, not the full entities.
122+
123+
#### Checkout Gateway App Response
124+
125+
```json
126+
[
127+
{
128+
"command": "remove-payment-method",
129+
"payload": {
130+
"paymentMethodTechnicalName": "payment-myApp-payment-method"
131+
}
132+
},
133+
{
134+
"command": "add-cart-error",
135+
"payload": {
136+
"reason": "Payment method not available for this cart.",
137+
"level": 20,
138+
"blockOrder": true
139+
}
140+
}
141+
]
142+
```
143+
144+
#### Event
145+
A new event `CheckoutGatewayCommandsCollectedEvent` will be introduced.
146+
This event is dispatched after the `AppCheckoutGateway` has collected all commands from all app servers.
147+
It allows plugins to manipulate the commands before they are executed, based on the same payload the app servers retrieve.
148+
149+
## Consequences
150+
### App PHP SDK
151+
The app-php-sdk will be enhanced to support the new endpoint and data types, ensuring seamless integration with the command structure.
152+
The following adaptations will be made:
153+
154+
Checkout gateway requests with payload will be deserialized into a `CheckoutGatewayRequest` object.
155+
Checkout gateway responses will be deserialized into a `CheckoutGatewayResponse` object.
156+
Every possible checkout gateway command will have a class representing it, facilitating easy manipulation of its payload.
Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
---
2+
title: Replace Vuex with Pinia
3+
date: 2024-06-17
4+
area: admin
5+
tags: [admin, vuex, pinia]
6+
---
7+
8+
# Replace Vuex with Pinia
9+
10+
::: info
11+
This document represents an architecture decision record (ADR) and has been mirrored from the ADR section in our Shopware 6 repository.
12+
You can find the original version [here](https://github.com/shopware/shopware/blob/trunk/adr/2024-06-17-replace-vuex-with-pinia.md)
13+
:::
14+
15+
# ADR: Replace Vuex with Pinia
16+
## Context
17+
It was brought to our attention that the latest version of Vuex `4.1.0` contains a bug that destroys getter reactivity under specific circumstances. The proposed fix was to downgrade to `4.0.2`. However, downgrading was not possible as `4.0.2` contains other bugs that caused modules to fail.
18+
19+
## Decision
20+
Pinia is the new documented standard with Vue 3; therefore, we will switch to Pinia.
21+
22+
## Consequences
23+
### Removal of Vuex
24+
Below you will find an overview of what will be removed on which Shopware Version.
25+
26+
#### 6.7
27+
For Shopware 6.7 we want to transition all our modules but still leave the possibility for you to use Vuex for your own states.
28+
29+
- All `Shopware.State` functions will cause warnings to appear in the DevTools. For example `Shopware.State.registerModule is deprecated. Use Shopware.Store.register instead!`
30+
- All Vuex state definitions will be transitioned to Pinia:
31+
- src/module/sw-bulk-edit/state/sw-bulk-edit.state.js
32+
- src/module/sw-product/page/sw-product-detail/state.js
33+
- src/module/sw-category/page/sw-category-detail/state.js
34+
- src/module/sw-extension/store/extensions.store.ts
35+
- src/module/sw-settings-payment/state/overview-cards.store.ts
36+
- src/module/sw-settings-seo/component/sw-seo-url/state.js
37+
- src/module/sw-settings-shipping/page/sw-settings-shipping-detail/state.js
38+
- src/app/state/notification.store.js
39+
- src/app/state/session.store.js
40+
- src/app/state/system.store.js
41+
- src/app/state/admin-menu.store.js
42+
- src/app/state/admin-help-center.store.ts
43+
- src/app/state/license-violation.store.js
44+
- src/app/state/context.store.ts
45+
- src/app/state/error.store.js
46+
- src/app/state/settings-item.store.js
47+
- src/app/state/shopware-apps.store.ts
48+
- src/app/state/extension-entry-routes.js
49+
- src/app/state/marketing.store.js
50+
- src/app/state/extension-component-sections.store.ts
51+
- src/app/state/extensions.store.ts
52+
- src/app/state/tabs.store.ts
53+
- src/app/state/menu-item.store.ts
54+
- src/app/state/extension-sdk-module.store.ts
55+
- src/app/state/modals.store.ts
56+
- src/app/state/main-module.store.ts
57+
- src/app/state/action-button.store.ts
58+
- src/app/state/rule-conditions-config.store.js
59+
- src/app/state/sdk-location.store.ts
60+
- src/app/state/usage-data.store.ts
61+
- src/module/sw-flow/state/flow.state.js
62+
- src/module/sw-order/state/order.store.ts
63+
- src/module/sw-order/state/order-detail.store.js
64+
- src/module/sw-profile/state/sw-profile.state.js
65+
- src/module/sw-promotion-v2/page/sw-promotion-v2-detail/state.js
66+
67+
#### 6.8
68+
With Shopware 6.8 we will entirely remove everything Vuex related including the dependency.
69+
70+
- `Shopware.State` - Will be removed. Use `Shopware.Store` instead.
71+
- `src/app/init-pre/state.init.ts` - Will be removed. Use `src/app/init-pre/store.init.ts` instead.
72+
- `src/core/factory/state.factory.ts` - Will be removed without replacement.
73+
- Interface `VuexRootState` will be removed from `global.types.ty`. Use `PiniaRootState` instead.
74+
- Package `vuex` will be removed.
75+
76+
## Transition to Pinia
77+
Pinia calls its state-holding entities `stores`. Therefore, we decided to hold everything Pinia-related under `Shopware.Store`.
78+
The `Shopware.Store` implementation follows the Singleton pattern. The private constructor controls the creation of the Pinia root state.
79+
This root state must be injected into Vue before the first store can be registered. The `init-pre/store.init.ts` takes care of this.
80+
81+
### Best practices
82+
1. All Pinia Stores must be written in TypeScript
83+
2. All Stores will export a type or interface like the `cms-page.state.ts`
84+
3. The state property of the exported type must be reused for the state definition.
85+
86+
You can always orientate on the `cms-page.state.ts`. It contains all best practices.
87+
88+
For now, we have decided to limit the public API of `Shopware.Store` to the following:
89+
90+
```typescript
91+
/**
92+
* Returns a list of all registered Pinia store IDs.
93+
*/
94+
public list(): string[];
95+
96+
/**
97+
* Gets the Pinia store with the given ID.
98+
*/
99+
public get(id: keyof PiniaRootState): PiniaStore;
100+
101+
/**
102+
* Registers a new Pinia store. Works similarly to Vuex's registerModule.
103+
*/
104+
public register(options: DefineStoreOptions): void;
105+
106+
/**
107+
* Unregisters a Pinia store. Works similarly to Vuex's unregisterModule.
108+
*/
109+
public unregister(id: keyof PiniaRootState): void;
110+
```
111+
112+
The rest of the previous Vuex (`Shopware.State`) public API is implemented into Pinia itself.
113+
114+
```typescript
115+
// Setup
116+
const piniaStore = Shopware.Store.get('...');
117+
118+
// From Vuex subscribe
119+
Shopware.State.subscribe(...);
120+
// To Pinia $subscribe
121+
store.$subscribe(...);
122+
123+
// From Vuex commit
124+
Shopware.State.commit(...);
125+
// To Pinia action call
126+
store.someAction(...);
127+
128+
// From Vuex dispatch
129+
Shopware.State.dispatch(...);
130+
// To Pinia action call
131+
store.someAsyncAction(...);
132+
```
133+
134+
### Example Implementation
135+
To prove that Vuex and Pinia can co-exist during the transition period, we picked a private Vuex state and decided to transition it.
136+
We chose the `cmsPageState`, which is heavily used in many components. The transition went smoothly without any major disturbances.
137+
138+
How to transition a Vuex module into a Pinia store:
139+
1. In Pinia, there are no `mutations`. Place every mutation under `actions`.
140+
2. `state` needs to be an arrow function returning an object: `state: () => ({})`.
141+
3. `actions` no longer need to use the `state` argument. They can access everything with correct type support via `this`.
142+
4. Point 3 also applies to `getters`.
143+
5. Use `Shopware.Store.register` instead of `Shopware.State.registerModule`.
144+
145+
Let's look at a simple Vuex module and how to transition it:
146+
147+
```typescript
148+
// Old Vuex implementation
149+
Shopware.State.registerModule('example', {
150+
state: {
151+
id: '',
152+
},
153+
getters: {
154+
idStart(state) {
155+
return state.id.substring(0, 4);
156+
}
157+
},
158+
mutations: {
159+
setId(state, id) {
160+
state.id = id;
161+
}
162+
},
163+
actions: {
164+
async asyncFoo({ commit }, id) {
165+
// Do some async stuff
166+
return Promise.resolve(() => {
167+
commit('setId', id);
168+
169+
return id;
170+
});
171+
}
172+
}
173+
});
174+
175+
// New Pinia implementation
176+
// Notice that the mutation setId was removed! You can directly modify a Pinia store state after retrieving it with Shopware.Store.get.
177+
Shopware.Store.register({
178+
id: 'example',
179+
state: () => ({
180+
id: '',
181+
}),
182+
getters: {
183+
idStart: () => this.id.substring(0, 4),
184+
},
185+
actions: {
186+
async asyncFoo(id) {
187+
// Do some async stuff
188+
return Promise.resolve(() => {
189+
this.id = id;
190+
191+
return id;
192+
});
193+
}
194+
}
195+
});
196+
```

0 commit comments

Comments
 (0)