From 39739b71980193406adf103ebba233613005ab0f Mon Sep 17 00:00:00 2001 From: raoulritter <59527829+raoulritter@users.noreply.github.com> Date: Wed, 13 Mar 2024 08:54:11 +0100 Subject: [PATCH 01/17] update --- .../GetAdyenPaymentMethodsBalance.php | 164 ++++++++++++++++++ 1 file changed, 164 insertions(+) create mode 100644 Model/Resolver/GetAdyenPaymentMethodsBalance.php diff --git a/Model/Resolver/GetAdyenPaymentMethodsBalance.php b/Model/Resolver/GetAdyenPaymentMethodsBalance.php new file mode 100644 index 000000000..29c622f5f --- /dev/null +++ b/Model/Resolver/GetAdyenPaymentMethodsBalance.php @@ -0,0 +1,164 @@ + + */ +declare(strict_types=1); + +namespace Adyen\Payment\Model\Resolver; + +use Adyen\Payment\Exception\GraphQlAdyenException; +use Adyen\Payment\Logger\AdyenLogger; +use Exception; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\GraphQl\Config\Element\Field; +use Magento\Framework\GraphQl\Exception\GraphQlInputException; +use Magento\Framework\GraphQl\Exception\GraphQlNoSuchEntityException; +use Magento\Framework\GraphQl\Query\Resolver\ContextInterface; +use Magento\Framework\GraphQl\Query\Resolver\Value; +use Magento\Framework\GraphQl\Query\ResolverInterface; +use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; +use Magento\Framework\Serialize\Serializer\Json; +use Magento\QuoteGraphQl\Model\Cart\GetCartForUser; +use Magento\Sales\Model\Order; +use Magento\GraphQl\Helper\Error\AggregateExceptionMessageFormatter; + +class GetAdyenPaymentDetails implements ResolverInterface +{ + /** + * @var GetCartForUser + */ + private $getCartForUser; + /** + * @var DataProvider\GetAdyenPaymentStatus + */ + protected $getAdyenPaymentStatusDataProvider; + /** + * @var Order + */ + protected $order; + /** + * @var Json + */ + protected $jsonSerializer; + /** + * @var AdyenLogger + */ + protected $adyenLogger; + + /** + * @param GetCartForUser $getCartForUser + * @param DataProvider\GetAdyenPaymentStatus $getAdyenPaymentStatusDataProvider + * @param Order $order + * @param Json $jsonSerializer + * @param AdyenLogger $adyenLogger + */ + public function __construct( + GetCartForUser $getCartForUser, + DataProvider\GetAdyenPaymentStatus $getAdyenPaymentStatusDataProvider, + Order $order, + Json $jsonSerializer, + AdyenLogger $adyenLogger + ) { + $this->getCartForUser = $getCartForUser; + $this->getAdyenPaymentStatusDataProvider = $getAdyenPaymentStatusDataProvider; + $this->order = $order; + $this->jsonSerializer = $jsonSerializer; + $this->adyenLogger = $adyenLogger; + } + + /** + * @inheritdoc + * + * @param Field $field + * @param ContextInterface $context + * @param ResolveInfo $info + * @param array|null $value + * @param array|null $args + * @return array|Value|mixed + * @throws GraphQlAdyenException + * @throws GraphQlInputException + * @throws GraphQlNoSuchEntityException + */ + public function resolve( + Field $field, + $context, + ResolveInfo $info, + array $value = null, + array $args = null + ) { + if (empty($args['payload'])) { + throw new GraphQlInputException(__('Required parameter "payload" is missing')); + } + if (empty($args['cart_id'])) { + throw new GraphQlInputException(__('Required parameter "cart_id" is missing')); + } + $payload = $this->jsonSerializer->unserialize($args['payload']); + if (!array_key_exists('orderId', $payload)) { + throw new GraphQlInputException(__('Missing "orderId" from payload')); + } + + $order = $this->order->loadByIncrementId($payload['orderId']); + $maskedCartId = $args['cart_id']; + $currentUserId = $context->getUserId(); + $storeId = (int)$context->getExtensionAttributes()->getStore()->getId(); + $cart = $this->getCartForUser->execute($maskedCartId, $currentUserId, $storeId); + if (is_null($order->getEntityId()) || $order->getQuoteId() !== $cart->getEntityId()) { + throw new GraphQlNoSuchEntityException(__('Order does not exist')); + } + + // Set the orderId in the payload to the entity id, instead of the incrementId + $payload['orderId'] = $order->getId(); + + try { + return $this->getAdyenPaymentStatusDataProvider->getGetAdyenPaymentDetails( + $this->jsonSerializer->serialize($payload), + $order, + $cart + ); + } catch (LocalizedException $e) { + throw $this->getFormattedException($e, $field, $context, $info); + } catch (Exception $exception) { + $this->adyenLogger->error(sprintf( + 'GraphQl payment details call failed with error message: %s', + $exception->getMessage() + )); + // In the future, use the message and the code passed by the exception. Since currently the message and code are not + // being passed, use this generic message. + throw new GraphQlAdyenException(__('An unknown error has occurred'), null, 000); + } + } + + /** + * @param $e + * @param Field $field + * @param ContextInterface $context + * @param ResolveInfo $info + * @return mixed + */ + private function getFormattedException($e, Field $field, ContextInterface $context, ResolveInfo $info) + { + if (class_exists(\Magento\QuoteGraphQl\Helper\Error\PlaceOrderMessageFormatter::class)) { + $errorMessageFormatter = \Magento\Framework\App\ObjectManager::getInstance() + ->get(\Magento\QuoteGraphQl\Helper\Error\PlaceOrderMessageFormatter::class); + return $errorMessageFormatter->getFormatted( + $e, + __('Unable to place order: A server error stopped your order from being placed. ' . + 'Please try to place your order again'), + 'Unable to place order', + $field, + $context, + $info + ); + } else { + return new GraphQlAdyenException(__('Unable to place order: A server error stopped your order from being placed. ' . + 'Please try to place your order again')); + } + } +} From bd17457f1c5d5c7359cb00f2803e40dcc0cb984c Mon Sep 17 00:00:00 2001 From: raoulritter <59527829+raoulritter@users.noreply.github.com> Date: Wed, 13 Mar 2024 08:55:13 +0100 Subject: [PATCH 02/17] update implementation sofar --- .../GetAdyenPaymentMethodsBalance.php | 135 +++--------------- etc/schema.graphqls | 38 +++++ 2 files changed, 61 insertions(+), 112 deletions(-) diff --git a/Model/Resolver/GetAdyenPaymentMethodsBalance.php b/Model/Resolver/GetAdyenPaymentMethodsBalance.php index 29c622f5f..ff9c9b619 100644 --- a/Model/Resolver/GetAdyenPaymentMethodsBalance.php +++ b/Model/Resolver/GetAdyenPaymentMethodsBalance.php @@ -28,137 +28,48 @@ use Magento\QuoteGraphQl\Model\Cart\GetCartForUser; use Magento\Sales\Model\Order; use Magento\GraphQl\Helper\Error\AggregateExceptionMessageFormatter; +use Adyen\Payment\Helper\GiftcardPayment; -class GetAdyenPaymentDetails implements ResolverInterface +class GetAdyenRedeemedGiftcards implements ResolverInterface { - /** - * @var GetCartForUser - */ - private $getCartForUser; - /** - * @var DataProvider\GetAdyenPaymentStatus - */ - protected $getAdyenPaymentStatusDataProvider; - /** - * @var Order - */ - protected $order; - /** - * @var Json - */ - protected $jsonSerializer; - /** - * @var AdyenLogger - */ - protected $adyenLogger; + private GiftcardPayment $giftcardPayment; + private GetCartForUser $getCartForUser; - /** - * @param GetCartForUser $getCartForUser - * @param DataProvider\GetAdyenPaymentStatus $getAdyenPaymentStatusDataProvider - * @param Order $order - * @param Json $jsonSerializer - * @param AdyenLogger $adyenLogger - */ public function __construct( - GetCartForUser $getCartForUser, - DataProvider\GetAdyenPaymentStatus $getAdyenPaymentStatusDataProvider, - Order $order, - Json $jsonSerializer, - AdyenLogger $adyenLogger + GiftcardPayment $giftcardPayment, + GetCartForUser $getCartForUser ) { + $this->giftcardPayment = $giftcardPayment; $this->getCartForUser = $getCartForUser; - $this->getAdyenPaymentStatusDataProvider = $getAdyenPaymentStatusDataProvider; - $this->order = $order; - $this->jsonSerializer = $jsonSerializer; - $this->adyenLogger = $adyenLogger; } - /** - * @inheritdoc - * - * @param Field $field - * @param ContextInterface $context - * @param ResolveInfo $info - * @param array|null $value - * @param array|null $args - * @return array|Value|mixed - * @throws GraphQlAdyenException - * @throws GraphQlInputException - * @throws GraphQlNoSuchEntityException - */ public function resolve( Field $field, - $context, + $context, ResolveInfo $info, array $value = null, array $args = null ) { - if (empty($args['payload'])) { - throw new GraphQlInputException(__('Required parameter "payload" is missing')); - } - if (empty($args['cart_id'])) { - throw new GraphQlInputException(__('Required parameter "cart_id" is missing')); - } - $payload = $this->jsonSerializer->unserialize($args['payload']); - if (!array_key_exists('orderId', $payload)) { - throw new GraphQlInputException(__('Missing "orderId" from payload')); + if (empty($args['cartId'])) { + throw new GraphQlInputException(__('Required parameter "cartId" is missing')); } + $cartId = (int) $args['cartId']; + $userId = $context->getUserId(); + $storeId = (int) $context->getExtensionAttributes()->getStore()->getId(); - $order = $this->order->loadByIncrementId($payload['orderId']); - $maskedCartId = $args['cart_id']; - $currentUserId = $context->getUserId(); - $storeId = (int)$context->getExtensionAttributes()->getStore()->getId(); - $cart = $this->getCartForUser->execute($maskedCartId, $currentUserId, $storeId); - if (is_null($order->getEntityId()) || $order->getQuoteId() !== $cart->getEntityId()) { - throw new GraphQlNoSuchEntityException(__('Order does not exist')); - } + // Fetch cart for validation and use it in helper if needed + $cart = $this->getCartForUser->execute($cartId, $userId, $storeId); - // Set the orderId in the payload to the entity id, instead of the incrementId - $payload['orderId'] = $order->getId(); + $redeemedGiftcardsResponse = $this->giftcardPayment->fetchRedeemedGiftcards($cart->getId()); - try { - return $this->getAdyenPaymentStatusDataProvider->getGetAdyenPaymentDetails( - $this->jsonSerializer->serialize($payload), - $order, - $cart - ); - } catch (LocalizedException $e) { - throw $this->getFormattedException($e, $field, $context, $info); - } catch (Exception $exception) { - $this->adyenLogger->error(sprintf( - 'GraphQl payment details call failed with error message: %s', - $exception->getMessage() - )); - // In the future, use the message and the code passed by the exception. Since currently the message and code are not - // being passed, use this generic message. - throw new GraphQlAdyenException(__('An unknown error has occurred'), null, 000); + // Assuming fetchRedeemedGiftcards returns JSON, decode it for GraphQL response + $response = json_decode($redeemedGiftcardsResponse, true); + if (json_last_error() !== JSON_ERROR_NONE) { + throw new \Exception('Error decoding the redeemed giftcards response'); } - } - /** - * @param $e - * @param Field $field - * @param ContextInterface $context - * @param ResolveInfo $info - * @return mixed - */ - private function getFormattedException($e, Field $field, ContextInterface $context, ResolveInfo $info) - { - if (class_exists(\Magento\QuoteGraphQl\Helper\Error\PlaceOrderMessageFormatter::class)) { - $errorMessageFormatter = \Magento\Framework\App\ObjectManager::getInstance() - ->get(\Magento\QuoteGraphQl\Helper\Error\PlaceOrderMessageFormatter::class); - return $errorMessageFormatter->getFormatted( - $e, - __('Unable to place order: A server error stopped your order from being placed. ' . - 'Please try to place your order again'), - 'Unable to place order', - $field, - $context, - $info - ); - } else { - return new GraphQlAdyenException(__('Unable to place order: A server error stopped your order from being placed. ' . - 'Please try to place your order again')); - } + return $response; } } + + diff --git a/etc/schema.graphqls b/etc/schema.graphqls index 0a395bc0c..fb4628747 100644 --- a/etc/schema.graphqls +++ b/etc/schema.graphqls @@ -9,6 +9,10 @@ type Query { shopper_locale: String @doc(description: "Language and country code combination separated by underscore (_), e.g. en_US") country: String @doc(description: "Country code to be used in paymentMethods call, e.g. US") ) : AdyenPaymentMethods @resolver(class: "Adyen\\Payment\\Model\\Resolver\\GetAdyenPaymentMethods") + + adyenPaymentMethodsBalance( + payload: String! @doc(description: "Payload JSON String with orderId, details, paymentData") + ): AdyenPaymentMethodsBalanceResponse @resolver(class: "Adyen\\Payment\\Model\\Resolver\\GetAdyenPaymentMethodsBalance") } type Mutation { @@ -16,6 +20,22 @@ type Mutation { payload: String! @doc(description: "Payload JSON String with orderId, details, paymentData and threeDSAuthenticationOnly.") cart_id: String! @doc(description: "Cart ID.") ) : AdyenPaymentStatus @resolver(class: "Adyen\\Payment\\Model\\Resolver\\GetAdyenPaymentDetails") + + adyenRedeemedGiftcards( + cartId: Int! @doc(description: "Cart ID for which to fetch redeemed gift cards.") + ): AdyenRedeemedGiftcardsResponse @resolver(class: "Adyen\\Payment\\Model\\Resolver\\GetAdyenRedeemedGiftcards") + + adyenSaveStateData( + stateData: String! @doc(description: "JSON string of Adyen state data."), + cartId: Int! @doc(description: "Cart ID associated with the state data.") + ): AdyenStateDataResponse @resolver(class: "Adyen\\Payment\\Model\\Resolver\\SaveAdyenStateData") + + adyenRemoveStateData( + stateDataId: Int! @doc(description: "ID of the state data to remove."), + cartId: Int! @doc(description: "Cart ID associated with the state data.") + ): AdyenStateDataResponse @resolver(class: "Adyen\\Payment\\Model\\Resolver\\RemoveAdyenStateData") +} + } type AdyenPaymentStatus { @@ -90,6 +110,24 @@ type AdyenPaymentMethodsDetailsItems { name: String @doc(description: "The display name.") } +type AdyenPaymentMethodsBalanceResponse { + balance: String @doc(description: "Balance of the payment method.") +} + +type AdyenRedeemedGiftcardsResponse { + giftcards: [AdyenGiftcard] @doc(description: "List of redeemed gift cards.") +} + +type AdyenGiftcard { + id: String @doc(description: "Gift card identifier.") + balance: String @doc(description: "Remaining balance on the gift card.") +} + +type AdyenStateDataResponse { + success: Boolean @doc(description: "Indicates if the operation was successful.") + message: String @doc(description: "Additional message related to the operation outcome.") +} + type AdyenPaymentMethodsExtraDetails { type: String @doc(description: "The unique payment method code.") icon: AdyenPaymentMethodIcon @doc(description: "Icon for the payment method.") From 6965b8d66f5c4a22425ed3dc39ebd0c152aabc3d Mon Sep 17 00:00:00 2001 From: raoulritter <59527829+raoulritter@users.noreply.github.com> Date: Wed, 13 Mar 2024 16:51:21 +0100 Subject: [PATCH 03/17] Implement GetAdyenPaymentMethodsBalance and GetAdyenRedeemedGiftcards in GraphQL --- .../GetAdyenPaymentMethodsBalance.php | 62 ++++++++-------- Model/Resolver/GetAdyenRedeemedGiftcards.php | 71 +++++++++++++++++++ etc/schema.graphqls | 8 +-- 3 files changed, 102 insertions(+), 39 deletions(-) create mode 100644 Model/Resolver/GetAdyenRedeemedGiftcards.php diff --git a/Model/Resolver/GetAdyenPaymentMethodsBalance.php b/Model/Resolver/GetAdyenPaymentMethodsBalance.php index ff9c9b619..80b516c18 100644 --- a/Model/Resolver/GetAdyenPaymentMethodsBalance.php +++ b/Model/Resolver/GetAdyenPaymentMethodsBalance.php @@ -14,61 +14,55 @@ namespace Adyen\Payment\Model\Resolver; use Adyen\Payment\Exception\GraphQlAdyenException; -use Adyen\Payment\Logger\AdyenLogger; +use Adyen\Payment\Model\Api\AdyenPaymentMethodsBalance; use Exception; use Magento\Framework\Exception\LocalizedException; use Magento\Framework\GraphQl\Config\Element\Field; use Magento\Framework\GraphQl\Exception\GraphQlInputException; -use Magento\Framework\GraphQl\Exception\GraphQlNoSuchEntityException; -use Magento\Framework\GraphQl\Query\Resolver\ContextInterface; -use Magento\Framework\GraphQl\Query\Resolver\Value; use Magento\Framework\GraphQl\Query\ResolverInterface; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; use Magento\Framework\Serialize\Serializer\Json; -use Magento\QuoteGraphQl\Model\Cart\GetCartForUser; -use Magento\Sales\Model\Order; -use Magento\GraphQl\Helper\Error\AggregateExceptionMessageFormatter; -use Adyen\Payment\Helper\GiftcardPayment; -class GetAdyenRedeemedGiftcards implements ResolverInterface +class GetAdyenPaymentMethodsBalance implements ResolverInterface { - private GiftcardPayment $giftcardPayment; - private GetCartForUser $getCartForUser; + private AdyenPaymentMethodsBalance $balance; + private Json $jsonSerializer; public function __construct( - GiftcardPayment $giftcardPayment, - GetCartForUser $getCartForUser - ) { - $this->giftcardPayment = $giftcardPayment; - $this->getCartForUser = $getCartForUser; + AdyenPaymentMethodsBalance $balance, + Json $jsonSerializer, + ) + { + $this->balance = $balance; + $this->jsonSerializer = $jsonSerializer; } public function resolve( - Field $field, - $context, + Field $field, + $context, ResolveInfo $info, - array $value = null, - array $args = null - ) { - if (empty($args['cartId'])) { - throw new GraphQlInputException(__('Required parameter "cartId" is missing')); + array $value = null, + array $args = null + ) + { + if (empty($args['payload'])) { + throw new GraphQlInputException(__('Required parameter "payload" is missing')); } - $cartId = (int) $args['cartId']; - $userId = $context->getUserId(); - $storeId = (int) $context->getExtensionAttributes()->getStore()->getId(); - // Fetch cart for validation and use it in helper if needed - $cart = $this->getCartForUser->execute($cartId, $userId, $storeId); - - $redeemedGiftcardsResponse = $this->giftcardPayment->fetchRedeemedGiftcards($cart->getId()); + $payload = $args['payload']; + try { + $balanceResponse = $this->balance->getBalance($payload); + } catch (LocalizedException $e) { + throw new GraphQlAdyenException(__($e->getMessage()), $e); + } catch (Exception $e) { + throw new GraphQlAdyenException(__('An error occurred while fetching the payment method balance.'), $e); + } - // Assuming fetchRedeemedGiftcards returns JSON, decode it for GraphQL response - $response = json_decode($redeemedGiftcardsResponse, true); if (json_last_error() !== JSON_ERROR_NONE) { - throw new \Exception('Error decoding the redeemed giftcards response'); + throw new \Exception('Error decoding the payment methods balance response'); } - return $response; + return ['balance' => $balanceResponse]; } } diff --git a/Model/Resolver/GetAdyenRedeemedGiftcards.php b/Model/Resolver/GetAdyenRedeemedGiftcards.php new file mode 100644 index 000000000..c8728f870 --- /dev/null +++ b/Model/Resolver/GetAdyenRedeemedGiftcards.php @@ -0,0 +1,71 @@ + + */ +declare(strict_types=1); + +namespace Adyen\Payment\Model\Resolver; + +use Adyen\Payment\Exception\GraphQlAdyenException; +use Adyen\Payment\Helper\GiftcardPayment; +use Exception; +use Magento\Quote\Model\QuoteIdMaskFactory; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\GraphQl\Config\Element\Field; +use Magento\Framework\GraphQl\Exception\GraphQlInputException; +use Magento\Framework\GraphQl\Query\ResolverInterface; +use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; +use Magento\Framework\Serialize\Serializer\Json; + +class GetAdyenRedeemedGiftcards implements ResolverInterface +{ + private GiftcardPayment $giftcardPayment; + private Json $jsonSerializer; + private QuoteIdMaskFactory $quoteIdMaskFactory; + + + public function __construct( + GiftcardPayment $giftcardPayment, + Json $jsonSerializer, + QuoteIdMaskFactory $quoteIdMaskFactory + ) + { + $this->giftcardPayment = $giftcardPayment; + $this->jsonSerializer = $jsonSerializer; + $this->quoteIdMaskFactory = $quoteIdMaskFactory; + } + + public function resolve( + Field $field, + $context, + ResolveInfo $info, + array $value = null, + array $args = null + ) + { + if (empty($args['cartId'])) { + throw new GraphQlInputException(__('Required parameter "cartId" is missing')); + } + $cartId = $args['cartId']; + $quoteIdMask = $this->quoteIdMaskFactory->create()->load($cartId, 'masked_id'); + $quoteId = $quoteIdMask->getQuoteId(); + $quoteId = (int)$quoteId; + try { + $redeemedGiftcardsJson = $this->giftcardPayment->fetchRedeemedGiftcards($quoteId); + $redeemedGiftcardsData = $this->jsonSerializer->unserialize($redeemedGiftcardsJson); + } catch (\Exception $e) { + throw new GraphQlInputException(__('An error occurred while fetching redeemed gift cards: %1', $e->getMessage())); + } + + return [ 'giftcards' => $redeemedGiftcardsData['redeemedGiftcards']]; // Adjust according to your schema + } +} + + diff --git a/etc/schema.graphqls b/etc/schema.graphqls index fb4628747..7b879257c 100644 --- a/etc/schema.graphqls +++ b/etc/schema.graphqls @@ -22,22 +22,20 @@ type Mutation { ) : AdyenPaymentStatus @resolver(class: "Adyen\\Payment\\Model\\Resolver\\GetAdyenPaymentDetails") adyenRedeemedGiftcards( - cartId: Int! @doc(description: "Cart ID for which to fetch redeemed gift cards.") + cartId: String! @doc(description: "Cart ID for which to fetch redeemed gift cards.") ): AdyenRedeemedGiftcardsResponse @resolver(class: "Adyen\\Payment\\Model\\Resolver\\GetAdyenRedeemedGiftcards") adyenSaveStateData( stateData: String! @doc(description: "JSON string of Adyen state data."), - cartId: Int! @doc(description: "Cart ID associated with the state data.") + cartId: String! @doc(description: "Cart ID associated with the state data.") ): AdyenStateDataResponse @resolver(class: "Adyen\\Payment\\Model\\Resolver\\SaveAdyenStateData") adyenRemoveStateData( stateDataId: Int! @doc(description: "ID of the state data to remove."), - cartId: Int! @doc(description: "Cart ID associated with the state data.") + cartId: String! @doc(description: "Cart ID associated with the state data.") ): AdyenStateDataResponse @resolver(class: "Adyen\\Payment\\Model\\Resolver\\RemoveAdyenStateData") } -} - type AdyenPaymentStatus { isFinal: Boolean @doc(description: "If True, no further action is required and customer should be redirect to success page.") resultCode: String @doc(description: "Current state of the order in Adyen.") From a930a30ff5972d1458661c48df57ffd7d58b0369 Mon Sep 17 00:00:00 2001 From: Can Demiralp Date: Tue, 19 Mar 2024 21:36:40 +0100 Subject: [PATCH 04/17] [ECP-8972] Implement save/remove gift card mutations and update schema definitions --- Api/AdyenStateDataInterface.php | 8 +- Api/GuestAdyenStateDataInterface.php | 7 +- Helper/StateData.php | 14 ++- Model/Api/AdyenStateData.php | 5 +- Model/Api/GuestAdyenStateData.php | 8 +- .../GetAdyenPaymentMethodsBalance.php | 45 +++++---- Model/Resolver/GetAdyenRedeemedGiftcards.php | 54 ++++++++--- Model/Resolver/RemoveAdyenStateData.php | 93 +++++++++++++++++++ Model/Resolver/SaveAdyenStateData.php | 87 +++++++++++++++++ etc/schema.graphqls | 45 +++++---- 10 files changed, 303 insertions(+), 63 deletions(-) create mode 100644 Model/Resolver/RemoveAdyenStateData.php create mode 100644 Model/Resolver/SaveAdyenStateData.php diff --git a/Api/AdyenStateDataInterface.php b/Api/AdyenStateDataInterface.php index 8dbf658e3..db86c55df 100644 --- a/Api/AdyenStateDataInterface.php +++ b/Api/AdyenStateDataInterface.php @@ -18,13 +18,15 @@ interface AdyenStateDataInterface { /** - * Persist the Adyen state data for the quote so it can be used in the payment request + * Persist the Adyen state data for the quote and returns the stateDataId. + * So it can be used in the payment request. + * * * @param string $stateData * @param int $cartId - * @return void + * @return int */ - public function save(string $stateData, int $cartId): void; + public function save(string $stateData, int $cartId): int; /** * Removes the Adyen state data with the given entity id diff --git a/Api/GuestAdyenStateDataInterface.php b/Api/GuestAdyenStateDataInterface.php index 199eebeb0..c3747e320 100644 --- a/Api/GuestAdyenStateDataInterface.php +++ b/Api/GuestAdyenStateDataInterface.php @@ -18,13 +18,14 @@ interface GuestAdyenStateDataInterface { /** - * Persist the Adyen state data for the quote so it can be used in the payment request + * Persist the Adyen state data for the quote and returns the stateDataId. + * So it can be used in the payment request. * * @param string $stateData * @param string $cartId - * @return void + * @return int */ - public function save(string $stateData, string $cartId): void; + public function save(string $stateData, string $cartId): int; /** * Removes the Adyen state data with the given entity id diff --git a/Helper/StateData.php b/Helper/StateData.php index 3a83fa8d2..a3675468c 100644 --- a/Helper/StateData.php +++ b/Helper/StateData.php @@ -16,7 +16,9 @@ use Adyen\Payment\Logger\AdyenLogger; use Adyen\Payment\Model\ResourceModel\StateData as StateDataResourceModel; use Adyen\Payment\Model\ResourceModel\StateData\Collection as StateDataCollection; +use Adyen\Payment\Model\StateData as AdyenStateData; use Adyen\Payment\Model\StateDataFactory; +use Magento\Framework\Exception\AlreadyExistsException; use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Exception\NoSuchEntityException; @@ -80,7 +82,14 @@ public function getPaymentMethodVariant(int $quoteId): string return $stateDataByQuoteId['paymentMethod']['type']; } - public function saveStateData(string $stateData, int $quoteId): void + /** + * @param string $stateData + * @param int $quoteId + * @return AdyenStateData + * @throws LocalizedException + * @throws AlreadyExistsException + */ + public function saveStateData(string $stateData, int $quoteId): AdyenStateData { // Decode payload from frontend $stateData = json_decode($stateData, true); @@ -92,10 +101,11 @@ public function saveStateData(string $stateData, int $quoteId): void $stateData = json_encode($this->checkoutStateDataValidator->getValidatedAdditionalData($stateData)); - /** @var \Adyen\Payment\Model\StateData $stateDataObj */ $stateDataObj = $this->stateDataFactory->create(); $stateDataObj->setQuoteId($quoteId)->setStateData((string)$stateData); $this->stateDataResourceModel->save($stateDataObj); + + return $stateDataObj; } /** diff --git a/Model/Api/AdyenStateData.php b/Model/Api/AdyenStateData.php index 4233fe026..aebba70fa 100644 --- a/Model/Api/AdyenStateData.php +++ b/Model/Api/AdyenStateData.php @@ -25,9 +25,10 @@ public function __construct( $this->stateDataHelper = $stateDataHelper; } - public function save(string $stateData, int $quoteId): void + public function save(string $stateData, int $quoteId): int { - $this->stateDataHelper->saveStateData($stateData, $quoteId); + $stateData = $this->stateDataHelper->saveStateData($stateData, $quoteId); + return $stateData->getEntityId(); } public function remove(int $stateDataId, int $quoteId): bool diff --git a/Model/Api/GuestAdyenStateData.php b/Model/Api/GuestAdyenStateData.php index 0e2f02cfe..a192d1520 100644 --- a/Model/Api/GuestAdyenStateData.php +++ b/Model/Api/GuestAdyenStateData.php @@ -33,14 +33,18 @@ public function __construct( } /** + * @param string $stateData + * @param string $cartId + * @return int * @throws InputException * @throws LocalizedException */ - public function save(string $stateData, string $cartId): void + public function save(string $stateData, string $cartId): int { $quoteId = $this->getQuoteIdFromMaskedCartId($cartId); + $stateData = $this->stateDataHelper->saveStateData($stateData, $quoteId); - $this->stateDataHelper->saveStateData($stateData, $quoteId); + return $stateData->getEntityId(); } /** diff --git a/Model/Resolver/GetAdyenPaymentMethodsBalance.php b/Model/Resolver/GetAdyenPaymentMethodsBalance.php index 80b516c18..bd5baf4b6 100644 --- a/Model/Resolver/GetAdyenPaymentMethodsBalance.php +++ b/Model/Resolver/GetAdyenPaymentMethodsBalance.php @@ -3,7 +3,7 @@ * * Adyen Payment Module * - * Copyright (c) 2021 Adyen B.V. + * Copyright (c) 2024 Adyen N.V. * This file is open source and available under the MIT license. * See the LICENSE file for more info. * @@ -16,35 +16,45 @@ use Adyen\Payment\Exception\GraphQlAdyenException; use Adyen\Payment\Model\Api\AdyenPaymentMethodsBalance; use Exception; -use Magento\Framework\Exception\LocalizedException; use Magento\Framework\GraphQl\Config\Element\Field; use Magento\Framework\GraphQl\Exception\GraphQlInputException; use Magento\Framework\GraphQl\Query\ResolverInterface; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; -use Magento\Framework\Serialize\Serializer\Json; class GetAdyenPaymentMethodsBalance implements ResolverInterface { + /** + * @var AdyenPaymentMethodsBalance + */ private AdyenPaymentMethodsBalance $balance; - private Json $jsonSerializer; + /** + * @param AdyenPaymentMethodsBalance $balance + */ public function __construct( - AdyenPaymentMethodsBalance $balance, - Json $jsonSerializer, - ) - { + AdyenPaymentMethodsBalance $balance + ) { $this->balance = $balance; - $this->jsonSerializer = $jsonSerializer; } + /** + * @param Field $field + * @param $context + * @param ResolveInfo $info + * @param array|null $value + * @param array|null $args + * @return array + * @throws GraphQlAdyenException + * @throws GraphQlInputException + * @throws Exception + */ public function resolve( Field $field, $context, ResolveInfo $info, array $value = null, array $args = null - ) - { + ): array { if (empty($args['payload'])) { throw new GraphQlInputException(__('Required parameter "payload" is missing')); } @@ -52,17 +62,14 @@ public function resolve( $payload = $args['payload']; try { $balanceResponse = $this->balance->getBalance($payload); - } catch (LocalizedException $e) { - throw new GraphQlAdyenException(__($e->getMessage()), $e); } catch (Exception $e) { - throw new GraphQlAdyenException(__('An error occurred while fetching the payment method balance.'), $e); + throw new GraphQlAdyenException( + __('An error occurred while fetching the payment method balance.'), + $e + ); } - if (json_last_error() !== JSON_ERROR_NONE) { - throw new \Exception('Error decoding the payment methods balance response'); - } - - return ['balance' => $balanceResponse]; + return ['balanceResponse' => $balanceResponse]; } } diff --git a/Model/Resolver/GetAdyenRedeemedGiftcards.php b/Model/Resolver/GetAdyenRedeemedGiftcards.php index c8728f870..82b4ceec2 100644 --- a/Model/Resolver/GetAdyenRedeemedGiftcards.php +++ b/Model/Resolver/GetAdyenRedeemedGiftcards.php @@ -3,7 +3,7 @@ * * Adyen Payment Module * - * Copyright (c) 2021 Adyen B.V. + * Copyright (c) 2024 Adyen N.V. * This file is open source and available under the MIT license. * See the LICENSE file for more info. * @@ -13,11 +13,8 @@ namespace Adyen\Payment\Model\Resolver; -use Adyen\Payment\Exception\GraphQlAdyenException; use Adyen\Payment\Helper\GiftcardPayment; -use Exception; use Magento\Quote\Model\QuoteIdMaskFactory; -use Magento\Framework\Exception\LocalizedException; use Magento\Framework\GraphQl\Config\Element\Field; use Magento\Framework\GraphQl\Exception\GraphQlInputException; use Magento\Framework\GraphQl\Query\ResolverInterface; @@ -26,46 +23,75 @@ class GetAdyenRedeemedGiftcards implements ResolverInterface { + /** + * @var GiftcardPayment + */ private GiftcardPayment $giftcardPayment; + + /** + * @var Json + */ private Json $jsonSerializer; - private QuoteIdMaskFactory $quoteIdMaskFactory; + /** + * @var QuoteIdMaskFactory + */ + private QuoteIdMaskFactory $quoteIdMaskFactory; + /** + * @param GiftcardPayment $giftcardPayment + * @param Json $jsonSerializer + * @param QuoteIdMaskFactory $quoteIdMaskFactory + */ public function __construct( GiftcardPayment $giftcardPayment, Json $jsonSerializer, QuoteIdMaskFactory $quoteIdMaskFactory - ) - { + ) { $this->giftcardPayment = $giftcardPayment; $this->jsonSerializer = $jsonSerializer; $this->quoteIdMaskFactory = $quoteIdMaskFactory; } + /** + * @param Field $field + * @param $context + * @param ResolveInfo $info + * @param array|null $value + * @param array|null $args + * @return array + * @throws GraphQlInputException + */ public function resolve( Field $field, $context, ResolveInfo $info, array $value = null, array $args = null - ) - { + ) { if (empty($args['cartId'])) { throw new GraphQlInputException(__('Required parameter "cartId" is missing')); } + $cartId = $args['cartId']; $quoteIdMask = $this->quoteIdMaskFactory->create()->load($cartId, 'masked_id'); $quoteId = $quoteIdMask->getQuoteId(); $quoteId = (int)$quoteId; + try { $redeemedGiftcardsJson = $this->giftcardPayment->fetchRedeemedGiftcards($quoteId); - $redeemedGiftcardsData = $this->jsonSerializer->unserialize($redeemedGiftcardsJson); } catch (\Exception $e) { - throw new GraphQlInputException(__('An error occurred while fetching redeemed gift cards: %1', $e->getMessage())); + throw new GraphQlInputException( + __('An error occurred while fetching redeemed gift cards: %1', $e->getMessage()) + ); } - return [ 'giftcards' => $redeemedGiftcardsData['redeemedGiftcards']]; // Adjust according to your schema + $redeemedGiftcardsData = $this->jsonSerializer->unserialize($redeemedGiftcardsJson); + + return [ + 'redeemedGiftcards' => $redeemedGiftcardsData['redeemedGiftcards'], + 'remainingAmount' => $redeemedGiftcardsData['remainingAmount'], + 'totalDiscount' => $redeemedGiftcardsData['totalDiscount'] + ]; } } - - diff --git a/Model/Resolver/RemoveAdyenStateData.php b/Model/Resolver/RemoveAdyenStateData.php new file mode 100644 index 000000000..494dc36c2 --- /dev/null +++ b/Model/Resolver/RemoveAdyenStateData.php @@ -0,0 +1,93 @@ + + */ +declare(strict_types=1); + +namespace Adyen\Payment\Model\Resolver; + +use Adyen\Payment\Exception\GraphQlAdyenException; +use Adyen\Payment\Model\Api\AdyenStateData; +use Exception; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\GraphQl\Config\Element\Field; +use Magento\Framework\GraphQl\Exception\GraphQlInputException; +use Magento\Framework\GraphQl\Query\ResolverInterface; +use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; +use Magento\Quote\Model\QuoteIdMaskFactory; + +class RemoveAdyenStateData implements ResolverInterface +{ + /** + * @var AdyenStateData + */ + private AdyenStateData $adyenStateData; + + /** + * @var QuoteIdMaskFactory + */ + private QuoteIdMaskFactory $quoteIdMaskFactory; + + /** + * @param AdyenStateData $adyenStateData + * @param QuoteIdMaskFactory $quoteIdMaskFactory + */ + public function __construct( + AdyenStateData $adyenStateData, + QuoteIdMaskFactory $quoteIdMaskFactory + ) { + $this->adyenStateData = $adyenStateData; + $this->quoteIdMaskFactory = $quoteIdMaskFactory; + } + + /** + * @param Field $field + * @param $context + * @param ResolveInfo $info + * @param array|null $value + * @param array|null $args + * @return array + * @throws GraphQlAdyenException + * @throws GraphQlInputException + * @throws LocalizedException + */ + public function resolve( + Field $field, + $context, + ResolveInfo $info, + array $value = null, + array $args = null + ): array { + if (empty($args['stateDataId'])) { + throw new GraphQlInputException(__('Required parameter "stateDataId" is missing')); + } + + if (empty($args['cartId'])) { + throw new GraphQlInputException(__('Required parameter "cartId" is missing')); + } + + $quoteIdMask = $this->quoteIdMaskFactory->create()->load($args['cartId'], 'masked_id'); + $quoteId = $quoteIdMask->getQuoteId(); + + try { + $result = $this->adyenStateData->remove((int) $args['stateDataId'], (int) $quoteId); + } catch (Exception $e) { + throw new GraphQlAdyenException(__('An error occurred while removing the state data.'), $e); + } + + if (!$result) { + throw new LocalizedException(__('An error occurred while removing the state data.')); + } + + return ['stateDataId' => $args['stateDataId']]; + } +} + + diff --git a/Model/Resolver/SaveAdyenStateData.php b/Model/Resolver/SaveAdyenStateData.php new file mode 100644 index 000000000..7fc1c2bee --- /dev/null +++ b/Model/Resolver/SaveAdyenStateData.php @@ -0,0 +1,87 @@ + + */ +declare(strict_types=1); + +namespace Adyen\Payment\Model\Resolver; + +use Adyen\Payment\Exception\GraphQlAdyenException; +use Adyen\Payment\Model\Api\AdyenStateData; +use Exception; +use Magento\Framework\GraphQl\Config\Element\Field; +use Magento\Framework\GraphQl\Exception\GraphQlInputException; +use Magento\Framework\GraphQl\Query\ResolverInterface; +use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; +use Magento\Quote\Model\QuoteIdMaskFactory; + +class SaveAdyenStateData implements ResolverInterface +{ + /** + * @var AdyenStateData + */ + private AdyenStateData $adyenStateData; + + /** + * @var QuoteIdMaskFactory + */ + private QuoteIdMaskFactory $quoteIdMaskFactory; + + /** + * @param AdyenStateData $adyenStateData + * @param QuoteIdMaskFactory $quoteIdMaskFactory + */ + public function __construct( + AdyenStateData $adyenStateData, + QuoteIdMaskFactory $quoteIdMaskFactory + ) { + $this->adyenStateData = $adyenStateData; + $this->quoteIdMaskFactory = $quoteIdMaskFactory; + } + + /** + * @param Field $field + * @param $context + * @param ResolveInfo $info + * @param array|null $value + * @param array|null $args + * @return array + * @throws GraphQlAdyenException + * @throws GraphQlInputException + */ + public function resolve( + Field $field, + $context, + ResolveInfo $info, + array $value = null, + array $args = null + ): array { + if (empty($args['stateData'])) { + throw new GraphQlInputException(__('Required parameter "stateData" is missing')); + } + + if (empty($args['cartId'])) { + throw new GraphQlInputException(__('Required parameter "cartId" is missing')); + } + + $quoteIdMask = $this->quoteIdMaskFactory->create()->load($args['cartId'], 'masked_id'); + $quoteId = $quoteIdMask->getQuoteId(); + + try { + $stateDataId = $this->adyenStateData->save($args['stateData'], (int) $quoteId); + } catch (Exception $e) { + throw new GraphQlAdyenException(__('An error occurred while saving the state data.'), $e); + } + + return ['stateDataId' => $stateDataId]; + } +} + + diff --git a/etc/schema.graphqls b/etc/schema.graphqls index 7b879257c..21cc07b11 100644 --- a/etc/schema.graphqls +++ b/etc/schema.graphqls @@ -10,9 +10,13 @@ type Query { country: String @doc(description: "Country code to be used in paymentMethods call, e.g. US") ) : AdyenPaymentMethods @resolver(class: "Adyen\\Payment\\Model\\Resolver\\GetAdyenPaymentMethods") - adyenPaymentMethodsBalance( - payload: String! @doc(description: "Payload JSON String with orderId, details, paymentData") + adyenPaymentMethodsBalance ( + payload: String! @doc(description: "JSON encoded payload with giftcard state data and amount.") ): AdyenPaymentMethodsBalanceResponse @resolver(class: "Adyen\\Payment\\Model\\Resolver\\GetAdyenPaymentMethodsBalance") + + adyenRedeemedGiftcards ( + cartId: String! @doc(description: "Cart ID for which to fetch redeemed gift cards.") + ): AdyenRedeemedGiftcardsResponse @resolver(class: "Adyen\\Payment\\Model\\Resolver\\GetAdyenRedeemedGiftcards") } type Mutation { @@ -21,19 +25,15 @@ type Mutation { cart_id: String! @doc(description: "Cart ID.") ) : AdyenPaymentStatus @resolver(class: "Adyen\\Payment\\Model\\Resolver\\GetAdyenPaymentDetails") - adyenRedeemedGiftcards( - cartId: String! @doc(description: "Cart ID for which to fetch redeemed gift cards.") - ): AdyenRedeemedGiftcardsResponse @resolver(class: "Adyen\\Payment\\Model\\Resolver\\GetAdyenRedeemedGiftcards") - - adyenSaveStateData( - stateData: String! @doc(description: "JSON string of Adyen state data."), + adyenSaveStateData ( + stateData: String! @doc(description: "JSON string of Adyen state data.") cartId: String! @doc(description: "Cart ID associated with the state data.") - ): AdyenStateDataResponse @resolver(class: "Adyen\\Payment\\Model\\Resolver\\SaveAdyenStateData") + ): AdyenStateData @resolver(class: "Adyen\\Payment\\Model\\Resolver\\SaveAdyenStateData") adyenRemoveStateData( - stateDataId: Int! @doc(description: "ID of the state data to remove."), + stateDataId: Int! @doc(description: "ID of the state data to remove.") cartId: String! @doc(description: "Cart ID associated with the state data.") - ): AdyenStateDataResponse @resolver(class: "Adyen\\Payment\\Model\\Resolver\\RemoveAdyenStateData") + ): AdyenStateData @resolver(class: "Adyen\\Payment\\Model\\Resolver\\RemoveAdyenStateData") } type AdyenPaymentStatus { @@ -109,21 +109,30 @@ type AdyenPaymentMethodsDetailsItems { } type AdyenPaymentMethodsBalanceResponse { - balance: String @doc(description: "Balance of the payment method.") + balanceResponse: String @doc(description: "Balance of the payment method.") } type AdyenRedeemedGiftcardsResponse { - giftcards: [AdyenGiftcard] @doc(description: "List of redeemed gift cards.") + redeemedGiftcards: [AdyenGiftcard] @doc(description: "List of redeemed gift cards.") + remainingAmount: String @doc(description: "Remaining order amount after giftcard discount.") + totalDiscount: String @doc(description: "Total giftcard discount applied to the cart.") +} + +type PaymentMethodBalance { + currency: String @doc(description: "Cart currency") + value: String @doc(description: "Total cart amount") } type AdyenGiftcard { - id: String @doc(description: "Gift card identifier.") - balance: String @doc(description: "Remaining balance on the gift card.") + stateDataId: String @doc(description: "Gift card identifier.") + brand: String @doc(description: "Gift card brand") + title: String @doc(description: "Gift card payment method title") + balance: PaymentMethodBalance @doc(description: "Remaining balance on the gift card.") + deductedAmount: String @doc(description: "Deducted balance from the gift card") } -type AdyenStateDataResponse { - success: Boolean @doc(description: "Indicates if the operation was successful.") - message: String @doc(description: "Additional message related to the operation outcome.") +type AdyenStateData { + stateDataId: String @doc(description: "ID of the inserted stateData object.") } type AdyenPaymentMethodsExtraDetails { From 2ce2568c009217255af87fac46a4ab05702d271d Mon Sep 17 00:00:00 2001 From: raoulritter <59527829+raoulritter@users.noreply.github.com> Date: Wed, 20 Mar 2024 13:57:18 +0100 Subject: [PATCH 05/17] First Test GetBalance --- .../GetAdyenPaymentMethodsBalanceTest.php | 101 ++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 Test/Unit/Model/Resolver/GetAdyenPaymentMethodsBalanceTest.php diff --git a/Test/Unit/Model/Resolver/GetAdyenPaymentMethodsBalanceTest.php b/Test/Unit/Model/Resolver/GetAdyenPaymentMethodsBalanceTest.php new file mode 100644 index 000000000..bdbcefcad --- /dev/null +++ b/Test/Unit/Model/Resolver/GetAdyenPaymentMethodsBalanceTest.php @@ -0,0 +1,101 @@ + + */ +namespace Adyen\Payment\Test\Model\Resolver; + +use Adyen\Payment\Model\Api\AdyenPaymentMethodsBalance; +use Adyen\Payment\Model\Resolver\GetAdyenPaymentMethodsBalance; +//use GraphQL\Type\Definition\ResolveInfo; +use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; +use PHPUnit\Framework\TestCase; +use Magento\Framework\GraphQl\Query\Fields; +use Magento\Framework\GraphQl\Exception\GraphQlInputException; +use Adyen\Payment\Api\AdyenPaymentMethodsBalanceInterface; +use Adyen\Payment\Exception\GraphQlAdyenException; +use Adyen\Payment\Test\Unit\AbstractAdyenTestCase; +use Magento\Framework\GraphQl\Query; +use PHPUnit\Framework\MockObject\MockObject; +use Exception; + +class GetAdyenPaymentMethodsBalanceTest extends TestCase +{ + private $balanceMock; + private $getAdyenPaymentMethodsBalance; + + + + protected function setUp(): void + { + $this->balanceMock = $this->createMock(AdyenPaymentMethodsBalance::class); + + $this->getAdyenPaymentMethodsBalance = new GetAdyenPaymentMethodsBalance( + $this->balanceMock + ); + } + + public function testWithMissingPayloadArgument() + { + $this->expectException(GraphQlInputException::class); + $this->expectExceptionMessage('Required parameter "payload" is missing'); + + $fieldMock = $this->createMock(\Magento\Framework\GraphQl\Config\Element\Field::class); + $contextMock = $this->createMock(\Magento\Framework\GraphQl\Config\Element\Field::class); + $resolveInfoMock = $this->createMock(\Magento\Framework\GraphQl\Schema\Type\ResolveInfo::class); + + $this->getAdyenPaymentMethodsBalance->resolve($fieldMock, $contextMock, $resolveInfoMock, [], []); + } + + public function testWithValidPayloadArgument() + { + $payload = '{\"paymentMethod\":{\"type\":\"giftcard\",\"brand\":\"svs\",\"encryptedCardNumber\":\"abc…\",\"encryptedSecurityCode\":\"xyz…\"},\"amount\":{\"currency\":\"EUR\",\"value\":1000}}'; + $args = ['payload' => $payload]; + $expectedBalanceResponse = '10'; + + $fieldMock = $this->createMock(\Magento\Framework\GraphQl\Config\Element\Field::class); + $contextMock = $this->createMock(\Magento\Framework\GraphQl\Config\Element\Field::class); + $resolveInfoMock = $this->createMock(ResolveInfo::class); + + $this->balanceMock->expects($this->once()) + ->method('getBalance') + ->with($payload) + ->willReturn($expectedBalanceResponse); + + $result = $this->getAdyenPaymentMethodsBalance->resolve($fieldMock, $contextMock, $resolveInfoMock, [], $args); + + $this->assertEquals(['balanceResponse' => $expectedBalanceResponse], $result); + } + +// public function testWithAdyensAPIReturningAnError() +// { +// $payload = '{"some":"data"}'; +// $args = ['payload' => $payload]; +// +// $this->balanceMock->expects($this->once()) +// ->method('getBalance') +// ->with($payload) +// ->willThrowException(new Exception('API Error')); +// +// $this->expectException(GraphQlAdyenException::class); +// $this->expectExceptionMessage('An error occurred while fetching the payment method balance.'); +// +// $fieldMock = $this->createMock(\Magento\Framework\GraphQl\Query\Fields::class); +// $contextMock = $this->createMock(\Magento\Framework\GraphQl\Config\Element\Field::class); +// $resolveInfoMock = $this->createMock(ResolveInfo::class); +// +// $result = $this->getAdyenPaymentMethodsBalance->resolve($fieldMock, $contextMock, $resolveInfoMock, [], $args); +// } + + +} + + + + + From e69e72ef69142e0a9a8007957614481314248d5c Mon Sep 17 00:00:00 2001 From: raoulritter <59527829+raoulritter@users.noreply.github.com> Date: Wed, 20 Mar 2024 15:08:40 +0100 Subject: [PATCH 06/17] Test Redeemed Giftcards --- .../GetAdyenRedeemedGiftcardsTest.php | 90 +++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 Test/Unit/Model/Resolver/GetAdyenRedeemedGiftcardsTest.php diff --git a/Test/Unit/Model/Resolver/GetAdyenRedeemedGiftcardsTest.php b/Test/Unit/Model/Resolver/GetAdyenRedeemedGiftcardsTest.php new file mode 100644 index 000000000..63dcdb073 --- /dev/null +++ b/Test/Unit/Model/Resolver/GetAdyenRedeemedGiftcardsTest.php @@ -0,0 +1,90 @@ + + */ +namespace Adyen\Payment\Test\Model\Resolver; + +use Adyen\Payment\Helper\GiftcardPayment; +use Adyen\Payment\Model\Resolver\GetAdyenRedeemedGiftcards; +use Adyen\Payment\Test\Unit\AbstractAdyenTestCase; +use PHPUnit\Framework\TestCase; +use Magento\Framework\Serialize\Serializer\Json; +use Magento\Quote\Model\QuoteIdMask; +use Magento\Quote\Model\QuoteIdMaskFactory; +use Adyen\Payment\Test\Model\Resolver\Order; +use Magento\Framework\GraphQl\Exception\GraphQlInputException; + +class GetAdyenRedeemedGiftcardsTest extends AbstractAdyenTestCase +{ + private $giftcardPaymentMock; + private $jsonSerializerMock; + private $quoteIdMaskFactoryMock; + private $quoteIdMaskMock; + private $getAdyenRedeemedGiftcards; + + protected function setUp(): void + { + $this->giftcardPaymentMock = $this->createMock(GiftcardPayment::class); + $this->jsonSerializerMock = $this->createMock(Json::class); + $this->quoteIdMaskFactoryMock = $this->createMock(QuoteIdMaskFactory::class); + $this->quoteIdMaskMock = $this->createMock(QuoteIdMask::class); + + $this->quoteIdMaskFactoryMock->method('create')->willReturn($this->quoteIdMaskMock); + + $this->getAdyenRedeemedGiftcards = new GetAdyenRedeemedGiftcards( + $this->giftcardPaymentMock, + $this->jsonSerializerMock, + $this->quoteIdMaskFactoryMock + ); + } + + public function testSuccessfulRetrievalOfRedeemedGiftCardDetailsWithValidCartId() + { + + $fieldMock = $this->createMock(\Magento\Framework\GraphQl\Config\Element\Field::class); + $contextMock = $this->createMock(\Magento\Framework\GraphQl\Config\Element\Field::class); + $resolveInfoMock = $this->createMock(\Magento\Framework\GraphQl\Schema\Type\ResolveInfo::class); + + + $cartId = 'test_cart_id'; + $quoteId = 0; + $args = ['cartId' => $cartId]; + $redeemedGiftcardsJson = '{"redeemedGiftcards": [], "remainingAmount": 100, "totalDiscount": 10}'; + $redeemedGiftcardsData = json_decode($redeemedGiftcardsJson, true); + + $this->quoteIdMaskMock->expects($this->once()) + ->method('load') + ->with($cartId, 'masked_id') + ->willReturn($this->quoteIdMaskMock); + + $this->quoteIdMaskMock = $this->createGeneratedMock(QuoteIdMask::class, ['load', 'getQuoteId']); + $this->quoteIdMaskMock->method('load')->willReturn($this->quoteIdMaskMock); + $this->quoteIdMaskMock->method('getQuoteId')->willReturn(1); + + $this->giftcardPaymentMock->expects($this->once()) + ->method('fetchRedeemedGiftcards') + ->with($quoteId) + ->willReturn($redeemedGiftcardsJson); + + $this->jsonSerializerMock->expects($this->once()) + ->method('unserialize') + ->with($redeemedGiftcardsJson) + ->willReturn($redeemedGiftcardsData); + + + $result = $this->getAdyenRedeemedGiftcards->resolve($fieldMock, $contextMock, $resolveInfoMock, [], $args); + + $this->assertEquals(['redeemedGiftcards' => $redeemedGiftcardsData['redeemedGiftcards'], 'remainingAmount' => $redeemedGiftcardsData['remainingAmount'], 'totalDiscount' => $redeemedGiftcardsData['totalDiscount']], $result); + } +} + + + + + From 0b1e4445d7042a0c129b90d579db9967c13d90d7 Mon Sep 17 00:00:00 2001 From: raoulritter <59527829+raoulritter@users.noreply.github.com> Date: Wed, 20 Mar 2024 16:10:46 +0100 Subject: [PATCH 07/17] test update --- Test/Unit/Model/Api/GuestAdyenStateDataTest.php | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/Test/Unit/Model/Api/GuestAdyenStateDataTest.php b/Test/Unit/Model/Api/GuestAdyenStateDataTest.php index d2a9110a9..29809c43d 100644 --- a/Test/Unit/Model/Api/GuestAdyenStateDataTest.php +++ b/Test/Unit/Model/Api/GuestAdyenStateDataTest.php @@ -14,6 +14,7 @@ use Adyen\Payment\Helper\StateData; use Adyen\Payment\Model\Api\GuestAdyenStateData; use Adyen\Payment\Test\Unit\AbstractAdyenTestCase; +use Magento\Framework\App\State; use Magento\Framework\Exception\InputException; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; use Magento\Quote\Model\QuoteIdMask; @@ -51,10 +52,16 @@ public function testSaveSuccessful() $stateData = '{"stateData":"dummyData"}'; $cartId = 'ABC123456789'; + $stateDataMock = $this->createMock(\Adyen\Payment\Model\StateData::class); + $stateDataMock->method('getEntityId')->willReturn(1); + $this->quoteIdMaskFactoryMock->method('create')->willReturn($this->quoteIdMaskMock); - $this->stateDataHelperMock->expects($this->once())->method('saveStateData'); + $this->stateDataHelperMock->expects($this->once())->method('saveStateData')->willReturn($stateDataMock); + //should return int + $this->guestAdyenStateDataModel->save($stateData, $cartId); +// $this->assertIsInt($result); } public function testRemoveSuccessful() @@ -64,7 +71,6 @@ public function testRemoveSuccessful() $this->quoteIdMaskFactoryMock->method('create')->willReturn($this->quoteIdMaskMock); $this->stateDataHelperMock->expects($this->once())->method('removeStateData'); - $this->guestAdyenStateDataModel->remove($stateDataId, $cartId); } From bdf8cf92d15db157cd2567557f41e89e7591be55 Mon Sep 17 00:00:00 2001 From: raoulritter <59527829+raoulritter@users.noreply.github.com> Date: Wed, 20 Mar 2024 16:19:08 +0100 Subject: [PATCH 08/17] Cleanup --- .../Model/Api/GuestAdyenStateDataTest.php | 3 --- .../GetAdyenPaymentMethodsBalanceTest.php | 22 ------------------- 2 files changed, 25 deletions(-) diff --git a/Test/Unit/Model/Api/GuestAdyenStateDataTest.php b/Test/Unit/Model/Api/GuestAdyenStateDataTest.php index 29809c43d..5f6f42db1 100644 --- a/Test/Unit/Model/Api/GuestAdyenStateDataTest.php +++ b/Test/Unit/Model/Api/GuestAdyenStateDataTest.php @@ -57,11 +57,8 @@ public function testSaveSuccessful() $this->quoteIdMaskFactoryMock->method('create')->willReturn($this->quoteIdMaskMock); $this->stateDataHelperMock->expects($this->once())->method('saveStateData')->willReturn($stateDataMock); - //should return int - $this->guestAdyenStateDataModel->save($stateData, $cartId); -// $this->assertIsInt($result); } public function testRemoveSuccessful() diff --git a/Test/Unit/Model/Resolver/GetAdyenPaymentMethodsBalanceTest.php b/Test/Unit/Model/Resolver/GetAdyenPaymentMethodsBalanceTest.php index bdbcefcad..01f905318 100644 --- a/Test/Unit/Model/Resolver/GetAdyenPaymentMethodsBalanceTest.php +++ b/Test/Unit/Model/Resolver/GetAdyenPaymentMethodsBalanceTest.php @@ -71,28 +71,6 @@ public function testWithValidPayloadArgument() $this->assertEquals(['balanceResponse' => $expectedBalanceResponse], $result); } - -// public function testWithAdyensAPIReturningAnError() -// { -// $payload = '{"some":"data"}'; -// $args = ['payload' => $payload]; -// -// $this->balanceMock->expects($this->once()) -// ->method('getBalance') -// ->with($payload) -// ->willThrowException(new Exception('API Error')); -// -// $this->expectException(GraphQlAdyenException::class); -// $this->expectExceptionMessage('An error occurred while fetching the payment method balance.'); -// -// $fieldMock = $this->createMock(\Magento\Framework\GraphQl\Query\Fields::class); -// $contextMock = $this->createMock(\Magento\Framework\GraphQl\Config\Element\Field::class); -// $resolveInfoMock = $this->createMock(ResolveInfo::class); -// -// $result = $this->getAdyenPaymentMethodsBalance->resolve($fieldMock, $contextMock, $resolveInfoMock, [], $args); -// } - - } From 59c15f2d110b7cd9440cab4512279d1d26eb4580 Mon Sep 17 00:00:00 2001 From: raoulritter <59527829+raoulritter@users.noreply.github.com> Date: Wed, 20 Mar 2024 16:27:06 +0100 Subject: [PATCH 09/17] Cleanup --- Test/Unit/Model/Api/GuestAdyenStateDataTest.php | 1 - Test/Unit/Model/Resolver/GetAdyenRedeemedGiftcardsTest.php | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/Test/Unit/Model/Api/GuestAdyenStateDataTest.php b/Test/Unit/Model/Api/GuestAdyenStateDataTest.php index 5f6f42db1..b440dc058 100644 --- a/Test/Unit/Model/Api/GuestAdyenStateDataTest.php +++ b/Test/Unit/Model/Api/GuestAdyenStateDataTest.php @@ -14,7 +14,6 @@ use Adyen\Payment\Helper\StateData; use Adyen\Payment\Model\Api\GuestAdyenStateData; use Adyen\Payment\Test\Unit\AbstractAdyenTestCase; -use Magento\Framework\App\State; use Magento\Framework\Exception\InputException; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; use Magento\Quote\Model\QuoteIdMask; diff --git a/Test/Unit/Model/Resolver/GetAdyenRedeemedGiftcardsTest.php b/Test/Unit/Model/Resolver/GetAdyenRedeemedGiftcardsTest.php index 63dcdb073..f9d09d1b6 100644 --- a/Test/Unit/Model/Resolver/GetAdyenRedeemedGiftcardsTest.php +++ b/Test/Unit/Model/Resolver/GetAdyenRedeemedGiftcardsTest.php @@ -32,7 +32,7 @@ protected function setUp(): void { $this->giftcardPaymentMock = $this->createMock(GiftcardPayment::class); $this->jsonSerializerMock = $this->createMock(Json::class); - $this->quoteIdMaskFactoryMock = $this->createMock(QuoteIdMaskFactory::class); + $this->quoteIdMaskFactoryMock = $this->createGeneratedMock(QuoteIdMaskFactory::class); $this->quoteIdMaskMock = $this->createMock(QuoteIdMask::class); $this->quoteIdMaskFactoryMock->method('create')->willReturn($this->quoteIdMaskMock); From 4d99e0a600eeb06bd3118880d7ab2ce86cfc45d4 Mon Sep 17 00:00:00 2001 From: Can Demiralp Date: Wed, 20 Mar 2024 22:33:30 +0100 Subject: [PATCH 10/17] [ECP-8972] Fix unit tests --- .../GetAdyenRedeemedGiftcardsTest.php | 30 +++++++++++-------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/Test/Unit/Model/Resolver/GetAdyenRedeemedGiftcardsTest.php b/Test/Unit/Model/Resolver/GetAdyenRedeemedGiftcardsTest.php index f9d09d1b6..a44cf065d 100644 --- a/Test/Unit/Model/Resolver/GetAdyenRedeemedGiftcardsTest.php +++ b/Test/Unit/Model/Resolver/GetAdyenRedeemedGiftcardsTest.php @@ -13,12 +13,12 @@ use Adyen\Payment\Helper\GiftcardPayment; use Adyen\Payment\Model\Resolver\GetAdyenRedeemedGiftcards; use Adyen\Payment\Test\Unit\AbstractAdyenTestCase; -use PHPUnit\Framework\TestCase; +use Magento\Framework\GraphQl\Config\Element\Field; +use Magento\Framework\GraphQl\Query\Resolver\ContextInterface; +use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; use Magento\Framework\Serialize\Serializer\Json; use Magento\Quote\Model\QuoteIdMask; use Magento\Quote\Model\QuoteIdMaskFactory; -use Adyen\Payment\Test\Model\Resolver\Order; -use Magento\Framework\GraphQl\Exception\GraphQlInputException; class GetAdyenRedeemedGiftcardsTest extends AbstractAdyenTestCase { @@ -32,9 +32,11 @@ protected function setUp(): void { $this->giftcardPaymentMock = $this->createMock(GiftcardPayment::class); $this->jsonSerializerMock = $this->createMock(Json::class); - $this->quoteIdMaskFactoryMock = $this->createGeneratedMock(QuoteIdMaskFactory::class); + $this->quoteIdMaskFactoryMock = $this->createGeneratedMock( + QuoteIdMaskFactory::class, + ['create'] + ); $this->quoteIdMaskMock = $this->createMock(QuoteIdMask::class); - $this->quoteIdMaskFactoryMock->method('create')->willReturn($this->quoteIdMaskMock); $this->getAdyenRedeemedGiftcards = new GetAdyenRedeemedGiftcards( @@ -46,11 +48,9 @@ protected function setUp(): void public function testSuccessfulRetrievalOfRedeemedGiftCardDetailsWithValidCartId() { - - $fieldMock = $this->createMock(\Magento\Framework\GraphQl\Config\Element\Field::class); - $contextMock = $this->createMock(\Magento\Framework\GraphQl\Config\Element\Field::class); - $resolveInfoMock = $this->createMock(\Magento\Framework\GraphQl\Schema\Type\ResolveInfo::class); - + $fieldMock = $this->createMock(Field::class); + $contextMock = $this->createMock(ContextInterface::class); + $resolveInfoMock = $this->createMock(ResolveInfo::class); $cartId = 'test_cart_id'; $quoteId = 0; @@ -77,10 +77,16 @@ public function testSuccessfulRetrievalOfRedeemedGiftCardDetailsWithValidCartId( ->with($redeemedGiftcardsJson) ->willReturn($redeemedGiftcardsData); - $result = $this->getAdyenRedeemedGiftcards->resolve($fieldMock, $contextMock, $resolveInfoMock, [], $args); - $this->assertEquals(['redeemedGiftcards' => $redeemedGiftcardsData['redeemedGiftcards'], 'remainingAmount' => $redeemedGiftcardsData['remainingAmount'], 'totalDiscount' => $redeemedGiftcardsData['totalDiscount']], $result); + $this->assertEquals( + [ + 'redeemedGiftcards' => $redeemedGiftcardsData['redeemedGiftcards'], + 'remainingAmount' => $redeemedGiftcardsData['remainingAmount'], + 'totalDiscount' => $redeemedGiftcardsData['totalDiscount'] + ], + $result + ); } } From adef2ba5d206dd8a59e493e44d0e117dcae6fa8b Mon Sep 17 00:00:00 2001 From: Can Demiralp Date: Thu, 21 Mar 2024 14:11:53 +0100 Subject: [PATCH 11/17] [ECP-8972] Update argument names --- Model/Api/AdyenStateData.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Model/Api/AdyenStateData.php b/Model/Api/AdyenStateData.php index aebba70fa..9559466a0 100644 --- a/Model/Api/AdyenStateData.php +++ b/Model/Api/AdyenStateData.php @@ -25,14 +25,14 @@ public function __construct( $this->stateDataHelper = $stateDataHelper; } - public function save(string $stateData, int $quoteId): int + public function save(string $stateData, int $cartId): int { - $stateData = $this->stateDataHelper->saveStateData($stateData, $quoteId); + $stateData = $this->stateDataHelper->saveStateData($stateData, $cartId); return $stateData->getEntityId(); } - public function remove(int $stateDataId, int $quoteId): bool + public function remove(int $stateDataId, int $cartId): bool { - return $this->stateDataHelper->removeStateData($stateDataId, $quoteId); + return $this->stateDataHelper->removeStateData($stateDataId, $cartId); } } From 4b5145a542d37dbf8b6dff3daf5c46781aab5dde Mon Sep 17 00:00:00 2001 From: Can Demiralp Date: Thu, 21 Mar 2024 14:12:09 +0100 Subject: [PATCH 12/17] [ECP-8972] Replace exception type --- Model/Resolver/GetAdyenRedeemedGiftcards.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Model/Resolver/GetAdyenRedeemedGiftcards.php b/Model/Resolver/GetAdyenRedeemedGiftcards.php index 82b4ceec2..758174462 100644 --- a/Model/Resolver/GetAdyenRedeemedGiftcards.php +++ b/Model/Resolver/GetAdyenRedeemedGiftcards.php @@ -13,6 +13,7 @@ namespace Adyen\Payment\Model\Resolver; +use Adyen\Payment\Exception\GraphQlAdyenException; use Adyen\Payment\Helper\GiftcardPayment; use Magento\Quote\Model\QuoteIdMaskFactory; use Magento\Framework\GraphQl\Config\Element\Field; @@ -60,6 +61,7 @@ public function __construct( * @param array|null $value * @param array|null $args * @return array + * @throws GraphQlAdyenException * @throws GraphQlInputException */ public function resolve( @@ -81,7 +83,7 @@ public function resolve( try { $redeemedGiftcardsJson = $this->giftcardPayment->fetchRedeemedGiftcards($quoteId); } catch (\Exception $e) { - throw new GraphQlInputException( + throw new GraphQlAdyenException( __('An error occurred while fetching redeemed gift cards: %1', $e->getMessage()) ); } From edfeb1ee3389f00aaf8c4bb3ccbd9e99a42620c4 Mon Sep 17 00:00:00 2001 From: Can Demiralp Date: Thu, 21 Mar 2024 14:13:15 +0100 Subject: [PATCH 13/17] [ECP-8972] Refactor class name to match with the filename --- Test/Unit/Setup/RecurringDataTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Test/Unit/Setup/RecurringDataTest.php b/Test/Unit/Setup/RecurringDataTest.php index 142f21b58..51f9a45de 100644 --- a/Test/Unit/Setup/RecurringDataTest.php +++ b/Test/Unit/Setup/RecurringDataTest.php @@ -18,7 +18,7 @@ use Adyen\Payment\Helper\PaymentMethodsFactory; use Adyen\Payment\Helper\PaymentMethods; -class RecurringTest extends AbstractAdyenTestCase +class RecurringDataTest extends AbstractAdyenTestCase { private RecurringData $recurringData; From 03e901513a27ff37927bb46d0ad4b97eba19119a Mon Sep 17 00:00:00 2001 From: Can Demiralp Date: Thu, 21 Mar 2024 14:13:42 +0100 Subject: [PATCH 14/17] [ECP-8972] Update unit tests --- Test/Unit/Model/Api/AdyenStateDataTest.php | 59 ++++++ .../GetAdyenPaymentMethodsBalanceTest.php | 41 ++-- .../GetAdyenRedeemedGiftcardsTest.php | 47 ++++- .../Resolver/RemoveAdyenStateDataTest.php | 175 ++++++++++++++++++ .../Model/Resolver/SaveAdyenStateDataTest.php | 152 +++++++++++++++ 5 files changed, 451 insertions(+), 23 deletions(-) create mode 100644 Test/Unit/Model/Api/AdyenStateDataTest.php create mode 100644 Test/Unit/Model/Resolver/RemoveAdyenStateDataTest.php create mode 100644 Test/Unit/Model/Resolver/SaveAdyenStateDataTest.php diff --git a/Test/Unit/Model/Api/AdyenStateDataTest.php b/Test/Unit/Model/Api/AdyenStateDataTest.php new file mode 100644 index 000000000..ef8bddffb --- /dev/null +++ b/Test/Unit/Model/Api/AdyenStateDataTest.php @@ -0,0 +1,59 @@ + + */ + +namespace Adyen\Payment\Test\Unit\Model\Api; + +use Adyen\Payment\Helper\StateData; +use Adyen\Payment\Model\Api\AdyenStateData; +use Adyen\Payment\Test\Unit\AbstractAdyenTestCase; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; + +class AdyenStateDataTest extends AbstractAdyenTestCase +{ + private $objectManager; + private $stateDataHelperMock; + private $adyenStateDataModel; + + protected function setUp(): void + { + $this->objectManager = new ObjectManager($this); + + $this->stateDataHelperMock = $this->createMock(StateData::class); + + $this->adyenStateDataModel = $this->objectManager->getObject(AdyenStateData::class, [ + 'stateDataHelper' => $this->stateDataHelperMock + ]); + } + + public function testSaveSuccessful() + { + $stateData = '{"stateData":"dummyData"}'; + $cartId = 100; + + $stateDataMock = $this->createMock(\Adyen\Payment\Model\StateData::class); + $stateDataMock->method('getEntityId')->willReturn(1); + + $this->stateDataHelperMock->expects($this->once()) + ->method('saveStateData') + ->willReturn($stateDataMock); + + $this->adyenStateDataModel->save($stateData, $cartId); + } + + public function testRemoveSuccessful() + { + $stateDataId = 1; + $cartId = 100; + + $this->stateDataHelperMock->expects($this->once())->method('removeStateData'); + $this->adyenStateDataModel->remove($stateDataId, $cartId); + } +} diff --git a/Test/Unit/Model/Resolver/GetAdyenPaymentMethodsBalanceTest.php b/Test/Unit/Model/Resolver/GetAdyenPaymentMethodsBalanceTest.php index 01f905318..51002e804 100644 --- a/Test/Unit/Model/Resolver/GetAdyenPaymentMethodsBalanceTest.php +++ b/Test/Unit/Model/Resolver/GetAdyenPaymentMethodsBalanceTest.php @@ -12,28 +12,28 @@ use Adyen\Payment\Model\Api\AdyenPaymentMethodsBalance; use Adyen\Payment\Model\Resolver\GetAdyenPaymentMethodsBalance; -//use GraphQL\Type\Definition\ResolveInfo; +use Magento\Catalog\Model\Layer\ContextInterface; +use Magento\Framework\GraphQl\Config\Element\Field; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; use PHPUnit\Framework\TestCase; -use Magento\Framework\GraphQl\Query\Fields; use Magento\Framework\GraphQl\Exception\GraphQlInputException; -use Adyen\Payment\Api\AdyenPaymentMethodsBalanceInterface; use Adyen\Payment\Exception\GraphQlAdyenException; -use Adyen\Payment\Test\Unit\AbstractAdyenTestCase; use Magento\Framework\GraphQl\Query; -use PHPUnit\Framework\MockObject\MockObject; -use Exception; class GetAdyenPaymentMethodsBalanceTest extends TestCase { private $balanceMock; + private $contextMock; + private $fieldMock; + private $infoMock; private $getAdyenPaymentMethodsBalance; - - protected function setUp(): void { $this->balanceMock = $this->createMock(AdyenPaymentMethodsBalance::class); + $this->contextMock = $this->createMock(ContextInterface::class); + $this->fieldMock = $this->createMock(Field::class); + $this->infoMock = $this->createMock(ResolveInfo::class); $this->getAdyenPaymentMethodsBalance = new GetAdyenPaymentMethodsBalance( $this->balanceMock @@ -45,11 +45,7 @@ public function testWithMissingPayloadArgument() $this->expectException(GraphQlInputException::class); $this->expectExceptionMessage('Required parameter "payload" is missing'); - $fieldMock = $this->createMock(\Magento\Framework\GraphQl\Config\Element\Field::class); - $contextMock = $this->createMock(\Magento\Framework\GraphQl\Config\Element\Field::class); - $resolveInfoMock = $this->createMock(\Magento\Framework\GraphQl\Schema\Type\ResolveInfo::class); - - $this->getAdyenPaymentMethodsBalance->resolve($fieldMock, $contextMock, $resolveInfoMock, [], []); + $this->getAdyenPaymentMethodsBalance->resolve($this->fieldMock, $this->contextMock, $this->infoMock, [], []); } public function testWithValidPayloadArgument() @@ -58,19 +54,28 @@ public function testWithValidPayloadArgument() $args = ['payload' => $payload]; $expectedBalanceResponse = '10'; - $fieldMock = $this->createMock(\Magento\Framework\GraphQl\Config\Element\Field::class); - $contextMock = $this->createMock(\Magento\Framework\GraphQl\Config\Element\Field::class); - $resolveInfoMock = $this->createMock(ResolveInfo::class); - $this->balanceMock->expects($this->once()) ->method('getBalance') ->with($payload) ->willReturn($expectedBalanceResponse); - $result = $this->getAdyenPaymentMethodsBalance->resolve($fieldMock, $contextMock, $resolveInfoMock, [], $args); + $result = $this->getAdyenPaymentMethodsBalance->resolve($this->fieldMock, $this->contextMock, $this->infoMock, [], $args); $this->assertEquals(['balanceResponse' => $expectedBalanceResponse], $result); } + + public function testWithFailingApiCall() + { + $this->expectException(GraphQlAdyenException::class); + + $args = [ + 'payload' => "{}" + ]; + + $this->balanceMock->method('getBalance')->willThrowException(new \Exception()); + + $this->getAdyenPaymentMethodsBalance->resolve($this->fieldMock, $this->contextMock, $this->infoMock, [], $args); + } } diff --git a/Test/Unit/Model/Resolver/GetAdyenRedeemedGiftcardsTest.php b/Test/Unit/Model/Resolver/GetAdyenRedeemedGiftcardsTest.php index a44cf065d..3f491ec4a 100644 --- a/Test/Unit/Model/Resolver/GetAdyenRedeemedGiftcardsTest.php +++ b/Test/Unit/Model/Resolver/GetAdyenRedeemedGiftcardsTest.php @@ -10,10 +10,13 @@ */ namespace Adyen\Payment\Test\Model\Resolver; +use Adyen\Payment\Exception\GraphQlAdyenException; use Adyen\Payment\Helper\GiftcardPayment; use Adyen\Payment\Model\Resolver\GetAdyenRedeemedGiftcards; use Adyen\Payment\Test\Unit\AbstractAdyenTestCase; +use Exception; use Magento\Framework\GraphQl\Config\Element\Field; +use Magento\Framework\GraphQl\Exception\GraphQlInputException; use Magento\Framework\GraphQl\Query\Resolver\ContextInterface; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; use Magento\Framework\Serialize\Serializer\Json; @@ -27,6 +30,9 @@ class GetAdyenRedeemedGiftcardsTest extends AbstractAdyenTestCase private $quoteIdMaskFactoryMock; private $quoteIdMaskMock; private $getAdyenRedeemedGiftcards; + private $fieldMock; + private $contextMock; + private $resolveInfoMock; protected function setUp(): void { @@ -39,6 +45,10 @@ protected function setUp(): void $this->quoteIdMaskMock = $this->createMock(QuoteIdMask::class); $this->quoteIdMaskFactoryMock->method('create')->willReturn($this->quoteIdMaskMock); + $this->fieldMock = $this->createMock(Field::class); + $this->contextMock = $this->createMock(ContextInterface::class); + $this->resolveInfoMock = $this->createMock(ResolveInfo::class); + $this->getAdyenRedeemedGiftcards = new GetAdyenRedeemedGiftcards( $this->giftcardPaymentMock, $this->jsonSerializerMock, @@ -48,10 +58,6 @@ protected function setUp(): void public function testSuccessfulRetrievalOfRedeemedGiftCardDetailsWithValidCartId() { - $fieldMock = $this->createMock(Field::class); - $contextMock = $this->createMock(ContextInterface::class); - $resolveInfoMock = $this->createMock(ResolveInfo::class); - $cartId = 'test_cart_id'; $quoteId = 0; $args = ['cartId' => $cartId]; @@ -77,7 +83,7 @@ public function testSuccessfulRetrievalOfRedeemedGiftCardDetailsWithValidCartId( ->with($redeemedGiftcardsJson) ->willReturn($redeemedGiftcardsData); - $result = $this->getAdyenRedeemedGiftcards->resolve($fieldMock, $contextMock, $resolveInfoMock, [], $args); + $result = $this->getAdyenRedeemedGiftcards->resolve($this->fieldMock, $this->contextMock, $this->resolveInfoMock, [], $args); $this->assertEquals( [ @@ -88,6 +94,37 @@ public function testSuccessfulRetrievalOfRedeemedGiftCardDetailsWithValidCartId( $result ); } + + public function testFailedRetrievalOfRedeemedGiftCards() + { + $this->expectException(GraphQlAdyenException::class); + + $cartId = 'test_cart_id'; + $args = ['cartId' => $cartId]; + + $this->quoteIdMaskMock->expects($this->once()) + ->method('load') + ->with($cartId, 'masked_id') + ->willReturn($this->quoteIdMaskMock); + + $this->quoteIdMaskMock = $this->createGeneratedMock(QuoteIdMask::class, ['load', 'getQuoteId']); + $this->quoteIdMaskMock->method('load')->willReturn($this->quoteIdMaskMock); + $this->quoteIdMaskMock->method('getQuoteId')->willReturn(1); + + $this->giftcardPaymentMock->method('fetchRedeemedGiftcards') + ->willThrowException(new Exception()); + + $this->getAdyenRedeemedGiftcards->resolve($this->fieldMock, $this->contextMock, $this->resolveInfoMock, [], $args); + } + + public function testFailedRetrievalOfRedeemedGiftCardsWithNullCartId() + { + $this->expectException(GraphQlInputException::class); + + $args = ['cartId' => ""]; + + $this->getAdyenRedeemedGiftcards->resolve($this->fieldMock, $this->contextMock, $this->resolveInfoMock, [], $args); + } } diff --git a/Test/Unit/Model/Resolver/RemoveAdyenStateDataTest.php b/Test/Unit/Model/Resolver/RemoveAdyenStateDataTest.php new file mode 100644 index 000000000..ef72d0181 --- /dev/null +++ b/Test/Unit/Model/Resolver/RemoveAdyenStateDataTest.php @@ -0,0 +1,175 @@ + + */ +namespace Adyen\Payment\Test\Model\Resolver; + +use _PHPStan_3a7a22dbd\Nette\Neon\Exception; +use Adyen\Payment\Exception\GraphQlAdyenException; +use Adyen\Payment\Model\Api\AdyenStateData; +use Adyen\Payment\Model\Resolver\RemoveAdyenStateData; +use Adyen\Payment\Test\Unit\AbstractAdyenTestCase; +use Magento\Catalog\Model\Layer\ContextInterface; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\GraphQl\Config\Element\Field; +use Magento\Framework\GraphQl\Exception\GraphQlInputException; +use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Quote\Model\QuoteIdMask; +use Magento\Quote\Model\QuoteIdMaskFactory; + +class RemoveAdyenStateDataTest extends AbstractAdyenTestCase +{ + private $removeAdyenStateDataResolver; + private $adyenStateDataHelperMock; + private $quoteIdMaskFactoryMock; + private $quoteIdMaskMock; + private $fieldMock; + private $contextMock; + private $infoMock; + + public function setUp(): void + { + $this->objectManager = new ObjectManager($this); + + $this->adyenStateDataHelperMock = $this->createMock(AdyenStateData::class); + $this->fieldMock = $this->createMock(Field::class); + $this->contextMock = $this->createMock(ContextInterface::class); + $this->infoMock = $this->createMock(ResolveInfo::class); + + $this->quoteIdMaskMock = $this->createGeneratedMock(QuoteIdMask::class, ['load', 'getQuoteId']); + $this->quoteIdMaskMock->method('load')->willReturn($this->quoteIdMaskMock); + $this->quoteIdMaskMock->method('getQuoteId')->willReturn(1); + + $this->quoteIdMaskFactoryMock = $this->createGeneratedMock(QuoteIdMaskFactory::class, [ + 'create' + ]); + $this->quoteIdMaskFactoryMock->method('create')->willReturn($this->quoteIdMaskMock); + + $this->removeAdyenStateDataResolver = $this->objectManager->getObject(RemoveAdyenStateData::class, [ + 'adyenStateData' => $this->adyenStateDataHelperMock, + 'quoteIdMaskFactory' => $this->quoteIdMaskFactoryMock + ]); + } + + public function testResolve() + { + $stateDataId = 1; + + $args = [ + 'stateDataId' => $stateDataId, + 'cartId' => 1 + ]; + + $this->adyenStateDataHelperMock->expects($this->once())->method('remove')->willReturn(true); + + $result = $this->removeAdyenStateDataResolver->resolve( + $this->fieldMock, + $this->contextMock, + $this->infoMock, + null, + $args + ); + + $this->assertArrayHasKey('stateDataId', $result); + $this->assertEquals($stateDataId, $result['stateDataId']); + } + + public function testResolveWithLocalizedException() + { + $this->expectException(LocalizedException::class); + + $stateDataId = 1; + + $args = [ + 'stateDataId' => $stateDataId, + 'cartId' => 1 + ]; + + $this->adyenStateDataHelperMock->expects($this->once())->method('remove')->willReturn(false); + + $result = $this->removeAdyenStateDataResolver->resolve( + $this->fieldMock, + $this->contextMock, + $this->infoMock, + null, + $args + ); + } + + public function testResolveWithGraphQLAdyenException() + { + $this->expectException(GraphQlAdyenException::class); + + $args = [ + 'stateDataId' => 1, + 'cartId' => 1 + ]; + + $this->adyenStateDataHelperMock->expects($this->once()) + ->method('remove') + ->willThrowException(new Exception()); + + $this->removeAdyenStateDataResolver->resolve( + $this->fieldMock, + $this->contextMock, + $this->infoMock, + null, + $args + ); + } + + /** + * @dataProvider inputFailureDataProvider + */ + public function testResolveFailureWithWrongInput($stateDataId, $cartId) + { + $this->expectException(GraphQlInputException::class); + + $args = [ + 'stateDataId' => $stateDataId, + 'cartId' => $cartId + ]; + + $this->removeAdyenStateDataResolver->resolve( + $this->fieldMock, + $this->contextMock, + $this->infoMock, + null, + $args + ); + } + + /** + * Data provider for testResolveFailureWithWrongInput test method + * + * @return array + */ + private static function inputFailureDataProvider(): array + { + return [ + [ + 'stateDataId' => '', + 'cartId' => 1 + ], + [ + 'stateDataId' => 1, + 'cartId' => '' + ], + [ + 'stateDataId' => '', + 'cartId' => '' + ], + [ + 'stateDataId' => null, + 'cartId' => 1 + ] + ]; + } +} diff --git a/Test/Unit/Model/Resolver/SaveAdyenStateDataTest.php b/Test/Unit/Model/Resolver/SaveAdyenStateDataTest.php new file mode 100644 index 000000000..871e291f5 --- /dev/null +++ b/Test/Unit/Model/Resolver/SaveAdyenStateDataTest.php @@ -0,0 +1,152 @@ + + */ +namespace Adyen\Payment\Test\Model\Resolver; + +use Adyen\Payment\Model\Api\AdyenStateData; +use Adyen\Payment\Model\Resolver\SaveAdyenStateData; +use Adyen\Payment\Test\Unit\AbstractAdyenTestCase; +use Exception; +use Magento\Catalog\Model\Layer\ContextInterface; +use Magento\Framework\GraphQl\Config\Element\Field; +use Magento\Framework\GraphQl\Exception\GraphQlInputException; +use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Quote\Model\QuoteIdMask; +use Magento\Quote\Model\QuoteIdMaskFactory; + +class SaveAdyenStateDataTest extends AbstractAdyenTestCase +{ + private $saveAdyenStateDataResolver; + private $adyenStateDataHelperMock; + private $quoteIdMaskFactoryMock; + private $quoteIdMaskMock; + private $fieldMock; + private $contextMock; + private $infoMock; + + public function setUp(): void + { + $this->objectManager = new ObjectManager($this); + + $this->adyenStateDataHelperMock = $this->createMock(AdyenStateData::class); + $this->fieldMock = $this->createMock(Field::class); + $this->contextMock = $this->createMock(ContextInterface::class); + $this->infoMock = $this->createMock(ResolveInfo::class); + + $this->quoteIdMaskMock = $this->createGeneratedMock(QuoteIdMask::class, ['load', 'getQuoteId']); + $this->quoteIdMaskMock->method('load')->willReturn($this->quoteIdMaskMock); + $this->quoteIdMaskMock->method('getQuoteId')->willReturn(1); + + $this->quoteIdMaskFactoryMock = $this->createGeneratedMock(QuoteIdMaskFactory::class, [ + 'create' + ]); + $this->quoteIdMaskFactoryMock->method('create')->willReturn($this->quoteIdMaskMock); + + $this->saveAdyenStateDataResolver = $this->objectManager->getObject(SaveAdyenStateData::class, [ + 'adyenStateData' => $this->adyenStateDataHelperMock, + 'quoteIdMaskFactory' => $this->quoteIdMaskFactoryMock + ]); + } + + public function testResolve() + { + $stateData = "{\"paymentMethod\":{\"type\":\"giftcard\",\"brand\":\"svs\",\"encryptedCardNumber\":\"abd...\",\"encryptedSecurityCode\":\"xyz...\"},\"giftcard\":{\"balance\":{\"currency\":\"EUR\",\"value\":5000},\"title\":\"SVS\"}}"; + $stateDataId = 1; + + $args = [ + 'stateData' => $stateData, + 'cartId' => 1 + ]; + + $this->adyenStateDataHelperMock->expects($this->once())->method('save')->willReturn($stateDataId); + + $result = $this->saveAdyenStateDataResolver->resolve( + $this->fieldMock, + $this->contextMock, + $this->infoMock, + null, + $args + ); + + $this->assertArrayHasKey('stateDataId', $result); + $this->assertEquals($stateDataId, $result['stateDataId']); + } + + public function testResolveFailedWithException() + { + $this->expectException(Exception::class); + + $args = [ + 'stateData' => "{}", + 'cartId' => 1 + ]; + + $this->adyenStateDataHelperMock->expects($this->once()) + ->method('save') + ->willThrowException(new Exception()); + + $result = $this->saveAdyenStateDataResolver->resolve( + $this->fieldMock, + $this->contextMock, + $this->infoMock, + null, + $args + ); + } + + /** + * @dataProvider inputFailureDataProvider + */ + public function testResolveFailureWithWrongInput($stateData, $cartId) + { + $this->expectException(GraphQlInputException::class); + + $args = [ + 'stateData' => $stateData, + 'cartId' => $cartId + ]; + + $this->saveAdyenStateDataResolver->resolve( + $this->fieldMock, + $this->contextMock, + $this->infoMock, + null, + $args + ); + } + + /** + * Data provider for testResolveFailureWithWrongInput test method + * + * @return array + */ + private static function inputFailureDataProvider(): array + { + return [ + [ + 'stateData' => '', + 'cartId' => 1 + ], + [ + 'stateData' => "{}", + 'cartId' => '' + ], + [ + 'stateData' => '', + 'cartId' => '' + ], + [ + 'stateData' => null, + 'cartId' => 1 + ] + ]; + } +} From db564589695b07e39048cb3de07a316e7e7798d9 Mon Sep 17 00:00:00 2001 From: Can Demiralp Date: Thu, 21 Mar 2024 14:13:59 +0100 Subject: [PATCH 15/17] [ECP-8972] Update method's PHPDocs --- Model/Api/GuestAdyenStateData.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Model/Api/GuestAdyenStateData.php b/Model/Api/GuestAdyenStateData.php index a192d1520..b78c9b2db 100644 --- a/Model/Api/GuestAdyenStateData.php +++ b/Model/Api/GuestAdyenStateData.php @@ -14,6 +14,7 @@ use Adyen\Payment\Api\GuestAdyenStateDataInterface; use Adyen\Payment\Helper\StateData as StateDataHelper; +use Magento\Framework\Exception\AlreadyExistsException; use Magento\Framework\Exception\InputException; use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Exception\NoSuchEntityException; @@ -38,6 +39,7 @@ public function __construct( * @return int * @throws InputException * @throws LocalizedException + * @throws AlreadyExistsException */ public function save(string $stateData, string $cartId): int { From 769dfe140e64821d6ca5e11b97187daca03daa41 Mon Sep 17 00:00:00 2001 From: Can Demiralp Date: Thu, 21 Mar 2024 14:24:45 +0100 Subject: [PATCH 16/17] [ECP-8972] Change class import --- Test/Unit/Model/Resolver/RemoveAdyenStateDataTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Test/Unit/Model/Resolver/RemoveAdyenStateDataTest.php b/Test/Unit/Model/Resolver/RemoveAdyenStateDataTest.php index ef72d0181..d4aa02201 100644 --- a/Test/Unit/Model/Resolver/RemoveAdyenStateDataTest.php +++ b/Test/Unit/Model/Resolver/RemoveAdyenStateDataTest.php @@ -10,11 +10,11 @@ */ namespace Adyen\Payment\Test\Model\Resolver; -use _PHPStan_3a7a22dbd\Nette\Neon\Exception; use Adyen\Payment\Exception\GraphQlAdyenException; use Adyen\Payment\Model\Api\AdyenStateData; use Adyen\Payment\Model\Resolver\RemoveAdyenStateData; use Adyen\Payment\Test\Unit\AbstractAdyenTestCase; +use Exception; use Magento\Catalog\Model\Layer\ContextInterface; use Magento\Framework\Exception\LocalizedException; use Magento\Framework\GraphQl\Config\Element\Field; From 38715c6f5f0c6e7e17f79b63e9f1f021acdd8750 Mon Sep 17 00:00:00 2001 From: Can Demiralp Date: Thu, 21 Mar 2024 16:12:38 +0100 Subject: [PATCH 17/17] [ECP-8972] Use only the state data from request and ignore DB on observers --- Observer/AdyenCcDataAssignObserver.php | 3 +-- Observer/AdyenMotoDataAssignObserver.php | 3 +-- Observer/AdyenPaymentMethodDataAssignObserver.php | 3 +-- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/Observer/AdyenCcDataAssignObserver.php b/Observer/AdyenCcDataAssignObserver.php index c354be8fb..1d86fadc6 100644 --- a/Observer/AdyenCcDataAssignObserver.php +++ b/Observer/AdyenCcDataAssignObserver.php @@ -116,9 +116,8 @@ public function execute(Observer $observer) // JSON decode state data from the frontend or fetch it from the DB entity with the quote ID if (!empty($additionalData[self::STATE_DATA])) { $stateData = json_decode((string) $additionalData[self::STATE_DATA], true); - } else { - $stateData = $this->stateDataCollection->getStateDataArrayWithQuoteId($paymentInfo->getData('quote_id')); } + // Get validated state data array if (!empty($stateData)) { $stateData = $this->checkoutStateDataValidator->getValidatedAdditionalData($stateData); diff --git a/Observer/AdyenMotoDataAssignObserver.php b/Observer/AdyenMotoDataAssignObserver.php index d49c162bf..09be9b649 100644 --- a/Observer/AdyenMotoDataAssignObserver.php +++ b/Observer/AdyenMotoDataAssignObserver.php @@ -106,9 +106,8 @@ public function execute(Observer $observer) // JSON decode state data from the frontend or fetch it from the DB entity with the quote ID if (!empty($additionalData[self::STATE_DATA])) { $orderStateData = json_decode((string) $additionalData[self::STATE_DATA], true); - } else { - $orderStateData = $this->stateDataCollection->getStateDataArrayWithQuoteId($paymentInfo->getData('quote_id')); } + // Get validated state data array if (!empty($orderStateData)) { $orderStateData = $this->checkoutStateDataValidator->getValidatedAdditionalData($orderStateData); diff --git a/Observer/AdyenPaymentMethodDataAssignObserver.php b/Observer/AdyenPaymentMethodDataAssignObserver.php index 0c18603c5..3416158d1 100644 --- a/Observer/AdyenPaymentMethodDataAssignObserver.php +++ b/Observer/AdyenPaymentMethodDataAssignObserver.php @@ -89,9 +89,8 @@ public function execute(Observer $observer) } elseif (!empty($additionalData[self::CC_NUMBER])) { $stateData = json_decode((string) $additionalData[self::CC_NUMBER], true); $paymentInfo->setAdditionalInformation(self::BRAND_CODE, $stateData['paymentMethod']['type']); - } else { - $stateData = $this->stateDataCollection->getStateDataArrayWithQuoteId($paymentInfo->getData('quote_id')); } + // Get validated state data array if (!empty($stateData)) { $stateData = $this->checkoutStateDataValidator->getValidatedAdditionalData($stateData);