diff --git a/CHANGELOG.md b/CHANGELOG.md index 1b98bbf1..28da3b5c 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,30 @@ 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.2.3.0][1.2.3.0] + +### Added +* An example for `prepayment` payment method. +* An example for `invoice` payment method. +* Charge methods `getCancelledAmount` and `getTotalAmount`. +* Authorize method `getCancelledAmount`. +* Detailed `keypair` fetch. +* Added properties to keypair resource. + +### Fixed +* A problem with HeidelpayApiException. +* A problem which resulted in an error when trying to create a `customer` implicitly with a transaction when its `customerId` was set. + +### Changed +* Replaced unreliable `Payment::cancel()` method with `Payment::cancelAmount()` which takes multiple cancellation scenarios into account. +* Replaced `ApiResponseCodes::API_ERROR_AUTHORIZE_ALREADY_CANCELLED` with `ApiResponseCodes::API_ERROR_ALREADY_CANCELLED`. +* Replaced `ApiResponseCodes::API_ERROR_CHARGE_ALREADY_CHARGED_BACK` with `ApiResponseCodes::API_ERROR_ALREADY_CHARGED_BACK`. +* Add deprecation notice for `Payment::cancelAllCharges` and `Payment::cancelAuthorization` +* Adapted integration tests with basket to changes in API. +* Refactor deprecation notices. +* Refactored and extended unit tests. +* Test keypair can now be set via environment variables. + ## [1.2.2.0][1.2.2.0] ### Fixed @@ -274,3 +298,4 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) a [1.2.0.0]: https://github.com/heidelpay/heidelpayPHP/compare/1.1.6.0..1.2.0.0 [1.2.1.0]: https://github.com/heidelpay/heidelpayPHP/compare/1.2.0.0..1.2.1.0 [1.2.2.0]: https://github.com/heidelpay/heidelpayPHP/compare/1.2.1.0..1.2.2.0 +[1.2.3.0]: https://github.com/heidelpay/heidelpayPHP/compare/1.2.2.0..1.2.3.0 diff --git a/examples/Invoice/Constants.php b/examples/Invoice/Constants.php new file mode 100644 index 00000000..82c782a9 --- /dev/null +++ b/examples/Invoice/Constants.php @@ -0,0 +1,30 @@ +<?php +/** + * This file defines the constants needed for the Invoice example. + * + * Copyright (C) 2019 heidelpay GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @link https://docs.heidelpay.com/ + * + * @author Simon Gabriel <development@heidelpay.com> + * + * @package heidelpayPHP/examples + */ + +require_once __DIR__ . '/../Constants.php'; + +define('EXAMPLE_PATH', __DIR__); +define('EXAMPLE_URL', EXAMPLE_BASE_FOLDER . 'Invoice'); +define('CONTROLLER_URL', EXAMPLE_URL . '/Controller.php'); diff --git a/examples/Invoice/Controller.php b/examples/Invoice/Controller.php new file mode 100644 index 00000000..8a2aaafe --- /dev/null +++ b/examples/Invoice/Controller.php @@ -0,0 +1,98 @@ +<?php +/** + * This is the controller for the Invoice example. + * It is called when the pay button on the index page is clicked. + * + * Copyright (C) 2019 heidelpay GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @link https://docs.heidelpay.com/ + * + * @author Simon Gabriel <development@heidelpay.com> + * + * @package heidelpayPHP/examples + */ + +/** Require the constants of this example */ +require_once __DIR__ . '/Constants.php'; + +/** @noinspection PhpIncludeInspection */ +/** Require the composer autoloader file */ +require_once __DIR__ . '/../../../../autoload.php'; + +use heidelpayPHP\examples\ExampleDebugHandler; +use heidelpayPHP\Exceptions\HeidelpayApiException; +use heidelpayPHP\Heidelpay; +use heidelpayPHP\Resources\CustomerFactory; +use heidelpayPHP\Resources\PaymentTypes\Invoice; + +session_start(); +session_unset(); + +$clientMessage = 'Something went wrong. Please try again later.'; +$merchantMessage = 'Something went wrong. Please try again later.'; + +function redirect($url, $merchantMessage = '', $clientMessage = '') +{ + $_SESSION['merchantMessage'] = $merchantMessage; + $_SESSION['clientMessage'] = $clientMessage; + header('Location: ' . $url); + die(); +} + +// Catch API errors, write the message to your log and show the ClientMessage to the client. +try { + // Create a heidelpay object using your private key and register a debug handler if you want to. + $heidelpay = new Heidelpay(HEIDELPAY_PHP_PAYMENT_API_PRIVATE_KEY); + $heidelpay->setDebugMode(true)->setDebugHandler(new ExampleDebugHandler()); + + /** @var Invoice $invoice */ + $invoice = $heidelpay->createPaymentType(new Invoice()); + + $customer = CustomerFactory::createCustomer('Max', 'Mustermann'); + $orderId = str_replace(['0.', ' '], '', microtime(false)); + + $transaction = $invoice->charge(12.99, 'EUR', CONTROLLER_URL, $customer, $orderId); + + // You'll need to remember the shortId to show it on the success or failure page + $_SESSION['ShortId'] = $transaction->getShortId(); + + // Redirect to the success or failure page depending on the state of the transaction + $payment = $transaction->getPayment(); + + if ($payment->isPending()) { + // In case of authorization this is normal since you will later charge the payment. + // You can create the order with status pending payment and show a success page to the customer if you want. + + // In cases of redirection to an external service (e.g. 3D secure, PayPal, etc) it sometimes takes time for + // the payment to update it's status. In this case it might be pending at first and change to cancel or success later. + // Use the webhooks feature to stay informed about changes of the payment (e.g. cancel, success) + // then you can cancel the order later or mark it paid as soon as the event is triggered. + + // In any case, the payment is not done when the payment is pending and you should ship until it changes to success. + redirect(PENDING_URL); + } + // If the payment is neither success nor pending something went wrong. + // In this case do not create the order. + // Redirect to an error page in your shop and show an message if you want. + + // Check the result message of the transaction to find out what went wrong. + $merchantMessage = $transaction->getMessage()->getCustomer(); +} catch (HeidelpayApiException $e) { + $merchantMessage = $e->getMerchantMessage(); + $clientMessage = $e->getClientMessage(); +} catch (RuntimeException $e) { + $merchantMessage = $e->getMessage(); +} +redirect(FAILURE_URL, $merchantMessage, $clientMessage); diff --git a/examples/Invoice/index.php b/examples/Invoice/index.php new file mode 100644 index 00000000..d3c1a749 --- /dev/null +++ b/examples/Invoice/index.php @@ -0,0 +1,55 @@ +<?php +/** + * This file provides an example implementation of the Invoice payment type. + * + * Copyright (C) 2019 heidelpay GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @link https://docs.heidelpay.com/ + * + * @author Simon Gabriel <development@heidelpay.com> + * + * @package heidelpayPHP/examples + */ + +/** Require the constants of this example */ +require_once __DIR__ . '/Constants.php'; + +/** @noinspection PhpIncludeInspection */ + +/** Require the composer autoloader file */ +require_once __DIR__ . '/../../../../autoload.php'; +?> + +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <title> + Heidelpay UI Examples + </title> + <script src="https://code.jquery.com/jquery-3.1.1.min.js" + integrity="sha256-hVVnYaiADRTO2PzUGmuLJr8BLUSjGIZsDYGmIJLv2b8=" crossorigin="anonymous"></script> + + <link rel="stylesheet" href="https://static.heidelpay.com/v1/heidelpay.css" /> +</head> + +<body style="margin: 70px 70px 0;"> + +<form id="payment-form" action="<?php echo CONTROLLER_URL; ?>" class="heidelpayUI form" novalidate> + <button class="heidelpayUI primary button fluid" id="submit-button" type="submit">Pay</button> +</form> + +</body> +</html> diff --git a/examples/Prepayment/Constants.php b/examples/Prepayment/Constants.php new file mode 100644 index 00000000..917dff7e --- /dev/null +++ b/examples/Prepayment/Constants.php @@ -0,0 +1,30 @@ +<?php +/** + * This file defines the constants needed for the Prepayment example. + * + * Copyright (C) 2019 heidelpay GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @link https://docs.heidelpay.com/ + * + * @author Simon Gabriel <development@heidelpay.com> + * + * @package heidelpayPHP/examples + */ + +require_once __DIR__ . '/../Constants.php'; + +define('EXAMPLE_PATH', __DIR__); +define('EXAMPLE_URL', EXAMPLE_BASE_FOLDER . 'Prepayment'); +define('CONTROLLER_URL', EXAMPLE_URL . '/Controller.php'); diff --git a/examples/Prepayment/Controller.php b/examples/Prepayment/Controller.php new file mode 100644 index 00000000..b33ad4d4 --- /dev/null +++ b/examples/Prepayment/Controller.php @@ -0,0 +1,98 @@ +<?php +/** + * This is the controller for the Prepayment example. + * It is called when the pay button on the index page is clicked. + * + * Copyright (C) 2019 heidelpay GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @link https://docs.heidelpay.com/ + * + * @author Simon Gabriel <development@heidelpay.com> + * + * @package heidelpayPHP/examples + */ + +/** Require the constants of this example */ +require_once __DIR__ . '/Constants.php'; + +/** @noinspection PhpIncludeInspection */ +/** Require the composer autoloader file */ +require_once __DIR__ . '/../../../../autoload.php'; + +use heidelpayPHP\examples\ExampleDebugHandler; +use heidelpayPHP\Exceptions\HeidelpayApiException; +use heidelpayPHP\Heidelpay; +use heidelpayPHP\Resources\CustomerFactory; +use heidelpayPHP\Resources\PaymentTypes\Prepayment; + +session_start(); +session_unset(); + +$clientMessage = 'Something went wrong. Please try again later.'; +$merchantMessage = 'Something went wrong. Please try again later.'; + +function redirect($url, $merchantMessage = '', $clientMessage = '') +{ + $_SESSION['merchantMessage'] = $merchantMessage; + $_SESSION['clientMessage'] = $clientMessage; + header('Location: ' . $url); + die(); +} + +// Catch API errors, write the message to your log and show the ClientMessage to the client. +try { + // Create a heidelpay object using your private key and register a debug handler if you want to. + $heidelpay = new Heidelpay(HEIDELPAY_PHP_PAYMENT_API_PRIVATE_KEY); + $heidelpay->setDebugMode(true)->setDebugHandler(new ExampleDebugHandler()); + + /** @var Prepayment $prepayment */ + $prepayment = $heidelpay->createPaymentType(new Prepayment()); + + $customer = CustomerFactory::createCustomer('Max', 'Mustermann'); + $orderId = str_replace(['0.', ' '], '', microtime(false)); + + $transaction = $prepayment->charge(12.99, 'EUR', CONTROLLER_URL, $customer, $orderId); + + // You'll need to remember the shortId to show it on the success or failure page + $_SESSION['ShortId'] = $transaction->getShortId(); + + // Redirect to the success or failure page depending on the state of the transaction + $payment = $transaction->getPayment(); + + if ($payment->isPending()) { + // In case of authorization this is normal since you will later charge the payment. + // You can create the order with status pending payment and show a success page to the customer if you want. + + // In cases of redirection to an external service (e.g. 3D secure, PayPal, etc) it sometimes takes time for + // the payment to update it's status. In this case it might be pending at first and change to cancel or success later. + // Use the webhooks feature to stay informed about changes of the payment (e.g. cancel, success) + // then you can cancel the order later or mark it paid as soon as the event is triggered. + + // In any case, the payment is not done when the payment is pending and you should ship until it changes to success. + redirect(PENDING_URL); + } + // If the payment is neither success nor pending something went wrong. + // In this case do not create the order. + // Redirect to an error page in your shop and show an message if you want. + + // Check the result message of the transaction to find out what went wrong. + $merchantMessage = $transaction->getMessage()->getCustomer(); +} catch (HeidelpayApiException $e) { + $merchantMessage = $e->getMerchantMessage(); + $clientMessage = $e->getClientMessage(); +} catch (RuntimeException $e) { + $merchantMessage = $e->getMessage(); +} +redirect(FAILURE_URL, $merchantMessage, $clientMessage); diff --git a/examples/Prepayment/index.php b/examples/Prepayment/index.php new file mode 100644 index 00000000..dedf71df --- /dev/null +++ b/examples/Prepayment/index.php @@ -0,0 +1,55 @@ +<?php +/** + * This file provides an example implementation of the Prepayment payment type. + * + * Copyright (C) 2019 heidelpay GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @link https://docs.heidelpay.com/ + * + * @author Simon Gabriel <development@heidelpay.com> + * + * @package heidelpayPHP/examples + */ + +/** Require the constants of this example */ +require_once __DIR__ . '/Constants.php'; + +/** @noinspection PhpIncludeInspection */ + +/** Require the composer autoloader file */ +require_once __DIR__ . '/../../../../autoload.php'; +?> + +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <title> + Heidelpay UI Examples + </title> + <script src="https://code.jquery.com/jquery-3.1.1.min.js" + integrity="sha256-hVVnYaiADRTO2PzUGmuLJr8BLUSjGIZsDYGmIJLv2b8=" crossorigin="anonymous"></script> + + <link rel="stylesheet" href="https://static.heidelpay.com/v1/heidelpay.css" /> +</head> + +<body style="margin: 70px 70px 0;"> + +<form id="payment-form" action="<?php echo CONTROLLER_URL; ?>" class="heidelpayUI form" novalidate> + <button class="heidelpayUI primary button fluid" id="submit-button" type="submit">Pay</button> +</form> + +</body> +</html> diff --git a/examples/_enableExamples.php b/examples/_enableExamples.php index 44337ee5..03980683 100755 --- a/examples/_enableExamples.php +++ b/examples/_enableExamples.php @@ -39,8 +39,6 @@ * Default Values are: * Private key: s-priv-2a102ZMq3gV4I3zJ888J7RR6u75oqK3n * Public key: s-pub-2a10ifVINFAjpQJ9qW8jBe5OJPBx6Gxa - * - * Please keep in mind, */ define('HEIDELPAY_PHP_PAYMENT_API_PRIVATE_KEY', 's-priv-2a102ZMq3gV4I3zJ888J7RR6u75oqK3n'); define('HEIDELPAY_PHP_PAYMENT_API_PUBLIC_KEY', 's-pub-2a10ifVINFAjpQJ9qW8jBe5OJPBx6Gxa'); diff --git a/examples/index.php b/examples/index.php index 6cf810b0..3b6cfd4c 100755 --- a/examples/index.php +++ b/examples/index.php @@ -140,6 +140,30 @@ Try </div> </div> + <div class="card olive"> + <div class="content"> + <div class="header"> + Prepayment + </div> + <div class="description"> + </div> + </div> + <div class="ui bottom attached green button" onclick="location.href='Prepayment/';"> + Try + </div> + </div> + <div class="card olive"> + <div class="content"> + <div class="header"> + Invoice + </div> + <div class="description"> + </div> + </div> + <div class="ui bottom attached green button" onclick="location.href='Invoice/';"> + Try + </div> + </div> <div class="card olive"> <div class="content"> <div class="header"> @@ -221,7 +245,7 @@ <div class="card olive"> <div class="content"> <div class="header"> - Flexipay (PIS) + Flexipay direct (PIS) </div> <div class="description"> </div> diff --git a/src/Constants/ApiResponseCodes.php b/src/Constants/ApiResponseCodes.php index 8a128895..5ec64324 100755 --- a/src/Constants/ApiResponseCodes.php +++ b/src/Constants/ApiResponseCodes.php @@ -44,8 +44,18 @@ class ApiResponseCodes const API_ERROR_IVF_REQUIRES_BASKET = 'API.330.100.023'; const API_ERROR_ADDRESSES_DO_NOT_MATCH = 'API.330.100.106'; const API_ERROR_CURRENCY_IS_NOT_SUPPORTED = 'API.330.100.202'; + /** + * @deprecated since 1.2.3.0 + * @see ApiResponseCodes::API_ERROR_ALREADY_CANCELLED + */ const API_ERROR_AUTHORIZE_ALREADY_CANCELLED = 'API.340.100.014'; + const API_ERROR_ALREADY_CANCELLED = 'API.340.100.014'; + /** + * @deprecated since 1.2.3.0 + * @see ApiResponseCodes::API_ERROR_ALREADY_CHARGED_BACK + */ const API_ERROR_CHARGE_ALREADY_CHARGED_BACK = 'API.340.100.015'; + const API_ERROR_ALREADY_CHARGED_BACK = 'API.340.100.015'; const API_ERROR_ALREADY_CHARGED = 'API.340.100.018'; const API_ERROR_CANCEL_REASON_CODE_IS_MISSING = 'API.340.100.024'; const API_ERROR_AMOUNT_IS_MISSING = 'API.340.200.130'; diff --git a/src/Exceptions/HeidelpayApiException.php b/src/Exceptions/HeidelpayApiException.php index 38f6e07b..9eb52118 100755 --- a/src/Exceptions/HeidelpayApiException.php +++ b/src/Exceptions/HeidelpayApiException.php @@ -45,13 +45,13 @@ class HeidelpayApiException extends Exception * @param string $code * @param string $errorId */ - public function __construct($merchantMessage = '', $clientMessage = '', $code = 'No error code provided', $errorId = 'No error id provided') + public function __construct($merchantMessage = '', $clientMessage = '', $code = null, $errorId = null) { $merchantMessage = empty($merchantMessage) ? static::MESSAGE : $merchantMessage; $this->clientMessage = empty($clientMessage) ? static::CLIENT_MESSAGE : $clientMessage; parent::__construct($merchantMessage); - $this->code = $code; - $this->errorId = $errorId; + $this->code = empty($code) ? 'No error code provided' : $code; + $this->errorId = empty($errorId) ? 'No error id provided' : $errorId; } /** diff --git a/src/Heidelpay.php b/src/Heidelpay.php index 7ba4c7fa..e563f33c 100755 --- a/src/Heidelpay.php +++ b/src/Heidelpay.php @@ -56,7 +56,7 @@ class Heidelpay implements HeidelpayParentInterface const BASE_URL = 'api.heidelpay.com'; const API_VERSION = 'v1'; const SDK_TYPE = 'HeidelpayPHP'; - const SDK_VERSION = '1.2.2.0'; + const SDK_VERSION = '1.2.3.0'; /** @var string $key */ private $key; @@ -396,14 +396,16 @@ public function fetchPaymentByOrderId($orderId): Payment /** * Read and return the public key and configured payment types from API. * + * @param bool $detailed If this flag is set detailed information are fetched. + * * @return Keypair The Keypair object composed of the data returned by the API. * * @throws HeidelpayApiException A HeidelpayApiException is thrown if there is an error returned on API-request. * @throws RuntimeException A RuntimeException is thrown when there is a error while using the SDK. */ - public function fetchKeypair(): AbstractHeidelpayResource + public function fetchKeypair($detailed = false): AbstractHeidelpayResource { - return $this->resourceService->fetchKeypair(); + return $this->resourceService->fetchKeypair($detailed); } //</editor-fold> diff --git a/src/Resources/AbstractHeidelpayResource.php b/src/Resources/AbstractHeidelpayResource.php index 909b6987..1a743056 100755 --- a/src/Resources/AbstractHeidelpayResource.php +++ b/src/Resources/AbstractHeidelpayResource.php @@ -24,7 +24,6 @@ */ namespace heidelpayPHP\Resources; -use function count; use DateTime; use heidelpayPHP\Adapter\HttpAdapterInterface; use heidelpayPHP\Exceptions\HeidelpayApiException; @@ -32,13 +31,14 @@ use heidelpayPHP\Interfaces\HeidelpayParentInterface; use heidelpayPHP\Services\ResourceNameService; use heidelpayPHP\Services\ResourceService; -use function is_array; -use function is_callable; -use function is_object; use ReflectionException; use ReflectionProperty; use RuntimeException; use stdClass; +use function count; +use function is_array; +use function is_callable; +use function is_object; abstract class AbstractHeidelpayResource implements HeidelpayParentInterface { @@ -54,15 +54,13 @@ abstract class AbstractHeidelpayResource implements HeidelpayParentInterface //<editor-fold desc="Getters/Setters"> /** - * {@inheritDoc} + * Returns the id of this resource. + * + * @return string|null */ public function getId() { - $resourceId = $this->id; - if ($resourceId === null) { - $resourceId = $this->getExternalId(); - } - return $resourceId; + return $this->id; } /** @@ -145,8 +143,12 @@ public function getHeidelpayObject(): Heidelpay public function getUri($appendId = true): string { $uri = [rtrim($this->getParentResource()->getUri(), '/'), $this->getResourcePath()]; - if ($appendId && $this->getId() !== null) { - $uri[] = $this->getId(); + if ($appendId) { + if ($this->getId() !== null) { + $uri[] = $this->getId(); + } elseif ($this->getExternalId() !== null) { + $uri[] = $this->getExternalId(); + } } $uri[] = ''; @@ -248,7 +250,7 @@ private function getResourceService(): ResourceService } /** - * Fetches the Resource if necessary. + * Fetches the Resource if it has not been fetched yet and the id is set. * * @param AbstractHeidelpayResource $resource * diff --git a/src/Resources/Basket.php b/src/Resources/Basket.php index 83e6a359..bfd65ee8 100755 --- a/src/Resources/Basket.php +++ b/src/Resources/Basket.php @@ -24,10 +24,10 @@ */ namespace heidelpayPHP\Resources; -use function count; use heidelpayPHP\Adapter\HttpAdapterInterface; use heidelpayPHP\Resources\EmbeddedResources\BasketItem; use stdClass; +use function count; class Basket extends AbstractHeidelpayResource { @@ -89,14 +89,15 @@ public function getAmountTotalGross(): float */ public function setAmountTotalGross(float $amountTotalGross): Basket { - $this->amountTotalGross = $amountTotalGross; + $this->amountTotalGross = round($amountTotalGross, 4); return $this; } /** * @return float * - * @deprecated since 1.2.0.0 Please use getAmountTotalGross instead. + * @deprecated since 1.2.0.0 + * @see Basket::getAmountTotalGross() */ public function getAmountTotal(): float { @@ -108,7 +109,8 @@ public function getAmountTotal(): float * * @return Basket * - * @deprecated since 1.2.0.0 Please use setAmountTotalGross instead. + * @deprecated since 1.2.0.0 + * @see Basket::setAmountTotalGross() */ public function setAmountTotal(float $amountTotal): Basket { @@ -130,7 +132,7 @@ public function getAmountTotalDiscount(): float */ public function setAmountTotalDiscount(float $amountTotalDiscount): Basket { - $this->amountTotalDiscount = $amountTotalDiscount; + $this->amountTotalDiscount = round($amountTotalDiscount, 4); return $this; } @@ -149,7 +151,7 @@ public function getAmountTotalVat(): float */ public function setAmountTotalVat(float $amountTotalVat): Basket { - $this->amountTotalVat = $amountTotalVat; + $this->amountTotalVat = round($amountTotalVat, 4); return $this; } diff --git a/src/Resources/Customer.php b/src/Resources/Customer.php index 734fa906..f22f85bb 100755 --- a/src/Resources/Customer.php +++ b/src/Resources/Customer.php @@ -28,8 +28,8 @@ use heidelpayPHP\Constants\Salutations; use heidelpayPHP\Resources\EmbeddedResources\Address; use heidelpayPHP\Resources\EmbeddedResources\CompanyInfo; -use function in_array; use stdClass; +use function in_array; class Customer extends AbstractHeidelpayResource { @@ -75,7 +75,10 @@ class Customer extends AbstractHeidelpayResource * @param string|null $firstname * @param string|null $lastname * - * @deprecated since Version 1.1.5.0 use CustomerFactory::createCustomer(...) in the future. + * @deprecated since Version 1.1.5.0 + * @see CustomerFactory::createCustomer() + * @see CustomerFactory::createNotRegisteredB2bCustomer() + * @see CustomerFactory::createRegisteredB2bCustomer() */ public function __construct(string $firstname = null, string $lastname = null) { diff --git a/src/Resources/EmbeddedResources/BasketItem.php b/src/Resources/EmbeddedResources/BasketItem.php index d24ef682..8834bfca 100755 --- a/src/Resources/EmbeddedResources/BasketItem.php +++ b/src/Resources/EmbeddedResources/BasketItem.php @@ -142,7 +142,7 @@ public function getVat(): float */ public function setVat(float $vat): BasketItem { - $this->vat = $vat; + $this->vat = round($vat, 4); return $this; } @@ -161,7 +161,7 @@ public function getAmountDiscount(): float */ public function setAmountDiscount(float $amountDiscount): BasketItem { - $this->amountDiscount = $amountDiscount; + $this->amountDiscount = round($amountDiscount, 4); return $this; } @@ -180,7 +180,7 @@ public function getAmountGross(): float */ public function setAmountGross(float $amountGross): BasketItem { - $this->amountGross = $amountGross; + $this->amountGross = round($amountGross, 4); return $this; } @@ -199,7 +199,7 @@ public function getAmountVat(): float */ public function setAmountVat(float $amountVat): BasketItem { - $this->amountVat = $amountVat; + $this->amountVat = round($amountVat, 4); return $this; } @@ -218,7 +218,7 @@ public function getAmountPerUnit(): float */ public function setAmountPerUnit(float $amountPerUnit): BasketItem { - $this->amountPerUnit = $amountPerUnit; + $this->amountPerUnit = round($amountPerUnit, 4); return $this; } @@ -237,7 +237,7 @@ public function getAmountNet(): float */ public function setAmountNet(float $amountNet): BasketItem { - $this->amountNet = $amountNet; + $this->amountNet = round($amountNet, 4); return $this; } diff --git a/src/Resources/Keypair.php b/src/Resources/Keypair.php index 993c0b4e..1775e7b5 100755 --- a/src/Resources/Keypair.php +++ b/src/Resources/Keypair.php @@ -24,6 +24,9 @@ */ namespace heidelpayPHP\Resources; +use heidelpayPHP\Adapter\HttpAdapterInterface; +use stdClass; + class Keypair extends AbstractHeidelpayResource { /** @var string $publicKey */ @@ -32,8 +35,34 @@ class Keypair extends AbstractHeidelpayResource /** @var string $privateKey */ private $privateKey; - /** @var array $availablePaymentTypes */ - private $availablePaymentTypes = []; + /** @var bool $detailed */ + private $detailed = false; + + /** @var array $paymentTypes */ + private $paymentTypes = []; + + /** @var string $secureLevel */ + private $secureLevel; + + /** @var string $alias */ + private $alias; + + /** @var string $merchantName */ + private $merchantName; + + /** @var string $merchantAddress */ + private $merchantAddress; + + /** + * Credentials on File / Card on File + * If true the credentials are stored for future transactions. + * + * @var bool|null $cof + */ + private $cof; + + /** @var bool $validateBasket */ + private $validateBasket; //<editor-fold desc="Getters/Setters"> @@ -69,12 +98,28 @@ protected function setPrivateKey(string $privateKey) $this->privateKey = $privateKey; } + /** + * @return array + */ + public function getPaymentTypes(): array + { + return $this->paymentTypes; + } + + /** + * @param array $paymentTypes + */ + protected function setPaymentTypes(array $paymentTypes) + { + $this->paymentTypes = $paymentTypes; + } + /** * @return array */ public function getAvailablePaymentTypes(): array { - return $this->availablePaymentTypes; + return $this->getPaymentTypes(); } /** @@ -82,7 +127,173 @@ public function getAvailablePaymentTypes(): array */ protected function setAvailablePaymentTypes(array $paymentTypes) { - $this->availablePaymentTypes = $paymentTypes; + $this->setPaymentTypes($paymentTypes); + } + + /** + * @return string + */ + public function getSecureLevel(): string + { + return $this->secureLevel ?: ''; + } + + /** + * @param string|null $secureLevel + * + * @return Keypair + */ + protected function setSecureLevel($secureLevel): Keypair + { + $this->secureLevel = $secureLevel; + return $this; + } + + /** + * @return string + */ + public function getAlias(): string + { + return $this->alias ?: ''; + } + + /** + * @param string|null $alias + * + * @return Keypair + */ + protected function setAlias($alias): Keypair + { + $this->alias = $alias; + return $this; + } + + /** + * @return string + */ + public function getMerchantName(): string + { + return $this->merchantName ?: ''; + } + + /** + * @param string|null $merchantName + * + * @return Keypair + */ + protected function setMerchantName($merchantName): Keypair + { + $this->merchantName = $merchantName; + return $this; + } + + /** + * @return string + */ + public function getMerchantAddress(): string + { + return $this->merchantAddress ?: ''; + } + + /** + * @param string|null $merchantAddress + * + * @return Keypair + */ + protected function setMerchantAddress($merchantAddress): Keypair + { + $this->merchantAddress = $merchantAddress; + return $this; + } + + /** + * @return bool + */ + public function isDetailed(): bool + { + return $this->detailed; + } + + /** + * @param bool $detailed + * + * @return Keypair + */ + public function setDetailed(bool $detailed): Keypair + { + $this->detailed = $detailed; + return $this; + } + + /** + * Returns true if Credentials are stored for later transactions. + * + * @return bool|null + */ + public function isCof() + { + return $this->cof; + } + + /** + * @param bool $cof + * + * @return Keypair + */ + protected function setCof(bool $cof): Keypair + { + $this->cof = $cof; + return $this; + } + + /** + * @return bool + */ + public function isValidateBasket(): bool + { + return $this->validateBasket; + } + + /** + * @param bool $validateBasket + * + * @return Keypair + */ + protected function setValidateBasket(bool $validateBasket): Keypair + { + $this->validateBasket = $validateBasket; + return $this; + } + + //</editor-fold> + + //<editor-fold desc="Overridable Methods"> + + /** + * @inheritDoc + */ + public function handleResponse(stdClass $response, $method = HttpAdapterInterface::REQUEST_GET) + { + parent::handleResponse($response, $method); + + $paymentTypes = []; + if (isset($response->paymentTypes)) { + $paymentTypes = $response->paymentTypes; + } elseif (isset($response->availablePaymentTypes)) { + $paymentTypes = $response->availablePaymentTypes; + } + + foreach ($paymentTypes as $paymentType) { + $this->paymentTypes[] = $paymentType; + } + } + + /** + * @inheritDoc + */ + protected function getResourcePath(): string + { + return parent::getResourcePath() . ($this->isDetailed() ? '/types' : ''); } //</editor-fold> diff --git a/src/Resources/Payment.php b/src/Resources/Payment.php index 3766cb04..c9227339 100755 --- a/src/Resources/Payment.php +++ b/src/Resources/Payment.php @@ -26,6 +26,7 @@ use heidelpayPHP\Adapter\HttpAdapterInterface; use heidelpayPHP\Constants\ApiResponseCodes; +use heidelpayPHP\Constants\CancelReasonCodes; use heidelpayPHP\Constants\IdStrings; use heidelpayPHP\Constants\TransactionTypes; use heidelpayPHP\Exceptions\HeidelpayApiException; @@ -41,9 +42,9 @@ use heidelpayPHP\Services\IdService; use heidelpayPHP\Traits\HasOrderId; use heidelpayPHP\Traits\HasPaymentState; -use function is_string; use RuntimeException; use stdClass; +use function is_string; class Payment extends AbstractHeidelpayResource { @@ -628,41 +629,112 @@ public function getExternalId() * If no amount is given a full cancel will be performed i. e. all Charges and Authorizations will be cancelled. * * @param float|null $amount The amount to canceled. + * @param string $reason * - * @return Cancellation The resulting Cancellation object. - * If more then one cancellation is performed the last one will be returned. + * @return Cancellation|null The resulting Cancellation object. + * If more then one cancellation is performed the last one will be returned. * * @throws HeidelpayApiException A HeidelpayApiException is thrown if there is an error returned on API-request. * @throws RuntimeException A RuntimeException is thrown when there is a error while using the SDK. + * + * @deprecated since 1.2.3.0 + * @see Payment::cancelAmount() */ - public function cancel($amount = null): Cancellation + public function cancel($amount = null, $reason = CancelReasonCodes::REASON_CODE_CANCEL) { - list($chargeCancels, $chargeExceptions) = $this->cancelAllCharges(); - list($authCancel, $authException) = $this->cancelAuthorization($amount); + $cancellations = $this->cancelAmount($amount, $reason); + + if (count($cancellations) > 0) { + return $cancellations[0]; + } - $cancels = array_merge($chargeCancels, $authCancel); - $exceptions = array_merge($chargeExceptions, $authException); + throw new RuntimeException('This Payment could not be cancelled.'); + } - if (isset($cancels[0]) && $cancels[0] instanceof Cancellation) { - return $cancels[0]; + /** + * Performs a Cancellation transaction on the Payment. + * If no amount is given a full cancel will be performed i. e. all Charges and Authorizations will be cancelled. + * + * @param float|null $totalCancelAmount The amount to canceled. + * @param string $reason + * + * @return Cancellation[] An array holding all Cancellation objects created with this cancel call. + * + * @throws HeidelpayApiException A HeidelpayApiException is thrown if there is an error returned on API-request. + * @throws RuntimeException A RuntimeException is thrown when there is a error while using the SDK. + */ + public function cancelAmount($totalCancelAmount = null, $reason = CancelReasonCodes::REASON_CODE_CANCEL): array + { + $charges = $this->charges; + $remainingAmountToCancel = $totalCancelAmount; + + $cancelWholePayment = $remainingAmountToCancel === null; + $cancellations = []; + $cancellation = null; + + if ($cancelWholePayment || $remainingAmountToCancel > 0.0) { + $cancellation = $this->cancelAuthorizationAmount($remainingAmountToCancel); + + if ($cancellation instanceof Cancellation) { + $cancellations[] = $cancellation; + if (!$cancelWholePayment) { + $remainingAmountToCancel -= $cancellation->getAmount(); + } + $cancellation = null; + } } - // throw the last exception if no cancellation has been created - if (isset($exceptions[0]) && $exceptions[0] instanceof HeidelpayApiException) { - throw $exceptions[0]; + if (!$cancelWholePayment && $remainingAmountToCancel <= 0.0) { + return $cancellations; } - throw new RuntimeException('This Payment could not be cancelled.'); + /** @var Charge $charge */ + foreach ($charges as $charge) { + $cancelAmount = null; + if (!$cancelWholePayment && $remainingAmountToCancel <= $charge->getTotalAmount()) { + $cancelAmount = $remainingAmountToCancel; + } + + try { + $cancellation = $charge->cancel($cancelAmount, $reason); + } catch (HeidelpayApiException $e) { + $allowedErrors = [ + ApiResponseCodes::API_ERROR_ALREADY_CANCELLED, + ApiResponseCodes::API_ERROR_ALREADY_CHARGED_BACK + ]; + + if (!in_array($e->getCode(), $allowedErrors, true)) { + throw $e; + } + continue; + } + + if ($cancellation instanceof Cancellation) { + $cancellations[] = $cancellation; + if (!$cancelWholePayment) { + $remainingAmountToCancel -= $cancellation->getAmount(); + } + $cancellation = null; + } + + if (!$cancelWholePayment && $remainingAmountToCancel <= 0) { + break; + } + } + + return $cancellations; } /** - * Cancels all charges of the payment and returns an array of the cancellations and already charged exceptions that - * occur. + * Cancels all charges of the payment and returns an array of the cancellations and exceptions that occur. * * @return array * * @throws HeidelpayApiException * @throws RuntimeException + * + * @deprecated since 1.2.3.0 + * @see Payment::cancelAmount() */ public function cancelAllCharges(): array { @@ -674,13 +746,11 @@ public function cancelAllCharges(): array try { $cancels[] = $charge->cancel(); } catch (HeidelpayApiException $e) { - if (!in_array($e->getCode(), - [ - ApiResponseCodes::API_ERROR_CHARGE_ALREADY_CHARGED_BACK, - ApiResponseCodes::API_ERROR_AUTHORIZE_ALREADY_CANCELLED - ], - true - )) { + $allowedErrors = [ + ApiResponseCodes::API_ERROR_ALREADY_CHARGED_BACK, + ApiResponseCodes::API_ERROR_ALREADY_CANCELLED, + ]; + if (!in_array($e->getCode(), $allowedErrors, true)) { throw $e; } $exceptions[] = $e; @@ -696,24 +766,62 @@ public function cancelAllCharges(): array * * @throws HeidelpayApiException * @throws RuntimeException + * + * @deprecated since 1.2.3.0 + * @see Payment::cancelAuthorizationAmount() */ public function cancelAuthorization($amount = null): array { $cancels = []; - $exceptions = []; + $cancel = $this->cancelAuthorizationAmount($amount); + + if ($cancel instanceof Cancellation) { + $cancels[] = $cancel; + } + + return array($cancels, []); + } + + /** + * Cancel the given amount of the payments authorization. + * + * @param float|null $amount The amount to be cancelled. If null the remaining uncharged amount of the authorization + * will be cancelled completely. If it exceeds the remaining uncharged amount the + * cancellation will only cancel the remaining uncharged amount. + * + * @return Cancellation|null + * + * @throws HeidelpayApiException + * @throws RuntimeException + */ + public function cancelAuthorizationAmount($amount = null) + { + $cancellation = null; + $completeCancel = $amount === null; + + $authorize = $this->getAuthorization(); + if ($authorize !== null) { + $cancelAmount = null; + if (!$completeCancel) { + $remainingAuthorized = $this->getAmount()->getRemaining(); + $cancelAmount = $amount > $remainingAuthorized ? $remainingAuthorized : $amount; + } - $authorization = $this->getAuthorization(); - if ($authorization instanceof Authorization) { try { - $cancels[] = $authorization->cancel($amount); + $cancellation = $authorize->cancel($cancelAmount); } catch (HeidelpayApiException $e) { - if (ApiResponseCodes::API_ERROR_AUTHORIZE_ALREADY_CANCELLED !== $e->getCode()) { + $allowedErrors = [ + ApiResponseCodes::API_ERROR_ALREADY_CANCELLED, + ApiResponseCodes::API_ERROR_ALREADY_CHARGED + ]; + + if (!in_array($e->getCode(), $allowedErrors, true)) { throw $e; } - $exceptions[] = $e; } } - return array($cancels, $exceptions); + + return $cancellation; } /** diff --git a/src/Resources/PaymentTypes/Paypage.php b/src/Resources/PaymentTypes/Paypage.php index f62b2c82..7feb3234 100644 --- a/src/Resources/PaymentTypes/Paypage.php +++ b/src/Resources/PaymentTypes/Paypage.php @@ -101,9 +101,9 @@ class Paypage extends BasePaymentType */ public function __construct(float $amount, string $currency, string $returnUrl) { - $this->amount = $amount; - $this->currency = $currency; - $this->returnUrl = $returnUrl; + $this->setAmount($amount); + $this->setCurrency($currency); + $this->setReturnUrl($returnUrl); } //<editor-fold desc="Getters/Setters"> @@ -123,7 +123,7 @@ public function getAmount(): float */ public function setAmount(float $amount): Paypage { - $this->amount = $amount; + $this->amount = round($amount, 4); return $this; } diff --git a/src/Resources/TransactionTypes/Authorization.php b/src/Resources/TransactionTypes/Authorization.php index a6c37f31..5afa3259 100755 --- a/src/Resources/TransactionTypes/Authorization.php +++ b/src/Resources/TransactionTypes/Authorization.php @@ -83,10 +83,24 @@ public function getAmount() */ public function setAmount($amount): self { - $this->amount = $amount; + $this->amount = $amount !== null ? round($amount, 4) : null; return $this; } + /** + * @return float|null + */ + public function getCancelledAmount() + { + $amount = 0.0; + foreach ($this->getCancellations() as $cancellation) { + /** @var Cancellation $cancellation */ + $amount += $cancellation->getAmount(); + } + + return $amount; + } + /** * @return string|null */ diff --git a/src/Resources/TransactionTypes/Cancellation.php b/src/Resources/TransactionTypes/Cancellation.php index 13a100a6..7ccf6cc3 100755 --- a/src/Resources/TransactionTypes/Cancellation.php +++ b/src/Resources/TransactionTypes/Cancellation.php @@ -74,7 +74,7 @@ public function getAmount() */ public function setAmount($amount): Cancellation { - $this->amount = $amount; + $this->amount = $amount !== null ? round($amount, 4) : null; return $this; } diff --git a/src/Resources/TransactionTypes/Charge.php b/src/Resources/TransactionTypes/Charge.php index bcad2364..c486938e 100755 --- a/src/Resources/TransactionTypes/Charge.php +++ b/src/Resources/TransactionTypes/Charge.php @@ -94,10 +94,32 @@ public function getAmount() */ public function setAmount($amount): self { - $this->amount = $amount; + $this->amount = $amount !== null ? round($amount, 4) : null; return $this; } + /** + * @return float|null + */ + public function getCancelledAmount() + { + $amount = 0.0; + foreach ($this->getCancellations() as $cancellation) { + /** @var Cancellation $cancellation */ + $amount += $cancellation->getAmount(); + } + + return $amount; + } + + /** + * @return float|null + */ + public function getTotalAmount() + { + return $this->getAmount() - $this->getCancelledAmount(); + } + /** * @return string|null */ diff --git a/src/Resources/TransactionTypes/Payout.php b/src/Resources/TransactionTypes/Payout.php index 396bc087..0e138b4d 100644 --- a/src/Resources/TransactionTypes/Payout.php +++ b/src/Resources/TransactionTypes/Payout.php @@ -75,7 +75,7 @@ public function getAmount() */ public function setAmount($amount): self { - $this->amount = $amount; + $this->amount = $amount !== null ? round($amount, 4) : null; return $this; } diff --git a/src/Resources/TransactionTypes/Shipment.php b/src/Resources/TransactionTypes/Shipment.php index 389fe585..a9fd4d3f 100755 --- a/src/Resources/TransactionTypes/Shipment.php +++ b/src/Resources/TransactionTypes/Shipment.php @@ -30,7 +30,7 @@ class Shipment extends AbstractTransactionType { use HasInvoiceId; - /** @var float $amount */ + /** @var float|null $amount */ protected $amount; //<editor-fold desc="Getters/Setters"> @@ -48,9 +48,9 @@ public function getAmount() * * @return Shipment */ - public function setAmount(float $amount): Shipment + public function setAmount($amount): Shipment { - $this->amount = $amount; + $this->amount = $amount !== null ? round($amount, 4) : null; return $this; } diff --git a/src/Services/EnvironmentService.php b/src/Services/EnvironmentService.php index e0bb9a0a..4a43f2e7 100755 --- a/src/Services/EnvironmentService.php +++ b/src/Services/EnvironmentService.php @@ -33,6 +33,11 @@ class EnvironmentService const ENV_VAR_NAME_DISABLE_TEST_LOGGING = 'HEIDELPAY_MGW_DISABLE_TEST_LOGGING'; + const ENV_VAR_TEST_PRIVATE_KEY = 'HEIDELPAY_MGW_TEST_PRIVATE_KEY'; + const ENV_VAR_TEST_PUBLIC_KEY = 'HEIDELPAY_MGW_TEST_PUBLIC_KEY'; + const DEFAULT_TEST_PRIVATE_KEY = 's-priv-2a102ZMq3gV4I3zJ888J7RR6u75oqK3n'; + const DEFAULT_TEST_PUBLIC_KEY = 's-pub-2a10ifVINFAjpQJ9qW8jBe5OJPBx6Gxa'; + const ENV_VAR_NAME_TIMEOUT = 'HEIDELPAY_MGW_TIMEOUT'; const ENV_VAR_DEFAULT_TIMEOUT = 60; @@ -68,4 +73,28 @@ public static function getTimeout(): int $timeout = $_SERVER[self::ENV_VAR_NAME_TIMEOUT] ?? ''; return is_numeric($timeout) ? (int)$timeout : self::ENV_VAR_DEFAULT_TIMEOUT; } + + /** + * Returns the private key string set via environment variable. + * Returns the default key if the environment variable is not set. + * + * @return string + */ + public function getTestPrivateKey(): string + { + $key = $_SERVER[self::ENV_VAR_TEST_PRIVATE_KEY] ?? ''; + return empty($key) ? self::DEFAULT_TEST_PRIVATE_KEY : $key; + } + + /** + * Returns the public key string set via environment variable. + * Returns the default key if the environment variable is not set. + * + * @return string + */ + public function getTestPublicKey(): string + { + $key = $_SERVER[self::ENV_VAR_TEST_PUBLIC_KEY] ?? ''; + return empty($key) ? self::DEFAULT_TEST_PUBLIC_KEY : $key; + } } diff --git a/src/Services/ResourceService.php b/src/Services/ResourceService.php index b3f35c00..ae4c532a 100755 --- a/src/Services/ResourceService.php +++ b/src/Services/ResourceService.php @@ -417,14 +417,16 @@ public function fetchPaymentByOrderId($orderId): Payment /** * Fetch public key and configured payment types from API. * + * @param bool $detailed If this flag is set detailed information are fetched. + * * @return Keypair * * @throws HeidelpayApiException * @throws RuntimeException */ - public function fetchKeypair(): AbstractHeidelpayResource + public function fetchKeypair($detailed = false): AbstractHeidelpayResource { - $keyPair = (new Keypair())->setParentResource($this->heidelpay); + $keyPair = (new Keypair())->setParentResource($this->heidelpay)->setDetailed($detailed); return $this->fetch($keyPair); } diff --git a/test/BasePaymentTest.php b/test/BasePaymentTest.php index cc618612..3edd9fdc 100755 --- a/test/BasePaymentTest.php +++ b/test/BasePaymentTest.php @@ -36,6 +36,7 @@ use heidelpayPHP\Resources\TransactionTypes\AbstractTransactionType; use heidelpayPHP\Resources\TransactionTypes\Authorization; use heidelpayPHP\Resources\TransactionTypes\Charge; +use heidelpayPHP\Services\EnvironmentService; use heidelpayPHP\test\Fixtures\CustomerFixtureTrait; use PHPUnit\Framework\AssertionFailedError; use PHPUnit\Framework\Exception; @@ -52,13 +53,6 @@ class BasePaymentTest extends TestCase const RETURN_URL = 'http://dev.heidelpay.com'; - // SAQ-D certified merchants are allowed to handle and store CreditCard data, - // thus can create a CreditCard via this SDK. - // If the merchant is not certified to handle the CreditCard data SAQ-A applies - // in which case the merchant has to embed our iFrame via JS (UIComponents). - const PRIVATE_KEY = 's-priv-2a102ZMq3gV4I3zJ888J7RR6u75oqK3n'; - const PUBLIC_KEY = 's-pub-2a10ifVINFAjpQJ9qW8jBe5OJPBx6Gxa'; - /** * {@inheritDoc} * @@ -66,8 +60,8 @@ class BasePaymentTest extends TestCase */ protected function setUp() { - $this->heidelpay = (new Heidelpay(self::PRIVATE_KEY)) - ->setDebugHandler(new TestDebugHandler())->setDebugMode(true); + $privateKey = (new EnvironmentService())->getTestPrivateKey(); + $this->heidelpay = (new Heidelpay($privateKey))->setDebugHandler(new TestDebugHandler())->setDebugMode(true); } //<editor-fold desc="Custom asserts"> @@ -205,16 +199,18 @@ protected function createCardObject(): Card /** * Creates and returns an Authorization object with the API which can be used in test methods. * + * @param float $amount + * * @return Authorization * - * @throws RuntimeException * @throws HeidelpayApiException + * @throws RuntimeException */ - public function createCardAuthorization(): Authorization + public function createCardAuthorization($amount = 100.0): Authorization { $card = $this->heidelpay->createPaymentType($this->createCardObject()); $orderId = microtime(true); - $authorization = $this->heidelpay->authorize(100.0, 'EUR', $card, self::RETURN_URL, null, $orderId, null, null, false); + $authorization = $this->heidelpay->authorize($amount, 'EUR', $card, self::RETURN_URL, null, $orderId, null, null, false); return $authorization; } @@ -238,15 +234,17 @@ public function createPaypalAuthorization(): Authorization /** * Creates and returns a Charge object with the API which can be used in test methods. * + * @param float $amount + * * @return Charge * - * @throws RuntimeException * @throws HeidelpayApiException + * @throws RuntimeException */ - public function createCharge(): Charge + public function createCharge($amount = 100.0): Charge { $card = $this->heidelpay->createPaymentType(new SepaDirectDebit('DE89370400440532013000')); - return $this->heidelpay->charge(100.0, 'EUR', $card, self::RETURN_URL); + return $this->heidelpay->charge($amount, 'EUR', $card, self::RETURN_URL); } /** diff --git a/test/integration/BasketTest.php b/test/integration/BasketTest.php index f544c105..d87a5253 100755 --- a/test/integration/BasketTest.php +++ b/test/integration/BasketTest.php @@ -185,7 +185,7 @@ public function authorizeTransactionsShouldPassAlongTheBasketIdIfSet() /** @var Paypal $paypal */ $paypal = $this->heidelpay->createPaymentType(new Paypal()); - $authorize = $paypal->authorize(10.0, 'EUR', 'https://heidelpay.com', null, null, null, $basket); + $authorize = $paypal->authorize(123.4, 'EUR', 'https://heidelpay.com', null, null, null, $basket); $fetchedPayment = $this->heidelpay->fetchPayment($authorize->getPaymentId()); $this->assertEquals($basket->expose(), $fetchedPayment->getBasket()->expose()); @@ -208,7 +208,7 @@ public function chargeTransactionsShouldPassAlongTheBasketIdIfSet() $this->heidelpay->createPaymentType($sdd); $customer = $this->getMaximumCustomerInclShippingAddress()->setShippingAddress($this->getBillingAddress()); - $charge = $sdd->charge(100.0, 'EUR', self::RETURN_URL, $customer, null, null, $basket); + $charge = $sdd->charge(123.4, 'EUR', self::RETURN_URL, $customer, null, null, $basket); $fetchedPayment = $this->heidelpay->fetchPayment($charge->getPaymentId()); $this->assertEquals($basket->expose(), $fetchedPayment->getBasket()->expose()); @@ -233,7 +233,7 @@ public function authorizeTransactionsShouldCreateBasketIfItDoesNotExistYet() /** @var Paypal $paypal */ $paypal = $this->heidelpay->createPaymentType(new Paypal()); - $authorize = $paypal->authorize(10.0, 'EUR', 'https://heidelpay.com', null, null, null, $basket); + $authorize = $paypal->authorize(123.4, 'EUR', 'https://heidelpay.com', null, null, null, $basket); $this->assertNotEmpty($basket->getId()); $fetchedPayment = $this->heidelpay->fetchPayment($authorize->getPaymentId()); @@ -260,7 +260,7 @@ public function chargeTransactionsShouldCreateBasketIfItDoesNotExistYet() /** @var Paypal $paypal */ $paypal = $this->heidelpay->createPaymentType(new Paypal()); - $charge = $paypal->charge(10.0, 'EUR', 'https://heidelpay.com', null, null, null, $basket); + $charge = $paypal->charge(123.4, 'EUR', 'https://heidelpay.com', null, null, null, $basket); $this->assertNotEmpty($basket->getId()); $fetchedPayment = $this->heidelpay->fetchPayment($charge->getPaymentId()); diff --git a/test/integration/CustomerTest.php b/test/integration/CustomerTest.php index 590fb2eb..c42da02f 100755 --- a/test/integration/CustomerTest.php +++ b/test/integration/CustomerTest.php @@ -32,8 +32,8 @@ use heidelpayPHP\Resources\Payment; use heidelpayPHP\Resources\PaymentTypes\Paypal; use heidelpayPHP\test\BasePaymentTest; -use function microtime; use RuntimeException; +use function microtime; class CustomerTest extends BasePaymentTest { @@ -162,7 +162,8 @@ public function customerCanBeFetchedByObjectWithData(Customer $customer) */ public function transactionShouldCreateAndReferenceCustomerIfItDoesNotExistYet() { - $customer = $this->getMaximumCustomerInclShippingAddress(); + $customerId = 'customer' . $this->generateRandomId(); + $customer = $this->getMaximumCustomerInclShippingAddress()->setCustomerId($customerId); /** @var Paypal $paypal */ $paypal = $this->heidelpay->createPaymentType(new Paypal()); diff --git a/test/integration/KeyTest.php b/test/integration/KeypairTest.php old mode 100755 new mode 100644 similarity index 73% rename from test/integration/KeyTest.php rename to test/integration/KeypairTest.php index a8b99fa7..f1cdcc8f --- a/test/integration/KeyTest.php +++ b/test/integration/KeypairTest.php @@ -29,7 +29,7 @@ use heidelpayPHP\test\BasePaymentTest; use RuntimeException; -class KeyTest extends BasePaymentTest +class KeypairTest extends BasePaymentTest { /** * Validate valid keys are accepted. @@ -64,7 +64,7 @@ public function invalidKeysShouldResultInException($key) } /** - * Verify key pair command can be performed. + * Verify key pair config can be fetched. * * @test * @@ -76,6 +76,26 @@ public function keypairShouldReturnExpectedValues() $keypair = $this->heidelpay->fetchKeypair(); $this->assertNotNull($keypair); $this->assertNotEmpty($keypair->getPublicKey()); + $this->assertNotEmpty($keypair->getPrivateKey()); $this->assertNotEmpty($keypair->getAvailablePaymentTypes()); + $this->assertNotEmpty($keypair->getSecureLevel()); + } + + /** + * Verify key pair config can be fetched with details. + * + * @test + * + * @throws RuntimeException + * @throws HeidelpayApiException + */ + public function keypairShouldBeFetchableWithDetails() + { + $keypair = $this->heidelpay->fetchKeypair(true); + $this->assertNotNull($keypair); + $this->assertNotEmpty($keypair->getPublicKey()); + $this->assertNotEmpty($keypair->getPrivateKey()); + $this->assertNotEmpty($keypair->getPaymentTypes()); + $this->assertNotEmpty($keypair->getSecureLevel()); } } diff --git a/test/integration/PaymentCancelTest.php b/test/integration/PaymentCancelTest.php new file mode 100644 index 00000000..8b922c6d --- /dev/null +++ b/test/integration/PaymentCancelTest.php @@ -0,0 +1,525 @@ +<?php +/** + * This class defines integration tests to verify functionality of the Payment charge method. + * + * Copyright (C) 2019 heidelpay GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @link https://docs.heidelpay.com/ + * + * @author Simon Gabriel <development@heidelpay.com> + * + * @package heidelpayPHP/test/integration + */ +namespace heidelpayPHP\test\integration; + +use heidelpayPHP\Exceptions\HeidelpayApiException; +use heidelpayPHP\Resources\PaymentTypes\Invoice; +use heidelpayPHP\test\BasePaymentTest; +use PHPUnit\Framework\AssertionFailedError; +use PHPUnit\Framework\Exception; +use RuntimeException; + +class PaymentCancelTest extends BasePaymentTest +{ + //<editor-fold desc="Tests"> + + /** + * Verify full cancel on cancelled authorize returns empty array. + * + * @test + * + * @throws HeidelpayApiException + * @throws RuntimeException + */ + public function doubleCancelOnAuthorizeShouldReturnEmptyArray() + { + $authorization = $this->createCardAuthorization(); + $payment = $this->heidelpay->fetchPayment($authorization->getPaymentId()); + $this->assertTrue($payment->isPending()); + $this->assertAmounts($payment, 100.0, 0.0, 100.0, 0.0); + + $cancellations = $payment->cancelAmount(); + $this->assertTrue($payment->isCanceled()); + $this->assertAmounts($payment, 0.0, 0.0, 0.0, 0.0); + $this->assertCount(1, $cancellations); + + $newCancellations = $payment->cancelAmount(); + $this->assertCount(0, $newCancellations); + } + + /** + * Verify full cancel on charge. + * AND + * Return empty array if charge is already fully cancelled. + * PHPLIB-228 - Case 1 + double cancel + * + * @test + * + * @throws HeidelpayApiException + * @throws RuntimeException + */ + public function cancelOnChargeAndDoubleCancel() + { + $charge = $this->createCharge(123.44); + $payment = $this->heidelpay->fetchPayment($charge->getPaymentId()); + $this->assertTrue($payment->isCompleted()); + $this->assertAmounts($payment, 0.0, 123.44, 123.44, 0.0); + + $cancellations = $payment->cancelAmount(); + $this->assertTrue($payment->isCanceled()); + $this->assertAmounts($payment, 0.0, 0.0, 123.44, 123.44); + $this->assertCount(1, $cancellations); + + $payment = $this->heidelpay->fetchPayment($charge->getPaymentId()); + $newCancellations = $payment->cancelAmount(); + $this->assertTrue($payment->isCanceled()); + $this->assertAmounts($payment, 0.0, 0.0, 123.44, 123.44); + $this->assertCount(0, $newCancellations); + } + + /** + * Verify full cancel on multiple charges. + * PHPLIB-228 - Case 2 + * + * @test + * + * @throws HeidelpayApiException + * @throws RuntimeException + */ + public function fullCancelOnPaymentWithAuthorizeAndMultipleChargesShouldBePossible() + { + $authorization = $this->createCardAuthorization(123.44); + $payment = $this->heidelpay->fetchPayment($authorization->getPaymentId()); + $this->assertTrue($payment->isPending()); + $this->assertAmounts($payment, 123.44, 0.0, 123.44, 0.0); + + $payment->charge(100.44); + $this->assertTrue($payment->isPartlyPaid()); + $this->assertAmounts($payment, 23.0, 100.44, 123.44, 0.0); + + $payment = $this->heidelpay->fetchPayment($authorization->getPaymentId()); + $payment->charge(23.00); + $this->assertTrue($payment->isCompleted()); + $this->assertAmounts($payment, 0.0, 123.44, 123.44, 0.0); + + $payment = $this->heidelpay->fetchPayment($authorization->getPaymentId()); + $this->assertCount(2, $payment->cancelAmount()); + $this->assertTrue($payment->isCanceled()); + $this->assertAmounts($payment, 0.0, 0.0, 123.44, 123.44); + } + + /** + * Verify partial cancel on charge. + * PHPLIB-228 - Case 3 + * + * @test + * + * @throws HeidelpayApiException + * @throws RuntimeException + */ + public function partialCancelAndFullCancelOnSingleCharge() + { + $charge = $this->createCharge(222.33); + $payment = $this->heidelpay->fetchPayment($charge->getPaymentId()); + $this->assertTrue($payment->isCompleted()); + $this->assertAmounts($payment, 0.0, 222.33, 222.33, 0.0); + + $this->assertCount(1, $payment->cancelAmount(123.12)); + $this->assertTrue($payment->isCompleted()); + $this->assertAmounts($payment, 0.0, 99.21, 222.33, 123.12); + + $payment = $this->heidelpay->fetchPayment($charge->getPaymentId()); + $this->assertCount(1, $payment->cancelAmount(99.21)); + $this->assertTrue($payment->isCanceled()); + $this->assertAmounts($payment, 0.0, 0.0, 222.33, 222.33); + } + + /** + * Verify partial cancel on multiple charges (cancel < last charge). + * PHPLIB-228 - Case 4 + 5 + * + * @test + * @dataProvider partCancelDataProvider + * + * @param float $amount + * @param int $numberCancels + * + * @throws AssertionFailedError + * @throws Exception + * @throws HeidelpayApiException + * @throws RuntimeException + */ + public function partialCancelOnMultipleChargedAuthorization($amount, $numberCancels) + { + $authorizeAmount = 123.44; + $authorization = $this->createCardAuthorization($authorizeAmount); + $payment = $this->heidelpay->fetchPayment($authorization->getPaymentId()); + + $payment->charge(23.00); + $this->assertTrue($payment->isPartlyPaid()); + $this->assertAmounts($payment, 100.44, 23.0, $authorizeAmount, 0.0); + + $payment = $this->heidelpay->fetchPayment($authorization->getPaymentId()); + $payment->charge(100.44); + $this->assertTrue($payment->isCompleted()); + $this->assertAmounts($payment, 0.0, $authorizeAmount, $authorizeAmount, 0.0); + + $payment = $this->heidelpay->fetchPayment($authorization->getPaymentId()); + $this->assertCount($numberCancels, $payment->cancelAmount($amount)); + $this->assertTrue($payment->isCompleted()); + $this->assertAmounts($payment, 0.0, $authorizeAmount - $amount, $authorizeAmount, $amount); + } + + /** + * Verify full cancel on authorize. + * PHPLIB-228 - Case 6 + * + * @test + * @dataProvider fullCancelDataProvider + * + * @param float $amount + * + * @throws AssertionFailedError + * @throws Exception + * @throws HeidelpayApiException + * @throws RuntimeException + */ + public function fullCancelOnAuthorize($amount) + { + $authorization = $this->createCardAuthorization(); + $payment = $this->heidelpay->fetchPayment($authorization->getPaymentId()); + $this->assertTrue($payment->isPending()); + $this->assertAmounts($payment, 100.0, 0.0, 100.0, 0.0); + + $this->assertCount(1, $payment->cancelAmount($amount)); + $this->assertTrue($payment->isCanceled()); + $this->assertAmounts($payment, 0.0, 0.0, 0.0, 0.0); + } + + /** + * Verify partial cancel on authorize. + * PHPLIB-228 - Case 7 + * + * @test + * + * @throws HeidelpayApiException + * @throws RuntimeException + */ + public function fullCancelOnPartCanceledAuthorize() + { + $authorization = $this->createCardAuthorization(); + $payment = $this->heidelpay->fetchPayment($authorization->getPaymentId()); + $this->assertTrue($payment->isPending()); + $this->assertAmounts($payment, 100.0, 0.0, 100.0, 0.0); + + $this->assertCount(1, $payment->cancelAmount(10.0)); + $this->assertTrue($payment->isPending()); + $this->assertAmounts($payment, 90.0, 0.0, 90.0, 0.0); + + $payment = $this->heidelpay->fetchPayment($authorization->getPaymentId()); + $this->assertCount(1, $payment->cancelAmount(10.0)); + $this->assertTrue($payment->isPending()); + $this->assertAmounts($payment, 80.0, 0.0, 80.0, 0.0); + + $payment = $this->heidelpay->fetchPayment($authorization->getPaymentId()); + $this->assertCount(1, $payment->cancelAmount()); + $this->assertTrue($payment->isCanceled()); + $this->assertAmounts($payment, 0.0, 0.0, 0.0, 0.0); + } + + /** + * Verify full cancel on fully charged authorize. + * PHPLIB-228 - Case 8 + * + * @test + * @dataProvider fullCancelDataProvider + * + * @param float $amount The amount to be cancelled. + * + * @throws AssertionFailedError + * @throws Exception + * @throws HeidelpayApiException + * @throws RuntimeException + */ + public function fullCancelOnFullyChargedAuthorize($amount) + { + $authorization = $this->createCardAuthorization(); + $payment = $this->heidelpay->fetchPayment($authorization->getPaymentId()); + $this->assertTrue($payment->isPending()); + $this->assertAmounts($payment, 100.0, 0.0, 100.0, 0.0); + + $payment->charge(); + $this->assertTrue($payment->isCompleted()); + $this->assertAmounts($payment, 0.0, 100.0, 100.0, 0.0); + + $payment = $this->heidelpay->fetchPayment($authorization->getPaymentId()); + $this->assertCount(1, $payment->cancelAmount($amount)); + $this->assertTrue($payment->isCanceled()); + $this->assertAmounts($payment, 0.0, 0.0, 100.0, 100.0); + } + + /** + * Verify full cancel on partly charged authorize. + * PHPLIB-228 - Case 9 + * + * @test + * @dataProvider fullCancelDataProvider + * + * @param $amount + * + * @throws AssertionFailedError + * @throws Exception + * @throws HeidelpayApiException + * @throws RuntimeException + */ + public function fullCancelOnPartlyChargedAuthorizeShouldBePossible($amount) + { + $authorization = $this->createCardAuthorization(); + $payment = $this->heidelpay->fetchPayment($authorization->getPaymentId()); + $this->assertTrue($payment->isPending()); + $this->assertAmounts($payment, 100.0, 0.0, 100.0, 0.0); + + $payment->charge(50.0); + $this->assertTrue($payment->isPartlyPaid()); + $this->assertAmounts($payment, 50.0, 50.0, 100.0, 0.0); + + $payment = $this->heidelpay->fetchPayment($authorization->getPaymentId()); + $this->assertCount(2, $payment->cancelAmount($amount)); + $this->assertTrue($payment->isCanceled()); + $this->assertAmounts($payment, 0.0, 0.0, 50.0, 50.0); + } + + /** + * Verify part cancel on uncharged authorize. + * PHPLIB-228 - Case 10 + * + * @test + * + * @throws AssertionFailedError + * @throws Exception + * @throws HeidelpayApiException + * @throws RuntimeException + */ + public function partCancelOnUnchargedAuthorize() + { + $authorization = $this->createCardAuthorization(); + $payment = $this->heidelpay->fetchPayment($authorization->getPaymentId()); + $this->assertTrue($payment->isPending()); + $this->assertAmounts($payment, 100.0, 0.0, 100.0, 0.0); + + $this->assertCount(1, $payment->cancelAmount(50.0)); + $this->assertTrue($payment->isPending()); + $this->assertAmounts($payment, 50.0, 0.0, 50.0, 0.0); + } + + /** + * Verify part cancel on partly charged authorize with cancel amount lt charged amount. + * PHPLIB-228 - Case 11 + * + * @test + * + * @throws AssertionFailedError + * @throws Exception + * @throws HeidelpayApiException + * @throws RuntimeException + */ + public function partCancelOnPartlyChargedAuthorizeWithAmountLtCharged() + { + $authorization = $this->createCardAuthorization(); + $payment = $this->heidelpay->fetchPayment($authorization->getPaymentId()); + $this->assertTrue($payment->isPending()); + $this->assertAmounts($payment, 100.0, 0.0, 100.0, 0.0); + + $payment->charge(25.0); + $this->assertTrue($payment->isPartlyPaid()); + $this->assertAmounts($payment, 75.0, 25.0, 100.0, 0.0); + + $payment = $this->heidelpay->fetchPayment($authorization->getPaymentId()); + $this->assertCount(1, $payment->cancelAmount(20.0)); + $this->assertTrue($payment->isPartlyPaid()); + $this->assertAmounts($payment, 55.0, 25.0, 80.0, 0.0); + } + + /** + * Verify part cancel on partly charged authorize with cancel amount gt charged amount. + * PHPLIB-228 - Case 12 + * + * @test + * + * @throws AssertionFailedError + * @throws Exception + * @throws HeidelpayApiException + * @throws RuntimeException + */ + public function partCancelOnPartlyChargedAuthorizeWithAmountGtCharged() + { + $authorization = $this->createCardAuthorization(); + $payment = $this->heidelpay->fetchPayment($authorization->getPaymentId()); + $this->assertTrue($payment->isPending()); + $this->assertAmounts($payment, 100.0, 0.0, 100.0, 0.0); + + $payment->charge(40.0); + $this->assertTrue($payment->isPartlyPaid()); + $this->assertAmounts($payment, 60.0, 40.0, 100.0, 0.0); + + $payment = $this->heidelpay->fetchPayment($authorization->getPaymentId()); + $this->assertCount(2, $payment->cancelAmount(80.0)); + $this->assertTrue($payment->isCompleted()); + $this->assertAmounts($payment, 0.0, 20.0, 40.0, 20.0); + } + + /** + * Verify full cancel on initial iv charge (reversal) + * PHPLIB-228 - Case 13 + * + * @test + * @dataProvider fullCancelDataProvider + * + * @param float $amount + * + * @throws AssertionFailedError + * @throws Exception + * @throws HeidelpayApiException + * @throws RuntimeException + */ + public function fullCancelOnInitialInvoiceCharge($amount) + { + /** @var Invoice $invoice */ + $invoice = $this->heidelpay->createPaymentType(new Invoice()); + $charge = $invoice->charge(100.0, 'EUR', self::RETURN_URL); + $payment = $this->heidelpay->fetchPayment($charge->getPaymentId()); + $this->assertTrue($payment->isPending()); + $this->assertAmounts($payment, 100.0, 0.0, 100.0, 0.0); + + $this->assertCount(1, $payment->cancelAmount($amount)); + $this->assertTrue($payment->isCanceled()); + $this->assertAmounts($payment, 0.0, 0.0, 0.0, 0.0); + } + + /** + * Verify part cancel on initial iv charge (reversal) + * PHPLIB-228 - Case 14 + * + * @test + * + * @throws AssertionFailedError + * @throws Exception + * @throws HeidelpayApiException + * @throws RuntimeException + */ + public function partCancelOnInitialInvoiceChargeShouldBePossible() + { + /** @var Invoice $invoice */ + $invoice = $this->heidelpay->createPaymentType(new Invoice()); + $charge = $invoice->charge(100.0, 'EUR', self::RETURN_URL); + $payment = $this->heidelpay->fetchPayment($charge->getPaymentId()); + $this->assertTrue($payment->isPending()); + $this->assertAmounts($payment, 100.0, 0.0, 100.0, 0.0); + + $this->assertCount(1, $payment->cancelAmount(50.0)); + $this->assertTrue($payment->isPending()); + $this->assertAmounts($payment, 50.0, 0.0, 50.0, 0.0); + } + + /** + * Verify cancelling more than was charged. + * PHPLIB-228 - Case 15 + * + * @test + * + * @throws AssertionFailedError + * @throws Exception + * @throws HeidelpayApiException + * @throws RuntimeException + */ + public function cancelMoreThanWasCharged() + { + $charge = $this->createCharge(50.0); + $payment = $this->heidelpay->fetchPayment($charge->getPaymentId()); + $this->assertTrue($payment->isCompleted()); + $this->assertAmounts($payment, 0.0, 50.0, 50.0, 0.0); + + $this->assertCount(1, $payment->cancelAmount(100.0)); + $this->assertTrue($payment->isCanceled()); + $this->assertAmounts($payment, 0.0, 0.0, 50.0, 50.0); + } + + /** + * Verify second cancel on partly cancelled charge. + * PHPLIB-228 - Case 16 + * + * @test + * + * @throws AssertionFailedError + * @throws Exception + * @throws HeidelpayApiException + * @throws RuntimeException + */ + public function secondCancelExceedsRemainderOfPartlyCancelledCharge() + { + $authorization = $this->createCardAuthorization(); + $payment = $this->heidelpay->fetchPayment($authorization->getPaymentId()); + $this->assertTrue($payment->isPending()); + $this->assertAmounts($payment, 100.0, 0.0, 100.0, 0.0); + + $payment->charge(50.0); + $this->assertTrue($payment->isPartlyPaid()); + $this->assertAmounts($payment, 50.0, 50.0, 100.0, 0.0); + + $payment = $this->heidelpay->fetchPayment($authorization->getPaymentId()); + $payment->charge(50.0); + $this->assertTrue($payment->isCompleted()); + $this->assertAmounts($payment, 0.0, 100.0, 100.0, 0.0); + + $payment = $this->heidelpay->fetchPayment($authorization->getPaymentId()); + $this->assertCount(1, $payment->cancelAmount(40.0)); + $this->assertTrue($payment->isCompleted()); + $this->assertAmounts($payment, 0.0, 60.0, 100.0, 40.0); + + $payment = $this->heidelpay->fetchPayment($authorization->getPaymentId()); + $this->assertCount(2, $payment->cancelAmount(30.0)); + $this->assertTrue($payment->isCompleted()); + $this->assertAmounts($payment, 0.0, 30.0, 100.0, 70.0); + } + + //</editor-fold> + + //<editor-fold desc="Data Providers"> + + /** + * @return array + */ + public function partCancelDataProvider(): array + { + return [ + 'cancel amount lt last charge' => [20, 1], + 'cancel amount eq to last charge' => [23, 1], + 'cancel amount gt last charge' => [40, 2] + ]; + } + + /** + * @return array + */ + public function fullCancelDataProvider(): array + { + return [ + 'no amount given' => [null], + 'amount given' => [100.0] + ]; + } + + //</editor-fold> +} diff --git a/test/integration/PaymentTest.php b/test/integration/PaymentTest.php index 6bb61939..41261c3a 100755 --- a/test/integration/PaymentTest.php +++ b/test/integration/PaymentTest.php @@ -129,82 +129,6 @@ public function partialChargeAfterAuthorization() $this->assertEquals('10.0', $charge->getAmount()); } - /** - * Verify full cancel on authorize throws exception if already canceled. - * - * @test - * - * @throws HeidelpayApiException - * @throws RuntimeException - */ - public function fullCancelOnAuthorizeShouldThrowExceptionIfAlreadyCanceled() - { - $authorization = $this->createCardAuthorization(); - $fetchedPayment = $this->heidelpay->fetchPayment($authorization->getPayment()->getId()); - $cancel = $fetchedPayment->getAuthorization()->cancel(); - $this->assertNotNull($cancel); - $this->assertEquals('s-cnl-1', $cancel->getId()); - $this->assertEquals($authorization->getAmount(), $cancel->getAmount()); - - $this->expectException(HeidelpayApiException::class); - $this->expectExceptionCode(ApiResponseCodes::API_ERROR_AUTHORIZE_ALREADY_CANCELLED); - $fetchedPayment->cancel(); - } - - /** - * Verify partial cancel on authorize. - * - * @test - * - * @throws HeidelpayApiException - * @throws RuntimeException - */ - public function partialCancelOnAuthorizeShouldBePossible() - { - $authorization = $this->createCardAuthorization(); - $fetchedPayment = $this->heidelpay->fetchPayment($authorization->getPayment()->getId()); - $this->assertAmounts($fetchedPayment, 100.0, 0, 100.0, 0); - - $cancel = $fetchedPayment->cancel(10.0); - $this->assertNotNull($cancel); - $this->assertEquals('s-cnl-1', $cancel->getId()); - $this->assertEquals('10.0', $cancel->getAmount()); - $this->assertAmounts($fetchedPayment, 90.0, 0, 90.0, 0); - } - - /** - * Verify full cancel on charge. - * - * @test - * - * @throws HeidelpayApiException - * @throws RuntimeException - */ - public function fullCancelOnChargeShouldBePossible() - { - $charge = $this->createCharge(); - $fetchedPayment = $this->heidelpay->fetchPayment($charge->getPayment()->getId()); - $fetchedCharge = $fetchedPayment->getCharge('s-chg-1'); - $cancellation = $fetchedCharge->cancel(); - $this->assertNotNull($cancellation); - } - - /** - * Verify partial cancel on charge. - * - * @test - * - * @throws HeidelpayApiException - * @throws RuntimeException - */ - public function partialCancelShouldBePossible() - { - $charge = $this->createCharge(); - $fetchedPayment = $this->heidelpay->fetchPayment($charge->getPayment()->getId()); - $cancel = $fetchedPayment->getChargeByIndex(0)->cancel(10.0); - $this->assertNotNull($cancel); - } - /** * Verify authorization on payment. * diff --git a/test/integration/PaymentTypes/InvoiceFactoringTest.php b/test/integration/PaymentTypes/InvoiceFactoringTest.php index efe8e223..e4483d45 100755 --- a/test/integration/PaymentTypes/InvoiceFactoringTest.php +++ b/test/integration/PaymentTypes/InvoiceFactoringTest.php @@ -139,15 +139,7 @@ public function invoiceFactoringShouldBeChargeable(InvoiceFactoring $invoiceFact $customer->setShippingAddress($customer->getBillingAddress()); $basket = $this->createBasket(); - $charge = $invoiceFactoring->charge( - 100.0, - 'EUR', - self::RETURN_URL, - $customer, - $basket->getOrderId(), - null, - $basket - ); + $charge = $invoiceFactoring->charge(123.4, 'EUR', self::RETURN_URL, $customer, $basket->getOrderId(), null, $basket); $this->assertNotNull($charge); $this->assertNotEmpty($charge->getId()); $this->assertNotEmpty($charge->getIban()); @@ -178,15 +170,7 @@ public function verifyInvoiceFactoringIsNotShippableWoInvoiceIdOnHeidelpayObject $customer->setShippingAddress($customer->getBillingAddress()); $basket = $this->createBasket(); - $charge = $invoiceFactoring->charge( - 100.0, - 'EUR', - self::RETURN_URL, - $customer, - $basket->getOrderId(), - null, - $basket - ); + $charge = $invoiceFactoring->charge(123.4, 'EUR', self::RETURN_URL, $customer, $basket->getOrderId(), null, $basket); // perform shipment $payment = $charge->getPayment(); @@ -215,15 +199,7 @@ public function verifyInvoiceFactoringIsNotShippableWoInvoiceIdOnPaymentObject() $customer->setShippingAddress($customer->getBillingAddress()); $basket = $this->createBasket(); - $charge = $invoiceFactoring->charge( - 100.0, - 'EUR', - self::RETURN_URL, - $customer, - $basket->getOrderId(), - null, - $basket - ); + $charge = $invoiceFactoring->charge(123.4, 'EUR', self::RETURN_URL, $customer, $basket->getOrderId(), null, $basket); // perform shipment $payment = $charge->getPayment(); @@ -251,15 +227,7 @@ public function verifyInvoiceFactoringShipmentWithInvoiceIdOnHeidelpayObject() $customer->setShippingAddress($customer->getBillingAddress()); $basket = $this->createBasket(); - $charge = $invoiceFactoring->charge( - 100.0, - 'EUR', - self::RETURN_URL, - $customer, - $basket->getOrderId(), - null, - $basket - ); + $charge = $invoiceFactoring->charge(123.4, 'EUR', self::RETURN_URL, $customer, $basket->getOrderId(), null, $basket); // perform shipment $payment = $charge->getPayment(); @@ -288,15 +256,7 @@ public function verifyInvoiceFactoringShipmentWithInvoiceIdOnPaymentObject() $customer->setShippingAddress($customer->getBillingAddress()); $basket = $this->createBasket(); - $charge = $invoiceFactoring->charge( - 100.0, - 'EUR', - self::RETURN_URL, - $customer, - $basket->getOrderId(), - null, - $basket - ); + $charge = $invoiceFactoring->charge(123.4, 'EUR', self::RETURN_URL, $customer, $basket->getOrderId(), null, $basket); $payment = $charge->getPayment(); $invoiceId = substr(str_replace(['0.',' '], '', microtime(false)), 0, 16); @@ -323,17 +283,7 @@ public function verifyInvoiceFactoringShipmentWithPreSetInvoiceId() $basket = $this->createBasket(); $invoiceId = substr(str_replace(['0.',' '], '', microtime(false)), 0, 16); - $charge = $invoiceFactoring->charge( - 100.0, - 'EUR', - self::RETURN_URL, - $customer, - $basket->getOrderId(), - null, - $basket, - null, - $invoiceId - ); + $charge = $invoiceFactoring->charge(123.4, 'EUR', self::RETURN_URL, $customer, $basket->getOrderId(), null, $basket, null, $invoiceId); $payment = $charge->getPayment(); $shipment = $this->heidelpay->ship($payment); diff --git a/test/integration/PaymentTypes/PaypageTest.php b/test/integration/PaymentTypes/PaypageTest.php index decd9668..024988b1 100644 --- a/test/integration/PaymentTypes/PaypageTest.php +++ b/test/integration/PaymentTypes/PaypageTest.php @@ -66,7 +66,7 @@ public function maximumPaypageChargeShouldBeCreatable() $basket = $this->createBasket(); $customer = CustomerFactory::createCustomer('Max', 'Mustermann'); $invoiceId = $this->generateRandomId(); - $paypage = (new Paypage(100.0, 'EUR', self::RETURN_URL)) + $paypage = (new Paypage(123.4, 'EUR', self::RETURN_URL)) ->setLogoImage('https://dev.heidelpay.com/devHeidelpay_400_180.jpg') ->setFullPageImage('https://www.heidelpay.com/fileadmin/content/header-Imges-neu/Header_Phone_12.jpg') ->setShopName('My Test Shop') @@ -120,7 +120,7 @@ public function maximumPaypageAuthorizeShouldBeCreatable() $basket = $this->createBasket(); $customer = CustomerFactory::createCustomer('Max', 'Mustermann'); $invoiceId = $this->generateRandomId(); - $paypage = (new Paypage(100.0, 'EUR', self::RETURN_URL)) + $paypage = (new Paypage(123.4, 'EUR', self::RETURN_URL)) ->setLogoImage('https://dev.heidelpay.com/devHeidelpay_400_180.jpg') ->setFullPageImage('https://www.heidelpay.com/fileadmin/content/header-Imges-neu/Header_Phone_12.jpg') ->setShopName('My Test Shop') diff --git a/test/integration/TransactionTypes/AuthorizationTest.php b/test/integration/TransactionTypes/AuthorizationTest.php index f2bde4e5..9da2d582 100644 --- a/test/integration/TransactionTypes/AuthorizationTest.php +++ b/test/integration/TransactionTypes/AuthorizationTest.php @@ -181,11 +181,11 @@ public function authorizeShouldAcceptAllParameters() $invoiceId = $this->generateRandomId(); $paymentReference = 'paymentReference'; - $authorize = $card->authorize(100.0, 'EUR', self::RETURN_URL, $customer, $orderId, $metadata, $basket, true, $invoiceId, $paymentReference); + $authorize = $card->authorize(123.4, 'EUR', self::RETURN_URL, $customer, $orderId, $metadata, $basket, true, $invoiceId, $paymentReference); $payment = $authorize->getPayment(); $this->assertSame($card, $payment->getPaymentType()); - $this->assertEquals(100.0, $authorize->getAmount()); + $this->assertEquals(123.4, $authorize->getAmount()); $this->assertEquals('EUR', $authorize->getCurrency()); $this->assertEquals(self::RETURN_URL, $authorize->getReturnUrl()); $this->assertSame($customer, $payment->getCustomer()); diff --git a/test/integration/TransactionTypes/ChargeTest.php b/test/integration/TransactionTypes/ChargeTest.php index 12734555..4a3baf19 100644 --- a/test/integration/TransactionTypes/ChargeTest.php +++ b/test/integration/TransactionTypes/ChargeTest.php @@ -105,23 +105,12 @@ public function chargeShouldAcceptAllParameters() $paymentReference = 'paymentReference'; // perform request - $charge = $paymentType->charge( - 100.0, - 'EUR', - self::RETURN_URL, - $customer, - $orderId, - $metadata, - $basket, - true, - $invoiceId, - $paymentReference - ); + $charge = $paymentType->charge(123.4, 'EUR', self::RETURN_URL, $customer, $orderId, $metadata, $basket, true, $invoiceId, $paymentReference); // verify the data sent and received match $payment = $charge->getPayment(); $this->assertSame($paymentType, $payment->getPaymentType()); - $this->assertEquals(100.0, $charge->getAmount()); + $this->assertEquals(123.4, $charge->getAmount()); $this->assertEquals('EUR', $charge->getCurrency()); $this->assertEquals(self::RETURN_URL, $charge->getReturnUrl()); $this->assertSame($customer, $payment->getCustomer()); @@ -166,23 +155,12 @@ public function chargeWithCustomerShouldAcceptAllParameters() $paymentReference = 'paymentReference'; // perform request - $charge = $ivg->charge( - 100.0, - 'EUR', - self::RETURN_URL, - $customer, - $orderId, - $metadata, - $basket, - null, - $invoiceId, - $paymentReference - ); + $charge = $ivg->charge(123.4, 'EUR', self::RETURN_URL, $customer, $orderId, $metadata, $basket, null, $invoiceId, $paymentReference); // verify the data sent and received match $payment = $charge->getPayment(); $this->assertSame($ivg, $payment->getPaymentType()); - $this->assertEquals(100.0, $charge->getAmount()); + $this->assertEquals(123.4, $charge->getAmount()); $this->assertEquals('EUR', $charge->getCurrency()); $this->assertEquals(self::RETURN_URL, $charge->getReturnUrl()); $this->assertSame($customer, $payment->getCustomer()); diff --git a/test/integration/TransactionTypes/PayoutTest.php b/test/integration/TransactionTypes/PayoutTest.php index a2554ebd..bf22d408 100644 --- a/test/integration/TransactionTypes/PayoutTest.php +++ b/test/integration/TransactionTypes/PayoutTest.php @@ -166,11 +166,11 @@ public function payoutShouldAcceptAllParameters() $invoiceId = $this->generateRandomId(); $paymentReference = 'paymentReference'; - $payout = $card->payout(100.0, 'EUR', self::RETURN_URL, $customer, $orderId, $metadata, $basket, $invoiceId, $paymentReference); + $payout = $card->payout(123.4, 'EUR', self::RETURN_URL, $customer, $orderId, $metadata, $basket, $invoiceId, $paymentReference); $payment = $payout->getPayment(); $this->assertSame($card, $payment->getPaymentType()); - $this->assertEquals(100.0, $payout->getAmount()); + $this->assertEquals(123.4, $payout->getAmount()); $this->assertEquals('EUR', $payout->getCurrency()); $this->assertEquals(self::RETURN_URL, $payout->getReturnUrl()); $this->assertSame($customer, $payment->getCustomer()); diff --git a/test/unit/Exceptions/HeidelpayApiExceptionTest.php b/test/unit/Exceptions/HeidelpayApiExceptionTest.php index 40b31a31..1c1279ff 100755 --- a/test/unit/Exceptions/HeidelpayApiExceptionTest.php +++ b/test/unit/Exceptions/HeidelpayApiExceptionTest.php @@ -42,6 +42,7 @@ public function heidelpayApiExceptionShouldReturnDefaultDataWhenNoneIsSet() $exception = new HeidelpayApiException(); $this->assertEquals(HeidelpayApiException::CLIENT_MESSAGE, $exception->getClientMessage()); $this->assertEquals(HeidelpayApiException::MESSAGE, $exception->getMerchantMessage()); + $this->assertEquals('No error id provided', $exception->getErrorId()); $this->assertEquals('No error code provided', $exception->getCode()); } @@ -58,9 +59,10 @@ public function heidelpayApiExceptionShouldReturnDefaultDataWhenNoneIsSet() */ public function heidelpayApiExceptionShouldReturnTheGivenData(array $testData, array $expected) { - $exception = new HeidelpayApiException($testData['message'], $testData['client_message'], $testData['code']); + $exception = new HeidelpayApiException($testData['message'], $testData['clientMessage'], $testData['code'], $testData['errorId']); $this->assertEquals($expected['message'], $exception->getMerchantMessage()); - $this->assertEquals($expected['client_message'], $exception->getClientMessage()); + $this->assertEquals($expected['clientMessage'], $exception->getClientMessage()); + $this->assertEquals($expected['errorId'], $exception->getErrorId()); $this->assertEquals($expected['code'], $exception->getCode()); } @@ -73,28 +75,24 @@ public function exceptionDataProvider(): array { return [ 'default' => [ - 'testData' => ['message' => null, 'client_message' => null, 'code' => null], - 'expected' => [ - 'message' => HeidelpayApiException::MESSAGE, - 'client_message' => HeidelpayApiException::CLIENT_MESSAGE, - 'code' => '' - ] + 'testData' => ['message' => null, 'clientMessage' => null, 'code' => null, 'errorId' => null], + 'expected' => ['message' => HeidelpayApiException::MESSAGE, 'clientMessage' => HeidelpayApiException::CLIENT_MESSAGE, 'code' => 'No error code provided', 'errorId' => 'No error id provided'] ], 'message' => [ - 'testData' => ['message' => 'myMessage', 'client_message' => null, 'code' => null], - 'expected' => [ - 'message' => 'myMessage', - 'client_message' => HeidelpayApiException::CLIENT_MESSAGE, - 'code' => '' - ] + 'testData' => ['message' => 'myMessage', 'clientMessage' => null, 'code' => null, 'errorId' => ''], + 'expected' => ['message' => 'myMessage', 'clientMessage' => HeidelpayApiException::CLIENT_MESSAGE, 'code' => 'No error code provided', 'errorId' => 'No error id provided'] ], 'clientMessage' => [ - 'testData' => ['message' => 'myMessage', 'client_message' => 'myClientMessage', 'code' => null], - 'expected' => ['message' => 'myMessage', 'client_message' => 'myClientMessage', 'code' => null] + 'testData' => ['message' => 'myMessage', 'clientMessage' => 'myClientMessage', 'code' => null, 'errorId' => null], + 'expected' => ['message' => 'myMessage', 'clientMessage' => 'myClientMessage', 'code' => 'No error code provided', 'errorId' => 'No error id provided'] ], 'code' => [ - 'testData' => ['message' => 'myMessage', 'client_message' => 'myClientMessage', 'code' => 'myCode'], - 'expected' => ['message' => 'myMessage', 'client_message' => 'myClientMessage', 'code' => 'myCode'] + 'testData' => ['message' => 'myMessage', 'clientMessage' => 'myClientMessage', 'code' => 'myCode', 'errorId' => null], + 'expected' => ['message' => 'myMessage', 'clientMessage' => 'myClientMessage', 'code' => 'myCode', 'errorId' => 'No error id provided'] + ], + 'errorId' => [ + 'testData' => ['message' => 'myMessage', 'clientMessage' => 'myClientMessage', 'code' => 'myCode', 'errorId' => 'myErrorId'], + 'expected' => ['message' => 'myMessage', 'clientMessage' => 'myClientMessage', 'code' => 'myCode', 'errorId' => 'myErrorId'] ] ]; } diff --git a/test/unit/Resources/BasketTest.php b/test/unit/Resources/BasketTest.php index ea16dc8d..97e31158 100755 --- a/test/unit/Resources/BasketTest.php +++ b/test/unit/Resources/BasketTest.php @@ -168,4 +168,21 @@ public function referenceIdShouldBeAutomaticallySetToTheArrayIndexIfItIsNotSet() $this->assertEquals('0', $basketItem3->getBasketItemReferenceId()); $this->assertEquals('1', $basketItem4->getBasketItemReferenceId()); } + + /** + * Verify amount total is replaced by amount total gross. + * + * @test + * + * @throws Exception + */ + public function amountTotalSetterGetterAccessAmountTotalGross() + { + $basket = new Basket(); + $this->assertEquals($basket->getAmountTotalGross(), $basket->getAmountTotal()); + $basket->setAmountTotalGross(123.45); + $this->assertEquals($basket->getAmountTotalGross(), $basket->getAmountTotal()); + $basket->setAmountTotal(45.321); + $this->assertEquals($basket->getAmountTotalGross(), $basket->getAmountTotal()); + } } diff --git a/test/unit/Resources/CustomerTest.php b/test/unit/Resources/CustomerTest.php index b2064a34..c4cc0f3d 100755 --- a/test/unit/Resources/CustomerTest.php +++ b/test/unit/Resources/CustomerTest.php @@ -191,6 +191,27 @@ public function gettersAndSettersOfCompanyInfoShouldWork() $this->assertSame($companyInfo, $customer->getCompanyInfo()); } + /** + * Verify removeRestrictedSymbols method works. + * + * @test + * + * @dataProvider removeRestrictedSymbolsMethodShouldReturnTheCorrectValueDP + * + * @throws Exception + * + * @param mixed $value + * @param mixed $expected + */ + public function removeRestrictedSymbolsMethodShouldReturnTheCorrectValue($value, $expected) + { + $companyInfo = new CompanyInfo(); + $this->assertNull($companyInfo->getFunction()); + + $companyInfo->setFunction($value); + $this->assertEquals($expected, $companyInfo->getFunction()); + } + /** * Verify salutation only uses the given values. * @@ -243,11 +264,31 @@ public function fetchCustomerByOrderIdShouldCreateCustomerObjectWithCustomerIdAn static function ($customer) use ($heidelpay) { return $customer instanceof Customer && $customer->getCustomerId() === 'myCustomerId' && - $customer->getId() === 'myCustomerId' && $customer->getHeidelpayObject() === $heidelpay; })); /** @var ResourceService $resourceSrvMock */ $resourceSrvMock->fetchCustomerByExtCustomerId('myCustomerId'); } + + //<editor-fold desc="Data providers"> + + /** + * DataProvider for removeRestrictedSymbolsMethodShouldReturnTheCorrectValue. + */ + public function removeRestrictedSymbolsMethodShouldReturnTheCorrectValueDP(): array + { + return [ + 'null' => [null, null], + 'empty' => ['', ''], + 'blank' => [' ', ' '], + 'string' => ['MyTestString', 'MyTestString'], + '<' => ['<', ''], + '>' => ['>', ''], + '<test>' => ['<test>', 'test'], + 'Text1' => [' >>>This >>>text >>>should <<<look <<<different ', ' This text should look different '] + ]; + } + + //</editor-fold> } diff --git a/test/unit/Resources/DummyHeidelpayResource.php b/test/unit/Resources/DummyHeidelpayResource.php index 6e67a44a..a8125574 100755 --- a/test/unit/Resources/DummyHeidelpayResource.php +++ b/test/unit/Resources/DummyHeidelpayResource.php @@ -42,6 +42,9 @@ public function __construct(Customer $customer) $this->customer = $customer; } + /** + * {@inheritDoc} + */ public function getLinkedResources(): array { return ['customer' => $this->customer]; diff --git a/test/unit/Resources/EmbeddedResources/BasketItemTest.php b/test/unit/Resources/EmbeddedResources/BasketItemTest.php index 02577610..80fa2d17 100755 --- a/test/unit/Resources/EmbeddedResources/BasketItemTest.php +++ b/test/unit/Resources/EmbeddedResources/BasketItemTest.php @@ -65,6 +65,7 @@ public function settersAndGettersShouldWork() $basketItem->setTitle('myTitle'); $basketItem->setSubTitle('mySubTitle'); $basketItem->setImageUrl('https://my.image.url'); + $basketItem->setType('myType'); $this->assertEquals(2, $basketItem->getQuantity()); $this->assertEquals(9876, $basketItem->getAmountDiscount()); @@ -77,6 +78,7 @@ public function settersAndGettersShouldWork() $this->assertEquals('myUnit', $basketItem->getUnit()); $this->assertEquals('myTitle', $basketItem->getTitle()); $this->assertEquals('mySubTitle', $basketItem->getSubTitle()); + $this->assertEquals('myType', $basketItem->getType()); $this->assertEquals('https://my.image.url', $basketItem->getImageUrl()); } } diff --git a/test/unit/Resources/KeypairTest.php b/test/unit/Resources/KeypairTest.php index 41609064..d1d3f29a 100755 --- a/test/unit/Resources/KeypairTest.php +++ b/test/unit/Resources/KeypairTest.php @@ -26,26 +26,51 @@ use heidelpayPHP\Resources\Keypair; use heidelpayPHP\test\BaseUnitTest; +use PHPUnit\Framework\AssertionFailedError; +use PHPUnit\Framework\Exception; use RuntimeException; use stdClass; class KeypairTest extends BaseUnitTest { /** - * Verify that an Authorization can be updated on handle response. + * Verify getters and setters work properly. * * @test * - * @throws RuntimeException + * @throws AssertionFailedError + * @throws Exception */ - public function anAuthorizationShouldBeUpdatedThroughResponseHandling() + public function gettersAndSettersWorkAsExpected() { $keypair = new Keypair(); + $this->assertFalse($keypair->isDetailed()); $this->assertNull($keypair->getPublicKey()); $this->assertNull($keypair->getPrivateKey()); - /** @noinspection UnnecessaryAssertionInspection */ - $this->assertInternalType('array', $keypair->getAvailablePaymentTypes()); - $this->assertEmpty($keypair->getAvailablePaymentTypes()); + $this->assertEmpty($keypair->getPaymentTypes()); + $this->assertSame($keypair->getPaymentTypes(), $keypair->getAvailablePaymentTypes()); + $this->assertNull($keypair->isCof()); + $this->assertEquals('', $keypair->getSecureLevel()); + $this->assertEquals('', $keypair->getMerchantName()); + $this->assertEquals('', $keypair->getMerchantAddress()); + $this->assertEquals('', $keypair->getAlias()); + $this->assertFalse($keypair->isDetailed()); + + $keypair->setDetailed(true); + + $this->assertTrue($keypair->isDetailed()); + } + + /** + * Verify that a key pair can be updated on handle response. + * + * @test + * + * @throws RuntimeException + */ + public function aKeypairShouldBeUpdatedThroughResponseHandling() + { + $keypair = new Keypair(); $paymentTypes = [ 'przelewy24', @@ -67,8 +92,71 @@ public function anAuthorizationShouldBeUpdatedThroughResponseHandling() $testResponse->availablePaymentTypes = $paymentTypes; $keypair->handleResponse($testResponse); - $this->assertArraySubset($paymentTypes, $keypair->getAvailablePaymentTypes()); + $this->assertArraySubset($paymentTypes, $keypair->getPaymentTypes()); + $this->assertEquals('s-pub-1234', $keypair->getPublicKey()); + $this->assertEquals('s-priv-4321', $keypair->getPrivateKey()); + } + + /** + * Verify that a key pair can be updated with details on handle response. + * + * @test + * + * @throws RuntimeException + */ + public function aKeypairShouldBeUpdatedWithDetailsThroughResponseHandling() + { + $keypair = new Keypair(); + + $paymentTypes = [ + (object) [ + 'supports' => [ + (object) [ + 'brands' => ['JCB', 'VISAELECTRON', 'MAESTRO', 'VISA', 'MASTER'], + 'countries' => [], + 'channel' => '31HA07BC819430D3495C56BC18C55622', + 'currency' => ['CHF', 'CNY', 'JPY', 'USD', 'GBP', 'EUR'] + ] + ], + 'type' => 'card', + 'allowCustomerTypes' => 'B2C', + 'allowCreditTransaction' => true, + '3ds' => true + ], + (object) [ + 'supports' => [ + (object) [ + 'brands' => ['CUP', 'SOLO', 'CARTEBLEUE', 'VISAELECTRON', 'MAESTRO', 'AMEX', 'VISA', 'MASTER'], + 'countries' => [], + 'channel' => '31HA07BC819430D3495C7C9D07B1A922', + 'currency' => ['MGA', 'USD', 'GBP', 'EUR'] + ] + ], + 'type' => 'card', + 'allowCustomerTypes' => 'B2C', + 'allowCreditTransaction' => true, + '3ds' => false + ] + ]; + + $testResponse = (object) [ + 'publicKey' => 's-pub-1234', + 'privateKey' => 's-priv-4321', + 'secureLevel' => 'SAQ-D', + 'alias' => 'Readme.io user', + 'merchantName' => 'Heidelpay GmbH', + 'merchantAddress' => 'Vangerowstraße 18, 69115 Heidelberg', + 'paymentTypes' => $paymentTypes + ]; + + $keypair->handleResponse($testResponse); + $this->assertEquals($paymentTypes, $keypair->getPaymentTypes()); + $this->assertSame($keypair->getAvailablePaymentTypes(), $keypair->getPaymentTypes()); $this->assertEquals('s-pub-1234', $keypair->getPublicKey()); $this->assertEquals('s-priv-4321', $keypair->getPrivateKey()); + $this->assertEquals('SAQ-D', $keypair->getSecureLevel()); + $this->assertEquals('Readme.io user', $keypair->getAlias()); + $this->assertEquals('Heidelpay GmbH', $keypair->getMerchantName()); + $this->assertEquals('Vangerowstraße 18, 69115 Heidelberg', $keypair->getMerchantAddress()); } } diff --git a/test/unit/Resources/PaymentCancelTest.php b/test/unit/Resources/PaymentCancelTest.php new file mode 100644 index 00000000..f1705583 --- /dev/null +++ b/test/unit/Resources/PaymentCancelTest.php @@ -0,0 +1,423 @@ +<?php +/** + * This class defines unit tests to verify cancel functionality of the Payment resource. + * + * Copyright (C) 2019 heidelpay GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @link https://docs.heidelpay.com/ + * + * @author Simon Gabriel <development@heidelpay.com> + * + * @package heidelpayPHP/test/unit + */ +namespace heidelpayPHP\test\unit\Resources; + +use heidelpayPHP\Constants\ApiResponseCodes; +use heidelpayPHP\Exceptions\HeidelpayApiException; +use heidelpayPHP\Resources\EmbeddedResources\Amount; +use heidelpayPHP\Resources\Payment; +use heidelpayPHP\Resources\TransactionTypes\Authorization; +use heidelpayPHP\Resources\TransactionTypes\Cancellation; +use heidelpayPHP\Resources\TransactionTypes\Charge; +use heidelpayPHP\test\BaseUnitTest; +use PHPUnit\Framework\AssertionFailedError; +use PHPUnit\Framework\Exception; +use PHPUnit\Framework\MockObject\MockObject; +use ReflectionException; +use RuntimeException; + +class PaymentCancelTest extends BaseUnitTest +{ + //<editor-fold desc="Deprecated since 1.2.3.0"> + + /** + * Verify payment:cancel calls cancelAllCharges and cancelAuthorizationAmount and returns first charge cancellation + * object. + * + * @test + * + * @throws HeidelpayApiException + * @throws ReflectionException + * @throws RuntimeException + * + * @deprecated since 1.2.3.0 + */ + public function cancelShouldCallCancelAllChargesAndCancelAuthorizationAndReturnFirstChargeCancellationObject() + { + $paymentMock = $this->getMockBuilder(Payment::class)->setMethods(['cancelAmount'])->getMock(); + $cancellation = new Cancellation(1.0); + $paymentMock->expects($this->once())->method('cancelAmount')->willReturn([$cancellation]); + + /** @var Payment $paymentMock */ + $this->assertSame($cancellation, $paymentMock->cancel()); + } + + /** + * Verify payment:cancel throws Exception if no cancellation and no auth existed to be cancelled. + * + * @test + * + * @throws ReflectionException + * @throws RuntimeException + * @throws HeidelpayApiException + * + * @deprecated since 1.2.3.0 + */ + public function cancelShouldThrowExceptionIfNoTransactionExistsToBeCancelled() + { + $paymentMock = $this->getMockBuilder(Payment::class)->setMethods(['cancelAllCharges', 'cancelAuthorization'])->getMock(); + + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage('This Payment could not be cancelled.'); + + /** @var Payment $paymentMock */ + $paymentMock->cancel(); + } + + /** + * Verify cancel all charges will call cancel on each existing charge of the payment and will return a list of + * cancels and exceptions. + * + * @test + * + * @throws HeidelpayApiException + * @throws ReflectionException + * @throws RuntimeException + * + * @deprecated since 1.2.3.0 + */ + public function cancelAllChargesShouldCallCancelOnAllChargesAndReturnCancelsAndExceptions() + { + $cancellation1 = new Cancellation(1.0); + $cancellation2 = new Cancellation(2.0); + $cancellation3 = new Cancellation(3.0); + $exception1 = new HeidelpayApiException('', '', ApiResponseCodes::API_ERROR_ALREADY_CHARGED_BACK); + $exception2 = new HeidelpayApiException('', '', ApiResponseCodes::API_ERROR_ALREADY_CHARGED_BACK); + + $chargeMock1 = $this->getMockBuilder(Charge::class)->setMethods(['cancel'])->getMock(); + $chargeMock1->expects($this->once())->method('cancel')->willReturn($cancellation1); + + $chargeMock2 = $this->getMockBuilder(Charge::class)->setMethods(['cancel'])->getMock(); + $chargeMock2->expects($this->once())->method('cancel')->willThrowException($exception1); + + $chargeMock3 = $this->getMockBuilder(Charge::class)->setMethods(['cancel'])->getMock(); + $chargeMock3->expects($this->once())->method('cancel')->willReturn($cancellation2); + + $chargeMock4 = $this->getMockBuilder(Charge::class)->setMethods(['cancel'])->getMock(); + $chargeMock4->expects($this->once())->method('cancel')->willThrowException($exception2); + + $chargeMock5 = $this->getMockBuilder(Charge::class)->setMethods(['cancel'])->getMock(); + $chargeMock5->expects($this->once())->method('cancel')->willReturn($cancellation3); + + /** + * @var Charge $chargeMock1 + * @var Charge $chargeMock2 + * @var Charge $chargeMock3 + * @var Charge $chargeMock4 + * @var Charge $chargeMock5 + */ + $payment = new Payment(); + $payment->addCharge($chargeMock1)->addCharge($chargeMock2)->addCharge($chargeMock3)->addCharge($chargeMock4)->addCharge($chargeMock5); + + list($cancellations, $exceptions) = $payment->cancelAllCharges(); + $this->assertArraySubset([$cancellation1, $cancellation2, $cancellation3], $cancellations); + $this->assertArraySubset([$exception1, $exception2], $exceptions); + } + + /** + * Verify cancelAllCharges will throw any exception with Code different to + * ApiResponseCodes::API_ERROR_CHARGE_ALREADY_CANCELED. + * + * @test + * + * @throws ReflectionException + * @throws RuntimeException + * + * @deprecated since 1.2.3.0 + */ + public function cancelAllChargesShouldThrowChargeCancelExceptionsOtherThanAlreadyCharged() + { + $ex1 = new HeidelpayApiException('', '', ApiResponseCodes::API_ERROR_ALREADY_CHARGED_BACK); + $ex2 = new HeidelpayApiException('', '', ApiResponseCodes::API_ERROR_CHARGED_AMOUNT_HIGHER_THAN_EXPECTED); + + $chargeMock1 = $this->getMockBuilder(Charge::class)->setMethods(['cancel'])->getMock(); + $chargeMock1->expects($this->once())->method('cancel')->willThrowException($ex1); + + $chargeMock2 = $this->getMockBuilder(Charge::class)->setMethods(['cancel'])->getMock(); + $chargeMock2->expects($this->once())->method('cancel')->willThrowException($ex2); + + /** + * @var Charge $chargeMock1 + * @var Charge $chargeMock2 + */ + $payment = (new Payment())->addCharge($chargeMock1)->addCharge($chargeMock2); + + try { + $payment->cancelAllCharges(); + $this->assertFalse(true, 'The expected exception has not been thrown.'); + } catch (HeidelpayApiException $e) { + $this->assertSame($ex2, $e); + } + } + + //</editor-fold> + + /** + * Verify cancelAmount will call cancelAuthorizationAmount with the amountToCancel. + * When cancelAmount is <= the value of the cancellation it Will return auth cancellation only. + * Charge cancel will not be called if the amount to cancel has been cancelled on the authorization. + * + * @test + * + * @throws HeidelpayApiException + * @throws ReflectionException + * @throws RuntimeException + * @throws Exception + * @throws \PHPUnit\Framework\MockObject\RuntimeException + */ + public function cancelAmountShouldCallCancelAuthorizationAmount() + { + /** @var MockObject|Payment $paymentMock */ + $paymentMock = $this->getMockBuilder(Payment::class)->setMethods(['cancelAuthorizationAmount'])->getMock(); + $chargeMock = $this->getMockBuilder(Charge::class)->setMethods(['cancel'])->getMock(); + + $paymentMock->setAuthorization((new Authorization(12.3))->setPayment($paymentMock)); + + $cancellation = new Cancellation(12.3); + $paymentMock->expects($this->exactly(2))->method('cancelAuthorizationAmount')->willReturn($cancellation); + $chargeMock->expects($this->never())->method('cancel'); + + $this->assertEquals([$cancellation], $paymentMock->cancelAmount(10.0)); + $this->assertEquals([$cancellation], $paymentMock->cancelAmount(12.3)); + } + + /** + * Verify that cancel amount will be cancelled on charges if auth does not exist. + * + * @test + * + * @throws Exception + * @throws HeidelpayApiException + * @throws ReflectionException + * @throws RuntimeException + * @throws \PHPUnit\Framework\MockObject\RuntimeException + */ + public function chargesShouldBeCancelledIfAuthDoesNotExist1() + { + /** @var MockObject|Payment $paymentMock */ + /** @var MockObject|Charge $chargeMock */ + $paymentMock = $this->getMockBuilder(Payment::class)->setMethods(['cancelAuthorizationAmount'])->getMock(); + $chargeMock = $this->getMockBuilder(Charge::class)->setMethods(['cancel'])->setConstructorArgs([10.0])->getMock(); + + $cancellation = new Cancellation(10.0); + + $paymentMock->expects($this->once())->method('cancelAuthorizationAmount')->willReturn(null); + $chargeMock->expects($this->once())->method('cancel')->with(10.0, 'CANCEL')->willReturn($cancellation); + $paymentMock->addCharge($chargeMock); + + $this->assertEquals([$cancellation], $paymentMock->cancelAmount(10.0)); + } + + /** + * Verify that cancel amount will be cancelled on charges if auth does not exist. + * + * @test + * + * @throws Exception + * @throws HeidelpayApiException + * @throws ReflectionException + * @throws RuntimeException + * @throws \PHPUnit\Framework\MockObject\RuntimeException + */ + public function chargesShouldBeCancelledIfAuthDoesNotExist2() + { + /** @var MockObject|Payment $paymentMock */ + /** @var MockObject|Charge $charge1Mock */ + /** @var MockObject|Charge $charge2Mock */ + $paymentMock = $this->getMockBuilder(Payment::class)->setMethods(['cancelAuthorizationAmount'])->getMock(); + $charge1Mock = $this->getMockBuilder(Charge::class)->setMethods(['cancel'])->setConstructorArgs([10.0])->getMock(); + $charge2Mock = $this->getMockBuilder(Charge::class)->setMethods(['cancel'])->setConstructorArgs([12.3])->getMock(); + + $cancellation1 = new Cancellation(10.0); + $cancellation2 = new Cancellation(2.3); + + $paymentMock->expects($this->exactly(3))->method('cancelAuthorizationAmount')->willReturn(null); + $charge1Mock->expects($this->exactly(3))->method('cancel')->withConsecutive([10.0, 'CANCEL'], [null, 'CANCEL'], [null, 'CANCEL'])->willReturn($cancellation1); + $charge2Mock->expects($this->exactly(2))->method('cancel')->withConsecutive([2.3, 'CANCEL'], [null, 'CANCEL'])->willReturn($cancellation2); + + $paymentMock->addCharge($charge1Mock)->addCharge($charge2Mock); + + $this->assertEquals([$cancellation1], $paymentMock->cancelAmount(10.0)); + $this->assertEquals([$cancellation1, $cancellation2], $paymentMock->cancelAmount(12.3)); + $this->assertEquals([$cancellation1, $cancellation2], $paymentMock->cancelAmount()); + } + + /** + * Verify certain errors are allowed during cancellation and will be ignored. + * + * @test + * @dataProvider allowedErrorCodesDuringChargeCancel + * + * @param string $allowedExceptionCode + * @param bool $shouldHaveThrownException + * + * @throws Exception + * @throws ReflectionException + * @throws RuntimeException + * @throws AssertionFailedError + * @throws \PHPUnit\Framework\MockObject\RuntimeException + */ + public function verifyAllowedErrorsWillBeIgnoredDuringChargeCancel($allowedExceptionCode, $shouldHaveThrownException) + { + /** @var MockObject|Payment $paymentMock */ + /** @var MockObject|Charge $chargeMock */ + $paymentMock = $this->getMockBuilder(Payment::class)->setMethods(['cancelAuthorizationAmount'])->getMock(); + $chargeMock = $this->getMockBuilder(Charge::class)->setMethods(['cancel'])->disableOriginalConstructor()->getMock(); + + $allowedException = new HeidelpayApiException(null, null, $allowedExceptionCode); + $chargeMock->method('cancel')->willThrowException($allowedException); + $paymentMock->addCharge($chargeMock); + + try { + $this->assertEquals([], $paymentMock->cancelAmount(12.3)); + $this->assertFalse($shouldHaveThrownException, 'Exception should have been thrown here!'); + } catch (HeidelpayApiException $e) { + $this->assertTrue($shouldHaveThrownException, "Exception should not have been thrown here! ({$e->getCode()})"); + } + } + + /** + * Verify cancelAuthorizationAmount will call cancel on the authorization and will return a list of cancels. + * + * @test + * + * @throws HeidelpayApiException + * @throws ReflectionException + * @throws RuntimeException + */ + public function cancelAuthorizationAmountShouldCallCancelOnTheAuthorizationAndReturnCancellation() + { + $cancellation = new Cancellation(1.0); + $authorizationMock = $this->getMockBuilder(Authorization::class)->setMethods(['cancel'])->getMock(); + $authorizationMock->expects($this->once())->method('cancel')->willReturn($cancellation); + + $paymentMock = $this->getMockBuilder(Payment::class)->setMethods(['getAuthorization'])->getMock(); + $paymentMock->expects($this->once())->method('getAuthorization')->willReturn($authorizationMock); + + /** + * @var Authorization $authorizationMock + * @var Payment $paymentMock + */ + $paymentMock->setAuthorization($authorizationMock); + $this->assertEquals($cancellation, $paymentMock->cancelAuthorizationAmount()); + } + + /** + * Verify cancelAuthorizationAmount will call cancel the given amount on the authorization of the payment. + * Cancellation amount will be the remaining amount of the payment at max. + * + * @test + * + * @throws Exception + * @throws HeidelpayApiException + * @throws ReflectionException + * @throws RuntimeException + * @throws \PHPUnit\Framework\MockObject\RuntimeException + */ + public function cancelAuthorizationAmountShouldCallCancelWithTheRemainingAmountAtMax() + { + $cancellation = new Cancellation(); + + /** @var MockObject|Authorization $authorizationMock */ + $authorizationMock = $this->getMockBuilder(Authorization::class)->setConstructorArgs([100.0])->setMethods(['cancel'])->getMock(); + $authorizationMock->expects($this->exactly(4))->method('cancel')->withConsecutive([null], [50.0], [100.0], [100.0])->willReturn($cancellation); + + $paymentMock = $this->getMockBuilder(Payment::class)->setMethods(['getAuthorization', 'getAmount'])->getMock(); + $paymentMock->method('getAmount')->willReturn((new Amount())->setRemaining(100.0)); + $paymentMock->expects($this->exactly(4))->method('getAuthorization')->willReturn($authorizationMock); + + /** + * @var Authorization $authorizationMock + * @var Payment $paymentMock + */ + $paymentMock->setAuthorization($authorizationMock); + $this->assertEquals($cancellation, $paymentMock->cancelAuthorizationAmount()); + $this->assertEquals($cancellation, $paymentMock->cancelAuthorizationAmount(50.0)); + $this->assertEquals($cancellation, $paymentMock->cancelAuthorizationAmount(100.0)); + $this->assertEquals($cancellation, $paymentMock->cancelAuthorizationAmount(101.0)); + } + + /** + * Verify certain errors are allowed during cancellation and will be ignored. + * + * @test + * @dataProvider allowedErrorCodesDuringAuthCancel + * + * @param string $allowedExceptionCode + * @param bool $shouldHaveThrownException + * + * @throws Exception + * @throws ReflectionException + * @throws RuntimeException + * @throws AssertionFailedError + * @throws \PHPUnit\Framework\MockObject\RuntimeException + */ + public function verifyAllowedErrorsWillBeIgnoredDuringAuthorizeCancel($allowedExceptionCode, $shouldHaveThrownException) + { + /** @var MockObject|Payment $paymentMock */ + /** @var MockObject|Authorization $authMock */ + $paymentMock = $this->getMockBuilder(Payment::class)->setMethods(['getAuthorization'])->getMock(); + $authMock = $this->getMockBuilder(Authorization::class)->setMethods(['cancel'])->disableOriginalConstructor()->getMock(); + + $allowedException = new HeidelpayApiException(null, null, $allowedExceptionCode); + $authMock->method('cancel')->willThrowException($allowedException); + $paymentMock->method('getAuthorization')->willReturn($authMock); + + try { + $this->assertEquals(null, $paymentMock->cancelAuthorizationAmount(12.3)); + $this->assertFalse($shouldHaveThrownException, 'Exception should have been thrown here!'); + } catch (HeidelpayApiException $e) { + $this->assertTrue($shouldHaveThrownException, "Exception should not have been thrown here! ({$e->getCode()})"); + } + } + + //<editor-fold desc="Data Providers"> + + /** + * @return array + */ + public function allowedErrorCodesDuringChargeCancel(): array + { + return [ + 'already cancelled' => [ApiResponseCodes::API_ERROR_ALREADY_CANCELLED, false], + 'already chargedBack' => [ApiResponseCodes::API_ERROR_ALREADY_CANCELLED, false], + 'other' => [ApiResponseCodes::API_ERROR_BASKET_ITEM_IMAGE_INVALID_EXTENSION, true] + ]; + } + + /** + * @return array + */ + public function allowedErrorCodesDuringAuthCancel(): array + { + return [ + 'already cancelled' => [ApiResponseCodes::API_ERROR_ALREADY_CANCELLED, false], + 'already chargedBack' => [ApiResponseCodes::API_ERROR_ALREADY_CHARGED, false], + 'other' => [ApiResponseCodes::API_ERROR_BASKET_ITEM_IMAGE_INVALID_EXTENSION, true] + ]; + } + + //</editor-fold> +} diff --git a/test/unit/Resources/PaymentTest.php b/test/unit/Resources/PaymentTest.php index ecc81841..b5b30833 100755 --- a/test/unit/Resources/PaymentTest.php +++ b/test/unit/Resources/PaymentTest.php @@ -24,7 +24,6 @@ */ namespace heidelpayPHP\test\unit\Resources; -use heidelpayPHP\Constants\ApiResponseCodes; use heidelpayPHP\Constants\PaymentState; use heidelpayPHP\Exceptions\HeidelpayApiException; use heidelpayPHP\Heidelpay; @@ -788,15 +787,35 @@ public function handleResponseShouldFetchAndUpdatePaymentTypeIfTheIdIsSet() */ public function handleResponseShouldFetchAndUpdateMetadataIfTheIdIsSet() { - $payment = (new Payment())->setId('myPaymentId'); - - $resourceServiceMock = $this->getMockBuilder(ResourceService::class) - ->disableOriginalConstructor()->setMethods(['fetchMetadata'])->getMock(); + $resourceServiceMock = $this->getMockBuilder(ResourceService::class)->disableOriginalConstructor()->setMethods(['fetchMetadata'])->getMock(); $resourceServiceMock->expects($this->once())->method('fetchMetadata')->with('MetadataId'); + /** @var ResourceService $resourceServiceMock */ + $heidelpayObj = (new Heidelpay('s-priv-123'))->setResourceService($resourceServiceMock); + $payment = (new Payment())->setId('myPaymentId')->setParentResource($heidelpayObj); + + $response = new stdClass(); + $response->resources = new stdClass(); + $response->resources->metadataId = 'MetadataId'; + $payment->handleResponse($response); + } + /** + * Verify handleResponse updates metadata. + * + * @test + * + * @throws HeidelpayApiException + * @throws RuntimeException + * @throws ReflectionException + */ + public function handleResponseShouldGetMetadataIfUnfetchedMetadataObjectWithIdIsGiven() + { + $metadata = (new Metadata())->setId('MetadataId'); + $resourceServiceMock = $this->getMockBuilder(ResourceService::class)->disableOriginalConstructor()->setMethods(['getResource'])->getMock(); + $resourceServiceMock->expects($this->once())->method('getResource')->with($metadata); /** @var ResourceService $resourceServiceMock */ $heidelpayObj = (new Heidelpay('s-priv-123'))->setResourceService($resourceServiceMock); - $payment->setParentResource($heidelpayObj); + $payment = (new Payment())->setId('myPaymentId')->setParentResource($heidelpayObj)->setMetadata($metadata); $response = new stdClass(); $response->resources = new stdClass(); @@ -1276,315 +1295,6 @@ public function handleResponseShouldAddPayoutFromResponseIfItDoesNotExists() //</editor-fold> - //<editor-fold desc="Cancel"> - - /** - * Verify payment:cancel calls cancelAllCharges and cancelAuthorization and returns first charge cancellation - * object. - * - * @test - * - * @throws HeidelpayApiException - * @throws ReflectionException - * @throws RuntimeException - */ - public function cancelShouldCallCancelAllChargesAndCancelAuthorizationAndReturnFirstChargeCancellationObject() - { - $paymentMock = $this->getMockBuilder(Payment::class) - ->setMethods(['cancelAllCharges', 'cancelAuthorization'])->getMock(); - $cancellation = new Cancellation(1.0); - $exception1 = new HeidelpayApiException('', '', ApiResponseCodes::API_ERROR_CHARGE_ALREADY_CHARGED_BACK); - $exception2 = new HeidelpayApiException('', '', ApiResponseCodes::API_ERROR_CHARGE_ALREADY_CHARGED_BACK); - $exception3 = new HeidelpayApiException('', '', ApiResponseCodes::API_ERROR_CHARGE_ALREADY_CHARGED_BACK); - $paymentMock->expects($this->once())->method('cancelAllCharges') - ->willReturn([[$cancellation], [$exception1, $exception2]]); - $paymentMock->expects($this->once())->method('cancelAuthorization')->willReturn([[], [$exception3]]); - - /** @var Payment $paymentMock */ - $this->assertSame($cancellation, $paymentMock->cancel()); - } - - /** - * Verify payment:cancel calls cancelAllCharges and cancelAuthorization and returns authorize cancellation object if - * no charge cancellation object exist. - * - * @test - * - * @throws HeidelpayApiException - * @throws ReflectionException - * @throws RuntimeException - */ - public function cancelShouldReturnAuthorizationCancellationObjectIfNoChargeCancellationsExist() - { - $paymentMock = $this->getMockBuilder(Payment::class) - ->setMethods(['cancelAllCharges', 'cancelAuthorization'])->getMock(); - $cancellation = new Cancellation(2.0); - $exception1 = new HeidelpayApiException('', '', ApiResponseCodes::API_ERROR_CHARGE_ALREADY_CHARGED_BACK); - $exception2 = new HeidelpayApiException('', '', ApiResponseCodes::API_ERROR_CHARGE_ALREADY_CHARGED_BACK); - $exception3 = new HeidelpayApiException('', '', ApiResponseCodes::API_ERROR_CHARGE_ALREADY_CHARGED_BACK); - $paymentMock->expects($this->once())->method('cancelAllCharges') - ->willReturn([[], [$exception1, $exception2]]); - $paymentMock->expects($this->once())->method('cancelAuthorization') - ->willReturn([[$cancellation], [$exception3]]); - - /** @var Payment $paymentMock */ - $this->assertSame($cancellation, $paymentMock->cancel()); - } - - /** - * Verify payment:cancel throws first charge exception if all charges and auth have already been cancelled. - * - * @test - * - * @throws ReflectionException - * @throws RuntimeException - */ - public function cancelShouldThrowFirstChargeAlreadyCancelledExceptionIfNoCancellationTookPlace() - { - $paymentMock = $this->getMockBuilder(Payment::class) - ->setMethods(['cancelAllCharges', 'cancelAuthorization'])->getMock(); - $exception1 = new HeidelpayApiException('', '', ApiResponseCodes::API_ERROR_CHARGE_ALREADY_CHARGED_BACK); - $exception2 = new HeidelpayApiException('', '', ApiResponseCodes::API_ERROR_CHARGE_ALREADY_CHARGED_BACK); - $exception3 = new HeidelpayApiException('', '', ApiResponseCodes::API_ERROR_CHARGE_ALREADY_CHARGED_BACK); - $paymentMock->expects($this->once())->method('cancelAllCharges')->willReturn([[], [$exception1, $exception2]]); - $paymentMock->expects($this->once())->method('cancelAuthorization')->willReturn([[], [$exception3]]); - - try { - /** @var Payment $paymentMock */ - $paymentMock->cancel(); - $this->assertFalse(true, 'The expected exception has not been thrown.'); - } catch (HeidelpayApiException $e) { - $this->assertSame($exception1, $e); - } - } - - /** - * Verify payment:cancel throws auth exception if no charges existed and the authorization was already cancelled. - * - * @test - * - * @throws ReflectionException - * @throws RuntimeException - */ - public function cancelShouldThrowAuthAlreadyCancelledExceptionIfNoCancellationTookPlaceAndNoChargeExceptionExists() - { - $paymentMock = $this->getMockBuilder(Payment::class) - ->setMethods(['cancelAllCharges', 'cancelAuthorization'])->getMock(); - $exception = new HeidelpayApiException('', '', ApiResponseCodes::API_ERROR_CHARGE_ALREADY_CHARGED_BACK); - $paymentMock->expects($this->once())->method('cancelAllCharges')->willReturn([[], []]); - $paymentMock->expects($this->once())->method('cancelAuthorization')->willReturn([[], [$exception]]); - - try { - /** @var Payment $paymentMock */ - $paymentMock->cancel(); - $this->assertFalse(true, 'The expected exception has not been thrown.'); - } catch (HeidelpayApiException $e) { - $this->assertSame($exception, $e); - } - } - - /** - * Verify payment:cancel throws Exception if no cancellation and no auth existed to be cancelled. - * - * @test - * - * @throws ReflectionException - * @throws RuntimeException - * @throws HeidelpayApiException - */ - public function cancelShouldThrowExceptionIfNoTransactionExistsToBeCancelled() - { - $paymentMock = $this->getMockBuilder(Payment::class) - ->setMethods(['cancelAllCharges', 'cancelAuthorization'])->getMock(); - $paymentMock->expects($this->once())->method('cancelAllCharges')->willReturn([[], []]); - $paymentMock->expects($this->once())->method('cancelAuthorization')->willReturn([[], []]); - - $this->expectException(RuntimeException::class); - $this->expectExceptionMessage('This Payment could not be cancelled.'); - - /** @var Payment $paymentMock */ - $paymentMock->cancel(); - } - - /** - * Verify cancel all charges will call cancel on each existing charge of the payment and will return a list of - * cancels and exceptions. - * - * @test - * - * @throws HeidelpayApiException - * @throws ReflectionException - * @throws RuntimeException - */ - public function cancelAllChargesShouldCallCancelOnAllChargesAndReturnCancelsAndExceptions() - { - $cancellation1 = new Cancellation(1.0); - $cancellation2 = new Cancellation(2.0); - $cancellation3 = new Cancellation(3.0); - $exception1 = new HeidelpayApiException('', '', ApiResponseCodes::API_ERROR_CHARGE_ALREADY_CHARGED_BACK); - $exception2 = new HeidelpayApiException('', '', ApiResponseCodes::API_ERROR_CHARGE_ALREADY_CHARGED_BACK); - - $chargeMock1 = $this->getMockBuilder(Charge::class)->setMethods(['cancel'])->getMock(); - $chargeMock1->expects($this->once())->method('cancel')->willReturn($cancellation1); - - $chargeMock2 = $this->getMockBuilder(Charge::class)->setMethods(['cancel'])->getMock(); - $chargeMock2->expects($this->once())->method('cancel')->willThrowException($exception1); - - $chargeMock3 = $this->getMockBuilder(Charge::class)->setMethods(['cancel'])->getMock(); - $chargeMock3->expects($this->once())->method('cancel')->willReturn($cancellation2); - - $chargeMock4 = $this->getMockBuilder(Charge::class)->setMethods(['cancel'])->getMock(); - $chargeMock4->expects($this->once())->method('cancel')->willThrowException($exception2); - - $chargeMock5 = $this->getMockBuilder(Charge::class)->setMethods(['cancel'])->getMock(); - $chargeMock5->expects($this->once())->method('cancel')->willReturn($cancellation3); - - /** - * @var Charge $chargeMock1 - * @var Charge $chargeMock2 - * @var Charge $chargeMock3 - * @var Charge $chargeMock4 - * @var Charge $chargeMock5 - */ - $payment = new Payment(); - $payment->addCharge($chargeMock1) - ->addCharge($chargeMock2) - ->addCharge($chargeMock3) - ->addCharge($chargeMock4) - ->addCharge($chargeMock5); - - list($cancellations, $exceptions) = $payment->cancelAllCharges(); - $this->assertArraySubset([$cancellation1, $cancellation2, $cancellation3], $cancellations); - $this->assertArraySubset([$exception1, $exception2], $exceptions); - } - - /** - * Verify cancelAllCharges will throw any erxception with Code different to - * ApiResponseCodes::API_ERROR_CHARGE_ALREADY_CANCELED. - * - * @test - * - * @throws ReflectionException - * @throws RuntimeException - */ - public function cancelAllChargesShouldThrowChargeCancelExceptionsOtherThanAlreadyCharged() - { - $ex1 = new HeidelpayApiException('', '', ApiResponseCodes::API_ERROR_CHARGE_ALREADY_CHARGED_BACK); - $ex2 = new HeidelpayApiException('', '', ApiResponseCodes::API_ERROR_CHARGED_AMOUNT_HIGHER_THAN_EXPECTED); - - $chargeMock1 = $this->getMockBuilder(Charge::class)->setMethods(['cancel'])->getMock(); - $chargeMock1->expects($this->once())->method('cancel')->willThrowException($ex1); - - $chargeMock2 = $this->getMockBuilder(Charge::class)->setMethods(['cancel'])->getMock(); - $chargeMock2->expects($this->once())->method('cancel')->willThrowException($ex2); - - /** - * @var Charge $chargeMock1 - * @var Charge $chargeMock2 - */ - $payment = (new Payment())->addCharge($chargeMock1)->addCharge($chargeMock2); - - try { - $payment->cancelAllCharges(); - $this->assertFalse(true, 'The expected exception has not been thrown.'); - } catch (HeidelpayApiException $e) { - $this->assertSame($ex2, $e); - } - } - - /** - * Verify cancelAuthorization will call cancel on the authorization and will return a list of cancels. - * - * @test - * - * @throws HeidelpayApiException - * @throws ReflectionException - * @throws RuntimeException - */ - public function cancelAuthorizationShouldCallCancelOnTheAuthorizationAndReturnCancels() - { - $cancellation = new Cancellation(1.0); - $authorizationMock = $this->getMockBuilder(Authorization::class)->setMethods(['cancel'])->getMock(); - $authorizationMock->expects($this->once())->method('cancel')->willReturn($cancellation); - - $paymentMock = $this->getMockBuilder(Payment::class)->setMethods(['getAuthorization'])->getMock(); - $paymentMock->expects($this->once())->method('getAuthorization')->willReturn($authorizationMock); - - /** - * @var Authorization $authorizationMock - * @var Payment $paymentMock - */ - $paymentMock->setAuthorization($authorizationMock); - list($cancellations, $exceptions) = $paymentMock->cancelAuthorization(); - $this->assertArraySubset([$cancellation], $cancellations); - $this->assertIsEmptyArray($exceptions); - } - - /** - * Verify cancelAuthorization will call cancel on the authorization and will return a list of exceptions. - * - * @test - * - * @throws HeidelpayApiException - * @throws ReflectionException - * @throws RuntimeException - */ - public function cancelAuthorizationShouldCallCancelOnTheAuthorizationAndReturnExceptions() - { - $exception = new HeidelpayApiException('', '', ApiResponseCodes::API_ERROR_AUTHORIZE_ALREADY_CANCELLED); - - $authorizationMock = $this->getMockBuilder(Authorization::class)->setMethods(['cancel'])->getMock(); - $authorizationMock->expects($this->once())->method('cancel')->willThrowException($exception); - - $paymentMock = $this->getMockBuilder(Payment::class)->setMethods(['getAuthorization'])->getMock(); - $paymentMock->expects($this->once())->method('getAuthorization')->willReturn($authorizationMock); - - /** - * @var Authorization $authorizationMock - * @var Payment $paymentMock - */ - $paymentMock->setAuthorization($authorizationMock); - list($cancellations, $exceptions) = $paymentMock->cancelAuthorization(); - - $this->assertIsEmptyArray($cancellations); - $this->assertArraySubset([$exception], $exceptions); - } - - /** - * Verify cancelAuthorization will throw any exception with Code different to - * ApiResponseCodes::API_ERROR_AUTHORIZATION_ALREADY_CANCELED. - * - * @test - * - * @throws ReflectionException - * @throws RuntimeException - */ - public function cancelAllChargesShouldThrowAuthorizationCancelExceptionsOtherThanAlreadyCharged() - { - $exception = new HeidelpayApiException('', '', ApiResponseCodes::API_ERROR_CHARGED_AMOUNT_HIGHER_THAN_EXPECTED); - - $authorizationMock = $this->getMockBuilder(Authorization::class)->setMethods(['cancel'])->getMock(); - $authorizationMock->expects($this->once())->method('cancel')->willThrowException($exception); - - $paymentMock = $this->getMockBuilder(Payment::class)->setMethods(['getAuthorization'])->getMock(); - $paymentMock->expects($this->once())->method('getAuthorization')->willReturn($authorizationMock); - - /** - * @var Authorization $authorizationMock - * @var Payment $paymentMock - */ - $paymentMock->setAuthorization($authorizationMock); - - try { - $paymentMock->cancelAuthorization(); - $this->assertFalse(true, 'The expected exception has not been thrown.'); - } catch (HeidelpayApiException $e) { - $this->assertSame($exception, $e); - } - } - - //</editor-fold> - /** * Verify charge will call chargePayment on heidelpay object. * @@ -1649,8 +1359,7 @@ public function setMetaDataShouldSetParentResourceAndCreateMetaDataObject() { $metadata = (new Metadata())->addMetadata('myData', 'myValue'); - $resourceSrvMock = $this->getMockBuilder(ResourceService::class)->setMethods(['create']) - ->disableOriginalConstructor()->getMock(); + $resourceSrvMock = $this->getMockBuilder(ResourceService::class)->setMethods(['create'])->disableOriginalConstructor()->getMock(); $resourceSrvMock->expects($this->once())->method('create')->with($metadata); /** @var ResourceService $resourceSrvMock */ @@ -1669,6 +1378,43 @@ public function setMetaDataShouldSetParentResourceAndCreateMetaDataObject() $this->assertSame($heidelpay, $metadata->getParentResource()); } + /** + * Verify setMetadata will not set the metadata property if it is not of type metadata. + * + * @test + * + * @throws HeidelpayApiException + * @throws RuntimeException + * @throws ReflectionException + */ + public function metadataMustBeOfTypeMetadata() + { + $metadata = new Metadata(); + $resourceSrvMock = $this->getMockBuilder(ResourceService::class)->setMethods(['create'])->disableOriginalConstructor()->getMock(); + $resourceSrvMock->expects($this->once())->method('create')->with($metadata); + /** @var ResourceService $resourceSrvMock */ + $heidelpay = (new Heidelpay('s-priv-1234'))->setResourceService($resourceSrvMock); + + // when + $payment = new Payment($heidelpay); + + // then + $this->assertNull($payment->getMetadata()); + + // when + /** @noinspection PhpParamsInspection */ + $payment->setMetadata('test'); + + // then + $this->assertNull($payment->getMetadata()); + + // when + $payment->setMetadata($metadata); + + // then + $this->assertSame($metadata, $payment->getMetadata()); + } + /** * Verify set Basket will call create if the given basket object does not exist yet. * diff --git a/test/unit/Resources/TransactionTypes/AuthorizationTest.php b/test/unit/Resources/TransactionTypes/AuthorizationTest.php index f03fa8de..3ba3f186 100755 --- a/test/unit/Resources/TransactionTypes/AuthorizationTest.php +++ b/test/unit/Resources/TransactionTypes/AuthorizationTest.php @@ -2,7 +2,7 @@ /** * This class defines unit tests to verify functionality of the Authorization transaction type. * - * Copyright (C) 2018 heidelpay GmbH + * Copyright (C) 2019 heidelpay GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -54,19 +54,22 @@ public function gettersAndSettersShouldWorkProperly() $this->assertNull($authorization->getCurrency()); $this->assertNull($authorization->getReturnUrl()); $this->assertNull($authorization->isCard3ds()); + $this->assertNull($authorization->getPaymentReference()); $authorization = new Authorization(123.4, 'myCurrency', 'https://my-return-url.test'); - $authorization->setCard3ds(true); + $authorization->setCard3ds(true)->setPaymentReference('my payment reference'); $this->assertEquals(123.4, $authorization->getAmount()); $this->assertEquals('myCurrency', $authorization->getCurrency()); $this->assertEquals('https://my-return-url.test', $authorization->getReturnUrl()); + $this->assertEquals('my payment reference', $authorization->getPaymentReference()); $this->assertTrue($authorization->isCard3ds()); $authorization->setAmount(567.8)->setCurrency('myNewCurrency')->setReturnUrl('https://another-return-url.test'); - $authorization->setCard3ds(false); + $authorization->setCard3ds(false)->setPaymentReference('different payment reference'); $this->assertEquals(567.8, $authorization->getAmount()); $this->assertEquals('myNewCurrency', $authorization->getCurrency()); $this->assertEquals('https://another-return-url.test', $authorization->getReturnUrl()); + $this->assertEquals('different payment reference', $authorization->getPaymentReference()); $this->assertFalse($authorization->isCard3ds()); } @@ -221,6 +224,30 @@ public function chargeShouldCallChargeAuthorizationOnHeidelpayObject() $authorization->charge(321.9); } + /** + * Verify getter for cancelled amount. + * + * @test + * + * @throws Exception + */ + public function getCancelledAmountReturnsTheCancelledAmount() + { + $authorization = new Authorization(); + $this->assertEquals(0.0, $authorization->getCancelledAmount()); + + $authorization = new Authorization(123.4, 'myCurrency', 'https://my-return-url.test'); + $this->assertEquals(0.0, $authorization->getCancelledAmount()); + + $cancellation1 = new Cancellation(10.0); + $authorization->addCancellation($cancellation1); + $this->assertEquals(10.0, $authorization->getCancelledAmount()); + + $cancellation2 = new Cancellation(10.0); + $authorization->addCancellation($cancellation2); + $this->assertEquals(20.0, $authorization->getCancelledAmount()); + } + //<editor-fold desc="Data Providers"> /** diff --git a/test/unit/Resources/TransactionTypes/ChargeTest.php b/test/unit/Resources/TransactionTypes/ChargeTest.php index 506d8250..f524411b 100755 --- a/test/unit/Resources/TransactionTypes/ChargeTest.php +++ b/test/unit/Resources/TransactionTypes/ChargeTest.php @@ -33,6 +33,7 @@ use heidelpayPHP\Resources\TransactionTypes\Charge; use heidelpayPHP\test\BaseUnitTest; use PHPUnit\Framework\Exception; +use PHPUnit\Framework\MockObject\MockObject; use ReflectionException; use RuntimeException; use stdClass; @@ -185,4 +186,53 @@ public function cancelShouldCallCancelChargeOnHeidelpayObject() $charge->cancel(); $charge->cancel(321.9); } + + /** + * Verify getter for cancelled amount. + * + * @test + * + * @throws Exception + */ + public function getCancelledAmountReturnsTheCancelledAmount() + { + $charge = new Charge(); + $this->assertEquals(0.0, $charge->getCancelledAmount()); + + $charge = new Charge(123.4, 'myCurrency', 'https://my-return-url.test'); + $this->assertEquals(0.0, $charge->getCancelledAmount()); + + $cancellation1 = new Cancellation(10.0); + $charge->addCancellation($cancellation1); + $this->assertEquals(10.0, $charge->getCancelledAmount()); + + $cancellation2 = new Cancellation(10.0); + $charge->addCancellation($cancellation2); + $this->assertEquals(20.0, $charge->getCancelledAmount()); + } + + /** + * Verify getter for total amount. + * + * @test + * + * @throws Exception + * @throws ReflectionException + * @throws \PHPUnit\Framework\MockObject\RuntimeException + */ + public function getTotalAmountReturnsAmountMinusCancelledAmount() + { + /** @var MockObject|Charge $chargeMock */ + $chargeMock = $this->getMockBuilder(Charge::class) + ->setMethods(['getCancelledAmount']) + ->setConstructorArgs([123.4, 'myCurrency', 'https://my-return-url.test']) + ->getMock(); + + $chargeMock->expects($this->exactly(3))->method('getCancelledAmount') + ->willReturnOnConsecutiveCalls(0.0, 100.0, 123.4); + + $this->assertEquals(123.4, $chargeMock->getTotalAmount()); + $this->assertEquals(23.4, $chargeMock->getTotalAmount()); + $this->assertEquals(0.0, $chargeMock->getTotalAmount()); + } } diff --git a/test/unit/Resources/TransactionTypes/PayoutTest.php b/test/unit/Resources/TransactionTypes/PayoutTest.php index 3fa655a0..c144c785 100644 --- a/test/unit/Resources/TransactionTypes/PayoutTest.php +++ b/test/unit/Resources/TransactionTypes/PayoutTest.php @@ -49,16 +49,21 @@ public function gettersAndSettersShouldWorkProperly() $this->assertNull($payout->getAmount()); $this->assertNull($payout->getCurrency()); $this->assertNull($payout->getReturnUrl()); + $this->assertNull($payout->getPaymentReference()); $payout = new Payout(123.4, 'myCurrency', 'https://my-return-url.test'); + $payout->setPaymentReference('my payment reference'); $this->assertEquals(123.4, $payout->getAmount()); $this->assertEquals('myCurrency', $payout->getCurrency()); $this->assertEquals('https://my-return-url.test', $payout->getReturnUrl()); + $this->assertEquals('my payment reference', $payout->getPaymentReference()); $payout->setAmount(567.8)->setCurrency('myNewCurrency')->setReturnUrl('https://another-return-url.test'); + $payout->setPaymentReference('different payment reference'); $this->assertEquals(567.8, $payout->getAmount()); $this->assertEquals('myNewCurrency', $payout->getCurrency()); $this->assertEquals('https://another-return-url.test', $payout->getReturnUrl()); + $this->assertEquals('different payment reference', $payout->getPaymentReference()); } /** diff --git a/test/unit/Services/HttpServiceTest.php b/test/unit/Services/HttpServiceTest.php index c10749e1..cd012d15 100755 --- a/test/unit/Services/HttpServiceTest.php +++ b/test/unit/Services/HttpServiceTest.php @@ -280,7 +280,7 @@ public function handleErrorsShouldThrowExceptionIfResponseCodeIsGoE400($response $this->expectException(HeidelpayApiException::class); $this->expectExceptionMessage('The payment api returned an error!'); - $this->expectExceptionCode(''); + $this->expectExceptionCode('No error code provided'); /** @var HttpService $httpServiceMock*/ $httpServiceMock->send('/my/uri/123', $resource); @@ -297,7 +297,6 @@ public function handleErrorsShouldThrowExceptionIfResponseCodeIsGoE400($response public function handleErrorsShouldThrowExceptionIfResponseContainsErrorField() { $httpServiceMock = $this->getMockBuilder(HttpService::class)->setMethods(['getAdapter'])->getMock(); - $adapterMock = $this->getMockBuilder(CurlAdapter::class)->setMethods( ['init', 'setUserAgent', 'setHeaders', 'execute', 'getResponseCode', 'close'] )->getMock(); @@ -306,13 +305,10 @@ public function handleErrorsShouldThrowExceptionIfResponseContainsErrorField() $secondResponse = '{"errors": [{"merchantMessage": "This is an error message for the merchant!"}]}'; $thirdResponse = '{"errors": [{"customerMessage": "This is an error message for the customer!"}]}'; $fourthResponse = '{"errors": [{"code": "This is the error code!"}]}'; + $fifthResponse = '{"errors": [{"code": "This is the error code!"}], "id": "s-err-1234"}'; + $sixthResponse = '{"errors": [{"code": "This is the error code!"}], "id": "s-rre-1234"}'; - $adapterMock->method('execute')->willReturnOnConsecutiveCalls( - $firstResponse, - $secondResponse, - $thirdResponse, - $fourthResponse - ); + $adapterMock->method('execute')->willReturnOnConsecutiveCalls($firstResponse, $secondResponse, $thirdResponse, $fourthResponse, $fifthResponse, $sixthResponse); $httpServiceMock->method('getAdapter')->willReturn($adapterMock); $resource = (new DummyResource())->setParentResource(new Heidelpay('s-priv-MyTestKey')); @@ -324,7 +320,8 @@ public function handleErrorsShouldThrowExceptionIfResponseContainsErrorField() } catch (HeidelpayApiException $e) { $this->assertEquals('The payment api returned an error!', $e->getMerchantMessage()); $this->assertEquals('The payment api returned an error!', $e->getClientMessage()); - $this->assertEmpty($e->getCode()); + $this->assertEquals('No error code provided', $e->getCode()); + $this->assertEquals('No error id provided', $e->getErrorId()); } try { @@ -333,7 +330,8 @@ public function handleErrorsShouldThrowExceptionIfResponseContainsErrorField() } catch (HeidelpayApiException $e) { $this->assertEquals('This is an error message for the merchant!', $e->getMerchantMessage()); $this->assertEquals('The payment api returned an error!', $e->getClientMessage()); - $this->assertEmpty($e->getCode()); + $this->assertEquals('No error code provided', $e->getCode()); + $this->assertEquals('No error id provided', $e->getErrorId()); } try { @@ -342,7 +340,8 @@ public function handleErrorsShouldThrowExceptionIfResponseContainsErrorField() } catch (HeidelpayApiException $e) { $this->assertEquals('The payment api returned an error!', $e->getMerchantMessage()); $this->assertEquals('This is an error message for the customer!', $e->getClientMessage()); - $this->assertEmpty($e->getCode()); + $this->assertEquals('No error code provided', $e->getCode()); + $this->assertEquals('No error id provided', $e->getErrorId()); } try { @@ -352,6 +351,27 @@ public function handleErrorsShouldThrowExceptionIfResponseContainsErrorField() $this->assertEquals('The payment api returned an error!', $e->getMerchantMessage()); $this->assertEquals('The payment api returned an error!', $e->getClientMessage()); $this->assertEquals('This is the error code!', $e->getCode()); + $this->assertEquals('No error id provided', $e->getErrorId()); + } + + try { + $httpServiceMock->send('/my/uri/123', $resource); + $this->assertTrue(false, 'The fifth exception should have been thrown!'); + } catch (HeidelpayApiException $e) { + $this->assertEquals('The payment api returned an error!', $e->getMerchantMessage()); + $this->assertEquals('The payment api returned an error!', $e->getClientMessage()); + $this->assertEquals('This is the error code!', $e->getCode()); + $this->assertEquals('s-err-1234', $e->getErrorId()); + } + + try { + $httpServiceMock->send('/my/uri/123', $resource); + $this->assertTrue(false, 'The sixth exception should have been thrown!'); + } catch (HeidelpayApiException $e) { + $this->assertEquals('The payment api returned an error!', $e->getMerchantMessage()); + $this->assertEquals('The payment api returned an error!', $e->getClientMessage()); + $this->assertEquals('This is the error code!', $e->getCode()); + $this->assertEquals('No error id provided', $e->getErrorId()); } } diff --git a/test/unit/Services/ResourceServiceTest.php b/test/unit/Services/ResourceServiceTest.php index 6fcb4249..b646736f 100755 --- a/test/unit/Services/ResourceServiceTest.php +++ b/test/unit/Services/ResourceServiceTest.php @@ -407,7 +407,6 @@ public function fetchPaymentByOrderIdShouldCreatePaymentObjectWithOrderIdAndCall static function ($payment) use ($heidelpay) { return $payment instanceof Payment && $payment->getOrderId() === 'myOrderId' && - $payment->getId() === 'myOrderId' && $payment->getHeidelpayObject() === $heidelpay; }));