Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CHANGELOG_de-DE.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
# 6.5.8
* Der Express-Checkout funktioniert unter Shopware 6.6 wieder zuverlässig, einschließlich PayPal und Apple Pay
* Beim Express-Checkout wird nun eine verständliche Fehlermeldung angezeigt, wenn die Erstellung der Zahlung fehlschlägt, anstatt den Kunden kommentarlos weiterzuleiten
* Bestellbestätigungen werden nun in der korrekten Sprache versendet und gehen nicht mehr verloren
* Benutzer ohne Berechtigung für die Erweiterung können die Plugin-Einstellungen nicht mehr anzeigen oder bearbeiten
* Produktbilder im Warenkorb werden nun automatisch in einer geeigneten Größe verarbeitet und verursachen keine Gateway-Fehler mehr
* Bestell- und Zahlungsstatus für Direktüberweisung werden nun zuverlässig anhand des Zahlungs- und Transaktionsstatus aktualisiert

# 6.5.7
* Zuverlässigerer Checkout: Der „Bezahlen“-Button wartet jetzt, bis alle erforderlichen Komponenten vollständig geladen sind, wodurch fehlgeschlagene Bestellungen verhindert werden.
* Leistungsverbesserung: Ein redundanter Abruf der Händlerkonfiguration wurde entfernt, wenn die Konfiguration bereits vorhanden ist.
Expand Down
8 changes: 8 additions & 0 deletions CHANGELOG_en-GB.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
# 6.5.8
* Express checkout no longer fails in Shopware 6.6, including PayPal and Apple Pay
* Express checkout now shows a clear error message when payment creation fails, instead of silently redirecting
* Order confirmation emails are sent in the correct language and no longer go missing
* Users without extension permissions can no longer view or edit the plugin settings
* Basket item images are resized so they no longer cause gateway errors
* Order and payment status for Direct Bank Transfer now update correctly based on payment and transaction state

# 6.5.7
* More reliable checkout: The "Pay" button now waits until all required components are fully loaded, preventing failed orders.
* Performance improvement: Eliminated a redundant merchant config fetch when the config is already provided.
Expand Down
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "unzerdev/shopware6",
"description": "Unzer payment integration for Shopware 6",
"version": "6.5.7",
"version": "6.5.8",
"type": "shopware-platform-plugin",
"license": "Apache-2.0",
"minimum-stability": "dev",
Expand Down
36 changes: 0 additions & 36 deletions src/Components/AddressHashGenerator/AddressHashGenerator.php

This file was deleted.

This file was deleted.

