From bc371cf53c05dbd41b01d4eef7a7740005fccb19 Mon Sep 17 00:00:00 2001 From: "David.Owusu" Date: Fri, 12 Mar 2021 17:19:12 +0100 Subject: [PATCH 01/28] [change] (UMCS-120) card examples: remove double display of validation errors. --- examples/Card/index.php | 1 - examples/CardExtended/index.php | 1 - examples/CardRecurring/index.php | 1 - 3 files changed, 3 deletions(-) diff --git a/examples/Card/index.php b/examples/Card/index.php index d5dd5532..6db254dc 100644 --- a/examples/Card/index.php +++ b/examples/Card/index.php @@ -151,7 +151,6 @@ $errorHolder.html('') } else { formFieldValid[e.type] = false; - $errorHolder.html(e.error) } payButton.disabled = !(formFieldValid.number && formFieldValid.expiry && formFieldValid.cvc); }; diff --git a/examples/CardExtended/index.php b/examples/CardExtended/index.php index 1acf4d15..ebfbb043 100644 --- a/examples/CardExtended/index.php +++ b/examples/CardExtended/index.php @@ -160,7 +160,6 @@ $errorHolder.html('') } else { formFieldValid[e.type] = false; - $errorHolder.html(e.error) } payButton.disabled = !( formFieldValid.number && diff --git a/examples/CardRecurring/index.php b/examples/CardRecurring/index.php index b69f0a20..7a371df7 100644 --- a/examples/CardRecurring/index.php +++ b/examples/CardRecurring/index.php @@ -124,7 +124,6 @@ $errorHolder.html('') } else { formFieldValid[e.type] = false; - $errorHolder.html(e.error) } payButton.disabled = !(formFieldValid.number && formFieldValid.expiry && formFieldValid.cvc && formFieldValid.email); }; From 28043dc56fbf34dda3bb495749bca820f0b062bf Mon Sep 17 00:00:00 2001 From: "David.Owusu" Date: Wed, 24 Mar 2021 16:50:14 +0100 Subject: [PATCH 02/28] [change] (UMCS-144) ApplePay: Add class for payment type. --- src/Resources/PaymentTypes/Applepay.php | 228 ++++++++++++++++++ .../Resources/AbstractUnzerResourceTest.php | 11 +- .../Resources/PaymentTypes/ApplePayTest.php | 64 +++++ 3 files changed, 299 insertions(+), 4 deletions(-) create mode 100644 src/Resources/PaymentTypes/Applepay.php create mode 100644 test/unit/Resources/PaymentTypes/ApplePayTest.php diff --git a/src/Resources/PaymentTypes/Applepay.php b/src/Resources/PaymentTypes/Applepay.php new file mode 100644 index 00000000..008bf008 --- /dev/null +++ b/src/Resources/PaymentTypes/Applepay.php @@ -0,0 +1,228 @@ + + * + * @package UnzerSDK\PaymentTypes + */ +namespace UnzerSDK\Resources\PaymentTypes; + +use UnzerSDK\Traits\CanAuthorize; +use UnzerSDK\Traits\CanDirectCharge; +use UnzerSDK\Traits\CanPayout; +use UnzerSDK\Traits\CanRecur; +use UnzerSDK\Traits\HasGeoLocation; + +class Applepay extends BasePaymentType +{ + use CanDirectCharge; + use CanAuthorize; + use CanPayout; + use CanRecur; + use HasGeoLocation; + + + /** @var string|null $applicationExpirationDate */ + protected $applicationExpirationDate; + + /** @var string|null $applicationPrimaryAccountNumber */ + protected $applicationPrimaryAccountNumber; + + /** @var string|null $currencyCode */ + protected $currencyCode; + + /** @var string|null $data */ + protected $data; + + /** @var string|null $method */ + protected $method; + + /** @var string|null $signature */ + protected $signature; + + /** @var float $transactionAmount */ + protected $transactionAmount = 0.0; + + /** @var string|null $version */ + protected $version; + + /** + * ApplePay constructor. + */ + public function __construct() + { + } + + //applicationExpirationDate; + } + + /** + * @return string|null + */ + public function getApplicationPrimaryAccountNumber(): ?string + { + return $this->applicationPrimaryAccountNumber; + } + + /** + * @return string|null + */ + public function getCurrencyCode(): ?string + { + return $this->currencyCode; + } + + /** + * @return string|null + */ + public function getData(): ?string + { + return $this->data; + } + + /** + * @return string|null + */ + public function getMethod(): ?string + { + return $this->method; + } + + /** + * @return string|null + */ + public function getSignature(): ?string + { + return $this->signature; + } + + /** + * @return float + */ + public function getTransactionAmount(): float + { + return $this->transactionAmount; + } + + /** + * @return string|null + */ + public function getVersion(): ?string + { + return $this->version; + } + + /** + * @param string|null $applicationExpirationDate + * + * @return $this + */ + public function setApplicationExpirationDate(?string $applicationExpirationDate): Applepay + { + $this->applicationExpirationDate = $applicationExpirationDate; + return $this; + } + + /** + * @param string|null $applicationPrimaryAccountNumber + * + * @return $this + */ + public function setApplicationPrimaryAccountNumber(?string $applicationPrimaryAccountNumber): Applepay + { + $this->applicationPrimaryAccountNumber = $applicationPrimaryAccountNumber; + return $this; + } + + /** + * @param string|null $currencyCode + * + * @return $this + */ + public function setCurrencyCode(?string $currencyCode): Applepay + { + $this->currencyCode = $currencyCode; + return $this; + } + + /** + * @param string|null $data + * + * @return $this + */ + public function setData(?string $data): Applepay + { + $this->data = $data; + return $this; + } + + /** + * @param string|null $method + * + * @return $this + */ + public function setMethod(?string $method): Applepay + { + $this->method = $method; + return $this; + } + + /** + * @param string|null $signature + * + * @return $this + */ + public function setSignature(?string $signature): Applepay + { + $this->signature = $signature; + return $this; + } + + /** + * @param float $transactionAmount + * + * @return $this + */ + public function setTransactionAmount(float $transactionAmount): Applepay + { + $this->transactionAmount = $transactionAmount; + return $this; + } + + /** + * @param string|null $version + * + * @return $this + */ + public function setVersion(?string $version): Applepay + { + $this->version = $version; + return $this; + } + + // +} diff --git a/test/unit/Resources/AbstractUnzerResourceTest.php b/test/unit/Resources/AbstractUnzerResourceTest.php index 494a680b..157fc0f0 100644 --- a/test/unit/Resources/AbstractUnzerResourceTest.php +++ b/test/unit/Resources/AbstractUnzerResourceTest.php @@ -33,6 +33,7 @@ use UnzerSDK\Constants\Salutations; use UnzerSDK\Constants\TransactionTypes; use UnzerSDK\Resources\InstalmentPlans; +use UnzerSDK\Resources\PaymentTypes\Applepay; use UnzerSDK\Unzer; use UnzerSDK\Resources\AbstractUnzerResource; use UnzerSDK\Resources\Basket; @@ -443,6 +444,7 @@ public function uriDataProvider(): array 'Ideal' => [new Ideal(), 'parent/resource/path/types/ideal'], 'EPS' => [new EPS(), 'parent/resource/path/types/eps'], 'Alipay' => [new Alipay(), 'parent/resource/path/types/alipay'], + 'ApplePay' => [new Applepay(), 'parent/resource/path/types/applepay'], 'SepaDirectDebit' => [new SepaDirectDebit(''), 'parent/resource/path/types/sepa-direct-debit'], 'SepaDirectDebitSecured' => [new SepaDirectDebitSecured(''), 'parent/resource/path/types/sepa-direct-debit-secured'], 'Invoice' => [new Invoice(), 'parent/resource/path/types/invoice'], @@ -467,14 +469,15 @@ public function fetchUriDataProvider() { return [ // Payment types. + 'Alipay' => [new Alipay(), 'parent/resource/path/types'], + 'ApplePay' => [new Applepay(), 'parent/resource/path/types'], 'Card' => [new Card('', '03/30'), 'parent/resource/path/types'], - 'Ideal' => [new Ideal(), 'parent/resource/path/types'], 'EPS' => [new EPS(), 'parent/resource/path/types'], - 'Alipay' => [new Alipay(), 'parent/resource/path/types'], + 'Ideal' => [new Ideal(), 'parent/resource/path/types'], + 'InstallmentSecured' => [new InstallmentSecured(), 'parent/resource/path/types'], + 'Invoice' => [new Invoice(), 'parent/resource/path/types'], 'SepaDirectDebit' => [new SepaDirectDebit(''), 'parent/resource/path/types'], 'SepaDirectDebitSecured' => [new SepaDirectDebitSecured(''), 'parent/resource/path/types'], - 'Invoice' => [new Invoice(), 'parent/resource/path/types'], - 'InstallmentSecured' => [new InstallmentSecured(), 'parent/resource/path/types'], // Other resources Uris should behave as before. 'Customer' => [new Customer(), 'parent/resource/path/customers'], diff --git a/test/unit/Resources/PaymentTypes/ApplePayTest.php b/test/unit/Resources/PaymentTypes/ApplePayTest.php new file mode 100644 index 00000000..895d80c7 --- /dev/null +++ b/test/unit/Resources/PaymentTypes/ApplePayTest.php @@ -0,0 +1,64 @@ +assertNull($applePay->getApplicationExpirationDate()); + $this->assertNull($applePay->getApplicationPrimaryAccountNumber()); + $this->assertNull($applePay->getCurrencyCode()); + $this->assertNull($applePay->getData()); + $this->assertNull($applePay->getMethod()); + $this->assertNull($applePay->getSignature()); + $this->assertEquals(0, $applePay->getTransactionAmount()); + $this->assertNull($applePay->getVersion()); + + // Call setters + $applePay->setApplicationExpirationDate('07/2020'); + $applePay->setApplicationPrimaryAccountNumber('123456789'); + $applePay->setCurrencyCode('EUR'); + $applePay->setData('some-Data'); + $applePay->setMethod('apple-pay'); + $applePay->setSignature('mySignature'); + $applePay->setTransactionAmount(100.19); + $applePay->setVersion('EC_v1'); + + $this->assertEquals('07/2020', $applePay->getApplicationExpirationDate()); + $this->assertEquals('123456789', $applePay->getApplicationPrimaryAccountNumber()); + $this->assertEquals('EUR', $applePay->getCurrencyCode()); + $this->assertEquals('some-Data', $applePay->getData()); + $this->assertEquals('apple-pay', $applePay->getMethod()); + $this->assertEquals('mySignature', $applePay->getSignature()); + $this->assertEquals(100.19, $applePay->getTransactionAmount()); + $this->assertEquals('EC_v1', $applePay->getVersion()); + + $applePay->setApplicationExpirationDate(null); + $applePay->setApplicationPrimaryAccountNumber(null); + $applePay->setCurrencyCode(null); + $applePay->setData(null); + $applePay->setMethod(null); + $applePay->setSignature(null); + $applePay->setTransactionAmount(0); + $applePay->setVersion(null); + + $this->assertNull($applePay->getApplicationExpirationDate()); + $this->assertNull($applePay->getApplicationPrimaryAccountNumber()); + $this->assertNull($applePay->getCurrencyCode()); + $this->assertNull($applePay->getData()); + $this->assertNull($applePay->getMethod()); + $this->assertNull($applePay->getSignature()); + $this->assertEquals(0, $applePay->getTransactionAmount()); + $this->assertNull($applePay->getVersion()); + } +} From 078a049e0e1b18e3d0bddfb077b3ce82924c25fd Mon Sep 17 00:00:00 2001 From: "David.Owusu" Date: Wed, 24 Mar 2021 16:51:37 +0100 Subject: [PATCH 03/28] [change] (UMCS-144) Update changelog and set version 1.1.2.0 --- CHANGELOG.md | 5 +++++ src/Unzer.php | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 409acb52..5974a786 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +## [1.1.2.0] +### Added +* Payment type ApplePay. + ## [1.1.1.1] ### Fix @@ -60,3 +64,4 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) a [1.1.0.0]: https://github.com/unzerdev/php-sdk/compare/1260b8314af1ac461e33f0cfb382ffcd0e87c105..1.1.0.0 [1.1.1.0]: https://github.com/unzerdev/php-sdk/compare/1.1.0.0..1.1.1.0 [1.1.1.1]: https://github.com/unzerdev/php-sdk/compare/1.1.1.0..1.1.1.1 +[1.1.2.0]: https://github.com/unzerdev/php-sdk/compare/1.1.1.1..1.1.2.0 diff --git a/src/Unzer.php b/src/Unzer.php index 65faadf8..1b0594fc 100644 --- a/src/Unzer.php +++ b/src/Unzer.php @@ -63,7 +63,7 @@ class Unzer implements UnzerParentInterface, PaymentServiceInterface, ResourceSe public const BASE_URL = 'api.unzer.com'; public const API_VERSION = 'v1'; public const SDK_TYPE = 'UnzerPHP'; - public const SDK_VERSION = '1.1.1.1'; + public const SDK_VERSION = '1.1.2.0'; /** @var string $key */ private $key; From fe22222ba282b3b3b0cc3019f6ea0684887a8ac1 Mon Sep 17 00:00:00 2001 From: "David.Owusu" Date: Thu, 25 Mar 2021 17:08:32 +0100 Subject: [PATCH 04/28] [change] (UMCS-144) Add ApplePay IdString and constructor. --- src/Constants/IdStrings.php | 2 ++ src/Resources/PaymentTypes/Applepay.php | 19 ++++++++++++++++--- src/Services/ResourceService.php | 4 ++++ .../Resources/AbstractUnzerResourceTest.php | 4 ++-- .../Resources/PaymentTypes/ApplePayTest.php | 10 +++++----- test/unit/Services/ResourceServiceTest.php | 19 ++++++++++--------- 6 files changed, 39 insertions(+), 19 deletions(-) diff --git a/src/Constants/IdStrings.php b/src/Constants/IdStrings.php index 13d4cd48..d80c3957 100755 --- a/src/Constants/IdStrings.php +++ b/src/Constants/IdStrings.php @@ -35,6 +35,7 @@ class IdStrings // Payment Types public const ALIPAY = 'ali'; + public const APPLEPAY = 'apl'; public const BANCONTACT = 'bct'; public const CARD = 'crd'; public const EPS = 'eps'; @@ -66,6 +67,7 @@ class IdStrings public const WEBHOOK = 'whk'; public const PAYMENT_TYPES = [ self::ALIPAY, + self::APPLEPAY, self::BANCONTACT, self::CARD, self::EPS, diff --git a/src/Resources/PaymentTypes/Applepay.php b/src/Resources/PaymentTypes/Applepay.php index 008bf008..11e85aea 100644 --- a/src/Resources/PaymentTypes/Applepay.php +++ b/src/Resources/PaymentTypes/Applepay.php @@ -65,9 +65,22 @@ class Applepay extends BasePaymentType /** * ApplePay constructor. - */ - public function __construct() - { + * + * @param string $version + * @param string $data + * @param string $signature + * @param ApplePayHeader $header + */ + public function __construct( + string $version, + string $data, + string $signature, + ApplePayHeader $header + ) { + $this->version = $version; + $this->data = $data; + $this->signature = $signature; + $this->header = $header; } // [new Ideal(), 'parent/resource/path/types/ideal'], 'EPS' => [new EPS(), 'parent/resource/path/types/eps'], 'Alipay' => [new Alipay(), 'parent/resource/path/types/alipay'], - 'ApplePay' => [new Applepay(), 'parent/resource/path/types/applepay'], + 'ApplePay' => [new Applepay('v1', 'data', 'sig'), 'parent/resource/path/types/applepay'], 'SepaDirectDebit' => [new SepaDirectDebit(''), 'parent/resource/path/types/sepa-direct-debit'], 'SepaDirectDebitSecured' => [new SepaDirectDebitSecured(''), 'parent/resource/path/types/sepa-direct-debit-secured'], 'Invoice' => [new Invoice(), 'parent/resource/path/types/invoice'], @@ -470,7 +470,7 @@ public function fetchUriDataProvider() return [ // Payment types. 'Alipay' => [new Alipay(), 'parent/resource/path/types'], - 'ApplePay' => [new Applepay(), 'parent/resource/path/types'], + 'ApplePay' => [new Applepay('v1', 'data', 'sig'), 'parent/resource/path/types'], 'Card' => [new Card('', '03/30'), 'parent/resource/path/types'], 'EPS' => [new EPS(), 'parent/resource/path/types'], 'Ideal' => [new Ideal(), 'parent/resource/path/types'], diff --git a/test/unit/Resources/PaymentTypes/ApplePayTest.php b/test/unit/Resources/PaymentTypes/ApplePayTest.php index 895d80c7..f19d1c7e 100644 --- a/test/unit/Resources/PaymentTypes/ApplePayTest.php +++ b/test/unit/Resources/PaymentTypes/ApplePayTest.php @@ -14,15 +14,15 @@ class ApplePayTest extends BasePaymentTest */ public function gettersAndSettersShouldWorkAsExpected(): void { - $applePay = new Applepay(); - $this->assertNull($applePay->getApplicationExpirationDate()); + $applePay = new Applepay('EC_v1', 'data1', 'signature'); $this->assertNull($applePay->getApplicationPrimaryAccountNumber()); + $this->assertNull($applePay->getApplicationExpirationDate()); $this->assertNull($applePay->getCurrencyCode()); - $this->assertNull($applePay->getData()); $this->assertNull($applePay->getMethod()); - $this->assertNull($applePay->getSignature()); $this->assertEquals(0, $applePay->getTransactionAmount()); - $this->assertNull($applePay->getVersion()); + $this->assertEquals('data1', $applePay->getData()); + $this->assertEquals('signature', $applePay->getSignature()); + $this->assertEquals('EC_v1', $applePay->getVersion()); // Call setters $applePay->setApplicationExpirationDate('07/2020'); diff --git a/test/unit/Services/ResourceServiceTest.php b/test/unit/Services/ResourceServiceTest.php index 5489518a..e6336f2f 100755 --- a/test/unit/Services/ResourceServiceTest.php +++ b/test/unit/Services/ResourceServiceTest.php @@ -1143,26 +1143,27 @@ public function fetchResourceByUrlShouldFetchTheDesiredResourceDP(): array public function fetchResourceByUrlForAPaymentTypeShouldCallFetchPaymentTypeDP(): array { return [ + 'ALIPAY' => ['s-ali-xen2ybcovn56', 'https://api.unzer.com/v1/types/alipay/s-ali-xen2ybcovn56/'], + 'APPLEPAY' => ['s-apl-xen2ybcovn56', 'https://api.unzer.com/v1/types/appelpay/s-apl-xen2ybcovn56/'], + 'BANCONTACT' => ['s-bct-xen2ybcovn56', 'https://api.unzer.com/v1/types/bancontact/s-bct-xen2ybcovn56/'], 'CARD' => ['s-crd-xen2ybcovn56', 'https://api.unzer.com/v1/types/card/s-crd-xen2ybcovn56/'], + 'EPS' => ['s-eps-xen2ybcovn56', 'https://api.unzer.com/v1/types/eps/s-eps-xen2ybcovn56/'], 'GIROPAY' => ['s-gro-xen2ybcovn56', 'https://api.unzer.com/v1/types/giropay/s-gro-xen2ybcovn56/'], + 'HIRE_PURCHASE_DIRECT_DEBIT' => ['s-hdd-xen2ybcovn56', 'https://api.unzer.com/v1/types/hire-purchase-direct-debit/s-hdd-xen2ybcovn56/'], 'IDEAL' => ['s-idl-xen2ybcovn56', 'https://api.unzer.com/v1/types/ideal/s-idl-xen2ybcovn56/'], 'INVOICE' => ['s-ivc-xen2ybcovn56', 'https://api.unzer.com/v1/types/invoice/s-ivc-xen2ybcovn56/'], + 'INVOICE_FACTORING' => ['s-ivf-xen2ybcovn56', 'https://api.unzer.com/v1/types/wechatpay/s-ivf-xen2ybcovn56/'], 'INVOICE_GUARANTEED' => ['s-ivg-xen2ybcovn56', 'https://api.unzer.com/v1/types/invoice-guaranteed/s-ivg-xen2ybcovn56/'], 'INVOICE_SECURED' => ['s-ivs-xen2ybcovn56', 'https://api.unzer.com/v1/types/invoice-secured/s-ivs-xen2ybcovn56/'], - 'INVOICE_FACTORING' => ['s-ivf-xen2ybcovn56', 'https://api.unzer.com/v1/types/wechatpay/s-ivf-xen2ybcovn56/'], + 'Installment_SECURED' => ['s-ins-xen2ybcovn56', 'https://api.unzer.com/v1/types/installment-secured/s-ins-xen2ybcovn56/'], 'PAYPAL' => ['s-ppl-xen2ybcovn56', 'https://api.unzer.com/v1/types/paypal/s-ppl-xen2ybcovn56/'], + 'PIS' => ['s-pis-xen2ybcovn56', 'https://api.unzer.com/v1/types/pis/s-pis-xen2ybcovn56/'], 'PREPAYMENT' => ['s-ppy-xen2ybcovn56', 'https://api.unzer.com/v1/types/prepayment/s-ppy-xen2ybcovn56/'], 'PRZELEWY24' => ['s-p24-xen2ybcovn56', 'https://api.unzer.com/v1/types/przelewy24/s-p24-xen2ybcovn56/'], - 'SEPA_DIRECT_DEBIT_GUARANTEED' => ['s-ddg-xen2ybcovn56', 'https://api.unzer.com/v1/types/direct-debit-guaranteed/s-ddg-xen2ybcovn56/'], 'SEPA_DIRECT_DEBIT' => ['s-sdd-xen2ybcovn56', 'https://api.unzer.com/v1/types/direct-debit/s-sdd-xen2ybcovn56/'], + 'SEPA_DIRECT_DEBIT_GUARANTEED' => ['s-ddg-xen2ybcovn56', 'https://api.unzer.com/v1/types/direct-debit-guaranteed/s-ddg-xen2ybcovn56/'], 'SOFORT' => ['s-sft-xen2ybcovn56', 'https://api.unzer.com/v1/types/sofort/s-sft-xen2ybcovn56/'], - 'PIS' => ['s-pis-xen2ybcovn56', 'https://api.unzer.com/v1/types/pis/s-pis-xen2ybcovn56/'], - 'EPS' => ['s-eps-xen2ybcovn56', 'https://api.unzer.com/v1/types/eps/s-eps-xen2ybcovn56/'], - 'ALIPAY' => ['s-ali-xen2ybcovn56', 'https://api.unzer.com/v1/types/alipay/s-ali-xen2ybcovn56/'], - 'WECHATPAY' => ['s-wcp-xen2ybcovn56', 'https://api.unzer.com/v1/types/wechatpay/s-wcp-xen2ybcovn56/'], - 'HIRE_PURCHASE_DIRECT_DEBIT' => ['s-hdd-xen2ybcovn56', 'https://api.unzer.com/v1/types/hire-purchase-direct-debit/s-hdd-xen2ybcovn56/'], - 'Installment_SECURED' => ['s-ins-xen2ybcovn56', 'https://api.unzer.com/v1/types/installment-secured/s-ins-xen2ybcovn56/'], - 'BANCONTACT' => ['s-bct-xen2ybcovn56', 'https://api.unzer.com/v1/types/bancontact/s-bct-xen2ybcovn56/'] + 'WECHATPAY' => ['s-wcp-xen2ybcovn56', 'https://api.unzer.com/v1/types/wechatpay/s-wcp-xen2ybcovn56/'] ]; } From 8381a56731f709bd56ae0dc8bd2b020181e2e36f Mon Sep 17 00:00:00 2001 From: "David.Owusu" Date: Thu, 25 Mar 2021 17:12:56 +0100 Subject: [PATCH 05/28] [change] (UMCS-144) Add Applepay header. --- .../EmbeddedResources/ApplePayHeader.php | 110 ++++++++++++++ src/Resources/PaymentTypes/Applepay.php | 64 +++++--- src/Services/ResourceService.php | 2 +- .../Resources/AbstractUnzerResourceTest.php | 4 +- .../EmbeddedResources/ApplePayHeaderTest.php | 47 ++++++ .../Resources/PaymentTypes/ApplePayTest.php | 142 ++++++++++++------ 6 files changed, 300 insertions(+), 69 deletions(-) create mode 100644 src/Resources/EmbeddedResources/ApplePayHeader.php create mode 100644 test/unit/Resources/EmbeddedResources/ApplePayHeaderTest.php diff --git a/src/Resources/EmbeddedResources/ApplePayHeader.php b/src/Resources/EmbeddedResources/ApplePayHeader.php new file mode 100644 index 00000000..094f12fe --- /dev/null +++ b/src/Resources/EmbeddedResources/ApplePayHeader.php @@ -0,0 +1,110 @@ + + * + * @package UnzerSDK\Resources\EmbeddedResources + */ +namespace UnzerSDK\Resources\EmbeddedResources; + +use UnzerSDK\Resources\AbstractUnzerResource; + +class ApplePayHeader extends AbstractUnzerResource +{ + /** @var string|null */ + protected $ephemeralPublicKey; + + /** @var string|null */ + protected $publicKeyHash; + + /** @var string|null */ + protected $transactionId; + + /** + * ApplePayHeader constructor. + * + * @param string|null $ephemeralPublicKey + * @param string|null $publicKeyHash + * @param string|null $transactionId + */ + public function __construct(?string $ephemeralPublicKey, ?string $publicKeyHash, ?string $transactionId = null) + { + $this->ephemeralPublicKey = $ephemeralPublicKey; + $this->publicKeyHash = $publicKeyHash; + $this->transactionId = $transactionId; + } + + /** + * @param string|null $ephemeralPublicKey + * + * @return ApplePayHeader + */ + public function setEphemeralPublicKey(?string $ephemeralPublicKey): ApplePayHeader + { + $this->ephemeralPublicKey = $ephemeralPublicKey; + return $this; + } + + /** + * @param string|null $publicKeyHash + * + * @return ApplePayHeader + */ + public function setPublicKeyHash(?string $publicKeyHash): ApplePayHeader + { + $this->publicKeyHash = $publicKeyHash; + return $this; + } + + /** + * @param string|null $transactionId + * + * @return ApplePayHeader + */ + public function setTransactionId(?string $transactionId): ApplePayHeader + { + $this->transactionId = $transactionId; + return $this; + } + + /** + * @return string|null + */ + public function getEphemeralPublicKey(): ?string + { + return $this->ephemeralPublicKey; + } + + /** + * @return string|null + */ + public function getPublicKeyHash(): ?string + { + return $this->publicKeyHash; + } + + /** + * @return string|null + */ + public function getTransactionId(): ?string + { + return $this->transactionId; + } +} diff --git a/src/Resources/PaymentTypes/Applepay.php b/src/Resources/PaymentTypes/Applepay.php index 11e85aea..035b4eb1 100644 --- a/src/Resources/PaymentTypes/Applepay.php +++ b/src/Resources/PaymentTypes/Applepay.php @@ -1,6 +1,6 @@ version = $version; $this->data = $data; @@ -117,6 +120,14 @@ public function getData(): ?string return $this->data; } + /** + * @return ApplePayHeader|null + */ + public function getHeader(): ?ApplePayHeader + { + return $this->header; + } + /** * @return string|null */ @@ -136,7 +147,7 @@ public function getSignature(): ?string /** * @return float */ - public function getTransactionAmount(): float + public function getTransactionAmount(): ?float { return $this->transactionAmount; } @@ -154,7 +165,7 @@ public function getVersion(): ?string * * @return $this */ - public function setApplicationExpirationDate(?string $applicationExpirationDate): Applepay + protected function setApplicationExpirationDate(?string $applicationExpirationDate): Applepay { $this->applicationExpirationDate = $applicationExpirationDate; return $this; @@ -165,7 +176,7 @@ public function setApplicationExpirationDate(?string $applicationExpirationDate) * * @return $this */ - public function setApplicationPrimaryAccountNumber(?string $applicationPrimaryAccountNumber): Applepay + protected function setApplicationPrimaryAccountNumber(?string $applicationPrimaryAccountNumber): Applepay { $this->applicationPrimaryAccountNumber = $applicationPrimaryAccountNumber; return $this; @@ -176,7 +187,7 @@ public function setApplicationPrimaryAccountNumber(?string $applicationPrimaryAc * * @return $this */ - public function setCurrencyCode(?string $currencyCode): Applepay + protected function setCurrencyCode(?string $currencyCode): Applepay { $this->currencyCode = $currencyCode; return $this; @@ -193,12 +204,23 @@ public function setData(?string $data): Applepay return $this; } + /** + * @param ApplePayHeader $header + * + * @return Applepay + */ + public function setHeader(ApplePayHeader $header): Applepay + { + $this->header = $header; + return $this; + } + /** * @param string|null $method * * @return $this */ - public function setMethod(?string $method): Applepay + protected function setMethod(?string $method): Applepay { $this->method = $method; return $this; @@ -220,7 +242,7 @@ public function setSignature(?string $signature): Applepay * * @return $this */ - public function setTransactionAmount(float $transactionAmount): Applepay + protected function setTransactionAmount(float $transactionAmount): Applepay { $this->transactionAmount = $transactionAmount; return $this; diff --git a/src/Services/ResourceService.php b/src/Services/ResourceService.php index e45bfeb7..ae571585 100755 --- a/src/Services/ResourceService.php +++ b/src/Services/ResourceService.php @@ -517,7 +517,7 @@ public function fetchPaymentType($typeId): BasePaymentType $paymentType = new Alipay(); break; case IdStrings::APPLEPAY: - $paymentType = new Applepay('v1', 'data', 'sig'); + $paymentType = new Applepay(null, null, null, null); break; case IdStrings::BANCONTACT: $paymentType = new Bancontact(); diff --git a/test/unit/Resources/AbstractUnzerResourceTest.php b/test/unit/Resources/AbstractUnzerResourceTest.php index 2ef16235..571cc0a6 100644 --- a/test/unit/Resources/AbstractUnzerResourceTest.php +++ b/test/unit/Resources/AbstractUnzerResourceTest.php @@ -444,7 +444,7 @@ public function uriDataProvider(): array 'Ideal' => [new Ideal(), 'parent/resource/path/types/ideal'], 'EPS' => [new EPS(), 'parent/resource/path/types/eps'], 'Alipay' => [new Alipay(), 'parent/resource/path/types/alipay'], - 'ApplePay' => [new Applepay('v1', 'data', 'sig'), 'parent/resource/path/types/applepay'], + 'ApplePay' => [new Applepay('EC_v1', 'data', 'sig', null), 'parent/resource/path/types/applepay'], 'SepaDirectDebit' => [new SepaDirectDebit(''), 'parent/resource/path/types/sepa-direct-debit'], 'SepaDirectDebitSecured' => [new SepaDirectDebitSecured(''), 'parent/resource/path/types/sepa-direct-debit-secured'], 'Invoice' => [new Invoice(), 'parent/resource/path/types/invoice'], @@ -470,7 +470,7 @@ public function fetchUriDataProvider() return [ // Payment types. 'Alipay' => [new Alipay(), 'parent/resource/path/types'], - 'ApplePay' => [new Applepay('v1', 'data', 'sig'), 'parent/resource/path/types'], + 'ApplePay' => [new Applepay('EC_v1', 'data', 'sig', null), 'parent/resource/path/types'], 'Card' => [new Card('', '03/30'), 'parent/resource/path/types'], 'EPS' => [new EPS(), 'parent/resource/path/types'], 'Ideal' => [new Ideal(), 'parent/resource/path/types'], diff --git a/test/unit/Resources/EmbeddedResources/ApplePayHeaderTest.php b/test/unit/Resources/EmbeddedResources/ApplePayHeaderTest.php new file mode 100644 index 00000000..0dc6a203 --- /dev/null +++ b/test/unit/Resources/EmbeddedResources/ApplePayHeaderTest.php @@ -0,0 +1,47 @@ + + * + * @package UnzerSDK\test\unit + */ +namespace UnzerSDK\test\unit\Resources\EmbeddedResources; + +use UnzerSDK\Resources\EmbeddedResources\ApplePayHeader; +use PHPUnit\Framework\TestCase; + +class ApplePayHeaderTest extends TestCase +{ + /** + * Verify the resource data is set properly. + * + * @test + */ + public function constructorShouldSetParameters(): void + { + $applepayHeader = new ApplePayHeader('ephemeralPublicKey', 'publicKeyHash', 'transactionId'); + + $this->assertEquals('ephemeralPublicKey', $applepayHeader->getEphemeralPublicKey()); + $this->assertEquals('publicKeyHash', $applepayHeader->getPublicKeyHash()); + $this->assertEquals('transactionId', $applepayHeader->getTransactionId()); + } +} diff --git a/test/unit/Resources/PaymentTypes/ApplePayTest.php b/test/unit/Resources/PaymentTypes/ApplePayTest.php index f19d1c7e..c2c28553 100644 --- a/test/unit/Resources/PaymentTypes/ApplePayTest.php +++ b/test/unit/Resources/PaymentTypes/ApplePayTest.php @@ -1,64 +1,116 @@ + * + * @package UnzerSDK\test\unit + */ namespace UnzerSDK\test\unit\Resources\PaymentTypes; +use UnzerSDK\Resources\EmbeddedResources\ApplePayHeader; use UnzerSDK\Resources\PaymentTypes\Applepay; use UnzerSDK\test\BasePaymentTest; class ApplePayTest extends BasePaymentTest { /** - * Verify getters and setters work as expected. + * Verify the resource data is set properly. * * @test */ - public function gettersAndSettersShouldWorkAsExpected(): void + public function constructorShouldSetParameters(): void { - $applePay = new Applepay('EC_v1', 'data1', 'signature'); - $this->assertNull($applePay->getApplicationPrimaryAccountNumber()); - $this->assertNull($applePay->getApplicationExpirationDate()); - $this->assertNull($applePay->getCurrencyCode()); - $this->assertNull($applePay->getMethod()); - $this->assertEquals(0, $applePay->getTransactionAmount()); - $this->assertEquals('data1', $applePay->getData()); - $this->assertEquals('signature', $applePay->getSignature()); - $this->assertEquals('EC_v1', $applePay->getVersion()); + $version = 'EC_v1'; + $data = 'some-Data'; + $signature = 'mySignature'; + $applepay = new Applepay($version, $data, $signature, $this->getTestApplePayHeader()); + + $this->assertEquals($version, $applepay->getVersion()); + $this->assertEquals($data, $applepay->getData()); + $this->assertEquals($signature, $applepay->getSignature()); + $this->assertInstanceOf(ApplePayHeader::class, $applepay->getHeader()); + } - // Call setters - $applePay->setApplicationExpirationDate('07/2020'); - $applePay->setApplicationPrimaryAccountNumber('123456789'); - $applePay->setCurrencyCode('EUR'); - $applePay->setData('some-Data'); - $applePay->setMethod('apple-pay'); - $applePay->setSignature('mySignature'); - $applePay->setTransactionAmount(100.19); - $applePay->setVersion('EC_v1'); + /** + * Test Apple Pay json serialization. + * + * @test + */ + public function jsonSerializationExposesOnlyRequestParameter(): void + { + $applepay = $this->getTestApplepay(); - $this->assertEquals('07/2020', $applePay->getApplicationExpirationDate()); - $this->assertEquals('123456789', $applePay->getApplicationPrimaryAccountNumber()); - $this->assertEquals('EUR', $applePay->getCurrencyCode()); - $this->assertEquals('some-Data', $applePay->getData()); - $this->assertEquals('apple-pay', $applePay->getMethod()); - $this->assertEquals('mySignature', $applePay->getSignature()); - $this->assertEquals(100.19, $applePay->getTransactionAmount()); - $this->assertEquals('EC_v1', $applePay->getVersion()); + $expectedJson = '{ "data": "data", "header": { "ephemeralPublicKey": "ephemeralPublicKey", "publicKeyHash": ' . + '"publicKeyHash", "transactionId": "transactionId" }, "signature": "sig", "version": "EC_v1" }'; - $applePay->setApplicationExpirationDate(null); - $applePay->setApplicationPrimaryAccountNumber(null); - $applePay->setCurrencyCode(null); - $applePay->setData(null); - $applePay->setMethod(null); - $applePay->setSignature(null); - $applePay->setTransactionAmount(0); - $applePay->setVersion(null); + $this->assertJsonStringEqualsJsonString($expectedJson, $applepay->jsonSerialize()); + } - $this->assertNull($applePay->getApplicationExpirationDate()); - $this->assertNull($applePay->getApplicationPrimaryAccountNumber()); - $this->assertNull($applePay->getCurrencyCode()); - $this->assertNull($applePay->getData()); - $this->assertNull($applePay->getMethod()); - $this->assertNull($applePay->getSignature()); - $this->assertEquals(0, $applePay->getTransactionAmount()); - $this->assertNull($applePay->getVersion()); + /** + * Test Apple Pay json response handling. + * + * @test + */ + public function responseShouldBeMappedCorrectly(): void + { + $applepay = new Applepay(null, null, null, null); + + $jsonResponse = '{ + "id": "s-apl-faucbirhd6yy", + "method": "apple-pay", + "recurring": false, + "geoLocation": { + "clientIp": "115.77.189.143", + "countryCode": "" + }, + "applicationPrimaryAccountNumber": "370295******922", + "applicationExpirationDate": "07/2020", + "currencyCode": "EUR", + "transactionAmount": "1.5000" + }'; + + $applepay->handleResponse(json_decode($jsonResponse)); + + $this->assertEquals('s-apl-faucbirhd6yy', $applepay->getId()); + $this->assertEquals('apple-pay', $applepay->getMethod()); + $this->assertEquals('370295******922', $applepay->getApplicationPrimaryAccountNumber()); + $this->assertEquals('07/2020', $applepay->getApplicationExpirationDate()); + $this->assertEquals('EUR', $applepay->getCurrencyCode()); + $this->assertSame(1.5000, $applepay->getTransactionAmount()); + } + + /** + * @return ApplePayHeader + */ + private function getTestApplePayHeader(): ApplePayHeader + { + return new ApplePayHeader('ephemeralPublicKey', 'publicKeyHash', 'transactionId'); + } + + /** + * @return Applepay + */ + private function getTestApplepay(): Applepay + { + return new Applepay('EC_v1', 'data', 'sig', $this->getTestApplePayHeader()); } } From c6583e28716ad92241702c3fe4fe512db2eb16dd Mon Sep 17 00:00:00 2001 From: "David.Owusu" Date: Tue, 30 Mar 2021 13:41:46 +0200 Subject: [PATCH 06/28] [change] (UMCS-144) Adjust supported functions of applepay. --- src/Resources/PaymentTypes/Applepay.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/Resources/PaymentTypes/Applepay.php b/src/Resources/PaymentTypes/Applepay.php index 035b4eb1..9d48e5ac 100644 --- a/src/Resources/PaymentTypes/Applepay.php +++ b/src/Resources/PaymentTypes/Applepay.php @@ -27,16 +27,12 @@ use UnzerSDK\Resources\EmbeddedResources\ApplePayHeader; use UnzerSDK\Traits\CanAuthorize; use UnzerSDK\Traits\CanDirectCharge; -use UnzerSDK\Traits\CanPayout; -use UnzerSDK\Traits\CanRecur; use UnzerSDK\Traits\HasGeoLocation; class Applepay extends BasePaymentType { use CanDirectCharge; use CanAuthorize; - use CanPayout; - use CanRecur; use HasGeoLocation; /** @var string|null $applicationExpirationDate */ From c8d1a5bca70d79eb4939f89343294f6ce7f6ca91 Mon Sep 17 00:00:00 2001 From: "David.Owusu" Date: Tue, 30 Mar 2021 15:17:16 +0200 Subject: [PATCH 07/28] [change] (UMCS-144) check for geoLocation in response handling test. --- test/unit/Resources/PaymentTypes/ApplePayTest.php | 1 + 1 file changed, 1 insertion(+) diff --git a/test/unit/Resources/PaymentTypes/ApplePayTest.php b/test/unit/Resources/PaymentTypes/ApplePayTest.php index c2c28553..bcdaef94 100644 --- a/test/unit/Resources/PaymentTypes/ApplePayTest.php +++ b/test/unit/Resources/PaymentTypes/ApplePayTest.php @@ -96,6 +96,7 @@ public function responseShouldBeMappedCorrectly(): void $this->assertEquals('07/2020', $applepay->getApplicationExpirationDate()); $this->assertEquals('EUR', $applepay->getCurrencyCode()); $this->assertSame(1.5000, $applepay->getTransactionAmount()); + $this->assertNotNull($applepay->getGeoLocation()); } /** From 142c25c12806959cc4b5cae5c15238e6c384f613 Mon Sep 17 00:00:00 2001 From: "David.Owusu" Date: Wed, 7 Apr 2021 21:17:22 +0200 Subject: [PATCH 08/28] [change] (UMCS-181) Add ApplepayAdapter and ApplepaySession class. --- src/Adapter/ApplepayAdapter.php | 121 ++++++++++++++++++++ src/Adapter/ApplepaySession.php | 128 ++++++++++++++++++++++ test/unit/Adapter/ApplepaySessionTest.php | 43 ++++++++ 3 files changed, 292 insertions(+) create mode 100644 src/Adapter/ApplepayAdapter.php create mode 100644 src/Adapter/ApplepaySession.php create mode 100644 test/unit/Adapter/ApplepaySessionTest.php diff --git a/src/Adapter/ApplepayAdapter.php b/src/Adapter/ApplepayAdapter.php new file mode 100644 index 00000000..ce2194c4 --- /dev/null +++ b/src/Adapter/ApplepayAdapter.php @@ -0,0 +1,121 @@ + + * + * @package UnzerSDK\Adapter + */ +namespace UnzerSDK\Adapter; + +use UnzerSDK\Services\EnvironmentService; +use UnzerSDK\Exceptions\UnzerApiException; +use function in_array; + +class ApplepayAdapter +{ + private $request; + + /** + * @param string $merchantValidationURL + * @param ApplepaySession $applePaySession + * @param string $merchantValidationCertificatePath + * @param string $merchantValidationCertificateKeyChainPath + * + * @return string|null + * + * @throws UnzerApiException + */ + public function validateApplePayMerchant( + string $merchantValidationURL, + ApplepaySession $applePaySession, + string $merchantValidationCertificatePath + ): ?string { + $payload = $applePaySession->jsonSerialize(); + $this->init($merchantValidationURL, $payload, $merchantValidationCertificatePath); + try { + return $this->execute(); + } catch (\Exception $exception) { + return $exception; + } + } + + /** + * {@inheritDoc} + */ + public function init($url, $payload, $sslCert): void + { + $timeout = EnvironmentService::getTimeout(); + $curlVerbose = EnvironmentService::isCurlVerbose(); + + $this->request = curl_init(); + $this->setOption(CURLOPT_URL, $url); + $this->setOption(CURLOPT_HTTPHEADER, ['Content-Type: application/json']); + $this->setOption(CURLOPT_POST, 1); + $this->setOption(CURLOPT_DNS_USE_GLOBAL_CACHE, false); + $this->setOption(CURLOPT_POSTFIELDS, $payload); + $this->setOption(CURLOPT_SSLCERT, $sslCert); + + + $this->setOption(CURLOPT_FAILONERROR, false); + $this->setOption(CURLOPT_TIMEOUT, $timeout); + $this->setOption(CURLOPT_CONNECTTIMEOUT, $timeout); + $this->setOption(CURLOPT_HTTP200ALIASES, (array)400); + $this->setOption(CURLOPT_RETURNTRANSFER, 1); + $this->setOption(CURLOPT_SSL_VERIFYPEER, 1); + $this->setOption(CURLOPT_SSL_VERIFYHOST, 2); + $this->setOption(CURLOPT_VERBOSE, $curlVerbose); + $this->setOption(CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2); + } + + /** + * {@inheritDoc} + */ + public function execute(): ?string + { + $response = curl_exec($this->request); + $error = curl_error($this->request); + $errorNo = curl_errno($this->request); + + switch ($errorNo) { + case 0: + return $response; + break; + case CURLE_OPERATION_TIMEDOUT: + $errorMessage = 'Timeout: The Payment API seems to be not available at the moment!'; + break; + default: + $errorMessage = $error . ' (curl_errno: '. $errorNo . ').'; + break; + } + throw new UnzerApiException($errorMessage); + } + + /** + * Sets curl option. + * + * @param $name + * @param $value + */ + private function setOption($name, $value): void + { + curl_setopt($this->request, $name, $value); + } +} diff --git a/src/Adapter/ApplepaySession.php b/src/Adapter/ApplepaySession.php new file mode 100644 index 00000000..e4d47e1d --- /dev/null +++ b/src/Adapter/ApplepaySession.php @@ -0,0 +1,128 @@ + + * + * @package UnzerSDK + * + */ + +namespace UnzerSDK\Adapter; + +class ApplepaySession +{ + /** + * This can be found in the Apple Developer Account + * + * @var string|null $merchantIdentifier + */ + private $merchantIdentifier; + + /** + * This is the Merchant-Name + * + * @var string|null $displayName + */ + private $displayName; + + /** + * This is the Domain Name which has been validated in the Apple Developer Account. + * + * @var string|null $domainName + */ + private $domainName; + + /** + * ApplepaySession constructor. + * + * @param string|null $merchantIdentifier + * @param string|null $displayName + * @param string|null $domainName + */ + public function __construct(string $merchantIdentifier, string $displayName, string $domainName) + { + $this->merchantIdentifier = $merchantIdentifier; + $this->displayName = $displayName; + $this->domainName = $domainName; + } + + public function jsonSerialize() + { + $properties = get_object_vars($this); + return json_encode($properties, JSON_UNESCAPED_SLASHES | JSON_PRESERVE_ZERO_FRACTION); + } + + /** + * @return string|null + */ + public function getMerchantIdentifier(): ?string + { + return $this->merchantIdentifier; + } + + /** + * @param string|null $merchantIdentifier + * + * @return ApplepaySession + */ + public function setMerchantIdentifier(?string $merchantIdentifier): ApplepaySession + { + $this->merchantIdentifier = $merchantIdentifier; + return $this; + } + + /** + * @return string|null + */ + public function getDisplayName(): ?string + { + return $this->displayName; + } + + /** + * @param string|null $displayName + * + * @return ApplepaySession + */ + public function setDisplayName(?string $displayName): ApplepaySession + { + $this->displayName = $displayName; + return $this; + } + + /** + * @return string|null + */ + public function getDomainName(): ?string + { + return $this->domainName; + } + + /** + * @param string|null $domainName + * + * @return ApplepaySession + */ + public function setDomainName(?string $domainName): ApplepaySession + { + $this->domainName = $domainName; + return $this; + } +} diff --git a/test/unit/Adapter/ApplepaySessionTest.php b/test/unit/Adapter/ApplepaySessionTest.php new file mode 100644 index 00000000..c4f8e281 --- /dev/null +++ b/test/unit/Adapter/ApplepaySessionTest.php @@ -0,0 +1,43 @@ + + * + * @package UnzerSDK + * + */ + +namespace UnzerSDK\test\Adapter; + +use UnzerSDK\Adapter\ApplepaySession; +use PHPUnit\Framework\TestCase; + +class ApplepaySessionTest extends TestCase +{ + public function testJsonSerialize() + { + $applepaySession = new ApplepaySession('merchantIdentifier', 'displayName', 'domainName'); + $expectedJson = '{"merchantIdentifier": "merchantIdentifier", "displayName": "displayName", "domainName": "domainName"}'; + + $jsonSerialize = $applepaySession->jsonSerialize(); + $this->assertJsonStringEqualsJsonString($expectedJson, $jsonSerialize); + echo $jsonSerialize; + } +} From 4145e47afd8dce7bc1e1049e679ea4fec684e873 Mon Sep 17 00:00:00 2001 From: "David.Owusu" Date: Thu, 8 Apr 2021 17:50:35 +0200 Subject: [PATCH 09/28] [change] (UMCS-145) added response handling for header in applepay class. --- src/Resources/PaymentTypes/Applepay.php | 17 ++++++++++ .../Resources/PaymentTypes/ApplePayTest.php | 31 +++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/src/Resources/PaymentTypes/Applepay.php b/src/Resources/PaymentTypes/Applepay.php index 9d48e5ac..7d48d6de 100644 --- a/src/Resources/PaymentTypes/Applepay.php +++ b/src/Resources/PaymentTypes/Applepay.php @@ -24,6 +24,8 @@ */ namespace UnzerSDK\Resources\PaymentTypes; +use stdClass; +use UnzerSDK\Adapter\HttpAdapterInterface; use UnzerSDK\Resources\EmbeddedResources\ApplePayHeader; use UnzerSDK\Traits\CanAuthorize; use UnzerSDK\Traits\CanDirectCharge; @@ -256,4 +258,19 @@ public function setVersion(?string $version): Applepay } // + + /** + * @inheritDoc + */ + public function handleResponse(stdClass $response, $method = HttpAdapterInterface::REQUEST_GET): void + { + parent::handleResponse($response, $method); + + if (isset($response->header)) { + $this->header = new ApplePayHeader(null, null, null); + $this->header->handleResponse($response->header); + } + } + + } diff --git a/test/unit/Resources/PaymentTypes/ApplePayTest.php b/test/unit/Resources/PaymentTypes/ApplePayTest.php index bcdaef94..37e78322 100644 --- a/test/unit/Resources/PaymentTypes/ApplePayTest.php +++ b/test/unit/Resources/PaymentTypes/ApplePayTest.php @@ -98,6 +98,37 @@ public function responseShouldBeMappedCorrectly(): void $this->assertSame(1.5000, $applepay->getTransactionAmount()); $this->assertNotNull($applepay->getGeoLocation()); } + /** + * Test Apple Pay json response handling. + * + * @test + */ + public function applepayAuthorizationShouldBeMappedCorrectly(): void + { + $applepay = new Applepay(null, null, null, null); + + $jsonResponse = '{ + "version": "EC_v1", + "data": "data", + "signature": "signature", + "header": { + "ephemeralPublicKey": "ephemeralPublicKey", + "publicKeyHash": "publicKeyHash", + "transactionId": "transactionId" + } + }'; + + $applepay->handleResponse(json_decode($jsonResponse)); + + $this->assertEquals('EC_v1', $applepay->getVersion()); + $this->assertEquals('data', $applepay->getData()); + $this->assertEquals('signature', $applepay->getSignature()); + $applePayHeader = $applepay->getHeader(); + $this->assertNotNull($applePayHeader); + $this->assertEquals('ephemeralPublicKey', $applePayHeader->getEphemeralPublicKey()); + $this->assertEquals('publicKeyHash', $applePayHeader->getPublicKeyHash()); + $this->assertEquals('transactionId', $applePayHeader->getTransactionId()); + } /** * @return ApplePayHeader From e1598e15cc1e9334b786859b27d258cfc5426683 Mon Sep 17 00:00:00 2001 From: "David.Owusu" Date: Thu, 8 Apr 2021 18:57:10 +0200 Subject: [PATCH 10/28] [change] (UMCS-145) Extend unit test and add integration test for applepay. --- src/Resources/PaymentTypes/Applepay.php | 2 - .../integration/PaymentTypes/ApplepayTest.php | 163 ++++++++++++++++++ .../Resources/PaymentTypes/ApplePayTest.php | 1 + 3 files changed, 164 insertions(+), 2 deletions(-) create mode 100644 test/integration/PaymentTypes/ApplepayTest.php diff --git a/src/Resources/PaymentTypes/Applepay.php b/src/Resources/PaymentTypes/Applepay.php index 7d48d6de..2eb12631 100644 --- a/src/Resources/PaymentTypes/Applepay.php +++ b/src/Resources/PaymentTypes/Applepay.php @@ -271,6 +271,4 @@ public function handleResponse(stdClass $response, $method = HttpAdapterInterfac $this->header->handleResponse($response->header); } } - - } diff --git a/test/integration/PaymentTypes/ApplepayTest.php b/test/integration/PaymentTypes/ApplepayTest.php new file mode 100644 index 00000000..d76ea066 --- /dev/null +++ b/test/integration/PaymentTypes/ApplepayTest.php @@ -0,0 +1,163 @@ + + * + * @package UnzerSDK + * + */ + +namespace UnzerSDK\test\integration\PaymentTypes; + +use UnzerSDK\Resources\PaymentTypes\Applepay; +use UnzerSDK\test\BaseIntegrationTest; + +class ApplepayTest extends BaseIntegrationTest +{ + /** + * Verify applepay can be created and fetched. + * + * @test + */ + public function applepayShouldBeCreatableAndFetchable(): void + { + $applepay = $this->createApplepayObject(); + $this->unzer->createPaymentType($applepay); + $this->assertNotNull($applepay->getId()); + + /** @var Applepay $fetchedPaymentTyp */ + $fetchedPaymentTyp = $this->unzer->fetchPaymentType($applepay->getId()); + $this->assertInstanceOf(Applepay::class, $fetchedPaymentTyp); + $this->assertNull($fetchedPaymentTyp->getVersion()); + $this->assertNull($fetchedPaymentTyp->getData()); + $this->assertNull($fetchedPaymentTyp->getSignature()); + $this->assertNull($fetchedPaymentTyp->getHeader()); + } + + /** + * Verify that applepay is chargeable + * + * @test + */ + public function applepayShouldBeChargeable(): void + { + $applepay = $this->createApplepayObject(); + $this->unzer->createPaymentType($applepay); + $charge = $applepay->charge(100.0, 'EUR', self::RETURN_URL); + $this->assertNotNull($charge->getId()); + $this->assertNull($charge->getRedirectUrl()); + } + + /** + * Verify that applepay is chargeable + * + * @test + */ + public function applepayCanBeAuthorized(): void + { + $applepay = $this->createApplepayObject(); + $this->unzer->createPaymentType($applepay); + $charge = $applepay->authorize(100.0, 'EUR', self::RETURN_URL); + + $this->assertNotNull($charge->getId()); + $this->assertNull($charge->getRedirectUrl()); + } + + /** + * Verify the applepay can charge the full amount of the authorization and the payment state is updated accordingly. + * + * @test + */ + public function fullChargeAfterAuthorize(): void + { + $applepay = $this->createApplepayObject(); + $this->unzer->createPaymentType($applepay); + + $authorization = $applepay->authorize(1.0, 'EUR', self::RETURN_URL, null, null, null, null, false); + $payment = $authorization->getPayment(); + + // pre-check to verify changes due to fullCharge call + $this->assertAmounts($payment, 1.0, 0.0, 1.0, 0.0); + $this->assertTrue($payment->isPending()); + + $charge = $this->unzer->chargeAuthorization($payment->getId()); + $paymentNew = $charge->getPayment(); + + // verify payment has been updated properly + $this->assertAmounts($paymentNew, 0.0, 1.0, 1.0, 0.0); + $this->assertTrue($paymentNew->isCompleted()); + } + + /** + * Verify applepay authorize can be canceled. + * + * @test + */ + public function applepayAuthorizeCanBeCanceled(): void + { + /** @var Applepay $applepay */ + $applepay = $this->unzer->createPaymentType($this->createApplepayObject()); + $authorize = $applepay->authorize(100.0, 'EUR', self::RETURN_URL, null, null, null, null, false); + + $cancel = $authorize->cancel(); + $this->assertNotNull($cancel); + $this->assertNotEmpty($cancel->getId()); + } + + /** + * @test + */ + public function fullCancelAfterCharge(): void + { + $applepay = $this->createApplepayObject(); + $this->unzer->createPaymentType($applepay); + $charge = $applepay->charge(100.0, 'EUR', self::RETURN_URL, null, null, null, null, false); + $payment = $charge->getPayment(); + + $this->assertAmounts($payment, 0.0, 100.0, 100.0, 0.0); + $this->assertTrue($payment->isCompleted()); + + $payment->cancelAmount(); + $this->assertAmounts($payment, 0.0, 0.0, 100.0, 100.0); + $this->assertTrue($payment->isCanceled()); + } + + /** + * @return Applepay + */ + private function createApplepayObject(): Applepay + { + $applepayAutorization = '{ + "version": "EC_v1", + "data": "TwQBBorcg6aEb5eidSJm5fNG5sih+R+xgeJbvAX8oMQ7EXhIWOE+ACnvBFHOkZOjI+ump/zVrBXTMRYSw32WMWXPuiRDlYu8DMNuV3qKrbC+G5Du5qfxsm8BxJCXkc/DqtGqc70o8TJCn9lM5ePQjS3io4HDonkN4b4L20GfyEVW1QyvozaMa1u7/gaS6OhhXNk65Z70+xCZlOGmgDtgcdZK+TQIYgRLzyP+1+mpqd61pQ3vJELB8ngMoleCGd1DHx2kVWsudZQ5q97sUjpZV2ySfPXLMhWHYYfvcvSx3dKDAywUoR8clUeDKtoZ4LsBO/B8XM/T4JKnFmWfr7Z25E88vfMWIs8JpxIC5OKAPZfVZoDSNs+4LR+twVxlD5B2xkvG6ln6j4cQ+CFmiq9FPSDgQJsn8O7K9Ag0odXiK6mZczOWt2HCHaw0thF/WpudObVlmw5NN1r54/Jxoichp+DJ2Hl1NJqDHKS1fNyXQcR5jqID7QOcpQi0gE332bOTIz/xe+u328GMCl6Rms3JJxFnnskfEA7nicIH8DLFeSbG8jloLyKBBLk=", + "signature": "MIAGCSqGSIb3DQEHAqCAMIACAQExDzANBglghkgBZQMEAgEFADCABgkqhkiG9w0BBwEAAKCAMIID5jCCA4ugAwIBAgIIaGD2mdnMpw8wCgYIKoZIzj0EAwIwejEuMCwGA1UEAwwlQXBwbGUgQXBwbGljYXRpb24gSW50ZWdyYXRpb24gQ0EgLSBHMzEmMCQGA1UECwwdQXBwbGUgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxEzARBgNVBAoMCkFwcGxlIEluYy4xCzAJBgNVBAYTAlVTMB4XDTE2MDYwMzE4MTY0MFoXDTIxMDYwMjE4MTY0MFowYjEoMCYGA1UEAwwfZWNjLXNtcC1icm9rZXItc2lnbl9VQzQtU0FOREJPWDEUMBIGA1UECwwLaU9TIFN5c3RlbXMxEzARBgNVBAoMCkFwcGxlIEluYy4xCzAJBgNVBAYTAlVTMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEgjD9q8Oc914gLFDZm0US5jfiqQHdbLPgsc1LUmeY+M9OvegaJajCHkwz3c6OKpbC9q+hkwNFxOh6RCbOlRsSlaOCAhEwggINMEUGCCsGAQUFBwEBBDkwNzA1BggrBgEFBQcwAYYpaHR0cDovL29jc3AuYXBwbGUuY29tL29jc3AwNC1hcHBsZWFpY2EzMDIwHQYDVR0OBBYEFAIkMAua7u1GMZekplopnkJxghxFMAwGA1UdEwEB/wQCMAAwHwYDVR0jBBgwFoAUI/JJxE+T5O8n5sT2KGw/orv9LkswggEdBgNVHSAEggEUMIIBEDCCAQwGCSqGSIb3Y2QFATCB/jCBwwYIKwYBBQUHAgIwgbYMgbNSZWxpYW5jZSBvbiB0aGlzIGNlcnRpZmljYXRlIGJ5IGFueSBwYXJ0eSBhc3N1bWVzIGFjY2VwdGFuY2Ugb2YgdGhlIHRoZW4gYXBwbGljYWJsZSBzdGFuZGFyZCB0ZXJtcyBhbmQgY29uZGl0aW9ucyBvZiB1c2UsIGNlcnRpZmljYXRlIHBvbGljeSBhbmQgY2VydGlmaWNhdGlvbiBwcmFjdGljZSBzdGF0ZW1lbnRzLjA2BggrBgEFBQcCARYqaHR0cDovL3d3dy5hcHBsZS5jb20vY2VydGlmaWNhdGVhdXRob3JpdHkvMDQGA1UdHwQtMCswKaAnoCWGI2h0dHA6Ly9jcmwuYXBwbGUuY29tL2FwcGxlYWljYTMuY3JsMA4GA1UdDwEB/wQEAwIHgDAPBgkqhkiG92NkBh0EAgUAMAoGCCqGSM49BAMCA0kAMEYCIQDaHGOui+X2T44R6GVpN7m2nEcr6T6sMjOhZ5NuSo1egwIhAL1a+/hp88DKJ0sv3eT3FxWcs71xmbLKD/QJ3mWagrJNMIIC7jCCAnWgAwIBAgIISW0vvzqY2pcwCgYIKoZIzj0EAwIwZzEbMBkGA1UEAwwSQXBwbGUgUm9vdCBDQSAtIEczMSYwJAYDVQQLDB1BcHBsZSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTETMBEGA1UECgwKQXBwbGUgSW5jLjELMAkGA1UEBhMCVVMwHhcNMTQwNTA2MjM0NjMwWhcNMjkwNTA2MjM0NjMwWjB6MS4wLAYDVQQDDCVBcHBsZSBBcHBsaWNhdGlvbiBJbnRlZ3JhdGlvbiBDQSAtIEczMSYwJAYDVQQLDB1BcHBsZSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTETMBEGA1UECgwKQXBwbGUgSW5jLjELMAkGA1UEBhMCVVMwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATwFxGEGddkhdUaXiWBB3bogKLv3nuuTeCN/EuT4TNW1WZbNa4i0Jd2DSJOe7oI/XYXzojLdrtmcL7I6CmE/1RFo4H3MIH0MEYGCCsGAQUFBwEBBDowODA2BggrBgEFBQcwAYYqaHR0cDovL29jc3AuYXBwbGUuY29tL29jc3AwNC1hcHBsZXJvb3RjYWczMB0GA1UdDgQWBBQj8knET5Pk7yfmxPYobD+iu/0uSzAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFLuw3qFYM4iapIqZ3r6966/ayySrMDcGA1UdHwQwMC4wLKAqoCiGJmh0dHA6Ly9jcmwuYXBwbGUuY29tL2FwcGxlcm9vdGNhZzMuY3JsMA4GA1UdDwEB/wQEAwIBBjAQBgoqhkiG92NkBgIOBAIFADAKBggqhkjOPQQDAgNnADBkAjA6z3KDURaZsYb7NcNWymK/9Bft2Q91TaKOvvGcgV5Ct4n4mPebWZ+Y1UENj53pwv4CMDIt1UQhsKMFd2xd8zg7kGf9F3wsIW2WT8ZyaYISb1T4en0bmcubCYkhYQaZDwmSHQAAMYIBjDCCAYgCAQEwgYYwejEuMCwGA1UEAwwlQXBwbGUgQXBwbGljYXRpb24gSW50ZWdyYXRpb24gQ0EgLSBHMzEmMCQGA1UECwwdQXBwbGUgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxEzARBgNVBAoMCkFwcGxlIEluYy4xCzAJBgNVBAYTAlVTAghoYPaZ2cynDzANBglghkgBZQMEAgEFAKCBlTAYBgkqhkiG9w0BCQMxCwYJKoZIhvcNAQcBMBwGCSqGSIb3DQEJBTEPFw0yMTA0MDgxNjA4MTFaMCoGCSqGSIb3DQEJNDEdMBswDQYJYIZIAWUDBAIBBQChCgYIKoZIzj0EAwIwLwYJKoZIhvcNAQkEMSIEIOkz+k59f4rvza+A8zqMCZevZJgynnkAoaVcIBhzE7uxMAoGCCqGSM49BAMCBEcwRQIgTpDgEPz4evB42QV7YrUsjg+n/6ObYCPO8w3zEbswOM8CIQDjvo3vluxulxHB+mTrtr7Gnyoc8ccN6rzuXvFG2wKnbAAAAAAAAA==", + "header": { + "ephemeralPublicKey": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEW7hYAxjCeE/r9SSRX/hJsfO+VxLvUqIzyeGn6lZ1v/pYYS66Bz0dsSzoMMZg8G32TAPXUr97AD4zCXfcQoZaOA==", + "publicKeyHash": "zqO5Y3ldWWm4NnIkfGCvJILw30rp3y46Jsf21gE8CNg=", + "transactionId": "94f6b37149ae2098efb287ed0ade704284cff3f672ef7f0dc17614b31e926b9d" + } + }'; + + $applepay = new Applepay(null, null, null, null); + $applepay->handleResponse(json_decode($applepayAutorization)); + return $applepay; + } +} diff --git a/test/unit/Resources/PaymentTypes/ApplePayTest.php b/test/unit/Resources/PaymentTypes/ApplePayTest.php index 37e78322..0b6374df 100644 --- a/test/unit/Resources/PaymentTypes/ApplePayTest.php +++ b/test/unit/Resources/PaymentTypes/ApplePayTest.php @@ -98,6 +98,7 @@ public function responseShouldBeMappedCorrectly(): void $this->assertSame(1.5000, $applepay->getTransactionAmount()); $this->assertNotNull($applepay->getGeoLocation()); } + /** * Test Apple Pay json response handling. * From 4a0f352c96a874a5327a981a4373f3cce29c659f Mon Sep 17 00:00:00 2001 From: "David.Owusu" Date: Tue, 13 Apr 2021 15:14:11 +0200 Subject: [PATCH 11/28] [change] (UMCS-181) Moved applepay session. --- .../ExternalResources}/ApplepaySession.php | 8 ++++---- test/unit/Adapter/ApplepaySessionTest.php | 7 +++---- 2 files changed, 7 insertions(+), 8 deletions(-) rename src/{Adapter => Resources/ExternalResources}/ApplepaySession.php (93%) diff --git a/src/Adapter/ApplepaySession.php b/src/Resources/ExternalResources/ApplepaySession.php similarity index 93% rename from src/Adapter/ApplepaySession.php rename to src/Resources/ExternalResources/ApplepaySession.php index e4d47e1d..d20caa57 100644 --- a/src/Adapter/ApplepaySession.php +++ b/src/Resources/ExternalResources/ApplepaySession.php @@ -1,8 +1,8 @@ + * @author David Owusu * * @package UnzerSDK * */ -namespace UnzerSDK\Adapter; +namespace UnzerSDK\Resources\ExternalResources; class ApplepaySession { diff --git a/test/unit/Adapter/ApplepaySessionTest.php b/test/unit/Adapter/ApplepaySessionTest.php index c4f8e281..39f06f39 100644 --- a/test/unit/Adapter/ApplepaySessionTest.php +++ b/test/unit/Adapter/ApplepaySessionTest.php @@ -2,7 +2,7 @@ /* * [DESCRIPTION] * - * Copyright (C) [ACTUAL YEAR] - today Unzer E-Com GmbH + * Copyright (C) 2021 - today Unzer E-Com GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,7 +18,7 @@ * * @link https://docs.unzer.com/ * - * @author [AUTHOR] + * @author David Owusu * * @package UnzerSDK * @@ -26,8 +26,8 @@ namespace UnzerSDK\test\Adapter; -use UnzerSDK\Adapter\ApplepaySession; use PHPUnit\Framework\TestCase; +use UnzerSDK\Resources\ExternalResources\ApplepaySession; class ApplepaySessionTest extends TestCase { @@ -38,6 +38,5 @@ public function testJsonSerialize() $jsonSerialize = $applepaySession->jsonSerialize(); $this->assertJsonStringEqualsJsonString($expectedJson, $jsonSerialize); - echo $jsonSerialize; } } From dbc0e26b0d477ed247281ccefa17c9cb2d51e1be Mon Sep 17 00:00:00 2001 From: "David.Owusu" Date: Tue, 13 Apr 2021 15:27:37 +0200 Subject: [PATCH 12/28] [change] (UMCS-181) Add environment variables for applepay merchant validation certificate. --- src/Services/EnvironmentService.php | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/Services/EnvironmentService.php b/src/Services/EnvironmentService.php index fbfdbaa9..5244d8e2 100755 --- a/src/Services/EnvironmentService.php +++ b/src/Services/EnvironmentService.php @@ -41,6 +41,9 @@ class EnvironmentService public const ENV_VAR_TEST_PRIVATE_KEY_NON_3DS = 'UNZER_PAPI_TEST_PRIVATE_KEY_NON_3DS'; public const ENV_VAR_TEST_PUBLIC_KEY_NON_3DS = 'UNZER_PAPI_TEST_PUBLIC_KEY_NON_3DS'; + public const ENV_VAR_TEST_APPLE_MERCHANT_CERTIFICATE = 'UNZER_APPLE_MERCHANT_CERTIFICATE_PATH'; + public const ENV_VAR_TEST_APPLE_CA_CERTIFICATE = 'UNZER_APPLE_CA_CERTIFICATE_PATH'; + private const ENV_VAR_NAME_TIMEOUT = 'UNZER_PAPI_TIMEOUT'; private const DEFAULT_TIMEOUT = 60; @@ -137,4 +140,22 @@ public static function getTestPublicKey($non3ds = false): string $key = stripslashes($_SERVER[$variableName] ?? ''); return empty($key) ? '' : $key; } + + /** + * Returns the apple merchant certificate path set via environment variable. + * @return string + */ + public static function getAppleMerchantCertificatePath(): string + { + return $_SERVER[self::ENV_VAR_TEST_APPLE_MERCHANT_CERTIFICATE] ?? ''; + } + + /** + * Returns the CA certificate path set via environment variable. + * @return string + */ + public static function getAppleCaCertificatePath(): string + { + return $_SERVER[self::ENV_VAR_TEST_APPLE_CA_CERTIFICATE] ?? ''; + } } From 6ea4b6216507060acee927971fe41874742b50c1 Mon Sep 17 00:00:00 2001 From: "David.Owusu" Date: Tue, 13 Apr 2021 15:29:00 +0200 Subject: [PATCH 13/28] [change] (UMCS-181) Add Applepay adapter test. --- src/Adapter/ApplepayAdapter.php | 86 ++++++++++------ .../ApplepayMerchantValidationException.php | 33 +++++++ src/Services/EnvironmentService.php | 2 + test/integration/ApplepayAdapterTest.php | 98 +++++++++++++++++++ 4 files changed, 188 insertions(+), 31 deletions(-) create mode 100644 src/Exceptions/ApplepayMerchantValidationException.php create mode 100644 test/integration/ApplepayAdapterTest.php diff --git a/src/Adapter/ApplepayAdapter.php b/src/Adapter/ApplepayAdapter.php index ce2194c4..22fc76e8 100644 --- a/src/Adapter/ApplepayAdapter.php +++ b/src/Adapter/ApplepayAdapter.php @@ -1,5 +1,6 @@ jsonSerialize(); - $this->init($merchantValidationURL, $payload, $merchantValidationCertificatePath); - try { - return $this->execute(); - } catch (\Exception $exception) { - return $exception; - } + $this->init( + $merchantValidationURL, + $payload, + $merchantValidationCertificatePath, + $merchantValidationCertificateKeyChainPath + ); + $sessionResponse = $this->execute(); + $this->close(); + return $sessionResponse; } /** * {@inheritDoc} */ - public function init($url, $payload, $sslCert): void + public function init($url, $payload, $sslCert, $caCert = null): void { $timeout = EnvironmentService::getTimeout(); $curlVerbose = EnvironmentService::isCurlVerbose(); - $this->request = curl_init(); - $this->setOption(CURLOPT_URL, $url); + $this->request = curl_init($url); $this->setOption(CURLOPT_HTTPHEADER, ['Content-Type: application/json']); $this->setOption(CURLOPT_POST, 1); $this->setOption(CURLOPT_DNS_USE_GLOBAL_CACHE, false); $this->setOption(CURLOPT_POSTFIELDS, $payload); - $this->setOption(CURLOPT_SSLCERT, $sslCert); - - $this->setOption(CURLOPT_FAILONERROR, false); $this->setOption(CURLOPT_TIMEOUT, $timeout); $this->setOption(CURLOPT_CONNECTTIMEOUT, $timeout); @@ -83,39 +84,62 @@ public function init($url, $payload, $sslCert): void $this->setOption(CURLOPT_SSL_VERIFYHOST, 2); $this->setOption(CURLOPT_VERBOSE, $curlVerbose); $this->setOption(CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2); + + $this->setOption(CURLOPT_SSLCERT, $sslCert); + if (isset($caCert)) { + $this->setOption(CURLOPT_CAINFO, $caCert); + } + } + + /** + * Sets curl option. + * + * @param $name + * @param $value + */ + private function setOption($name, $value): void + { + curl_setopt($this->request, $name, $value); } /** * {@inheritDoc} + * + * @throws ApplepayMerchantValidationException */ public function execute(): ?string { $response = curl_exec($this->request); - $error = curl_error($this->request); - $errorNo = curl_errno($this->request); + $error = curl_error($this->request); + $errorNo = curl_errno($this->request); switch ($errorNo) { case 0: return $response; break; case CURLE_OPERATION_TIMEDOUT: - $errorMessage = 'Timeout: The Payment API seems to be not available at the moment!'; + $errorMessage = 'Timeout: The Applepay API seems to be not available at the moment!'; break; default: - $errorMessage = $error . ' (curl_errno: '. $errorNo . ').'; + $errorMessage = $error . ' (curl_errno: ' . $errorNo . ').'; break; } - throw new UnzerApiException($errorMessage); + throw new ApplepayMerchantValidationException($errorMessage); } /** - * Sets curl option. - * - * @param $name - * @param $value + * @inheritDoc */ - private function setOption($name, $value): void + public function close(): void { - curl_setopt($this->request, $name, $value); + curl_close($this->request); + } + + /** + * @inheritDoc + */ + public function getResponseCode(): string + { + return curl_getinfo($this->request, CURLINFO_HTTP_CODE); } } diff --git a/src/Exceptions/ApplepayMerchantValidationException.php b/src/Exceptions/ApplepayMerchantValidationException.php new file mode 100644 index 00000000..ba61e821 --- /dev/null +++ b/src/Exceptions/ApplepayMerchantValidationException.php @@ -0,0 +1,33 @@ + + * + * @package UnzerSDK + * + */ + +namespace UnzerSDK\Exceptions; + +use Exception; + +class ApplepayMerchantValidationException extends Exception +{ +} diff --git a/src/Services/EnvironmentService.php b/src/Services/EnvironmentService.php index 5244d8e2..a6063039 100755 --- a/src/Services/EnvironmentService.php +++ b/src/Services/EnvironmentService.php @@ -143,6 +143,7 @@ public static function getTestPublicKey($non3ds = false): string /** * Returns the apple merchant certificate path set via environment variable. + * * @return string */ public static function getAppleMerchantCertificatePath(): string @@ -152,6 +153,7 @@ public static function getAppleMerchantCertificatePath(): string /** * Returns the CA certificate path set via environment variable. + * * @return string */ public static function getAppleCaCertificatePath(): string diff --git a/test/integration/ApplepayAdapterTest.php b/test/integration/ApplepayAdapterTest.php new file mode 100644 index 00000000..0aad0d6b --- /dev/null +++ b/test/integration/ApplepayAdapterTest.php @@ -0,0 +1,98 @@ + + * + * @package UnzerSDK + * + */ + +namespace UnzerSDK\test\integration; + +use UnzerSDK\Adapter\ApplepayAdapter; +use UnzerSDK\Resources\ExternalResources\ApplepaySession; +use UnzerSDK\Services\EnvironmentService; +use UnzerSDK\test\BaseIntegrationTest; + +class ApplepayAdapterTest extends BaseIntegrationTest +{ + private $merchantValidationUrl; + private $merchantValidationCertificatePath; + private $appleCaCertificatePath; + + /** + * @inheritDoc + */ + protected function setUp(): void + { + $this->merchantValidationUrl = 'https://apple-pay-gateway-cert.apple.com/paymentservices/startSession'; + $this->merchantValidationCertificatePath = EnvironmentService::getAppleMerchantCertificatePath(); + $this->appleCaCertificatePath = EnvironmentService::getAppleCaCertificatePath(); + } + + /** + * test merchant validation request. + * + * @test + */ + public function verifyMerchantValidationRequest(): void + { + $applepaySession = $this->createApplepaySession(); + $appleAdapter = new ApplepayAdapter(); + + $validationResponse = $appleAdapter->validateApplePayMerchant( + $this->merchantValidationUrl, + $applepaySession, + $this->merchantValidationCertificatePath, + $this->appleCaCertificatePath + ); + + $this->assertNotNull($validationResponse); + } + + /** + * test merchant validation request without ca certificate. + * + * @test + */ + public function merchantValidationWorksWithoutCaCert(): void + { + $merchantValidationCertificatePath = EnvironmentService::getAppleMerchantCertificatePath(); + + $applepaySession = $this->createApplepaySession(); + $appleAdapter = new ApplepayAdapter(); + + $validationResponse = $appleAdapter->validateApplePayMerchant( + $this->merchantValidationUrl, + $applepaySession, + $this->merchantValidationCertificatePath + ); + + $this->assertNotNull($validationResponse); + } + + /** + * @return ApplepaySession + */ + private function createApplepaySession(): ApplepaySession + { + return new ApplepaySession('merchantIdentifier', 'displayName', 'domainName'); + } +} From 4bedaa0fda8564d76173696d514267d1fa5ee1a0 Mon Sep 17 00:00:00 2001 From: "David.Owusu" Date: Thu, 15 Apr 2021 11:14:29 +0200 Subject: [PATCH 14/28] [change] (UMCS-181) Check domain merchantValidationUrl. --- src/Adapter/ApplepayAdapter.php | 18 +++++++ src/Constants/ApplepayValidationDomains.php | 52 +++++++++++++++++++ test/integration/ApplepayAdapterTest.php | 57 ++++++++++++++++++++- test/unit/Adapter/ApplepaySessionTest.php | 4 +- 4 files changed, 127 insertions(+), 4 deletions(-) create mode 100644 src/Constants/ApplepayValidationDomains.php diff --git a/src/Adapter/ApplepayAdapter.php b/src/Adapter/ApplepayAdapter.php index 22fc76e8..b7c9ad7d 100644 --- a/src/Adapter/ApplepayAdapter.php +++ b/src/Adapter/ApplepayAdapter.php @@ -26,6 +26,7 @@ */ namespace UnzerSDK\Adapter; +use UnzerSDK\Constants\ApplepayValidationDomains; use UnzerSDK\Exceptions\ApplepayMerchantValidationException; use UnzerSDK\Resources\ExternalResources\ApplepaySession; use UnzerSDK\Services\EnvironmentService; @@ -50,6 +51,9 @@ public function validateApplePayMerchant( string $merchantValidationCertificatePath, ?string $merchantValidationCertificateKeyChainPath = null ): ?string { + if (!$this->validMerchantValidationDomain($merchantValidationURL)) { + throw new ApplepayMerchantValidationException('Invalid URL used merchantValidation request.'); + } $payload = $applePaySession->jsonSerialize(); $this->init( $merchantValidationURL, @@ -62,6 +66,20 @@ public function validateApplePayMerchant( return $sessionResponse; } + /** + * Check whether domain of merchantValidationURL is allowed for validation request. + * + * @param string $merchantValidationURL URL used for merchant validation request. + * + */ + public function validMerchantValidationDomain(string $merchantValidationURL): bool + { + $domain = explode('/', $merchantValidationURL)[2] ?? ''; + + $UrlList = ApplepayValidationDomains::ALLOWED_VALIDATION_URLS; + return in_array($domain, $UrlList); + } + /** * {@inheritDoc} */ diff --git a/src/Constants/ApplepayValidationDomains.php b/src/Constants/ApplepayValidationDomains.php new file mode 100644 index 00000000..9f9824ab --- /dev/null +++ b/src/Constants/ApplepayValidationDomains.php @@ -0,0 +1,52 @@ + + * + * @package UnzerSDK\Constants + */ +namespace UnzerSDK\Constants; + +class ApplepayValidationDomains +{ + // URL list + public const ALLOWED_VALIDATION_URLS = [ + 'apple-pay-gateway.apple.com', + 'cn-apple-pay-gateway.apple.com', + 'apple-pay-gateway-nc-pod1.apple.com', + 'apple-pay-gateway-nc-pod2.apple.com', + 'apple-pay-gateway-nc-pod3.apple.com', + 'apple-pay-gateway-nc-pod4.apple.com', + 'apple-pay-gateway-nc-pod5.apple.com', + 'apple-pay-gateway-pr-pod1.apple.com', + 'apple-pay-gateway-pr-pod2.apple.com', + 'apple-pay-gateway-pr-pod3.apple.com', + 'apple-pay-gateway-pr-pod4.apple.com', + 'apple-pay-gateway-pr-pod5.apple.com', + 'cn-apple-pay-gateway-sh-pod1.apple.com', + 'cn-apple-pay-gateway-sh-pod2.apple.com', + 'cn-apple-pay-gateway-sh-pod3.apple.com', + 'cn-apple-pay-gateway-tj-pod1.apple.com', + 'cn-apple-pay-gateway-tj-pod2.apple.com', + 'cn-apple-pay-gateway-tj-pod3.apple.com', + 'apple-pay-gateway-cert.apple.com', + 'cn-apple-pay-gateway-cert.apple.com' + ]; +} diff --git a/test/integration/ApplepayAdapterTest.php b/test/integration/ApplepayAdapterTest.php index 0aad0d6b..57fc5462 100644 --- a/test/integration/ApplepayAdapterTest.php +++ b/test/integration/ApplepayAdapterTest.php @@ -27,6 +27,7 @@ namespace UnzerSDK\test\integration; use UnzerSDK\Adapter\ApplepayAdapter; +use UnzerSDK\Exceptions\ApplepayMerchantValidationException; use UnzerSDK\Resources\ExternalResources\ApplepaySession; use UnzerSDK\Services\EnvironmentService; use UnzerSDK\test\BaseIntegrationTest; @@ -37,6 +38,17 @@ class ApplepayAdapterTest extends BaseIntegrationTest private $merchantValidationCertificatePath; private $appleCaCertificatePath; + public function domainShouldBeValidatedCorrectlyDP() + { + return [ + 'invalid: example.domain.com' => ['https://example.domain.com', false], + 'valid: https://apple-pay-gateway.apple.com/some/path' => ['https://apple-pay-gateway.apple.com/some/path', true], + 'valid: https://cn-apple-pay-gateway.apple.com' => ['https://cn-apple-pay-gateway.apple.com', true], + 'invalid: apple-pay-gateway-nc-pod1.apple.com' => ['apple-pay-gateway-nc-pod1.apple.com', false], + 'invalid: (empty)' => ['', false], + ]; + } + /** * @inheritDoc */ @@ -51,6 +63,8 @@ protected function setUp(): void * test merchant validation request. * * @test + * + * @throws \UnzerSDK\Exceptions\ApplepayMerchantValidationException */ public function verifyMerchantValidationRequest(): void { @@ -71,11 +85,11 @@ public function verifyMerchantValidationRequest(): void * test merchant validation request without ca certificate. * * @test + * + * @throws \UnzerSDK\Exceptions\ApplepayMerchantValidationException */ public function merchantValidationWorksWithoutCaCert(): void { - $merchantValidationCertificatePath = EnvironmentService::getAppleMerchantCertificatePath(); - $applepaySession = $this->createApplepaySession(); $appleAdapter = new ApplepayAdapter(); @@ -88,6 +102,45 @@ public function merchantValidationWorksWithoutCaCert(): void $this->assertNotNull($validationResponse); } + /** + * Merchant validation call should throw Exception if domain of Validation url is invalid. + * + * @test + * + */ + public function merchantValidationThrowsErrorForInvalidDomain(): void + { + $applepaySession = $this->createApplepaySession(); + $appleAdapter = new ApplepayAdapter(); + + $this->expectException(ApplepayMerchantValidationException::class); + $this->expectExceptionMessage('Invalid URL used merchantValidation request.'); + + $appleAdapter->validateApplePayMerchant( + 'https://invalid.domain.com/some/path', + $applepaySession, + $this->merchantValidationCertificatePath + ); + } + + /** + * test merchant validation request without ca certificate. + * + * @dataProvider domainShouldBeValidatedCorrectlyDP + * @test + * + * @param mixed $validationUrl + * @param mixed $expectedResult + * + */ + public function domainShouldBeValidatedCorrectly($validationUrl, $expectedResult): void + { + $appleAdapter = new ApplepayAdapter(); + + $domainValidation = $appleAdapter->validMerchantValidationDomain($validationUrl); + $this->assertEquals($expectedResult, $domainValidation); + } + /** * @return ApplepaySession */ diff --git a/test/unit/Adapter/ApplepaySessionTest.php b/test/unit/Adapter/ApplepaySessionTest.php index 39f06f39..0fc7e529 100644 --- a/test/unit/Adapter/ApplepaySessionTest.php +++ b/test/unit/Adapter/ApplepaySessionTest.php @@ -24,14 +24,14 @@ * */ -namespace UnzerSDK\test\Adapter; +namespace UnzerSDK\test\unit\Adapter; use PHPUnit\Framework\TestCase; use UnzerSDK\Resources\ExternalResources\ApplepaySession; class ApplepaySessionTest extends TestCase { - public function testJsonSerialize() + public function testJsonSerialize(): void { $applepaySession = new ApplepaySession('merchantIdentifier', 'displayName', 'domainName'); $expectedJson = '{"merchantIdentifier": "merchantIdentifier", "displayName": "displayName", "domainName": "domainName"}'; From 68368e1d869fabfa7f99e1a1b54b63d1c623bf0a Mon Sep 17 00:00:00 2001 From: "David.Owusu" Date: Thu, 15 Apr 2021 11:36:15 +0200 Subject: [PATCH 15/28] [change] (UMCS-181) Check domain merchantValidationUrl. --- src/Adapter/ApplepayAdapter.php | 2 +- test/integration/ApplepayAdapterTest.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Adapter/ApplepayAdapter.php b/src/Adapter/ApplepayAdapter.php index b7c9ad7d..cd33937a 100644 --- a/src/Adapter/ApplepayAdapter.php +++ b/src/Adapter/ApplepayAdapter.php @@ -52,7 +52,7 @@ public function validateApplePayMerchant( ?string $merchantValidationCertificateKeyChainPath = null ): ?string { if (!$this->validMerchantValidationDomain($merchantValidationURL)) { - throw new ApplepayMerchantValidationException('Invalid URL used merchantValidation request.'); + throw new ApplepayMerchantValidationException('Invalid URL used for merchantValidation request.'); } $payload = $applePaySession->jsonSerialize(); $this->init( diff --git a/test/integration/ApplepayAdapterTest.php b/test/integration/ApplepayAdapterTest.php index 57fc5462..98f002b7 100644 --- a/test/integration/ApplepayAdapterTest.php +++ b/test/integration/ApplepayAdapterTest.php @@ -114,7 +114,7 @@ public function merchantValidationThrowsErrorForInvalidDomain(): void $appleAdapter = new ApplepayAdapter(); $this->expectException(ApplepayMerchantValidationException::class); - $this->expectExceptionMessage('Invalid URL used merchantValidation request.'); + $this->expectExceptionMessage('Invalid URL used for merchantValidation request.'); $appleAdapter->validateApplePayMerchant( 'https://invalid.domain.com/some/path', From 6f803a67c638c41c8cfa8bdda6049ca9572f9009 Mon Sep 17 00:00:00 2001 From: "David.Owusu" Date: Thu, 15 Apr 2021 13:34:31 +0200 Subject: [PATCH 16/28] [change] (UMCS-181) strip slashes of env vars. --- src/Services/EnvironmentService.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Services/EnvironmentService.php b/src/Services/EnvironmentService.php index a6063039..f505492b 100755 --- a/src/Services/EnvironmentService.php +++ b/src/Services/EnvironmentService.php @@ -148,7 +148,7 @@ public static function getTestPublicKey($non3ds = false): string */ public static function getAppleMerchantCertificatePath(): string { - return $_SERVER[self::ENV_VAR_TEST_APPLE_MERCHANT_CERTIFICATE] ?? ''; + return stripslashes($_SERVER[self::ENV_VAR_TEST_APPLE_MERCHANT_CERTIFICATE]) ?? ''; } /** @@ -158,6 +158,6 @@ public static function getAppleMerchantCertificatePath(): string */ public static function getAppleCaCertificatePath(): string { - return $_SERVER[self::ENV_VAR_TEST_APPLE_CA_CERTIFICATE] ?? ''; + return stripslashes($_SERVER[self::ENV_VAR_TEST_APPLE_CA_CERTIFICATE]) ?? ''; } } From dea0f85fee9c87c02909e1247dac191531f2aacc Mon Sep 17 00:00:00 2001 From: "David.Owusu" Date: Thu, 15 Apr 2021 14:58:56 +0200 Subject: [PATCH 17/28] [change] (UMCS-185) -Add additional test cases for applepay. --- .../integration/PaymentTypes/ApplepayTest.php | 269 +++++++++++++++++- 1 file changed, 267 insertions(+), 2 deletions(-) diff --git a/test/integration/PaymentTypes/ApplepayTest.php b/test/integration/PaymentTypes/ApplepayTest.php index d76ea066..bad9c4c8 100644 --- a/test/integration/PaymentTypes/ApplepayTest.php +++ b/test/integration/PaymentTypes/ApplepayTest.php @@ -27,6 +27,8 @@ namespace UnzerSDK\test\integration\PaymentTypes; +use UnzerSDK\Constants\ApiResponseCodes; +use UnzerSDK\Exceptions\UnzerApiException; use UnzerSDK\Resources\PaymentTypes\Applepay; use UnzerSDK\test\BaseIntegrationTest; @@ -36,6 +38,8 @@ class ApplepayTest extends BaseIntegrationTest * Verify applepay can be created and fetched. * * @test + * + * @throws UnzerApiException */ public function applepayShouldBeCreatableAndFetchable(): void { @@ -56,6 +60,8 @@ public function applepayShouldBeCreatableAndFetchable(): void * Verify that applepay is chargeable * * @test + * + * @throws UnzerApiException */ public function applepayShouldBeChargeable(): void { @@ -70,21 +76,75 @@ public function applepayShouldBeChargeable(): void * Verify that applepay is chargeable * * @test + * + * @throws \UnzerSDK\Exceptions\UnzerApiException */ public function applepayCanBeAuthorized(): void { $applepay = $this->createApplepayObject(); $this->unzer->createPaymentType($applepay); - $charge = $applepay->authorize(100.0, 'EUR', self::RETURN_URL); + $authorization = $applepay->authorize(1.0, 'EUR', self::RETURN_URL); + // verify authorization has been created + $this->assertNotNull($authorization->getId()); + $this->assertNull($authorization->getRedirectUrl()); + + // verify payment object has been created + $payment = $authorization->getPayment(); + $this->assertNotNull($payment); + $this->assertNotNull($payment->getId()); + + // verify resources are linked properly + $this->assertSame($authorization, $payment->getAuthorization()); + $this->assertSame($applepay, $payment->getPaymentType()); + + // verify the payment object has been updated properly + $this->assertAmounts($payment, 1.0, 0.0, 1.0, 0.0); + $this->assertTrue($payment->isPending()); + } + + /** + * Verify the applepay can perform charges and creates a payment object doing so. + * + * @test + * + * @throws UnzerApiException + */ + public function applepayCanPerformChargeAndCreatesPaymentObject(): void + { + $applepay = $this->createApplepayObject(); + /** @var Applepay $applepay */ + $applepay = $this->unzer->createPaymentType($applepay); + + $charge = $applepay->charge(1.0, 'EUR', self::RETURN_URL, null, null, null, null, false); + + // applepay recurring is activated through charge transaction + /** @var Applepay $fetchedApplepay */ + $fetchedApplepay = $this->unzer->fetchPaymentType($applepay->getId()); + + // verify charge has been created $this->assertNotNull($charge->getId()); - $this->assertNull($charge->getRedirectUrl()); + + // verify payment object has been created + $payment = $charge->getPayment(); + $this->assertNotNull($payment); + $this->assertNotNull($payment->getId()); + + // verify resources are linked properly + $this->assertEquals($charge->expose(), $payment->getCharge($charge->getId())->expose()); + $this->assertSame($applepay, $payment->getPaymentType()); + + // verify the payment object has been updated properly + $this->assertAmounts($payment, 0.0, 1.0, 1.0, 0.0); + $this->assertTrue($payment->isCompleted()); } /** * Verify the applepay can charge the full amount of the authorization and the payment state is updated accordingly. * * @test + * + * @throws UnzerApiException */ public function fullChargeAfterAuthorize(): void { @@ -106,10 +166,83 @@ public function fullChargeAfterAuthorize(): void $this->assertTrue($paymentNew->isCompleted()); } + /** + * Verify the applepay can charge part of the authorized amount and the payment state is updated accordingly. + * + * @test + * + * @throws UnzerApiException + */ + public function partialChargeAfterAuthorization(): void + { + $applepay = $this->createApplepayObject(); + /** @var Applepay $applepay */ + $applepay = $this->unzer->createPaymentType($applepay); + $authorization = $this->unzer->authorize( + 100.0, + 'EUR', + $applepay, + self::RETURN_URL, + null, + null, + null, + null, + false + ); + + $payment = $authorization->getPayment(); + $this->assertAmounts($payment, 100.0, 0.0, 100.0, 0.0); + $this->assertTrue($payment->isPending()); + + $charge = $this->unzer->chargeAuthorization($payment->getId(), 20); + $payment1 = $charge->getPayment(); + $this->assertAmounts($payment1, 80.0, 20.0, 100.0, 0.0); + $this->assertTrue($payment1->isPartlyPaid()); + + $charge = $this->unzer->chargeAuthorization($payment->getId(), 20); + $payment2 = $charge->getPayment(); + $this->assertAmounts($payment2, 60.0, 40.0, 100.0, 0.0); + $this->assertTrue($payment2->isPartlyPaid()); + + $charge = $this->unzer->chargeAuthorization($payment->getId(), 60); + $payment3 = $charge->getPayment(); + $this->assertAmounts($payment3, 00.0, 100.0, 100.0, 0.0); + $this->assertTrue($payment3->isCompleted()); + } + + /** + * Verify that an exception is thrown when trying to charge more than authorized. + * + * @test + * + * @throws UnzerApiException + */ + public function exceptionShouldBeThrownWhenChargingMoreThenAuthorized(): void + { + $applepay = $this->createApplepayObject(); + /** @var Applepay $applepay */ + $applepay = $this->unzer->createPaymentType($applepay); + $authorization = $applepay->authorize(100.0000, 'EUR', self::RETURN_URL, null, null, null, null, false); + $payment = $authorization->getPayment(); + $this->assertAmounts($payment, 100.0, 0.0, 100.0, 0.0); + $this->assertTrue($payment->isPending()); + + $charge = $this->unzer->chargeAuthorization($payment->getId(), 50); + $payment1 = $charge->getPayment(); + $this->assertAmounts($payment1, 50.0, 50.0, 100.0, 0.0); + $this->assertTrue($payment1->isPartlyPaid()); + + $this->expectException(UnzerApiException::class); + $this->expectExceptionCode(ApiResponseCodes::API_ERROR_CHARGED_AMOUNT_HIGHER_THAN_EXPECTED); + $this->unzer->chargeAuthorization($payment->getId(), 70); + } + /** * Verify applepay authorize can be canceled. * * @test + * + * @throws UnzerApiException */ public function applepayAuthorizeCanBeCanceled(): void { @@ -123,7 +256,57 @@ public function applepayAuthorizeCanBeCanceled(): void } /** + * Verify the applepay payment can be charged until it is fully charged and the payment is updated accordingly. + * + * @test + * + * @throws UnzerApiException + */ + public function partialAndFullChargeAfterAuthorization(): void + { + $applepay = $this->createApplepayObject(); + /** @var Applepay $applepay */ + $applepay = $this->unzer->createPaymentType($applepay); + $authorization = $applepay->authorize(100.0000, 'EUR', self::RETURN_URL, null, null, null, null, false); + $payment = $authorization->getPayment(); + + $this->assertAmounts($payment, 100.0, 0.0, 100.0, 0.0); + $this->assertTrue($payment->isPending()); + + $charge = $this->unzer->chargeAuthorization($payment->getId(), 20); + $payment1 = $charge->getPayment(); + $this->assertAmounts($payment1, 80.0, 20.0, 100.0, 0.0); + $this->assertTrue($payment1->isPartlyPaid()); + + $charge = $this->unzer->chargeAuthorization($payment->getId()); + $payment2 = $charge->getPayment(); + $this->assertAmounts($payment2, 0.0, 100.0, 100.0, 0.0); + $this->assertTrue($payment2->isCompleted()); + } + + /** + * Authorization can be fetched. + * * @test + * + * @throws UnzerApiException + */ + public function authorizationShouldBeFetchable(): void + { + $applepay = $this->createApplepayObject(); + /** @var Applepay $applepay */ + $applepay = $this->unzer->createPaymentType($applepay); + $authorization = $applepay->authorize(100.0000, 'EUR', self::RETURN_URL); + $payment = $authorization->getPayment(); + + $fetchedAuthorization = $this->unzer->fetchAuthorization($payment->getId()); + $this->assertEquals($fetchedAuthorization->getId(), $authorization->getId()); + } + + /** + * @test + * + * @throws UnzerApiException */ public function fullCancelAfterCharge(): void { @@ -140,6 +323,88 @@ public function fullCancelAfterCharge(): void $this->assertTrue($payment->isCanceled()); } + /** + * Verify a applepay payment can be cancelled after being fully charged. + * + * @test + * + * @throws UnzerApiException + */ + public function fullCancelOnFullyChargedPayment(): void + { + $applepay = $this->createApplepayObject(); + /** @var Applepay $applepay */ + $applepay = $this->unzer->createPaymentType($applepay); + + $authorization = $applepay->authorize(100.0000, 'EUR', self::RETURN_URL, null, null, null, null, false); + $payment = $authorization->getPayment(); + + $this->assertAmounts($payment, 100.0, 0.0, 100.0, 0.0); + $this->assertTrue($payment->isPending()); + + $payment->charge(10.0); + $this->assertAmounts($payment, 90.0, 10.0, 100.0, 0.0); + $this->assertTrue($payment->isPartlyPaid()); + + $payment->charge(90.0); + $this->assertAmounts($payment, 0.0, 100.0, 100.0, 0.0); + $this->assertTrue($payment->isCompleted()); + + $cancellation = $payment->cancelAmount(); + $this->assertNotEmpty($cancellation); + $this->assertAmounts($payment, 0.0, 0.0, 100.0, 100.0); + $this->assertTrue($payment->isCanceled()); + } + + /** + * Full cancel on partly charged auth canceled charges. + * + * @test + * + * @throws UnzerApiException + */ + public function fullCancelOnPartlyPaidAuthWithCanceledCharges(): void + { + $applepay = $this->createApplepayObject(); + /** @var Applepay $applepay */ + $applepay = $this->unzer->createPaymentType($applepay); + + $authorization = $applepay->authorize(100.0000, 'EUR', self::RETURN_URL, null, null, null, null, false); + $payment = $authorization->getPayment(); + + $payment->charge(10.0); + $this->assertAmounts($payment, 90.0, 10.0, 100.0, 0.0); + + $charge = $payment->charge(10.0); + $this->assertAmounts($payment, 80.0, 20.0, 100.0, 0.0); + $this->assertTrue($payment->isPartlyPaid()); + + $charge->cancel(); + $this->assertAmounts($payment, 80.0, 10.0, 100.0, 10.0); + $this->assertTrue($payment->isPartlyPaid()); + + $payment->cancelAmount(); + $this->assertTrue($payment->isCanceled()); + } + + /** + * Verify applepay charge can be canceled. + * + * @test + * + * @throws UnzerApiException + */ + public function applepayChargeCanBeCanceled(): void + { + /** @var Applepay $applepay */ + $applepay = $this->unzer->createPaymentType($this->createApplepayObject()); + $charge = $applepay->charge(100.0, 'EUR', self::RETURN_URL, null, null, null, null, false); + + $cancel = $charge->cancel(); + $this->assertNotNull($cancel); + $this->assertNotEmpty($cancel->getId()); + } + /** * @return Applepay */ From 92d4476508ff8070f8c50077985085fc16d90104 Mon Sep 17 00:00:00 2001 From: "David.Owusu" Date: Thu, 15 Apr 2021 15:21:08 +0200 Subject: [PATCH 18/28] [change] (UMCS-181) -code style. --- src/Services/EnvironmentService.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Services/EnvironmentService.php b/src/Services/EnvironmentService.php index f505492b..4888a8d2 100755 --- a/src/Services/EnvironmentService.php +++ b/src/Services/EnvironmentService.php @@ -148,7 +148,7 @@ public static function getTestPublicKey($non3ds = false): string */ public static function getAppleMerchantCertificatePath(): string { - return stripslashes($_SERVER[self::ENV_VAR_TEST_APPLE_MERCHANT_CERTIFICATE]) ?? ''; + return stripslashes($_SERVER[self::ENV_VAR_TEST_APPLE_MERCHANT_CERTIFICATE] ?? ''); } /** @@ -158,6 +158,6 @@ public static function getAppleMerchantCertificatePath(): string */ public static function getAppleCaCertificatePath(): string { - return stripslashes($_SERVER[self::ENV_VAR_TEST_APPLE_CA_CERTIFICATE]) ?? ''; + return stripslashes($_SERVER[self::ENV_VAR_TEST_APPLE_CA_CERTIFICATE] ?? ''); } } From 4a1ff73c7275df858d11b55f4ab928122cf9a5cc Mon Sep 17 00:00:00 2001 From: "David.Owusu" Date: Thu, 15 Apr 2021 17:35:02 +0200 Subject: [PATCH 19/28] [change] (UMCS-120) - update changelog. --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 409acb52..71de63a0 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +## [1.1.2.0] +### Changed +* Card Examples: Ensure that error messages are displayed just one time. ## [1.1.1.1] From be297c654cf76154e729bd416b4e6a2a756aa193 Mon Sep 17 00:00:00 2001 From: "David.Owusu" Date: Fri, 16 Apr 2021 12:02:13 +0200 Subject: [PATCH 20/28] [change] (UMCS-181) - fix PR comments. --- src/Resources/ExternalResources/ApplepaySession.php | 11 ++++++++--- test/unit/Adapter/ApplepaySessionTest.php | 2 +- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/Resources/ExternalResources/ApplepaySession.php b/src/Resources/ExternalResources/ApplepaySession.php index d20caa57..8fb4a416 100644 --- a/src/Resources/ExternalResources/ApplepaySession.php +++ b/src/Resources/ExternalResources/ApplepaySession.php @@ -52,9 +52,9 @@ class ApplepaySession /** * ApplepaySession constructor. * - * @param string|null $merchantIdentifier - * @param string|null $displayName - * @param string|null $domainName + * @param string $merchantIdentifier + * @param string $displayName + * @param string $domainName */ public function __construct(string $merchantIdentifier, string $displayName, string $domainName) { @@ -63,6 +63,11 @@ public function __construct(string $merchantIdentifier, string $displayName, str $this->domainName = $domainName; } + /** + * Returns the json representation of this object's properties. + * + * @return false|string + */ public function jsonSerialize() { $properties = get_object_vars($this); diff --git a/test/unit/Adapter/ApplepaySessionTest.php b/test/unit/Adapter/ApplepaySessionTest.php index 0fc7e529..84dfa01a 100644 --- a/test/unit/Adapter/ApplepaySessionTest.php +++ b/test/unit/Adapter/ApplepaySessionTest.php @@ -1,6 +1,6 @@ Date: Fri, 16 Apr 2021 14:30:23 +0200 Subject: [PATCH 21/28] [change] (UMCS-145) - fix PR comments. --- test/integration/PaymentTypes/ApplepayTest.php | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/test/integration/PaymentTypes/ApplepayTest.php b/test/integration/PaymentTypes/ApplepayTest.php index bad9c4c8..aaf3d53a 100644 --- a/test/integration/PaymentTypes/ApplepayTest.php +++ b/test/integration/PaymentTypes/ApplepayTest.php @@ -77,7 +77,7 @@ public function applepayShouldBeChargeable(): void * * @test * - * @throws \UnzerSDK\Exceptions\UnzerApiException + * @throws UnzerApiException */ public function applepayCanBeAuthorized(): void { @@ -118,10 +118,6 @@ public function applepayCanPerformChargeAndCreatesPaymentObject(): void $charge = $applepay->charge(1.0, 'EUR', self::RETURN_URL, null, null, null, null, false); - // applepay recurring is activated through charge transaction - /** @var Applepay $fetchedApplepay */ - $fetchedApplepay = $this->unzer->fetchPaymentType($applepay->getId()); - // verify charge has been created $this->assertNotNull($charge->getId()); From 9b15fb506f7b5a58b2111aaf4de1c465849d32b3 Mon Sep 17 00:00:00 2001 From: "David.Owusu" Date: Fri, 16 Apr 2021 15:21:34 +0200 Subject: [PATCH 22/28] [change] (UMCS-144) - change wording of changelog. --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5974a786..d703c7a1 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) a ## [1.1.2.0] ### Added -* Payment type ApplePay. +* Introduce the payment type Applepay. ## [1.1.1.1] From 81082d76f0c3e6050986d582ca9d1f67d1c2d23c Mon Sep 17 00:00:00 2001 From: "David.Owusu" Date: Fri, 16 Apr 2021 15:39:04 +0200 Subject: [PATCH 23/28] [change] (UMCS-144) - changelog: simplify compare links --- CHANGELOG.md | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d703c7a1..5d090340 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,11 +3,11 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). -## [1.1.2.0] +## [1.1.2.0](https://github.com/unzerdev/php-sdk/compare/1.1.1.1..1.1.2.0) ### Added * Introduce the payment type Applepay. -## [1.1.1.1] +## [1.1.1.1](https://github.com/unzerdev/php-sdk/compare/1.1.1.0..1.1.1.1) ### Fix * Change debug logging of failed tests that depend on another one to work as expected. @@ -23,13 +23,13 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) a * Card example and paypage examples use a dummy customer-email to ensure they work with 3ds2. * Several minor changes. -## [1.1.1.0] +## [1.1.1.0](https://github.com/unzerdev/php-sdk/compare/1.1.0.0..1.1.1.0) ### Changed * Add email property to payment type `card` to meet 3Ds2.x regulations. * Several minor changes. -## [1.1.0.0] +## [1.1.0.0](https://github.com/unzerdev/php-sdk/compare/1260b8314af1ac461e33f0cfb382ffcd0e87c105..1.1.0.0) ### Changed * Rebranding of the SDK. @@ -59,9 +59,4 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) a * API_ERROR_AUTHORIZE_ALREADY_CANCELLED * API_ERROR_CHARGE_ALREADY_CHARGED_BACK * API_ERROR_BASKET_ITEM_IMAGE_INVALID_EXTENSION - * ENV_VAR_NAME_DISABLE_TEST_LOGGING - -[1.1.0.0]: https://github.com/unzerdev/php-sdk/compare/1260b8314af1ac461e33f0cfb382ffcd0e87c105..1.1.0.0 -[1.1.1.0]: https://github.com/unzerdev/php-sdk/compare/1.1.0.0..1.1.1.0 -[1.1.1.1]: https://github.com/unzerdev/php-sdk/compare/1.1.1.0..1.1.1.1 -[1.1.2.0]: https://github.com/unzerdev/php-sdk/compare/1.1.1.1..1.1.2.0 + * ENV_VAR_NAME_DISABLE_TEST_LOGGING \ No newline at end of file From ad5db26ffa94f50fbddff59564a27759a0d07623 Mon Sep 17 00:00:00 2001 From: "David.Owusu" Date: Mon, 19 Apr 2021 18:09:20 +0200 Subject: [PATCH 24/28] [change] (UMCS-120) - update changelog --- CHANGELOG.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 00e631e1..c840f515 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,14 +2,14 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). -## [1.1.2.0] -### Changed -* Card Examples: Ensure that error messages are displayed just one time. ## [1.1.2.0](https://github.com/unzerdev/php-sdk/compare/1.1.1.1..1.1.2.0) ### Added * Introduce the payment type Applepay. +### Changed +* Card Examples: Ensure that error messages are displayed just one time. + ## [1.1.1.1](https://github.com/unzerdev/php-sdk/compare/1.1.1.0..1.1.1.1) ### Fix From 439f9aaee0a3bdad61401055e63ba4e1ee2298c1 Mon Sep 17 00:00:00 2001 From: "David.Owusu" Date: Tue, 20 Apr 2021 15:51:14 +0200 Subject: [PATCH 25/28] [change] (UMCS-171) - update example configuration. --- CHANGELOG.md | 5 ++++- examples/_enableExamples.php | 7 ++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c840f515..d25bd14d 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,10 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) a * Introduce the payment type Applepay. ### Changed -* Card Examples: Ensure that error messages are displayed just one time. +* Examples: + * Card Examples - Ensure that error messages are displayed just one time. + * Configuration - Change default protocol to https. + * Configuration - Correct vendor name of path constant `UNZER_PAPI_FOLDER`. ## [1.1.1.1](https://github.com/unzerdev/php-sdk/compare/1.1.1.0..1.1.1.1) diff --git a/examples/_enableExamples.php b/examples/_enableExamples.php index 8abb33cb..748f594c 100755 --- a/examples/_enableExamples.php +++ b/examples/_enableExamples.php @@ -28,11 +28,12 @@ /* Set to true if you want to enable the examples */ define('UNZER_PAPI_EXAMPLES', false); -/* Please set this to your url. It must be reachable over the net*/ -define('UNZER_PAPI_URL', 'http://'.$_SERVER['HTTP_HOST']); +/* Please set this to your url. It must be reachable over the net +Webhooks will work with https only. However protocol can be changed to http if necessary. */ +define('UNZER_PAPI_URL', 'https://'.$_SERVER['HTTP_HOST']); /* Please enter the path from root directory to the example folder */ -define('UNZER_PAPI_FOLDER', '/vendor/unzer/php-sdk/examples/'); +define('UNZER_PAPI_FOLDER', '/vendor/unzerdev/php-sdk/examples/'); /* Please provide your own sandbox-keypair here. */ define('UNZER_PAPI_PRIVATE_KEY', 's-priv-***'); From 2d843633938a2bf81ce50dc65068a5e142832167 Mon Sep 17 00:00:00 2001 From: "David.Owusu" Date: Tue, 20 Apr 2021 18:02:46 +0200 Subject: [PATCH 26/28] [change] (UMCS-148) Update documentation links. --- CHANGELOG.md | 1 + README.md | 8 ++++---- examples/Bancontact/index.php | 2 +- examples/BankTransfer/index.php | 2 +- examples/Card/index.php | 2 +- examples/CardExtended/index.php | 2 +- examples/CardRecurring/index.php | 2 +- examples/EmbeddedPayPage/index.php | 2 +- examples/IDeal/index.php | 2 +- examples/InstallmentSecured/index.php | 2 +- examples/InvoiceSecured/index.php | 2 +- examples/PayPal/index.php | 2 +- examples/PayPalRecurring/index.php | 2 +- examples/Przelewy24/index.php | 2 +- examples/SepaDirectDebitSecured/index.php | 2 +- examples/Sofort/index.php | 2 +- examples/Wechatpay/index.php | 2 +- examples/index.php | 4 ++-- src/Interfaces/ResourceServiceInterface.php | 2 +- src/Unzer.php | 2 +- 20 files changed, 24 insertions(+), 23 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c840f515..97cb2305 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) a ### Changed * Card Examples: Ensure that error messages are displayed just one time. +* Update documentation links. ## [1.1.1.1](https://github.com/unzerdev/php-sdk/compare/1.1.1.0..1.1.1.1) diff --git a/README.md b/README.md index f79fa14a..a4e1a0ff 100755 --- a/README.md +++ b/README.md @@ -11,10 +11,10 @@ This SDK provides for an easy way to connect to the Unzer Rest API. Please refer to the following documentation for installation instructions and usage information. -* [API Documentation](https://docs.unzer.com/docs/introduction) -* [PHP SDK Documentation](https://docs.unzer.com/docs/php-sdk) -* [How to use the examples](https://docs.unzer.com/docs/example-implementations) -* [Debugging](https://docs.unzer.com/docs/logging-and-debugging) +* [API Documentation](https://docs.unzer.com/overview) +* [PHP SDK Documentation](https://docs.unzer.com/integrate/php-sdk) +* [How to use the examples](https://docs.unzer.com/integrate/php-sdk/example-implementations) +* [Debugging](https://docs.unzer.com/integrate/php-sdk/logging-and-debugging) ## Supported payment types * Alipay diff --git a/examples/Bancontact/index.php b/examples/Bancontact/index.php index e5f4c6bc..adaef604 100644 --- a/examples/Bancontact/index.php +++ b/examples/Bancontact/index.php @@ -45,7 +45,7 @@ -

Click here to open our test data in new tab.

+

Click here to open our test data in new tab.

diff --git a/examples/BankTransfer/index.php b/examples/BankTransfer/index.php index 63f03aa7..fe927b51 100644 --- a/examples/BankTransfer/index.php +++ b/examples/BankTransfer/index.php @@ -45,7 +45,7 @@ -

Click here to open our test data in new tab.

+

Click here to open our test data in new tab.

diff --git a/examples/Card/index.php b/examples/Card/index.php index 6db254dc..644bf090 100644 --- a/examples/Card/index.php +++ b/examples/Card/index.php @@ -60,7 +60,7 @@
  • Secret: secret3
  • -

    Click here to open our test data in new tab.

    +

    Click here to open our test data in new tab.

    diff --git a/examples/CardExtended/index.php b/examples/CardExtended/index.php index ebfbb043..c057c30e 100644 --- a/examples/CardExtended/index.php +++ b/examples/CardExtended/index.php @@ -60,7 +60,7 @@
  • Secret: secret3
  • -

    Click here to open our test data in new tab.

    +

    Click here to open our test data in new tab.

    diff --git a/examples/CardRecurring/index.php b/examples/CardRecurring/index.php index 7a371df7..3ddcce28 100644 --- a/examples/CardRecurring/index.php +++ b/examples/CardRecurring/index.php @@ -60,7 +60,7 @@
  • Secret: secret3
  • -

    Click here to open our test data in new tab.

    +

    Click here to open our test data in new tab.

    diff --git a/examples/EmbeddedPayPage/index.php b/examples/EmbeddedPayPage/index.php index 77d8d26b..eec9b940 100644 --- a/examples/EmbeddedPayPage/index.php +++ b/examples/EmbeddedPayPage/index.php @@ -48,7 +48,7 @@ -

    Click here to open our test data in new tab.

    +

    Click here to open our test data in new tab.

    diff --git a/examples/IDeal/index.php b/examples/IDeal/index.php index 5788460b..ea306c78 100644 --- a/examples/IDeal/index.php +++ b/examples/IDeal/index.php @@ -45,7 +45,7 @@ -

    Click here to open our test data in new tab.

    +

    Click here to open our test data in new tab.

    diff --git a/examples/InstallmentSecured/index.php b/examples/InstallmentSecured/index.php index dadd4408..67e15bc9 100644 --- a/examples/InstallmentSecured/index.php +++ b/examples/InstallmentSecured/index.php @@ -45,7 +45,7 @@ -

    Click here to open our test data in new tab.

    +

    Click here to open our test data in new tab.

    diff --git a/examples/InvoiceSecured/index.php b/examples/InvoiceSecured/index.php index 12bc02b3..8fb40887 100644 --- a/examples/InvoiceSecured/index.php +++ b/examples/InvoiceSecured/index.php @@ -46,7 +46,7 @@ -

    Click here to open our test data in new tab.

    +

    Click here to open our test data in new tab.

    diff --git a/examples/PayPal/index.php b/examples/PayPal/index.php index f0f44b68..0432b529 100755 --- a/examples/PayPal/index.php +++ b/examples/PayPal/index.php @@ -51,7 +51,7 @@ Attention: We recommend to create your own PayPal test account here. -

    Click here to open our test data in new tab.

    +

    Click here to open our test data in new tab.

    diff --git a/examples/PayPalRecurring/index.php b/examples/PayPalRecurring/index.php index 2f6c0082..3ac46e2a 100644 --- a/examples/PayPalRecurring/index.php +++ b/examples/PayPalRecurring/index.php @@ -51,7 +51,7 @@ Attention: We recommend to create your own PayPal test account here. -

    Click here to open our test data in new tab.

    +

    Click here to open our test data in new tab.

    diff --git a/examples/Przelewy24/index.php b/examples/Przelewy24/index.php index dae5474d..c083145c 100644 --- a/examples/Przelewy24/index.php +++ b/examples/Przelewy24/index.php @@ -45,7 +45,7 @@ -

    Click here to open our test data in new tab.

    +

    Click here to open our test data in new tab.

    diff --git a/examples/SepaDirectDebitSecured/index.php b/examples/SepaDirectDebitSecured/index.php index fbc95b41..1e93311e 100644 --- a/examples/SepaDirectDebitSecured/index.php +++ b/examples/SepaDirectDebitSecured/index.php @@ -45,7 +45,7 @@ -

    Click here to open our test data in new tab.

    +

    Click here to open our test data in new tab.

    diff --git a/examples/Sofort/index.php b/examples/Sofort/index.php index 326f9859..ae49203f 100644 --- a/examples/Sofort/index.php +++ b/examples/Sofort/index.php @@ -45,7 +45,7 @@ -

    Click here to open our test data in new tab.

    +

    Click here to open our test data in new tab.

    diff --git a/examples/Wechatpay/index.php b/examples/Wechatpay/index.php index feec9920..0b5f688c 100755 --- a/examples/Wechatpay/index.php +++ b/examples/Wechatpay/index.php @@ -50,7 +50,7 @@
  • Password: 123
  • -

    Click here to open our test data in new tab.

    +

    Click here to open our test data in new tab.

    diff --git a/examples/index.php b/examples/index.php index 1564cdcf..decc85d9 100755 --- a/examples/index.php +++ b/examples/index.php @@ -310,7 +310,7 @@ function printMessage($type, $title, $text) server and redirected to a given RedirectUrl.
    -
    +
    Documentation
    @@ -327,7 +327,7 @@ function printMessage($type, $title, $text) The Payment Page will be shown as an Overlay in your own shop.
    -
    +
    Documentation
    diff --git a/src/Interfaces/ResourceServiceInterface.php b/src/Interfaces/ResourceServiceInterface.php index 5fbf4798..551eab6c 100644 --- a/src/Interfaces/ResourceServiceInterface.php +++ b/src/Interfaces/ResourceServiceInterface.php @@ -171,7 +171,7 @@ public function updateBasket(Basket $basket): Basket; /** * Creates a PaymentType resource from the given PaymentType object. * This is used to create the payment object prior to any transaction. - * Usually this will be done by the unzerUI components (https://docs.unzer.com/docs/web-integration#step-3-create-your-payment-method) + * Usually this will be done by the unzerUI components (https://docs.unzer.com/integrate/web-integration/#step-3-create-your-payment-method) * * @param BasePaymentType $paymentType * diff --git a/src/Unzer.php b/src/Unzer.php index 1b0594fc..bd9a1be6 100644 --- a/src/Unzer.php +++ b/src/Unzer.php @@ -98,7 +98,7 @@ class Unzer implements UnzerParentInterface, PaymentServiceInterface, ResourceSe * @param string $key The private key your received from your Unzer contact person. * @param string $locale The locale of the customer defining defining the translation (e.g. 'en-GB' or 'de-DE'). * - * @link https://docs.unzer.com/docs/web-integration#section-localization-and-languages + * @link https://docs.unzer.com/integrate/web-integration/#section-localization-and-languages * * @throws RuntimeException A RuntimeException will be thrown if the key is not of type private. */ From e42a42b0d54ea9a56404afc59720f736567972f6 Mon Sep 17 00:00:00 2001 From: "David.Owusu" Date: Tue, 27 Apr 2021 16:45:22 +0200 Subject: [PATCH 27/28] [change] (UMCS-181) Refactor applepay adapter to set certificates/key separately. --- src/Adapter/ApplepayAdapter.php | 40 +++++++++++++----------- test/integration/ApplepayAdapterTest.php | 17 +++++----- 2 files changed, 29 insertions(+), 28 deletions(-) diff --git a/src/Adapter/ApplepayAdapter.php b/src/Adapter/ApplepayAdapter.php index cd33937a..72e496ba 100644 --- a/src/Adapter/ApplepayAdapter.php +++ b/src/Adapter/ApplepayAdapter.php @@ -36,10 +36,8 @@ class ApplepayAdapter private $request; /** - * @param string $merchantValidationURL URL for merchant validation request - * @param ApplepaySession $applePaySession Containing applepay session data. - * @param string $merchantValidationCertificatePath Path to merchant identification certificate - * @param string|null $merchantValidationCertificateKeyChainPath + * @param string $merchantValidationURL URL for merchant validation request + * @param ApplepaySession $applePaySession Containing applepay session data. * * @return string|null * @@ -47,20 +45,18 @@ class ApplepayAdapter */ public function validateApplePayMerchant( string $merchantValidationURL, - ApplepaySession $applePaySession, - string $merchantValidationCertificatePath, - ?string $merchantValidationCertificateKeyChainPath = null + ApplepaySession $applePaySession ): ?string { if (!$this->validMerchantValidationDomain($merchantValidationURL)) { - throw new ApplepayMerchantValidationException('Invalid URL used for merchantValidation request.'); + throw new ApplepayMerchantValidationException("Invalid URL used for merchantValidation request."); + } + if ($this->request === null) { + throw new ApplepayMerchantValidationException('No curl adapter initiated yet. Make sure to cal init() function before.'); } $payload = $applePaySession->jsonSerialize(); - $this->init( - $merchantValidationURL, - $payload, - $merchantValidationCertificatePath, - $merchantValidationCertificateKeyChainPath - ); + $this->setOption(CURLOPT_URL, $merchantValidationURL); + $this->setOption(CURLOPT_POSTFIELDS, $payload); + $sessionResponse = $this->execute(); $this->close(); return $sessionResponse; @@ -71,6 +67,7 @@ public function validateApplePayMerchant( * * @param string $merchantValidationURL URL used for merchant validation request. * + * @return bool */ public function validMerchantValidationDomain(string $merchantValidationURL): bool { @@ -81,18 +78,20 @@ public function validMerchantValidationDomain(string $merchantValidationURL): bo } /** - * {@inheritDoc} + * @param string $sslCert Path to merchant identification certificate. + * @param string|null $sslKey Path to merchant identification key file. + * This is necessary if the ssl cert file doesn't contain key already. + * @param string|null $caCert Path to CA certificate. */ - public function init($url, $payload, $sslCert, $caCert = null): void + public function init(string $sslCert, string $sslKey = null, string $caCert = null): void { $timeout = EnvironmentService::getTimeout(); $curlVerbose = EnvironmentService::isCurlVerbose(); - $this->request = curl_init($url); + $this->request = curl_init(); $this->setOption(CURLOPT_HTTPHEADER, ['Content-Type: application/json']); $this->setOption(CURLOPT_POST, 1); $this->setOption(CURLOPT_DNS_USE_GLOBAL_CACHE, false); - $this->setOption(CURLOPT_POSTFIELDS, $payload); $this->setOption(CURLOPT_FAILONERROR, false); $this->setOption(CURLOPT_TIMEOUT, $timeout); $this->setOption(CURLOPT_CONNECTTIMEOUT, $timeout); @@ -104,7 +103,10 @@ public function init($url, $payload, $sslCert, $caCert = null): void $this->setOption(CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2); $this->setOption(CURLOPT_SSLCERT, $sslCert); - if (isset($caCert)) { + if (isset($sslKey) && !empty($sslKey)) { + $this->setOption(CURLOPT_SSLKEY, $sslKey); + } + if (isset($caCert) && !empty($caCert)) { $this->setOption(CURLOPT_CAINFO, $caCert); } } diff --git a/test/integration/ApplepayAdapterTest.php b/test/integration/ApplepayAdapterTest.php index 98f002b7..011db0d0 100644 --- a/test/integration/ApplepayAdapterTest.php +++ b/test/integration/ApplepayAdapterTest.php @@ -64,18 +64,17 @@ protected function setUp(): void * * @test * - * @throws \UnzerSDK\Exceptions\ApplepayMerchantValidationException + * @throws ApplepayMerchantValidationException */ public function verifyMerchantValidationRequest(): void { $applepaySession = $this->createApplepaySession(); $appleAdapter = new ApplepayAdapter(); + $appleAdapter->init($this->merchantValidationCertificatePath, null, $this->appleCaCertificatePath); $validationResponse = $appleAdapter->validateApplePayMerchant( $this->merchantValidationUrl, - $applepaySession, - $this->merchantValidationCertificatePath, - $this->appleCaCertificatePath + $applepaySession ); $this->assertNotNull($validationResponse); @@ -86,17 +85,17 @@ public function verifyMerchantValidationRequest(): void * * @test * - * @throws \UnzerSDK\Exceptions\ApplepayMerchantValidationException + * @throws ApplepayMerchantValidationException */ public function merchantValidationWorksWithoutCaCert(): void { $applepaySession = $this->createApplepaySession(); $appleAdapter = new ApplepayAdapter(); + $appleAdapter->init($this->merchantValidationCertificatePath); $validationResponse = $appleAdapter->validateApplePayMerchant( $this->merchantValidationUrl, - $applepaySession, - $this->merchantValidationCertificatePath + $applepaySession ); $this->assertNotNull($validationResponse); @@ -112,14 +111,14 @@ public function merchantValidationThrowsErrorForInvalidDomain(): void { $applepaySession = $this->createApplepaySession(); $appleAdapter = new ApplepayAdapter(); + $appleAdapter->init($this->merchantValidationCertificatePath); $this->expectException(ApplepayMerchantValidationException::class); $this->expectExceptionMessage('Invalid URL used for merchantValidation request.'); $appleAdapter->validateApplePayMerchant( 'https://invalid.domain.com/some/path', - $applepaySession, - $this->merchantValidationCertificatePath + $applepaySession ); } From 74ede46ebb98d81c7d77cac23555cbc83a797e90 Mon Sep 17 00:00:00 2001 From: "David.Owusu" Date: Wed, 28 Apr 2021 10:34:13 +0200 Subject: [PATCH 28/28] [change] (UMCS-181) Refactor applepay adapter: Add test for merchant id key file. --- src/Services/EnvironmentService.php | 8 +- test/integration/ApplepayAdapterTest.php | 125 ++++++++++++++++++----- 2 files changed, 105 insertions(+), 28 deletions(-) diff --git a/src/Services/EnvironmentService.php b/src/Services/EnvironmentService.php index 4888a8d2..ac1ea74b 100755 --- a/src/Services/EnvironmentService.php +++ b/src/Services/EnvironmentService.php @@ -41,7 +41,7 @@ class EnvironmentService public const ENV_VAR_TEST_PRIVATE_KEY_NON_3DS = 'UNZER_PAPI_TEST_PRIVATE_KEY_NON_3DS'; public const ENV_VAR_TEST_PUBLIC_KEY_NON_3DS = 'UNZER_PAPI_TEST_PUBLIC_KEY_NON_3DS'; - public const ENV_VAR_TEST_APPLE_MERCHANT_CERTIFICATE = 'UNZER_APPLE_MERCHANT_CERTIFICATE_PATH'; + public const ENV_VAR_TEST_APPLE_MERCHANT_ID_FOLDER = 'UNZER_APPLE_MERCHANT_ID_PATH'; public const ENV_VAR_TEST_APPLE_CA_CERTIFICATE = 'UNZER_APPLE_CA_CERTIFICATE_PATH'; private const ENV_VAR_NAME_TIMEOUT = 'UNZER_PAPI_TIMEOUT'; @@ -142,13 +142,13 @@ public static function getTestPublicKey($non3ds = false): string } /** - * Returns the apple merchant certificate path set via environment variable. + * Returns the path to apple merchant ID folder set via environment variable. * * @return string */ - public static function getAppleMerchantCertificatePath(): string + public static function getAppleMerchantIdPath(): string { - return stripslashes($_SERVER[self::ENV_VAR_TEST_APPLE_MERCHANT_CERTIFICATE] ?? ''); + return stripslashes($_SERVER[self::ENV_VAR_TEST_APPLE_MERCHANT_ID_FOLDER] ?? ''); } /** diff --git a/test/integration/ApplepayAdapterTest.php b/test/integration/ApplepayAdapterTest.php index 011db0d0..323f3bab 100644 --- a/test/integration/ApplepayAdapterTest.php +++ b/test/integration/ApplepayAdapterTest.php @@ -34,20 +34,16 @@ class ApplepayAdapterTest extends BaseIntegrationTest { + /** @var string $appleCaCertificatePath Path to ca Certificate file. */ + protected $appleCaCertificatePath; + /** @var string $merchantValidationUrl merchant validation url for testing. */ private $merchantValidationUrl; - private $merchantValidationCertificatePath; - private $appleCaCertificatePath; - - public function domainShouldBeValidatedCorrectlyDP() - { - return [ - 'invalid: example.domain.com' => ['https://example.domain.com', false], - 'valid: https://apple-pay-gateway.apple.com/some/path' => ['https://apple-pay-gateway.apple.com/some/path', true], - 'valid: https://cn-apple-pay-gateway.apple.com' => ['https://cn-apple-pay-gateway.apple.com', true], - 'invalid: apple-pay-gateway-nc-pod1.apple.com' => ['apple-pay-gateway-nc-pod1.apple.com', false], - 'invalid: (empty)' => ['', false], - ]; - } + /** @var string $applepayCombinedCertPath Path to combined certificate file. */ + private $applepayCombinedCertPath; + /** @var string $applepayCertPath Path to merchant ID certificate file. */ + private $applepayCertPath; + /** @var string $applepayKeyPath Path to merchant ID key file. */ + private $applepayKeyPath; /** * @inheritDoc @@ -55,12 +51,16 @@ public function domainShouldBeValidatedCorrectlyDP() protected function setUp(): void { $this->merchantValidationUrl = 'https://apple-pay-gateway-cert.apple.com/paymentservices/startSession'; - $this->merchantValidationCertificatePath = EnvironmentService::getAppleMerchantCertificatePath(); + + $appleMerchantIdPath = EnvironmentService::getAppleMerchantIdPath(); + $this->applepayCertPath = $appleMerchantIdPath . 'merchant_id.pem'; + $this->applepayKeyPath = $appleMerchantIdPath . 'merchant_id.key'; + $this->applepayCombinedCertPath = $appleMerchantIdPath . 'apple-pay-cert.pem'; $this->appleCaCertificatePath = EnvironmentService::getAppleCaCertificatePath(); } /** - * test merchant validation request. + * Test merchant validation request. * * @test * @@ -70,7 +70,7 @@ public function verifyMerchantValidationRequest(): void { $applepaySession = $this->createApplepaySession(); $appleAdapter = new ApplepayAdapter(); - $appleAdapter->init($this->merchantValidationCertificatePath, null, $this->appleCaCertificatePath); + $appleAdapter->init($this->applepayCertPath, $this->applepayKeyPath, $this->appleCaCertificatePath); $validationResponse = $appleAdapter->validateApplePayMerchant( $this->merchantValidationUrl, @@ -81,17 +81,25 @@ public function verifyMerchantValidationRequest(): void } /** - * test merchant validation request without ca certificate. + * @return ApplepaySession + */ + private function createApplepaySession(): ApplepaySession + { + return new ApplepaySession('merchantIdentifier', 'displayName', 'domainName'); + } + + /** + * Test merchant validation request without ca certificate. * * @test * * @throws ApplepayMerchantValidationException */ - public function merchantValidationWorksWithoutCaCert(): void + public function merchantValidationWorksWithApplepayCertOnly(): void { $applepaySession = $this->createApplepaySession(); $appleAdapter = new ApplepayAdapter(); - $appleAdapter->init($this->merchantValidationCertificatePath); + $appleAdapter->init($this->applepayCombinedCertPath); $validationResponse = $appleAdapter->validateApplePayMerchant( $this->merchantValidationUrl, @@ -101,6 +109,68 @@ public function merchantValidationWorksWithoutCaCert(): void $this->assertNotNull($validationResponse); } + /** + * Test merchant validation request without ca certificate. + * + * @test + * + * @throws ApplepayMerchantValidationException + */ + public function merchantValidationWorksWithCertAndKey(): void + { + $applepaySession = $this->createApplepaySession(); + $appleAdapter = new ApplepayAdapter(); + $appleAdapter->init($this->applepayCertPath, $this->applepayKeyPath); + + $validationResponse = $appleAdapter->validateApplePayMerchant( + $this->merchantValidationUrl, + $applepaySession + ); + + $this->assertNotNull($validationResponse); + } + + /** + * Test merchant validation request without key and only the merchant id certificate should throw exception. + * + * @test + * + * @throws ApplepayMerchantValidationException + */ + public function missingKeyShouldThrowException(): void + { + $applepaySession = $this->createApplepaySession(); + $appleAdapter = new ApplepayAdapter(); + $appleAdapter->init($this->applepayCertPath); + + $this->expectException(ApplepayMerchantValidationException::class); + $appleAdapter->validateApplePayMerchant( + $this->merchantValidationUrl, + $applepaySession + ); + } + + /** + * Test merchant validation request without init() call should throw exception. + * + * @test + * + * @throws ApplepayMerchantValidationException + */ + public function missingInitCallThrowsException(): void + { + $applepaySession = $this->createApplepaySession(); + $appleAdapter = new ApplepayAdapter(); + + $this->expectException(ApplepayMerchantValidationException::class); + $this->expectExceptionMessage('No curl adapter initiated yet. Make sure to cal init() function before.'); + + $appleAdapter->validateApplePayMerchant( + $this->merchantValidationUrl, + $applepaySession + ); + } + /** * Merchant validation call should throw Exception if domain of Validation url is invalid. * @@ -111,7 +181,7 @@ public function merchantValidationThrowsErrorForInvalidDomain(): void { $applepaySession = $this->createApplepaySession(); $appleAdapter = new ApplepayAdapter(); - $appleAdapter->init($this->merchantValidationCertificatePath); + $appleAdapter->init($this->applepayCombinedCertPath); $this->expectException(ApplepayMerchantValidationException::class); $this->expectExceptionMessage('Invalid URL used for merchantValidation request.'); @@ -140,11 +210,18 @@ public function domainShouldBeValidatedCorrectly($validationUrl, $expectedResult $this->assertEquals($expectedResult, $domainValidation); } - /** - * @return ApplepaySession + /** Provides different urls to test domain validation. + * + * @return array[] */ - private function createApplepaySession(): ApplepaySession + public function domainShouldBeValidatedCorrectlyDP(): array { - return new ApplepaySession('merchantIdentifier', 'displayName', 'domainName'); + return [ + 'invalid: example.domain.com' => ['https://example.domain.com', false], + 'valid: https://apple-pay-gateway.apple.com/some/path' => ['https://apple-pay-gateway.apple.com/some/path', true], + 'valid: https://cn-apple-pay-gateway.apple.com' => ['https://cn-apple-pay-gateway.apple.com', true], + 'invalid: apple-pay-gateway-nc-pod1.apple.com' => ['apple-pay-gateway-nc-pod1.apple.com', false], + 'invalid: (empty)' => ['', false], + ]; } }