Skip to content

Commit

Permalink
Merge branch 'main' into ECP-9561
Browse files Browse the repository at this point in the history
  • Loading branch information
candemiralp authored Dec 31, 2024
2 parents 362ab65 + 0ad2747 commit e71f6fd
Show file tree
Hide file tree
Showing 39 changed files with 1,462 additions and 116 deletions.
2 changes: 1 addition & 1 deletion .github/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ configure: n98-magerun2.phar
bin/magento config:set payment/adyen_abstract/merchant_account "${ADYEN_MERCHANT}"
bin/magento config:set payment/adyen_abstract/notifications_ip_check 0
bin/magento config:set payment/adyen_abstract/payment_authorized 'processing'
bin/magento config:set payment/adyen_abstract/payment_pre_authorized 'pending_payment'
bin/magento config:set payment/adyen_abstract/payment_pre_authorized 'adyen_authorized'
bin/magento config:set payment/adyen_abstract/capture_mode 'manual'
bin/magento config:set payment/adyen_abstract/paypal_capture_mode 0
bin/magento config:set payment/adyen_abstract/recurring_configuration '{"adyen_cc":{"name":"Cards","enabled":"1","recurringProcessingModel":"CardOnFile"},"adyen_sepadirectdebit":{"name":"SEPA Direct Debit","enabled":"1","recurringProcessingModel":"CardOnFile"}}'
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php
/**
*
* Adyen Payment Module
*
* Copyright (c) 2024 Adyen N.V.
* This file is open source and available under the MIT license.
* See the LICENSE file for more info.
*
* Author: Adyen <[email protected]>
*/

namespace Adyen\Payment\Api\InstantPurchase\PaymentMethodIntegration;

use Magento\InstantPurchase\PaymentMethodIntegration\AvailabilityCheckerInterface;

interface AdyenAvailabilityCheckerInterface extends AvailabilityCheckerInterface
{
/**
* Checks if Adyen alternative payment method may be used for instant purchase.
*
* This interface extends the default `AvailabilityCheckerInterface` and implements
* a new method with payment method argument. This interface is used in `InstantPurchaseIntegrations`
* plugin to override the `AvailabilityCheckerInterface` which doesn't have payment method argument.
*
* @param string $paymentMethodCode
*
* @return bool
*/
public function isAvailableAdyenMethod(string $paymentMethodCode): bool;
}
2 changes: 1 addition & 1 deletion Gateway/Http/Client/TransactionCancel.php
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ public function placeRequest(TransferInterface $transferObject): array
$headers['idempotencyExtraData'] ?? null
);
$requestOptions['idempotencyKey'] = $idempotencyKey;
$requestOptions['headers'] = $this->adyenHelper->buildRequestHeaders();
$requestOptions['headers'] = $headers;
$this->adyenHelper->logRequest($request, Client::API_CHECKOUT_VERSION, '/cancels');
$request['applicationInfo'] = $this->adyenHelper->buildApplicationInfo($client);
$paymentCancelRequest = new PaymentCancelRequest($request);
Expand Down
7 changes: 5 additions & 2 deletions Gateway/Http/Client/TransactionCapture.php
Original file line number Diff line number Diff line change
Expand Up @@ -77,16 +77,19 @@ public function placeRequest(TransferInterface $transferObject): array
$client = $this->adyenHelper->initializeAdyenClientWithClientConfig($clientConfig);
$service = $this->adyenHelper->initializeModificationsApi($client);

$requestOptions['headers'] = $this->adyenHelper->buildRequestHeaders();
$requestOptions['headers'] = $headers;
$request['applicationInfo'] = $this->adyenHelper->buildApplicationInfo($client);

if (array_key_exists(self::MULTIPLE_AUTHORIZATIONS, $request)) {
return $this->placeMultipleCaptureRequests($service, $request, $requestOptions);
}

$idempotencyKeyExtraData = $request['idempotencyExtraData'];
unset($request['idempotencyExtraData']);

$idempotencyKey = $this->idempotencyHelper->generateIdempotencyKey(
$request,
$headers['idempotencyExtraData'] ?? null
$idempotencyKeyExtraData ?? null
);
$requestOptions['idempotencyKey'] = $idempotencyKey;