6 changes: 3 additions & 3 deletions src/Components/PaymentHandler/AbstractUnzerPaymentHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -257,9 +257,9 @@ protected function executeFailTransition(string $transactionId, Context $context
protected function getUnzerCustomer(string $unzerCustomerId, string $paymentMethodId, OrderTransactionEntity $orderTransaction, SalesChannelContext $salesChannelContext): AbstractUnzerResource
{
$customer = $salesChannelContext->getCustomer();
if (empty($orderTransaction->getOrder())) {
$orderTransaction = $this->fetchTransactionById($orderTransaction->getId(), $salesChannelContext->getContext());
}
// re-fetch to make sure all necessary fields are filled
$orderTransaction = $this->fetchTransactionById($orderTransaction->getId(), $salesChannelContext->getContext());

$fetchedCustomer = null;

if (!empty($unzerCustomerId)) {
Expand Down
14 changes: 9 additions & 5 deletions src/Components/PaymentHandler/UnzerPayPalPaymentHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -237,11 +237,15 @@ public function finalize(
&& $this->paymentType instanceof Paypal
&& $this->paymentType->getEmail() !== null
) {
$this->saveToDeviceVault(
$salesChannelContext->getCustomer(),
UnzerPaymentDeviceEntity::DEVICE_TYPE_PAYPAL,
$salesChannelContext->getContext()
);
try {
$this->saveToDeviceVault(
$salesChannelContext->getCustomer(),
UnzerPaymentDeviceEntity::DEVICE_TYPE_PAYPAL,
$salesChannelContext->getContext()
);
} catch (\Throwable $e) {
$this->logger->warning('Could not save to device vault: ' . $e->getMessage());
}
}

$this->transactionStateHandler->transformTransactionState(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,32 @@

namespace UnzerPayment6\Components\PaymentTransitionMapper;

use UnzerPayment6\Components\PaymentTransitionMapper\Traits\IsBasicPaymentMethodTransitionMapper;
use Shopware\Core\System\StateMachine\Aggregation\StateMachineTransition\StateMachineTransitionActions;
use UnzerPayment6\Components\PaymentTransitionMapper\Exception\TransitionMapperException;
use UnzerSDK\Resources\Payment;
use UnzerSDK\Resources\PaymentTypes\BasePaymentType;
use UnzerSDK\Resources\PaymentTypes\OpenbankingPis;

class OpenBankingTransitionMapper extends AbstractTransitionMapper
{
use IsBasicPaymentMethodTransitionMapper;
public function getTargetPaymentStatus(Payment $paymentObject, string $orderTransactionId): string
{
try {
$charges = $paymentObject->getCharges();
$charge = reset($charges);
if ($paymentObject->isCompleted() && $charge->isPending()) {
return StateMachineTransitionActions::ACTION_REOPEN;
}

return parent::getTargetPaymentStatus($paymentObject, $orderTransactionId);
} catch (TransitionMapperException $exception) {
if ($paymentObject->isPending()) {
return StateMachineTransitionActions::ACTION_REOPEN;
}

throw $exception;
}
}

public function supports(BasePaymentType $paymentType): bool
{
Expand Down
11 changes: 0 additions & 11 deletions src/Components/ResourceHydrator/BasketResourceHydrator.php
Original file line number Diff line number Diff line change
Expand Up @@ -108,15 +108,6 @@ protected function hydrateLineItems(
$basketItem->setTitle($label);
$basketItem->setQuantity($lineItem->getQuantity());
$basketItem->setType($lineItem->getUnitPrice() < 0 ? BasketItemTypes::VOUCHER : BasketItemTypes::GOODS);
if (!empty($lineItem->getCover()?->getUrl()) && !str_contains($lineItem->getCover()?->getUrl(), '.ddev.site')) {
try {
$media = $lineItem->getCover();
$url = $media?->getThumbnails()?->first()?->getUrl() ?? $media?->getUrl();
$basketItem->setImageUrl($url);
} catch (\Exception $e) {
$basketItem->setImageUrl($lineItem->getCover()?->getUrl());
}
}

$taxCounter = 0;
$amountTax = 0.0;
Expand Down Expand Up @@ -169,14 +160,12 @@ protected function hydrateShippingCosts(
$amountPerUnit = round($shippingCosts->getUnitPrice(), $currencyPrecision);
} else {
$priceGross = 0.00;
$amountVat = 0.00;
$taxRate = 0;
$taxCounter = 0;

/** @var CalculatedTax $tax */
foreach ($shippingCosts->getCalculatedTaxes() as $tax) {
$priceGross += $tax->getPrice();
$amountVat += $tax->getTax();
$taxRate += $tax->getTaxRate();
++$taxCounter;

Expand Down
15 changes: 15 additions & 0 deletions src/Components/Struct/Webhook.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ class Webhook

private string $retrieveUrl;

private string $paymentId;

public function __construct(string $jsonData)
{
$this->fromJson($jsonData);
Expand All @@ -24,6 +26,7 @@ public function fromJson(string $jsonData): void
$this->event = $webhookData['event'] ?? '';
$this->publicKey = $webhookData['publicKey'] ?? '';
$this->retrieveUrl = $webhookData['retrieveUrl'] ?? '';
$this->paymentId = $webhookData['paymentId'] ?? '';
}

public function getEvent(): string
Expand Down Expand Up @@ -61,4 +64,16 @@ public function setRetrieveUrl(string $retrieveUrl): self

return $this;
}

public function getPaymentId(): string
{
return $this->paymentId;
}

public function setPaymentId(string $paymentId): self
{
$this->paymentId = $paymentId;

return $this;
}
}
28 changes: 24 additions & 4 deletions src/Components/WebhookHandler/PaymentStatusWebhookHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,15 @@

namespace UnzerPayment6\Components\WebhookHandler;

use Doctrine\DBAL\Connection;
use Psr\Log\LoggerInterface;
use Shopware\Core\Checkout\Order\Aggregate\OrderTransaction\OrderTransactionEntity;
use Shopware\Core\Defaults;
use Shopware\Core\Framework\Context;
use Shopware\Core\Framework\DataAbstractionLayer\EntityRepository;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
use Shopware\Core\Framework\Uuid\Exception\InvalidUuidException;
use Shopware\Core\Framework\Uuid\Uuid;
use Shopware\Core\System\SalesChannel\SalesChannelContext;
use UnzerPayment6\Components\ClientFactory\ClientFactoryInterface;
use UnzerPayment6\Components\CustomFieldsHelper\CustomFieldsHelperInterface;
Expand All @@ -27,19 +30,25 @@ public function __construct(
private readonly ClientFactoryInterface $clientFactory,
private readonly EntityRepository $orderTransactionRepository,
private readonly LoggerInterface $logger,
private readonly CustomFieldsHelperInterface $customFieldsHelper
private readonly CustomFieldsHelperInterface $customFieldsHelper,
private readonly Connection $connection
) {
}

public function supports(Webhook $webhook, SalesChannelContext $context): bool
{
return stripos($webhook->getEvent(), 'payment.') !== false;
return stripos($webhook->getEvent(), 'payment.') !== false || stripos($webhook->getEvent(), 'charge.succeeded') !== false;
}

public function execute(Webhook $webhook, SalesChannelContext $context): void
{
$client = $this->clientFactory->createClientFromPublicKey($webhook->getPublicKey(), $context->getSalesChannelId());
$payment = $client->getResourceService()->fetchResourceByUrl($webhook->getRetrieveUrl());

if (stripos($webhook->getEvent(), 'charge.') !== false) {
$payment = $client->fetchPayment($webhook->getPaymentId());
} else {
$payment = $client->getResourceService()->fetchResourceByUrl($webhook->getRetrieveUrl());
}

if (!$payment instanceof Payment) {
$this->logger->error(
Expand All @@ -65,7 +74,7 @@ public function execute(Webhook $webhook, SalesChannelContext $context): void
return;
}

$context->getContext()->assign(['languageIdChain' => [$transaction->getOrder()->getLanguageId()]]);
$context->getContext()->assign(['languageIdChain' => $this->getLanguageChain($transaction->getOrder()->getLanguageId())]);
$this->customFieldsHelper->setOrderTransactionCustomFields($transaction, $context->getContext());

$this->transactionStateHandler->transformTransactionState(
Expand Down Expand Up @@ -94,4 +103,15 @@ private function getOrderTransaction(?string $orderId, Context $context): ?Order
return null;
}
}

private function getLanguageChain(string $languageId): array
{
$parent = $this->connection->fetchOne(
'SELECT LOWER(HEX(language.parent_id)) FROM language WHERE language.id = :languageId',
['languageId' => Uuid::fromHexToBytes($languageId)]
);
$chain = array_filter(array_unique([$languageId, $parent, Defaults::LANGUAGE_SYSTEM]));

return $chain;
}
}
46 changes: 45 additions & 1 deletion src/Components/WebhookRegistrator/WebhookRegistrator.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,48 @@ public function __construct(
) {
}

public function registerWebhookDirectly(RequestDataBag $requestDataBag): array
{
$returnData = [];
$url = $requestDataBag->get('url', '');
$privateKey = $requestDataBag->get('privateKey');
try {
$relativePath = $this->router->generate('frontend.unzer.webhook.execute', [], UrlGeneratorInterface::ABSOLUTE_PATH);
if (!str_contains($url, $relativePath)) {
$url .= $relativePath;
}
$result = $this->clientFactory
->createClientFromPrivateKey($privateKey)
->createWebhook($url, 'all');

$returnData = [
'success' => true,
'url' => $url,
'data' => $result,
'message' => 'unzer-payment-settings.webhook.register.done',
];

$this->logger->info(\sprintf('Webhooks registered for domain %s', $url));
} catch (UnzerApiException|\Throwable $exception) {
$returnData[$url] = [
'success' => false,
'message' => 'unzer-payment-settings.webhook.register.error',
];

$this->logger->error(
\sprintf('Webhook registration failed for domain %s', $url),
[
'message' => $exception->getMessage(),
'code' => $exception->getCode(),
'file' => $exception->getFile(),
'trace' => $exception->getTraceAsString(),
]
);
}

return $returnData;
}

public function registerWebhook(RequestDataBag $salesChannelDomains): array
{
$returnData = [];
Expand All @@ -54,7 +96,9 @@ public function registerWebhook(RequestDataBag $salesChannelDomains): array

try {
$relativePath = $this->router->generate('frontend.unzer.webhook.execute', [], UrlGeneratorInterface::ABSOLUTE_PATH);
$url = $domainUrl . $relativePath;
if (!str_contains($domainUrl, $relativePath)) {
$url = $domainUrl . $relativePath;
}

$result = $this->clientFactory
->createClientFromPrivateKey($privateKey, $salesChannelId)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,15 @@ public function registerWebhooks(RequestDataBag $dataBag): JsonResponse
);
}

#[Route(path: '/api/_action/unzer-payment/register-webhook-directly', name: 'api.action.unzer.webhook.register-directly', methods: ['POST'])]
public function registerWebhookDirectly(RequestDataBag $dataBag): JsonResponse
{
return new JsonResponse(
$this->webhookRegistrator->registerWebhookDirectly($dataBag),
200
);
}

#[Route(path: '/api/_action/unzer-payment/clear-webhooks', name: 'api.action.unzer.webhooks.clear', methods: ['POST'])]
public function clearWebhooks(RequestDataBag $dataBag): JsonResponse
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ class UnzerPaymentTransactionController extends AbstractController
public function __construct(
private readonly ClientFactoryInterface $clientFactory,
private readonly UnzerTransactionUtil $unzerTransactionUtil,
private readonly PaymentActionService $paymentTransactionService,
private readonly PaymentActionService $paymentActionService,
private readonly PaymentResourceHydratorInterface $hydrator,
private readonly CancelServiceInterface $cancelService,
private readonly ShipServiceInterface $shipService,
Expand Down Expand Up @@ -197,7 +197,7 @@ public function unifiedRefund(Request $request, Context $context): JsonResponse
$comment = (string) $request->get('comment', '');

try {
$this->paymentTransactionService->doUnifiedRefund(
$this->paymentActionService->doUnifiedRefund(
orderTransaction: $orderTransaction,
amount: $amount,
context: $context,
Expand Down
Loading
Loading