Skip to content

Commit

Permalink
[ECP-8707] Setup custom headers for each request made to Adyen (#2444)
Browse files Browse the repository at this point in the history
* setup custom headers in each request to adyen

* add unit tests for newly added methods in data helper

* add unit tests for TransactionPayment

* add unit test to assert the headers in the request

* added unit tests for PaymentDetails helper

* remove comments, redundant code and add namespace

* add transaction refund client unit test for custom headers

* remove code smells

* Update Gateway/Request/CheckoutDataBuilder.php

Co-authored-by: Can Demiralp <ecandemiralp@gmail.com>

---------

Co-authored-by: Can Demiralp <ecandemiralp@gmail.com>
Co-authored-by: Khushboo <khushboo.singhvi@adyen.com>
  • Loading branch information
3 people authored Jan 25, 2024
1 parent e78b9ec commit 8f14f60
Show file tree
Hide file tree
Showing 15 changed files with 423 additions and 10 deletions.
1 change: 1 addition & 0 deletions Controller/Return/Index.php
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,7 @@ protected function validatePayloadAndReturnResponse(array $result): array

$request["details"] = $details;
$requestOptions['idempotencyKey'] = $this->idempotencyHelper->generateIdempotencyKey($request);
$requestOptions['headers'] = $this->adyenDataHelper->buildRequestHeaders();

try {
$response = $service->paymentsDetails($request, $requestOptions);
Expand Down
1 change: 1 addition & 0 deletions Gateway/Http/Client/TransactionCancel.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ public function placeRequest(TransferInterface $transferObject): array
$headers['idempotencyExtraData'] ?? null
);
$requestOptions['idempotencyKey'] = $idempotencyKey;
$requestOptions['headers'] = $this->adyenHelper->buildRequestHeaders();
$this->adyenHelper->logRequest($requests, Client::API_CHECKOUT_VERSION, '/cancels');
try {
$responses = $service->cancels($requests, $requestOptions);
Expand Down
1 change: 1 addition & 0 deletions Gateway/Http/Client/TransactionCapture.php
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ public function placeRequest(TransferInterface $transferObject): array
);

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

if (array_key_exists(self::MULTIPLE_AUTHORIZATIONS, $request)) {
return $this->placeMultipleCaptureRequests($service, $request, $requestOptions);
Expand Down
1 change: 1 addition & 0 deletions Gateway/Http/Client/TransactionDonate.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ public function placeRequest(TransferInterface $transferObject)
);

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

$this->adyenHelper->logRequest($request, Client::API_CHECKOUT_VERSION, 'donations');
try {
Expand Down
1 change: 1 addition & 0 deletions Gateway/Http/Client/TransactionPayment.php
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ public function placeRequest(TransferInterface $transferObject): array
$headers['idempotencyExtraData'] ?? null
);
$requestOptions['idempotencyKey'] = $idempotencyKey;
$requestOptions['headers'] = $this->adyenHelper->buildRequestHeaders();

$this->adyenHelper->logRequest($request, Client::API_CHECKOUT_VERSION, '/payments');
$response = $service->payments($request, $requestOptions);
Expand Down
1 change: 1 addition & 0 deletions Gateway/Http/Client/TransactionPaymentLinks.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ public function placeRequest(TransferInterface $transferObject): array
);

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

$this->adyenHelper->logRequest($request, Client::API_CHECKOUT_VERSION, '/paymentLinks');
try {
Expand Down
3 changes: 2 additions & 1 deletion Gateway/Http/Client/TransactionRefund.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,9 @@ public function placeRequest(TransferInterface $transferObject): array
$headers['idempotencyExtraData'] ?? null
);
$requestOptions['idempotencyKey'] = $idempotencyKey;
$this->adyenHelper->logRequest($request, Client::API_CHECKOUT_VERSION, '/refunds');
$requestOptions['headers'] = $this->adyenHelper->buildRequestHeaders();

