Skip to content
This repository was archived by the owner on Dec 19, 2019. It is now read-only.

GraphQL-60: Manage Stored Payment Methods #228

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);

namespace Magento\VaultGraphQl\Model\Resolver\PaymentToken;

use Magento\Framework\Webapi\ServiceOutputProcessor;
use Magento\Framework\Serialize\SerializerInterface;
use Magento\Framework\Api\DataObjectHelper;
use Magento\Vault\Api\Data\PaymentTokenInterface;
use Magento\Vault\Api\PaymentTokenRepositoryInterface;

/**
* Payment Token field data provider, used for GraphQL request processing.
*/
class PaymentTokenDataProvider
{
/**
* @var ServiceOutputProcessor
*/
private $serviceOutputProcessor;

/**
* @var SerializerInterface
*/
private $jsonSerializer;

/**
* @var DataObjectHelper
*/
private $dataObjectHelper;

/**
* @param ServiceOutputProcessor $serviceOutputProcessor
* @param SerializerInterface $jsonSerializer
* @param DataObjectHelper $dataObjectHelper
*/
public function __construct(
ServiceOutputProcessor $serviceOutputProcessor,
SerializerInterface $jsonSerializer,
DataObjectHelper $dataObjectHelper
) {
$this->serviceOutputProcessor = $serviceOutputProcessor;
$this->jsonSerializer = $jsonSerializer;
$this->dataObjectHelper = $dataObjectHelper;
}

/**
* Transform array of payment token data to array of object array format
*
* @param PaymentTokenInterface[] $paymentTokens
* @return array
*/
public function processPaymentTokenArray(array $paymentTokens): array
{
$result = [];
/** @var PaymentTokenInterface $paymentToken */
foreach ($paymentTokens as $paymentToken) {
$result[] = $this->processPaymentToken($paymentToken);
}
return $result;
}

/**
* Transform single payment token data from object to an array format
*
* @param PaymentTokenInterface $paymentTokenObject
* @return array
*/
public function processPaymentToken(PaymentTokenInterface $paymentTokenObject): array
{
$paymentToken = $this->serviceOutputProcessor->process(
$paymentTokenObject,
PaymentTokenRepositoryInterface::class,
'getById'
);
$detailsAttributes = [];
if (!empty($paymentTokenObject->getTokenDetails())) {
$details = $this->jsonSerializer->unserialize($paymentTokenObject->getTokenDetails());
foreach ($details as $key => $attribute) {
$isArray = false;
if (is_array($attribute)) {
$isArray = true;
foreach ($attribute as $attributeValue) {
if (is_array($attributeValue)) {
$detailsAttributes[] = [
'code' => $key,
'value' => $this->jsonSerializer->serialize($attribute)
];
continue;
}
$detailsAttributes[] = ['code' => $key, 'value' => implode(',', $attribute)];
continue;
}
}
if ($isArray) {
continue;
}
$detailsAttributes[] = ['code' => $key, 'value' => $attribute];
}
}
$paymentToken['details'] = $detailsAttributes;
return $paymentToken;
}

/**
* Add $tokenInput array information to a $token object
*
* @param PaymentTokenInterface $token
* @param array $tokenInput
* @return PaymentTokenInterface
*/
public function fillPaymentToken(PaymentTokenInterface $token, array $tokenInput): PaymentTokenInterface
{
$this->dataObjectHelper->populateWithArray(
$token,
$tokenInput,
PaymentTokenInterface::class
);
$tokenDetails = [];
foreach ($tokenInput['details'] as $attribute) {
$tokenDetails[$attribute['code']] = $attribute['value'];
}
$token->setTokenDetails($this->jsonSerializer->serialize($tokenDetails));
return $token;
}
}
150 changes: 150 additions & 0 deletions app/code/Magento/VaultGraphQl/Model/Resolver/PaymentTokenAdd.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);

namespace Magento\VaultGraphQl\Model\Resolver;

use Magento\Authorization\Model\UserContextInterface;
use Magento\Framework\Exception\AlreadyExistsException;
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
use Magento\Framework\GraphQl\Config\Element\Field;
use Magento\Framework\GraphQl\Query\ResolverInterface;
use Magento\Framework\GraphQl\Query\Resolver\ContextInterface;
use Magento\Framework\GraphQl\Exception\GraphQlAuthorizationException;
use Magento\Framework\GraphQl\Exception\GraphQlInputException;
use Magento\Framework\GraphQl\Exception\GraphQlAlreadyExistsException;
use Magento\Vault\Api\PaymentTokenRepositoryInterface;
use Magento\Vault\Api\PaymentTokenManagementInterface;
use Magento\Vault\Api\Data\PaymentTokenFactoryInterface;
use Magento\Vault\Api\Data\PaymentTokenInterface;
use Magento\VaultGraphQl\Model\Resolver\PaymentToken\PaymentTokenDataProvider;

