Release 7.3.4

This commit is contained in:
andrewrowanwallee
2026-05-28 17:34:20 +02:00
parent 93f24a2cac
commit 05c83b1ac6
17 changed files with 206 additions and 44 deletions
+8
View File
@@ -1,3 +1,11 @@
# 7.3.4
- Shopware 6.7.10.0 compatible
- Fix for cart being lost when changing payment methods
- Fixed for incorrectly reference function (getState())
- Fix cache not being cleaned after payment errors
- Fix missing shipping costs
- Minor fix for Customer Id null inconsistency
# 7.3.3
- Shopware 6.7.9.0 compatible
- Fix for only showing 25 sales channels in selector
+8
View File
@@ -1,3 +1,11 @@
# 7.3.4
- Kompatibel mit Shopware 6.7.10.0
- Problem behoben, bei dem der Warenkorb beim Ändern der Zahlungsmethode verloren ging
- Fehlerhafte Referenzierung der Funktion getState() behoben
- Problem behoben, bei dem der Cache nach Zahlungsfehlern nicht geleert wurde
- Fehlende Versandkosten behoben
- Kleinere Korrektur eines Fehlers mit der Kunden-ID (null)
# 7.3.3
- Kompatibel mit Shopware 6.7.9.0
- Problem behoben, dass im Selektor nur 25 Vertriebskanäle angezeigt wurden
+4 -4
View File
@@ -13,10 +13,10 @@ Please note that this plugin is for versions 6.5, 6.6 or 6.7. For the 6.4 plugin
## Documentation
- For English documentation click [here](https://docs.plugin-documentation.vr-payment.de/vr-payment/shopware-6/7.3.3/docs/en/documentation.html)
- Für die deutsche Dokumentation klicken Sie [hier](https://docs.plugin-documentation.vr-payment.de/vr-payment/shopware-6/7.3.3/docs/de/documentation.html)
- Pour la documentation Française, cliquez [ici](https://docs.plugin-documentation.vr-payment.de/vr-payment/shopware-6/7.3.3/docs/fr/documentation.html)
- Per la documentazione in tedesco, clicca [qui](https://docs.plugin-documentation.vr-payment.de/vr-payment/shopware-6/7.3.3/docs/it/documentation.html)
- For English documentation click [here](https://docs.plugin-documentation.vr-payment.de/vr-payment/shopware-6/7.3.4/docs/en/documentation.html)
- Für die deutsche Dokumentation klicken Sie [hier](https://docs.plugin-documentation.vr-payment.de/vr-payment/shopware-6/7.3.4/docs/de/documentation.html)
- Pour la documentation Française, cliquez [ici](https://docs.plugin-documentation.vr-payment.de/vr-payment/shopware-6/7.3.4/docs/fr/documentation.html)
- Per la documentazione in tedesco, clicca [qui](https://docs.plugin-documentation.vr-payment.de/vr-payment/shopware-6/7.3.4/docs/it/documentation.html)
## Installation
+1 -1
View File
@@ -59,5 +59,5 @@
"vrpayment/sdk": "^4.0.0"
},
"type": "shopware-platform-plugin",
"version": "7.3.3"
"version": "7.3.4"
}
+1 -1
View File
@@ -23,7 +23,7 @@
</a>
</li>
<li>
<a href="https://github.com/vr-payment/shopware-6/releases/tag/7.3.3/">
<a href="https://github.com/vr-payment/shopware-6/releases/tag/7.3.4/">
Source
</a>
</li>
+1 -1
View File
@@ -23,7 +23,7 @@
</a>
</li>
<li>
<a href="https://github.com/vr-payment/shopware-6/releases/tag/7.3.3/">
<a href="https://github.com/vr-payment/shopware-6/releases/tag/7.3.4/">
Source
</a>
</li>
+1 -1
View File
@@ -23,7 +23,7 @@
</a>
</li>
<li>
<a href="https://github.com/vr-payment/shopware-6/releases/tag/7.3.3/">
<a href="https://github.com/vr-payment/shopware-6/releases/tag/7.3.4/">
Source
</a>
</li>
+1 -1
View File
@@ -23,7 +23,7 @@
</a>
</li>
<li>
<a href="https://github.com/vr-payment/shopware-6/releases/tag/7.3.3/">
<a href="https://github.com/vr-payment/shopware-6/releases/tag/7.3.4/">
Source
</a>
</li>
@@ -32,6 +32,7 @@ use VRPayment\Sdk\Model\{
LineItemAttributeCreate,
LineItemCreate,
LineItemType,
TaxCreate,
TokenizationMode,
Transaction,
TransactionCreate,
@@ -606,9 +607,17 @@ class TransactionService
if ($customer === null) {
throw new \Exception('Customer is required to create a transaction');
}
$lineItems = $this->extractLineItems($event);
$lineItems = $this->extractLineItems(
$event,
$salesChannelContext,
);
$customerId = "";
/*
* For guest checkouts, the customer ID is set to null rather than an empty string.
* This ensures consistency with TransactionPayload which also uses null for guests,
* preventing the Portal from treating the difference as an update/change in customer details.
*/
$customerId = null;
if ($customer->getGuest() === false) {
$customerId = $customer->getCustomerNumber();
}
@@ -690,13 +699,16 @@ class TransactionService
}
/**
* Extracts line items from the given source (Event or Cart).
* Extracts line items from the given source (Event or Cart) and appends shipping costs.
*
* @param mixed $source
* @param SalesChannelContext|null $salesChannelContext
* @return array
*/
public function extractLineItems($source): array
{
public function extractLineItems(
$source,
?SalesChannelContext $salesChannelContext = null,
): array {
$lineItems = [];
if ($source) {
if ($source instanceof CheckoutConfirmPageLoadedEvent) {
@@ -721,6 +733,38 @@ class TransactionService
$lineItems[] = $this->createTempLineItem($cartLineItem);
}
}
// Extract and append shipping costs as a line item if applicable.
$shippingCosts = null;
$taxStatus = 'gross';
if ($source instanceof CheckoutConfirmPageLoadedEvent) {
$cart = $source->getPage()->getCart();
$shippingCosts = $cart->getDeliveries()->getShippingCosts();
if ($salesChannelContext !== null) {
$taxStatus = $salesChannelContext->getTaxState();
}
} elseif ($source instanceof AccountEditOrderPageLoadedEvent) {
$order = $source->getPage()->getOrder();
$shippingCosts = $order->getShippingCosts();
$taxStatus = $order->getTaxStatus();
} elseif ($source instanceof \Shopware\Core\Checkout\Cart\Cart) {
$shippingCosts = $source->getDeliveries()->getShippingCosts();
if ($salesChannelContext !== null) {
$taxStatus = $salesChannelContext->getTaxState();
}
}
if ($shippingCosts !== null) {
$shippingLineItem = $this->extractShippingLineItem(
$shippingCosts,
$taxStatus,
$salesChannelContext,
);
if ($shippingLineItem !== null) {
$lineItems[] = $shippingLineItem;
}
}
}
return $lineItems;
}
@@ -862,6 +906,7 @@ class TransactionService
$address->setOrganizationName($addressEntity->getCompany());
$address->setPhoneNumber($addressEntity->getPhoneNumber());
$address->setCountry($addressEntity->getCountry()->getIso());
$address->setCity($addressEntity->getCity() ?: '');
$postalState = $addressEntity?->getCountryState()?->getName()
?: $addressEntity?->getCountryState()?->getShortCode()
@@ -967,7 +1012,7 @@ class TransactionService
*
* @param SalesChannelContext $salesChannelContext
*/
private function clearTransactionIdFromContext(SalesChannelContext $salesChannelContext): void
public function clearTransactionIdFromContext(SalesChannelContext $salesChannelContext): void
{
// Clear from cache key.
$cacheKey = $this->getPendingTransactionCacheKey($salesChannelContext);
@@ -1028,4 +1073,76 @@ class TransactionService
return $lineItem;
}
/**
* Extracts shipping line item from cart/order shipping costs.
*
* @param mixed $shippingCosts
* @param string $taxStatus
* @param SalesChannelContext|null $salesChannelContext
* @return LineItemCreate|null
*/
private function extractShippingLineItem(
$shippingCosts,
string $taxStatus,
?SalesChannelContext $salesChannelContext = null,
): ?LineItemCreate {
// When shipping costs are extracted from a Cart, they are returned as a PriceCollection
// containing multiple CalculatedPrice items. We must sum them to get a single aggregated price.
if ($shippingCosts instanceof \Shopware\Core\Checkout\Cart\Price\Struct\PriceCollection) {
$shippingCosts = $shippingCosts->sum();
}
if (!$shippingCosts instanceof \Shopware\Core\Checkout\Cart\Price\Struct\CalculatedPrice) {
return null;
}
if ($shippingCosts->getTotalPrice() <= 0) {
return null;
}
$amount = $shippingCosts->getTotalPrice();
if ($taxStatus === 'net') {
$amount += $shippingCosts->getCalculatedTaxes()->getAmount();
}
$roundedAmount = $this->round($amount);
$shippingMethodName = $salesChannelContext?->getShippingMethod()?->getName();
$translator = $this->container->has('translator') ? $this->container->get('translator') : null;
$fallbackName = $translator ? $translator->trans('vrpayment.payload.shipping.name') : 'Shipping';
$shippingName = $shippingMethodName ?? $fallbackName;
$shippingLineItem = new LineItemCreate();
$shippingLineItem->setAmountIncludingTax($roundedAmount)
->setName($this->fixLength($shippingName . ' ' . ($translator ? $translator->trans('vrpayment.payload.shipping.lineItem') : 'Shipping'), 150))
->setQuantity($shippingCosts->getQuantity() ?? 1)
->setSku($this->fixLength($shippingName . '-Shipping', 200))
->setType(LineItemType::SHIPPING)
->setUniqueId($this->fixLength($shippingName . '-Shipping', 200));
if ($taxStatus !== 'tax-free') {
$taxes = [];
foreach ($shippingCosts->getCalculatedTaxes() as $calculatedTax) {
$tax = (new TaxCreate())
->setRate($calculatedTax->getTaxRate())
->setTitle($this->fixLength($shippingName . ' : ' . $calculatedTax->getTaxRate(), 40));
$taxes[] = $tax;
}
$shippingLineItem->setTaxes($taxes);
}
return $shippingLineItem;
}
/**
* Fix string length to specific length.
*
* @param string $string
* @param int $maxLength
* @return string
*/
private function fixLength(string $string, int $maxLength): string
{
return \mb_substr($string, 0, $maxLength, 'UTF-8');
}
}
@@ -700,16 +700,6 @@ class WebHookController extends AbstractController {
private function unholdAndCancelDelivery(string $orderId, Context $context): void
{
$order = $this->getOrderEntity($orderId, $context);
try {
$this->orderService->orderStateTransition(
$order->getId(),
StateMachineTransitionActions::ACTION_CANCEL,
new ParameterBag(),
$context
);
} catch (\Exception $exception) {
$this->logger->info($exception->getMessage(), $exception->getTrace());
}
try {
/**
@@ -378,16 +378,6 @@ abstract class WebHookStrategyBase implements WebHookStrategyInterface {
protected function unholdAndCancelDelivery(string $orderId, Context $context): void
{
$order = $this->getOrderEntity($orderId, $context);
try {
$this->orderService->orderStateTransition(
$order->getId(),
StateMachineTransitionActions::ACTION_CANCEL,
new ParameterBag(),
$context
);
} catch (\Exception $exception) {
$this->logger->info($exception->getMessage(), $exception->getTrace());
}
try {
@@ -151,10 +151,18 @@ class VRPaymentPaymentHandler extends AbstractPaymentHandler
}
return new RedirectResponse($redirectUrl);
} catch (\Throwable $e) {
// Clear the transaction ID from the cache or session context depending on whether the SalesChannelContext was initialized
// to prevent subsequent checkout attempts from reusing a failed/invalid transaction ID.
if (isset($salesChannelContext)) {
$this->pluginTransactionService->clearTransactionIdFromContext($salesChannelContext);
} else {
$request->getSession()->remove('transactionId');
}
$errorMessage = 'An error occurred during the communication with external payment gateway : ' . $e->getMessage();
$this->logger->critical($errorMessage);
if ($orderTransaction->getState()?->getTechnicalName() === OrderTransactionStates::STATE_CANCELLED) {
// If the transaction has already been marked as cancelled (e.g. via webhook or concurrent request),
// throw an interrupted exception to signal that payment processing cannot be finalized normally.
if ($orderTransaction->getStateMachineState()?->getTechnicalName() === OrderTransactionStates::STATE_CANCELLED) {
throw PaymentException::asyncFinalizeInterrupted($orderTransaction->getOrder()->getId(), $errorMessage);
}
throw PaymentException::customerCanceled($transaction->getOrderTransactionId(), $errorMessage);
@@ -196,12 +204,27 @@ class VRPaymentPaymentHandler extends AbstractPaymentHandler
$transactionEntity->getSalesChannelId()
);
if (in_array($vRPaymentTransaction->getState(), [TransactionState::FAILED])) {
if (in_array($vRPaymentTransaction->getState(), [TransactionState::FAILED, TransactionState::DECLINE, TransactionState::VOIDED,])) {
$errorMessage = strtr('Customer canceled payment for :orderId on SalesChannel :salesChannelName', [
':orderId' => $orderTransaction->getOrder()->getId(),
':salesChannelName' => $transactionEntity->getSalesChannelId(),
]);
// Retrieve the sales channel context parameters to clear the transaction ID from cache or session context,
// ensuring that a failed transaction is not reused when the customer retries payment.
$token = $this->getContextToken($request);
if ($token) {
$parameters = new SalesChannelContextServiceParameters(
$transactionEntity->getSalesChannelId(),
$token,
originalContext: $context,
);
$salesChannelContext = $this->salesChannelContextService->get($parameters);
$this->pluginTransactionService->clearTransactionIdFromContext($salesChannelContext);
} else {
$request->getSession()->remove('transactionId');
}
$this->logger->info($errorMessage);
throw PaymentException::customerCanceled($orderTransactionId, $errorMessage);
}
@@ -112,7 +112,10 @@ class TransactionManagementService
$addressHash = $customer ? md5(json_encode((array) $customer)) : null;
$currency = (string)$salesChannelContext->getCurrency()->getIsoCode();
$lineItems = $this->transactionService->extractLineItems($event);
$lineItems = $this->transactionService->extractLineItems(
$event,
$salesChannelContext,
);
$lineItemHash = !empty($lineItems) ? md5(json_encode($lineItems)) : $oldLineItemHash;
$needsUpdate = ($oldAddressHash !== $addressHash)
@@ -9,6 +9,7 @@ use Shopware\Core\System\SalesChannel\SalesChannelContext;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Attribute\Route;
use VRPaymentPayment\Core\Api\Transaction\Service\TransactionService;
use VRPaymentPayment\Core\Checkout\Service\CartRecoveryService;
#[Package('checkout')]
@@ -26,11 +27,21 @@ class CartRecoveryRoute
private CartRecoveryService $cartRecoveryService;
/**
* @param CartRecoveryService $cartRecoveryService
* @var TransactionService
* Service to clear and manage transactions.
*/
public function __construct(CartRecoveryService $cartRecoveryService)
{
private TransactionService $transactionService;
/**
* @param CartRecoveryService $cartRecoveryService
* @param TransactionService $transactionService
*/
public function __construct(
CartRecoveryService $cartRecoveryService,
TransactionService $transactionService,
) {
$this->cartRecoveryService = $cartRecoveryService;
$this->transactionService = $transactionService;
}
/**
@@ -57,6 +68,10 @@ class CartRecoveryRoute
return new JsonResponse(['error' => 'Sales channel mismatch'], 403);
}
// Clear the transaction ID from cache and session to prevent the subsequent
// checkout attempt from reusing a stale/failed transaction.
$this->transactionService->clearTransactionIdFromContext($context);
// Perform the cart reconstruction.
$cart = $this->cartRecoveryService->recreateCartFromOrder($order, $context);
@@ -167,6 +167,9 @@ class CheckoutController extends StorefrontController
if ($this->logger) {
$this->logger->error($e->getMessage());
}
// Clear the transaction ID from the cache/session context on error to ensure
// subsequent payment attempts will create/retrieve a clean transaction.
$this->transactionService->clearTransactionIdFromContext($salesChannelContext);
$this->addFlash('danger', $this->trans('vrpayment.paymentMethod.notAvailable'));
return $this->redirectToRoute('frontend.home.page');
}
@@ -219,6 +222,10 @@ class CheckoutController extends StorefrontController
$this->addFlash('danger', $transaction->getUserFailureMessage());
}
}
// Clear the transaction ID from cache and session to prevent the subsequent
// checkout attempt from reusing a stale/failed transaction.
$this->transactionService->clearTransactionIdFromContext($salesChannelContext);
$this->cartRecoveryService->recreateCartFromOrder($order, $salesChannelContext);
} catch (\Exception $exception) {
$this->addFlash('danger', $this->trans('error.addToCartError'));
+1 -1
View File
@@ -26,7 +26,7 @@ class Analytics {
self::SHOP_SYSTEM => 'shopware',
self::SHOP_SYSTEM_VERSION => '6',
self::SHOP_SYSTEM_AND_VERSION => 'shopware-6',
self::PLUGIN_SYSTEM_VERSION => '7.3.3',
self::PLUGIN_SYSTEM_VERSION => '7.3.4',
];
}
@@ -18,6 +18,7 @@
<service id="VRPaymentPayment\Core\Checkout\StoreApi\Route\CartRecoveryRoute">
<argument type="service" id="VRPaymentPayment\Core\Checkout\Service\CartRecoveryService"/>
<argument type="service" id="VRPaymentPayment\Core\Api\Transaction\Service\TransactionService"/>
</service>
<service id="VRPaymentPayment\Core\Checkout\StoreApi\Route\InvoiceRoute">