diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 23ae528e2..0e620eca4 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1 +1 @@ -* @msilvagarcia @cyattilakiss @Aleffio @AlexandrosMor @rikterbeek @acampos1916 +* @msilvagarcia @cyattilakiss @AlexandrosMor @acampos1916 @Aleffio @rikterbeek diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index c2f8d72eb..a4b36576d 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -19,7 +19,21 @@ jobs: - name: Install dependencies run: composer install --prefer-dist --no-progress - - name: Run test suite + - name: Run integration tests + run: vendor/bin/phpunit --testsuite=integration --no-coverage + env: + INTEGRATION_USERNAME: ${{ secrets.INTEGRATION_USERNAME }} + INTEGRATION_PASSWORD: ${{ secrets.INTEGRATION_PASSWORD }} + INTEGRATION_X_API_KEY: ${{ secrets.INTEGRATION_X_API_KEY }} + INTEGRATION_MERCHANT_ACCOUNT: ${{ secrets.INTEGRATION_MERCHANT_ACCOUNT }} + INTEGRATION_SKIN_CODE: ${{ secrets.INTEGRATION_SKIN_CODE }} + INTEGRATION_HMAC_SIGNATURE: ${{ secrets.INTEGRATION_HMAC_SIGNATURE }} + INTEGRATION_STORE_PAYOUT_USERNAME: ${{ secrets.INTEGRATION_STORE_PAYOUT_USERNAME }} + INTEGRATION_STORE_PAYOUT_PASSWORD: ${{ secrets.INTEGRATION_STORE_PAYOUT_PASSWORD }} + INTEGRATION_REVIEW_PAYOUT_USERNAME: ${{ secrets.INTEGRATION_REVIEW_PAYOUT_USERNAME }} + INTEGRATION_REVIEW_PAYOUT_PASSWORD: ${{ secrets.INTEGRATION_REVIEW_PAYOUT_PASSWORD }} + + - name: Run unit tests run: vendor/bin/phpunit --testsuite=unit --coverage-clover build/clover.xml --log-junit build/tests-log.xml # PHPUnit generates absolute file paths and SonarCloud expects relative file paths. This command removes the diff --git a/phpunit.xml b/phpunit.xml index d791e48d2..f54081198 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -30,6 +30,9 @@ ./tests/Unit + + ./tests/Integration + ./tests diff --git a/src/Adyen/Client.php b/src/Adyen/Client.php index 7b456d07a..1169e8ad3 100644 --- a/src/Adyen/Client.php +++ b/src/Adyen/Client.php @@ -10,7 +10,7 @@ class Client { - const LIB_VERSION = "6.2.0"; + const LIB_VERSION = "6.3.0"; const LIB_NAME = "adyen-php-api-library"; const USER_AGENT_SUFFIX = "adyen-php-api-library/"; const ENDPOINT_TEST = "https://pal-test.adyen.com"; diff --git a/src/Adyen/Util/HmacSignature.php b/src/Adyen/Util/HmacSignature.php index f0fe8b8cd..0e9a2ed70 100644 --- a/src/Adyen/Util/HmacSignature.php +++ b/src/Adyen/Util/HmacSignature.php @@ -27,6 +27,7 @@ class HmacSignature { + const EVENT_CODE = "eventCode"; /** * @param string $hmacKey Can be found in Customer Area * @param array $params The response from Adyen @@ -69,7 +70,7 @@ private function getNotificationDataToSign($params) // `empty` treats too many value types as empty. `isset` should prevent some of these cases. $value = (isset($params['amount']['value'])) ? $params['amount']['value'] : ""; $currency = (!empty($params['amount']['currency'])) ? $params['amount']['currency'] : ""; - $eventCode = (!empty($params['eventCode'])) ? $params['eventCode'] : ""; + $eventCode = (!empty($params[self::EVENT_CODE])) ? $params[self::EVENT_CODE] : ""; $success = (!empty($params['success'])) ? $params['success'] : ""; $dataToSign = array( @@ -103,4 +104,59 @@ public function isValidNotificationHMAC($hmacKey, $params) return $expectedSign == $merchantSign; } + /** + * Returns true when the event code support HMAC validation + * + * @param $response + */ + public function isHmacSupportedEventCode($response) + { + $eventCodes = array( + "ADVICE_OF_DEBIT", + "AUTHORISATION", + "AUTHORISATION_PENDING", + "AUTHORISE_REFERRAL", + "CANCELLATION", + "CANCEL_OR_REFUND", + "CAPTURE", + "CAPTURE_FAILED", + "CAPTURE_WITH_EXTERNAL_AUTH", + "CHARGEBACK", + "CHARGEBACK_REVERSED", + "DEACTIVATE_RECURRING", + "FRAUD_ONLY", + "FUND_TRANSFER", + "HANDLED_EXTERNALLY", + "MANUAL_REVIEW_ACCEPT", + "NOTIFICATION_OF_CHARGEBACK", + "NOTIFICATION_OF_FRAUD", + "OFFER_CLOSED", + "ORDER_OPENED", + "PAIDOUT_REVERSED", + "PAYOUT_DECLINE", + "PAYOUT_EXPIRE", + "PAYOUT_THIRDPARTY", + "PREARBITRATION_LOST", + "PREARBITRATION_WON", + "PROCESS_RETRY", + "RECURRING_CONTRACT", + "REFUND", + "REFUNDED_REVERSED", + "REFUND_FAILED", + "REFUND_WITH_DATA", + "REQUEST_FOR_INFORMATION", + "SECOND_CHARGEBACK", + "SUBMIT_RECURRING", + "VOID_PENDING_REFUND", + "POSTPONED_REFUND", + "TECHNICAL_CANCEL", + "AUTHORISATION_ADJUSTMENT", + "CANCEL_AUTORESCUE", + "AUTORESCUE" + ); + if (array_key_exists(self::EVENT_CODE, $response) && in_array($response[self::EVENT_CODE], $eventCodes)) { + return true; + } + return false; + } } diff --git a/tests/BinLookupTest.php b/tests/Integration/BinLookupTest.php similarity index 96% rename from tests/BinLookupTest.php rename to tests/Integration/BinLookupTest.php index b62993aec..11e6ebd66 100644 --- a/tests/BinLookupTest.php +++ b/tests/Integration/BinLookupTest.php @@ -21,9 +21,10 @@ * */ -namespace Adyen; +namespace Adyen\Integration; -use Adyen\Util\Util; +use Adyen\TestCase; +use Adyen\Service; class BinLookupTest extends TestCase { diff --git a/tests/CheckoutTest.php b/tests/Integration/CheckoutTest.php similarity index 98% rename from tests/CheckoutTest.php rename to tests/Integration/CheckoutTest.php index 5b4dbe175..4713979c3 100644 --- a/tests/CheckoutTest.php +++ b/tests/Integration/CheckoutTest.php @@ -21,8 +21,10 @@ * */ -namespace Adyen; +namespace Adyen\Integration; +use Adyen\TestCase; +use Adyen\Service; use Adyen\Util\Uuid; class CheckoutTest extends TestCase diff --git a/tests/CreatePaymentRequestTest.php b/tests/Integration/CreatePaymentRequestTest.php similarity index 86% rename from tests/CreatePaymentRequestTest.php rename to tests/Integration/CreatePaymentRequestTest.php index 8b662ca41..dfc86fc7e 100644 --- a/tests/CreatePaymentRequestTest.php +++ b/tests/Integration/CreatePaymentRequestTest.php @@ -1,6 +1,30 @@ testCreatePaymentSuccess(); @@ -33,7 +58,7 @@ public function testCancelModification() public function testRefundModification() { // create a payment - require_once __DIR__.'/CreatePaymentRequestTest.php'; + require_once __DIR__ . '/CreatePaymentRequestTest.php'; $test = new CreatePaymentRequestTest(); $result = $test->testCreatePaymentSuccess(); @@ -63,7 +88,7 @@ public function testRefundModification() public function testAdjustDecreaseModification() { // create a payment - require_once __DIR__.'/CreatePaymentRequestTest.php'; + require_once __DIR__ . '/CreatePaymentRequestTest.php'; $test = new CreatePaymentRequestTest(); $result = $test->testCreatePaymentSuccess(); @@ -90,7 +115,7 @@ public function testAdjustDecreaseModification() public function testAdjustIncreaseModification() { // create a payment - require_once __DIR__.'/CreatePaymentRequestTest.php'; + require_once __DIR__ . '/CreatePaymentRequestTest.php'; $test = new CreatePaymentRequestTest(); $result = $test->testCreatePaymentSuccess(); diff --git a/tests/PayoutThirdPartyTest.php b/tests/Integration/PayoutThirdPartyTest.php similarity index 96% rename from tests/PayoutThirdPartyTest.php rename to tests/Integration/PayoutThirdPartyTest.php index 4e5664da6..9f3f16294 100644 --- a/tests/PayoutThirdPartyTest.php +++ b/tests/Integration/PayoutThirdPartyTest.php @@ -21,10 +21,23 @@ * */ -namespace Adyen; +namespace Adyen\Integration; +use Adyen\TestCase; +use Adyen\Service; + +/** + * Class PayoutThirdPartyTest + * + * @package Adyen\Integration + */ class PayoutThirdPartyTest extends TestCase { + protected function setUp(): void + { + $this->markTestIncomplete('Configure Payout accounts'); + } + public function testStoreDetailAndSubmitPayoutThirdPartyMissingReference() { // initialize client @@ -326,7 +339,7 @@ public function testConfirmPayoutThirdPartyInvalidReference() // check if exception is correct $this->assertEquals('Adyen\AdyenException', get_class($e)); $this->assertEquals( - 'Invalid Request: Original pspReference is invalid for this environment!', + 'Original pspReference required for this operation', $e->getMessage() ); } @@ -367,7 +380,7 @@ public function testDeclinePayoutThirdPartySuccess() public function testDeclinePayoutThirdPartyInvalidReference() { $this->expectException('Adyen\AdyenException'); - $this->expectExceptionMessage('Invalid Request: Original pspReference is invalid for this environment!'); + $this->expectExceptionMessage('Original pspReference required for this operation'); // initialize client $client = $this->createReviewPayoutClient(); diff --git a/tests/PosPaymentTest.php b/tests/Integration/PosPaymentTest.php similarity index 94% rename from tests/PosPaymentTest.php rename to tests/Integration/PosPaymentTest.php index 6634cf726..7aeafce67 100644 --- a/tests/PosPaymentTest.php +++ b/tests/Integration/PosPaymentTest.php @@ -1,8 +1,30 @@ settings = $this->loadConfigIni(); + $this->settings = $this->loadConfig(); $this->merchantAccount = $this->getMerchantAccount(); $this->skinCode = $this->getSkinCode(); $this->hmacSignature = $this->getHmacSignature(); @@ -252,6 +273,25 @@ protected function getPOIID() return $settings['POIID']; } + protected function loadConfig() + { + if (file_exists($this->getTestFilePath())) { + return $this->loadConfigIni(); + } + return array( + 'username' => getenv('INTEGRATION_USERNAME'), + 'password' => getenv('INTEGRATION_PASSWORD'), + 'x-api-key' => getenv('INTEGRATION_X_API_KEY'), + 'merchantAccount' => getenv('INTEGRATION_MERCHANT_ACCOUNT'), + 'skinCode' => getenv('INTEGRATION_SKIN_CODE'), + 'hmacSignature' => getenv('INTEGRATION_HMAC_SIGNATURE'), + 'storePayoutUsername' => getenv('INTEGRATION_STORE_PAYOUT_USERNAME'), + 'storePayoutPassword' => getenv('INTEGRATION_STORE_PAYOUT_PASSWORD'), + 'reviewPayoutUsername' => getenv('INTEGRATION_REVIEW_PAYOUT_USERNAME'), + 'reviewPayoutPassword' => getenv('INTEGRATION_REVIEW_PAYOUT_PASSWORD'), + ); + } + /** * Loads the settings into and array from the config/test.ini file * @@ -259,7 +299,7 @@ protected function getPOIID() */ protected function loadConfigIni() { - return parse_ini_file(__DIR__ . DIRECTORY_SEPARATOR . 'config' . DIRECTORY_SEPARATOR . 'test.ini', true); + return parse_ini_file($this->getTestFilePath(), true); } @@ -313,4 +353,12 @@ protected function getMethod($class, $name) $method->setAccessible(true); return $method; } + + /** + * @return string + */ + protected function getTestFilePath() + { + return __DIR__ . DIRECTORY_SEPARATOR . 'config' . DIRECTORY_SEPARATOR . 'test.ini'; + } } diff --git a/tests/Unit/Util/HmacSignatureTest.php b/tests/Unit/Util/HmacSignatureTest.php index 4a6baa4ea..a2e41abe7 100644 --- a/tests/Unit/Util/HmacSignatureTest.php +++ b/tests/Unit/Util/HmacSignatureTest.php @@ -127,4 +127,26 @@ public function testHmacSignatureEscaping() $this->fail('Unexpected exception'); } } + + public function testIsHmacSupportedEventCode() + { + $params = json_decode('{ + "pspReference": "7914073381342284", + "merchantAccountCode": "TestMerchant", + "merchantReference": "TestPayment-1407325143704", + "amount": { + "value": 0, + "currency": "EUR" + }, + "eventCode": "AUTHORISATION", + "success": "true" + }', true); + $hmac = new HmacSignature(); + try { + $hmacSupportedEventCode = $hmac->isHmacSupportedEventCode($params); + $this->assertTrue($hmacSupportedEventCode); + } catch (AdyenException $e) { + $this->fail('Unexpected exception'); + } + } } diff --git a/tests/config/test.ini b/tests/config/test.ini.sample similarity index 81% rename from tests/config/test.ini rename to tests/config/test.ini.sample index b79a75dbe..f1946c6d9 100644 --- a/tests/config/test.ini +++ b/tests/config/test.ini.sample @@ -5,9 +5,8 @@ merchantAccount = YOUR MERCHANT ACCOUNT skinCode = YOUR SKIN CODE hmacSignature = YOUR HMAC SIGNATURE x-api-key = YOUR X-API KEY -POIID = UNIQUETERMINALID storePayoutUsername = YOUR STORE PAYOUT USERNAME storePayoutPassword = "YOUR STORE PAYOUT PASSWORD" reviewPayoutUsername = YOUR REVIEW PAYOUT USERNAME -reviewPayoutPassword = "YOUR REVIEW PAYOUT PASSWORD" \ No newline at end of file +reviewPayoutPassword = "YOUR REVIEW PAYOUT PASSWORD"