Expand Down
8 changes: 5 additions & 3 deletions Gateway/Http/Client/TransactionPaymentLinks.php
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,12 @@ public function placeRequest(TransferInterface $transferObject): array
{
$request = $transferObject->getBody();
$headers = $transferObject->getHeaders();
$idempotencyKeyExtraData = $headers['idempotencyExtraData'] ?? null;
unset($headers['idempotencyExtraData']);
$clientConfig = $transferObject->getClientConfig();

$client = $this->adyenHelper->initializeAdyenClientWithClientConfig($clientConfig);
$service = new PaymentLinksApi($client);
$service = $this->adyenHelper->initializePaymentLinksApi($client);

// If the payment links call is already done return the request
if (!empty($request['resultCode'])) {
Expand All @@ -68,11 +70,11 @@ public function placeRequest(TransferInterface $transferObject): array

$idempotencyKey = $this->idempotencyHelper->generateIdempotencyKey(
$request,
$headers['idempotencyExtraData'] ?? null
$idempotencyKeyExtraData
);

$requestOptions['idempotencyKey'] = $idempotencyKey;
$requestOptions['headers'] = $this->adyenHelper->buildRequestHeaders();
$requestOptions['headers'] = $headers;
$request['applicationInfo'] = $this->adyenHelper->buildApplicationInfo($client);

$this->adyenHelper->logRequest($request, Client::API_CHECKOUT_VERSION, '/paymentLinks');
Expand Down
7 changes: 5 additions & 2 deletions Gateway/Http/Client/TransactionRefund.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ public function placeRequest(TransferInterface $transferObject): array
{
$requests = $transferObject->getBody();
$headers = $transferObject->getHeaders();
$idempotencyKeyExtraData = $headers['idempotencyExtraData'];
unset($headers['idempotencyExtraData']);
$clientConfig = $transferObject->getClientConfig();

$client = $this->adyenHelper->initializeAdyenClientWithClientConfig($clientConfig);
Expand All @@ -66,10 +68,11 @@ public function placeRequest(TransferInterface $transferObject): array
$responseData = [];
$idempotencyKey = $this->idempotencyHelper->generateIdempotencyKey(
$request,
$headers['idempotencyExtraData'] ?? null
$idempotencyKeyExtraData ?? null
);
$requestOptions['idempotencyKey'] = $idempotencyKey;
$requestOptions['headers'] = $this->adyenHelper->buildRequestHeaders();
$requestOptions['headers'] = $headers;

$this->adyenHelper->logRequest($request, Client::API_CHECKOUT_VERSION, '/refunds');
$request['applicationInfo'] = $this->adyenHelper->buildApplicationInfo($client);
$paymentRefundRequest = new PaymentRefundRequest($request);
Expand Down
10 changes: 4 additions & 6 deletions Gateway/Request/CaptureDataBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,10 @@ public function build(array $buildSubject): array
$requestBody = [
"amount" => $modificationAmount,
"reference" => $payment->getOrder()->getIncrementId(),
"paymentPspReference" => $pspReference
"paymentPspReference" => $pspReference,
"idempotencyExtraData" => [
'totalInvoiced' => $payment->getOrder()->getTotalInvoiced() ?? 0
]
];

//Check additionaldata
Expand All @@ -119,11 +122,6 @@ public function build(array $buildSubject): array
}
$request['body'] = $requestBody;
$request['clientConfig'] = ["storeId" => $payment->getOrder()->getStoreId()];
$request['headers'] = [
'idempotencyExtraData' => [
'totalInvoiced' => $payment->getOrder()->getTotalInvoiced() ?? 0
]
];

return $request;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,15 @@
* Author: Adyen <[email protected]>
*/

namespace Adyen\Payment\Gateway\Request;
namespace Adyen\Payment\Gateway\Request\Header;

use Adyen\Payment\Helper\Data;
use Magento\Payment\Gateway\Data\PaymentDataObject;
use Magento\Payment\Gateway\Helper\SubjectReader;
use Magento\Payment\Gateway\Request\BuilderInterface;

class HeaderDataBuilder implements BuilderInterface
class HeaderDataBuilder implements BuilderInterface, HeaderDataBuilderInterface
{
const FRONTENDTYPE = 'external-platform-frontendtype';
const FRONTENDTYPE_HEADLESS = 'headless';

/**
* @var Data
*/
Expand Down
15 changes: 15 additions & 0 deletions Gateway/Request/Header/HeaderDataBuilderInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php

namespace Adyen\Payment\Gateway\Request\Header;

interface HeaderDataBuilderInterface
{
const EXTERNAL_PLATFORM_NAME = 'external-platform-name';
const EXTERNAL_PLATFORM_VERSION = 'external-platform-version';
const EXTERNAL_PLATFORM_EDITION = 'external-platform-edition';
const EXTERNAL_PLATFORM_FRONTEND_TYPE = 'external-platform-frontendtype';
const MERCHANT_APPLICATION_NAME = 'merchant-application-name';
const MERCHANT_APPLICATION_VERSION = 'merchant-application-version';

const ADDITIONAL_DATA_FRONTEND_TYPE_KEY = 'frontendType';
}
90 changes: 51 additions & 39 deletions Gateway/Request/RecurringVaultDataBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
*
* Adyen Payment module (https://www.adyen.com/)
*
* Copyright (c) 2023 Adyen N.V. (https://www.adyen.com/)
* Copyright (c) 2024 Adyen N.V. (https://www.adyen.com/)
* See LICENSE.txt for license details.
*
* Author: Adyen <[email protected]>
Expand All @@ -15,75 +15,87 @@
use Adyen\Payment\Helper\StateData;
use Adyen\Payment\Helper\Vault;
use Adyen\Payment\Model\Config\Source\ThreeDSFlow;
use Adyen\Payment\Model\Ui\AdyenCcConfigProvider;
use Magento\Framework\Exception\LocalizedException;
use Magento\Payment\Gateway\Data\PaymentDataObject;
use Magento\Payment\Gateway\Helper\SubjectReader;
use Magento\Payment\Gateway\Request\BuilderInterface;
use Magento\Vault\Api\Data\PaymentTokenFactoryInterface;

class RecurringVaultDataBuilder implements BuilderInterface
{
private StateData $stateData;
private Vault $vaultHelper;
private Config $configHelper;

/**
* @param StateData $stateData
* @param Vault $vaultHelper
* @param Config $configHelper
*/
public function __construct(
StateData $stateData,
Vault $vaultHelper,
Config $configHelper
) {
$this->stateData = $stateData;
$this->vaultHelper = $vaultHelper;
$this->configHelper = $configHelper;
}
private readonly StateData $stateData,
private readonly Vault $vaultHelper,
private readonly Config $configHelper
) { }

/**
* @throws LocalizedException
*/
public function build(array $buildSubject): array
{
/** @var PaymentDataObject $paymentDataObject */
$paymentDataObject = SubjectReader::readPayment($buildSubject);

$payment = $paymentDataObject->getPayment();
$paymentMethod = $payment->getMethodInstance();

$order = $payment->getOrder();
$extensionAttributes = $payment->getExtensionAttributes();

$paymentToken = $extensionAttributes->getVaultPaymentToken();
$details = json_decode((string) ($paymentToken->getTokenDetails() ?: '{}'), true);

// Initialize the request body with the current state data
$requestBody = $this->stateData->getStateData($order->getQuoteId());
if ($paymentToken->getType() === PaymentTokenFactoryInterface::TOKEN_TYPE_CREDIT_CARD) {
// Build base request for card token payments (including card wallets)

// For now this will only be used by tokens created trough adyen_hpp payment methods
if (array_key_exists(Vault::TOKEN_TYPE, $details)) {
$requestBody['recurringProcessingModel'] = $details[Vault::TOKEN_TYPE];
} else {
// If recurringProcessingModel doesn't exist in the token details, use the default value from config.
$requestBody['recurringProcessingModel'] = $this->vaultHelper->getPaymentMethodRecurringProcessingModel(
$paymentMethod->getProviderCode(),
$order->getStoreId()
);
}
$isInstantPurchase = (bool) $payment->getAdditionalInformation('instant-purchase');

if ($isInstantPurchase) {
// `Instant Purchase` doesn't have the component and state data. Build the `paymentMethod` object.
$requestBody['paymentMethod']['type'] = 'scheme';
$requestBody['paymentMethod']['storedPaymentMethodId'] = $paymentToken->getGatewayToken();
} else {
// Initialize the request body with the current state data if it's not `Instant Purchase`.
$requestBody = $this->stateData->getStateData($order->getQuoteId());
}

/*
* allow3DS flag is required to trigger the native 3DS challenge.
* Otherwise, shopper will be redirected to the issuer for challenge.
* Due to new VISA compliance requirements, holderName is added to the payments call
*/
if ($paymentMethod->getCode() === AdyenCcConfigProvider::CC_VAULT_CODE) {
/*
* `allow3DS: true` flag is required to trigger the native 3DS challenge.
* Otherwise, shopper will be redirected to the issuer for challenge.
*/
$requestBody['additionalData']['allow3DS2'] =
$this->configHelper->getThreeDSFlow($order->getStoreId()) === ThreeDSFlow::THREEDS_NATIVE;

// Due to new VISA compliance requirements, holderName is added to the payments call
$requestBody['paymentMethod']['holderName'] = $details['cardHolderName'] ?? null;
}
} else {
// Build base request for alternative payment methods for regular checkout and Instant Purchase

/**
* Build paymentMethod object for alternative payment methods
*/
if ($paymentMethod->getCode() !== AdyenCcConfigProvider::CC_VAULT_CODE) {
$requestBody['paymentMethod'] = [
'type' => $details['type'],
'storedPaymentMethodId' => $paymentToken->getGatewayToken()
];
}

$request['body'] = $requestBody;
// Check the `stateData` if `recurringProcessingModel` is added through a headless request.
if (array_key_exists(Vault::TOKEN_TYPE, $details)) {
$requestBody['recurringProcessingModel'] = $details[Vault::TOKEN_TYPE];
} else {
// If recurringProcessingModel doesn't exist in the token details, use the default value from config.
$requestBody['recurringProcessingModel'] = $this->vaultHelper->getPaymentMethodRecurringProcessingModel(
$paymentMethod->getProviderCode(),
$order->getStoreId()
);
}

return $request;
return [
'body' => $requestBody
];
}
}
10 changes: 10 additions & 0 deletions Helper/Config.php
Original file line number Diff line number Diff line change
Expand Up @@ -592,6 +592,16 @@ public function getThreeDSFlow(int $storeId = null): string
);
}

public function getIsCvcRequiredForRecurringCardPayments(int $storeId = null): bool
{
return (bool) $this->getConfigData(
'require_cvc',
Config::XML_ADYEN_CC_VAULT,
$storeId,
true
);
}

public function getConfigData(string $field, string $xmlPrefix, ?int $storeId, bool $flag = false): mixed
{
$path = implode("/", [self::XML_PAYMENT_PREFIX, $xmlPrefix, $field]);
Expand Down
24 changes: 15 additions & 9 deletions Helper/Data.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@
use Adyen\Model\Checkout\ApplicationInfo;
use Adyen\Model\Checkout\CommonField;
use Adyen\Model\Checkout\UtilityRequest;
use Adyen\Payment\Gateway\Request\Header\HeaderDataBuilderInterface;
use Adyen\Payment\Helper\Config as ConfigHelper;
use Adyen\Payment\Gateway\Request\HeaderDataBuilder;
use Adyen\Service\Checkout;
use Adyen\Payment\Logger\AdyenLogger;
use Adyen\Payment\Model\Config\Source\RenderMode;
Expand All @@ -27,6 +27,7 @@
use Adyen\Payment\Observer\AdyenPaymentMethodDataAssignObserver;
use Adyen\Service\Checkout\ModificationsApi;
use Adyen\Service\Checkout\OrdersApi;
use Adyen\Service\Checkout\PaymentLinksApi;
use Adyen\Service\Checkout\PaymentsApi;
use Adyen\Service\Checkout\UtilityApi;
use Adyen\Service\PosPayment;
Expand Down Expand Up @@ -1173,16 +1174,16 @@ public function buildRequestHeaders($payment = null)
{
$magentoDetails = $this->getMagentoDetails();
$headers = [
'external-platform-name' => $magentoDetails['name'],
'external-platform-version' => $magentoDetails['version'],
'external-platform-edition' => $magentoDetails['edition'],
'merchant-application-name' => $this->getModuleName(),
'merchant-application-version' => $this->getModuleVersion()
HeaderDataBuilderInterface::EXTERNAL_PLATFORM_NAME => $magentoDetails['name'],
HeaderDataBuilderInterface::EXTERNAL_PLATFORM_VERSION => $magentoDetails['version'],
HeaderDataBuilderInterface::EXTERNAL_PLATFORM_EDITION => $magentoDetails['edition'],
HeaderDataBuilderInterface::MERCHANT_APPLICATION_NAME => $this->getModuleName(),
HeaderDataBuilderInterface::MERCHANT_APPLICATION_VERSION => $this->getModuleVersion()
];

if(isset($payment) && !is_null($payment->getAdditionalInformation(HeaderDataBuilder::FRONTENDTYPE))) {
$headers[HeaderDataBuilder::FRONTENDTYPE] =
$payment->getAdditionalInformation(HeaderDataBuilder::FRONTENDTYPE);
if(isset($payment) && !is_null($payment->getAdditionalInformation(HeaderDataBuilderInterface::ADDITIONAL_DATA_FRONTEND_TYPE_KEY))) {
$headers[HeaderDataBuilderInterface::EXTERNAL_PLATFORM_FRONTEND_TYPE] =
$payment->getAdditionalInformation(HeaderDataBuilderInterface::ADDITIONAL_DATA_FRONTEND_TYPE_KEY);
}

return $headers;
Expand Down Expand Up @@ -1248,6 +1249,11 @@ public function initializeOrdersApi(Client $client): OrdersApi
return new OrdersApi($client);
}

public function initializePaymentLinksApi(Client $client):PaymentLinksApi
{
return new PaymentLinksApi($client);
}

/**
* @param Client $client
* @return PosPayment
Expand Down
Loading

0 comments on commit e71f6fd

Please sign in to comment.