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
10 changes: 10 additions & 0 deletions CHANGELOG_de-DE.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
# 7.1.10
* PayPal Express funktioniert im Checkout unter Shopware 6.7 wieder korrekt
* Klarna-Zahlungen schlagen unter Shopware 6.7 nicht mehr mit dem Fehler „Zahlungsmethode nicht gefunden“ fehl
* 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
* Die Hinweise zur Lastschriftzahlung wurden aus dem Checkout entfernt, da sie nicht mehr erforderlich sind

# 7.1.9
* Anpassungen für Unzer Shopware 6.7 plugin zur Unterstützung des Unzer Refund Manager

Expand Down
10 changes: 10 additions & 0 deletions CHANGELOG_en-GB.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
# 7.1.10
* PayPal Express now works correctly at checkout in Shopware 6.7
* Klarna payments no longer fail with a “payment method not found” error in Shopware 6.7
* 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
* Direct Debit payment instructions have been removed from the checkout flow, as they’re no longer needed

# 7.1.9
* Changes for Unzer Shopware 6.7 plugin to support Unzer Refund Manager

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": "7.1.9",
"version": "7.1.10",
"type": "shopware-platform-plugin",
"license": "Apache-2.0",
"minimum-stability": "dev",
Expand Down
37 changes: 0 additions & 37 deletions src/Components/AddressHashGenerator/AddressHashGenerator.php

This file was deleted.

This file was deleted.

5 changes: 5 additions & 0 deletions src/Components/ConfigReader/ConfigReader.php
Original file line number Diff line number Diff line change
Expand Up @@ -77,4 +77,9 @@ public function read(string $salesChannelId = '', bool $fallback = true): Config

return new Configuration($config);
}

public function getSingleValue(string $key, ?string $salesChannelId = null): mixed
{
return $this->systemConfigService->get($key, $salesChannelId);
}
}
2 changes: 2 additions & 0 deletions src/Components/ConfigReader/ConfigReaderInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,6 @@
interface ConfigReaderInterface
{
public function read(string $salesChannelId = '', bool $fallback = true): Configuration;

public function getSingleValue(string $key, ?string $salesChannelId = null): mixed;
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\Routing\Router;
use UnzerPayment6\Components\BookingMode;
use UnzerPayment6\Components\ClientFactory\ClientFactoryInterface;
use UnzerPayment6\Components\ConfigReader\ConfigReaderInterface;
Expand Down Expand Up @@ -77,7 +78,8 @@ public function __construct(
protected readonly CustomFieldsHelperInterface $customFieldsHelper,
protected readonly UnzerTransactionUtil $transactionUtil,
protected readonly EntityRepository $customerRepository,
protected ?UnzerPaymentDeviceRepositoryInterface $deviceRepository = null
protected readonly Router $router,
protected ?UnzerPaymentDeviceRepositoryInterface $deviceRepository = null,
) {
}

Expand Down
12 changes: 8 additions & 4 deletions src/Components/PaymentHandler/UnzerKlarnaPaymentHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@
use Shopware\Core\Framework\Struct\Struct;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use UnzerPayment6\Components\PaymentHandler\Traits\CanAuthorize;
use UnzerPayment6\Components\PaymentHandler\Traits\CanCharge;
use UnzerSDK\Resources\PaymentTypes\BasePaymentType;
use UnzerSDK\Resources\PaymentTypes\Klarna;
use UnzerSDK\Resources\TransactionTypes\Authorization;
use UnzerSDK\Resources\TransactionTypes\Charge;
Expand Down Expand Up @@ -39,9 +39,13 @@ public function pay(
);

try {
$transactionModifier = function (Authorization|Charge $authorization): void {
$authorization->setTermsAndConditionUrl('https://unzer.com');
$authorization->setPrivacyPolicyUrl('https://unzer.com');
$orderTransaction = $this->transactionUtil->getOrderTransaction($transaction->getOrderTransactionId(), $context);
$salesChannelId = $orderTransaction->getOrder()->getSalesChannelId();
$transactionModifier = function (Authorization|Charge $authorization) use ($salesChannelId): void {
$termsUrl = $this->router->generate('frontend.cms.page.full', ['id' => $this->configReader->getSingleValue('core.basicInformation.tosPage', $salesChannelId)], UrlGeneratorInterface::ABSOLUTE_URL);
$privacyUrl = $this->router->generate('frontend.cms.page.full', ['id' => $this->configReader->getSingleValue('core.basicInformation.privacyPage', $salesChannelId)], UrlGeneratorInterface::ABSOLUTE_URL);
$authorization->setTermsAndConditionUrl($termsUrl);
$authorization->setPrivacyPolicyUrl($privacyUrl);
};

$returnUrl = $this->authorize(
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 @@ -168,11 +168,15 @@ public function finalize(
&& $this->paymentType instanceof Paypal
&& $this->paymentType->getEmail() !== null
) {
$this->saveToDeviceVault(
$customer,
UnzerPaymentDeviceEntity::DEVICE_TYPE_PAYPAL,
$context
);
try {
$this->saveToDeviceVault(
$customer,
UnzerPaymentDeviceEntity::DEVICE_TYPE_PAYPAL,
$context
);
} 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
9 changes: 0 additions & 9 deletions src/Components/ResourceHydrator/BasketResourceHydrator.php
Original file line number Diff line number Diff line change
Expand Up @@ -99,15 +99,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
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
Loading
Loading