$this->adyenHelper->logRequest($request, Client::API_CHECKOUT_VERSION, '/refunds');
try {
$response = $service->refunds($request, $requestOptions);
// Add amount original reference and amount information to response
Expand Down
4 changes: 0 additions & 4 deletions Gateway/Request/CheckoutDataBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -210,10 +210,6 @@ public function build(array $buildSubject): array

$requestBody['additionalData']['allow3DS2'] = true;

if (isset($requestBodyPaymentMethod)) {
$requestBody['paymentMethod'] = $requestBodyPaymentMethod;
}

return [
'body' => $requestBody
];
Expand Down
27 changes: 24 additions & 3 deletions Helper/Data.php
Original file line number Diff line number Diff line change
Expand Up @@ -1146,9 +1146,9 @@ public function initializeAdyenClient(
$client->setRegion($checkoutFrontendRegion);
}

$moduleVersion = $this->getModuleVersion();
$client->setMerchantApplication($this->getModuleName(), $moduleVersion);
$client->setExternalPlatform($this->productMetadata->getName(), $this->productMetadata->getVersion(), 'Adyen');
$client->setMerchantApplication($this->getModuleName(), $this->getModuleVersion());
$platformData = $this->getMagentoDetails();
$client->setExternalPlatform($platformData['name'], $platformData['version'], 'Adyen');
if ($isDemo) {
$client->setEnvironment(Environment::TEST);
} else {
Expand All @@ -1158,6 +1158,27 @@ public function initializeAdyenClient(
return $client;
}

public function getMagentoDetails()
{
return [
'name' => $this->productMetadata->getName(),
'version' => $this->productMetadata->getVersion(),
'edition' => $this->productMetadata->getEdition(),
];
}

public function buildRequestHeaders()
{
$magentoDetails = $this->getMagentoDetails();
return [
'external-platform-name' => $this->getModuleName(),
'external-platform-version' => $this->getModuleVersion(),
'merchant-application-name' => $magentoDetails['name'],
'merchant-application-version' => $magentoDetails['version'],
'merchant-application-edition' => $magentoDetails['edition'],
];
}

/**
* @throws AdyenException
* @throws NoSuchEntityException
Expand Down
1 change: 1 addition & 0 deletions Helper/PaymentsDetails.php
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ public function initiatePaymentDetails(OrderInterface $order, string $payload):
$service = $this->adyenHelper->createAdyenCheckoutService($client);

$requestOptions['idempotencyKey'] = $this->idempotencyHelper->generateIdempotencyKey($apiPayload);
$requestOptions['headers'] = $this->adyenHelper->buildRequestHeaders();

$paymentDetails = $service->paymentsDetails($apiPayload, $requestOptions);
} catch (AdyenException $e) {
Expand Down
165 changes: 165 additions & 0 deletions Test/Unit/Gateway/Http/Client/TransactionPaymentTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
<?php
/**
*
* Adyen Payment module (https://www.adyen.com/)
*
* Copyright (c) 2024 Adyen N.V. (https://www.adyen.com/)
* See LICENSE.txt for license details.
*
* Author: Adyen <magento@adyen.com>
*/

namespace Adyen\Payment\Test\Unit\Gateway\Http\Client;

use Adyen\Payment\Api\Data\PaymentResponseInterface;
use Adyen\Payment\Model\PaymentResponse;
use Adyen\Payment\Test\Unit\AbstractAdyenTestCase;
use Adyen\Service\Checkout;
use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;
use Adyen\Payment\Helper\Data;
use Adyen\Payment\Model\PaymentResponseFactory;
use Adyen\Payment\Model\ResourceModel\PaymentResponse as PaymentResponseResourceModel;
use Adyen\Payment\Helper\Idempotency;
use Adyen\Payment\Helper\OrdersApi;
use Adyen\Payment\Gateway\Http\Client\TransactionPayment;
use Magento\Store\Model\StoreManagerInterface;
use Adyen\Payment\Helper\GiftcardPayment;
use Magento\Payment\Gateway\Http\TransferInterface;

class TransactionPaymentTest extends AbstractAdyenTestCase
{
private $adyenHelperMock;
private $paymentResponseFactoryMock;
private $paymentResponseResourceModelMock;
private $idempotencyHelperMock;
private $orderApiHelperMock;
private $storeManagerMock;
private $giftcardPaymentHelperMock;
private $transactionPayment;

protected function setUp(): void
{
$objectManager = new ObjectManager($this);

$this->adyenHelperMock = $this->createMock(Data::class);
$this->paymentResponseResourceModelMock = $this->createMock(PaymentResponseResourceModel::class);
$this->idempotencyHelperMock = $this->createMock(Idempotency::class);
$this->orderApiHelperMock = $this->createMock(OrdersApi::class);
$this->storeManagerMock = $this->createMock(StoreManagerInterface::class);
$this->giftcardPaymentHelperMock = $this->createMock(GiftcardPayment::class);
$paymentResponseInterfaceMock = $this->createMock(PaymentResponseInterface::class);
$paymentResponseMock = $this->createMock(PaymentResponse::class);
$paymentResponseMock->method('setResponse')->willReturn($paymentResponseInterfaceMock);
$paymentResponseMock->method('setResultCode')->willReturn($paymentResponseInterfaceMock);
$paymentResponseMock->method('setMerchantReference')->willReturn($paymentResponseInterfaceMock);
$this->paymentResponseFactoryMock = $this->createGeneratedMock(PaymentResponseFactory::class, ['create']);
$this->paymentResponseFactoryMock->method('create')->willReturn($paymentResponseMock);

$this->transactionPayment = $objectManager->getObject(
TransactionPayment::class,
[
'adyenHelper' => $this->adyenHelperMock,
'paymentResponseFactory' => $this->paymentResponseFactoryMock,
'paymentResponseResourceModel' => $this->paymentResponseResourceModelMock,
'idempotencyHelper' => $this->idempotencyHelperMock,
'orderApiHelper' => $this->orderApiHelperMock,
'storeManager' => $this->storeManagerMock,
'giftcardPaymentHelper' => $this->giftcardPaymentHelperMock,
]
);
}

public function testPlaceRequestWithResultCode()
{
$transferObjectMock = $this->createMock(TransferInterface::class);
$requestBody = ['resultCode' => 'Authorised', 'amount' => ['value' => 1000]];
$transferObjectMock->method('getBody')->willReturn($requestBody);

$result = $this->transactionPayment->placeRequest($transferObjectMock);

$this->assertEquals($requestBody, $result);
}

public function testPlaceRequestGeneratesIdempotencyKey()
{
$requestBody = ['reference' => 'ABC12345', 'amount' => ['value' => 100]];
$transferObjectMock = $this->createConfiguredMock(TransferInterface::class, [
'getBody' => $requestBody,
'getHeaders' => ['idempotencyExtraData' => ['someData']],
'getClientConfig' => []
]);

$expectedIdempotencyKey = 'generated_idempotency_key';
$this->idempotencyHelperMock->expects($this->once())
->method('generateIdempotencyKey')
->with(
$this->equalTo(['reference' => 'ABC12345', 'amount' => ['value' => 100]]),
$this->equalTo(['someData'])
)
->willReturn($expectedIdempotencyKey);

$mockedPaymentResponse = [
'reference' => 'ABC12345',
'amount' => ['value' => 100],
'resultCode' => 'Authorised'
];
$serviceMock = $this->createMock(Checkout::class);
$serviceMock->expects($this->once())
->method('payments')
->with(
$this->anything(),
$this->callback(function ($requestOptions) use ($expectedIdempotencyKey) {
return isset($requestOptions['idempotencyKey']) &&
$requestOptions['idempotencyKey'] === $expectedIdempotencyKey;
})
)
->willReturn($mockedPaymentResponse);

$this->adyenHelperMock->method('createAdyenCheckoutService')->willReturn($serviceMock);

$response = $this->transactionPayment->placeRequest($transferObjectMock);

$this->assertArrayHasKey('resultCode', $response);
$this->assertEquals('Authorised', $response['resultCode']);
}

public function testRequestHeadersAreAddedToPaymentsCall()
{
$requestBody = ['reference' => 'ABC12345', 'amount' => ['value' => 1000]];
$expectedHeaders = ['header1' => 'value1', 'header2' => 'value2'];

$transferObjectMock = $this->createConfiguredMock(TransferInterface::class, [
'getBody' => ['reference' => 'ABC12345', 'amount' => ['value' => 1000]],
'getHeaders' => ['header1' => 'value1', 'header2' => 'value2'],
'getClientConfig' => []
]);

$this->adyenHelperMock->expects($this->once())
->method('buildRequestHeaders')
->willReturn($expectedHeaders);

$mockedPaymentResponse = [
'reference' => 'ABC12345',
'amount' => ['value' => 100],
'resultCode' => 'Authorised'
];

$serviceMock = $this->createMock(Checkout::class);
$serviceMock->expects($this->once())
->method('payments')
->with(
$this->equalTo($requestBody),
$this->callback(function ($requestOptions) use ($expectedHeaders) {
return isset($requestOptions['headers']) && $requestOptions['headers'] === $expectedHeaders;
})
)
->willReturn($mockedPaymentResponse);

$this->adyenHelperMock->method('createAdyenCheckoutService')->willReturn($serviceMock);

$response = $this->transactionPayment->placeRequest($transferObjectMock);

$this->assertArrayHasKey('resultCode', $response);
$this->assertEquals('Authorised', $response['resultCode']);
}
}
98 changes: 98 additions & 0 deletions Test/Unit/Gateway/Http/Client/TransactionRefundTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
<?php
/**
*
* Adyen Payment module (https://www.adyen.com/)
*
* Copyright (c) 2024 Adyen N.V. (https://www.adyen.com/)
* See LICENSE.txt for license details.
*
* Author: Adyen <magento@adyen.com>
*/

namespace Adyen\Payment\Test\Unit\Gateway\Http\Client;

use Adyen\Client;
use Adyen\Payment\Gateway\Http\Client\TransactionRefund;
use Adyen\Payment\Helper\Data;
use Adyen\Payment\Helper\Idempotency;
use Adyen\Payment\Test\Unit\AbstractAdyenTestCase;
use Adyen\Service\Checkout;
use Magento\Payment\Gateway\Http\TransferInterface;
use PHPUnit\Framework\MockObject\MockObject;

class TransactionRefundTest extends AbstractAdyenTestCase
{
/**
* @var Data|MockObject
*/
private $adyenHelperMock;

/**
* @var Idempotency|MockObject
*/
private $idempotencyHelperMock;

/**
* @var TransactionRefund
*/
private $transactionRefund;

protected function setUp(): void
{
$this->adyenHelperMock = $this->createMock(Data::class);
$this->idempotencyHelperMock = $this->createMock(Idempotency::class);

$this->transactionRefund = new TransactionRefund(
$this->adyenHelperMock,
$this->idempotencyHelperMock
);
}

public function testPlaceRequestIncludesHeadersInRequest()
{
$requestBody = [
'amount' => ['value' => 1000, 'currency' => 'EUR'],
'paymentPspReference' => '123456789'
];

$headers = ['idempotencyExtraData' => ['order_id' => '1001']];

$transferObjectMock = $this->createConfiguredMock(TransferInterface::class, [
'getBody' => [$requestBody],
'getHeaders' => $headers,
'getClientConfig' => []
]);

$checkoutServiceMock = $this->createMock(Checkout::class);
$adyenClientMock = $this->createMock(Client::class);

$this->adyenHelperMock->method('initializeAdyenClientWithClientConfig')->willReturn($adyenClientMock);
$this->adyenHelperMock->method('createAdyenCheckoutService')->willReturn($checkoutServiceMock);
$this->adyenHelperMock->method('buildRequestHeaders')->willReturn(['custom-header' => 'value']);

$this->idempotencyHelperMock->expects($this->once())
->method('generateIdempotencyKey')
->with($requestBody, $headers['idempotencyExtraData'])
->willReturn('generated_idempotency_key');

$checkoutServiceMock->expects($this->once())
->method('refunds')
->with(
$this->equalTo($requestBody),
$this->callback(function ($requestOptions) {
$this->assertArrayHasKey('idempotencyKey', $requestOptions);
$this->assertArrayHasKey('headers', $requestOptions);
$this->assertEquals('generated_idempotency_key', $requestOptions['idempotencyKey']);
$this->assertArrayHasKey('custom-header', $requestOptions['headers']);
return true;
})
)
->willReturn(['pspReference' => 'refund_psp_reference']);

$responses = $this->transactionRefund->placeRequest($transferObjectMock);

$this->assertIsArray($responses);
$this->assertCount(1, $responses);
$this->assertArrayHasKey('pspReference', $responses[0]);
}
}
Loading

0 comments on commit 8f14f60

Please sign in to comment.