/**
* Store Payment Add, used for GraphQL request processing.
*/
class PaymentTokenAdd implements ResolverInterface
{
/**
* Payment token required attributes
*/
const REQUIRED_ATTRIBUTES = [
'public_hash',
'payment_method_code',
'type',
'gateway_token'
];

/**
* @var PaymentTokenRepositoryInterface
*/
private $paymentTokenRepository;

/**
* @var PaymentTokenManagementInterface
*/
private $paymentTokenManagement;

/**
* @var PaymentTokenFactoryInterface
*/
private $paymentTokenFactory;

/**
* @var PaymentTokenDataProvider
*/
private $paymentTokenDataProvider;

/**
* @param PaymentTokenRepositoryInterface $paymentTokenRepository
* @param PaymentTokenManagementInterface $paymentTokenManagement
* @param PaymentTokenFactoryInterface $paymentTokenFactory
* @param PaymentTokenDataProvider $paymentTokenDataProvider
*/
public function __construct(
PaymentTokenRepositoryInterface $paymentTokenRepository,
PaymentTokenManagementInterface $paymentTokenManagement,
PaymentTokenFactoryInterface $paymentTokenFactory,
PaymentTokenDataProvider $paymentTokenDataProvider
) {
$this->paymentTokenRepository = $paymentTokenRepository;
$this->paymentTokenManagement = $paymentTokenManagement;
$this->paymentTokenFactory = $paymentTokenFactory;
$this->paymentTokenDataProvider = $paymentTokenDataProvider;
}

/**
* @inheritdoc
*/
public function resolve(
Field $field,
$context,
ResolveInfo $info,
array $value = null,
array $args = null
) {
/** @var ContextInterface $context */
if ((!$context->getUserId()) || $context->getUserType() == UserContextInterface::USER_TYPE_GUEST) {
throw new GraphQlAuthorizationException(
__(
'A guest customer cannot access resource "%1".',
['store_payment_token']
)
);
}
$customerId = $context->getUserId();
return $this->paymentTokenDataProvider->processPaymentToken(
$this->processPaymentTokenAdd($customerId, $args['input'])
);
}

/**
* Check input data
*
* @param array $tokenInfo
* @return bool|string
*/
private function getInputError(array $tokenInfo)
{
foreach (self::REQUIRED_ATTRIBUTES as $attributeName) {
if (!isset($tokenInfo[$attributeName]) || empty($tokenInfo[$attributeName])) {
return $attributeName;
}
}
return false;
}

/**
* Process payment token add
*
* @param int $customerId
* @param array $tokenInfo
* @return PaymentTokenInterface
* @throws GraphQlInputException
* @throws GraphQlAlreadyExistsException
*/
private function processPaymentTokenAdd($customerId, array $tokenInfo): PaymentTokenInterface
{
$errorInput = $this->getInputError($tokenInfo);
if ($errorInput) {
throw new GraphQlInputException(
__('The required parameter %1 is missing.', [$errorInput])
);
}
/** @var PaymentTokenInterface $token */
$token = $this->paymentTokenDataProvider->fillPaymentToken(
$this->paymentTokenFactory->create($tokenInfo['type']),
$tokenInfo
);
$token->setCustomerId($customerId);
try {
$this->paymentTokenRepository->save($token);
// Reload current token object from repository to get "created_at" updated
return $this->paymentTokenManagement->getByPublicHash($token->getPublicHash(), $customerId);
} catch (AlreadyExistsException $e) {
throw new GraphQlAlreadyExistsException(__($e->getMessage()), $e);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);

namespace Magento\VaultGraphQl\Model\Resolver;

use Magento\Authorization\Model\UserContextInterface;
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
use Magento\Framework\GraphQl\Config\Element\Field;
use Magento\Framework\GraphQl\Query\ResolverInterface;
use Magento\Framework\GraphQl\Query\Resolver\ContextInterface;
use Magento\Framework\GraphQl\Exception\GraphQlAuthorizationException;
use Magento\Framework\GraphQl\Exception\GraphQlNoSuchEntityException;
use Magento\Vault\Api\PaymentTokenRepositoryInterface;
use Magento\Vault\Api\PaymentTokenManagementInterface;

/**
* Store Payment Delete, used for GraphQL request processing.
*/
class PaymentTokenDelete implements ResolverInterface
{
/**
* @var PaymentTokenRepositoryInterface
*/
private $paymentTokenRepository;

/**
* @var PaymentTokenManagementInterface
*/
private $paymentTokenManagement;

/**
* @param PaymentTokenRepositoryInterface $paymentTokenRepository
* @param PaymentTokenManagementInterface $paymentTokenManagement
*/
public function __construct(
PaymentTokenRepositoryInterface $paymentTokenRepository,
PaymentTokenManagementInterface $paymentTokenManagement
) {
$this->paymentTokenRepository = $paymentTokenRepository;
$this->paymentTokenManagement = $paymentTokenManagement;
}

/**
* @inheritdoc
*/
public function resolve(
Field $field,
$context,
ResolveInfo $info,
array $value = null,
array $args = null
) {
/** @var ContextInterface $context */
if ((!$context->getUserId()) || $context->getUserType() == UserContextInterface::USER_TYPE_GUEST) {
throw new GraphQlAuthorizationException(
__(
'A guest customer cannot access resource "%1".',
['store_payment_token']
)
);
}
$customerId = $context->getUserId();
return $this->deleteToken($customerId, $args['public_hash']);
}

/**
* Process delete request
*
* @param int $customerId
* @param string $publicHash
* @return bool
* @throws GraphQlAuthorizationException
* @throws GraphQlNoSuchEntityException
*/
private function deleteToken($customerId, $publicHash)
{
$token = $this->paymentTokenManagement->getByPublicHash($publicHash, $customerId);
if (!$token) {
throw new GraphQlNoSuchEntityException(
__('Payment token public_hash %1 does not exist.', [$publicHash])
);
}
return $this->paymentTokenRepository->delete($token);
}
}
Loading