mirror of
https://github.com/vr-payment/shopware-6.git
synced 2026-06-04 19:03:01 +00:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 05c83b1ac6 |
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -59,5 +59,5 @@
|
||||
"vrpayment/sdk": "^4.0.0"
|
||||
},
|
||||
"type": "shopware-platform-plugin",
|
||||
"version": "7.3.3"
|
||||
"version": "7.3.4"
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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) {
|
||||
$request->getSession()->remove('transactionId');
|
||||
// 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(),
|
||||
]);
|
||||
$request->getSession()->remove('transactionId');
|
||||
|
||||
// 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'));
|
||||
|
||||
@@ -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">
|
||||
|
||||
Reference in New Issue
Block a user