diff --git a/CHANGELOG.md b/CHANGELOG.md
index ef6797a1..409acb52 100755
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,43 +3,60 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
-## [1.1.1.0][1.1.1.0]
+## [1.1.1.1]
+
+### Fix
+* Change debug logging of failed tests that depend on another one to work as expected.
+* PayPal recurring example: Response handling changed to check the recurring status of the payment type.
+
+### Added
+* Extended testing for Instalment payment type.
+* Cards (extended) example using email UI element.
+
+### Changed
+* Remove PhpUnit 8 support.
+* Card recurring example using email UI element.
+* Card example and paypage examples use a dummy customer-email to ensure they work with 3ds2.
+* Several minor changes.
+
+## [1.1.1.0]
### Changed
-* Add email property to payment type `card` to meet 3Ds2.x regulations.
-* Several minor changes.
+* Add email property to payment type `card` to meet 3Ds2.x regulations.
+* Several minor changes.
-## [1.1.0.0][1.1.0.0]
+## [1.1.0.0]
### Changed
-* Rebranding of the SDK.
-* Removed payment type string from URL when fetching a payment type resource.
-* Replace payment methods guaranteed/factoring by secured payment methods, i.e.:
- * `InvoiceGuaranteed` and `InvoiceFactoring` replaced by `InvoiceSecured`
- * `SepaDirectDebitGuaranteed` replaced by `SepaDirectDebitSecured`
- * `HirePurchaseDirectDebit` replaced by `InstallmentSecured`
- * Basket is now mandatory for all those payment types above.
-* Added mapping of old payment type ids to the new payment type resources.
-* Constant in `\UnzerSDK\Constants\ApiResponseCodes` got renamed:
- * `API_ERROR_IVF_REQUIRES_CUSTOMER` renamed to `API_ERROR_FACTORING_REQUIRES_CUSTOMER`.
- * `API_ERROR_IVF_REQUIRES_BASKET` renamed to `API_ERROR_FACTORING_REQUIRES_BASKET`.
-* Several minor changes.
+* Rebranding of the SDK.
+* Removed payment type string from URL when fetching a payment type resource.
+* Replace payment methods guaranteed/factoring by secured payment methods, i.e.:
+ * `InvoiceGuaranteed` and `InvoiceFactoring` replaced by `InvoiceSecured`
+ * `SepaDirectDebitGuaranteed` replaced by `SepaDirectDebitSecured`
+ * `HirePurchaseDirectDebit` replaced by `InstallmentSecured`
+ * Basket is now mandatory for all those payment types above.
+* Added mapping of old payment type ids to the new payment type resources.
+* Constant in `\UnzerSDK\Constants\ApiResponseCodes` got renamed:
+ * `API_ERROR_IVF_REQUIRES_CUSTOMER` renamed to `API_ERROR_FACTORING_REQUIRES_CUSTOMER`.
+ * `API_ERROR_IVF_REQUIRES_BASKET` renamed to `API_ERROR_FACTORING_REQUIRES_BASKET`.
+* Several minor changes.
### Remove
-* Remove deprecated methods:
- * getAmountTotal
- * setAmountTotal
- * getCardHolder
- * setHolder
- * cancel
- * cancelAllCharges
- * cancelAuthorization
- * getResource
- * fetchResource
-* Remove deprecated constants:
- * API_ERROR_AUTHORIZE_ALREADY_CANCELLED
- * API_ERROR_CHARGE_ALREADY_CHARGED_BACK
- * API_ERROR_BASKET_ITEM_IMAGE_INVALID_EXTENSION
- * ENV_VAR_NAME_DISABLE_TEST_LOGGING
+* Remove deprecated methods:
+ * getAmountTotal
+ * setAmountTotal
+ * getCardHolder
+ * setHolder
+ * cancel
+ * cancelAllCharges
+ * cancelAuthorization
+ * getResource
+ * fetchResource
+* Remove deprecated constants:
+ * API_ERROR_AUTHORIZE_ALREADY_CANCELLED
+ * API_ERROR_CHARGE_ALREADY_CHARGED_BACK
+ * API_ERROR_BASKET_ITEM_IMAGE_INVALID_EXTENSION
+ * ENV_VAR_NAME_DISABLE_TEST_LOGGING
[1.1.0.0]: https://github.com/unzerdev/php-sdk/compare/1260b8314af1ac461e33f0cfb382ffcd0e87c105..1.1.0.0
[1.1.1.0]: https://github.com/unzerdev/php-sdk/compare/1.1.0.0..1.1.1.0
+[1.1.1.1]: https://github.com/unzerdev/php-sdk/compare/1.1.1.0..1.1.1.1
diff --git a/composer.json b/composer.json
index c9927508..56c11db6 100755
--- a/composer.json
+++ b/composer.json
@@ -8,7 +8,7 @@
"ext-json": "*"
},
"require-dev": {
- "phpunit/phpunit": ">6.5 <9.0",
+ "phpunit/phpunit": ">6.5 <8.0",
"friendsofphp/php-cs-fixer": "^2.0"
},
"suggest": {
diff --git a/examples/Card/Controller.php b/examples/Card/Controller.php
index 28d599a7..60f79664 100644
--- a/examples/Card/Controller.php
+++ b/examples/Card/Controller.php
@@ -70,6 +70,8 @@ function redirect($url, $merchantMessage = '', $clientMessage = '')
// The 3D secured flag can be used to switch between 3ds and non-3ds.
// If your merchant is only configured for one of those you can omit the flag.
$customer = CustomerFactory::createCustomer('Max', 'Mustermann');
+ $customer->setEmail('test@test.com');
+
switch ($transactionType) {
case 'charge':
$transaction = $unzer->charge(12.99, 'EUR', $paymentTypeId, RETURN_CONTROLLER_URL, $customer, null, null, null, $use3Ds);
diff --git a/examples/CardExtended/Constants.php b/examples/CardExtended/Constants.php
new file mode 100644
index 00000000..fc0ac781
--- /dev/null
+++ b/examples/CardExtended/Constants.php
@@ -0,0 +1,29 @@
+
+ *
+ * @package UnzerSDK\examples
+ */
+
+require_once __DIR__ . '/../Constants.php';
+
+define('EXAMPLE_URL', EXAMPLE_BASE_FOLDER . 'CardExtended');
+define('CONTROLLER_URL', EXAMPLE_URL . '/Controller.php');
diff --git a/examples/CardExtended/Controller.php b/examples/CardExtended/Controller.php
new file mode 100644
index 00000000..ecc18524
--- /dev/null
+++ b/examples/CardExtended/Controller.php
@@ -0,0 +1,112 @@
+
+ *
+ * @package UnzerSDK\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 UnzerSDK\examples\ExampleDebugHandler;
+use UnzerSDK\Exceptions\UnzerApiException;
+use UnzerSDK\Unzer;
+use UnzerSDK\Resources\CustomerFactory;
+
+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();
+}
+
+// You will need the id of the payment type created in the frontend (index.php)
+if (!isset($_POST['resourceId'])) {
+ redirect(FAILURE_URL, 'Resource id is missing!', $clientMessage);
+}
+$paymentTypeId = $_POST['resourceId'];
+
+// These lines are just for this example
+$transactionType = $_POST['transaction_type'] ?? 'authorize';
+
+// Catch API errors, write the message to your log and show the ClientMessage to the client.
+try {
+ // Create an Unzer object using your private key and register a debug handler if you want to.
+ $unzer = new Unzer(UNZER_PAPI_PRIVATE_KEY);
+ $unzer->setDebugMode(true)->setDebugHandler(new ExampleDebugHandler());
+
+ // Create a charge/authorize transaction
+ // For 3Ds2 compliance an email need to be set either in card type or in customer resource.
+ // If your merchant is only configured for one of those you can omit the flag.
+ $customer = CustomerFactory::createCustomer('Max', 'Mustermann');
+ switch ($transactionType) {
+ case 'charge':
+ $transaction = $unzer->charge(12.99, 'EUR', $paymentTypeId, RETURN_CONTROLLER_URL, $customer, null, null, null, true);
+ break;
+ case 'payout':
+ $transaction = $unzer->payout(12.99, 'EUR', $paymentTypeId, RETURN_CONTROLLER_URL, $customer);
+ break;
+ default:
+ $transaction = $unzer->authorize(12.99, 'EUR', $paymentTypeId, RETURN_CONTROLLER_URL, $customer, null, null, null, true);
+ break;
+ }
+
+ // You'll need to remember the paymentId for later in the ReturnController (in case of 3ds)
+ $_SESSION['PaymentId'] = $transaction->getPaymentId();
+ $_SESSION['ShortId'] = $transaction->getShortId();
+
+ // Redirect to the 3ds page or to success depending on the state of the transaction
+ $payment = $transaction->getPayment();
+ $redirect = !empty($transaction->getRedirectUrl());
+
+ switch (true) {
+ case (!$redirect && $transaction->isSuccess()):
+ redirect(SUCCESS_URL);
+ break;
+ case (!$redirect && $transaction->isPending()):
+ redirect(PENDING_URL);
+ break;
+ case ($redirect && $transaction->isPending()):
+ redirect($transaction->getRedirectUrl());
+ break;
+ }
+
+ // Check the result message of the transaction to find out what went wrong.
+ $merchantMessage = $transaction->getMessage()->getCustomer();
+} catch (UnzerApiException $e) {
+ $merchantMessage = $e->getMerchantMessage();
+ $clientMessage = $e->getClientMessage();
+} catch (RuntimeException $e) {
+ $merchantMessage = $e->getMessage();
+}
+redirect(FAILURE_URL, $merchantMessage, $clientMessage);
diff --git a/examples/CardExtended/index.php b/examples/CardExtended/index.php
new file mode 100644
index 00000000..1acf4d15
--- /dev/null
+++ b/examples/CardExtended/index.php
@@ -0,0 +1,200 @@
+
+ *
+ * @package UnzerSDK\examples
+ */
+
+/** Require the constants of this example */
+require_once __DIR__ . '/Constants.php';
+
+/** @noinspection PhpIncludeInspection */
+/** Require the composer autoloader file */
+require_once __DIR__ . '/../../../../autoload.php';
+?>
+
+
+
+
@@ -102,6 +105,10 @@
containerId: 'card-element-id-cvc',
onlyIframe: false
});
+ Card.create('email', {
+ containerId: 'card-element-id-email',
+ onlyIframe: false
+ });
// General event handling
let formFieldValid = {};
@@ -119,7 +126,7 @@
formFieldValid[e.type] = false;
$errorHolder.html(e.error)
}
- payButton.disabled = !(formFieldValid.number && formFieldValid.expiry && formFieldValid.cvc);
+ payButton.disabled = !(formFieldValid.number && formFieldValid.expiry && formFieldValid.cvc && formFieldValid.email);
};
Card.addEventListener('change', eventHandlerCardInput);
diff --git a/examples/EmbeddedPayPage/Controller.php b/examples/EmbeddedPayPage/Controller.php
index d1ba78ef..937b71ec 100644
--- a/examples/EmbeddedPayPage/Controller.php
+++ b/examples/EmbeddedPayPage/Controller.php
@@ -59,6 +59,7 @@
// Create a charge/authorize transaction
$customer = CustomerFactory::createCustomer('Max', 'Mustermann');
+ $customer->setEmail('test@test.com');
// These are the mandatory parameters for the payment page ...
$paypage = new Paypage(119.00, 'EUR', RETURN_CONTROLLER_URL);
diff --git a/examples/HostedPayPage/Controller.php b/examples/HostedPayPage/Controller.php
index f8a275a7..f14ba82c 100644
--- a/examples/HostedPayPage/Controller.php
+++ b/examples/HostedPayPage/Controller.php
@@ -64,6 +64,7 @@ function redirect($url, $merchantMessage = '', $clientMessage = '')
// Create a charge/authorize transaction
$customer = CustomerFactory::createCustomer('Max', 'Mustermann');
+ $customer->setEmail('test@test.com');
// These are the mandatory parameters for the payment page ...
$paypage = new Paypage(119.0, 'EUR', RETURN_CONTROLLER_URL);
diff --git a/examples/InstallmentSecured/PlaceOrderController.php b/examples/InstallmentSecured/PlaceOrderController.php
index 615f7a23..998f359a 100644
--- a/examples/InstallmentSecured/PlaceOrderController.php
+++ b/examples/InstallmentSecured/PlaceOrderController.php
@@ -72,7 +72,7 @@ function redirect($url, $merchantMessage = '', $clientMessage = '')
}
// Check the result message of the transaction to find out what went wrong.
- $merchantMessage = $authorize->getMessage()->getCustomer();
+ $merchantMessage = $charge->getMessage()->getCustomer();
} catch (UnzerApiException $e) {
$merchantMessage = $e->getMerchantMessage();
$clientMessage = $e->getClientMessage();
diff --git a/examples/PayPalRecurring/Constants.php b/examples/PayPalRecurring/Constants.php
index d0fd6277..68f70111 100644
--- a/examples/PayPalRecurring/Constants.php
+++ b/examples/PayPalRecurring/Constants.php
@@ -27,3 +27,4 @@
define('EXAMPLE_URL', EXAMPLE_BASE_FOLDER . 'PayPalRecurring');
define('CONTROLLER_URL', EXAMPLE_URL . '/Controller.php');
+define('MY_RETURN_CONTROLLER_URL', EXAMPLE_URL . '/ReturnController.php');
diff --git a/examples/PayPalRecurring/Controller.php b/examples/PayPalRecurring/Controller.php
index 8fb8535d..990b2e60 100644
--- a/examples/PayPalRecurring/Controller.php
+++ b/examples/PayPalRecurring/Controller.php
@@ -61,7 +61,7 @@ function redirect($url, $merchantMessage = '', $clientMessage = '')
$unzer = new Unzer(UNZER_PAPI_PRIVATE_KEY);
$unzer->setDebugMode(true)->setDebugHandler(new ExampleDebugHandler());
- $recurring = $unzer->activateRecurringPayment($paymentTypeId, RETURN_CONTROLLER_URL);
+ $recurring = $unzer->activateRecurringPayment($paymentTypeId, MY_RETURN_CONTROLLER_URL);
// You'll need to remember the paymentId for later in the ReturnController (in case of 3ds)
$_SESSION['PaymentTypeId'] = $paymentTypeId;
diff --git a/examples/PayPalRecurring/ReturnController.php b/examples/PayPalRecurring/ReturnController.php
new file mode 100644
index 00000000..13beb304
--- /dev/null
+++ b/examples/PayPalRecurring/ReturnController.php
@@ -0,0 +1,79 @@
+
+ *
+ * @package UnzerSDK\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 UnzerSDK\examples\ExampleDebugHandler;
+use UnzerSDK\Exceptions\UnzerApiException;
+use UnzerSDK\Unzer;
+use UnzerSDK\Resources\PaymentTypes\Card;
+
+session_start();
+
+$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();
+}
+
+// Retrieve the paymentId you remembered within the Controller
+if (!isset($_SESSION['PaymentTypeId'])) {
+ redirect(FAILURE_URL, 'The payment type id is missing.', $clientMessage);
+}
+$paymentTypeId = $_SESSION['PaymentTypeId'];
+
+// Catch API errors, write the message to your log and show the ClientMessage to the client.
+try {
+ // Create an Unzer object using your private key and register a debug handler if you want to.
+ $unzer = new Unzer(UNZER_PAPI_PRIVATE_KEY);
+ $unzer->setDebugMode(true)->setDebugHandler(new ExampleDebugHandler());
+
+ // Redirect to success if the payment has been successfully completed or is still in handled.
+ /** @var Card $paymentType */
+ $paymentType = $unzer->fetchPaymentType($paymentTypeId);
+
+ if ($paymentType->isRecurring()) {
+ redirect(SUCCESS_URL);
+ }
+
+} catch (UnzerApiException $e) {
+ $merchantMessage = $e->getMerchantMessage();
+ $clientMessage = $e->getClientMessage();
+} catch (RuntimeException $e) {
+ $merchantMessage = $e->getMessage();
+}
+
+redirect(FAILURE_URL, $merchantMessage, $clientMessage);
diff --git a/examples/index.php b/examples/index.php
index 7dc8360f..1564cdcf 100755
--- a/examples/index.php
+++ b/examples/index.php
@@ -88,12 +88,26 @@ function printMessage($type, $title, $text)
Card
You can try authorize, charge and payout transactions with or without 3Ds.
+ This example submits email via customer resource.
Try
+
+
+
Card (extended)
+
+ Including email and holder fields.
+ Adding more information to the card can improve risk acceptance.
+ This example submits email via payment type resource.
+
+
+
+ Try
+
+
Card Recurring
diff --git a/phpunit.xml b/phpunit.xml
index d0d58a86..58ad4061 100755
--- a/phpunit.xml
+++ b/phpunit.xml
@@ -1,6 +1,6 @@
unzer->getDebugHandler();
+ $debugHandler = self::getDebugHandler();
if ($this->getStatus() === BaseTestRunner::STATUS_PASSED) {
$debugHandler->clearTempLog();
diff --git a/test/BasePaymentTest.php b/test/BasePaymentTest.php
index 91a28b90..69407b6a 100755
--- a/test/BasePaymentTest.php
+++ b/test/BasePaymentTest.php
@@ -52,6 +52,19 @@ class BasePaymentTest extends TestCase
/** @var Unzer $unzer */
protected $unzer;
+ protected static $debughandler;
+
+ /**
+ * @return TestDebugHandler
+ */
+ public static function getDebugHandler(): TestDebugHandler
+ {
+ if (!self::$debughandler instanceof TestDebugHandler) {
+ self::$debughandler = new TestDebugHandler();
+ }
+ return self::$debughandler;
+ }
+
/**
* Creates and returns an Unzer object if it does not exist yet.
* Uses an invalid but well formed default key if no key is given.
@@ -66,7 +79,7 @@ protected function getUnzerObject($privateKey = 's-priv-1234'): Unzer
{
if (!$this->unzer instanceof Unzer) {
$this->unzer = (new Unzer($privateKey))
- ->setDebugHandler(new TestDebugHandler())
+ ->setDebugHandler(self::getDebugHandler())
->setDebugMode(true);
}
return $this->unzer;
diff --git a/test/integration/PaymentTypes/InstallmentSecuredTest.php b/test/integration/PaymentTypes/InstallmentSecuredTest.php
index 72152aa3..4eb5f5c0 100644
--- a/test/integration/PaymentTypes/InstallmentSecuredTest.php
+++ b/test/integration/PaymentTypes/InstallmentSecuredTest.php
@@ -29,6 +29,7 @@
use UnzerSDK\Constants\ApiResponseCodes;
use UnzerSDK\Exceptions\UnzerApiException;
+use UnzerSDK\Resources\AbstractUnzerResource;
use UnzerSDK\Resources\Customer;
use UnzerSDK\Resources\CustomerFactory;
use UnzerSDK\Resources\EmbeddedResources\Address;
@@ -81,7 +82,7 @@ public function instalmentPlanShouldBeSelectable(): void
*
* @test
*/
- public function hddTypeShouldBeFechable(): InstallmentSecured
+ public function hddTypeShouldBeFetchable(): InstallmentSecured
{
// Mock a hdd Type
$date = $this->getTodaysDateString();
@@ -130,9 +131,11 @@ public function hddTypeShouldBeFechable(): InstallmentSecured
* Verify fetched hdd type can be authorized and charged
*
* @test
- * @depends hddTypeShouldBeFechable
+ * @depends hddTypeShouldBeFetchable
*
- * @param InvoiceSecured $hddType fetched ins type.
+ * @param InstallmentSecured $hddType fetched ins type.
+ *
+ * @return AbstractUnzerResource|Charge
*
* @throws UnzerApiException
*/
@@ -140,7 +143,7 @@ public function hddTypeAuthorizeAndCharge(InstallmentSecured $hddType)
{
$customer = $this->getMaximumCustomer();
$basket = $this->createBasket();
- /** @var Authorization $auth */
+
$auth = $hddType->authorize(119.00, 'EUR', 'https://unzer.com', $customer, null, null, $basket);
$charge = $auth->getPayment()->charge();
$this->assertNotNull($auth);
@@ -320,6 +323,67 @@ public function verifyPartlyCancelChargedInstallmentSecured(): void
$this->assertTrue($payment->isCompleted());
}
+ /**
+ * Verify full cancel of charged HP after shipment.
+ *
+ * @test
+ *
+ * @depends verifyChargingAnInitializedInstallmentSecured
+ */
+ public function verifyChargeAndFullCancelAnInitializedInstallmentSecuredAfterShipment(): void
+ {
+ $yesterday = $this->getYesterdaysTimestamp();
+ $plans = $this->unzer->fetchInstallmentPlans(119.0, 'EUR', 4.99, $yesterday);
+ $this->assertGreaterThan(0, count($plans->getPlans()));
+
+ /** @var InstalmentPlan $selectedPlan */
+ $selectedPlan = $plans->getPlans()[0];
+ $ins = new InstallmentSecured($selectedPlan, 'DE89370400440532013000', 'Manuel Weißmann', $yesterday, 'COBADEFFXXX', $this->getTodaysDateString(), $this->getTomorrowsTimestamp());
+ $this->unzer->createPaymentType($ins);
+
+ $authorize = $ins->authorize(119.0, 'EUR', self::RETURN_URL, $this->getCustomer(), null, null, $basket = $this->createBasket());
+ $payment = $authorize->getPayment();
+
+ $hddCharge = $payment->charge();
+ $invoiceId = 'i' . self::generateRandomId();
+ $ship = $this->unzer->ship($hddCharge->getPayment(), $invoiceId);
+ $this->assertNotNull($ship);
+
+ $cancel = $payment->cancelAmount();
+ $this->assertGreaterThan(0, count($cancel));
+ }
+
+ /**
+ * Verify full cancel of charged HP after shipment.
+ *
+ * @test
+ *
+ * @depends verifyChargingAnInitializedInstallmentSecured
+ */
+ public function verifyPartlyCancelChargedInstallmentSecuredAfterShipment(): void
+ {
+ $yesterday = $this->getYesterdaysTimestamp();
+ $plans = $this->unzer->fetchInstallmentPlans(119.0, 'EUR', 4.99, $yesterday);
+ $this->assertGreaterThan(0, count($plans->getPlans()));
+
+ /** @var InstalmentPlan $selectedPlan */
+ $selectedPlan = $plans->getPlans()[0];
+ $ins = new InstallmentSecured($selectedPlan, 'DE89370400440532013000', 'Manuel Weißmann', $yesterday, 'COBADEFFXXX', $this->getTodaysDateString(), $this->getTomorrowsTimestamp());
+ $this->unzer->createPaymentType($ins);
+
+ $authorize = $ins->authorize(119.0, 'EUR', self::RETURN_URL, $this->getCustomer(), null, null, $basket = $this->createBasket());
+ $payment = $authorize->getPayment();
+
+ $hddCharge = $payment->charge();
+ $invoiceId = 'i' . self::generateRandomId();
+ $ship = $this->unzer->ship($hddCharge->getPayment(), $invoiceId);
+ $this->assertNotNull($ship);
+
+ $cancel = $payment->cancelAmount(59.5, null, null, 50.0, 9.5);
+ $this->assertCount(1, $cancel);
+ $this->assertTrue($payment->isCompleted());
+ }
+
//
//