Compare commits

..

3 Commits

Author SHA1 Message Date
andrewrowanwallee 68592a9409 Release 6.1.17 2025-11-03 13:29:47 +01:00
andrewrowanwallee 1393f4ff7c Release 6.1.16 2025-10-01 10:15:25 +02:00
andrewrowanwallee d714cf2f84 release 6.1.15 2025-09-17 12:11:32 +02:00
55 changed files with 1220 additions and 1025 deletions
+8 -9
View File
@@ -1,14 +1,13 @@
# 7.0.1 # 6.1.17
## Feature - Sales channels now support different spaces
- Add plugin version metric - Upgraded SDK to include latest fallback CA Bundle
- Fixed error screen when returning from portal on failed payment
## Bugfix # 6.1.16
- Fixed error message when refund amount exceeds total - Fixed issue with pending orders remaining open
- Fixed bug where only 25 sales channels showed in the dropdown
- Removed erroneous logs
# 7.0.0 # 6.1.15
- Compatibility with Shopware 6.7.0 - Fixed issue with shipping costs not being processed correctly
# 6.1.14 # 6.1.14
- Disable Recreate Cart for Headless Storefront Order - Disable Recreate Cart for Headless Storefront Order
+9 -7
View File
@@ -1,11 +1,13 @@
# 7.0.1 # 6.1.17
- Plugin-Versionsmetrik hinzugefügt - Vertriebskanäle unterstützen jetzt verschiedene Bereiche
- Fehlermeldung behoben, wenn der Rückerstattungsbetrag den Gesamtbetrag überschreitet - SDK aktualisiert und enthält nun das neueste CA-Fallback-Bundle
- Fehler behoben, bei dem nur 25 Vertriebskanäle in der Dropdown-Liste angezeigt wurden - Fehlerbildschirm beim Zurückkehren vom Portal nach fehlgeschlagener Zahlung behoben
- Fehlerhafte Protokolle entfernt
# 7.0.0 # 6.1.16
- Kompatibilität mit Shopware 6.7.0 - Problem behoben, bei dem die Versandkosten nicht korrekt verarbeitet wurden
# 6.1.15
- Problem behoben, bei dem ausstehende Bestellungen offen blieben
# 6.1.14 # 6.1.14
Warenkorb neu erstellen für Headless Storefront Order deaktivieren Warenkorb neu erstellen für Headless Storefront Order deaktivieren
+10 -10
View File
@@ -3,20 +3,21 @@
VR Payment Integration for Shopware 6 VR Payment Integration for Shopware 6
============================= =============================
The VR Payment plugin wraps around the VR Payment API. This library facilitates your interaction with various services such as transactions. ## **Overview**
Please note that this plugin is for versions 6.5, 6.6 or 6.7. For the 6.4 plugin please visit [our Shopware 6.4 plugin](https://github.com/vr-payment/shopware-6-4). The VR Payment Payment Plugin integrates modern payment processing into Shopware 6, offering features like iFrame-based payments, refunds, captures, and PCI compliance. It supports seamless integration with the [VR Payment Portal](https://gateway.vr-payment.de/) for managing transactions and payment methods.
## Requirements ## Requirements
- Shopware 6.7.x, 6.6.x or 6.5.x. See table below. - **Shopware Version:** 6.5.x or 6.6.x (see [compatibility table](#compatibility)).
- PHP minimum version supported by the each shop version. - **PHP:** Minimum version as required by your Shopware installation (e.g., 7.4+).
- **VR Payment Account:** Obtain `Space ID`, `User ID`, and `API Key` from the [VR Payment Dashboard](https://gateway.vr-payment.de/).
## Documentation ## Documentation
- For English documentation click [here](https://docs.plugin-documentation.vr-payment.de/vr-payment/shopware-6/7.0.1/docs/en/documentation.html) - For English documentation click [here](https://docs.plugin-documentation.vr-payment.de/vr-payment/shopware-6/6.1.17/docs/en/documentation.html)
- Für die deutsche Dokumentation klicken Sie [hier](https://docs.plugin-documentation.vr-payment.de/vr-payment/shopware-6/7.0.1/docs/de/documentation.html) - Für die deutsche Dokumentation klicken Sie [hier](https://docs.plugin-documentation.vr-payment.de/vr-payment/shopware-6/6.1.17/docs/de/documentation.html)
- Pour la documentation Française, cliquez [ici](https://docs.plugin-documentation.vr-payment.de/vr-payment/shopware-6/7.0.1/docs/fr/documentation.html) - Pour la documentation Française, cliquez [ici](https://docs.plugin-documentation.vr-payment.de/vr-payment/shopware-6/6.1.17/docs/fr/documentation.html)
- Per la documentazione in tedesco, clicca [qui](https://docs.plugin-documentation.vr-payment.de/vr-payment/shopware-6/7.0.1/docs/it/documentation.html) - Per la documentazione in tedesco, clicca [qui](https://docs.plugin-documentation.vr-payment.de/vr-payment/shopware-6/6.1.17/docs/it/documentation.html)
## Installation ## Installation
@@ -66,8 +67,7 @@ Configure supported methods (e.g., credit cards, Apple Pay) via the [VR Payment
___________________________________________________________________________________ ___________________________________________________________________________________
| Shopware 6 version | Plugin major version | Supported until | | Shopware 6 version | Plugin major version | Supported until |
|-------------------------------|------------------------|------------------------| |-------------------------------|------------------------|------------------------|
| Shopware 6.7.x | 7.x | Further notice | | Shopware 6.6.x | 6.x | Further notice |
| Shopware 6.6.x | 6.x | December 2025 |
| Shopware 6.5.x | 5.x | October 2024 | | Shopware 6.5.x | 5.x | October 2024 |
----------------------------------------------------------------------------------- -----------------------------------------------------------------------------------
+4 -4
View File
@@ -53,11 +53,11 @@
"ext-json": "*", "ext-json": "*",
"ext-mbstring": "*", "ext-mbstring": "*",
"php": ">=8.2", "php": ">=8.2",
"shopware/core": "~6.7.0", "shopware/core": "~6.6.0",
"shopware/administration": "~6.7.0", "shopware/administration": "~6.6.0",
"shopware/storefront":"~6.7.0", "shopware/storefront":"~6.6.0",
"vrpayment/sdk": "^4.0.0" "vrpayment/sdk": "^4.0.0"
}, },
"type": "shopware-platform-plugin", "type": "shopware-platform-plugin",
"version": "7.0.1" "version": "6.1.17"
} }
+3 -3
View File
@@ -5,7 +5,7 @@
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<meta name="keywords" value="VR Payment, Shopware, Shopware Plugin, Payment, Payment Integration, Documentation"><meta name="description" value="The documentation for the Shopware 6 plugin that enables processing payments with VR Payment."> <meta name="keywords" value="VR Payment, Shopware, Shopware Plugin, Payment, Payment Integration, Documentation"><meta name="description" value="The documentation for the Shopware 6 plugin that enables processing payments with VR Payment.">
<link rel="canonical" href="https://plugin-documentation.wallee.com/wallee-payment/shopware-6/master/VRPaymentPayment/docs/de/documentation.html" /> <link rel="canonical" href="@WalleeCanonicalPath(https://plugin-documentation.wallee.com/wallee-payment, VRPaymentPayment/docs/de/documentation.html)" />
<title>VR Payment Zahlungs-Plugin für Shopware 6</title> <title>VR Payment Zahlungs-Plugin für Shopware 6</title>
<link href="assets/monokai-sublime.css" rel="stylesheet" /> <link href="assets/monokai-sublime.css" rel="stylesheet" />
<link href="assets/base.css" rel="stylesheet" /> <link href="assets/base.css" rel="stylesheet" />
@@ -23,7 +23,7 @@
</a> </a>
</li> </li>
<li> <li>
<a href="https://github.com/vr-payment/shopware-6/releases/tag/7.0.1/"> <a href="@WalleeReleasePath()">
Source Source
</a> </a>
</li> </li>
@@ -185,7 +185,7 @@ php bin/console plugin:install --activate --clearCache VRPaymentPayment</code></
</div> </div>
<div class="chapter-body"> <div class="chapter-body">
<div class="paragraph"> <div class="paragraph">
<p>Gehen Sie zu <a href="https://gateway.vr-payment.de/user/login/user/login">VR Payment</a> und erstellen Sie ein Konto, falls Sie noch keines haben.</p> <p>Gehen Sie zu <a href="https://gateway.vr-payment.de/user/login">VR Payment</a> und erstellen Sie ein Konto, falls Sie noch keines haben.</p>
</div><div class="admonitionblock tip"> </div><div class="admonitionblock tip">
<table> <table>
<tr> <tr>
+3 -3
View File
@@ -5,7 +5,7 @@
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<meta name="keywords" value="VR Payment, Shopware, Shopware Plugin, Payment, Payment Integration, Documentation"><meta name="description" value="The documentation for the Shopware 6 plugin that enables processing payments with VR Payment."> <meta name="keywords" value="VR Payment, Shopware, Shopware Plugin, Payment, Payment Integration, Documentation"><meta name="description" value="The documentation for the Shopware 6 plugin that enables processing payments with VR Payment.">
<link rel="canonical" href="https://plugin-documentation.wallee.com/wallee-payment/shopware-6/master/VRPaymentPayment/docs/en/documentation.html" /> <link rel="canonical" href="@WalleeCanonicalPath(https://plugin-documentation.wallee.com/wallee-payment, VRPaymentPayment/docs/en/documentation.html)" />
<title>VR Payment Shopware 6 Documentation</title> <title>VR Payment Shopware 6 Documentation</title>
<link href="assets/monokai-sublime.css" rel="stylesheet" /> <link href="assets/monokai-sublime.css" rel="stylesheet" />
<link href="assets/base.css" rel="stylesheet" /> <link href="assets/base.css" rel="stylesheet" />
@@ -23,7 +23,7 @@
</a> </a>
</li> </li>
<li> <li>
<a href="https://github.com/vr-payment/shopware-6/releases/tag/7.0.1/"> <a href="@WalleeReleasePath()">
Source Source
</a> </a>
</li> </li>
@@ -179,7 +179,7 @@ php bin/console plugin:install --activate --clearCache VRPaymentPayment</code></
</div> </div>
<div class="chapter-body"> <div class="chapter-body">
<div class="paragraph"> <div class="paragraph">
<p>Go to <a href="https://gateway.vr-payment.de/user/login/user/login">VR Payment</a> and create an Account if you do not already have one.</p> <p>Go to <a href="https://gateway.vr-payment.de/user/login">VR Payment</a> and create an Account if you do not already have one.</p>
</div><div class="admonitionblock tip"> </div><div class="admonitionblock tip">
<table> <table>
<tr> <tr>
+3 -3
View File
@@ -5,7 +5,7 @@
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<meta name="keywords" value="VR Payment, Shopware, Shopware Plugin, Payment, Payment Integration, Documentation"><meta name="description" value="The documentation for the Shopware 6 plugin that enables processing payments with VR Payment."> <meta name="keywords" value="VR Payment, Shopware, Shopware Plugin, Payment, Payment Integration, Documentation"><meta name="description" value="The documentation for the Shopware 6 plugin that enables processing payments with VR Payment.">
<link rel="canonical" href="https://plugin-documentation.wallee.com/wallee-payment/shopware-6/master/VRPaymentPayment/docs/fr/documentation.html" /> <link rel="canonical" href="@WalleeCanonicalPath(https://plugin-documentation.wallee.com/wallee-payment, VRPaymentPayment/docs/fr/documentation.html)" />
<title>Wallee Payment Plugin pour Shopware 6</title> <title>Wallee Payment Plugin pour Shopware 6</title>
<link href="assets/monokai-sublime.css" rel="stylesheet" /> <link href="assets/monokai-sublime.css" rel="stylesheet" />
<link href="assets/base.css" rel="stylesheet" /> <link href="assets/base.css" rel="stylesheet" />
@@ -23,7 +23,7 @@
</a> </a>
</li> </li>
<li> <li>
<a href="https://github.com/vr-payment/shopware-6/releases/tag/7.0.1/"> <a href="@WalleeReleasePath()">
Source Source
</a> </a>
</li> </li>
@@ -172,7 +172,7 @@ php bin/console plugin:install --activate --clearCache VRPaymentPayment</code></
</div> </div>
<div class="chapter-body"> <div class="chapter-body">
<div class="paragraph"> <div class="paragraph">
<p>Allez sur <a href="https://gateway.vr-payment.de/user/login/user/login">VR Payment</a> et créez un Compte si vous nen avez pas déjà un</p> <p>Allez sur hhttps://gateway.vr-payment.de/user/login[VR Payment] et créez un Compte si vous nen avez pas déjà un</p>
</div><div class="admonitionblock tip"> </div><div class="admonitionblock tip">
<table> <table>
<tr> <tr>
+3 -3
View File
@@ -5,7 +5,7 @@
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<meta name="keywords" value="VR Payment, Shopware, Shopware Plugin, Payment, Payment Integration, Documentation"><meta name="description" value="The documentation for the Shopware 6 plugin that enables processing payments with VR Payment."> <meta name="keywords" value="VR Payment, Shopware, Shopware Plugin, Payment, Payment Integration, Documentation"><meta name="description" value="The documentation for the Shopware 6 plugin that enables processing payments with VR Payment.">
<link rel="canonical" href="https://plugin-documentation.wallee.com/wallee-payment/shopware-6/master/VRPaymentPayment/docs/it/documentation.html" /> <link rel="canonical" href="@WalleeCanonicalPath(https://plugin-documentation.wallee.com/wallee-payment, VRPaymentPayment/docs/it/documentation.html)" />
<title>VR Payment Shopware 6 Documentation</title> <title>VR Payment Shopware 6 Documentation</title>
<link href="assets/monokai-sublime.css" rel="stylesheet" /> <link href="assets/monokai-sublime.css" rel="stylesheet" />
<link href="assets/base.css" rel="stylesheet" /> <link href="assets/base.css" rel="stylesheet" />
@@ -23,7 +23,7 @@
</a> </a>
</li> </li>
<li> <li>
<a href="https://github.com/vr-payment/shopware-6/releases/tag/7.0.1/"> <a href="@WalleeReleasePath()">
Source Source
</a> </a>
</li> </li>
@@ -179,7 +179,7 @@ php bin/console plugin:install --activate --clearCache VRPaymentPayment</code></
</div> </div>
<div class="chapter-body"> <div class="chapter-body">
<div class="paragraph"> <div class="paragraph">
<p>Andate su <a href="https://gateway.vr-payment.de/user/login/user/login">VR Payment</a> e create un account se non ne avete già uno.</p> <p>Andate su <a href="https://gateway.vr-payment.de/user/login">VR Payment</a> e create un account se non ne avete già uno.</p>
</div><div class="admonitionblock tip"> </div><div class="admonitionblock tip">
<table> <table>
<tr> <tr>
@@ -251,9 +251,9 @@ class PaymentMethodConfigurationService {
{ {
$data = []; $data = [];
$paymentMethodData = []; $paymentMethodData = [];
$salesChannelPaymentMethodData = [];
$criteria = (new Criteria())->addFilter(new EqualsFilter('state', 'ACTIVE')); $criteria = (new Criteria())->addFilter(new EqualsFilter('state', 'ACTIVE'))
->addFilter(new EqualsFilter('spaceId', $this->getSpaceId()));
/** /**
* @var $vRPaymentPMConfigurationRepository * @var $vRPaymentPMConfigurationRepository
@@ -276,7 +276,7 @@ class PaymentMethodConfigurationService {
]; ];
$paymentMethodData[] = [ $paymentMethodData[] = [
'id' => $paymentMethodConfigurationEntity->getId(), 'id' => $paymentMethodConfigurationEntity->getPaymentMethodId(),
'active' => false, 'active' => false,
]; ];
} }
@@ -349,35 +349,68 @@ class PaymentMethodConfigurationService {
*/ */
foreach ($paymentMethodConfigurations as $paymentMethodConfiguration) { foreach ($paymentMethodConfigurations as $paymentMethodConfiguration) {
$paymentMethodConfigurationEntity = $this->getPaymentMethodConfigurationEntity( $entity = $this->getPaymentMethodConfigurationEntity(
$paymentMethodConfiguration->getSpaceId(), $paymentMethodConfiguration->getSpaceId(),
$paymentMethodConfiguration->getId(), $paymentMethodConfiguration->getId(),
$context $context
); );
$id = is_null($paymentMethodConfigurationEntity) ? Uuid::randomHex() : $paymentMethodConfigurationEntity->getId();
$configId = $entity ? $entity->getId() : Uuid::randomHex();
$technicalName = $paymentMethodConfiguration->getName();
$paymentMethodId = $this->getOrCreatePaymentMethodId(
$technicalName,
VRPaymentPaymentHandler::class,
$context
);
$data = [ $data = [
'id' => $id, 'id' => $configId,
'paymentMethodConfigurationId' => $paymentMethodConfiguration->getId(), 'paymentMethodConfigurationId' => $paymentMethodConfiguration->getId(),
'paymentMethodId' => $id, 'paymentMethodId' => $paymentMethodId,
'data' => json_decode(strval($paymentMethodConfiguration), true), 'data' => json_decode(strval($paymentMethodConfiguration), true),
'sortOrder' => $paymentMethodConfiguration->getSortOrder(), 'sortOrder' => $paymentMethodConfiguration->getSortOrder(),
'spaceId' => $paymentMethodConfiguration->getSpaceId(), 'spaceId' => $paymentMethodConfiguration->getSpaceId(),
'state' => CreationEntityState::ACTIVE, 'state' => CreationEntityState::ACTIVE,
]; ];
$this->upsertPaymentMethod($id, $paymentMethodConfiguration, $context);
try { try {
$this->container->get(PaymentMethodConfigurationEntityDefinition::ENTITY_NAME . '.repository')->upsert([$data], $context); $this->upsertPaymentMethod($paymentMethodId, $paymentMethodConfiguration, $context);
$this->container
->get(PaymentMethodConfigurationEntityDefinition::ENTITY_NAME . '.repository')
->upsert([$data], $context);
} catch (\Exception $e) { } catch (\Exception $e) {
$this->logger->error($e->getMessage(), [$e->getTraceAsString()]); $this->logger->error($e->getMessage(), [$e->getTraceAsString()]);
} }
} }
} }
private function getOrCreatePaymentMethodId(string $technicalName, string $handlerIdentifier, Context $context): string
{
$criteria = new Criteria();
$criteria->addFilter(new EqualsFilter('technicalName', $technicalName));
$criteria->setLimit(1);
$existing = $this->paymentMethodRepository->search($criteria, $context)->first();
if ($existing !== null) {
return $existing->getId();
}
$paymentMethodId = Uuid::randomHex();
$this->paymentMethodRepository->upsert([[
'id' => $paymentMethodId,
'handlerIdentifier' => $handlerIdentifier,
'technicalName' => $technicalName,
'name' => $technicalName,
'active' => false,
]], $context);
return $paymentMethodId;
}
/** /**
* Fetch active merchant payment methods from VRPayment API * Fetch active merchant payment methods from VRPayment API
* *
@@ -482,8 +515,7 @@ class PaymentMethodConfigurationService {
string $id, string $id,
PaymentMethodConfiguration $paymentMethodConfiguration, PaymentMethodConfiguration $paymentMethodConfiguration,
Context $context Context $context
): void ): void {
{
/** @var PluginIdProvider $pluginIdProvider */ /** @var PluginIdProvider $pluginIdProvider */
$pluginIdProvider = $this->container->get(PluginIdProvider::class); $pluginIdProvider = $this->container->get(PluginIdProvider::class);
$pluginId = $pluginIdProvider->getPluginIdByBaseClass( $pluginId = $pluginIdProvider->getPluginIdByBaseClass(
@@ -502,16 +534,16 @@ class PaymentMethodConfigurationService {
'technicalName' => $paymentMethodConfiguration->getName(), 'technicalName' => $paymentMethodConfiguration->getName(),
]; ];
$data['mediaId'] = $this->upsertMedia($id, $paymentMethodConfiguration, $context); $mediaId = $this->upsertMedia($id, $paymentMethodConfiguration, $context);
if ($mediaId) {
$data = array_filter($data); $data['mediaId'] = $mediaId;
}
try { try {
$this->paymentMethodRepository->upsert([$data], $context); $this->paymentMethodRepository->upsert([$data], $context);
} catch (\Exception $e) { } catch (\Exception $e) {
$this->logger->error($e->getMessage(), [$e->getTraceAsString()]); $this->logger->error($e->getMessage(), [$e->getTraceAsString()]);
} }
} }
/** /**
@@ -613,46 +645,58 @@ class PaymentMethodConfigurationService {
* *
* @return string|null * @return string|null
*/ */
/**
* Upload or update Payment Method icons
*/
protected function upsertMedia(string $id, PaymentMethodConfiguration $paymentMethodConfiguration, Context $context): ?string protected function upsertMedia(string $id, PaymentMethodConfiguration $paymentMethodConfiguration, Context $context): ?string
{ {
try { try {
$existingRecord = $this->getMediaDefaultFolderForPaymentMethod($paymentMethodConfiguration, $context); $folderKey = 'payment_method_' . $paymentMethodConfiguration->getId();
if ($existingRecord->count() > 0) { // Check existing default folder
$id = $existingRecord->first()->getId(); $criteria = new Criteria();
$criteria->addFilter(new EqualsFilter('entity', $folderKey));
$existingFolder = $this->mediaDefaultFolderRepository->search($criteria, $context);
$folderId = $id;
if ($existingFolder->count() > 0) {
$folderId = $existingFolder->first()->getId();
} }
// Ensure default folder
$this->mediaDefaultFolderRepository->upsert([ $this->mediaDefaultFolderRepository->upsert([
[ [
'id' => $id, 'id' => $folderId,
'associationFields' => [], 'associationFields' => [],
'entity' => 'payment_method_' . $paymentMethodConfiguration->getId(), 'entity' => $folderKey,
], ],
], $context); ], $context);
// Ensure media folder
$this->mediaFolderRepository->upsert([ $this->mediaFolderRepository->upsert([
[ [
'id' => $id, 'id' => $folderId,
'defaultFolderId' => $id, 'defaultFolderId' => $folderId,
'name' => $paymentMethodConfiguration->getName(), 'name' => $paymentMethodConfiguration->getName(),
'useParentConfiguration' => false, 'useParentConfiguration' => false,
'configuration' => [], 'configuration' => [],
], ],
], $context); ], $context);
/** // Media insert/update
* @var \Shopware\Core\Content\Media\MediaDefinition
*/
$mediaDefinition = $this->container->get(MediaDefinition::class); $mediaDefinition = $this->container->get(MediaDefinition::class);
$this->mediaSerializer->setRegistry($this->serializerRegistry); $this->mediaSerializer->setRegistry($this->serializerRegistry);
$data = [ $data = [
'id' => $id, 'id' => $id,
'title' => $paymentMethodConfiguration->getName(), 'title' => $paymentMethodConfiguration->getName(),
'url' => $paymentMethodConfiguration->getResolvedImageUrl(), 'url' => $paymentMethodConfiguration->getResolvedImageUrl(),
'mediaFolderId' => $id, 'mediaFolderId' => $folderId,
]; ];
$data = $this->mediaSerializer->deserialize(new Config([], [], []), $mediaDefinition, $data); $data = $this->mediaSerializer->deserialize(new Config([], [], []), $mediaDefinition, $data);
$this->mediaRepository->upsert([$data], $context); $this->mediaRepository->upsert([$data], $context);
return $id; return $id;
} catch (\Exception $e) { } catch (\Exception $e) {
$this->logger->critical($e->getMessage(), [$e->getTraceAsString()]); $this->logger->critical($e->getMessage(), [$e->getTraceAsString()]);
@@ -115,11 +115,7 @@ class RefundController extends AbstractController
$apiClient = $settings->getApiClient(); $apiClient = $settings->getApiClient();
$transaction = $apiClient->getTransactionService()->read($settings->getSpaceId(), $transactionId); $transaction = $apiClient->getTransactionService()->read($settings->getSpaceId(), $transactionId);
$refund = $this->refundService->createRefundByAmount($transaction, $refundableAmount, $context); $this->refundService->createRefundByAmount($transaction, $refundableAmount, $context);
if ($refund === null) {
return new Response('refundExceedsAmount', Response::HTTP_BAD_REQUEST);
}
return new Response(null, Response::HTTP_NO_CONTENT); return new Response(null, Response::HTTP_NO_CONTENT);
} }
@@ -8,13 +8,14 @@ use Shopware\Core\{
Checkout\Cart\CartException, Checkout\Cart\CartException,
Checkout\Cart\LineItem\LineItem, Checkout\Cart\LineItem\LineItem,
Checkout\Order\OrderEntity, Checkout\Order\OrderEntity,
Checkout\Payment\Cart\PaymentTransactionStruct, Checkout\Payment\Cart\AsyncPaymentTransactionStruct,
Framework\Context, Framework\Context,
Framework\DataAbstractionLayer\Search\Criteria, Framework\DataAbstractionLayer\Search\Criteria,
Framework\DataAbstractionLayer\Search\Filter\EqualsFilter, Framework\DataAbstractionLayer\Search\Filter\EqualsFilter,
System\SalesChannel\SalesChannelContext System\SalesChannel\SalesChannelContext
}; };
use Shopware\Storefront\Page\Checkout\Confirm\CheckoutConfirmPageLoadedEvent; use Shopware\Storefront\Page\Checkout\Confirm\CheckoutConfirmPageLoadedEvent;
use Shopware\Storefront\Page\Account\Order\AccountEditOrderPageLoadedEvent;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface; use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use VRPayment\Sdk\{ use VRPayment\Sdk\{
Model\AddressCreate, Model\AddressCreate,
@@ -46,6 +47,9 @@ use VRPaymentPayment\Core\{
Util\Payload\TransactionPayload Util\Payload\TransactionPayload
}; };
use Shopware\Core\Checkout\Order\Aggregate\OrderLineItem\OrderLineItemEntity;
use Shopware\Core\Framework\Struct\ArrayEntity;
/** /**
* Class TransactionService * Class TransactionService
* *
@@ -115,7 +119,7 @@ class TransactionService
* *
* A redirect to the url will be performed * A redirect to the url will be performed
* *
* @param \Shopware\Core\Checkout\Payment\Cart\PaymentTransactionStruct $transaction * @param \Shopware\Core\Checkout\Payment\Cart\AsyncPaymentTransactionStruct $transaction
* @param \Shopware\Core\System\SalesChannel\SalesChannelContext $salesChannelContext * @param \Shopware\Core\System\SalesChannel\SalesChannelContext $salesChannelContext
* *
* @return string * @return string
@@ -124,14 +128,10 @@ class TransactionService
* @throws \VRPayment\Sdk\VersioningException * @throws \VRPayment\Sdk\VersioningException
*/ */
public function create( public function create(
PaymentTransactionStruct $transaction, AsyncPaymentTransactionStruct $transaction,
SalesChannelContext $salesChannelContext SalesChannelContext $salesChannelContext
): string ): string
{ {
$criteria = new Criteria([$transaction->getOrderTransactionId()]);
$criteria->addAssociation('order');
$orderTransaction = $this->container->get('order_transaction.repository')->search($criteria, $salesChannelContext->getContext())->first();
$salesChannelId = $salesChannelContext->getSalesChannel()->getId(); $salesChannelId = $salesChannelContext->getSalesChannel()->getId();
$settings = $this->settingsService->getSettings($salesChannelId); $settings = $this->settingsService->getSettings($salesChannelId);
$apiClient = $settings->getApiClient(); $apiClient = $settings->getApiClient();
@@ -169,7 +169,7 @@ class TransactionService
$redirectUrl = $this->container->get('router')->generate( $redirectUrl = $this->container->get('router')->generate(
'frontend.vrpayment.checkout.pay', 'frontend.vrpayment.checkout.pay',
['orderId' => $orderTransaction->getOrder()->getId(),], ['orderId' => $transaction->getOrder()->getId(),],
UrlGeneratorInterface::ABSOLUTE_URL UrlGeneratorInterface::ABSOLUTE_URL
); );
@@ -181,35 +181,44 @@ class TransactionService
$this->upsert( $this->upsert(
$createdTransaction, $createdTransaction,
$salesChannelContext->getContext(), $salesChannelContext->getContext(),
$orderTransaction->getPaymentMethodId(), $transaction->getOrderTransaction()->getPaymentMethodId(),
$orderTransaction->getOrder()->getSalesChannelId() $transaction->getOrder()->getSalesChannelId()
);
$salesChannelContext->getContext()->addExtension(
'checkoutState',
new ArrayEntity([
'transactionId' => null,
'addressHash' => null,
'currency' => null,
])
);
$salesChannelContext->getContext()->addExtension(
'possibleMethods',
new ArrayEntity(['ids' => []])
); );
$_SESSION['transactionId'] = null;
$_SESSION['arrayOfPossibleMethods'] = null;
$_SESSION['addressCheck'] = null;
$_SESSION['currencyCheck'] = null;
$this->holdDelivery($orderTransaction->getOrder()->getId(), $salesChannelContext->getContext()); $this->holdDelivery($transaction->getOrder()->getId(), $salesChannelContext->getContext());
return $redirectUrl; return $redirectUrl;
} }
/** /**
* @param \Shopware\Core\Checkout\Payment\Cart\PaymentTransactionStruct $transaction * @param \Shopware\Core\Checkout\Payment\Cart\AsyncPaymentTransactionStruct $transaction
* @param \Shopware\Core\Framework\Context $context * @param \Shopware\Core\Framework\Context $context
* @param int $vrpaymentTransactionId * @param int $vrpaymentTransactionId
* @param int $spaceId * @param int $spaceId
*/ */
protected function addVRPaymentTransactionId( protected function addVRPaymentTransactionId(
PaymentTransactionStruct $transaction, AsyncPaymentTransactionStruct $transaction,
Context $context, Context $context,
int $vrpaymentTransactionId, int $vrpaymentTransactionId,
int $spaceId int $spaceId
): void ): void
{ {
$data = [ $data = [
'id' => $transaction->getOrderTransactionId(), 'id' => $transaction->getOrderTransaction()->getId(),
'customFields' => [ 'customFields' => [
TransactionPayload::ORDER_TRANSACTION_CUSTOM_FIELDS_VRPAYMENT_TRANSACTION_ID => $vrpaymentTransactionId, TransactionPayload::ORDER_TRANSACTION_CUSTOM_FIELDS_VRPAYMENT_TRANSACTION_ID => $vrpaymentTransactionId,
TransactionPayload::ORDER_TRANSACTION_CUSTOM_FIELDS_VRPAYMENT_SPACE_ID => $spaceId, TransactionPayload::ORDER_TRANSACTION_CUSTOM_FIELDS_VRPAYMENT_SPACE_ID => $spaceId,
@@ -347,7 +356,7 @@ class TransactionService
* *
* @return \Shopware\Core\Checkout\Order\OrderEntity * @return \Shopware\Core\Checkout\Order\OrderEntity
*/ */
protected function getOrderEntity(string $orderId, Context $context): OrderEntity private function getOrderEntity(string $orderId, Context $context): OrderEntity
{ {
try { try {
$criteria = (new Criteria([$orderId]))->addAssociations(['deliveries']); $criteria = (new Criteria([$orderId]))->addAssociations(['deliveries']);
@@ -482,14 +491,18 @@ class TransactionService
/** /**
* @param SalesChannelContext $salesChannelContext * @param SalesChannelContext $salesChannelContext
* @param CheckoutConfirmPageLoadedEvent|null $event * @param $event
* @return int * @return int
*/ */
public function createPendingTransaction(SalesChannelContext $salesChannelContext, ?CheckoutConfirmPageLoadedEvent $event = null): int
public function createPendingTransaction(SalesChannelContext $salesChannelContext, $event = null): int
{ {
$expiredTransaction = true; $expiredTransaction = true;
$transactionId = $_SESSION['transactionId'] ?? null; $transactionId = $_SESSION['transactionId'] ?? null;
$settings = $this->settingsService->getValidSettings($salesChannelContext->getSalesChannel()->getId()); $settings = $this->settingsService->getValidSettings($salesChannelContext->getSalesChannel()->getId());
if (!$settings) {
throw new \Exception('Space settings not configured');
}
if ($transactionId) { if ($transactionId) {
$transactionService = $settings->getApiClient()->getTransactionService(); $transactionService = $settings->getApiClient()->getTransactionService();
@@ -498,6 +511,7 @@ class TransactionService
TransactionState::DECLINE, TransactionState::DECLINE,
TransactionState::FAILED, TransactionState::FAILED,
TransactionState::VOIDED, TransactionState::VOIDED,
null
]; ];
if (!in_array($pendingTransaction->getState(), $failedStates)) { if (!in_array($pendingTransaction->getState(), $failedStates)) {
$expiredTransaction = false; $expiredTransaction = false;
@@ -571,6 +585,7 @@ class TransactionService
$lineItems = []; $lineItems = [];
if ($event) { if ($event) {
if ($event instanceof CheckoutConfirmPageLoadedEvent) {
$cartLineItems = $event->getPage()->getCart()->getLineItems()->getElements(); $cartLineItems = $event->getPage()->getCart()->getLineItems()->getElements();
foreach ($cartLineItems as $cartLineItem) { foreach ($cartLineItems as $cartLineItem) {
if ($cartLineItem->getType() === CustomProductsLineItemTypes::LINE_ITEM_TYPE_CUSTOMIZED_PRODUCTS) { if ($cartLineItem->getType() === CustomProductsLineItemTypes::LINE_ITEM_TYPE_CUSTOMIZED_PRODUCTS) {
@@ -578,6 +593,12 @@ class TransactionService
} }
$lineItems[] = $this->createTempLineItem($cartLineItem); $lineItems[] = $this->createTempLineItem($cartLineItem);
} }
} elseif ($event instanceof AccountEditOrderPageLoadedEvent) {
$order = $event->getPage()->getOrder();
foreach ($order->getLineItems() as $orderLineItem) {
$lineItems[] = $this->createTempLineItem($orderLineItem);
}
}
} }
$customerId = ""; $customerId = "";
@@ -735,14 +756,26 @@ class TransactionService
* @param LineItem $productData * @param LineItem $productData
* @return LineItemCreate * @return LineItemCreate
*/ */
private function createTempLineItem(LineItem $productData): LineItemCreate private function createTempLineItem($productData): LineItemCreate
{ {
$lineItem = new LineItemCreate(); $lineItem = new LineItemCreate();
if ($productData instanceof LineItem) {
$lineItem->setName($productData->getLabel()); $lineItem->setName($productData->getLabel());
$lineItem->setUniqueId($productData->getId()); $lineItem->setUniqueId($productData->getId());
$lineItem->setSku($productData->getId()); $lineItem->setSku($productData->getReferencedId() ?? $productData->getId());
$lineItem->setQuantity($productData->getQuantity()); $lineItem->setQuantity($productData->getQuantity());
$lineItem->setAmountIncludingTax($productData->getPrice()->getUnitPrice()); $lineItem->setAmountIncludingTax($productData->getPrice()->getUnitPrice());
} elseif ($productData instanceof OrderLineItemEntity) {
$lineItem->setName($productData->getLabel());
$lineItem->setUniqueId($productData->getId());
$lineItem->setSku($productData->getProductId() ?? $productData->getIdentifier() ?? $productData->getId());
$lineItem->setQuantity($productData->getQuantity());
$lineItem->setAmountIncludingTax($productData->getUnitPrice());
} else {
throw new \InvalidArgumentException('Unsupported line item type: ' . get_class($productData));
}
$lineItem->setType(LineItemType::PRODUCT); $lineItem->setType(LineItemType::PRODUCT);
return $lineItem; return $lineItem;
@@ -226,6 +226,14 @@ class WebHookController extends AbstractController {
$this->settings = $this->settingsService->getSettings($salesChannelId); $this->settings = $this->settingsService->getSettings($salesChannelId);
$signature = $request->server->get('HTTP_X_SIGNATURE'); $signature = $request->server->get('HTTP_X_SIGNATURE');
$requestJson = json_decode($request->getContent(), true); $requestJson = json_decode($request->getContent(), true);
if ($requestJson['eventId'] == null && $requestJson['entityId'] == null && $requestJson['listenerEntityId'] == null && $requestJson['listenerEntityId'] == null && $requestJson['listenerEntityTechnicalName'] == null && $requestJson['spaceId'] == null) {
throw new \InvalidArgumentException('Empty webhook');
}
if (!$this->settings->getSpaceId() || !$this->settings->getUserId() || !$this->settings->getApplicationKey()) {
throw new \InvalidArgumentException('Not correct webhook configuration for salesChannelId: ' . $salesChannelId . ' Debug: ' . var_dump($requestJson));
}
$apiClient = $this->settings->getApiClient(); $apiClient = $this->settings->getApiClient();
$callBackData->assign($requestJson); $callBackData->assign($requestJson);
@@ -1,59 +0,0 @@
<?php declare(strict_types=1);
namespace VRPaymentPayment\Core\Checkout\Cart;
use Shopware\Core\Checkout\Cart\AbstractCartPersister;
use Shopware\Core\Checkout\Cart\Cart;
use Shopware\Core\Checkout\Payment\PaymentMethodEntity;
use Shopware\Core\System\SalesChannel\SalesChannelContext;
use VRPaymentPayment\Core\Checkout\PaymentHandler\VRPaymentPaymentHandler;
class CustomCartPersister extends AbstractCartPersister
{
private AbstractCartPersister $inner;
public function __construct(AbstractCartPersister $inner)
{
$this->inner = $inner;
}
public function delete(string $token, SalesChannelContext $context): void
{
if (!$context->getContext()->hasState('do-cart-delete') && $this->isWhiteLabelPayment($context)) {
return;
}
$this->inner->delete($token, $context);
}
public function load(string $token, SalesChannelContext $context): Cart
{
return $this->inner->load($token, $context);
}
public function save(Cart $cart, SalesChannelContext $context): void
{
$this->inner->save($cart, $context);
}
public function replace(string $oldToken, string $newToken, SalesChannelContext $context): void
{
$this->inner->replace($oldToken, $newToken, $context);
}
public function getDecorated(): AbstractCartPersister
{
return $this->inner;
}
private function isWhiteLabelPayment(SalesChannelContext $context): bool
{
$paymentMethod = $context->getPaymentMethod();
if (!$paymentMethod instanceof PaymentMethodEntity) {
return false;
}
return $paymentMethod->getHandlerIdentifier() === VRPaymentPaymentHandler::class;
}
}
@@ -4,30 +4,16 @@ namespace VRPaymentPayment\Core\Checkout\PaymentHandler;
use Psr\Log\LoggerInterface; use Psr\Log\LoggerInterface;
use Shopware\Core\{ use Shopware\Core\{
Checkout\Order\Aggregate\OrderTransaction\OrderTransactionEntity,
Checkout\Order\Aggregate\OrderTransaction\OrderTransactionStateHandler, Checkout\Order\Aggregate\OrderTransaction\OrderTransactionStateHandler,
Checkout\Payment\Cart\PaymentTransactionStruct, Checkout\Payment\Cart\AsyncPaymentTransactionStruct,
Checkout\Payment\Cart\PaymentHandler\AbstractPaymentHandler, Checkout\Payment\Cart\PaymentHandler\AsynchronousPaymentHandlerInterface,
Checkout\Payment\Cart\PaymentHandler\PaymentHandlerType, Checkout\Payment\Exception\AsyncPaymentFinalizeException,
Checkout\Payment\Exception\AsyncPaymentProcessException,
Checkout\Payment\PaymentException, Checkout\Payment\PaymentException,
Checkout\Payment\Exception\CustomerCanceledAsyncPaymentException, Checkout\Payment\Exception\CustomerCanceledAsyncPaymentException,
Framework\App\AppException,
Framework\Api\Context\SalesChannelApiSource,
Framework\Context,
Framework\DataAbstractionLayer\EntityRepository,
Framework\DataAbstractionLayer\Search\Criteria,
Framework\DataAbstractionLayer\Search\Sorting\FieldSorting,
Framework\Struct\Struct,
Framework\Validation\DataBag\RequestDataBag, Framework\Validation\DataBag\RequestDataBag,
System\SalesChannel\Context\SalesChannelContextService, System\SalesChannel\SalesChannelContext
System\SalesChannel\Context\SalesChannelContextServiceParameters
}; };
use Shopware\Core\Framework\Util\Random;
use VRPaymentPayment\Core\Checkout\Cart\CustomCartPersister;
use Shopware\Core\Checkout\Cart\Cart;
use Shopware\Core\Checkout\Cart\CartPersister;
use Shopware\Core\System\SalesChannel\SalesChannelContext;
use Symfony\Component\{ use Symfony\Component\{
HttpFoundation\RedirectResponse, HttpFoundation\RedirectResponse,
HttpFoundation\Request HttpFoundation\Request
@@ -41,14 +27,9 @@ use VRPaymentPayment\Core\Api\Transaction\Service\TransactionService;
* *
* @package VRPaymentPayment\Core\Checkout\PaymentHandler * @package VRPaymentPayment\Core\Checkout\PaymentHandler
*/ */
class VRPaymentPaymentHandler extends AbstractPaymentHandler class VRPaymentPaymentHandler implements AsynchronousPaymentHandlerInterface
{ {
/**
* @var CustomCartPersister
*/
private CustomCartPersister $cartPersister;
/** /**
* @var \VRPaymentPayment\Core\Api\Transaction\Service\TransactionService * @var \VRPaymentPayment\Core\Api\Transaction\Service\TransactionService
*/ */
@@ -63,34 +44,22 @@ class VRPaymentPaymentHandler extends AbstractPaymentHandler
*/ */
private $orderTransactionStateHandler; private $orderTransactionStateHandler;
protected SalesChannelContextService $salesChannelContextService;
protected EntityRepository $orderTransactionRepository;
/** /**
* VRPaymentPaymentHandler constructor. * VRPaymentPaymentHandler constructor.
* *
* @param \VRPaymentPayment\Core\Api\Transaction\Service\TransactionService $transactionService * @param \VRPaymentPayment\Core\Api\Transaction\Service\TransactionService $transactionService
* @param \Shopware\Core\Checkout\Order\Aggregate\OrderTransaction\OrderTransactionStateHandler $orderTransactionStateHandler * @param \Shopware\Core\Checkout\Order\Aggregate\OrderTransaction\OrderTransactionStateHandler $orderTransactionStateHandler
* @param SalesChannelContextService $salesChannelContextService
* @param EntityRepository $orderTransactionRepository
*/ */
public function __construct( public function __construct(TransactionService $transactionService, OrderTransactionStateHandler $orderTransactionStateHandler)
CustomCartPersister $cartPersister, {
TransactionService $transactionService,
OrderTransactionStateHandler $orderTransactionStateHandler,
SalesChannelContextService $salesChannelContextService,
EntityRepository $orderTransactionRepository
) {
$this->cartPersister = $cartPersister;
$this->transactionService = $transactionService; $this->transactionService = $transactionService;
$this->orderTransactionStateHandler = $orderTransactionStateHandler; $this->orderTransactionStateHandler = $orderTransactionStateHandler;
$this->salesChannelContextService = $salesChannelContextService;
$this->orderTransactionRepository = $orderTransactionRepository;
} }
/** /**
* @param \Psr\Log\LoggerInterface $logger * @param \Psr\Log\LoggerInterface $logger
* @internal
* @required
* *
*/ */
public function setLogger(LoggerInterface $logger): void public function setLogger(LoggerInterface $logger): void
@@ -104,123 +73,78 @@ class VRPaymentPaymentHandler extends AbstractPaymentHandler
* *
* A redirect to the url will be performed * A redirect to the url will be performed
* *
* @param \Symfony\Component\HttpFoundation\Request * @param \Shopware\Core\Checkout\Payment\Cart\AsyncPaymentTransactionStruct $transaction
* @param \Shopware\Core\Checkout\Payment\Cart\PaymentTransactionStruct $transaction * @param \Shopware\Core\Framework\Validation\DataBag\RequestDataBag $dataBag
* @param \Shopware\Core\Framework\Context $context * @param \Shopware\Core\System\SalesChannel\SalesChannelContext $salesChannelContext
* @param \Shopware\Core\Framework\Struct\Struct $validateStruct
* @return \Symfony\Component\HttpFoundation\RedirectResponse * @return \Symfony\Component\HttpFoundation\RedirectResponse
*/ */
public function pay( public function pay(
Request $request, AsyncPaymentTransactionStruct $transaction,
PaymentTransactionStruct $transaction, RequestDataBag $dataBag,
Context $context, SalesChannelContext $salesChannelContext
?Struct $validateStruct
): RedirectResponse ): RedirectResponse
{ {
try { try {
$orderTransactionId = $transaction->getOrderTransactionId();
$orderTransaction = $this->orderTransactionRepository->search(
(new Criteria([$orderTransactionId]))
->addAssociation('order'), $context
)->getEntities()->first();
$contextSource = $context->getSource();
if ($contextSource instanceof SalesChannelApiSource) {
$salesChannelContextId = $contextSource->getSalesChannelId();
}
$parameters = new SalesChannelContextServiceParameters($salesChannelContextId, $request->getSession()->get("sw-context-token", Random::getAlphanumericString(32)), originalContext: $context);
$salesChannelContext = $this->salesChannelContextService->get($parameters);
$redirectUrl = $transaction->getReturnUrl(); $redirectUrl = $transaction->getReturnUrl();
if ($transaction->getOrder()->getAmountTotal() > 0) {
if ($orderTransaction->getOrder()->getAmountTotal() > 0) {
$transactionId = $_SESSION['transactionId'] ?? null; $transactionId = $_SESSION['transactionId'] ?? null;
if ($transactionId === null) { if ($transactionId === null) {
$this->transactionService->createPendingTransaction($transaction, $salesChannelContext); $this->transactionService->createPendingTransaction($salesChannelContext);
} }
$redirectUrl = $this->transactionService->create($transaction, $salesChannelContext); $redirectUrl = $this->transactionService->create($transaction, $salesChannelContext);
} }
return new RedirectResponse($redirectUrl); return new RedirectResponse($redirectUrl);
} catch (\Throwable $e) { } catch (\Exception $e) {
unset($_SESSION['transactionId']); unset($_SESSION['transactionId']);
$errorMessage = 'An error occurred during the communication with external payment gateway : ' . $e->getMessage(); $errorMessage = 'An error occurred during the communication with external payment gateway : ' . $e->getMessage();
$this->logger->critical($errorMessage); $this->logger->critical($errorMessage);
throw PaymentException::customerCanceled($transaction->getOrderTransaction()->getId(), $errorMessage); throw new \Exception($transaction->getOrderTransaction()->getId() . ': ' . $errorMessage);
} }
} }
/** /**
* The finalize function will be called when the user is redirected back to shop from the payment gateway. * The finalize function will be called when the user is redirected back to shop from the payment gateway.
* *
* Throw a @param \Shopware\Core\Checkout\Payment\Cart\AsyncPaymentTransactionStruct $transaction
* @param \Symfony\Component\HttpFoundation\Request $request * @param \Symfony\Component\HttpFoundation\Request $request
* @param \Shopware\Core\Checkout\Payment\Cart\PaymentTransactionStruct $transaction * @param \Shopware\Core\System\SalesChannel\SalesChannelContext $salesChannelContext
* @param \Shopware\Core\Framework\Context $context
* @throws \VRPayment\Sdk\ApiException * @throws \VRPayment\Sdk\ApiException
* @throws \VRPayment\Sdk\Http\ConnectionException * @throws \VRPayment\Sdk\Http\ConnectionException
* @throws \VRPayment\Sdk\VersioningException * @throws \VRPayment\Sdk\VersioningException
* @throws \Exception when the payment was canceled by the customer * @see AsyncPaymentFinalizeException exception if an error ocurres while calling an external payment API
* Throw a @see CustomerCanceledAsyncPaymentException exception if the customer canceled the payment process on
* payment provider page
*
*/ */
public function finalize( public function finalize(
AsyncPaymentTransactionStruct $transaction,
Request $request, Request $request,
PaymentTransactionStruct $transaction, SalesChannelContext $salesChannelContext
Context $context
): void ): void
{ {
$orderTransactionId = $transaction->getOrderTransactionId(); if ($transaction->getOrder()->getAmountTotal() > 0) {
$orderTransaction = $this->orderTransactionRepository->search(
(new Criteria([$orderTransactionId]))
->addAssociation('order'), $context
)->getEntities()->first();
if ($orderTransaction->getOrder()->getAmountTotal() > 0) {
$transactionEntity = $this->transactionService->getByOrderId( $transactionEntity = $this->transactionService->getByOrderId(
$orderTransaction->getOrder()->getId(), $transaction->getOrder()->getId(),
$context $salesChannelContext->getContext()
); );
$vRPaymentTransaction = $this->transactionService->read( $vRPaymentTransaction = $this->transactionService->read(
$transactionEntity->getTransactionId(), $transactionEntity->getTransactionId(),
$transactionEntity->getSalesChannelId() $salesChannelContext->getSalesChannel()->getId()
); );
if (in_array($vRPaymentTransaction->getState(), [TransactionState::FAILED])) { if (in_array($vRPaymentTransaction->getState(), [TransactionState::FAILED])) {
$errorMessage = strtr('Customer canceled payment for :orderId on SalesChannel :salesChannelName', [ $errorMessage = strtr('Customer canceled payment for :orderId on SalesChannel :salesChannelName', [
':orderId' => $orderTransaction->getOrder()->getId(), ':orderId' => $transaction->getOrder()->getId(),
':salesChannelName' => $transactionEntity->getSalesChannelId(), ':salesChannelName' => $salesChannelContext->getSalesChannel()->getName(),
]); ]);
unset($_SESSION['transactionId']); unset($_SESSION['transactionId']);
$this->logger->info($errorMessage); $this->logger->info($errorMessage);
throw PaymentException::customerCanceled($transaction->getOrderTransaction()->getId(), $errorMessage); throw PaymentException::customerCanceled($transaction->getOrderTransaction()->getId(), $errorMessage);
} }
} else { } else {
$this->orderTransactionStateHandler->paid($orderTransaction->getId(), $context); $this->orderTransactionStateHandler->paid($transaction->getOrderTransaction()->getId(), $salesChannelContext->getContext());
}
$token = $request->getSession()->get('sw-context-token');
if ($token) {
$salesChannelId = $transactionEntity->getSalesChannelId();
$parameters = new SalesChannelContextServiceParameters($salesChannelId, $token, originalContext: $context);
$salesChannelContext = $this->salesChannelContextService->get($parameters);
$salesChannelContext->getContext()->addState('do-cart-delete');
$this->logger->info('Clearing cart with token: ' . $token);
$this->cartPersister->delete($salesChannelContext->getToken(), $salesChannelContext);
} }
} }
/**
* {@inheritDoc}
*/
public function supports(
PaymentHandlerType $type,
string $paymentMethodId,
Context $context
): bool {
if ($type === PaymentHandlerType::RECURRING) {
return false;
}
return true;
}
} }
@@ -2,15 +2,21 @@
namespace VRPaymentPayment\Core\Storefront\Checkout\Controller; namespace VRPaymentPayment\Core\Storefront\Checkout\Controller;
use Psr\Log\LoggerInterface; use Psr\{
Log\LoggerInterface,
Cache\CacheItemPoolInterface
};
use Shopware\Core\{ use Shopware\Core\{
Checkout\Payment\PaymentException,
Checkout\Cart\Cart, Checkout\Cart\Cart,
Checkout\Cart\CartException, Checkout\Cart\CartException,
Checkout\Cart\LineItemFactoryRegistry, Checkout\Cart\LineItemFactoryRegistry,
Checkout\Cart\SalesChannel\CartService, Checkout\Cart\SalesChannel\CartService,
Checkout\Order\Aggregate\OrderLineItem\OrderLineItemCollection, Checkout\Order\Aggregate\OrderLineItem\OrderLineItemCollection,
Checkout\Order\Aggregate\OrderLineItem\OrderLineItemEntity, Checkout\Order\Aggregate\OrderLineItem\OrderLineItemEntity,
Checkout\Order\Aggregate\OrderTransaction\OrderTransactionStateHandler,
Checkout\Order\OrderEntity, Checkout\Order\OrderEntity,
Checkout\Order\OrderDefinition,
Checkout\Order\SalesChannel\AbstractOrderRoute, Checkout\Order\SalesChannel\AbstractOrderRoute,
Framework\Context, Framework\Context,
Framework\DataAbstractionLayer\Search\Criteria, Framework\DataAbstractionLayer\Search\Criteria,
@@ -21,7 +27,9 @@ use Shopware\Core\{
Framework\Uuid\Uuid, Framework\Uuid\Uuid,
Framework\Uuid\Exception\InvalidUuidException, Framework\Uuid\Exception\InvalidUuidException,
Framework\Validation\DataBag\RequestDataBag, Framework\Validation\DataBag\RequestDataBag,
System\SalesChannel\SalesChannelContext System\SalesChannel\SalesChannelContext,
System\StateMachine\StateMachineRegistry,
System\StateMachine\Transition,
}; };
use Shopware\Storefront\{ use Shopware\Storefront\{
Controller\StorefrontController, Controller\StorefrontController,
@@ -31,9 +39,13 @@ use Shopware\Storefront\{
use Symfony\Component\{ use Symfony\Component\{
HttpFoundation\Request, HttpFoundation\Request,
HttpFoundation\Response, HttpFoundation\Response,
HttpFoundation\RedirectResponse,
Routing\Attribute\Route, Routing\Attribute\Route,
Routing\Generator\UrlGeneratorInterface Routing\Generator\UrlGeneratorInterface,
Cache\Adapter\FilesystemAdapter,
DependencyInjection\ParameterBag\ParameterBagInterface
}; };
use Symfony\Contracts\Cache\ItemInterface;
use VRPayment\Sdk\{ use VRPayment\Sdk\{
Model\Transaction, Model\Transaction,
Model\TransactionState Model\TransactionState
@@ -43,10 +55,10 @@ use VRPaymentPayment\Core\{
Settings\Options\Integration, Settings\Options\Integration,
Settings\Service\SettingsService, Settings\Service\SettingsService,
Storefront\Checkout\Struct\CheckoutPageData, Storefront\Checkout\Struct\CheckoutPageData,
Util\Payload\CustomProducts\CustomProductsLineItemTypes Util\Payload\CustomProducts\CustomProductsLineItemTypes,
Util\Payload\TransactionPayload
}; };
/** /**
* Class CheckoutController * Class CheckoutController
* *
@@ -57,6 +69,18 @@ use VRPaymentPayment\Core\{
#[Route(defaults: ['_routeScope' => ['storefront']])] #[Route(defaults: ['_routeScope' => ['storefront']])]
class CheckoutController extends StorefrontController { class CheckoutController extends StorefrontController {
public const ORDER_STATE_CANCEL = 'cancel';
/**
* @var \Shopware\Core\System\StateMachine\StateMachineRegistry
*/
private $stateMachineRegistry;
/**
* @var \Shopware\Core\Checkout\Order\Aggregate\OrderTransaction\OrderTransactionStateHandler
*/
protected $orderTransactionStateHandler;
/** /**
* @var \Shopware\Storefront\Page\GenericPageLoader * @var \Shopware\Storefront\Page\GenericPageLoader
*/ */
@@ -97,6 +121,11 @@ class CheckoutController extends StorefrontController {
*/ */
private $orderRoute; private $orderRoute;
/**
* @var \Psr\Cache\CacheItemPoolInterface
*/
private CacheItemPoolInterface $cache;
/** /**
* PaymentController constructor. * PaymentController constructor.
* *
@@ -106,6 +135,9 @@ class CheckoutController extends StorefrontController {
* @param \VRPaymentPayment\Core\Api\Transaction\Service\TransactionService $transactionService * @param \VRPaymentPayment\Core\Api\Transaction\Service\TransactionService $transactionService
* @param \Shopware\Storefront\Page\GenericPageLoaderInterface $genericLoader * @param \Shopware\Storefront\Page\GenericPageLoaderInterface $genericLoader
* @param \Shopware\Core\Checkout\Order\SalesChannel\AbstractOrderRoute $orderRoute * @param \Shopware\Core\Checkout\Order\SalesChannel\AbstractOrderRoute $orderRoute
* @param \Shopware\Core\Checkout\Order\Aggregate\OrderTransaction\OrderTransactionStateHandler $orderTransactionStateHandler
* @param \Shopware\Core\System\StateMachine\StateMachineRegistry $stateMachineRegistry
* @param Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface $params
*/ */
public function __construct( public function __construct(
LineItemFactoryRegistry $lineItemFactoryRegistry, LineItemFactoryRegistry $lineItemFactoryRegistry,
@@ -113,7 +145,10 @@ class CheckoutController extends StorefrontController {
SettingsService $settingsService, SettingsService $settingsService,
TransactionService $transactionService, TransactionService $transactionService,
GenericPageLoaderInterface $genericLoader, GenericPageLoaderInterface $genericLoader,
AbstractOrderRoute $orderRoute AbstractOrderRoute $orderRoute,
OrderTransactionStateHandler $orderTransactionStateHandler,
StateMachineRegistry $stateMachineRegistry,
ParameterBagInterface $params
) )
{ {
$this->cartService = $cartService; $this->cartService = $cartService;
@@ -122,6 +157,9 @@ class CheckoutController extends StorefrontController {
$this->transactionService = $transactionService; $this->transactionService = $transactionService;
$this->lineItemFactoryRegistry = $lineItemFactoryRegistry; $this->lineItemFactoryRegistry = $lineItemFactoryRegistry;
$this->orderRoute = $orderRoute; $this->orderRoute = $orderRoute;
$this->orderTransactionStateHandler = $orderTransactionStateHandler;
$this->stateMachineRegistry = $stateMachineRegistry;
$this->cache = new FilesystemAdapter('vrpayment', 0, rtrim($params->get('kernel.cache_dir'), '/') . '/vrpayment-cache');
} }
/** /**
@@ -363,6 +401,32 @@ class CheckoutController extends StorefrontController {
if($orderEntity->getSalesChannelId() !== $salesChannelContext->getSalesChannelId()) { if($orderEntity->getSalesChannelId() !== $salesChannelContext->getSalesChannelId()) {
$this->settings = $this->settingsService->getSettings($orderEntity->getSalesChannelId()); $this->settings = $this->settingsService->getSettings($orderEntity->getSalesChannelId());
$trans = $this->getTransaction($orderId, $salesChannelContext->getContext()); $trans = $this->getTransaction($orderId, $salesChannelContext->getContext());
// Adoption in case of duplicate requests
// Get order specific value from cache
$cacheKey = 'vrpayment_recreate_order_' . $orderId;
$isFound = $this->cache->get($cacheKey, function (ItemInterface $item) {
$item->expiresAfter(10);
return false;
});
// If value is found in cache - send user directly to successful checkout confirmation page for unpaid transactions
if ($isFound === true && in_array($trans->getState(), [TransactionState::FAILED])) {
$unpaidUrl = $this->getUnpaidUrlFromToken($trans->getSuccessUrl())
?? $this->buildUnpaidUrl($orderEntity->getSalesChannelId(), $salesChannelContext, $orderId);
if ($unpaidUrl) {
return new RedirectResponse(
$unpaidUrl . (parse_url($unpaidUrl, \PHP_URL_QUERY) ? '&' : '?') . 'error-code=' . PaymentException::PAYMENT_CUSTOMER_CANCELED_EXTERNAL
);
}
}
// Cache order specific value for some time on first request
$this->cache->delete($cacheKey);
$this->cache->get($cacheKey, function (ItemInterface $item) {
$item->expiresAfter(10);
return true;
});
return $this->redirect($trans->getSuccessUrl()); return $this->redirect($trans->getSuccessUrl());
} }
// End Adoption for Headless Storefronts // End Adoption for Headless Storefronts
@@ -380,6 +444,7 @@ class CheckoutController extends StorefrontController {
} }
$transaction = $this->getTransaction($orderId, $salesChannelContext->getContext()); $transaction = $this->getTransaction($orderId, $salesChannelContext->getContext());
$orderTransactionId = $transaction->getMetaData()[TransactionPayload::VRPAYMENT_METADATA_ORDER_TRANSACTION_ID];
if (!empty($transaction->getUserFailureMessage())) { if (!empty($transaction->getUserFailureMessage())) {
$this->addFlash('danger', $transaction->getUserFailureMessage()); $this->addFlash('danger', $transaction->getUserFailureMessage());
} }
@@ -414,6 +479,18 @@ class CheckoutController extends StorefrontController {
} }
// Close the old, existing order to prevent confusion for the customer
$this->orderTransactionStateHandler->cancel($orderTransactionId, $salesChannelContext->getContext());
$this->stateMachineRegistry->transition(
new Transition(
OrderDefinition::ENTITY_NAME,
$orderId,
self::ORDER_STATE_CANCEL,
'stateId'
),
$salesChannelContext->getContext()
);
} catch (\Exception $exception) { } catch (\Exception $exception) {
$this->addFlash('danger', $this->trans('error.addToCartError')); $this->addFlash('danger', $this->trans('error.addToCartError'));
$this->logger->critical($exception->getMessage()); $this->logger->critical($exception->getMessage());
@@ -423,6 +500,74 @@ class CheckoutController extends StorefrontController {
return $this->redirectToRoute('frontend.checkout.confirm.page'); return $this->redirectToRoute('frontend.checkout.confirm.page');
} }
/**
* Tries to return successful checkout confirmation url for unpaid transactions.
*
* It achieves that by getting payment token from successUrl, parsing and decoding
* it, and finally reading the claims.
*
* @param string $successUrl
*
* @return string|null
*/
private function getUnpaidUrlFromToken(string $successUrl): ?string {
$query = [];
parse_str((string) parse_url($successUrl, PHP_URL_QUERY), $query);
$jwt = $query['_sw_payment_token'] ?? null;
if (!$jwt) {
return null;
}
$data = explode('.', $jwt, 3);
if (count($data) !== 3) {
return null;
}
[, $c, ] = $data;
try {
$urlSafeData = strtr($c, '-_', '+/');
$paddedData = str_pad($urlSafeData, \strlen($urlSafeData) % 4, '=');
$decoded = base64_decode($paddedData, true);
if (!$decoded) {
return null;
}
$claims = json_decode(json: $decoded, associative: true, flags: JSON_THROW_ON_ERROR);
$unpaidUrl = $claims['eul'] ?? null;
return $unpaidUrl;
} catch (\Throwable $e) {
$this->logger->warning("CheckoutController::getUnpaidUrlFromToken - JWT parse failed: {errorMessage}", [
'errorMessage' => $e->getMessage(),
]);
return null;
}
}
/**
* Tries to return successful checkout confirmation url for unpaid transactions.
*
* It achieves that by fetching headless storefront's base url,
* and building custom url.
*
* @param string $salesChannelId
* @param SalesChannelContext $salesChannelContext
* @param string $orderId
*
* @return string|null
*/
private function buildUnpaidUrl(string $salesChannelId, SalesChannelContext $salesChannelContext, string $orderId): ?string {
$salesChannelDomainRepo = $this->container->get('sales_channel_domain.repository');
$criteria = new Criteria();
$criteria->addFilter(new EqualsFilter('salesChannelId', $salesChannelId))->setLimit(10);
$domain = $salesChannelDomainRepo->search($criteria, $salesChannelContext->getContext())->first();
if(!$domain) {
return null;
}
$baseUrl = rtrim($domain->getUrl(), '/');
return sprintf('%s/checkout/success/%s/unpaid', $baseUrl, $orderId);
}
/** /**
* @param OrderLineItemCollection $orderItems * @param OrderLineItemCollection $orderItems
* *
@@ -7,11 +7,18 @@ use Shopware\Core\{Checkout\Order\Aggregate\OrderTransaction\OrderTransactionCol
Checkout\Order\Aggregate\OrderTransaction\OrderTransactionStates, Checkout\Order\Aggregate\OrderTransaction\OrderTransactionStates,
Checkout\Order\OrderEntity, Checkout\Order\OrderEntity,
Content\MailTemplate\Service\Event\MailBeforeValidateEvent}; Content\MailTemplate\Service\Event\MailBeforeValidateEvent};
use Shopware\Core\Checkout\Payment\PaymentMethodCollection;
use Shopware\Core\Framework\DataAbstractionLayer\EntityRepository;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\MultiFilter;
use Shopware\Core\System\SalesChannel\SalesChannelContext; use Shopware\Core\System\SalesChannel\SalesChannelContext;
use Shopware\Storefront\Page\Account\Order\AccountEditOrderPageLoadedEvent;
use Shopware\Storefront\Page\Account\PaymentMethod\AccountPaymentMethodPageLoadedEvent;
use Shopware\Storefront\Page\Checkout\Confirm\CheckoutConfirmPageLoadedEvent; use Shopware\Storefront\Page\Checkout\Confirm\CheckoutConfirmPageLoadedEvent;
use Shopware\Storefront\Page\Checkout\Finish\CheckoutFinishPageLoadedEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use VRPaymentPayment\Core\{Api\Transaction\Service\OrderMailService, use VRPaymentPayment\Core\{Api\Transaction\Service\TransactionService,
Api\Transaction\Service\TransactionService,
Checkout\PaymentHandler\VRPaymentPaymentHandler, Checkout\PaymentHandler\VRPaymentPaymentHandler,
Settings\Service\SettingsService, Settings\Service\SettingsService,
Settings\Struct\Settings, Settings\Struct\Settings,
@@ -31,6 +38,7 @@ use VRPaymentPayment\Sdk\{Model\AddressCreate,
Model\Transaction, Model\Transaction,
Model\TransactionCreate, Model\TransactionCreate,
Model\TransactionPending}; Model\TransactionPending};
use Shopware\Core\Framework\Struct\ArrayEntity;
/** /**
* Class CheckoutSubscriber * Class CheckoutSubscriber
@@ -65,6 +73,9 @@ class CheckoutSubscriber implements EventSubscriberInterface
*/ */
private $paymentMethodUtil; private $paymentMethodUtil;
/** @var EntityRepository */
private EntityRepository $paymentMethodRepository;
/** /**
* CheckoutSubscriber constructor. * CheckoutSubscriber constructor.
* *
@@ -73,12 +84,13 @@ class CheckoutSubscriber implements EventSubscriberInterface
* @param \VRPaymentPayment\Core\Settings\Service\SettingsService $settingsService * @param \VRPaymentPayment\Core\Settings\Service\SettingsService $settingsService
* @param \VRPaymentPayment\Core\Util\PaymentMethodUtil $paymentMethodUtil * @param \VRPaymentPayment\Core\Util\PaymentMethodUtil $paymentMethodUtil
*/ */
public function __construct(PaymentMethodConfigurationService $paymentMethodConfigurationService, TransactionService $transactionService, SettingsService $settingsService, PaymentMethodUtil $paymentMethodUtil) public function __construct(PaymentMethodConfigurationService $paymentMethodConfigurationService, TransactionService $transactionService, SettingsService $settingsService, PaymentMethodUtil $paymentMethodUtil, EntityRepository $paymentMethodRepository)
{ {
$this->paymentMethodConfigurationService = $paymentMethodConfigurationService; $this->paymentMethodConfigurationService = $paymentMethodConfigurationService;
$this->transactionService = $transactionService; $this->transactionService = $transactionService;
$this->settingsService = $settingsService; $this->settingsService = $settingsService;
$this->paymentMethodUtil = $paymentMethodUtil; $this->paymentMethodUtil = $paymentMethodUtil;
$this->paymentMethodRepository = $paymentMethodRepository;
} }
/** /**
@@ -99,7 +111,9 @@ class CheckoutSubscriber implements EventSubscriberInterface
public static function getSubscribedEvents(): array public static function getSubscribedEvents(): array
{ {
return [ return [
CheckoutConfirmPageLoadedEvent::class => ['onConfirmPageLoaded', 1], CheckoutConfirmPageLoadedEvent::class => 'onCheckoutConfirmLoaded',
AccountEditOrderPageLoadedEvent::class => 'onAccountOrderEditLoaded',
AccountPaymentMethodPageLoadedEvent::class => 'onAccountPaymentMethodLoaded',
MailBeforeValidateEvent::class => ['onMailBeforeValidate', 1], MailBeforeValidateEvent::class => ['onMailBeforeValidate', 1],
]; ];
} }
@@ -153,9 +167,10 @@ class CheckoutSubscriber implements EventSubscriberInterface
} }
/** /**
* @param \Shopware\Storefront\Page\Checkout\Confirm\CheckoutConfirmPageLoadedEvent $event * @param CheckoutConfirmPageLoadedEvent $event
* @return void
*/ */
public function onConfirmPageLoaded(CheckoutConfirmPageLoadedEvent $event): void public function onCheckoutConfirmLoaded(CheckoutConfirmPageLoadedEvent $event): void
{ {
try { try {
$salesChannelContext = $event->getSalesChannelContext(); $salesChannelContext = $event->getSalesChannelContext();
@@ -168,7 +183,7 @@ class CheckoutSubscriber implements EventSubscriberInterface
$createdTransactionId = $this->transactionService->createPendingTransaction($salesChannelContext, $event); $createdTransactionId = $this->transactionService->createPendingTransaction($salesChannelContext, $event);
$this->updateTempTransactionIfNeeded($salesChannelContext, $createdTransactionId); $this->updateTempTransactionIfNeeded($salesChannelContext, $createdTransactionId);
$this->getAvailablePaymentMethods($settings, $createdTransactionId); $this->getAvailablePaymentMethods($settings, $createdTransactionId, $salesChannelContext);
$this->setPossiblePaymentMethods($settings->getSpaceId(), $event); $this->setPossiblePaymentMethods($settings->getSpaceId(), $event);
} catch (\Exception $e) { } catch (\Exception $e) {
$this->logger->error($e->getMessage()); $this->logger->error($e->getMessage());
@@ -176,10 +191,74 @@ class CheckoutSubscriber implements EventSubscriberInterface
} }
} }
/**
* @param AccountEditOrderPageLoadedEvent $event
* @return void
*/
public function onAccountOrderEditLoaded(AccountEditOrderPageLoadedEvent $event): void
{
try {
$this->handlePaymentMethodFiltering($event);
} catch (\Throwable $e) {
$this->logger->error($e->getMessage());
$this->removeVRPaymentPaymentMethodFromConfirmPage($event);
}
}
/**
* @param AccountPaymentMethodPageLoadedEvent $event
* @return void
*/
public function onAccountPaymentMethodLoaded(AccountPaymentMethodPageLoadedEvent $event): void
{
try {
$this->handlePaymentMethodFiltering($event);
} catch (\Throwable $e) {
$this->logger->error($e->getMessage());
$this->removeVRPaymentPaymentMethodFromConfirmPage($event);
}
}
/** /**
* @param \Shopware\Storefront\Page\Checkout\Confirm\CheckoutConfirmPageLoadedEvent $event * @param \Shopware\Storefront\Page\Checkout\Confirm\CheckoutConfirmPageLoadedEvent $event
*/ */
private function removeVRPaymentPaymentMethodFromConfirmPage(CheckoutConfirmPageLoadedEvent $event): void public function onConfirmPageLoaded(CheckoutConfirmPageLoadedEvent $event): void
{
try {
$this->handlePaymentMethodFiltering($event);
} catch (\Throwable $e) {
$this->logger->error($e->getMessage());
$this->removeVRPaymentPaymentMethodFromConfirmPage($event);
}
}
/**
* @param $event
* @return void
*/
private function handlePaymentMethodFiltering($event): void
{
$salesChannelContext = $event->getSalesChannelContext();
$settings = $this->settingsService->getValidSettings($salesChannelContext->getSalesChannel()->getId());
if (is_null($settings)) {
$this->logger->notice('Removing payment methods because settings are invalid');
$this->removeVRPaymentPaymentMethodFromConfirmPage($event);
return;
}
$createdTransactionId = $this->transactionService->createPendingTransaction($salesChannelContext, $event);
$this->updateTempTransactionIfNeeded($salesChannelContext, $createdTransactionId);
$this->getAvailablePaymentMethods($settings, $createdTransactionId, $salesChannelContext);
$this->setPossiblePaymentMethods($settings->getSpaceId(), $event);
}
/**
* @param $event
* @return void
*/
private function removeVRPaymentPaymentMethodFromConfirmPage($event): void
{ {
$paymentMethodCollection = $event->getPage()->getPaymentMethods(); $paymentMethodCollection = $event->getPage()->getPaymentMethods();
$paymentMethodIds = $this->paymentMethodUtil->getVRPaymentPaymentMethodIds($event->getContext()); $paymentMethodIds = $this->paymentMethodUtil->getVRPaymentPaymentMethodIds($event->getContext());
@@ -193,7 +272,7 @@ class CheckoutSubscriber implements EventSubscriberInterface
* @param int $createdTransactionId * @param int $createdTransactionId
* @return void * @return void
*/ */
private function getAvailablePaymentMethods(Settings $settings, int $createdTransactionId): void private function getAvailablePaymentMethods(Settings $settings, int $createdTransactionId, SalesChannelContext $salesChannelContext): void
{ {
$transactionService = $settings->getApiClient()->getTransactionService(); $transactionService = $settings->getApiClient()->getTransactionService();
$possiblePaymentMethods = $transactionService->fetchPaymentMethods( $possiblePaymentMethods = $transactionService->fetchPaymentMethods(
@@ -203,9 +282,13 @@ class CheckoutSubscriber implements EventSubscriberInterface
); );
$arrayOfPossibleMethods = []; $arrayOfPossibleMethods = [];
foreach ($possiblePaymentMethods as $possiblePaymentMethod) { foreach ($possiblePaymentMethods as $possiblePaymentMethod) {
$arrayOfPossibleMethods[] = $possiblePaymentMethod->getid(); $arrayOfPossibleMethods[] = $possiblePaymentMethod->getId();
} }
$_SESSION['arrayOfPossibleMethods'] = $arrayOfPossibleMethods;
$salesChannelContext->getContext()->addExtension(
'possibleMethods',
new ArrayEntity(['ids' => $arrayOfPossibleMethods])
);
} }
/** /**
@@ -213,26 +296,55 @@ class CheckoutSubscriber implements EventSubscriberInterface
* @param CheckoutConfirmPageLoadedEvent $event * @param CheckoutConfirmPageLoadedEvent $event
* @return void * @return void
*/ */
private function setPossiblePaymentMethods(int $spaceId, CheckoutConfirmPageLoadedEvent $event): void private function setPossiblePaymentMethods(int $spaceId, $event): void
{ {
$localPaymentMethods = []; $paymentIds = [];
$paymentMethodConfigurations = $this->paymentMethodConfigurationService->getAllPaymentMethodConfigurations($spaceId, $event->getSalesChannelContext()->getContext()); $paymentMethodCollection = $event->getPage()->getPaymentMethods();
foreach ($paymentMethodConfigurations as $paymentMethodConfiguration) {
$localPaymentMethods[$paymentMethodConfiguration->getId()] = $paymentMethodConfiguration->getPaymentMethodConfigurationId(); foreach ($paymentMethodCollection as $paymentMethodCollectionItem) {
$isVRPaymentPM = VRPaymentPaymentHandler::class === $paymentMethodCollectionItem->getHandlerIdentifier();
if (!$isVRPaymentPM) {
$paymentIds[] = $paymentMethodCollectionItem->getId();
}
} }
$paymentMethodCollection = $event->getPage()->getPaymentMethods(); $allowedWLMethods = [];
foreach ($paymentMethodCollection as $paymentMethodCollectionItem) { $paymentMethodConfigurations = $this->paymentMethodConfigurationService
$isVRPaymentPM = VRPaymentPaymentHandler::class == $paymentMethodCollectionItem->getHandlerIdentifier(); ->getAllPaymentMethodConfigurations($spaceId, $event->getSalesChannelContext()->getContext());
if (!$isVRPaymentPM) {
foreach ($paymentMethodConfigurations as $paymentMethodConfiguration) {
if ($paymentMethodConfiguration->getPaymentMethod() === null) {
continue; continue;
} }
$paymentMethodConfigurationId = $localPaymentMethods[$paymentMethodCollectionItem->getId()]; $pmId = $paymentMethodConfiguration->getPaymentMethod()->getId();
if (!\in_array($paymentMethodConfigurationId, $_SESSION['arrayOfPossibleMethods'])) { $pmConfigId = $paymentMethodConfiguration->getPaymentMethodConfigurationId();
$paymentMethodCollection->remove($paymentMethodCollectionItem->getId()); $allowedIds = $this->getAllowedPaymentMethodIds($event->getSalesChannelContext());
if ($paymentMethodConfiguration->getSpaceId() === $spaceId
&& \in_array($pmConfigId, $allowedIds, true)) {
$allowedWLMethods[] = $pmId;
} }
} }
$allPaymentIds = array_unique(array_merge($paymentIds, $allowedWLMethods));
$collection = new PaymentMethodCollection();
if (!empty($allPaymentIds)) {
$criteria = new Criteria($allPaymentIds);
$criteria->addFilter(new EqualsFilter('active', true));
$criteria->addFilter(
new EqualsFilter('salesChannels.id', $event->getSalesChannelContext()->getSalesChannelId())
);
$result = $this->paymentMethodRepository->search($criteria, $event->getContext());
foreach ($result->getEntities() as $method) {
if (!$collection->has($method->getId())) {
$collection->add($method);
}
}
}
$event->getPage()->setPaymentMethods($collection);
} }
/** /**
@@ -242,19 +354,43 @@ class CheckoutSubscriber implements EventSubscriberInterface
*/ */
private function updateTempTransactionIfNeeded(SalesChannelContext $salesChannelContext, int $createdTransactionId): void private function updateTempTransactionIfNeeded(SalesChannelContext $salesChannelContext, int $createdTransactionId): void
{ {
$addressCheck = $_SESSION['addressCheck'] ?? null; $ctx = $salesChannelContext->getContext();
$currencyCheck = $_SESSION['currencyCheck'] ?? null;
/** @var ArrayEntity|null $ext */
$ext = $ctx->getExtension('checkoutState');
$oldAddressHash = $ext instanceof ArrayEntity ? $ext->get('addressHash') : null;
$oldCurrency = $ext instanceof ArrayEntity ? $ext->get('currency') : null;
$customer = $salesChannelContext->getCustomer(); $customer = $salesChannelContext->getCustomer();
$addressHash = md5(json_encode((array)$customer)); $addressHash = md5(json_encode((array) $customer));
$currency = $salesChannelContext->getCurrency()->getIsoCode(); $currency = $salesChannelContext->getCurrency()->getIsoCode();
if (($addressCheck && $currencyCheck) && $addressCheck !== $addressHash || $currencyCheck !== $currency) {
$needsUpdate = ($oldAddressHash !== $addressHash) || ($oldCurrency !== $currency);
if ($needsUpdate) {
if ($createdTransactionId) { if ($createdTransactionId) {
$this->transactionService->updateTempTransaction($salesChannelContext, $createdTransactionId); $this->transactionService->updateTempTransaction($salesChannelContext, $createdTransactionId);
} }
$_SESSION['arrayOfPossibleMethods'] = null;
$_SESSION['addressCheck'] = $addressHash; $ctx->addExtension('possibleMethods', new ArrayEntity(['ids' => []]));
$_SESSION['currencyCheck'] = $currency; $ctx->addExtension(
'checkoutState',
new ArrayEntity([
'addressHash' => $addressHash,
'currency' => $currency,
])
);
} }
} }
/**
* @param SalesChannelContext $salesChannelContext
* @return array
*/
private function getAllowedPaymentMethodIds(SalesChannelContext $salesChannelContext): array
{
$ext = $salesChannelContext->getContext()->getExtension('possibleMethods');
return $ext instanceof ArrayEntity ? ($ext->get('ids') ?? []) : [];
}
} }
+59 -6
View File
@@ -3,6 +3,7 @@
namespace VRPaymentPayment\Core\Util\Analytics; namespace VRPaymentPayment\Core\Util\Analytics;
use VRPayment\Sdk\ApiClient; use VRPayment\Sdk\ApiClient;
use Shopware\Core\Kernel;
/** /**
* Class Analytics * Class Analytics
@@ -19,26 +20,78 @@ class Analytics {
/** /**
* @return array * @return array
*/ */
public static function getDefaultData() public static function getDefaultData(): array
{ {
$shopwareVersion = self::getShopwareVersion();
return [ return [
self::SHOP_SYSTEM => 'shopware', self::SHOP_SYSTEM => 'shopware',
self::SHOP_SYSTEM_VERSION => '6', self::SHOP_SYSTEM_VERSION => $shopwareVersion,
self::SHOP_SYSTEM_AND_VERSION => 'shopware-6', self::SHOP_SYSTEM_AND_VERSION => 'shopware-' . $shopwareVersion,
self::PLUGIN_SYSTEM_VERSION => '7.0.1', self::PLUGIN_SYSTEM_VERSION => '6.1.17',
]; ];
} }
/** /**
* @param \VRPayment\Sdk\ApiClient $apiClient * @param \VRPayment\Sdk\ApiClient $apiClient
*/ */
public static function addHeaders(ApiClient &$apiClient) public static function addHeaders(ApiClient &$apiClient): void
{ {
$data = self::getDefaultData(); $data = self::getDefaultData();
foreach ($data as $key => $value) { foreach ($data as $key => $value) {
$apiClient->addDefaultHeader($key, $value); $apiClient->addDefaultHeader($key, $value);
} }
} }
/**
* Reads Shopware version and caches it for performance.
*
* @return string
*/
public static function getShopwareVersion(): string
{
static $cachedVersion = null;
if ($cachedVersion !== null) {
return $cachedVersion;
}
$basePath = dirname(__DIR__, 7);
$installedFile = $basePath . '/vendor/composer/installed.php';
if (is_file($installedFile)) {
$installed = include $installedFile;
$packages = [];
if (isset($installed['versions'])) {
$packages = $installed['versions'];
} elseif (is_array($installed)) {
foreach ($installed as $section) {
if (isset($section['versions'])) {
$packages = $section['versions'];
break;
}
}
}
if (isset($packages['shopware/core']['pretty_version'])) {
return $cachedVersion = ltrim($packages['shopware/core']['pretty_version'], 'v');
}
}
$lockFile = $basePath . '/composer.lock';
if (is_file($lockFile)) {
$data = json_decode((string) file_get_contents($lockFile), true);
if (!empty($data['packages'])) {
foreach ($data['packages'] as $package) {
if (($package['name'] ?? '') === 'shopware/core') {
return $cachedVersion = ltrim($package['version'], 'v');
}
}
}
}
return $cachedVersion = Kernel::SHOPWARE_FALLBACK_VERSION;
}
} }
+83 -170
View File
@@ -8,14 +8,11 @@ use Shopware\Core\{Checkout\Cart\Tax\Struct\CalculatedTaxCollection,
Checkout\Customer\Aggregate\CustomerAddress\CustomerAddressEntity, Checkout\Customer\Aggregate\CustomerAddress\CustomerAddressEntity,
Checkout\Customer\CustomerEntity, Checkout\Customer\CustomerEntity,
Checkout\Order\Aggregate\OrderLineItem\OrderLineItemEntity, Checkout\Order\Aggregate\OrderLineItem\OrderLineItemEntity,
Checkout\Order\OrderEntity, Checkout\Payment\Cart\AsyncPaymentTransactionStruct,
Checkout\Payment\Cart\PaymentTransactionStruct,
Framework\DataAbstractionLayer\Search\Criteria, Framework\DataAbstractionLayer\Search\Criteria,
System\SalesChannel\SalesChannelContext System\SalesChannel\SalesChannelContext
}; };
use Shopware\Core\Framework\DataAbstractionLayer\EntityRepository;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter; use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface; use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Contracts\Translation\TranslatorInterface; use Symfony\Contracts\Translation\TranslatorInterface;
use VRPayment\Sdk\{Model\AddressCreate, use VRPayment\Sdk\{Model\AddressCreate,
@@ -40,10 +37,6 @@ use VRPaymentPayment\Core\{Api\PaymentMethodConfiguration\Entity\PaymentMethodCo
Util\Payload\CustomProducts\CustomProductsLineItemTypes Util\Payload\CustomProducts\CustomProductsLineItemTypes
}; };
use Shopware\Core\System\SystemConfig\SystemConfigService;
use Shopware\Core\Framework\Context;
use Shopware\Core\System\Tax\TaxEntity;
/** /**
* Class TransactionPayload * Class TransactionPayload
* *
@@ -69,7 +62,7 @@ class TransactionPayload extends AbstractPayload
protected $salesChannelContext; protected $salesChannelContext;
/** /**
* @var \Shopware\Core\Checkout\Payment\Cart\PaymentTransactionStruct * @var \Shopware\Core\Checkout\Payment\Cart\AsyncPaymentTransactionStruct
*/ */
protected $transaction; protected $transaction;
@@ -93,10 +86,6 @@ class TransactionPayload extends AbstractPayload
*/ */
protected $translator; protected $translator;
protected EntityRepository $orderTransactionRepository;
protected OrderEntity $order;
/** /**
* TransactionPayload constructor. * TransactionPayload constructor.
* *
@@ -104,14 +93,14 @@ class TransactionPayload extends AbstractPayload
* @param \VRPaymentPayment\Core\Util\LocaleCodeProvider $localeCodeProvider * @param \VRPaymentPayment\Core\Util\LocaleCodeProvider $localeCodeProvider
* @param \Shopware\Core\System\SalesChannel\SalesChannelContext $salesChannelContext * @param \Shopware\Core\System\SalesChannel\SalesChannelContext $salesChannelContext
* @param \VRPaymentPayment\Core\Settings\Struct\Settings $settings * @param \VRPaymentPayment\Core\Settings\Struct\Settings $settings
* @param \Shopware\Core\Checkout\Payment\Cart\PaymentTransactionStruct $transaction * @param \Shopware\Core\Checkout\Payment\Cart\AsyncPaymentTransactionStruct $transaction
*/ */
public function __construct( public function __construct(
ContainerInterface $container, ContainerInterface $container,
LocaleCodeProvider $localeCodeProvider, LocaleCodeProvider $localeCodeProvider,
SalesChannelContext $salesChannelContext, SalesChannelContext $salesChannelContext,
Settings $settings, Settings $settings,
PaymentTransactionStruct $transaction AsyncPaymentTransactionStruct $transaction
) )
{ {
$this->localeCodeProvider = $localeCodeProvider; $this->localeCodeProvider = $localeCodeProvider;
@@ -120,23 +109,6 @@ class TransactionPayload extends AbstractPayload
$this->transaction = $transaction; $this->transaction = $transaction;
$this->container = $container; $this->container = $container;
$this->translator = $this->container->get('translator'); $this->translator = $this->container->get('translator');
$this->orderTransactionRepository = $this->container->get('order_transaction.repository');
$criteria = (new Criteria());
$criteria->addFilter(new EqualsFilter('id', $this->transaction->getOrderTransactionId()));
$orders = $this->orderTransactionRepository->search($criteria, $this->salesChannelContext->getContext())->getEntities();
$orderId = $orders->first()->getOrderId();
$criteria = new Criteria([$orderId]);
$criteria
->addAssociation('lineItems')
->addAssociation('orderCustomer')
->addAssociation('transactions')
->addAssociation('currency')
;
$this->order = $this->container->get('order.repository')->search($criteria, $this->salesChannelContext->getContext())->getEntities()->first();
} }
/** /**
@@ -147,21 +119,13 @@ class TransactionPayload extends AbstractPayload
*/ */
public function get(int $version): TransactionPending public function get(int $version): TransactionPending
{ {
$customerId = $this->order->getOrderCustomer()->getCustomerId(); $customer = $this->salesChannelContext->getCustomer();
$criteria = new Criteria([$customerId]);
$criteria->addAssociation('activeBillingAddress')
->addAssociation('activeShippingAddress')
->addAssociation('activeShippingAddress')
->addAssociation('defaultBillingAddress')
->addAssociation('defaultShippingAddress')
->addAssociation('salutation');
$customer = $this->container->get('customer.repository')->search($criteria, $this->salesChannelContext->getContext())->getEntities()->first();
$lineItems = $this->getLineItems(); $lineItems = $this->getLineItems();
$billingAddress = $this->getAddressPayload($customer, $customer->getActiveBillingAddress()); $billingAddress = $this->getAddressPayload($customer, $customer->getActiveBillingAddress());
$shippingAddress = $this->getAddressPayload($customer, $customer->getActiveShippingAddress(), false); $shippingAddress = $this->getAddressPayload($customer, $customer->getActiveShippingAddress(), false);
$customerId = null; $customerId = null;
$customerName = null; $customerName = null;
if ($customer->getGuest() === false) { if ($customer->getGuest() === false) {
@@ -174,14 +138,14 @@ class TransactionPayload extends AbstractPayload
} }
$transactionData = [ $transactionData = [
'currency' => $this->order->getCurrency()->getIsoCode(), 'currency' => $this->salesChannelContext->getCurrency()->getIsoCode(),
'customer_email_address' => $customer->getEmail(), 'customer_email_address' => $billingAddress->getEmailAddress(),
'customer_id' => $customerId, 'customer_id' => $customerId,
'language' => $this->localeCodeProvider->getLocaleCodeFromContext($this->salesChannelContext->getContext()) ?? null, 'language' => $this->localeCodeProvider->getLocaleCodeFromContext($this->salesChannelContext->getContext()) ?? null,
'merchant_reference' => $this->fixLength($this->order->getOrderNumber(), 100), 'merchant_reference' => $this->fixLength($this->transaction->getOrder()->getOrderNumber(), 100),
'meta_data' => [ 'meta_data' => [
self::VRPAYMENT_METADATA_ORDER_ID => $this->order->getId(), self::VRPAYMENT_METADATA_ORDER_ID => $this->transaction->getOrder()->getId(),
self::VRPAYMENT_METADATA_ORDER_TRANSACTION_ID => $this->order->getTransactions()->first()->getId(), self::VRPAYMENT_METADATA_ORDER_TRANSACTION_ID => $this->transaction->getOrderTransaction()->getId(),
self::VRPAYMENT_METADATA_SALES_CHANNEL_ID => $this->salesChannelContext->getSalesChannel()->getId(), self::VRPAYMENT_METADATA_SALES_CHANNEL_ID => $this->salesChannelContext->getSalesChannel()->getId(),
self::VRPAYMENT_METADATA_CUSTOMER_NAME => $customerName, self::VRPAYMENT_METADATA_CUSTOMER_NAME => $customerName,
], ],
@@ -198,8 +162,8 @@ class TransactionPayload extends AbstractPayload
$transactionData['meta_data']['additionalAddress2'] = $additionalAddress2; $transactionData['meta_data']['additionalAddress2'] = $additionalAddress2;
} }
if (!empty($this->order->getCustomerComment())) { if (!empty($this->transaction->getOrder()->getCustomerComment())) {
$transactionData['meta_data']['customer_comment'] = $this->order->getCustomerComment(); $transactionData['meta_data']['customer_comment'] = $this->transaction->getOrder()->getCustomerComment();
} }
$vatIds = $customer->getVatIds(); $vatIds = $customer->getVatIds();
@@ -230,12 +194,18 @@ class TransactionPayload extends AbstractPayload
->setShippingAddress($shippingAddress) ->setShippingAddress($shippingAddress)
->setShippingMethod($transactionData['shipping_method']); ->setShippingMethod($transactionData['shipping_method']);
$paymentConfiguration = $this->getPaymentConfiguration($this->salesChannelContext->getPaymentMethod()->getId()); $paymentConfiguration = $this->getPaymentConfiguration(
$this->salesChannelContext->getPaymentMethod()->getId(),
$transactionPayload->setAllowedPaymentMethodConfigurations([$paymentConfiguration->getPaymentMethodConfigurationId()]); $this->settings->getSpaceId()
);
if ($paymentConfiguration) {
$transactionPayload->setAllowedPaymentMethodConfigurations([
$paymentConfiguration->getPaymentMethodConfigurationId()
]);
}
$successUrl = $this->transaction->getReturnUrl() . '&status=paid'; $successUrl = $this->transaction->getReturnUrl() . '&status=paid';
$failedUrl = $this->getFailUrl($this->order->getId()) . '&status=fail'; $failedUrl = $this->getFailUrl($this->transaction->getOrder()->getId()) . '&status=fail';
$transactionPayload->setSuccessUrl($successUrl) $transactionPayload->setSuccessUrl($successUrl)
->setFailedUrl($failedUrl); ->setFailedUrl($failedUrl);
@@ -247,6 +217,23 @@ class TransactionPayload extends AbstractPayload
return $transactionPayload; return $transactionPayload;
} }
/**
* @param string $paymentMethodId
* @param int $spaceId
* @return PaymentMethodConfigurationEntity|null
*/
protected function getPaymentConfiguration(string $paymentMethodId, int $spaceId): ?PaymentMethodConfigurationEntity
{
$criteria = new Criteria();
$criteria->addFilter(new EqualsFilter('paymentMethodId', $paymentMethodId));
$criteria->addFilter(new EqualsFilter('spaceId', $spaceId));
return $this->container->get('vrpayment_payment_method_configuration.repository')
->search($criteria, $this->salesChannelContext->getContext())
->first();
}
/** /**
* Get transaction line items * Get transaction line items
* *
@@ -256,7 +243,7 @@ class TransactionPayload extends AbstractPayload
protected function getLineItems(): array protected function getLineItems(): array
{ {
$lineItems = []; $lineItems = [];
$items = $this->order->getLineItems() ?? []; $items = $this->transaction->getOrder()->getLineItems();
foreach ($items as $shopLineItem) { foreach ($items as $shopLineItem) {
if ($this->shouldSkipLineItem($shopLineItem)) { if ($this->shouldSkipLineItem($shopLineItem)) {
@@ -344,90 +331,27 @@ class TransactionPayload extends AbstractPayload
protected function addDiscountLineItem($discount, array &$lineItems): void protected function addDiscountLineItem($discount, array &$lineItems): void
{ {
$calculatedPrice = $discount->getPrice(); $calculatedPrice = $discount->getPrice();
$discountName = $discount->getLabel() ?? 'Unnamed';
$definition = $discount->getPriceDefinition();
if ($this->order->getTaxStatus() === 'net' || $definition instanceof \Shopware\Core\Checkout\Cart\Price\Struct\AbsolutePriceDefinition) {
$calculatedTaxesCollection = $calculatedPrice->getCalculatedTaxes(); $calculatedTaxesCollection = $calculatedPrice->getCalculatedTaxes();
foreach ($calculatedTaxesCollection as $calculatedTax) { foreach ($calculatedTaxesCollection as $calculatedTax) {
$rate = $calculatedTax->getTaxRate(); $rate = $calculatedTax->getTaxRate();
$lineItem = new LineItemCreate();
$amount = $this->calculateDiscountAmount($calculatedTax); $amount = $this->calculateDiscountAmount($calculatedTax);
$lineItems[] = $this->createDiscountLineItem($discountName, $amount, $rate); $discountName = $discount->getLabel();
}
} else {
$taxRules = $calculatedPrice->getTaxRules();
if ($taxRules && $taxRules->count() > 0) {
foreach ($taxRules as $taxRule) {
$rate = $taxRule->getTaxRate();
$amount = $calculatedPrice->getTotalPrice();
$lineItems[] = $this->createDiscountLineItem($discountName, $amount, $rate);
}
} else {
$rate = $this->getDefaultTaxRate();
$amount = $calculatedPrice->getTotalPrice();
$lineItems[] = $this->createDiscountLineItem($discountName, $amount, $rate);
}
}
}
/**
* @param string $discountName
* @param float $amount
* @param float $rate
* @return LineItemCreate
*/
private function createDiscountLineItem(string $discountName, float $amount, float $rate): LineItemCreate
{
$lineItem = new LineItemCreate();
$discountSkuName = 'sku-discount-' . $rate . '-' . $discountName;
$discountTitle = sprintf('DISCOUNT: %s (%s%% tax)', $discountName, $rate);
if ($this->order->getTaxStatus() === 'tax-free') {
$discountSkuName = 'sku-discount-' . $discountName;
$discountTitle = sprintf('DISCOUNT: %s', $discountName);
}
$lineItem->setAmountIncludingTax($amount) $lineItem->setAmountIncludingTax($amount)
->setName($discountTitle) ->setName(sprintf('DISCOUNT: %s (%s%% tax)', $discount->getLabel(), $rate))
->setQuantity(1) ->setQuantity(1)
->setShippingRequired(false) ->setShippingRequired(false)
->setSku($discountSkuName, 200) ->setSku('sku-discount-' . $rate . '-' . $discountName, 200)
->setType(LineItemType::DISCOUNT) ->setType(LineItemType::DISCOUNT)
->setUniqueId('coupon-' . $discountSkuName); ->setUniqueId('coupon-sku-discount-' . $rate . '-' . $rate . '-' . $discountName);
$taxRate = new TaxCreate([ $taxRate = new TaxCreate(['title' => 'Discount Tax: ' . $rate, 'rate' => $rate]);
'title' => 'Discount Tax: ' . $rate,
'rate' => $rate,
]);
if ($this->order->getTaxStatus() !== 'tax-free') {
$lineItem->setTaxes([$taxRate]); $lineItem->setTaxes([$taxRate]);
$lineItems[] = $lineItem;
} }
return $lineItem;
}
/**
* @return float
*/
private function getDefaultTaxRate(): float
{
/** @var SystemConfigService $systemConfigService */
$systemConfigService = $this->container->get(SystemConfigService::class);
$taxId = $systemConfigService->get('core.tax.defaultTaxRate');
if (!$taxId || !is_string($taxId)) {
return 21.0;
}
$criteria = new Criteria([$taxId]);
/** @var TaxRepository $taxRepository */
$taxRepository = $this->container->get('tax.repository');
$tax = $taxRepository->search($criteria, Context::createDefaultContext())->get($taxId);
return $tax instanceof TaxEntity ? $tax->getTaxRate() : 21.0;
} }
/** /**
@@ -436,7 +360,7 @@ class TransactionPayload extends AbstractPayload
protected function calculateDiscountAmount($calculatedTax): float protected function calculateDiscountAmount($calculatedTax): float
{ {
$amount = self::round($calculatedTax->getPrice()); $amount = self::round($calculatedTax->getPrice());
if ($this->order->getTaxStatus() === 'net') { if ($this->transaction->getOrder()->getTaxStatus() === 'net') {
$amount = self::round($amount + $calculatedTax->getTax()); $amount = self::round($amount + $calculatedTax->getTax());
} }
return $amount; return $amount;
@@ -457,7 +381,9 @@ class TransactionPayload extends AbstractPayload
*/ */
protected function addOptionalLineItems(array &$lineItems): void protected function addOptionalLineItems(array &$lineItems): void
{ {
if (count($this->order->getShippingCosts()->getCalculatedTaxes()) === 1) { $shippingCosts = $this->transaction->getOrder()->getShippingCosts();
if ($shippingCosts && $this->transaction->getOrder()->getShippingTotal() > 0) {
if ($shippingLineItem = $this->getShippingLineItem()) { if ($shippingLineItem = $this->getShippingLineItem()) {
$lineItems[] = $shippingLineItem; $lineItems[] = $shippingLineItem;
} }
@@ -479,7 +405,7 @@ class TransactionPayload extends AbstractPayload
protected function getCustomProductOptionLabel(string $lineItemParentId): string protected function getCustomProductOptionLabel(string $lineItemParentId): string
{ {
$label = ''; $label = '';
foreach ($this->order->getLineItems() as $shopLineItem) { foreach ($this->transaction->getOrder()->getLineItems() as $shopLineItem) {
if ($shopLineItem->getParentId() === $lineItemParentId && $shopLineItem->getType() === CustomProductsLineItemTypes::LINE_ITEM_TYPE_PRODUCT) { if ($shopLineItem->getParentId() === $lineItemParentId && $shopLineItem->getType() === CustomProductsLineItemTypes::LINE_ITEM_TYPE_PRODUCT) {
$label = $shopLineItem->getLabel(); $label = $shopLineItem->getLabel();
break; break;
@@ -504,11 +430,10 @@ class TransactionPayload extends AbstractPayload
$sku = $payLoad['productNumber']; $sku = $payLoad['productNumber'];
} }
$sku = $this->fixLength($sku, 200); $sku = $this->fixLength($sku, 200);
$amount = $shopLineItem->getTotalPrice() ? self::round($shopLineItem->getTotalPrice()) : 0; $amount = $shopLineItem->getTotalPrice() ? self::round($shopLineItem->getTotalPrice()) : 0;
//include Tax Excluded for Net Tax display customer group //include Tax Excluded for Net Tax display customer group
if ($this->order->getTaxStatus() === 'net') { if ($this->transaction->getOrder()->getTaxStatus() === 'net') {
$amount = self::round($amount + $shopLineItem->getPrice()->getCalculatedTaxes()->getAmount()); $amount = self::round($amount + $shopLineItem->getPrice()->getCalculatedTaxes()->getAmount());
} }
@@ -544,10 +469,8 @@ class TransactionPayload extends AbstractPayload
} }
if (!empty($taxes)) { if (!empty($taxes)) {
if ($this->order->getTaxStatus() !== 'tax-free') {
$lineItem->setTaxes($taxes); $lineItem->setTaxes($taxes);
} }
}
if ($shopLineItem->getTotalPrice() >= 0) { if ($shopLineItem->getTotalPrice() >= 0) {
$lineItem->setType(LineItemType::PRODUCT); $lineItem->setType(LineItemType::PRODUCT);
@@ -622,34 +545,31 @@ class TransactionPayload extends AbstractPayload
{ {
try { try {
$amount = $this->order->getShippingTotal(); $amount = $this->transaction->getOrder()->getShippingTotal();
$amount = self::round($amount); $amount = self::round($amount);
if ($amount > 0) { if ($amount > 0) {
$shippingName = $this->salesChannelContext->getShippingMethod()->getName() ?? $this->translator->trans('vrpayment.payload.shipping.name'); $shippingName = $this->salesChannelContext->getShippingMethod()->getName() ?? $this->translator->trans('vrpayment.payload.shipping.name');
$taxes = $this->getTaxes( $taxes = $this->getTaxes(
$this->order->getShippingCosts()->getCalculatedTaxes(), $this->transaction->getOrder()->getShippingCosts()->getCalculatedTaxes(),
$shippingName $shippingName
); );
if ($this->order->getTaxStatus() === 'net') { if ($this->transaction->getOrder()->getTaxStatus() === 'net') {
$amount = self::round($amount + $this->order->getShippingCosts()->getCalculatedTaxes()->getAmount()); $amount = self::round($amount + $this->transaction->getOrder()->getShippingCosts()->getCalculatedTaxes()->getAmount());
} }
$lineItem = (new LineItemCreate()) $lineItem = (new LineItemCreate())
->setAmountIncludingTax($amount) ->setAmountIncludingTax($amount)
->setName($this->fixLength($shippingName . ' ' . $this->translator->trans('vrpayment.payload.shipping.lineItem'), 150)) ->setName($this->fixLength($shippingName . ' ' . $this->translator->trans('vrpayment.payload.shipping.lineItem'), 150))
->setQuantity($this->order->getShippingCosts()->getQuantity() ?? 1) ->setQuantity($this->transaction->getOrder()->getShippingCosts()->getQuantity() ?? 1)
->setTaxes($taxes)
->setSku($this->fixLength($shippingName . '-Shipping', 200)) ->setSku($this->fixLength($shippingName . '-Shipping', 200))
/** @noinspection PhpParamsInspection */ /** @noinspection PhpParamsInspection */
->setType(LineItemType::SHIPPING) ->setType(LineItemType::SHIPPING)
->setUniqueId($this->fixLength($shippingName . '-Shipping', 200)); ->setUniqueId($this->fixLength($shippingName . '-Shipping', 200));
if ($this->order->getTaxStatus() !== 'tax-free') {
$lineItem->setTaxes($taxes);
}
if (!$lineItem->valid()) { if (!$lineItem->valid()) {
$this->logger->critical('Shipping LineItem payload invalid:', $lineItem->listInvalidProperties()); $this->logger->critical('Shipping LineItem payload invalid:', $lineItem->listInvalidProperties());
throw new InvalidPayloadException('Shipping LineItem payload invalid:' . json_encode($lineItem->listInvalidProperties())); throw new InvalidPayloadException('Shipping LineItem payload invalid:' . json_encode($lineItem->listInvalidProperties()));
@@ -670,15 +590,15 @@ class TransactionPayload extends AbstractPayload
protected function getMultipleShippingLineItems(): array protected function getMultipleShippingLineItems(): array
{ {
try { try {
if ($this->order->getShippingTotal() > 0) { if ($this->transaction->getOrder()->getShippingTotal() > 0) {
$lineItems = []; $lineItems = [];
$shippingName = $this->salesChannelContext->getShippingMethod()->getName() ?? $this->translator->trans('vrpayment.payload.shipping.name'); $shippingName = $this->salesChannelContext->getShippingMethod()->getName() ?? $this->translator->trans('vrpayment.payload.shipping.name');
$isFirst = true; $isFirst = true;
foreach ($this->order->getShippingCosts()->getCalculatedTaxes() as $taxItem) { foreach ($this->transaction->getOrder()->getShippingCosts()->getCalculatedTaxes() as $taxItem) {
$amount = self::round($taxItem->getPrice()); $amount = self::round($taxItem->getPrice());
if ($this->order->getTaxStatus() === 'net') { if ($this->transaction->getOrder()->getTaxStatus() === 'net') {
$amount = self::round($amount + $taxItem->getTax()); $amount = self::round($amount + $taxItem->getTax());
} }
$taxRate = $taxItem->getTaxRate(); $taxRate = $taxItem->getTaxRate();
@@ -690,15 +610,12 @@ class TransactionPayload extends AbstractPayload
$lineItem = (new LineItemCreate()) $lineItem = (new LineItemCreate())
->setAmountIncludingTax($amount) ->setAmountIncludingTax($amount)
->setName($this->fixLength($name . ' ' . $this->translator->trans('vrpayment.payload.shipping.lineItem'), 150)) ->setName($this->fixLength($name . ' ' . $this->translator->trans('vrpayment.payload.shipping.lineItem'), 150))
->setQuantity($this->order->getShippingCosts()->getQuantity() ?? 1) ->setQuantity($this->transaction->getOrder()->getShippingCosts()->getQuantity() ?? 1)
->setTaxes([$tax])
->setSku($this->fixLength($name . '-Shipping', 200)) ->setSku($this->fixLength($name . '-Shipping', 200))
->setType($isFirst ? LineItemType::SHIPPING : LineItemType::FEE) // First item as SHIPPING, rest as FEE ->setType($isFirst ? LineItemType::SHIPPING : LineItemType::FEE) // First item as SHIPPING, rest as FEE
->setUniqueId($this->fixLength($name . '-Shipping', 200)); ->setUniqueId($this->fixLength($name . '-Shipping', 200));
if ($this->order->getTaxStatus() !== 'tax-free') {
$lineItem->setTaxes([$tax]);
}
if (!$lineItem->valid()) { if (!$lineItem->valid()) {
$this->logger->critical('Shipping LineItem payload invalid:', $lineItem->listInvalidProperties()); $this->logger->critical('Shipping LineItem payload invalid:', $lineItem->listInvalidProperties());
throw new InvalidPayloadException('Shipping LineItem payload invalid:' . json_encode($lineItem->listInvalidProperties())); throw new InvalidPayloadException('Shipping LineItem payload invalid:' . json_encode($lineItem->listInvalidProperties()));
@@ -728,18 +645,28 @@ class TransactionPayload extends AbstractPayload
{ {
$lineItem = null; $lineItem = null;
$lineItemPriceTotal = array_sum(array_map(static function (LineItemCreate $lineItem) { // Calculate total of all current line items
return $lineItem->getAmountIncludingTax(); $lineItemPriceTotal = array_sum(array_map(static fn(LineItemCreate $li) => $li->getAmountIncludingTax(), $lineItems));
}, $lineItems));
$adjustmentPrice = $this->order->getAmountTotal() - $lineItemPriceTotal; $this->logger->debug("LineItem price total before adjustment: $lineItemPriceTotal");
$adjustmentPrice = self::round($adjustmentPrice);
// Get shipping total including taxes from the order
$shippingCosts = $this->transaction->getOrder()->getShippingCosts();
$shippingTotal = $shippingCosts ? self::round($shippingCosts->getTotalPrice()) : 0.0;
// Add shipping to the line items total if it's not already included
$hasShippingLineItem = array_filter($lineItems, static fn(LineItemCreate $li) => $li->getType() === LineItemType::SHIPPING);
if (!$hasShippingLineItem && $shippingTotal > 0) {
$lineItemPriceTotal += $shippingTotal;
}
$adjustmentPrice = self::round($this->transaction->getOrder()->getAmountTotal() - $lineItemPriceTotal);
if (abs($adjustmentPrice) != 0) { if (abs($adjustmentPrice) != 0) {
if ($this->settings->isLineItemConsistencyEnabled()) { if ($this->settings->isLineItemConsistencyEnabled()) {
$error = strtr('LineItems total :lineItemTotal does not add up to order total :orderTotal', [ $error = strtr('LineItems total :lineItemTotal does not add up to order total :orderTotal', [
':lineItemTotal' => $lineItemPriceTotal, ':lineItemTotal' => $lineItemPriceTotal,
':orderTotal' => $this->order->getAmountTotal(), ':orderTotal' => $this->transaction->getOrder()->getAmountTotal(),
]); ]);
$this->logger->critical($error); $this->logger->critical($error);
throw new \Exception($error); throw new \Exception($error);
@@ -889,20 +816,6 @@ class TransactionPayload extends AbstractPayload
return $addressPayload; return $addressPayload;
} }
/**
* @param string $id
*
* @return \VRPaymentPayment\Core\Api\PaymentMethodConfiguration\Entity\PaymentMethodConfigurationEntity
*/
protected function getPaymentConfiguration(string $id): PaymentMethodConfigurationEntity
{
$criteria = (new Criteria([$id]));
return $this->container->get('vrpayment_payment_method_configuration.repository')
->search($criteria, $this->salesChannelContext->getContext())
->getEntities()->first();
}
/** /**
* Get failure URL * Get failure URL
* *
@@ -4,21 +4,21 @@
@modal-close="$emit('modal-close')"> @modal-close="$emit('modal-close')">
{% block vrpayment_order_action_completion_amount %} {% block vrpayment_order_action_completion_amount %}
<mt-checkbox <sw-checkbox-field
:label="$tc('vrpayment-order.captureAction.button.text')" :label="$tc('vrpayment-order.captureAction.button.text')"
v-model:checked="isCompletion"> v-model:value="isCompletion">
</mt-checkbox> </sw-checkbox-field>
{% endblock %} {% endblock %}
{% block vrpayment_order_action_completion_confirm_button %} {% block vrpayment_order_action_completion_confirm_button %}
<template #modal-footer> <template #modal-footer>
<mt-button variant="primary" <sw-button variant="primary"
@click="completion"> @click="completion">
{{ $tc('vrpayment-order.refundAction.confirmButton.text') }} {{ $tc('vrpayment-order.refundAction.confirmButton.text') }}
</mt-button> </sw-button>
</template> </template>
{% endblock %} {% endblock %}
<mt-loader v-if="isLoading"></mt-loader> <sw-loader v-if="isLoading"></sw-loader>
</sw-modal> </sw-modal>
{% endblock %} {% endblock %}
@@ -4,23 +4,23 @@
@modal-close="$emit('modal-close')"> @modal-close="$emit('modal-close')">
{% block vrpayment_order_action_refund_amount_by_amount %} {% block vrpayment_order_action_refund_amount_by_amount %}
<mt-number-field <sw-number-field
:max="refundableAmount" :max="refundableAmount"
:min="0" :min="0"
v-model="refundAmount" v-model:value="refundAmount"
:label="$tc('vrpayment-order.refund.refundAmount.label')" :label="$tc('vrpayment-order.refund.refundAmount.label')"
:suffix="currency"> :suffix="currency">
</mt-number-field> </sw-number-field>
{% endblock %} {% endblock %}
{% block vrpayment_order_action_refund_confirm_button_by_amount %} {% block vrpayment_order_action_refund_confirm_button_by_amount %}
<template #modal-footer> <template #modal-footer>
<mt-button variant="primary" @click="refundByAmount()"> <sw-button variant="primary" @click="refundByAmount()">
{{ $tc('vrpayment-order.refundAction.confirmButton.text') }} {{ $tc('vrpayment-order.refundAction.confirmButton.text') }}
</mt-button> </sw-button>
</template> </template>
{% endblock %} {% endblock %}
<mt-loader v-if="isLoading"></mt-loader> <sw-loader v-if="isLoading"></sw-loader>
</sw-modal> </sw-modal>
{% endblock %} {% endblock %}
@@ -70,18 +70,9 @@ Component.register('vrpayment-order-action-refund-by-amount', {
}); });
}).catch((errorResponse) => { }).catch((errorResponse) => {
try { try {
var errorTitle;
var errorMessage;
if (errorResponse.response.data == 'refundExceedsAmount') {
errorTitle = this.$tc('vrpayment-order.refundAction.refundExceedsTotalError.title');
errorMessage = this.$tc('vrpayment-order.refundAction.refundExceedsTotalError.messageRefundAmountExceedsAvailableBalance');
} else {
errorTitle = errorResponse.response.data.errors[0].title;
errorMessage = errorResponse.response.data.errors[0].detail;
}
this.createNotificationError({ this.createNotificationError({
title: errorTitle, title: errorResponse.response.data.errors[0].title,
message: errorMessage, message: errorResponse.response.data.errors[0].detail,
autoClose: false autoClose: false
}); });
} catch (e) { } catch (e) {
@@ -4,13 +4,13 @@
@modal-close="$emit('modal-close')"> @modal-close="$emit('modal-close')">
{% block vrpayment_order_action_refund_amount_partial %} {% block vrpayment_order_action_refund_amount_partial %}
<mt-number-field <sw-number-field
:max="this.$parent.$parent.itemRefundableAmount" :max="this.$parent.$parent.itemRefundableAmount"
:min="0.00" :min="0.00"
v-model="refundAmount" v-model:value="refundAmount"
:label="$tc('vrpayment-order.refund.refundAmount.label')" :label="$tc('vrpayment-order.refund.refundAmount.label')"
:suffix="currency"> :suffix="currency">
</mt-number-field> </sw-number-field>
<div> <div>
{{ $tc('vrpayment-order.refundAction.maxAvailableAmountToRefund') }}: {{ $tc('vrpayment-order.refundAction.maxAvailableAmountToRefund') }}:
@@ -20,12 +20,12 @@
{% block vrpayment_order_action_refund_confirm_button_partial %} {% block vrpayment_order_action_refund_confirm_button_partial %}
<template #modal-footer> <template #modal-footer>
<mt-button variant="primary" @click="createPartialRefund(this.$parent.$parent.currentLineItem)"> <sw-button variant="primary" @click="createPartialRefund(this.$parent.$parent.currentLineItem)">
{{ $tc('vrpayment-order.refundAction.confirmButton.text') }} {{ $tc('vrpayment-order.refundAction.confirmButton.text') }}
</mt-button> </sw-button>
</template> </template>
{% endblock %} {% endblock %}
<mt-loader v-if="isLoading"></mt-loader> <sw-loader v-if="isLoading"></sw-loader>
</sw-modal> </sw-modal>
{% endblock %} {% endblock %}
@@ -47,9 +47,7 @@ Component.register('vrpayment-order-action-refund-partial', {
createdComponent() { createdComponent() {
this.isLoading = false; this.isLoading = false;
this.currency = this.transactionData.transactions[0].currency; this.currency = this.transactionData.transactions[0].currency;
if (!this.refundAmount) {
this.refundAmount = this.$parent.$parent.itemRefundableAmount; this.refundAmount = this.$parent.$parent.itemRefundableAmount;
}
}, },
createPartialRefund(itemUniqueId) { createPartialRefund(itemUniqueId) {
@@ -5,12 +5,12 @@
{% block vrpayment_order_action_refund_confirm_button_selected %} {% block vrpayment_order_action_refund_confirm_button_selected %}
<template #modal-footer> <template #modal-footer>
<mt-button variant="primary" @click="refundSelected()"> <sw-button variant="primary" @click="refundSelected()">
{{ $tc('vrpayment-order.refundAction.confirmButton.text') }} {{ $tc('vrpayment-order.refundAction.confirmButton.text') }}
</mt-button> </sw-button>
</template> </template>
{% endblock %} {% endblock %}
<mt-loader v-if="isLoading"></mt-loader> <sw-loader v-if="isLoading"></sw-loader>
</sw-modal> </sw-modal>
{% endblock %} {% endblock %}
@@ -5,12 +5,12 @@
{% block vrpayment_order_action_refund_amount %} {% block vrpayment_order_action_refund_amount %}
<mt-number-field <sw-number-field
:max="this.$parent.$parent.itemRefundableQuantity" :max="this.$parent.$parent.itemRefundableQuantity"
:min="0" :min="0"
v-model="refundQuantity" v-model:value="refundQuantity"
:label="$tc('vrpayment-order.refund.refundQuantity.label')"> :label="$tc('vrpayment-order.refund.refundQuantity.label')">
</mt-number-field> </sw-number-field>
<div> <div>
{{ $tc('vrpayment-order.refundAction.maxAvailableItemsToRefund') }}: {{ $tc('vrpayment-order.refundAction.maxAvailableItemsToRefund') }}:
@@ -20,12 +20,12 @@
{% block vrpayment_order_action_refund_confirm_button %} {% block vrpayment_order_action_refund_confirm_button %}
<template #modal-footer> <template #modal-footer>
<mt-button variant="primary" @click="refund()"> <sw-button variant="primary" @click="refund()">
{{ $tc('vrpayment-order.refundAction.confirmButton.text') }} {{ $tc('vrpayment-order.refundAction.confirmButton.text') }}
</mt-button> </sw-button>
</template> </template>
{% endblock %} {% endblock %}
<mt-loader v-if="isLoading"></mt-loader> <sw-loader v-if="isLoading"></sw-loader>
</sw-modal> </sw-modal>
{% endblock %} {% endblock %}
@@ -4,22 +4,21 @@
@modal-close="$emit('modal-close')"> @modal-close="$emit('modal-close')">
{% block vrpayment_order_action_void_amount %} {% block vrpayment_order_action_void_amount %}
{# Review if this v-model:checked="isVoid" needs to change to checked #} <sw-checkbox-field
<mt-checkbox
:label="$tc('vrpayment-order.voidAction.confirm.message')" :label="$tc('vrpayment-order.voidAction.confirm.message')"
v-model:checked="isVoid"> v-model:value="isVoid">
</mt-checkbox> </sw-checkbox-field>
{% endblock %} {% endblock %}
{% block vrpayment_order_action_void_confirm_button %} {% block vrpayment_order_action_void_confirm_button %}
<template #modal-footer> <template #modal-footer>
<mt-button variant="primary" <sw-button variant="primary"
@click="voidPayment"> @click="voidPayment">
{{ $tc('vrpayment-order.refundAction.confirmButton.text') }} {{ $tc('vrpayment-order.refundAction.confirmButton.text') }}
</mt-button> </sw-button>
</template> </template>
{% endblock %} {% endblock %}
<mt-loader v-if="isLoading"></mt-loader> <sw-loader v-if="isLoading"></sw-loader>
</sw-modal> </sw-modal>
{% endblock %} {% endblock %}
@@ -1,7 +1,6 @@
{% block sw_order_detail_content_tabs_general %} {% block sw_order_detail_content_tabs_general %}
{% parent %} {% parent %}
{# sw-tabs-item will dissappear. See: https://github.com/shopware/shopware/blob/trunk/UPGRADE-6.7.md#sw-tabs-is-removed #}
<sw-tabs-item v-if="isVRPaymentPayment" <sw-tabs-item v-if="isVRPaymentPayment"
:route="{ name: 'vrpayment.order.detail', params: { id: $route.params.id } }" :route="{ name: 'vrpayment.order.detail', params: { id: $route.params.id } }"
:title="$tc('vrpayment-order.header')"> :title="$tc('vrpayment-order.header')">
@@ -3,7 +3,7 @@
margin-top: 40px; margin-top: 40px;
} }
.sw-order-detail-base .mt-card-view__content { .sw-order-detail-base .sw-card-view__content {
overflow-x: visible; overflow-x: visible;
overflow-y: visible; overflow-y: visible;
} }
@@ -1,61 +1,61 @@
{% block vrpayment_order_detail %} {% block vrpayment_order_detail %}
<div class="vrpayment-order-detail"> <div class="vrpayment-order-detail">
<div v-if="!isLoading"> <div v-if="!isLoading">
<mt-card :title="$tc('vrpayment-order.paymentDetails.cardTitle')"> <sw-card :title="$tc('vrpayment-order.paymentDetails.cardTitle')">
<template #grid> <template #grid>
{% block vrpayment_order_actions_section %} {% block vrpayment_order_actions_section %}
<mt-card-section secondary slim> <sw-card-section secondary slim>
{% block vrpayment_order_transaction_refunds_action_button %} {% block vrpayment_order_transaction_refunds_action_button %}
<mt-button <sw-button
variant="primary" variant="primary"
size="small" size="small"
:disabled="transaction.state != 'FULFILL' || refundableAmount <= 0" :disabled="transaction.state != 'FULFILL' || refundableAmount <= 0"
@click="spawnModal('refundByAmount')"> @click="spawnModal('refundByAmount')">
{{ $tc('vrpayment-order.buttons.label.refund') }} {{ $tc('vrpayment-order.buttons.label.refund') }}
</mt-button> </sw-button>
{% endblock %} {% endblock %}
{% block vrpayment_order_transaction_completion_action_button %} {% block vrpayment_order_transaction_completion_action_button %}
<mt-button <sw-button
variant="primary" variant="primary"
size="small" size="small"
:disabled="transaction.state != 'AUTHORIZED' || isLoading" :disabled="transaction.state != 'AUTHORIZED' || isLoading"
@click="spawnModal('completion')"> @click="spawnModal('completion')">
{{ $tc('vrpayment-order.buttons.label.completion') }} {{ $tc('vrpayment-order.buttons.label.completion') }}
</mt-button> </sw-button>
{% endblock %} {% endblock %}
{% block vrpayment_order_transaction_void_action_button %} {% block vrpayment_order_transaction_void_action_button %}
<mt-button <sw-button
variant="primary" variant="primary"
size="small" size="small"
:disabled="transaction.state != 'AUTHORIZED' || isLoading" :disabled="transaction.state != 'AUTHORIZED' || isLoading"
@click="spawnModal('void')"> @click="spawnModal('void')">
{{ $tc('vrpayment-order.buttons.label.void') }} {{ $tc('vrpayment-order.buttons.label.void') }}
</mt-button> </sw-button>
{% endblock %} {% endblock %}
{% block vrpayment_order_transaction_download_invoice_action_button %} {% block vrpayment_order_transaction_download_invoice_action_button %}
<mt-button <sw-button
variant="primary" variant="primary"
size="small" size="small"
:disabled="transaction.state != 'FULFILL'" :disabled="transaction.state != 'FULFILL'"
@click="downloadInvoice()"> @click="downloadInvoice()">
{{ $tc('vrpayment-order.buttons.label.download-invoice') }} {{ $tc('vrpayment-order.buttons.label.download-invoice') }}
</mt-button> </sw-button>
{% endblock %} {% endblock %}
{% block vrpayment_order_transaction_download_packing_slip_action_button %} {% block vrpayment_order_transaction_download_packing_slip_action_button %}
<mt-button <sw-button
variant="primary" variant="primary"
size="small" size="small"
:disabled="transaction.state != 'FULFILL'" :disabled="transaction.state != 'FULFILL'"
@click="downloadPackingSlip()"> @click="downloadPackingSlip()">
{{ $tc('vrpayment-order.buttons.label.download-packing-slip') }} {{ $tc('vrpayment-order.buttons.label.download-packing-slip') }}
</mt-button> </sw-button>
{% endblock %} {% endblock %}
</mt-card-section> </sw-card-section>
{% endblock %} {% endblock %}
</template> </template>
</mt-card> </sw-card>
{% block vrpayment_order_transaction_history_card %} {% block vrpayment_order_transaction_history_card %}
<mt-card :title="$tc('vrpayment-order.transactionHistory.cardTitle')"> <sw-card :title="$tc('vrpayment-order.transactionHistory.cardTitle')">
<template #grid> <template #grid>
{% block vrpayment_order_transaction_history_grid %} {% block vrpayment_order_transaction_history_grid %}
@@ -78,10 +78,10 @@
{% endblock %} {% endblock %}
</template> </template>
</mt-card> </sw-card>
{% endblock %} {% endblock %}
{% block vrpayment_order_transaction_line_items_card %} {% block vrpayment_order_transaction_line_items_card %}
<mt-card :title="$tc('vrpayment-order.lineItem.cardTitle')"> <sw-card :title="$tc('vrpayment-order.lineItem.cardTitle')">
<template #grid> <template #grid>
{% block vrpayment_order_transaction_line_items_grid %} {% block vrpayment_order_transaction_line_items_grid %}
@@ -131,10 +131,10 @@
</sw-data-grid> </sw-data-grid>
{% endblock %} {% endblock %}
</template> </template>
</mt-card> </sw-card>
{% endblock %} {% endblock %}
{% block vrpayment_order_transaction_refunds_card %} {% block vrpayment_order_transaction_refunds_card %}
<mt-card :title="$tc('vrpayment-order.refund.cardTitle')" v-if="transactionData.refunds.length > 0"> <sw-card :title="$tc('vrpayment-order.refund.cardTitle')" v-if="transactionData.refunds.length > 0">
<template #grid> <template #grid>
{% block vrpayment_order_transaction_refunds_grid %} {% block vrpayment_order_transaction_refunds_grid %}
@@ -147,7 +147,7 @@
{% endblock %} {% endblock %}
</template> </template>
</mt-card> </sw-card>
{% endblock %} {% endblock %}
{% block vrpayment_order_actions_modal_refund_partial %} {% block vrpayment_order_actions_modal_refund_partial %}
<vrpayment-order-action-refund-partial <vrpayment-order-action-refund-partial
@@ -195,6 +195,6 @@
</vrpayment-order-action-void> </vrpayment-order-action-void>
{% endblock %} {% endblock %}
</div> </div>
<mt-loader v-if="isLoading"></mt-loader> <sw-loader v-if="isLoading"></sw-loader>
</div> </div>
{% endblock %} {% endblock %}
@@ -77,11 +77,7 @@
"successMessage": "Ihre Rückerstattung war erfolgreich", "successMessage": "Ihre Rückerstattung war erfolgreich",
"successTitle": "Erfolg", "successTitle": "Erfolg",
"maxAvailableItemsToRefund": "Maximal Verfügbare Artikel zum Erstatten", "maxAvailableItemsToRefund": "Maximal Verfügbare Artikel zum Erstatten",
"maxAvailableAmountToRefund": "Maximal verfügbarer Erstattungsbetrag", "maxAvailableAmountToRefund": "Maximal verfügbarer Erstattungsbetrag"
"refundExceedsTotalError": {
"title": "Fehler beim Erstellen der Rückerstattung.",
"messageRefundAmountExceedsAvailableBalance": "Der Rückerstattungsbetrag übersteigt das verfügbare Guthaben."
}
}, },
"transactionHistory": { "transactionHistory": {
"cardTitle": "Einzelheiten", "cardTitle": "Einzelheiten",
@@ -78,11 +78,7 @@
"successMessage": "Your refund was successful.", "successMessage": "Your refund was successful.",
"successTitle": "Success", "successTitle": "Success",
"maxAvailableItemsToRefund": "Maximum available items to refund", "maxAvailableItemsToRefund": "Maximum available items to refund",
"maxAvailableAmountToRefund": "Maximum available amount to refund", "maxAvailableAmountToRefund": "Maximum available amount to refund"
"refundExceedsTotalError": {
"title": "Error while creating the refund.",
"messageRefundAmountExceedsAvailableBalance": "Refund amount exceeds available balance."
}
}, },
"transactionHistory": { "transactionHistory": {
"cardTitle": "Details", "cardTitle": "Details",
@@ -77,11 +77,7 @@
"successMessage": "Votre remboursement a été effectué avec succès.", "successMessage": "Votre remboursement a été effectué avec succès.",
"successTitle": "Succès", "successTitle": "Succès",
"maxAvailableItemsToRefund": "Nombre maximum d'articles disponibles pour le remboursement", "maxAvailableItemsToRefund": "Nombre maximum d'articles disponibles pour le remboursement",
"maxAvailableAmountToRefund": "Montant maximal disponible pour le remboursement", "maxAvailableAmountToRefund": "Montant maximal disponible pour le remboursement"
"refundExceedsTotalError": {
"title": "Erreur lors de la création du remboursement.",
"messageRefundAmountExceedsAvailableBalance": "Le montant du remboursement dépasse le solde disponible."
}
}, },
"transactionHistory": { "transactionHistory": {
"cardTitle": "Détails", "cardTitle": "Détails",
@@ -77,11 +77,7 @@
"successMessage": "Il tuo rimborso è andato a buon fine.", "successMessage": "Il tuo rimborso è andato a buon fine.",
"successTitle": "Successo", "successTitle": "Successo",
"maxAvailableItemsToRefund": "Numero massimo di articoli disponibili da rimborsare", "maxAvailableItemsToRefund": "Numero massimo di articoli disponibili da rimborsare",
"maxAvailableAmountToRefund": "Importo massimo disponibile per il rimborso", "maxAvailableAmountToRefund": "Importo massimo disponibile per il rimborso"
"refundExceedsTotalError": {
"title": "Errore durante la creazione del rimborso.",
"messageRefundAmountExceedsAvailableBalance": "LL'importo del rimborso supera il saldo disponibile."
}
}, },
"transactionHistory": { "transactionHistory": {
"cardTitle": "Dettagli", "cardTitle": "Dettagli",
@@ -1,4 +1,4 @@
<mt-card class="mt-card" <sw-card class="sw-card"
:title="$tc('vrpayment-settings.settingForm.advancedOptions.cardTitle')"> :title="$tc('vrpayment-settings.settingForm.advancedOptions.cardTitle')">
<sw-container> <sw-container>
<div v-if="actualConfigData" class="vrpayment-settings-advanced-options-fields"> <div v-if="actualConfigData" class="vrpayment-settings-advanced-options-fields">
@@ -7,16 +7,16 @@
:inheritedValue="selectedSalesChannelId == null ? null : allConfigs['null'][CONFIG_STOREFRONT_WEBHOOKS_UPDATE_ENABLED]" :inheritedValue="selectedSalesChannelId == null ? null : allConfigs['null'][CONFIG_STOREFRONT_WEBHOOKS_UPDATE_ENABLED]"
:customInheritationCheckFunction="checkBoolFieldInheritance"> :customInheritationCheckFunction="checkBoolFieldInheritance">
<template #content="props"> <template #content="props">
<mt-switch <sw-switch-field
:name="CONFIG_STOREFRONT_WEBHOOKS_UPDATE_ENABLED" :name="CONFIG_STOREFRONT_WEBHOOKS_UPDATE_ENABLED"
bordered bordered
:mapInheritance="props" :mapInheritance="props"
:label="$tc('vrpayment-settings.settingForm.advancedOptions.webhooksUpdateEnabled.label')" :label="$tc('vrpayment-settings.settingForm.advancedOptions.webhooksUpdateEnabled.label')"
:helpText="$tc('vrpayment-settings.settingForm.advancedOptions.webhooksUpdateEnabled.tooltipText')" :helpText="$tc('vrpayment-settings.settingForm.advancedOptions.webhooksUpdateEnabled.tooltipText')"
:disabled="props.isInherited" :disabled="props.isInherited"
:checked="props.currentValue" :value="props.currentValue"
@update:checked="props.updateCurrentValue"> @update:value="props.updateCurrentValue">
</mt-switch> </sw-switch-field>
</template> </template>
</sw-inherit-wrapper> </sw-inherit-wrapper>
@@ -25,19 +25,19 @@
:inheritedValue="selectedSalesChannelId == null ? null : allConfigs['null'][CONFIG_STOREFRONT_PAYMENTS_UPDATE_ENABLED]" :inheritedValue="selectedSalesChannelId == null ? null : allConfigs['null'][CONFIG_STOREFRONT_PAYMENTS_UPDATE_ENABLED]"
:customInheritationCheckFunction="checkBoolFieldInheritance"> :customInheritationCheckFunction="checkBoolFieldInheritance">
<template #content="props"> <template #content="props">
<mt-switch <sw-switch-field
:name="CONFIG_STOREFRONT_PAYMENTS_UPDATE_ENABLED" :name="CONFIG_STOREFRONT_PAYMENTS_UPDATE_ENABLED"
bordered bordered
:mapInheritance="props" :mapInheritance="props"
:label="$tc('vrpayment-settings.settingForm.advancedOptions.paymentsUpdateEnabled.label')" :label="$tc('vrpayment-settings.settingForm.advancedOptions.paymentsUpdateEnabled.label')"
:helpText="$tc('vrpayment-settings.settingForm.advancedOptions.paymentsUpdateEnabled.tooltipText')" :helpText="$tc('vrpayment-settings.settingForm.advancedOptions.paymentsUpdateEnabled.tooltipText')"
:disabled="props.isInherited" :disabled="props.isInherited"
:checked="props.currentValue" :value="props.currentValue"
@update:checked="props.updateCurrentValue"> @update:value="props.updateCurrentValue">
</mt-switch> </sw-switch-field>
</template> </template>
</sw-inherit-wrapper> </sw-inherit-wrapper>
</div> </div>
</sw-container> </sw-container>
</mt-card> </sw-card>
@@ -1,6 +1,6 @@
{% block vrpayment_settings_content_card_channel_config_credentials %} {% block vrpayment_settings_content_card_channel_config_credentials %}
<mt-card <sw-card
class="mt-card" class="sw-card"
:title="$tc('vrpayment-settings.settingForm.credentials.cardTitle')" :title="$tc('vrpayment-settings.settingForm.credentials.cardTitle')"
v-if="actualConfigData" v-if="actualConfigData"
> >
@@ -14,20 +14,20 @@
{% block vrpayment_settings_content_card_channel_config_credentials_card_container_settings_space_id %} {% block vrpayment_settings_content_card_channel_config_credentials_card_container_settings_space_id %}
<sw-inherit-wrapper <sw-inherit-wrapper
v-model:value="actualConfigData[CONFIG_SPACE_ID]" v-model:value="actualConfigData[CONFIG_SPACE_ID]"
:inheritedValue="selectedSalesChannelId === null ? null : allConfigs['null'][CONFIG_SPACE_ID]" :inheritedValue="getInheritedValue(CONFIG_SPACE_ID)"
:customInheritationCheckFunction="checkNumberFieldInheritance"> :customInheritationCheckFunction="checkNumberFieldInheritance">
<template #content="props"> <template #content="props">
<mt-number-field <sw-number-field
:name="CONFIG_SPACE_ID" :name="CONFIG_SPACE_ID"
:required="true" :required="true"
:mapInheritance="props" :mapInheritance="props"
:label="$tc('vrpayment-settings.settingForm.credentials.spaceId.label')" :label="$tc('vrpayment-settings.settingForm.credentials.spaceId.label')"
:helpText="$tc('vrpayment-settings.settingForm.credentials.spaceId.tooltipText')" :helpText="$tc('vrpayment-settings.settingForm.credentials.spaceId.tooltipText')"
:disabled="props.isInherited || !acl.can('vrpayment.editor')" :disabled="!acl.can('vrpayment.editor')"
:model-value="props.currentValue" :value="props.currentValue"
:error="spaceIdErrorState" :error="spaceIdErrorState"
@update:model-value="props.updateCurrentValue"> @update:value="props.updateCurrentValue">
</mt-number-field> </sw-number-field>
</template> </template>
</sw-inherit-wrapper> </sw-inherit-wrapper>
{% endblock %} {% endblock %}
@@ -35,20 +35,20 @@
{% block vrpayment_settings_content_card_channel_config_credentials_card_container_settings_user_id %} {% block vrpayment_settings_content_card_channel_config_credentials_card_container_settings_user_id %}
<sw-inherit-wrapper <sw-inherit-wrapper
v-model:value="actualConfigData[CONFIG_USER_ID]" v-model:value="actualConfigData[CONFIG_USER_ID]"
:inheritedValue="selectedSalesChannelId === null ? null : allConfigs['null'][CONFIG_USER_ID]" :inheritedValue="getInheritedValue(CONFIG_USER_ID)"
:customInheritationCheckFunction="checkNumberFieldInheritance"> :customInheritationCheckFunction="checkNumberFieldInheritance">
<template #content="props"> <template #content="props">
<mt-number-field <sw-number-field
:name="CONFIG_USER_ID" :name="CONFIG_USER_ID"
:required="true" :required="true"
:mapInheritance="props" :mapInheritance="props"
:label="$tc('vrpayment-settings.settingForm.credentials.userId.label')" :label="$tc('vrpayment-settings.settingForm.credentials.userId.label')"
:helpText="$tc('vrpayment-settings.settingForm.credentials.userId.tooltipText')" :helpText="$tc('vrpayment-settings.settingForm.credentials.userId.tooltipText')"
:disabled="props.isInherited || !acl.can('vrpayment.editor')" :disabled="!acl.can('vrpayment.editor')"
:model-value="props.currentValue" :value="props.currentValue"
:error="userIdErrorState" :error="userIdErrorState"
@update:model-value="props.updateCurrentValue"> @update:value="props.updateCurrentValue">
</mt-number-field> </sw-number-field>
</template> </template>
</sw-inherit-wrapper> </sw-inherit-wrapper>
{% endblock %} {% endblock %}
@@ -56,40 +56,37 @@
{% block vrpayment_settings_content_card_channel_config_credentials_card_container_settings_application_key %} {% block vrpayment_settings_content_card_channel_config_credentials_card_container_settings_application_key %}
<sw-inherit-wrapper <sw-inherit-wrapper
v-model:value="actualConfigData[CONFIG_APPLICATION_KEY]" v-model:value="actualConfigData[CONFIG_APPLICATION_KEY]"
:inheritedValue="selectedSalesChannelId === null ? null : allConfigs['null'][CONFIG_APPLICATION_KEY]" :inheritedValue="getInheritedValue(CONFIG_APPLICATION_KEY)"
:customInheritationCheckFunction="checkTextFieldInheritance"> :customInheritationCheckFunction="checkTextFieldInheritance">
<template #content="props"> <template #content="props">
<mt-password-field <sw-password-field
:name="CONFIG_APPLICATION_KEY" :name="CONFIG_APPLICATION_KEY"
:required="true" :required="true"
:passwordToggleAble="true" :passwordToggleAble="true"
:mapInheritance="props" :mapInheritance="props"
:label="$tc('vrpayment-settings.settingForm.credentials.applicationKey.label')" :label="$tc('vrpayment-settings.settingForm.credentials.applicationKey.label')"
:helpText="$tc('vrpayment-settings.settingForm.credentials.applicationKey.tooltipText')" :helpText="$tc('vrpayment-settings.settingForm.credentials.applicationKey.tooltipText')"
:disabled="props.isInherited || !acl.can('vrpayment.editor')" :disabled="!acl.can('vrpayment.editor')"
:model-value="props.currentValue" :value="props.currentValue"
:error="applicationKeyErrorState" :error="applicationKeyErrorState"
@update:model-value="props.updateCurrentValue"> @update:value="props.updateCurrentValue">
</mt-password-field> </sw-password-field>
</template> </template>
</sw-inherit-wrapper> </sw-inherit-wrapper>
{% endblock %} {% endblock %}
</div> </div>
{% endblock %} {% endblock %}
{% verbatim %}
<sw-container columns="1fr 1fr" gap="0px 30px"> <sw-container columns="1fr 1fr" gap="0px 30px">
<mt-button <sw-button-process
variant="primary"
:isLoading="isTesting" :isLoading="isTesting"
@click="emitCheckApiConnectionEvent"> @click="emitCheckApiConnectionEvent">
{{ $tc('vrpayment-settings.settingForm.credentials.button.label') }} {{ $tc('vrpayment-settings.settingForm.credentials.button.label') }}
</mt-button> </sw-button-process>
</sw-container> </sw-container>
{% endverbatim %}
</sw-container> </sw-container>
{% endblock %} {% endblock %}
</mt-card> </sw-card>
{% endblock %} {% endblock %}
@@ -6,7 +6,7 @@ import constants from '../../page/vrpayment-settings/configuration-constants'
const {Component, Mixin} = Shopware; const {Component, Mixin} = Shopware;
Component.register('sw-vrpayment-credentials', { Component.register('sw-vrpayment-credentials', {
template: template, template,
name: 'VRPaymentCredentials', name: 'VRPaymentCredentials',
@@ -29,7 +29,9 @@ Component.register('sw-vrpayment-credentials', {
}, },
selectedSalesChannelId: { selectedSalesChannelId: {
required: true type: [String, null],
required: false,
default: null
}, },
spaceIdFilled: { spaceIdFilled: {
type: Boolean, type: Boolean,
@@ -68,22 +70,24 @@ Component.register('sw-vrpayment-credentials', {
}; };
}, },
computed: {
currentConfig() {
if (this.selectedSalesChannelId && this.allConfigs[this.selectedSalesChannelId]) {
return this.allConfigs[this.selectedSalesChannelId];
}
return this.allConfigs['null'] || {};
}
},
methods: { methods: {
checkTextFieldInheritance(value) { checkTextFieldInheritance(value) {
if (typeof value !== 'string') { return !value || value.length <= 0;
return true;
}
return value.length <= 0;
}, },
checkNumberFieldInheritance(value) { checkNumberFieldInheritance(value) {
if (typeof value !== 'number') { return value == null || value === '';
return true;
}
return value.length <= 0;
}, },
checkBoolFieldInheritance(value) { checkBoolFieldInheritance(value) {
@@ -94,12 +98,16 @@ Component.register('sw-vrpayment-credentials', {
// Used to trigger API connection testing from this component. // Used to trigger API connection testing from this component.
emitCheckApiConnectionEvent() { emitCheckApiConnectionEvent() {
const apiConnectionParams = { const apiConnectionParams = {
spaceId: this.actualConfigData[constants.CONFIG_SPACE_ID], spaceId: this.currentConfig[constants.CONFIG_SPACE_ID],
userId: this.actualConfigData[constants.CONFIG_USER_ID], userId: this.currentConfig[constants.CONFIG_USER_ID],
applicationKey: this.actualConfigData[constants.CONFIG_APPLICATION_KEY] applicationKey: this.currentConfig[constants.CONFIG_APPLICATION_KEY]
}; };
this.$emit('check-api-connection-event', apiConnectionParams); this.$emit('check-api-connection-event', apiConnectionParams);
},
getInheritedValue(key) {
return this.allConfigs['null']?.[key] ?? null;
} }
} }
}); });
@@ -1,5 +1,5 @@
{% block vrpayment_settings_content_card_channel_config_options %} {% block vrpayment_settings_content_card_channel_config_options %}
<mt-card class="mt-card" <sw-card class="sw-card"
:title="$tc('vrpayment-settings.settingForm.options.cardTitle')"> :title="$tc('vrpayment-settings.settingForm.options.cardTitle')">
{% block vrpayment_settings_content_card_channel_config_credentials_card_container %} {% block vrpayment_settings_content_card_channel_config_credentials_card_container %}
@@ -14,15 +14,15 @@
:inheritedValue="selectedSalesChannelId === null ? null : allConfigs['null'][CONFIG_SPACE_VIEW_ID]" :inheritedValue="selectedSalesChannelId === null ? null : allConfigs['null'][CONFIG_SPACE_VIEW_ID]"
:customInheritationCheckFunction="checkNumberFieldInheritance"> :customInheritationCheckFunction="checkNumberFieldInheritance">
<template #content="props"> <template #content="props">
<mt-number-field <sw-number-field
:name="CONFIG_SPACE_VIEW_ID" :name="CONFIG_SPACE_VIEW_ID"
:mapInheritance="props" :mapInheritance="props"
:label="$tc('vrpayment-settings.settingForm.options.spaceViewId.label')" :label="$tc('vrpayment-settings.settingForm.options.spaceViewId.label')"
:helpText="$tc('vrpayment-settings.settingForm.options.spaceViewId.tooltipText')" :helpText="$tc('vrpayment-settings.settingForm.options.spaceViewId.tooltipText')"
:disabled="props.isInherited" :disabled="props.isInherited"
:model-value="props.currentValue" :value="props.currentValue"
@update:model-value="props.updateCurrentValue"> @update:value="props.updateCurrentValue">
</mt-number-field> </sw-number-field>
</template> </template>
</sw-inherit-wrapper> </sw-inherit-wrapper>
{% endblock %} {% endblock %}
@@ -55,16 +55,16 @@
:inheritedValue="selectedSalesChannelId == null ? null : allConfigs['null'][CONFIG_LINE_ITEM_CONSISTENCY_ENABLED]" :inheritedValue="selectedSalesChannelId == null ? null : allConfigs['null'][CONFIG_LINE_ITEM_CONSISTENCY_ENABLED]"
:customInheritationCheckFunction="checkBoolFieldInheritance"> :customInheritationCheckFunction="checkBoolFieldInheritance">
<template #content="props"> <template #content="props">
<mt-switch <sw-switch-field
:name="CONFIG_LINE_ITEM_CONSISTENCY_ENABLED" :name="CONFIG_LINE_ITEM_CONSISTENCY_ENABLED"
bordered bordered
:mapInheritance="props" :mapInheritance="props"
:label="$tc('vrpayment-settings.settingForm.options.lineItemConsistencyEnabled.label')" :label="$tc('vrpayment-settings.settingForm.options.lineItemConsistencyEnabled.label')"
:helpText="$tc('vrpayment-settings.settingForm.options.lineItemConsistencyEnabled.tooltipText')" :helpText="$tc('vrpayment-settings.settingForm.options.lineItemConsistencyEnabled.tooltipText')"
:disabled="props.isInherited" :disabled="props.isInherited"
:checked="props.currentValue" :value="props.currentValue"
@update:checked="props.updateCurrentValue"> @update:value="props.updateCurrentValue">
</mt-switch> </sw-switch-field>
</template> </template>
</sw-inherit-wrapper> </sw-inherit-wrapper>
{% endblock %} {% endblock %}
@@ -75,16 +75,16 @@
:inheritedValue="selectedSalesChannelId == null ? null : allConfigs['null'][CONFIG_EMAIL_ENABLED]" :inheritedValue="selectedSalesChannelId == null ? null : allConfigs['null'][CONFIG_EMAIL_ENABLED]"
:customInheritationCheckFunction="checkBoolFieldInheritance"> :customInheritationCheckFunction="checkBoolFieldInheritance">
<template #content="props"> <template #content="props">
<mt-switch <sw-switch-field
:name="CONFIG_EMAIL_ENABLED" :name="CONFIG_EMAIL_ENABLED"
bordered bordered
:mapInheritance="props" :mapInheritance="props"
:label="$tc('vrpayment-settings.settingForm.options.emailEnabled.label')" :label="$tc('vrpayment-settings.settingForm.options.emailEnabled.label')"
:helpText="$tc('vrpayment-settings.settingForm.options.emailEnabled.tooltipText')" :helpText="$tc('vrpayment-settings.settingForm.options.emailEnabled.tooltipText')"
:disabled="props.isInherited" :disabled="props.isInherited"
:checked="props.currentValue" :value="props.currentValue"
@update:checked="props.updateCurrentValue"> @update:value="props.updateCurrentValue">
</mt-switch> </sw-switch-field>
</template> </template>
</sw-inherit-wrapper> </sw-inherit-wrapper>
{% endblock %} {% endblock %}
@@ -92,6 +92,6 @@
{% endblock %} {% endblock %}
</sw-container> </sw-container>
{% endblock %} {% endblock %}
</mt-card> </sw-card>
{% endblock %} {% endblock %}
@@ -1,5 +1,5 @@
{% block vrpayment_settings_icon %} {% block vrpayment_settings_icon %}
<span class="mt-icon icon--vrpayment-multicolor mt-icon--multicolor" style="width: 16px; height: 16px;"> <span class="sw-icon icon--vrpayment-multicolor sw-icon--multicolor">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" contentScriptType="text/ecmascript" y="0px" zoomAndPan="magnify" style="enable-background:new 0 0 632.126 170.079;" contentStyleType="text/css" viewBox="0 0 632.126 170.079" preserveAspectRatio="xMidYMid meet" xml:space="preserve" version="1.1"> <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" contentScriptType="text/ecmascript" y="0px" zoomAndPan="magnify" style="enable-background:new 0 0 632.126 170.079;" contentStyleType="text/css" viewBox="0 0 632.126 170.079" preserveAspectRatio="xMidYMid meet" xml:space="preserve" version="1.1">
<style type="text/css" xml:space="preserve"> <style type="text/css" xml:space="preserve">
.st0{fill:none;} .st0{fill:none;}
@@ -1,4 +1,4 @@
<mt-card class="mt-card" <sw-card class="sw-card"
:title="$tc('vrpayment-settings.settingForm.storefrontOptions.cardTitle')"> :title="$tc('vrpayment-settings.settingForm.storefrontOptions.cardTitle')">
<sw-container> <sw-container>
<div v-if="actualConfigData" class="vrpayment-settings-storefront-options-fields"> <div v-if="actualConfigData" class="vrpayment-settings-storefront-options-fields">
@@ -7,19 +7,19 @@
:inheritedValue="selectedSalesChannelId == null ? null : allConfigs['null'][CONFIG_STOREFRONT_INVOICE_DOWNLOAD_ENABLED]" :inheritedValue="selectedSalesChannelId == null ? null : allConfigs['null'][CONFIG_STOREFRONT_INVOICE_DOWNLOAD_ENABLED]"
:customInheritationCheckFunction="checkBoolFieldInheritance"> :customInheritationCheckFunction="checkBoolFieldInheritance">
<template #content="props"> <template #content="props">
<mt-switch <sw-switch-field
:name="CONFIG_STOREFRONT_INVOICE_DOWNLOAD_ENABLED" :name="CONFIG_STOREFRONT_INVOICE_DOWNLOAD_ENABLED"
bordered bordered
:mapInheritance="props" :mapInheritance="props"
:label="$tc('vrpayment-settings.settingForm.storefrontOptions.invoiceDownloadEnabled.label')" :label="$tc('vrpayment-settings.settingForm.storefrontOptions.invoiceDownloadEnabled.label')"
:helpText="$tc('vrpayment-settings.settingForm.storefrontOptions.invoiceDownloadEnabled.tooltipText')" :helpText="$tc('vrpayment-settings.settingForm.storefrontOptions.invoiceDownloadEnabled.tooltipText')"
:disabled="props.isInherited" :disabled="props.isInherited"
:checked="props.currentValue" :value="props.currentValue"
@update:checked="props.updateCurrentValue"> @update:value="props.updateCurrentValue">
</mt-switch> </sw-switch-field>
</template> </template>
</sw-inherit-wrapper> </sw-inherit-wrapper>
</div> </div>
</sw-container> </sw-container>
</mt-card> </sw-card>
@@ -1,6 +1,6 @@
{% block vrpayment_settings %} {% block vrpayment_settings %}
<sw-page class="vrpayment-settings">
<sw-page class="vrpayment-settings">
{% block vrpayment_settings_header %} {% block vrpayment_settings_header %}
<template #smart-bar-header> <template #smart-bar-header>
<h2> <h2>
@@ -45,15 +45,14 @@
<mt-card title="Sales Channel Switch"> <mt-card title="Sales Channel Switch">
{% block vrpayment_settings_content_card_channel_config_sales_channel_card_title %} {% block vrpayment_settings_content_card_channel_config_sales_channel_card_title %}
<sw-entity-single-select <sw-single-select
v-model:value="selectedSalesChannelId" :value="selectedSalesChannelId"
labelProperty="translated.name" :options="salesChannel.map(sc => ({ id: sc.id, name: sc.translated.name }))"
labelProperty="name"
valueProperty="id" valueProperty="id"
:mapInheritance="props"
:isLoading="isLoading" :isLoading="isLoading"
entity="sales_channel" @update:value="onInput"
@update:value="onInput"> />
</sw-entity-single-select>
{% endblock %} {% endblock %}
{% block vrpayment_settings_content_card_channel_config_sales_channel_card_footer %} {% block vrpayment_settings_content_card_channel_config_sales_channel_card_footer %}
<template #footer> <template #footer>
@@ -142,5 +141,5 @@
</template> </template>
{% endblock %} {% endblock %}
</sw-page> </sw-page>
{% endblock %} {% endblock %}
@@ -65,10 +65,22 @@ Component.register('vrpayment-settings', {
}; };
}, },
created() {
// Registers a listener for the 'check-api-connection-event'.
// Triggered when this event is emitted.
this.$on('check-api-connection-event', this.onCheckApiConnection);
},
beforeDestroy() {
// Removes the listener for the 'check-api-connection-event'
// before the component is destroyed to prevent memory leaks.
this.$off('check-api-connection-event', this.onCheckApiConnection);
},
watch: { watch: {
config: { config: {
handler(configData) { handler(configData) {
const defaultConfig = this.$refs.configComponent.allConfigs.null; const defaultConfig = (this.$refs.configComponent.allConfigs || {}).null || {};
const salesChannelId = this.$refs.configComponent.selectedSalesChannelId; const salesChannelId = this.$refs.configComponent.selectedSalesChannelId;
if (salesChannelId === null) { if (salesChannelId === null) {
File diff suppressed because one or more lines are too long
@@ -6,21 +6,12 @@
<services> <services>
<service id="VRPaymentPayment\Core\Checkout\PaymentHandler\VRPaymentPaymentHandler"> <service id="VRPaymentPayment\Core\Checkout\PaymentHandler\VRPaymentPaymentHandler">
<argument type="service" id="VRPaymentPayment\Core\Checkout\Cart\CustomCartPersister"/>
<argument type="service" id="VRPaymentPayment\Core\Api\Transaction\Service\TransactionService"/> <argument type="service" id="VRPaymentPayment\Core\Api\Transaction\Service\TransactionService"/>
<argument type="service" id="Shopware\Core\Checkout\Order\Aggregate\OrderTransaction\OrderTransactionStateHandler"/> <argument type="service" id="Shopware\Core\Checkout\Order\Aggregate\OrderTransaction\OrderTransactionStateHandler"/>
<argument type="service" id="Shopware\Core\System\SalesChannel\Context\SalesChannelContextService"/>
<argument type="service" id="order_transaction.repository"/>
<call method="setLogger"> <call method="setLogger">
<argument type="service" id="monolog.logger.vrpayment_payment"/> <argument type="service" id="monolog.logger.vrpayment_payment"/>
</call> </call>
<tag name="shopware.payment.method"/> <tag name="shopware.payment.method.async"/>
</service>
<service id="VRPaymentPayment\Core\Checkout\Cart\CustomCartPersister"
decorates="Shopware\Core\Checkout\Cart\CartPersister"
decoration-inner-name="VRPaymentPayment\Core\Checkout\Cart\CustomCartPersister.inner">
<argument type="service" id="VRPaymentPayment\Core\Checkout\Cart\CustomCartPersister.inner"/>
</service> </service>
</services> </services>
@@ -13,16 +13,18 @@
<argument type="service" id="VRPaymentPayment\Core\Api\Transaction\Service\TransactionService"/> <argument type="service" id="VRPaymentPayment\Core\Api\Transaction\Service\TransactionService"/>
<argument type="service" id="Shopware\Storefront\Page\GenericPageLoader"/> <argument type="service" id="Shopware\Storefront\Page\GenericPageLoader"/>
<argument type="service" id="Shopware\Core\Checkout\Order\SalesChannel\OrderRoute"/> <argument type="service" id="Shopware\Core\Checkout\Order\SalesChannel\OrderRoute"/>
<argument type="service" id="Shopware\Core\Checkout\Order\Aggregate\OrderTransaction\OrderTransactionStateHandler"/>
<argument type="service" id="Shopware\Core\System\StateMachine\StateMachineRegistry"/>
<argument type="service" id="Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface"/>
<call method="setLogger"> <call method="setLogger">
<argument type="service" id="monolog.logger.vrpayment_payment"/> <argument type="service" id="monolog.logger.vrpayment_payment"/>
</call> </call>
<call method="setContainer"> <call method="setContainer">
<argument type="service" id="service_container"/> <argument type="service" id="service_container"/>
</call> </call>
<!-- Removed in 6.7 --> <call method="setTwig">
<!-- <call method="setTwig">
<argument type="service" id="twig"/> <argument type="service" id="twig"/>
</call> --> </call>
</service> </service>
<!-- Subscribers --> <!-- Subscribers -->
@@ -31,6 +33,7 @@
<argument id="VRPaymentPayment\Core\Api\Transaction\Service\TransactionService" type="service"/> <argument id="VRPaymentPayment\Core\Api\Transaction\Service\TransactionService" type="service"/>
<argument id="VRPaymentPayment\Core\Settings\Service\SettingsService" type="service"/> <argument id="VRPaymentPayment\Core\Settings\Service\SettingsService" type="service"/>
<argument id="VRPaymentPayment\Core\Util\PaymentMethodUtil" type="service"/> <argument id="VRPaymentPayment\Core\Util\PaymentMethodUtil" type="service"/>
<argument id="payment_method.repository" type="service"/>
<call method="setLogger"> <call method="setLogger">
<argument type="service" id="monolog.logger.vrpayment_payment"/> <argument type="service" id="monolog.logger.vrpayment_payment"/>
</call> </call>
@@ -1,25 +0,0 @@
{
"base": "/bundles/vrpaymentpayment/administration/",
"entryPoints": {
"v-r-payment-payment": {
"css": [
"/bundles/vrpaymentpayment/administration/assets/v-r-payment-payment-D4AH6HY2.css"
],
"dynamic": [],
"js": [
"/bundles/vrpaymentpayment/administration/assets/v-r-payment-payment-C6eiDWfX.js"
],
"legacy": false,
"preload": []
}
},
"legacy": false,
"metadatas": {},
"version": [
"7.1.0",
7,
1,
0
],
"viteServer": null
}
@@ -1,11 +0,0 @@
{
"main.js": {
"file": "assets/v-r-payment-payment-C6eiDWfX.js",
"name": "v-r-payment-payment",
"src": "main.js",
"isEntry": true,
"css": [
"assets/v-r-payment-payment-D4AH6HY2.css"
]
}
}
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
.sw-order-detail .sw-tabs{margin-top:40px}.sw-order-detail .sw-order-detail-base .mt-card-view__content{overflow-x:visible;overflow-y:visible}.vrpayment-order-detail__data{display:grid}.vrpayment-order-detail__heading{padding-top:15px}
@@ -0,0 +1,2 @@
.sw-order-detail .sw-tabs{margin-top:40px}.sw-order-detail .sw-order-detail-base .sw-card-view__content{overflow-x:visible;overflow-y:visible}
.vrpayment-order-detail__data{display:grid}.vrpayment-order-detail__heading{padding-top:15px}
File diff suppressed because one or more lines are too long
+2 -2
View File
@@ -29,7 +29,7 @@
payment_method_handler_status: 'input[name="vrpayment_payment_handler_validation_status"]', payment_method_handler_status: 'input[name="vrpayment_payment_handler_validation_status"]',
payment_form_id: 'confirmOrderForm', payment_form_id: 'confirmOrderForm',
button_cancel_id: 'vrpaymentOrderCancel', button_cancel_id: 'vrpaymentOrderCancel',
// button_home_override: 'vrpaymentHomeLink', button_home_override: 'vrpaymentHomeLink',
loader_id: 'vrpaymentLoader', loader_id: 'vrpaymentLoader',
checkout_url: null, checkout_url: null,
checkout_url_id: 'checkoutUrl', checkout_url_id: 'checkoutUrl',
@@ -46,7 +46,7 @@
this.cart_recreate_url = document.getElementById(this.cart_recreate_url_id).value; this.cart_recreate_url = document.getElementById(this.cart_recreate_url_id).value;
document.getElementById(this.button_cancel_id).addEventListener('click', this.recreateCart, false); document.getElementById(this.button_cancel_id).addEventListener('click', this.recreateCart, false);
// document.getElementById(this.button_home_override).addEventListener('click', this.recreateCart, false); document.getElementById(this.button_home_override).addEventListener('click', this.recreateCart, false);
document.getElementById(this.payment_form_id).addEventListener('submit', this.submitPayment, false); document.getElementById(this.payment_form_id).addEventListener('submit', this.submitPayment, false);
VRPaymentCheckout.getIframe(); VRPaymentCheckout.getIframe();
@@ -1,9 +1,13 @@
{% sw_extends '@Storefront/storefront/page/checkout/_page.html.twig' %} {% sw_extends '@Storefront/storefront/page/checkout/_page.html.twig' %}
{% block base_header %}
{% sw_include '@VRPaymentPayment/storefront/page/checkout/order/vrpayment_header.html.twig' %}
{% endblock %}
{% block base_navigation %}{% endblock %}
{% block base_body_classes %}vrpayment-payment is-act-confirmpage{% endblock %} {% block base_body_classes %}vrpayment-payment is-act-confirmpage{% endblock %}
{% block page_checkout_main_content %} {% block page_checkout_main_content %}
<div id="vrpaymentOrderCancel"></div>
{% block page_checkout_pay %} {% block page_checkout_pay %}
{% block page_checkout_confirm_header %} {% block page_checkout_confirm_header %}
<h1 class="confirm-main-header"> <h1 class="confirm-main-header">
@@ -140,6 +144,10 @@
</div> </div>
{% endblock %} {% endblock %}
{% block base_footer %}
{% sw_include '@Storefront/storefront/layout/footer/footer-minimal.html.twig' %}
{% endblock %}
{% block base_body_script %} {% block base_body_script %}
{{ parent() }} {{ parent() }}
{% if page.extensions.vRPaymentData %} {% if page.extensions.vRPaymentData %}
@@ -0,0 +1,53 @@
{% sw_extends '@Storefront/storefront/layout/header/header-minimal.html.twig' %}
{% block layout_header_minimal_logo %}
<div class="col-6 col-md-4 header-minimal-logo">
{% block layout_header_logo_inner %}
<div class="header-logo-main">
{% block layout_header_logo_link %}
<a class="header-logo-main-link"
id="vrpaymentHomeLink"
href="{{ path('frontend.home.page') }}"
title="{{ "header.logoLink"|trans|striptags }}">
{% block layout_header_logo_image %}
<picture class="header-logo-picture">
{% block layout_header_logo_image_tablet %}
{% if theme_config('sw-logo-tablet') and theme_config('sw-logo-tablet') != theme_config('sw-logo-desktop') %}
<source srcset="{{ theme_config('sw-logo-tablet') |sw_encode_url }}"
media="(min-width: {{ theme_config('breakpoint.md') }}px) and (max-width: {{ theme_config('breakpoint.lg') - 1 }}px)">
{% endif %}
{% endblock %}
{% block layout_header_logo_image_mobile %}
{% if theme_config('sw-logo-mobile') and theme_config('sw-logo-mobile') != theme_config('sw-logo-desktop') %}
<source srcset="{{ theme_config('sw-logo-mobile') |sw_encode_url }}"
media="(max-width: {{ theme_config('breakpoint.md') - 1 }}px)">
{% endif %}
{% endblock %}
{% block layout_header_logo_image_default %}
{% if theme_config('sw-logo-desktop') %}
<img src="{{ theme_config('sw-logo-desktop') |sw_encode_url }}"
alt="{{ "header.logoLink"|trans|striptags }}"
class="img-fluid header-logo-main-img"/>
{% endif %}
{% endblock %}
</picture>
{% endblock %}
</a>
{% endblock %}
</div>
{% endblock %}
</div>
{% endblock %}
{% block layout_header_minimal_button %}
<div class="col-md-4 header-minimal-back-to-shop">
<button type="button"
id="vrpaymentOrderCancel"
class="btn btn-outline-primary header-minimal-back-to-shop-button"
title="{{ "checkout.finishButtonBackToShop"|trans|striptags }}">
{{ "checkout.finishButtonBackToShop"|trans|striptags }}
</button>
</div>
{% endblock %}
+3 -7
View File
@@ -3,7 +3,6 @@
namespace VRPaymentPayment; namespace VRPaymentPayment;
use Shopware\Core\{ use Shopware\Core\{
Framework\Feature,
Framework\Plugin, Framework\Plugin,
Framework\Plugin\Context\ActivateContext, Framework\Plugin\Context\ActivateContext,
Framework\Plugin\Context\DeactivateContext, Framework\Plugin\Context\DeactivateContext,
@@ -22,7 +21,6 @@ use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Loader\DirectoryLoader; use Symfony\Component\DependencyInjection\Loader\DirectoryLoader;
use Symfony\Component\DependencyInjection\Loader\GlobFileLoader; use Symfony\Component\DependencyInjection\Loader\GlobFileLoader;
use Symfony\Component\DependencyInjection\Loader\YamlFileLoader; use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
// expect the vendor folder on Shopware store releases // expect the vendor folder on Shopware store releases
if (file_exists(dirname(__DIR__) . '/vendor/autoload.php')) { if (file_exists(dirname(__DIR__) . '/vendor/autoload.php')) {
@@ -84,21 +82,19 @@ class VRPaymentPayment extends Plugin {
{ {
parent::build($container); parent::build($container);
$confDir = \rtrim($this->getPath(), '/') . '/Resources/config'; $locator = new FileLocator('Resources/config');
$locator = new FileLocator($confDir);
$resolver = new LoaderResolver([ $resolver = new LoaderResolver([
new YamlFileLoader($container, $locator), new YamlFileLoader($container, $locator),
new XmlFileLoader($container, $locator),
new GlobFileLoader($container, $locator), new GlobFileLoader($container, $locator),
new DirectoryLoader($container, $locator), new DirectoryLoader($container, $locator),
]); ]);
$configLoader = new DelegatingLoader($resolver); $configLoader = new DelegatingLoader($resolver);
$configLoader->load($confDir . '/{packages}/*.yaml', 'glob'); $confDir = \rtrim($this->getPath(), '/') . '/Resources/config';
$configLoader->load('services/core/checkout.xml'); $configLoader->load($confDir . '/{packages}/*.yaml', 'glob');
} }
public function enrichPrivileges(): array public function enrichPrivileges(): array