diff --git a/src/Adyen/Client.php b/src/Adyen/Client.php index 004f427cc..2b717cedc 100644 --- a/src/Adyen/Client.php +++ b/src/Adyen/Client.php @@ -222,6 +222,17 @@ public function setAdyenPaymentSource($name, $version) $this->config->set('adyenPaymentSource', array('name' => $name, 'version' => $version)); } + /** + * Set merchant application name and version + * + * @param string $name + * @param string $version + */ + public function setMerchantApplication($name, $version) + { + $this->config->set('merchantApplication', array('name' => $name, 'version' => $version)); + } + /** * Type can be json or array * diff --git a/src/Adyen/Config.php b/src/Adyen/Config.php index ce2baf079..c9bd9184c 100644 --- a/src/Adyen/Config.php +++ b/src/Adyen/Config.php @@ -150,6 +150,14 @@ public function getAdyenPaymentSource() return isset($this->data['adyenPaymentSource']) ? $this->data['adyenPaymentSource'] : null; } + /** + * @return array|null an array with 'name' and 'version' + */ + public function getMerchantApplication() + { + return isset($this->data['merchantApplication']) ? $this->data['merchantApplication'] : null; + } + /** * @return mixed|null */ diff --git a/src/Adyen/ConfigInterface.php b/src/Adyen/ConfigInterface.php index 0962ab279..9a2aef873 100644 --- a/src/Adyen/ConfigInterface.php +++ b/src/Adyen/ConfigInterface.php @@ -2,6 +2,11 @@ namespace Adyen; +/** + * Interface ConfigInterface + * @deprecated Please do not use your own interface as we will deprecate this in the future for improvements on the library + * @package Adyen + */ Interface ConfigInterface { diff --git a/src/Adyen/Service/AbstractResource.php b/src/Adyen/Service/AbstractResource.php index 4891fdb89..85bc100fc 100644 --- a/src/Adyen/Service/AbstractResource.php +++ b/src/Adyen/Service/AbstractResource.php @@ -19,18 +19,25 @@ abstract class AbstractResource */ protected $allowApplicationInfo; - /** - * AbstractResource constructor. - * - * @param \Adyen\Service $service - * @param $endpoint - * @param bool $allowApplicationInfo - */ - public function __construct(\Adyen\Service $service, $endpoint, $allowApplicationInfo = false) + /** + * @var bool + */ + protected $allowApplicationInfoPOS; + + /** + * AbstractResource constructor. + * + * @param \Adyen\Service $service + * @param string $endpoint + * @param bool $allowApplicationInfo + * @param bool $allowApplicationInfoPOS + */ + public function __construct(\Adyen\Service $service, $endpoint, $allowApplicationInfo = false, $allowApplicationInfoPOS = false) { $this->service = $service; $this->endpoint = $endpoint; $this->allowApplicationInfo = $allowApplicationInfo; + $this->allowApplicationInfoPOS = $allowApplicationInfoPOS; } /** @@ -61,7 +68,16 @@ public function request($params, $requestOptions = null) $params = $this->addDefaultParametersToRequest($params); - $params = $this->handleApplicationInfoInRequest($params); + if ($this->allowApplicationInfo) { + $params = $this->handleApplicationInfoInRequest($params); + } elseif ($this->allowApplicationInfoPOS) { + $params = $this->handleApplicationInfoInRequestPOS($params); + } else { + // remove if exists + if (isset($params['applicationInfo'])) { + unset($params['applicationInfo']); + } + } $curlClient = $this->service->getClient()->getHttpClient(); return $curlClient->requestJson($this->service, $this->endpoint, $params, $requestOptions); @@ -110,33 +126,89 @@ private function addDefaultParametersToRequest($params) */ private function handleApplicationInfoInRequest($params) { - // Only add if allowed - if ($this->allowApplicationInfo) { - // add/overwrite applicationInfo adyenLibrary even if it's already set - $params['applicationInfo']['adyenLibrary']['name'] = $this->service->getClient()->getLibraryName(); - $params['applicationInfo']['adyenLibrary']['version'] = $this->service->getClient()->getLibraryVersion(); - - if ($adyenPaymentSource = $this->service->getClient()->getConfig()->getAdyenPaymentSource()) { - $params['applicationInfo']['adyenPaymentSource']['version'] = $adyenPaymentSource['version']; - $params['applicationInfo']['adyenPaymentSource']['name'] = $adyenPaymentSource['name']; - } + // add/overwrite applicationInfo adyenLibrary even if it's already set + $params['applicationInfo']['adyenLibrary']['name'] = $this->service->getClient()->getLibraryName(); + $params['applicationInfo']['adyenLibrary']['version'] = $this->service->getClient()->getLibraryVersion(); - if ($externalPlatform = $this->service->getClient()->getConfig()->getExternalPlatform()) { - $params['applicationInfo']['externalPlatform']['version'] = $externalPlatform['version']; - $params['applicationInfo']['externalPlatform']['name'] = $externalPlatform['name']; + if ($adyenPaymentSource = $this->service->getClient()->getConfig()->getAdyenPaymentSource()) { + $params['applicationInfo']['adyenPaymentSource']['version'] = $adyenPaymentSource['version']; + $params['applicationInfo']['adyenPaymentSource']['name'] = $adyenPaymentSource['name']; + } - if (!empty($externalPlatform['integrator'])) { - $params['applicationInfo']['externalPlatform']['integrator'] = $externalPlatform['integrator']; - } - } + if ($externalPlatform = $this->service->getClient()->getConfig()->getExternalPlatform()) { + $params['applicationInfo']['externalPlatform']['version'] = $externalPlatform['version']; + $params['applicationInfo']['externalPlatform']['name'] = $externalPlatform['name']; + + if (!empty($externalPlatform['integrator'])) { + $params['applicationInfo']['externalPlatform']['integrator'] = $externalPlatform['integrator']; + } + } + + if ($merchantApplication = $this->service->getClient()->getConfig()->getMerchantApplication()) { + $params['applicationInfo']['merchantApplication']['version'] = $merchantApplication['version']; + $params['applicationInfo']['merchantApplication']['name'] = $merchantApplication['name']; + } - } else { - // remove if exists - if (isset($params['applicationInfo'])) { - unset($params['applicationInfo']); - } - } return $params; } + + /** + * If allowApplicationInfoPOS is true, this function does the following: + * 1) Converts SaleToAcquirerData to array(from querystring or base64encoded) + * 2) Adds ApplicationInfo to the array + * 3) Base64 encodes SaleToAcquirerData + * + * @param $params + * @return array|null + */ + private function handleApplicationInfoInRequestPOS(array $params) + { + //If the POS request is not a payment request, do not add application info + if (empty($params['SaleToPOIRequest']['PaymentRequest'])) { + return $params; + } + + // Initialize $saleToAcquirerData + $saleToAcquirerData = array(); + + if (!empty($params['SaleToPOIRequest']['PaymentRequest']['SaleData']['SaleToAcquirerData'])) { + $saleToAcquirerData = $params['SaleToPOIRequest']['PaymentRequest']['SaleData']['SaleToAcquirerData']; + + //If SaleToAcquirerData is a querystring convert it to array + parse_str($saleToAcquirerData, $queryString); + $queryStringValues = array_values($queryString); + + //check if querystring is nonempty and contains a value + if (!empty($queryString) && !empty($queryStringValues[0])) { + $saleToAcquirerData = $queryString; + } + + //If SaleToAcquirerData is a base64encoded string decode it and convert it to array + elseif ($this->isBase64Encoded($saleToAcquirerData)) { + $saleToAcquirerData = json_decode(base64_decode($saleToAcquirerData, true), true); + } + } + + //add Application Information + $saleToAcquirerData = $this->handleApplicationInfoInRequest($saleToAcquirerData); + $saleToAcquirerData = base64_encode(json_encode($saleToAcquirerData)); + $params['SaleToPOIRequest']['PaymentRequest']['SaleData']['SaleToAcquirerData'] = $saleToAcquirerData; + + return $params; + + } + + /** + * @param $data + * @return bool + */ + private function isBase64Encoded($data) + { + if (preg_match('%^[a-zA-Z0-9/+]*={0,2}$%', $data) && !empty($data)) { + return true; + } else { + return false; + } + } } diff --git a/src/Adyen/Service/ResourceModel/Payment/TerminalCloudAPI.php b/src/Adyen/Service/ResourceModel/Payment/TerminalCloudAPI.php index 18efafbd8..dbf13ce83 100644 --- a/src/Adyen/Service/ResourceModel/Payment/TerminalCloudAPI.php +++ b/src/Adyen/Service/ResourceModel/Payment/TerminalCloudAPI.php @@ -9,6 +9,20 @@ class TerminalCloudAPI extends \Adyen\Service\AbstractResource */ protected $endpoint; + /** + * Include applicationInfo key in the request parameters + * + * @var bool + */ + protected $allowApplicationInfo = false; + + /** + * Include applicationInfo key in the request parameters + * + * @var bool + */ + protected $allowApplicationInfoPOS = true; + /** * TerminalCloudAPI constructor. * @@ -22,6 +36,6 @@ public function __construct($service, $asynchronous) } else { $this->endpoint = $service->getClient()->getConfig()->get('endpointTerminalCloud') . '/sync'; } - parent::__construct($service, $this->endpoint); + parent::__construct($service, $this->endpoint, $this->allowApplicationInfo, $this->allowApplicationInfoPOS); } } diff --git a/tests/PosPaymentTest.php b/tests/PosPaymentTest.php index 9a4a361ac..5b617af56 100644 --- a/tests/PosPaymentTest.php +++ b/tests/PosPaymentTest.php @@ -9,7 +9,7 @@ class PosPaymentTest extends TestCase public function testCreatePosPaymentSuccess() { - if (empty($this->settings['POIID']) || $this->settings['POIID'] = 'UNIQUETERMINALID') { + if (empty($this->settings['POIID']) || $this->settings['POIID'] == 'UNIQUETERMINALID') { $this->skipTest("Skipped the test. Configure your POIID in the config"); } @@ -74,7 +74,7 @@ public function testCreatePosPaymentSuccess() public function testCreatePosPaymentDeclined() { - if (empty($this->settings['POIID']) || $this->settings['POIID'] = 'UNIQUETERMINALID') { + if (empty($this->settings['POIID']) || $this->settings['POIID'] == 'UNIQUETERMINALID') { $this->skipTest("Skipped the test. Configure your POIID in the config"); } @@ -138,7 +138,7 @@ public function testCreatePosPaymentDeclined() public function testCreatePosEMVRefundSuccess() { - if (empty($this->settings['POIID']) || $this->settings['POIID'] = 'UNIQUETERMINALID') { + if (empty($this->settings['POIID']) || $this->settings['POIID'] == 'UNIQUETERMINALID') { $this->skipTest("Skipped the test. Configure your POIID in the config"); } @@ -200,20 +200,38 @@ public function testCreatePosEMVRefundSuccess() } - /** - * After timeout, an Exception will be returned with code: CURLE_OPERATION_TIMEOUTED - * @covers \Adyen\HttpClient\CurlClient::handleCurlError - */ - public function testPosPaymentFailTimeout() + public function testGetConnectedTerminals() + { + // initialize client + $client = $this->createTerminalCloudAPIClient(); + + // initialize service + $service = new Service\PosPayment($client); + + //Construct request + $json = '{ + "merchantAccount": "' . $this->settings['merchantAccount'] . '" + }'; + + $params = json_decode($json, true); //Create associative array for passing along + + try { + $result = $service->getConnectedTerminals($params); + } catch (\Exception $e) { + $this->validateApiPermission($e); + } + + $this->assertTrue(isset($result['uniqueTerminalIds'])); + } + + public function testCreatePosPaymentSuccessWithOneclickQuerystring() { - if (empty($this->settings['POIID']) || $this->settings['POIID'] = 'UNIQUETERMINALID') { + if (empty($this->settings['POIID']) || $this->settings['POIID'] == 'UNIQUETERMINALID') { $this->skipTest("Skipped the test. Configure your POIID in the config"); } // initialize client $client = $this->createTerminalCloudAPIClient(); - $client->setTimeout(1); - // initialize service $service = new Service\PosPayment($client); @@ -236,7 +254,7 @@ public function testPosPaymentFailTimeout() "PaymentRequest": { "SaleData": { "SaleTransactionID": { - "TransactionID": "POStimeout", + "TransactionID": "POSauthWithOneclickQuerystring", "TimeStamp": "' . $timeStamper . '" }, "TokenRequestedType": "Customer", @@ -258,38 +276,175 @@ public function testPosPaymentFailTimeout() $params = json_decode($json, true); //Create associative array for passing along + $recurringDetails = array( + 'shopperEmail' => "save@oneclick.card", + 'shopperReference' => strval(300), + 'recurringContract' => "ONECLICK" + ); + $params['SaleToPOIRequest']['PaymentRequest']['SaleData']['SaleToAcquirerData'] = http_build_query($recurringDetails); try { $result = $service->runTenderSync($params); + } catch (\Exception $e) { + $this->validateApiPermission($e); + } + + $this->assertTrue(isset($result['SaleToPOIResponse'])); + $this->assertEquals('Success', $result['SaleToPOIResponse']['PaymentResponse']['Response']['Result']); + try { + $additionalResponse = json_decode(base64_decode($result['SaleToPOIResponse']['PaymentResponse']['Response']['AdditionalResponse']), true); + $this->assertNotNull($additionalResponse['additionalData']['recurring.recurringDetailReference']); + } + catch (\Exception $e){ $this->fail(); + } + + } + + public function testCreatePosPaymentSuccessWithOneclickBase64() + { + if (empty($this->settings['POIID']) || $this->settings['POIID'] == 'UNIQUETERMINALID') { + $this->skipTest("Skipped the test. Configure your POIID in the config"); + } + + // initialize client + $client = $this->createTerminalCloudAPIClient(); + // initialize service + $service = new Service\PosPayment($client); + + //Set merchantApplication + $client->setMerchantApplication("merchantPosApplication", "0.0.test"); + + //Construct request + $transactionType = \Adyen\TransactionType::NORMAL; + $serviceID = date("dHis"); + $timeStamper = date("Y-m-d") . "T" . date("H:i:s+00:00"); + + $json = '{ + "SaleToPOIRequest": { + "MessageHeader": { + "MessageType": "Request", + "MessageClass": "Service", + "MessageCategory": "Payment", + "SaleID": "PosTestLibrary", + "POIID": "' . $this->getPOIID() . '", + "ProtocolVersion": "3.0", + "ServiceID": "' . $serviceID . '" + }, + "PaymentRequest": { + "SaleData": { + "SaleTransactionID": { + "TransactionID": "POSauthWithOneclickBase64", + "TimeStamp": "' . $timeStamper . '" + }, + "TokenRequestedType": "Customer", + "SaleReferenceID": "SalesRefABC" + }, + "PaymentTransaction": { + "AmountsReq": { + "Currency": "EUR", + "RequestedAmount": ' . 14.91 . ' + } + }, + "PaymentData": { + "PaymentType": "' . $transactionType . '" + } + } + } + } + '; + + $params = json_decode($json, true); //Create associative array for passing along + + $recurringDetails = array( + 'shopperEmail' => "save@oneclick.card", + 'shopperReference' => strval(301), + 'recurringContract' => "ONECLICK" + ); + $params['SaleToPOIRequest']['PaymentRequest']['SaleData']['SaleToAcquirerData'] = base64_encode(json_encode($recurringDetails)); + try { + $result = $service->runTenderSync($params); } catch (\Exception $e) { - $this->assertEquals(CURLE_OPERATION_TIMEOUTED, $e->getCode()); $this->validateApiPermission($e); } + $this->assertTrue(isset($result['SaleToPOIResponse'])); + $this->assertEquals('Success', $result['SaleToPOIResponse']['PaymentResponse']['Response']['Result']); + try { + $additionalResponse = json_decode(base64_decode($result['SaleToPOIResponse']['PaymentResponse']['Response']['AdditionalResponse']), + true); + $this->assertNotNull($additionalResponse['additionalData']['recurring.recurringDetailReference']); + } catch (\Exception $e) { + $this->fail(); + } } - public function testGetConnectedTerminals() + /** + * After timeout, an Exception will be returned with code: CURLE_OPERATION_TIMEOUTED + * @covers \Adyen\HttpClient\CurlClient::handleCurlError + */ + public function testPosPaymentFailTimeout() { + if (empty($this->settings['POIID']) || $this->settings['POIID'] == 'UNIQUETERMINALID') { + $this->skipTest("Skipped the test. Configure your POIID in the config"); + } + // initialize client $client = $this->createTerminalCloudAPIClient(); + $client->setTimeout(1); // initialize service $service = new Service\PosPayment($client); //Construct request + $transactionType = \Adyen\TransactionType::NORMAL; + $serviceID = date("dHis"); + $timeStamper = date("Y-m-d") . "T" . date("H:i:s+00:00"); + $json = '{ - "merchantAccount": "' . $this->settings['merchantAccount'] . '" - }'; + "SaleToPOIRequest": { + "MessageHeader": { + "MessageType": "Request", + "MessageClass": "Service", + "MessageCategory": "Payment", + "SaleID": "PosTestLibrary", + "POIID": "' . $this->getPOIID() . '", + "ProtocolVersion": "3.0", + "ServiceID": "' . $serviceID . '" + }, + "PaymentRequest": { + "SaleData": { + "SaleTransactionID": { + "TransactionID": "POStimeout", + "TimeStamp": "' . $timeStamper . '" + }, + "TokenRequestedType": "Customer", + "SaleReferenceID": "SalesRefABC" + }, + "PaymentTransaction": { + "AmountsReq": { + "Currency": "EUR", + "RequestedAmount": ' . 14.91 . ' + } + }, + "PaymentData": { + "PaymentType": "' . $transactionType . '" + } + } + } + } + '; $params = json_decode($json, true); //Create associative array for passing along try { - $result = $service->getConnectedTerminals($params); + $result = $service->runTenderSync($params); + $this->fail(); } catch (\Exception $e) { + $this->assertEquals(CURLE_OPERATION_TIMEOUTED, $e->getCode()); $this->validateApiPermission($e); } - $this->assertTrue(isset($result['uniqueTerminalIds'])); } + } diff --git a/tests/Unit/AbstractResourceTest.php b/tests/Unit/AbstractResourceTest.php index 80269eae2..2ca133e30 100644 --- a/tests/Unit/AbstractResourceTest.php +++ b/tests/Unit/AbstractResourceTest.php @@ -9,36 +9,6 @@ class AbstractResourceTest extends TestCase */ private $className = "Adyen\Service\AbstractResource"; - /** - * If the allowApplicationInfo is false the applicationInfo key should be removed from the params - * - * @covers \Adyen\Service\AbstractResource::handleApplicationInfoInRequest - */ - public function testHandleApplicationInfoInRequestShouldRemoveApplicationInfoFromParams() - { - $params = array( - "applicationInfo" => array( - "adyenLibrary" => array( - "name" => "test", - "version" => "test" - ) - ) - ); - - // Mock client without the Test ini settings - $mockedClient = $this->createClientWithoutTestIni(); - - // Mock abstract class with mocked client and $paramsToFilter parameters - $mockedClass = $this->getMockForAbstractClass($this->className, array((new \Adyen\Service($mockedClient)), "", false)); - - // Get private method as testable public method - $method = $this->getMethod($this->className, "handleApplicationInfoInRequest"); - - // Test against function - $result = $method->invokeArgs($mockedClass, array($params)); - $this->assertArrayNotHasKey("applicationInfo", $result); - } - /** * If the allowApplicationInfo is true the applicationInfo Adyen Library should be overwritten with the real values * @@ -164,4 +134,199 @@ public function testHandleApplicationInfoInRequestShouldNotAddApplicationInfoAdy $this->assertArrayHasKey("externalPlatform", $result["applicationInfo"]); $this->assertArrayNotHasKey("integrator", $result["applicationInfo"]["externalPlatform"]); } + + public function testHandleApplicationInfoInRequestPOSShouldAddBase64EncodedApplicationInfo() + { + $json = '{ + "SaleToPOIRequest": { + "MessageHeader": { + "MessageType": "Request", + "MessageClass": "Service", + "MessageCategory": "Payment", + "SaleID": "PosTestLibrary", + "POIID": "' . "POS-432123" . '", + "ProtocolVersion": "3.0", + "ServiceID": "' . "serviceID" . '" + }, + "PaymentRequest": { + "SaleData": { + "SaleTransactionID": { + "TransactionID": "POSauth", + "TimeStamp": "' . "time" . '" + }, + "TokenRequestedType": "Customer", + "SaleReferenceID": "SalesRefABC" + }, + "PaymentTransaction": { + "AmountsReq": { + "Currency": "EUR", + "RequestedAmount": ' . 14.91 . ' + } + }, + "PaymentData": { + "PaymentType": "' . \Adyen\TransactionType::NORMAL . '" + } + } + } + } + '; + + $params = json_decode($json, true); //Create associative array for passing along + + // Mock client without the Test ini settings + $mockedClient = $this->createClientWithoutTestIni(); + + // Mock abstract class with mocked client and $paramsToFilter parameters + $mockedClass = $this->getMockForAbstractClass($this->className, array((new \Adyen\Service($mockedClient)), "", true)); + + // Get private method as testable public method + $method = $this->getMethod($this->className, "handleApplicationInfoInRequestPOS"); + + // Test against function + $result = $method->invokeArgs($mockedClass, array($params)); + $resultDecoded = base64_decode($result['SaleToPOIRequest']['PaymentRequest']['SaleData']['SaleToAcquirerData']); + $resultArray = json_decode($resultDecoded,true); + $this->assertArrayHasKey("applicationInfo", $resultArray); + $this->assertArrayHasKey("adyenLibrary", $resultArray['applicationInfo']); + + } + + public function testHandleApplicationInfoInRequestPOSWithQueryStringSaleToAcquirerDataAddBase64EncodedApplicationInfo() + { + $json = '{ + "SaleToPOIRequest": { + "MessageHeader": { + "MessageType": "Request", + "MessageClass": "Service", + "MessageCategory": "Payment", + "SaleID": "PosTestLibrary", + "POIID": "' . "POS-432123" . '", + "ProtocolVersion": "3.0", + "ServiceID": "' . "serviceID" . '" + }, + "PaymentRequest": { + "SaleData": { + "SaleTransactionID": { + "TransactionID": "POSauthWithOneclickQuerystring", + "TimeStamp": "' . "time" . '" + }, + "TokenRequestedType": "Customer", + "SaleReferenceID": "SalesRefABC" + }, + "PaymentTransaction": { + "AmountsReq": { + "Currency": "EUR", + "RequestedAmount": ' . 14.91 . ' + } + }, + "PaymentData": { + "PaymentType": "' . \Adyen\TransactionType::NORMAL . '" + } + } + } + } + '; + + $params = json_decode($json, true); //Create associative array for passing along + + $recurringDetails = array( + 'shopperEmail' => "save@oneclick.card", + 'shopperReference' => strval(300), + 'recurringContract' => "ONECLICK" + ); + + // Add RecurringDetails using querystring + $params['SaleToPOIRequest']['PaymentRequest']['SaleData']['SaleToAcquirerData'] = http_build_query($recurringDetails); + + // Mock client without the Test ini settings + $mockedClient = $this->createClientWithoutTestIni(); + + // Mock abstract class with mocked client and $paramsToFilter parameters + $mockedClass = $this->getMockForAbstractClass($this->className, array((new \Adyen\Service($mockedClient)), "", true)); + + // Get private method as testable public method + $method = $this->getMethod($this->className, "handleApplicationInfoInRequestPOS"); + + // Test against function + $result = $method->invokeArgs($mockedClass, array($params)); + + // Base64 decode the result and verify if all fields are still present + $resultDecoded = base64_decode($result['SaleToPOIRequest']['PaymentRequest']['SaleData']['SaleToAcquirerData']); + $resultArray = json_decode($resultDecoded,true); + $this->assertArrayHasKey("recurringContract", $resultArray); + $this->assertArrayHasKey("shopperEmail", $resultArray); + $this->assertArrayHasKey("shopperReference", $resultArray); + $this->assertArrayHasKey("applicationInfo", $resultArray); + $this->assertArrayHasKey("adyenLibrary", $resultArray['applicationInfo']); + + } + + public function testHandleApplicationInfoInRequestPOSWithBase64AddBase64EncodedApplicationInfo() + { + $json = '{ + "SaleToPOIRequest": { + "MessageHeader": { + "MessageType": "Request", + "MessageClass": "Service", + "MessageCategory": "Payment", + "SaleID": "PosTestLibrary", + "POIID": "' . "POS-432123" . '", + "ProtocolVersion": "3.0", + "ServiceID": "' . "serviceID" . '" + }, + "PaymentRequest": { + "SaleData": { + "SaleTransactionID": { + "TransactionID": "POSauthWithOneclickBase64", + "TimeStamp": "' . "time" . '" + }, + "TokenRequestedType": "Customer", + "SaleReferenceID": "SalesRefABC" + }, + "PaymentTransaction": { + "AmountsReq": { + "Currency": "EUR", + "RequestedAmount": ' . 14.91 . ' + } + }, + "PaymentData": { + "PaymentType": "' . \Adyen\TransactionType::NORMAL . '" + } + } + } + } + '; + + $params = json_decode($json, true); //Create associative array for passing along + + $recurringDetails = array( + 'shopperEmail' => "save@oneclick.card", + 'shopperReference' => strval(300), + 'recurringContract' => "ONECLICK" + ); + + // Add RecurringDetails using base64 + $params['SaleToPOIRequest']['PaymentRequest']['SaleData']['SaleToAcquirerData'] = base64_encode(json_encode($recurringDetails)); + + // Mock client without the Test ini settings + $mockedClient = $this->createClientWithoutTestIni(); + + // Mock abstract class with mocked client and $paramsToFilter parameters + $mockedClass = $this->getMockForAbstractClass($this->className, array((new \Adyen\Service($mockedClient)), "", true)); + + // Get private method as testable public method + $method = $this->getMethod($this->className, "handleApplicationInfoInRequestPOS"); + + // Test against function + $result = $method->invokeArgs($mockedClass, array($params)); + + // Base64 decode the result and verify if all fields are still present + $resultDecoded = base64_decode($result['SaleToPOIRequest']['PaymentRequest']['SaleData']['SaleToAcquirerData']); + $resultArray = json_decode($resultDecoded,true); + $this->assertArrayHasKey("recurringContract", $resultArray); + $this->assertArrayHasKey("shopperEmail", $resultArray); + $this->assertArrayHasKey("shopperReference", $resultArray); + $this->assertArrayHasKey("applicationInfo", $resultArray); + $this->assertArrayHasKey("adyenLibrary", $resultArray['applicationInfo']); + } }