@@ -965,18 +983,23 @@ La tokenizzazione non è disponibile per i checkout degli ospiti.
Tokenization
-
+
8.7
+ Pagamenti ricorrenti
+
+
+
+ 8.8
Key Features
- 8.8
+ 8.9
Risoluzione dei Problemi
- 8.9
+ 8.10
FAQs
diff --git a/docs/it/resource/api-key.png b/docs/it/resource/api-key.png
new file mode 100644
index 0000000..c74fe13
Binary files /dev/null and b/docs/it/resource/api-key.png differ
diff --git a/docs/it/resource/application-users.png b/docs/it/resource/application-users.png
new file mode 100644
index 0000000..51a97b9
Binary files /dev/null and b/docs/it/resource/application-users.png differ
diff --git a/docs/it/resource/assign-role.png b/docs/it/resource/assign-role.png
new file mode 100644
index 0000000..616a1dd
Binary files /dev/null and b/docs/it/resource/assign-role.png differ
diff --git a/docs/it/resource/bogus-processor.png b/docs/it/resource/bogus-processor.png
new file mode 100644
index 0000000..db79bb1
Binary files /dev/null and b/docs/it/resource/bogus-processor.png differ
diff --git a/docs/it/resource/capture-transaction.png b/docs/it/resource/capture-transaction.png
new file mode 100644
index 0000000..5fb2044
Binary files /dev/null and b/docs/it/resource/capture-transaction.png differ
diff --git a/docs/it/resource/cc-disable.png b/docs/it/resource/cc-disable.png
new file mode 100644
index 0000000..88048c8
Binary files /dev/null and b/docs/it/resource/cc-disable.png differ
diff --git a/docs/it/resource/cc-enable.png b/docs/it/resource/cc-enable.png
new file mode 100644
index 0000000..9b7e655
Binary files /dev/null and b/docs/it/resource/cc-enable.png differ
diff --git a/docs/it/resource/connectors.png b/docs/it/resource/connectors.png
new file mode 100644
index 0000000..056a892
Binary files /dev/null and b/docs/it/resource/connectors.png differ
diff --git a/docs/it/resource/loading-roles.png b/docs/it/resource/loading-roles.png
new file mode 100644
index 0000000..5d0d501
Binary files /dev/null and b/docs/it/resource/loading-roles.png differ
diff --git a/docs/it/resource/name-processor.png b/docs/it/resource/name-processor.png
new file mode 100644
index 0000000..d028107
Binary files /dev/null and b/docs/it/resource/name-processor.png differ
diff --git a/docs/it/resource/order-confirmation-email.png b/docs/it/resource/order-confirmation-email.png
new file mode 100644
index 0000000..b7e8d57
Binary files /dev/null and b/docs/it/resource/order-confirmation-email.png differ
diff --git a/docs/it/resource/payment-method-configuration.png b/docs/it/resource/payment-method-configuration.png
new file mode 100644
index 0000000..c84e6c0
Binary files /dev/null and b/docs/it/resource/payment-method-configuration.png differ
diff --git a/docs/it/resource/payment-methods.png b/docs/it/resource/payment-methods.png
new file mode 100644
index 0000000..4bbea90
Binary files /dev/null and b/docs/it/resource/payment-methods.png differ
diff --git a/docs/it/resource/payment-settings.png b/docs/it/resource/payment-settings.png
new file mode 100644
index 0000000..bd758a5
Binary files /dev/null and b/docs/it/resource/payment-settings.png differ
diff --git a/docs/it/resource/plugin-configuration.png b/docs/it/resource/plugin-configuration.png
new file mode 100644
index 0000000..1fa6bd4
Binary files /dev/null and b/docs/it/resource/plugin-configuration.png differ
diff --git a/docs/it/resource/plugin-installation.png b/docs/it/resource/plugin-installation.png
new file mode 100644
index 0000000..5b32274
Binary files /dev/null and b/docs/it/resource/plugin-installation.png differ
diff --git a/docs/it/resource/refund-transaction.png b/docs/it/resource/refund-transaction.png
new file mode 100644
index 0000000..a03d38c
Binary files /dev/null and b/docs/it/resource/refund-transaction.png differ
diff --git a/docs/it/resource/roles.png b/docs/it/resource/roles.png
new file mode 100644
index 0000000..6dd3548
Binary files /dev/null and b/docs/it/resource/roles.png differ
diff --git a/docs/it/resource/save-role.png b/docs/it/resource/save-role.png
new file mode 100644
index 0000000..b5d30cb
Binary files /dev/null and b/docs/it/resource/save-role.png differ
diff --git a/docs/it/resource/shopware_6_stage_graph_delivery.svg b/docs/it/resource/shopware_6_stage_graph_delivery.svg
new file mode 100644
index 0000000..03e5a1d
--- /dev/null
+++ b/docs/it/resource/shopware_6_stage_graph_delivery.svg
@@ -0,0 +1,3 @@
+
+
+
Hold Open Open
Transaction ful... Transaction c... Canceled Transaction decline / fail / void
Transaction de... 1
2
3
Viewer does not support full SVG 1.1
\ No newline at end of file
diff --git a/docs/it/resource/shopware_6_stage_graph_order.svg b/docs/it/resource/shopware_6_stage_graph_order.svg
new file mode 100644
index 0000000..89a9ea7
--- /dev/null
+++ b/docs/it/resource/shopware_6_stage_graph_order.svg
@@ -0,0 +1,3 @@
+
+
+
In Progress Paid Open
Transaction Invoice paid / not applicable
Transaction Inv... Transaction a... Transaction fai... Canceled Transaction decline / void
Transaction de... Failed 1
4
2
3
Viewer does not support full SVG 1.1
\ No newline at end of file
diff --git a/docs/it/resource/state_graph_order.svg b/docs/it/resource/state_graph_order.svg
new file mode 100644
index 0000000..46dbc35
--- /dev/null
+++ b/docs/it/resource/state_graph_order.svg
@@ -0,0 +1,2 @@
+
+
[Not supported by viewer] [Not supported by viewer] [Not supported by viewer] [Not supported by viewer] [Not supported by viewer] [Not supported by viewer] [Not supported by viewer] Depending on configuration
[Not supported by viewer] Transaction decline / void
[Not supported by viewer] [Not supported by viewer] [Not supported by viewer] [Not supported by viewer] [Not supported by viewer] [Not supported by viewer] [Not supported by viewer]
\ No newline at end of file
diff --git a/docs/it/resource/state_graph_payment_state.svg b/docs/it/resource/state_graph_payment_state.svg
new file mode 100644
index 0000000..d09a614
--- /dev/null
+++ b/docs/it/resource/state_graph_payment_state.svg
@@ -0,0 +1,3 @@
+
+
+
In Progress Paid Open
Transaction complete / fulfill
Transaction com... Transaction a... Transaction fai... Canceled Transaction decline / void
Transaction de... Failed 1
4
2
3
Viewer does not support full SVG 1.1
\ No newline at end of file
diff --git a/docs/it/resource/token.png b/docs/it/resource/token.png
new file mode 100644
index 0000000..15be72e
Binary files /dev/null and b/docs/it/resource/token.png differ
diff --git a/docs/it/resource/user.png b/docs/it/resource/user.png
new file mode 100644
index 0000000..5673a04
Binary files /dev/null and b/docs/it/resource/user.png differ
diff --git a/docs/it/resource/void-transaction.png b/docs/it/resource/void-transaction.png
new file mode 100644
index 0000000..8037d28
Binary files /dev/null and b/docs/it/resource/void-transaction.png differ
diff --git a/docs/it/resource/webhook-listeners.png b/docs/it/resource/webhook-listeners.png
new file mode 100644
index 0000000..7b7f2b4
Binary files /dev/null and b/docs/it/resource/webhook-listeners.png differ
diff --git a/docs/it/resource/webhooks.png b/docs/it/resource/webhooks.png
new file mode 100644
index 0000000..27a58e1
Binary files /dev/null and b/docs/it/resource/webhooks.png differ
diff --git a/src/Core/Api/PaymentMethodConfiguration/Service/PaymentMethodConfigurationService.php b/src/Core/Api/PaymentMethodConfiguration/Service/PaymentMethodConfigurationService.php
index 508f807..60ec19b 100644
--- a/src/Core/Api/PaymentMethodConfiguration/Service/PaymentMethodConfigurationService.php
+++ b/src/Core/Api/PaymentMethodConfiguration/Service/PaymentMethodConfigurationService.php
@@ -499,6 +499,7 @@ class PaymentMethodConfigurationService {
'afterOrderEnabled' => true,
'active' => true,
'translations' => $this->getPaymentMethodConfigurationTranslation($paymentMethodConfiguration, $context),
+ 'technicalName' => $paymentMethodConfiguration->getName(),
];
$data['mediaId'] = $this->upsertMedia($id, $paymentMethodConfiguration, $context);
diff --git a/src/Core/Api/Refund/Controller/RefundController.php b/src/Core/Api/Refund/Controller/RefundController.php
index 0133c85..b6b8661 100644
--- a/src/Core/Api/Refund/Controller/RefundController.php
+++ b/src/Core/Api/Refund/Controller/RefundController.php
@@ -115,7 +115,11 @@ class RefundController extends AbstractController
$apiClient = $settings->getApiClient();
$transaction = $apiClient->getTransactionService()->read($settings->getSpaceId(), $transactionId);
- $this->refundService->createRefundByAmount($transaction, $refundableAmount, $context);
+ $refund = $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);
}
diff --git a/src/Core/Api/Transaction/Service/TransactionService.php b/src/Core/Api/Transaction/Service/TransactionService.php
index 64f4754..f8ea0dd 100644
--- a/src/Core/Api/Transaction/Service/TransactionService.php
+++ b/src/Core/Api/Transaction/Service/TransactionService.php
@@ -8,7 +8,7 @@ use Shopware\Core\{
Checkout\Cart\CartException,
Checkout\Cart\LineItem\LineItem,
Checkout\Order\OrderEntity,
- Checkout\Payment\Cart\AsyncPaymentTransactionStruct,
+ Checkout\Payment\Cart\PaymentTransactionStruct,
Framework\Context,
Framework\DataAbstractionLayer\Search\Criteria,
Framework\DataAbstractionLayer\Search\Filter\EqualsFilter,
@@ -16,22 +16,23 @@ use Shopware\Core\{
};
use Shopware\Storefront\Page\Checkout\Confirm\CheckoutConfirmPageLoadedEvent;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
-use VRPayment\Sdk\{
- Model\AddressCreate,
- Model\ChargeAttempt,
- Model\CreationEntityState,
- Model\CriteriaOperator,
- Model\EntityQuery,
- Model\EntityQueryFilter,
- Model\EntityQueryFilterType,
- Model\Gender,
- Model\LineItemAttributeCreate,
- Model\LineItemCreate,
- Model\LineItemType,
- Model\Transaction,
- Model\TransactionCreate,
- Model\TransactionPending,
- Model\TransactionState,
+use VRPayment\Sdk\Model\{
+ AddressCreate,
+ ChargeAttempt,
+ CreationEntityState,
+ CriteriaOperator,
+ EntityQuery,
+ EntityQueryFilter,
+ EntityQueryFilterType,
+ Gender,
+ LineItemAttributeCreate,
+ LineItemCreate,
+ LineItemType,
+ TokenizationMode,
+ Transaction,
+ TransactionCreate,
+ TransactionPending,
+ TransactionState,
};
use VRPaymentPayment\Core\{
Api\OrderDeliveryState\Handler\OrderDeliveryStateHandler,
@@ -115,7 +116,7 @@ class TransactionService
*
* A redirect to the url will be performed
*
- * @param \Shopware\Core\Checkout\Payment\Cart\AsyncPaymentTransactionStruct $transaction
+ * @param \Shopware\Core\Checkout\Payment\Cart\PaymentTransactionStruct $transaction
* @param \Shopware\Core\System\SalesChannel\SalesChannelContext $salesChannelContext
*
* @return string
@@ -124,10 +125,14 @@ class TransactionService
* @throws \VRPayment\Sdk\VersioningException
*/
public function create(
- AsyncPaymentTransactionStruct $transaction,
- SalesChannelContext $salesChannelContext
+ PaymentTransactionStruct $transaction,
+ SalesChannelContext $salesChannelContext
): 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();
$settings = $this->settingsService->getSettings($salesChannelId);
$apiClient = $settings->getApiClient();
@@ -165,7 +170,7 @@ class TransactionService
$redirectUrl = $this->container->get('router')->generate(
'frontend.vrpayment.checkout.pay',
- ['orderId' => $transaction->getOrder()->getId(),],
+ ['orderId' => $orderTransaction->getOrder()->getId(),],
UrlGeneratorInterface::ABSOLUTE_URL
);
@@ -177,8 +182,8 @@ class TransactionService
$this->upsert(
$createdTransaction,
$salesChannelContext->getContext(),
- $transaction->getOrderTransaction()->getPaymentMethodId(),
- $transaction->getOrder()->getSalesChannelId()
+ $orderTransaction->getPaymentMethodId(),
+ $orderTransaction->getOrder()->getSalesChannelId()
);
$_SESSION['transactionId'] = null;
$_SESSION['arrayOfPossibleMethods'] = null;
@@ -186,26 +191,45 @@ class TransactionService
$_SESSION['currencyCheck'] = null;
- $this->holdDelivery($transaction->getOrder()->getId(), $salesChannelContext->getContext());
+ $this->holdDelivery($orderTransaction->getOrder()->getId(), $salesChannelContext->getContext());
return $redirectUrl;
}
/**
- * @param \Shopware\Core\Checkout\Payment\Cart\AsyncPaymentTransactionStruct $transaction
+ * Creates the transaction in the portal using the SDK.
+ *
+ * @return void
+ */
+ public function createRecurringTransaction(TransactionCreate $sdkTransactionCreate, string $spaceId = ""): Transaction {
+ $settings = $this->settingsService->getSettings();
+ if (empty($spaceId)) {
+ $spaceId = $settings->getSpaceId();
+ }
+
+ $sdkTransaction = $settings->getApiClient()->getTransactionService()->create($spaceId, $sdkTransactionCreate);
+ if ($sdkTransaction->valid()) {
+ return $settings->getApiClient()->getTransactionService()->processWithoutUserInteraction($spaceId, $sdkTransaction->getId());
+ }
+
+ throw new \Exception("The transacion is not valid and could not be created.");
+ }
+
+ /**
+ * @param \Shopware\Core\Checkout\Payment\Cart\PaymentTransactionStruct $transaction
* @param \Shopware\Core\Framework\Context $context
* @param int $vrpaymentTransactionId
* @param int $spaceId
*/
protected function addVRPaymentTransactionId(
- AsyncPaymentTransactionStruct $transaction,
+ PaymentTransactionStruct $transaction,
Context $context,
int $vrpaymentTransactionId,
int $spaceId
): void
{
$data = [
- 'id' => $transaction->getOrderTransaction()->getId(),
+ 'id' => $transaction->getOrderTransactionId(),
'customFields' => [
TransactionPayload::ORDER_TRANSACTION_CUSTOM_FIELDS_VRPAYMENT_TRANSACTION_ID => $vrpaymentTransactionId,
TransactionPayload::ORDER_TRANSACTION_CUSTOM_FIELDS_VRPAYMENT_SPACE_ID => $spaceId,
@@ -343,7 +367,7 @@ class TransactionService
*
* @return \Shopware\Core\Checkout\Order\OrderEntity
*/
- private function getOrderEntity(string $orderId, Context $context): OrderEntity
+ protected function getOrderEntity(string $orderId, Context $context): OrderEntity
{
try {
$criteria = (new Criteria([$orderId]))->addAssociations(['deliveries']);
@@ -387,7 +411,7 @@ class TransactionService
* @throws \VRPayment\Sdk\Http\ConnectionException
* @throws \VRPayment\Sdk\VersioningException
*/
- public function read(int $transactionId, string $salesChannelId): Transaction
+ public function read(int $transactionId, string $salesChannelId = ""): Transaction
{
$settings = $this->settingsService->getSettings($salesChannelId);
return $settings->getApiClient()->getTransactionService()->read($settings->getSpaceId(), $transactionId);
@@ -594,7 +618,8 @@ class TransactionService
->setCustomerEmailAddress($customer->getEmail())
->setCustomerId($customerId)
->setSuccessUrl($homeUrl . '?success')
- ->setFailedUrl($homeUrl . '?fail');
+ ->setFailedUrl($homeUrl . '?fail')
+ ->setTokenizationMode(TokenizationMode::FORCE_CREATION);
$transactionService = $settings->getApiClient()->getTransactionService();
$transaction = $transactionService->create($settings->getSpaceId(), $transactionPayload);
diff --git a/src/Core/Api/WebHooks/Strategy/WebHookTransactionStrategy.php b/src/Core/Api/WebHooks/Strategy/WebHookTransactionStrategy.php
index a100f45..f0b75f7 100644
--- a/src/Core/Api/WebHooks/Strategy/WebHookTransactionStrategy.php
+++ b/src/Core/Api/WebHooks/Strategy/WebHookTransactionStrategy.php
@@ -9,12 +9,13 @@ use Shopware\Core\{
Checkout\Cart\CartException,
Framework\Context,
System\StateMachine\Exception\IllegalTransitionException};
-use VRPayment\Sdk\{
- Model\RefundState,
- Model\Transaction,
- Model\TransactionInvoiceState,
- Model\TransactionState,
- Model\TransactionInvoice,};
+use VRPayment\Sdk\Model\{
+ RefundState,
+ Transaction,
+ TransactionInvoiceState,
+ TransactionState,
+ TransactionInvoice,
+ Token};
use VRPaymentPayment\Core\{
Api\WebHooks\Service\WebHooksService,
Api\WebHooks\Struct\WebHookRequest,
@@ -51,7 +52,7 @@ class WebHookTransactionStrategy extends WebHookStrategyBase implements WebhookS
* @throws \VRPayment\Sdk\Http\ConnectionException ConnectionException.
* @throws \VRPayment\Sdk\VersioningException VersioningException.
*/
- public function getTransaction(WebHookRequest $request) {
+ public function getTransaction(WebHookRequest $request): Transaction {
return $this->settings->getApiClient()
->getTransactionService()
->read($request->getSpaceId(), $request->getEntityId());
@@ -60,7 +61,7 @@ class WebHookTransactionStrategy extends WebHookStrategyBase implements WebhookS
/**
* @inheritDoc
*/
- public function getOrderIdByTransaction($transaction): string
+ public function getOrderIdByTransaction(Transaction $transaction): string
{
/** @var \VRPayment\Sdk\Model\Transaction $transaction */
return $transaction->getMetaData()[TransactionPayload::VRPAYMENT_METADATA_ORDER_ID];
@@ -96,7 +97,7 @@ class WebHookTransactionStrategy extends WebHookStrategyBase implements WebhookS
*/
public function process(WebHookRequest $request): Response
{
- return $this->updateTransaction($request, $this->getContext());
+ return $this->processTransaction($request, $this->getContext());
}
/**
@@ -107,16 +108,17 @@ class WebHookTransactionStrategy extends WebHookStrategyBase implements WebhookS
* @param Context $context The operational context providing settings and environment for transaction processing.
* @return Response Returns a JSON response indicating the result of the transaction update operation.
*/
- private function updateTransaction(WebHookRequest $request, Context $context): Response
+ private function processTransaction(WebHookRequest $request, Context $context): Response
{
$status = Response::HTTP_UNPROCESSABLE_ENTITY;
try {
/** @var \Shopware\Core\Checkout\Order\OrderEntity $order */
$transaction = $this->getTransaction($request);
- $orderId = $transaction->getMetaData()[TransactionPayload::VRPAYMENT_METADATA_ORDER_ID];
- if(!empty($orderId) && !$transaction->getParent()) {
- $this->executeLocked($orderId, $context, function () use ($orderId, $transaction, $context, $request) {
+ $token = $transaction->getToken();
+ $orderId = $transaction->getMetaData()[TransactionPayload::VRPAYMENT_METADATA_ORDER_ID];
+ if (!empty($orderId) && !$transaction->getParent()) {
+ $this->executeLocked($orderId, $context, function () use ($orderId, $transaction, $context, $request, $token) {
$this->transactionService->upsert($transaction, $context);
$orderTransactionId = $transaction->getMetaData()[TransactionPayload::VRPAYMENT_METADATA_ORDER_TRANSACTION_ID];
$orderTransaction = $this->getOrderTransaction($orderId, $context);
@@ -143,6 +145,16 @@ class WebHookTransactionStrategy extends WebHookStrategyBase implements WebhookS
$this->unholdDelivery($orderId, $context);
break;
case TransactionState::AUTHORIZED:
+ if ($token instanceof Token) {
+ // Update orderTransaction with the authorized token:
+ $data = [
+ 'id' => $orderTransactionId,
+ 'customFields' => [
+ TransactionPayload::ORDER_TRANSACTION_CUSTOM_FIELDS_VRPAYMENT_TOKEN => $token->getId(),
+ ],
+ ];
+ $this->container->get('order_transaction.repository')->update([$data], $context);
+ }
$this->orderTransactionStateHandler->process($orderTransactionId, $context);
$this->sendEmail($transaction, $context, $orderId);
break;
diff --git a/src/Core/Api/WebHooks/Strategy/WebhookStrategyActionsInterface.php b/src/Core/Api/WebHooks/Strategy/WebhookStrategyActionsInterface.php
index a2e25de..250db67 100644
--- a/src/Core/Api/WebHooks/Strategy/WebhookStrategyActionsInterface.php
+++ b/src/Core/Api/WebHooks/Strategy/WebhookStrategyActionsInterface.php
@@ -57,5 +57,5 @@ interface WebhookStrategyActionsInterface {
* @param Transaction|TransactionInvoiceState|Refund|mixed $transaction The transaction object from which the order ID should be extracted.
* @return string The order ID as a string.
*/
- public function getOrderIdByTransaction($transaction): string;
+ public function getOrderIdByTransaction(Transaction $transaction): string;
}
diff --git a/src/Core/Checkout/Cart/CustomCartPersister.php b/src/Core/Checkout/Cart/CustomCartPersister.php
new file mode 100644
index 0000000..584dd1e
--- /dev/null
+++ b/src/Core/Checkout/Cart/CustomCartPersister.php
@@ -0,0 +1,59 @@
+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;
+ }
+}
diff --git a/src/Core/Checkout/PaymentHandler/VRPaymentPaymentHandler.php b/src/Core/Checkout/PaymentHandler/VRPaymentPaymentHandler.php
index 669ad23..fd8c9bb 100644
--- a/src/Core/Checkout/PaymentHandler/VRPaymentPaymentHandler.php
+++ b/src/Core/Checkout/PaymentHandler/VRPaymentPaymentHandler.php
@@ -4,22 +4,42 @@ namespace VRPaymentPayment\Core\Checkout\PaymentHandler;
use Psr\Log\LoggerInterface;
use Shopware\Core\{
+ Checkout\Order\OrderEntity,
+ Checkout\Order\Aggregate\OrderTransaction\OrderTransactionEntity,
Checkout\Order\Aggregate\OrderTransaction\OrderTransactionStateHandler,
- Checkout\Payment\Cart\AsyncPaymentTransactionStruct,
- Checkout\Payment\Cart\PaymentHandler\AsynchronousPaymentHandlerInterface,
- Checkout\Payment\Exception\AsyncPaymentFinalizeException,
- Checkout\Payment\Exception\AsyncPaymentProcessException,
+ Checkout\Order\Aggregate\OrderTransaction\OrderTransactionStates,
+ Checkout\Payment\Cart\PaymentTransactionStruct,
+ Checkout\Payment\Cart\PaymentHandler\AbstractPaymentHandler,
+ Checkout\Payment\Cart\PaymentHandler\PaymentHandlerType,
Checkout\Payment\PaymentException,
Checkout\Payment\Exception\CustomerCanceledAsyncPaymentException,
+ Framework\App\AppException,
+ Framework\Api\Context\SalesChannelApiSource,
+ Framework\Context,
+ Framework\DataAbstractionLayer\EntityRepository,
+ Framework\DataAbstractionLayer\Search\Criteria,
+ Framework\DataAbstractionLayer\Search\Filter\EqualsFilter,
+ Framework\DataAbstractionLayer\Search\Sorting\FieldSorting,
+ Framework\Struct\Struct,
Framework\Validation\DataBag\RequestDataBag,
- System\SalesChannel\SalesChannelContext
+ System\StateMachine\Aggregation\StateMachineState\StateMachineStateEntity,
+ System\SalesChannel\Context\SalesChannelContextService,
+ 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\{
HttpFoundation\RedirectResponse,
HttpFoundation\Request
};
use VRPayment\Sdk\Model\TransactionState;
-use VRPaymentPayment\Core\Api\Transaction\Service\TransactionService;
+use VRPaymentPayment\Core\Api\Transaction\Service\TransactionService as PluginTransactionService;
+use VRPaymentPayment\Core\Util\Payload\TransactionPayload;
+
/**
@@ -27,13 +47,18 @@ use VRPaymentPayment\Core\Api\Transaction\Service\TransactionService;
*
* @package VRPaymentPayment\Core\Checkout\PaymentHandler
*/
-class VRPaymentPaymentHandler implements AsynchronousPaymentHandlerInterface
+class VRPaymentPaymentHandler extends AbstractPaymentHandler
{
/**
- * @var \VRPaymentPayment\Core\Api\Transaction\Service\TransactionService
+ * @var CustomCartPersister
*/
- protected $transactionService;
+ private CustomCartPersister $cartPersister;
+
+ /**
+ * @var \VRPaymentPayment\Core\Api\Transaction\Service\PluginTransactionService
+ */
+ protected $pluginTransactionService;
/**
* @var \Psr\Log\LoggerInterface
@@ -44,22 +69,32 @@ class VRPaymentPaymentHandler implements AsynchronousPaymentHandlerInterface
*/
private $orderTransactionStateHandler;
- /**
- * VRPaymentPaymentHandler constructor.
- *
- * @param \VRPaymentPayment\Core\Api\Transaction\Service\TransactionService $transactionService
- * @param \Shopware\Core\Checkout\Order\Aggregate\OrderTransaction\OrderTransactionStateHandler $orderTransactionStateHandler
- */
- public function __construct(TransactionService $transactionService, OrderTransactionStateHandler $orderTransactionStateHandler)
- {
- $this->transactionService = $transactionService;
- $this->orderTransactionStateHandler = $orderTransactionStateHandler;
- }
+ protected SalesChannelContextService $salesChannelContextService;
+
+ protected EntityRepository $orderTransactionRepository;
+
+ protected ?EntityRepository $subscriptionRepository;
+ /**
+ * VRPaymentPaymentHandler constructor.
+ */
+ public function __construct(
+ CustomCartPersister $cartPersister,
+ PluginTransactionService $pluginTransactionService,
+ OrderTransactionStateHandler $orderTransactionStateHandler,
+ SalesChannelContextService $salesChannelContextService,
+ EntityRepository $orderTransactionRepository,
+ ?EntityRepository $subscriptionRepository,
+ ) {
+ $this->cartPersister = $cartPersister;
+ $this->pluginTransactionService = $pluginTransactionService;
+ $this->orderTransactionStateHandler = $orderTransactionStateHandler;
+ $this->salesChannelContextService = $salesChannelContextService;
+ $this->orderTransactionRepository = $orderTransactionRepository;
+ $this->subscriptionRepository = $subscriptionRepository;
+ }
/**
* @param \Psr\Log\LoggerInterface $logger
- * @internal
- * @required
*
*/
public function setLogger(LoggerInterface $logger): void
@@ -73,78 +108,352 @@ class VRPaymentPaymentHandler implements AsynchronousPaymentHandlerInterface
*
* A redirect to the url will be performed
*
- * @param \Shopware\Core\Checkout\Payment\Cart\AsyncPaymentTransactionStruct $transaction
- * @param \Shopware\Core\Framework\Validation\DataBag\RequestDataBag $dataBag
- * @param \Shopware\Core\System\SalesChannel\SalesChannelContext $salesChannelContext
+ * @param \Symfony\Component\HttpFoundation\Request
+ * @param \Shopware\Core\Checkout\Payment\Cart\PaymentTransactionStruct $transaction
+ * @param \Shopware\Core\Framework\Context $context
+ * @param \Shopware\Core\Framework\Struct\Struct $validateStruct
* @return \Symfony\Component\HttpFoundation\RedirectResponse
*/
public function pay(
- AsyncPaymentTransactionStruct $transaction,
- RequestDataBag $dataBag,
- SalesChannelContext $salesChannelContext
+ Request $request,
+ PaymentTransactionStruct $transaction,
+ Context $context,
+ ?Struct $validateStruct
): RedirectResponse
{
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();
- if ($transaction->getOrder()->getAmountTotal() > 0) {
- $transactionId = $_SESSION['transactionId'] ?? null;
+
+ if ($orderTransaction->getOrder()->getAmountTotal() > 0) {
+ $transactionId = $request->getSession()->get('transactionId');
if ($transactionId === null) {
- $this->transactionService->createPendingTransaction($salesChannelContext);
+ $this->pluginTransactionService->createPendingTransaction($salesChannelContext);
}
- $redirectUrl = $this->transactionService->create($transaction, $salesChannelContext);
+ $redirectUrl = $this->pluginTransactionService->create($transaction, $salesChannelContext);
}
return new RedirectResponse($redirectUrl);
-
- } catch (\Exception $e) {
- unset($_SESSION['transactionId']);
+ } catch (\Throwable $e) {
+ $request->getSession()->remove('transactionId');
$errorMessage = 'An error occurred during the communication with external payment gateway : ' . $e->getMessage();
$this->logger->critical($errorMessage);
- throw new \Exception($transaction->getOrderTransaction()->getId() . ': ' . $errorMessage);
+ throw PaymentException::customerCanceled($transaction->getOrderTransaction()->getId(), $errorMessage);
}
}
/**
* 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 \Shopware\Core\System\SalesChannel\SalesChannelContext $salesChannelContext
+ * @param \Shopware\Core\Checkout\Payment\Cart\PaymentTransactionStruct $transaction
+ * @param \Shopware\Core\Framework\Context $context
* @throws \VRPayment\Sdk\ApiException
* @throws \VRPayment\Sdk\Http\ConnectionException
* @throws \VRPayment\Sdk\VersioningException
- * @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
- *
+ * @throws \Exception when the payment was canceled by the customer
*/
public function finalize(
- AsyncPaymentTransactionStruct $transaction,
- Request $request,
- SalesChannelContext $salesChannelContext
+ Request $request,
+ PaymentTransactionStruct $transaction,
+ Context $context
): void
{
- if ($transaction->getOrder()->getAmountTotal() > 0) {
- $transactionEntity = $this->transactionService->getByOrderId(
- $transaction->getOrder()->getId(),
- $salesChannelContext->getContext()
+ $orderTransactionId = $transaction->getOrderTransactionId();
+ $orderTransaction = $this->orderTransactionRepository->search(
+ (new Criteria([$orderTransactionId]))
+ ->addAssociation('order'), $context
+ )->getEntities()->first();
+
+ if ($orderTransaction->getOrder()->getAmountTotal() > 0) {
+ $transactionEntity = $this->pluginTransactionService->getByOrderId(
+ $orderTransaction->getOrder()->getId(),
+ $context
);
- $vRPaymentTransaction = $this->transactionService->read(
+ $vRPaymentTransaction = $this->pluginTransactionService->read(
$transactionEntity->getTransactionId(),
- $salesChannelContext->getSalesChannel()->getId()
+ $transactionEntity->getSalesChannelId()
);
if (in_array($vRPaymentTransaction->getState(), [TransactionState::FAILED])) {
$errorMessage = strtr('Customer canceled payment for :orderId on SalesChannel :salesChannelName', [
- ':orderId' => $transaction->getOrder()->getId(),
- ':salesChannelName' => $salesChannelContext->getSalesChannel()->getName(),
+ ':orderId' => $orderTransaction->getOrder()->getId(),
+ ':salesChannelName' => $transactionEntity->getSalesChannelId(),
]);
- unset($_SESSION['transactionId']);
+ $request->getSession()->remove('transactionId');
$this->logger->info($errorMessage);
throw PaymentException::customerCanceled($transaction->getOrderTransaction()->getId(), $errorMessage);
}
} else {
- $this->orderTransactionStateHandler->paid($transaction->getOrderTransaction()->getId(), $salesChannelContext->getContext());
+ $this->orderTransactionStateHandler->paid($orderTransaction->getId(), $context);
+ }
+
+ $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 {
+ // Both PaymentHandlerType::RECURRING and PaymentHandlerType::REFUND are supported
+ //TODO: check that the payment method really supports recurring.
+ // In order to do that, we need to get this information in when synching the payment methods.
+ // The payment methods in the portal are managed by their Connectors. The Connectors need
+ // to support the recurrin and the refunding. These values are 1453357059666L and 1453351315899L for
+ // tokenization and refunding respectively.
+ return true;
+ }
+
+ public function recurring(
+ PaymentTransactionStruct $transaction,
+ Context $context
+ ): void {
+ if ($this->subscriptionRepository === null || !class_exists(\Shopware\Commercial\Subscription\Entity\Subscription\SubscriptionEntity::class)) {
+ throw PaymentException::paymentTypeUnsupported(
+ $transaction->getOrderTransactionId(),
+ 'Shopware Commercial plugin with Subscription feature is not installed or active. Recurring payments cannot be processed.'
+ );
+ }
+
+ if ($transaction->isRecurring() === false) {
+ //TODO: Provide payment-method-id instead of order-transaction-id
+ throw PaymentException::paymentTypeUnsupported($transaction->getOrderTransaction()->getId(), PaymentHandlerType::RECURRING);
+ }
+
+ $recurringData = $transaction->getRecurring();
+ $newTransactionId = $transaction->getOrderTransactionId();
+
+ if ($recurringData === null) {
+ throw PaymentException::recurringInterrupted($newTransactionId, 'Recurring payment data is missing from the transaction struct.');
+ }
+
+ try {
+ // Get information about the subscription
+ $subscriptionId = $recurringData->getSubscriptionId();
+ $criteria = new Criteria([$subscriptionId]);
+ $criteria->addAssociation('orders.transactions.stateMachineState');
+
+ /** @var SubscriptionEntity|null $subscription */
+ $subscription = $this->subscriptionRepository->search($criteria, $context)->get($subscriptionId);
+
+ if ($subscription === null) {
+ throw PaymentException::recurringInterrupted($newTransactionId, sprintf('Subscription with ID "%s" could not be found.', $subscriptionId));
+ }
+
+ // Find the original order and transaction
+ $orders = $subscription->getOrders();
+ if ($orders === null || $orders->count() === 0) {
+ throw PaymentException::recurringInterrupted($newTransactionId, 'No orders found associated with the subscription.');
+ }
+
+ $orders->sort(fn (OrderEntity $a, OrderEntity $b) => $a->getCreatedAt() <=> $b->getCreatedAt());
+ /** @var OrderEntity|null $originalOrder */
+ $originalOrder = $orders->first();
+
+ $originalTransactions = $originalOrder->getTransactions();
+
+ if ($originalTransactions === null) {
+ throw PaymentException::recurringInterrupted($newTransactionId, 'No transactions found on the original order.');
+ }
+
+ /** @var OrderTransactionEntity|null $originalTransaction */
+ $originalTransaction = $originalTransactions->filter(
+ fn (OrderTransactionEntity $t) => $t->getStateMachineState()?->getTechnicalName() === OrderTransactionStates::STATE_PAID
+ )->first();
+
+ if ($originalTransaction === null) {
+ throw PaymentException::recurringInterrupted($newTransactionId, 'A successful, paid transaction could not be found on the original order to retrieve payment details.');
+ }
+
+ $newOrderTransaction = $this->orderTransactionRepository->search(
+ (new Criteria([$newTransactionId]))
+ ->addAssociation('order'), $context
+ )->getEntities()->first();
+ $orderNumber = $newOrderTransaction->getOrder()->getOrderNumber();
+
+ // Access the custom fields for getting the original transaction details
+ $customFields = $originalTransaction->getCustomFields();
+ // The tokenReference is not really needed because it's also stored in the original transaction
+ $tokenReference = $customFields[TransactionPayload::ORDER_TRANSACTION_CUSTOM_FIELDS_VRPAYMENT_TOKEN] ?? null;
+ $spaceId = (string) $customFields[TransactionPayload::ORDER_TRANSACTION_CUSTOM_FIELDS_VRPAYMENT_SPACE_ID] ?? null;
+ $sdkTransactionId = $customFields[TransactionPayload::ORDER_TRANSACTION_CUSTOM_FIELDS_VRPAYMENT_TRANSACTION_ID] ?? null;
+
+ if ($sdkTransactionId === null || $spaceId === null) {
+ throw PaymentException::recurringInterrupted($newTransactionId, 'Required original transaction ID and spaceId is missing from order transaction custom fields.');
+ }
+
+ /** @var \VRPayment\Sdk\Model\Transaction $originalSdkTransaction */
+ $originalSdkTransaction = $this->pluginTransactionService->read($sdkTransactionId, "");
+
+ //TODO: Consider moving this logic to its own function for improved readability
+ $sdkTransactionCreate = new \VRPayment\Sdk\Model\TransactionCreate;
+
+ // Build the new transaction based on the original transaction
+ $sdkTransactionCreate->setCurrency($originalSdkTransaction->getCurrency());
+ $sdkTransactionCreate->setBillingAddress($this->addressCreateFromSdk($originalSdkTransaction->getBillingAddress()));
+ $sdkTransactionCreate->setShippingAddress($this->addressCreateFromSdk($originalSdkTransaction->getShippingAddress()));
+ $sdkTransactionCreate->setShippingMethod($originalSdkTransaction->getShippingMethod());
+ $sdkTransactionCreate->setCustomerEmailAddress($originalSdkTransaction->getCustomerEmailAddress());
+ $sdkTransactionCreate->setCustomerId($originalSdkTransaction->getCustomerId());
+ $sdkTransactionCreate->setLanguage($originalSdkTransaction->getLanguage());
+ // Get the merchant reference from the new Order, not the original one
+ $sdkTransactionCreate->setMerchantReference($orderNumber);
+ $sdkTransactionCreate->setInvoiceMerchantReference($originalSdkTransaction->getInvoiceMerchantReference());
+
+ $lineItems = $originalSdkTransaction->getLineItems();
+ $lineItemsCreate = [];
+ foreach ($lineItems as $lineItem) {
+ $lineItemsCreate[] = $this->lineItemCreateFromSdk($lineItem);
+ }
+ if (count($lineItemsCreate) > 0) {
+ $sdkTransactionCreate->setLineItems($lineItemsCreate);
+ }
+
+ $sdkTransactionCreate->setSuccessUrl($originalSdkTransaction->getSuccessUrl());
+ $sdkTransactionCreate->setToken($originalSdkTransaction->getToken());
+ $sdkTransactionCreate->setTokenizationMode($originalSdkTransaction->getTokenizationMode());
+ $sdkTransactionCreate->setMetaData($originalSdkTransaction->getMetaData());
+
+ // Create the new recurring transaction
+ $newSdkTransaction = $this->pluginTransactionService->createRecurringTransaction($sdkTransactionCreate, $spaceId);
+
+ // Set the new state for the new order transaction
+ if (in_array($newSdkTransaction->getState(), [TransactionState::AUTHORIZED, TransactionState::COMPLETED, TransactionState::CONFIRMED, TransactionState::FULFILL])) {
+ $this->orderTransactionStateHandler->paid($newTransactionId, $context);
+ } elseif (in_array($newSdkTransaction->getState(), [TransactionState::DECLINE, TransactionState::FAILED, TransactionState::VOIDED])) {
+ $this->orderTransactionStateHandler->fail($newTransactionId, $context);
+ } elseif (in_array($newSdkTransaction->getState(), [TransactionState::PENDING, TransactionState::PROCESSING])) {
+ $this->orderTransactionStateHandler->process($newTransactionId, $context);
+ } else {
+ $this->orderTransactionStateHandler->reopen($newTransactionId, $context);
+ }
+
+ $data = [
+ 'id' => $newTransactionId,
+ 'customFields' => [
+ TransactionPayload::ORDER_TRANSACTION_CUSTOM_FIELDS_VRPAYMENT_TRANSACTION_ID => $newSdkTransaction->getId(),
+ TransactionPayload::ORDER_TRANSACTION_CUSTOM_FIELDS_VRPAYMENT_SPACE_ID => $spaceId,
+ TransactionPayload::ORDER_TRANSACTION_CUSTOM_FIELDS_VRPAYMENT_TOKEN => $tokenReference,
+ ],
+ ];
+
+ // Update the new order transaction with the new transaction details
+ $this->orderTransactionRepository->update([$data], $context);
+ $this->pluginTransactionService->upsert($newSdkTransaction, $context);
+ }
+ catch (\Throwable $e) {
+ $errorMessage = 'An error occurred during the communication with external payment gateway : ' . $e->getMessage();
+ $this->logger->critical($errorMessage);
+ throw PaymentException::recurringInterrupted($transaction->getOrderTransactionId(), $errorMessage);
+ }
+ }
+
+ /**
+ * Creates a new AddressCreate instance from the given SDK Address model.
+ *
+ * @param \VRPayment\Sdk\Model\Address $address The address model from the SDK.
+ * @return \VRPayment\Sdk\Model\AddressCreate The newly created AddressCreate instance.
+ */
+ private function addressCreateFromSdk(\VRPayment\Sdk\Model\Address $address): \VRPayment\Sdk\Model\AddressCreate {
+ $addressCreate = new \VRPayment\Sdk\Model\AddressCreate;
+
+ $addressCreate->setCity($address->getCity());
+ $addressCreate->setCommercialRegisterNumber($address->getCommercialRegisterNumber());
+ $addressCreate->setCountry($address->getCountry());
+ $addressCreate->setDateOfBirth($address->getDateOfBirth());
+ $addressCreate->setDependentLocality($address->getDependentLocality());
+ $addressCreate->setEmailAddress($address->getEmailAddress());
+ $addressCreate->setFamilyName($address->getFamilyName());
+ $addressCreate->setGender($address->getGender());
+ $addressCreate->setGivenName($address->getGivenName());
+ $addressCreate->setMobilePhoneNumber($address->getMobilePhoneNumber());
+ $addressCreate->setOrganizationName($address->getOrganizationName());
+ $addressCreate->setPhoneNumber($address->getPhoneNumber());
+ $addressCreate->setPostalState($address->getPostalState());
+ $addressCreate->setPostcode($address->getPostcode());
+ $addressCreate->setSalesTaxNumber($address->getSalesTaxNumber());
+ $addressCreate->setSalutation($address->getSalutation());
+ $addressCreate->setSocialSecurityNumber($address->getSocialSecurityNumber());
+ $addressCreate->setSortingCode($address->getSortingCode());
+ $addressCreate->setStreet($address->getStreet());
+
+ return $addressCreate;
+ }
+
+ /**
+ * Creates a LineItemCreate object from a given SDK LineItem.
+ *
+ * This method takes a \VRPayment\Sdk\Model\LineItem instance and transforms it into a
+ * \VRPayment\Sdk\Model\LineItemCreate object, which can be used for further processing
+ * or integration with the VRPayment payment SDK.
+ *
+ * @param \VRPayment\Sdk\Model\LineItem $lineItem The line item from the SDK to convert.
+ * @return \VRPayment\Sdk\Model\LineItemCreate The created LineItemCreate object.
+ */
+ private function lineItemCreateFromSdk(\VRPayment\Sdk\Model\LineItem $lineItem): \VRPayment\Sdk\Model\LineItemCreate
+ {
+ $lineItemCreate = new \VRPayment\Sdk\Model\LineItemCreate();
+
+ $lineItemCreate->setAmountIncludingTax($lineItem->getAmountIncludingTax());
+
+ $attributes = $lineItem->getAttributes();
+ $attributesCreate = [];
+ foreach ($attributes as $id => $attribute) {
+ $attributeCreate = new \VRPayment\Sdk\Model\LineItemAttributeCreate();
+ $attributeCreate->setLabel($attribute->getLabel());
+ $attributeCreate->setValue($attribute->getValue());
+ $attributesCreate[$id] = $attributeCreate;
+ }
+ if (count($attributesCreate) > 0) {
+ $lineItemCreate->setAttributes($attributesCreate);
+ }
+
+ $lineItemCreate->setDiscountIncludingTax($lineItem->getDiscountIncludingTax());
+ $lineItemCreate->setName($lineItem->getName());
+ $lineItemCreate->setQuantity($lineItem->getQuantity());
+ $lineItemCreate->setShippingRequired($lineItem->getShippingRequired());
+ $lineItemCreate->setSku($lineItem->getSku());
+
+ $taxes = $lineItem->getTaxes();
+ $taxesCreate = [];
+ foreach ($taxes as $tax) {
+ $taxCreate = new \VRPayment\Sdk\Model\TaxCreate();
+ $taxCreate->setRate($tax->getRate());
+ $taxCreate->setTitle($tax->getTitle());
+ $taxesCreate[] = $taxCreate;
+ }
+ if (count($taxesCreate) > 0) {
+ $lineItemCreate->setTaxes($taxesCreate);
+ }
+
+ $lineItemCreate->setType($lineItem->getType());
+ $lineItemCreate->setUniqueId($lineItem->getUniqueId());
+
+ return $lineItemCreate;
+ }
}
diff --git a/src/Core/Checkout/Subscription/Command/GenerateSubscriptionOrderCommand.php b/src/Core/Checkout/Subscription/Command/GenerateSubscriptionOrderCommand.php
new file mode 100644
index 0000000..8719a5b
--- /dev/null
+++ b/src/Core/Checkout/Subscription/Command/GenerateSubscriptionOrderCommand.php
@@ -0,0 +1,116 @@
+addArgument('subscriptionIdentifier', InputArgument::REQUIRED, 'The ID or Number of the subscription to process.');
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output): int
+ {
+ $io = new ShopwareStyle($input, $output);
+
+ if ($this->subscriptionRepository === null || !class_exists(\Shopware\Commercial\Subscription\Entity\Subscription\SubscriptionEntity::class)) {
+ $io->error('Subscription functionality is not available in this Shopware instance. Please ensure the Subscription plugin is installed and enabled.');
+ return self::FAILURE;
+ }
+
+ $identifier = $input->getArgument('subscriptionIdentifier');
+
+ if (!is_string($identifier)) {
+ $io->error('Invalid Subscription ID provided.');
+ return self::FAILURE;
+ }
+ $subscriptionId = $this->findSubscriptionId($identifier, $io);
+
+ if ($subscriptionId === null) {
+ // Error message is already printed in findSubscriptionId
+ return self::FAILURE;
+ }
+
+ $io->text(sprintf('Forcing next schedule for subscription ID: %s', $subscriptionId));
+ $this->forceNextSchedule($subscriptionId);
+
+ $io->title('Subscription Order Generation');
+ $io->text(sprintf('Dispatching GenerateSubscriptionOrder message for subscription ID: %s', $subscriptionId));
+
+ $this->bus->dispatch(new GenerateSubscriptionOrder($subscriptionId));
+
+ $io->success('Message dispatched successfully!');
+ $io->note('Ensure a message consumer is running to process the queue: "bin/console messenger:consume async"');
+
+ return self::SUCCESS;
+ }
+
+ /**
+ * Set the next schedule date to the current time, so it will be processed immediately.
+ *
+ * @param string $subscriptionId
+ * @return void
+ */
+ private function forceNextSchedule(string $subscriptionId): void
+ {
+ $context = Context::createDefaultContext();
+ $this->subscriptionRepository->update([
+ [
+ 'id' => $subscriptionId,
+ 'nextSchedule' => (new \DateTime())->format(Defaults::STORAGE_DATE_TIME_FORMAT),
+ ]
+ ], $context);
+ }
+
+ private function findSubscriptionId(string $identifier, ShopwareStyle $io): ?string
+ {
+ $context = Context::createDefaultContext();
+ if (Uuid::isValid($identifier)) {
+ // Check if a subscription with this ID actually exists
+ $result = $this->subscriptionRepository->searchIds(new Criteria([$identifier]), $context);
+ if ($result->firstId()) {
+ return $identifier;
+ }
+ $io->error(sprintf('No subscription found with ID "%s".', $identifier));
+ return null;
+ }
+
+ // If not a UUID, assume it's a subscription number
+ $criteria = new Criteria();
+ $criteria->addFilter(new EqualsFilter('subscriptionNumber', $identifier));
+ $result = $this->subscriptionRepository->searchIds($criteria, $context);
+
+ if ($result->firstId() === null) {
+ $io->error(sprintf('No subscription found with number "%s".', $identifier));
+ return null;
+ }
+
+ return $result->firstId();
+ }
+}
diff --git a/src/Core/Storefront/Checkout/Subscriber/CheckoutSubscriber.php b/src/Core/Storefront/Checkout/Subscriber/CheckoutSubscriber.php
index 9baf214..9109457 100644
--- a/src/Core/Storefront/Checkout/Subscriber/CheckoutSubscriber.php
+++ b/src/Core/Storefront/Checkout/Subscriber/CheckoutSubscriber.php
@@ -100,6 +100,7 @@ class CheckoutSubscriber implements EventSubscriberInterface
{
return [
CheckoutConfirmPageLoadedEvent::class => ['onConfirmPageLoaded', 1],
+ "subscription." . CheckoutConfirmPageLoadedEvent::class => ['onConfirmPageLoaded', 1],
MailBeforeValidateEvent::class => ['onMailBeforeValidate', 1],
];
}
diff --git a/src/Core/Util/Analytics/Analytics.php b/src/Core/Util/Analytics/Analytics.php
index cf2fd15..2a02274 100644
--- a/src/Core/Util/Analytics/Analytics.php
+++ b/src/Core/Util/Analytics/Analytics.php
@@ -25,7 +25,7 @@ class Analytics {
self::SHOP_SYSTEM => 'shopware',
self::SHOP_SYSTEM_VERSION => '6',
self::SHOP_SYSTEM_AND_VERSION => 'shopware-6',
- self::PLUGIN_SYSTEM_VERSION => '6.1.15',
+ self::PLUGIN_SYSTEM_VERSION => '7.1.0',
];
}
diff --git a/src/Core/Util/Payload/TransactionPayload.php b/src/Core/Util/Payload/TransactionPayload.php
index 8f9bc59..30368e8 100644
--- a/src/Core/Util/Payload/TransactionPayload.php
+++ b/src/Core/Util/Payload/TransactionPayload.php
@@ -8,10 +8,14 @@ use Shopware\Core\{Checkout\Cart\Tax\Struct\CalculatedTaxCollection,
Checkout\Customer\Aggregate\CustomerAddress\CustomerAddressEntity,
Checkout\Customer\CustomerEntity,
Checkout\Order\Aggregate\OrderLineItem\OrderLineItemEntity,
- Checkout\Payment\Cart\AsyncPaymentTransactionStruct,
+ Checkout\Order\OrderEntity,
+ Checkout\Payment\Cart\PaymentTransactionStruct,
Framework\DataAbstractionLayer\Search\Criteria,
System\SalesChannel\SalesChannelContext
};
+use Shopware\Core\Framework\DataAbstractionLayer\EntityRepository;
+use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter;
+
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
use VRPayment\Sdk\{Model\AddressCreate,
@@ -36,6 +40,10 @@ use VRPaymentPayment\Core\{Api\PaymentMethodConfiguration\Entity\PaymentMethodCo
Util\Payload\CustomProducts\CustomProductsLineItemTypes
};
+use Shopware\Core\System\SystemConfig\SystemConfigService;
+use Shopware\Core\Framework\Context;
+use Shopware\Core\System\Tax\TaxEntity;
+
/**
* Class TransactionPayload
*
@@ -48,6 +56,7 @@ class TransactionPayload extends AbstractPayload
public const ORDER_TRANSACTION_CUSTOM_FIELDS_VRPAYMENT_SPACE_ID = 'vrpayment_space_id';
public const ORDER_TRANSACTION_CUSTOM_FIELDS_VRPAYMENT_TRANSACTION_ID = 'vrpayment_transaction_id';
+ public const ORDER_TRANSACTION_CUSTOM_FIELDS_VRPAYMENT_TOKEN = 'vrpayment_token';
public const VRPAYMENT_METADATA_SALES_CHANNEL_ID = 'salesChannelId';
public const VRPAYMENT_METADATA_ORDER_ID = 'orderId';
@@ -61,7 +70,7 @@ class TransactionPayload extends AbstractPayload
protected $salesChannelContext;
/**
- * @var \Shopware\Core\Checkout\Payment\Cart\AsyncPaymentTransactionStruct
+ * @var \Shopware\Core\Checkout\Payment\Cart\PaymentTransactionStruct
*/
protected $transaction;
@@ -85,6 +94,10 @@ class TransactionPayload extends AbstractPayload
*/
protected $translator;
+ protected EntityRepository $orderTransactionRepository;
+
+ protected OrderEntity $order;
+
/**
* TransactionPayload constructor.
*
@@ -92,14 +105,14 @@ class TransactionPayload extends AbstractPayload
* @param \VRPaymentPayment\Core\Util\LocaleCodeProvider $localeCodeProvider
* @param \Shopware\Core\System\SalesChannel\SalesChannelContext $salesChannelContext
* @param \VRPaymentPayment\Core\Settings\Struct\Settings $settings
- * @param \Shopware\Core\Checkout\Payment\Cart\AsyncPaymentTransactionStruct $transaction
+ * @param \Shopware\Core\Checkout\Payment\Cart\PaymentTransactionStruct $transaction
*/
public function __construct(
ContainerInterface $container,
LocaleCodeProvider $localeCodeProvider,
SalesChannelContext $salesChannelContext,
Settings $settings,
- AsyncPaymentTransactionStruct $transaction
+ PaymentTransactionStruct $transaction
)
{
$this->localeCodeProvider = $localeCodeProvider;
@@ -108,6 +121,23 @@ class TransactionPayload extends AbstractPayload
$this->transaction = $transaction;
$this->container = $container;
$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();
}
/**
@@ -118,13 +148,21 @@ class TransactionPayload extends AbstractPayload
*/
public function get(int $version): TransactionPending
{
- $customer = $this->salesChannelContext->getCustomer();
+ $customerId = $this->order->getOrderCustomer()->getCustomerId();
+ $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();
+
$billingAddress = $this->getAddressPayload($customer, $customer->getActiveBillingAddress());
$shippingAddress = $this->getAddressPayload($customer, $customer->getActiveShippingAddress(), false);
-
$customerId = null;
$customerName = null;
if ($customer->getGuest() === false) {
@@ -137,14 +175,14 @@ class TransactionPayload extends AbstractPayload
}
$transactionData = [
- 'currency' => $this->salesChannelContext->getCurrency()->getIsoCode(),
- 'customer_email_address' => $billingAddress->getEmailAddress(),
+ 'currency' => $this->order->getCurrency()->getIsoCode(),
+ 'customer_email_address' => $customer->getEmail(),
'customer_id' => $customerId,
'language' => $this->localeCodeProvider->getLocaleCodeFromContext($this->salesChannelContext->getContext()) ?? null,
- 'merchant_reference' => $this->fixLength($this->transaction->getOrder()->getOrderNumber(), 100),
+ 'merchant_reference' => $this->fixLength($this->order->getOrderNumber(), 100),
'meta_data' => [
- self::VRPAYMENT_METADATA_ORDER_ID => $this->transaction->getOrder()->getId(),
- self::VRPAYMENT_METADATA_ORDER_TRANSACTION_ID => $this->transaction->getOrderTransaction()->getId(),
+ self::VRPAYMENT_METADATA_ORDER_ID => $this->order->getId(),
+ self::VRPAYMENT_METADATA_ORDER_TRANSACTION_ID => $this->order->getTransactions()->first()->getId(),
self::VRPAYMENT_METADATA_SALES_CHANNEL_ID => $this->salesChannelContext->getSalesChannel()->getId(),
self::VRPAYMENT_METADATA_CUSTOMER_NAME => $customerName,
],
@@ -161,8 +199,8 @@ class TransactionPayload extends AbstractPayload
$transactionData['meta_data']['additionalAddress2'] = $additionalAddress2;
}
- if (!empty($this->transaction->getOrder()->getCustomerComment())) {
- $transactionData['meta_data']['customer_comment'] = $this->transaction->getOrder()->getCustomerComment();
+ if (!empty($this->order->getCustomerComment())) {
+ $transactionData['meta_data']['customer_comment'] = $this->order->getCustomerComment();
}
$vatIds = $customer->getVatIds();
@@ -198,7 +236,7 @@ class TransactionPayload extends AbstractPayload
$transactionPayload->setAllowedPaymentMethodConfigurations([$paymentConfiguration->getPaymentMethodConfigurationId()]);
$successUrl = $this->transaction->getReturnUrl() . '&status=paid';
- $failedUrl = $this->getFailUrl($this->transaction->getOrder()->getId()) . '&status=fail';
+ $failedUrl = $this->getFailUrl($this->order->getId()) . '&status=fail';
$transactionPayload->setSuccessUrl($successUrl)
->setFailedUrl($failedUrl);
@@ -219,7 +257,7 @@ class TransactionPayload extends AbstractPayload
protected function getLineItems(): array
{
$lineItems = [];
- $items = $this->transaction->getOrder()->getLineItems();
+ $items = $this->order->getLineItems() ?? [];
foreach ($items as $shopLineItem) {
if ($this->shouldSkipLineItem($shopLineItem)) {
@@ -307,36 +345,99 @@ class TransactionPayload extends AbstractPayload
protected function addDiscountLineItem($discount, array &$lineItems): void
{
$calculatedPrice = $discount->getPrice();
- $calculatedTaxesCollection = $calculatedPrice->getCalculatedTaxes();
+ $discountName = $discount->getLabel() ?? 'Unnamed';
+ $definition = $discount->getPriceDefinition();
- foreach ($calculatedTaxesCollection as $calculatedTax) {
- $rate = $calculatedTax->getTaxRate();
- $lineItem = new LineItemCreate();
- $amount = $this->calculateDiscountAmount($calculatedTax);
+ if ($this->order->getTaxStatus() === 'net' || $definition instanceof \Shopware\Core\Checkout\Cart\Price\Struct\AbsolutePriceDefinition) {
+ $calculatedTaxesCollection = $calculatedPrice->getCalculatedTaxes();
+ foreach ($calculatedTaxesCollection as $calculatedTax) {
+ $rate = $calculatedTax->getTaxRate();
+ $amount = $this->calculateDiscountAmount($calculatedTax);
- $discountName = $discount->getLabel();
- $lineItem->setAmountIncludingTax($amount)
- ->setName(sprintf('DISCOUNT: %s (%s%% tax)', $discount->getLabel(), $rate))
- ->setQuantity(1)
- ->setShippingRequired(false)
- ->setSku('sku-discount-' . $rate . '-' . $discountName, 200)
- ->setType(LineItemType::DISCOUNT)
- ->setUniqueId('coupon-sku-discount-' . $rate . '-' . $rate . '-' . $discountName);
+ $lineItems[] = $this->createDiscountLineItem($discountName, $amount, $rate);
+ }
+ } else {
+ $taxRules = $calculatedPrice->getTaxRules();
- $taxRate = new TaxCreate(['title' => 'Discount Tax: ' . $rate, 'rate' => $rate]);
- $lineItem->setTaxes([$taxRate]);
-
- $lineItems[] = $lineItem;
+ 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)
+ ->setName($discountTitle)
+ ->setQuantity(1)
+ ->setShippingRequired(false)
+ ->setSku($discountSkuName, 200)
+ ->setType(LineItemType::DISCOUNT)
+ ->setUniqueId('coupon-' . $discountSkuName);
+
+ $taxRate = new TaxCreate([
+ 'title' => 'Discount Tax: ' . $rate,
+ 'rate' => $rate,
+ ]);
+
+ if ($this->order->getTaxStatus() !== 'tax-free') {
+ $lineItem->setTaxes([$taxRate]);
+ }
+
+ 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;
+ }
+
/**
* Calculate discount amount including tax if necessary.
*/
protected function calculateDiscountAmount($calculatedTax): float
{
$amount = self::round($calculatedTax->getPrice());
- if ($this->transaction->getOrder()->getTaxStatus() === 'net') {
+ if ($this->order->getTaxStatus() === 'net') {
$amount = self::round($amount + $calculatedTax->getTax());
}
return $amount;
@@ -357,9 +458,7 @@ class TransactionPayload extends AbstractPayload
*/
protected function addOptionalLineItems(array &$lineItems): void
{
- $shippingCosts = $this->transaction->getOrder()->getShippingCosts();
-
- if ($shippingCosts && $this->transaction->getOrder()->getShippingTotal() > 0) {
+ if ($this->order->getShippingCosts() && $this->order->getShippingTotal() > 0) {
if ($shippingLineItem = $this->getShippingLineItem()) {
$lineItems[] = $shippingLineItem;
}
@@ -381,7 +480,7 @@ class TransactionPayload extends AbstractPayload
protected function getCustomProductOptionLabel(string $lineItemParentId): string
{
$label = '';
- foreach ($this->transaction->getOrder()->getLineItems() as $shopLineItem) {
+ foreach ($this->order->getLineItems() as $shopLineItem) {
if ($shopLineItem->getParentId() === $lineItemParentId && $shopLineItem->getType() === CustomProductsLineItemTypes::LINE_ITEM_TYPE_PRODUCT) {
$label = $shopLineItem->getLabel();
break;
@@ -406,10 +505,11 @@ class TransactionPayload extends AbstractPayload
$sku = $payLoad['productNumber'];
}
$sku = $this->fixLength($sku, 200);
+
$amount = $shopLineItem->getTotalPrice() ? self::round($shopLineItem->getTotalPrice()) : 0;
//include Tax Excluded for Net Tax display customer group
- if ($this->transaction->getOrder()->getTaxStatus() === 'net') {
+ if ($this->order->getTaxStatus() === 'net') {
$amount = self::round($amount + $shopLineItem->getPrice()->getCalculatedTaxes()->getAmount());
}
@@ -445,7 +545,9 @@ class TransactionPayload extends AbstractPayload
}
if (!empty($taxes)) {
- $lineItem->setTaxes($taxes);
+ if ($this->order->getTaxStatus() !== 'tax-free') {
+ $lineItem->setTaxes($taxes);
+ }
}
if ($shopLineItem->getTotalPrice() >= 0) {
@@ -521,31 +623,34 @@ class TransactionPayload extends AbstractPayload
{
try {
- $amount = $this->transaction->getOrder()->getShippingTotal();
+ $amount = $this->order->getShippingTotal();
$amount = self::round($amount);
if ($amount > 0) {
$shippingName = $this->salesChannelContext->getShippingMethod()->getName() ?? $this->translator->trans('vrpayment.payload.shipping.name');
$taxes = $this->getTaxes(
- $this->transaction->getOrder()->getShippingCosts()->getCalculatedTaxes(),
+ $this->order->getShippingCosts()->getCalculatedTaxes(),
$shippingName
);
- if ($this->transaction->getOrder()->getTaxStatus() === 'net') {
- $amount = self::round($amount + $this->transaction->getOrder()->getShippingCosts()->getCalculatedTaxes()->getAmount());
+ if ($this->order->getTaxStatus() === 'net') {
+ $amount = self::round($amount + $this->order->getShippingCosts()->getCalculatedTaxes()->getAmount());
}
$lineItem = (new LineItemCreate())
->setAmountIncludingTax($amount)
->setName($this->fixLength($shippingName . ' ' . $this->translator->trans('vrpayment.payload.shipping.lineItem'), 150))
- ->setQuantity($this->transaction->getOrder()->getShippingCosts()->getQuantity() ?? 1)
- ->setTaxes($taxes)
+ ->setQuantity($this->order->getShippingCosts()->getQuantity() ?? 1)
->setSku($this->fixLength($shippingName . '-Shipping', 200))
/** @noinspection PhpParamsInspection */
->setType(LineItemType::SHIPPING)
->setUniqueId($this->fixLength($shippingName . '-Shipping', 200));
+ if ($this->order->getTaxStatus() !== 'tax-free') {
+ $lineItem->setTaxes($taxes);
+ }
+
if (!$lineItem->valid()) {
$this->logger->critical('Shipping LineItem payload invalid:', $lineItem->listInvalidProperties());
throw new InvalidPayloadException('Shipping LineItem payload invalid:' . json_encode($lineItem->listInvalidProperties()));
@@ -566,15 +671,15 @@ class TransactionPayload extends AbstractPayload
protected function getMultipleShippingLineItems(): array
{
try {
- if ($this->transaction->getOrder()->getShippingTotal() > 0) {
+ if ($this->order->getShippingTotal() > 0) {
$lineItems = [];
$shippingName = $this->salesChannelContext->getShippingMethod()->getName() ?? $this->translator->trans('vrpayment.payload.shipping.name');
$isFirst = true;
- foreach ($this->transaction->getOrder()->getShippingCosts()->getCalculatedTaxes() as $taxItem) {
+ foreach ($this->order->getShippingCosts()->getCalculatedTaxes() as $taxItem) {
$amount = self::round($taxItem->getPrice());
- if ($this->transaction->getOrder()->getTaxStatus() === 'net') {
+ if ($this->order->getTaxStatus() === 'net') {
$amount = self::round($amount + $taxItem->getTax());
}
$taxRate = $taxItem->getTaxRate();
@@ -586,12 +691,15 @@ class TransactionPayload extends AbstractPayload
$lineItem = (new LineItemCreate())
->setAmountIncludingTax($amount)
->setName($this->fixLength($name . ' ' . $this->translator->trans('vrpayment.payload.shipping.lineItem'), 150))
- ->setQuantity($this->transaction->getOrder()->getShippingCosts()->getQuantity() ?? 1)
- ->setTaxes([$tax])
+ ->setQuantity($this->order->getShippingCosts()->getQuantity() ?? 1)
->setSku($this->fixLength($name . '-Shipping', 200))
->setType($isFirst ? LineItemType::SHIPPING : LineItemType::FEE) // First item as SHIPPING, rest as FEE
->setUniqueId($this->fixLength($name . '-Shipping', 200));
+ if ($this->order->getTaxStatus() !== 'tax-free') {
+ $lineItem->setTaxes([$tax]);
+ }
+
if (!$lineItem->valid()) {
$this->logger->critical('Shipping LineItem payload invalid:', $lineItem->listInvalidProperties());
throw new InvalidPayloadException('Shipping LineItem payload invalid:' . json_encode($lineItem->listInvalidProperties()));
@@ -625,9 +733,8 @@ class TransactionPayload extends AbstractPayload
$lineItemPriceTotal = array_sum(array_map(static fn(LineItemCreate $li) => $li->getAmountIncludingTax(), $lineItems));
$this->logger->debug("LineItem price total before adjustment: $lineItemPriceTotal");
-
// Get shipping total including taxes from the order
- $shippingCosts = $this->transaction->getOrder()->getShippingCosts();
+ $shippingCosts = $this->order->getShippingCosts();
$shippingTotal = $shippingCosts ? self::round($shippingCosts->getTotalPrice()) : 0.0;
// Add shipping to the line items total if it's not already included
@@ -636,13 +743,13 @@ class TransactionPayload extends AbstractPayload
$lineItemPriceTotal += $shippingTotal;
}
- $adjustmentPrice = self::round($this->transaction->getOrder()->getAmountTotal() - $lineItemPriceTotal);
+ $adjustmentPrice = self::round($this->order->getAmountTotal() - $lineItemPriceTotal);
if (abs($adjustmentPrice) != 0) {
if ($this->settings->isLineItemConsistencyEnabled()) {
$error = strtr('LineItems total :lineItemTotal does not add up to order total :orderTotal', [
':lineItemTotal' => $lineItemPriceTotal,
- ':orderTotal' => $this->transaction->getOrder()->getAmountTotal(),
+ ':orderTotal' => $this->order->getAmountTotal(),
]);
$this->logger->critical($error);
throw new \Exception($error);
diff --git a/src/Resources/app/administration/src/module/vrpayment-order/component/vrpayment-order-action-completion/index.html.twig b/src/Resources/app/administration/src/module/vrpayment-order/component/vrpayment-order-action-completion/index.html.twig
index 506861f..ec0cc3b 100644
--- a/src/Resources/app/administration/src/module/vrpayment-order/component/vrpayment-order-action-completion/index.html.twig
+++ b/src/Resources/app/administration/src/module/vrpayment-order/component/vrpayment-order-action-completion/index.html.twig
@@ -4,21 +4,21 @@
@modal-close="$emit('modal-close')">
{% block vrpayment_order_action_completion_amount %}
-
-
+ v-model:checked="isCompletion">
+
{% endblock %}
{% block vrpayment_order_action_completion_confirm_button %}
-
{{ $tc('vrpayment-order.refundAction.confirmButton.text') }}
-
+
{% endblock %}
-
+
{% endblock %}
diff --git a/src/Resources/app/administration/src/module/vrpayment-order/component/vrpayment-order-action-refund-by-amount/index.html.twig b/src/Resources/app/administration/src/module/vrpayment-order/component/vrpayment-order-action-refund-by-amount/index.html.twig
index 3f00152..6f86754 100644
--- a/src/Resources/app/administration/src/module/vrpayment-order/component/vrpayment-order-action-refund-by-amount/index.html.twig
+++ b/src/Resources/app/administration/src/module/vrpayment-order/component/vrpayment-order-action-refund-by-amount/index.html.twig
@@ -4,23 +4,23 @@
@modal-close="$emit('modal-close')">
{% block vrpayment_order_action_refund_amount_by_amount %}
-
-
+
{% endblock %}
{% block vrpayment_order_action_refund_confirm_button_by_amount %}
-
+
{{ $tc('vrpayment-order.refundAction.confirmButton.text') }}
-
+
{% endblock %}
-
+
{% endblock %}
diff --git a/src/Resources/app/administration/src/module/vrpayment-order/component/vrpayment-order-action-refund-by-amount/index.js b/src/Resources/app/administration/src/module/vrpayment-order/component/vrpayment-order-action-refund-by-amount/index.js
index 2a0e0ad..f11d712 100644
--- a/src/Resources/app/administration/src/module/vrpayment-order/component/vrpayment-order-action-refund-by-amount/index.js
+++ b/src/Resources/app/administration/src/module/vrpayment-order/component/vrpayment-order-action-refund-by-amount/index.js
@@ -70,9 +70,18 @@ Component.register('vrpayment-order-action-refund-by-amount', {
});
}).catch((errorResponse) => {
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({
- title: errorResponse.response.data.errors[0].title,
- message: errorResponse.response.data.errors[0].detail,
+ title: errorTitle,
+ message: errorMessage,
autoClose: false
});
} catch (e) {
diff --git a/src/Resources/app/administration/src/module/vrpayment-order/component/vrpayment-order-action-refund-partial/index.html.twig b/src/Resources/app/administration/src/module/vrpayment-order/component/vrpayment-order-action-refund-partial/index.html.twig
index c3aa0cc..37a7e2b 100644
--- a/src/Resources/app/administration/src/module/vrpayment-order/component/vrpayment-order-action-refund-partial/index.html.twig
+++ b/src/Resources/app/administration/src/module/vrpayment-order/component/vrpayment-order-action-refund-partial/index.html.twig
@@ -4,13 +4,13 @@
@modal-close="$emit('modal-close')">
{% block vrpayment_order_action_refund_amount_partial %}
-
-
+
{{ $tc('vrpayment-order.refundAction.maxAvailableAmountToRefund') }}:
@@ -20,12 +20,12 @@
{% block vrpayment_order_action_refund_confirm_button_partial %}
-
+
{{ $tc('vrpayment-order.refundAction.confirmButton.text') }}
-
+
{% endblock %}
-
+
{% endblock %}
diff --git a/src/Resources/app/administration/src/module/vrpayment-order/component/vrpayment-order-action-refund-partial/index.js b/src/Resources/app/administration/src/module/vrpayment-order/component/vrpayment-order-action-refund-partial/index.js
index b9274e8..d581002 100644
--- a/src/Resources/app/administration/src/module/vrpayment-order/component/vrpayment-order-action-refund-partial/index.js
+++ b/src/Resources/app/administration/src/module/vrpayment-order/component/vrpayment-order-action-refund-partial/index.js
@@ -47,7 +47,9 @@ Component.register('vrpayment-order-action-refund-partial', {
createdComponent() {
this.isLoading = false;
this.currency = this.transactionData.transactions[0].currency;
- this.refundAmount = this.$parent.$parent.itemRefundableAmount;
+ if (!this.refundAmount) {
+ this.refundAmount = this.$parent.$parent.itemRefundableAmount;
+ }
},
createPartialRefund(itemUniqueId) {
diff --git a/src/Resources/app/administration/src/module/vrpayment-order/component/vrpayment-order-action-refund-selected/index.html.twig b/src/Resources/app/administration/src/module/vrpayment-order/component/vrpayment-order-action-refund-selected/index.html.twig
index b1cf34d..a2bfbb1 100644
--- a/src/Resources/app/administration/src/module/vrpayment-order/component/vrpayment-order-action-refund-selected/index.html.twig
+++ b/src/Resources/app/administration/src/module/vrpayment-order/component/vrpayment-order-action-refund-selected/index.html.twig
@@ -5,12 +5,12 @@
{% block vrpayment_order_action_refund_confirm_button_selected %}
-
+
{{ $tc('vrpayment-order.refundAction.confirmButton.text') }}
-
+
{% endblock %}
-
+
{% endblock %}
diff --git a/src/Resources/app/administration/src/module/vrpayment-order/component/vrpayment-order-action-refund/index.html.twig b/src/Resources/app/administration/src/module/vrpayment-order/component/vrpayment-order-action-refund/index.html.twig
index d25ef15..2e3e421 100644
--- a/src/Resources/app/administration/src/module/vrpayment-order/component/vrpayment-order-action-refund/index.html.twig
+++ b/src/Resources/app/administration/src/module/vrpayment-order/component/vrpayment-order-action-refund/index.html.twig
@@ -5,12 +5,12 @@
{% block vrpayment_order_action_refund_amount %}
-
-
+
{{ $tc('vrpayment-order.refundAction.maxAvailableItemsToRefund') }}:
@@ -20,12 +20,12 @@
{% block vrpayment_order_action_refund_confirm_button %}
-
+
{{ $tc('vrpayment-order.refundAction.confirmButton.text') }}
-
+
{% endblock %}
-
+
{% endblock %}
diff --git a/src/Resources/app/administration/src/module/vrpayment-order/component/vrpayment-order-action-void/index.html.twig b/src/Resources/app/administration/src/module/vrpayment-order/component/vrpayment-order-action-void/index.html.twig
index 98c67c1..62abf11 100644
--- a/src/Resources/app/administration/src/module/vrpayment-order/component/vrpayment-order-action-void/index.html.twig
+++ b/src/Resources/app/administration/src/module/vrpayment-order/component/vrpayment-order-action-void/index.html.twig
@@ -4,21 +4,22 @@
@modal-close="$emit('modal-close')">
{% block vrpayment_order_action_void_amount %}
-
-
+ v-model:checked="isVoid">
+
{% endblock %}
{% block vrpayment_order_action_void_confirm_button %}
-
{{ $tc('vrpayment-order.refundAction.confirmButton.text') }}
-
+
{% endblock %}
-
+
{% endblock %}
diff --git a/src/Resources/app/administration/src/module/vrpayment-order/extension/sw-order/sw-order.html.twig b/src/Resources/app/administration/src/module/vrpayment-order/extension/sw-order/sw-order.html.twig
index 27d4108..aeac916 100644
--- a/src/Resources/app/administration/src/module/vrpayment-order/extension/sw-order/sw-order.html.twig
+++ b/src/Resources/app/administration/src/module/vrpayment-order/extension/sw-order/sw-order.html.twig
@@ -1,6 +1,7 @@
{% block sw_order_detail_content_tabs_general %}
{% parent %}
+{# sw-tabs-item will dissappear. See: https://github.com/shopware/shopware/blob/trunk/UPGRADE-6.7.md#sw-tabs-is-removed #}
diff --git a/src/Resources/app/administration/src/module/vrpayment-order/extension/sw-order/sw-order.scss b/src/Resources/app/administration/src/module/vrpayment-order/extension/sw-order/sw-order.scss
index ff18ca8..f7ab85e 100644
--- a/src/Resources/app/administration/src/module/vrpayment-order/extension/sw-order/sw-order.scss
+++ b/src/Resources/app/administration/src/module/vrpayment-order/extension/sw-order/sw-order.scss
@@ -3,7 +3,7 @@
margin-top: 40px;
}
- .sw-order-detail-base .sw-card-view__content {
+ .sw-order-detail-base .mt-card-view__content {
overflow-x: visible;
overflow-y: visible;
}
diff --git a/src/Resources/app/administration/src/module/vrpayment-order/page/vrpayment-order-detail/index.html.twig b/src/Resources/app/administration/src/module/vrpayment-order/page/vrpayment-order-detail/index.html.twig
index 5d68090..30d37cc 100644
--- a/src/Resources/app/administration/src/module/vrpayment-order/page/vrpayment-order-detail/index.html.twig
+++ b/src/Resources/app/administration/src/module/vrpayment-order/page/vrpayment-order-detail/index.html.twig
@@ -1,61 +1,61 @@
{% block vrpayment_order_detail %}
-
+
{% block vrpayment_order_actions_section %}
-
+
{% block vrpayment_order_transaction_refunds_action_button %}
-
{{ $tc('vrpayment-order.buttons.label.refund') }}
-
+
{% endblock %}
{% block vrpayment_order_transaction_completion_action_button %}
-
{{ $tc('vrpayment-order.buttons.label.completion') }}
-
+
{% endblock %}
{% block vrpayment_order_transaction_void_action_button %}
-
{{ $tc('vrpayment-order.buttons.label.void') }}
-
+
{% endblock %}
{% block vrpayment_order_transaction_download_invoice_action_button %}
-
{{ $tc('vrpayment-order.buttons.label.download-invoice') }}
-
+
{% endblock %}
{% block vrpayment_order_transaction_download_packing_slip_action_button %}
-
{{ $tc('vrpayment-order.buttons.label.download-packing-slip') }}
-
+
{% endblock %}
-
+
{% endblock %}
-
+
{% block vrpayment_order_transaction_history_card %}
-
+
{% block vrpayment_order_transaction_history_grid %}
@@ -78,10 +78,10 @@
{% endblock %}
-
+
{% endblock %}
{% block vrpayment_order_transaction_line_items_card %}
-
+
{% block vrpayment_order_transaction_line_items_grid %}
@@ -131,10 +131,10 @@
{% endblock %}
-
+
{% endblock %}
{% block vrpayment_order_transaction_refunds_card %}
-
+
{% block vrpayment_order_transaction_refunds_grid %}
@@ -147,7 +147,7 @@
{% endblock %}
-
+
{% endblock %}
{% block vrpayment_order_actions_modal_refund_partial %}
{% endblock %}
-
+
{% endblock %}
diff --git a/src/Resources/app/administration/src/module/vrpayment-order/snippet/de-DE.json b/src/Resources/app/administration/src/module/vrpayment-order/snippet/de-DE.json
index ecd1e48..d712fb1 100644
--- a/src/Resources/app/administration/src/module/vrpayment-order/snippet/de-DE.json
+++ b/src/Resources/app/administration/src/module/vrpayment-order/snippet/de-DE.json
@@ -77,7 +77,11 @@
"successMessage": "Ihre Rückerstattung war erfolgreich",
"successTitle": "Erfolg",
"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": {
"cardTitle": "Einzelheiten",
diff --git a/src/Resources/app/administration/src/module/vrpayment-order/snippet/en-GB.json b/src/Resources/app/administration/src/module/vrpayment-order/snippet/en-GB.json
index 8f3f943..370354c 100644
--- a/src/Resources/app/administration/src/module/vrpayment-order/snippet/en-GB.json
+++ b/src/Resources/app/administration/src/module/vrpayment-order/snippet/en-GB.json
@@ -78,7 +78,11 @@
"successMessage": "Your refund was successful.",
"successTitle": "Success",
"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": {
"cardTitle": "Details",
diff --git a/src/Resources/app/administration/src/module/vrpayment-order/snippet/fr-FR.json b/src/Resources/app/administration/src/module/vrpayment-order/snippet/fr-FR.json
index 97688e3..b4e9f00 100644
--- a/src/Resources/app/administration/src/module/vrpayment-order/snippet/fr-FR.json
+++ b/src/Resources/app/administration/src/module/vrpayment-order/snippet/fr-FR.json
@@ -77,7 +77,11 @@
"successMessage": "Votre remboursement a été effectué avec succès.",
"successTitle": "Succès",
"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": {
"cardTitle": "Détails",
diff --git a/src/Resources/app/administration/src/module/vrpayment-order/snippet/it-IT.json b/src/Resources/app/administration/src/module/vrpayment-order/snippet/it-IT.json
index 12d174b..12a9ac8 100644
--- a/src/Resources/app/administration/src/module/vrpayment-order/snippet/it-IT.json
+++ b/src/Resources/app/administration/src/module/vrpayment-order/snippet/it-IT.json
@@ -77,7 +77,11 @@
"successMessage": "Il tuo rimborso è andato a buon fine.",
"successTitle": "Successo",
"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": {
"cardTitle": "Dettagli",
diff --git a/src/Resources/app/administration/src/module/vrpayment-settings/component/sw-vrpayment-advanced-options/index.html.twig b/src/Resources/app/administration/src/module/vrpayment-settings/component/sw-vrpayment-advanced-options/index.html.twig
index 024b3ff..68ca180 100644
--- a/src/Resources/app/administration/src/module/vrpayment-settings/component/sw-vrpayment-advanced-options/index.html.twig
+++ b/src/Resources/app/administration/src/module/vrpayment-settings/component/sw-vrpayment-advanced-options/index.html.twig
@@ -1,4 +1,4 @@
-
@@ -7,16 +7,16 @@
:inheritedValue="selectedSalesChannelId == null ? null : allConfigs['null'][CONFIG_STOREFRONT_WEBHOOKS_UPDATE_ENABLED]"
:customInheritationCheckFunction="checkBoolFieldInheritance">
-
-
+ :checked="props.currentValue"
+ @update:checked="props.updateCurrentValue">
+
@@ -25,19 +25,19 @@
:inheritedValue="selectedSalesChannelId == null ? null : allConfigs['null'][CONFIG_STOREFRONT_PAYMENTS_UPDATE_ENABLED]"
:customInheritationCheckFunction="checkBoolFieldInheritance">
-
-
+ :checked="props.currentValue"
+ @update:checked="props.updateCurrentValue">
+
-
+
diff --git a/src/Resources/app/administration/src/module/vrpayment-settings/component/sw-vrpayment-credentials/index.html.twig b/src/Resources/app/administration/src/module/vrpayment-settings/component/sw-vrpayment-credentials/index.html.twig
index 90f9b5e..117ac2d 100644
--- a/src/Resources/app/administration/src/module/vrpayment-settings/component/sw-vrpayment-credentials/index.html.twig
+++ b/src/Resources/app/administration/src/module/vrpayment-settings/component/sw-vrpayment-credentials/index.html.twig
@@ -1,6 +1,6 @@
{% block vrpayment_settings_content_card_channel_config_credentials %}
-
@@ -17,17 +17,17 @@
:inheritedValue="selectedSalesChannelId === null ? null : allConfigs['null'][CONFIG_SPACE_ID]"
:customInheritationCheckFunction="checkNumberFieldInheritance">
-
-
+ @update:model-value="props.updateCurrentValue">
+
{% endblock %}
@@ -38,17 +38,17 @@
:inheritedValue="selectedSalesChannelId === null ? null : allConfigs['null'][CONFIG_USER_ID]"
:customInheritationCheckFunction="checkNumberFieldInheritance">
-
-
+ @update:model-value="props.updateCurrentValue">
+
{% endblock %}
@@ -59,7 +59,7 @@
:inheritedValue="selectedSalesChannelId === null ? null : allConfigs['null'][CONFIG_APPLICATION_KEY]"
:customInheritationCheckFunction="checkTextFieldInheritance">
-
-
+ @update:model-value="props.updateCurrentValue">
+
{% endblock %}
{% endblock %}
+ {% verbatim %}
-
{{ $tc('vrpayment-settings.settingForm.credentials.button.label') }}
-
+
+ {% endverbatim %}
{% endblock %}
-
+
{% endblock %}
diff --git a/src/Resources/app/administration/src/module/vrpayment-settings/component/sw-vrpayment-options/index.html.twig b/src/Resources/app/administration/src/module/vrpayment-settings/component/sw-vrpayment-options/index.html.twig
index 0760ce1..252b010 100644
--- a/src/Resources/app/administration/src/module/vrpayment-settings/component/sw-vrpayment-options/index.html.twig
+++ b/src/Resources/app/administration/src/module/vrpayment-settings/component/sw-vrpayment-options/index.html.twig
@@ -1,5 +1,5 @@
{% block vrpayment_settings_content_card_channel_config_options %}
-
{% block vrpayment_settings_content_card_channel_config_credentials_card_container %}
@@ -14,15 +14,15 @@
:inheritedValue="selectedSalesChannelId === null ? null : allConfigs['null'][CONFIG_SPACE_VIEW_ID]"
:customInheritationCheckFunction="checkNumberFieldInheritance">
-
-
+ :model-value="props.currentValue"
+ @update:model-value="props.updateCurrentValue">
+
{% endblock %}
@@ -55,16 +55,16 @@
:inheritedValue="selectedSalesChannelId == null ? null : allConfigs['null'][CONFIG_LINE_ITEM_CONSISTENCY_ENABLED]"
:customInheritationCheckFunction="checkBoolFieldInheritance">
-
-
+ :checked="props.currentValue"
+ @update:checked="props.updateCurrentValue">
+
{% endblock %}
@@ -75,16 +75,16 @@
:inheritedValue="selectedSalesChannelId == null ? null : allConfigs['null'][CONFIG_EMAIL_ENABLED]"
:customInheritationCheckFunction="checkBoolFieldInheritance">
-
-
+ :checked="props.currentValue"
+ @update:checked="props.updateCurrentValue">
+
{% endblock %}
@@ -92,6 +92,6 @@
{% endblock %}
{% endblock %}
-
+
{% endblock %}
diff --git a/src/Resources/app/administration/src/module/vrpayment-settings/component/sw-vrpayment-settings-icon/index.html.twig b/src/Resources/app/administration/src/module/vrpayment-settings/component/sw-vrpayment-settings-icon/index.html.twig
index ad1f95e..3fad643 100644
--- a/src/Resources/app/administration/src/module/vrpayment-settings/component/sw-vrpayment-settings-icon/index.html.twig
+++ b/src/Resources/app/administration/src/module/vrpayment-settings/component/sw-vrpayment-settings-icon/index.html.twig
@@ -1,5 +1,5 @@
{% block vrpayment_settings_icon %}
-
+