diff --git a/composer.json b/composer.json index b4460e50..da409c32 100644 --- a/composer.json +++ b/composer.json @@ -6,7 +6,7 @@ } ], "description": "Official Shopware 6 Plugin to connect to Payment Service Provider Adyen", - "version": "3.14.3", + "version": "3.14.4", "type": "shopware-platform-plugin", "license": "MIT", "require": { diff --git a/src/Entity/PaymentResponse/PaymentResponseEntity.php b/src/Entity/PaymentResponse/PaymentResponseEntity.php index 68a81a18..a5dee447 100644 --- a/src/Entity/PaymentResponse/PaymentResponseEntity.php +++ b/src/Entity/PaymentResponse/PaymentResponseEntity.php @@ -66,6 +66,11 @@ class PaymentResponseEntity extends Entity */ protected $createdAt; + /** + * @var string + */ + protected $pspreference; + /** * @return string */ @@ -161,4 +166,20 @@ public function setResponse(string $response): void { $this->response = $response; } + + /** + * @return string|null + */ + public function getPspreference(): ?string + { + return $this->pspreference; + } + + /** + * @param string|null $pspreference + */ + public function setPspreference(?string $pspreference): void + { + $this->pspreference = $pspreference; + } } diff --git a/src/Entity/PaymentResponse/PaymentResponseEntityDefinition.php b/src/Entity/PaymentResponse/PaymentResponseEntityDefinition.php index 408b4d15..55163ef6 100644 --- a/src/Entity/PaymentResponse/PaymentResponseEntityDefinition.php +++ b/src/Entity/PaymentResponse/PaymentResponseEntityDefinition.php @@ -32,8 +32,6 @@ use Shopware\Core\Framework\DataAbstractionLayer\Field\IdField; use Shopware\Core\Framework\DataAbstractionLayer\Field\LongTextField; use Shopware\Core\Framework\DataAbstractionLayer\Field\ManyToOneAssociationField; -use Shopware\Core\Framework\DataAbstractionLayer\Field\OneToManyAssociationField; -use Shopware\Core\Framework\DataAbstractionLayer\Field\OneToOneAssociationField; use Shopware\Core\Framework\DataAbstractionLayer\Field\StringField; use Shopware\Core\Framework\DataAbstractionLayer\Field\UpdatedAtField; use Shopware\Core\Framework\DataAbstractionLayer\FieldCollection; @@ -67,6 +65,7 @@ protected function defineFields(): FieldCollection OrderTransactionDefinition::class ))->addFlags(new Required()), new StringField('result_code', 'resultCode'), + new StringField('pspreference', 'pspreference'), new LongTextField('response', 'response'), new CreatedAtField(), new UpdatedAtField(), diff --git a/src/Handlers/AbstractPaymentMethodHandler.php b/src/Handlers/AbstractPaymentMethodHandler.php index 4622aff6..684f7f7b 100644 --- a/src/Handlers/AbstractPaymentMethodHandler.php +++ b/src/Handlers/AbstractPaymentMethodHandler.php @@ -660,7 +660,7 @@ protected function preparePaymentsRequest( $imageUrl = null; } - if (isset($product) && !is_null($product->getCategories())) { + if (isset($product) && !is_null($product->getCategories()) && $product->getCategories()->count() > 0) { $productCategory = $product->getCategories()->first()->getName(); } else { $productCategory = null; diff --git a/src/Migration/Migration1705588547AlterAdyenPaymentResponse.php b/src/Migration/Migration1705588547AlterAdyenPaymentResponse.php new file mode 100644 index 00000000..a0e656f0 --- /dev/null +++ b/src/Migration/Migration1705588547AlterAdyenPaymentResponse.php @@ -0,0 +1,37 @@ +executeStatement(<<executeStatement(<<executeStatement(<< + diff --git a/src/ScheduledTask/FetchPaymentMethodLogosHandler.php b/src/ScheduledTask/FetchPaymentMethodLogosHandler.php index cc88a8c9..01c641b7 100644 --- a/src/ScheduledTask/FetchPaymentMethodLogosHandler.php +++ b/src/ScheduledTask/FetchPaymentMethodLogosHandler.php @@ -51,7 +51,7 @@ class FetchPaymentMethodLogosHandler extends ScheduledTaskHandler */ private $paymentMethodRepository; /** - * @var EntityRepository + * @var EntityRepository|\Shopware\Core\Content\Media\DataAbstractionLayer\MediaRepositoryDecorator */ private $mediaRepository; /** @@ -62,8 +62,8 @@ class FetchPaymentMethodLogosHandler extends ScheduledTaskHandler public function __construct( EntityRepository $scheduledTaskRepository, MediaService $mediaService, - EntityRepository $paymentMethodRepository, - EntityRepository $mediaRepository, + $paymentMethodRepository, + $mediaRepository, bool $enableUrlUploadFeature = true ) { parent::__construct($scheduledTaskRepository); diff --git a/src/ScheduledTask/ProcessNotificationsHandler.php b/src/ScheduledTask/ProcessNotificationsHandler.php index 93b985e1..a74e2b84 100644 --- a/src/ScheduledTask/ProcessNotificationsHandler.php +++ b/src/ScheduledTask/ProcessNotificationsHandler.php @@ -30,6 +30,7 @@ use Adyen\Shopware\Service\AdyenPaymentService; use Adyen\Shopware\Service\CaptureService; use Adyen\Shopware\Service\NotificationService; +use Adyen\Shopware\Service\PaymentResponseService; use Adyen\Shopware\Service\Repository\OrderRepository; use Adyen\Shopware\Service\Repository\OrderTransactionRepository; use Adyen\Webhook\Exception\InvalidDataException; @@ -102,6 +103,11 @@ class ProcessNotificationsHandler extends ScheduledTaskHandler */ private static WebhookHandlerFactory $webhookHandlerFactory; + /** + * @var PaymentResponseService $paymentResponseService + */ + private PaymentResponseService $paymentResponseService; + /** * @var array Map Shopware transaction states to payment states in the webhook module. */ @@ -124,16 +130,18 @@ class ProcessNotificationsHandler extends ScheduledTaskHandler * @param AdyenPaymentService $adyenPaymentService * @param CaptureService $captureService * @param WebhookHandlerFactory $webhookHandlerFactory + * @param PaymentResponseService $paymentResponseService */ public function __construct( EntityRepository $scheduledTaskRepository, NotificationService $notificationService, OrderRepository $orderRepository, - EntityRepository $paymentMethodRepository, + $paymentMethodRepository, OrderTransactionRepository $orderTransactionRepository, AdyenPaymentService $adyenPaymentService, CaptureService $captureService, - WebhookHandlerFactory $webhookHandlerFactory + WebhookHandlerFactory $webhookHandlerFactory, + PaymentResponseService $paymentResponseService ) { parent::__construct($scheduledTaskRepository); $this->notificationService = $notificationService; @@ -142,6 +150,7 @@ public function __construct( $this->orderTransactionRepository = $orderTransactionRepository; $this->adyenPaymentService = $adyenPaymentService; $this->captureService = $captureService; + $this->paymentResponseService = $paymentResponseService; self::$webhookHandlerFactory = $webhookHandlerFactory; } @@ -320,10 +329,23 @@ private function getOrderTransaction( NotificationEntity $notification, array $logContext ): ?OrderTransactionEntity { - $orderTransaction = $this->orderTransactionRepository->getFirstAdyenOrderTransactionByStates( - $order->getId(), - self::WEBHOOK_TRANSACTION_STATES - ); + $orderTransaction = null; + + /* Fetch the related order_transaction entity with the given pspreference. + * There might be several order transactions if there have been multiple payment attempts. */ + $adyenPaymentResponse = $this->paymentResponseService->getWithPspreference($notification->getPspreference()); + if (isset($adyenPaymentResponse)) { + $orderTransaction = $this->orderTransactionRepository->getWithId( + $adyenPaymentResponse->getOrderTransactionId() + ); + } + + if (is_null($orderTransaction)) { + $orderTransaction = $this->orderTransactionRepository->getFirstAdyenOrderTransactionByStates( + $order->getId(), + self::WEBHOOK_TRANSACTION_STATES + ); + } // Skip if orderTransaction not found (non-Adyen) if (is_null($orderTransaction)) { diff --git a/src/Service/PaymentResponseService.php b/src/Service/PaymentResponseService.php index 07b523eb..bedb07bb 100644 --- a/src/Service/PaymentResponseService.php +++ b/src/Service/PaymentResponseService.php @@ -76,6 +76,16 @@ public function getWithOrderId(string $orderId): ?PaymentResponseEntity return $this->getWithOrderTransaction($orderTransaction); } + public function getWithPspreference(string $pspreference): ?PaymentResponseEntity + { + $criteria = new Criteria(); + $criteria->addFilter((new EqualsFilter('pspreference', $pspreference))); + + return $this->repository + ->search($criteria, Context::createDefaultContext()) + ->first(); + } + public function getWithOrderTransaction(OrderTransactionEntity $orderTransaction): ?PaymentResponseEntity { return $this->repository @@ -102,6 +112,7 @@ public function insertPaymentResponse( $fields['orderTransactionId'] = $orderTransaction->getId(); $fields['resultCode'] = $paymentResponse["resultCode"]; $fields['response'] = json_encode($paymentResponse); + $fields['pspreference'] = $paymentResponse["pspReference"] ?? null; $this->repository->upsert( [$fields], diff --git a/src/Service/Repository/OrderTransactionRepository.php b/src/Service/Repository/OrderTransactionRepository.php index 83177d05..6dfce039 100644 --- a/src/Service/Repository/OrderTransactionRepository.php +++ b/src/Service/Repository/OrderTransactionRepository.php @@ -100,4 +100,20 @@ public function getFirstAdyenOrderTransaction(string $orderId): ?OrderTransactio return $this->repository->search($criteria, Context::createDefaultContext())->first(); } + + /** + * @param string $orderTransactionId + * @return OrderTransactionEntity|null + */ + public function getWithId(string $orderTransactionId): ?OrderTransactionEntity + { + $criteria = new Criteria(); + $criteria->addAssociation('order'); + $criteria->addAssociation('order.currency'); + $criteria->addAssociation('paymentMethod'); + $criteria->addAssociation('paymentMethod.plugin'); + $criteria->addFilter(new EqualsFilter('id', $orderTransactionId)); + + return $this->repository->search($criteria, Context::createDefaultContext())->first(); + } }