Skip to content

Commit 011cc4e

Browse files
authored
CC-1576/preauth (#201)
* [CC-1576] Add preauthorization type * [CC-1576] Add webhook events for preauthorization type. * [CC-1576] Prepare release
1 parent e737aa5 commit 011cc4e

File tree

11 files changed

+277
-17
lines changed

11 files changed

+277
-17
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres
66
to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
77

8+
## [3.11.0](https://github.com/unzerdev/php-sdk/compare/3.10.0..3.11.0)
9+
10+
### Added
11+
12+
* Add support for `preauthorize` transaction.
13+
814
## [3.10.0](https://github.com/unzerdev/php-sdk/compare/3.9.0..3.10.0)
915

1016
### Added

src/Constants/IdStrings.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,10 @@ class IdStrings
1313
public const AUTHORIZE = 'aut';
1414
public const CANCEL = 'cnl';
1515
public const CHARGE = 'chg';
16+
public const CHARGEBACK = 'cbk';
1617
public const PAYOUT = 'out';
18+
public const PREAUTHORIZE = 'preaut';
1719
public const SHIPMENT = 'shp';
18-
public const CHARGEBACK = 'cbk';
1920

2021
// Payment Types
2122
public const ALIPAY = 'ali';

src/Constants/TransactionTypes.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
class TransactionTypes
1212
{
1313
public const AUTHORIZATION = 'authorize';
14+
public const PREAUTHORIZATION = 'preauthorize';
1415
public const CHARGE = 'charge';
1516
public const REVERSAL = 'cancel-authorize';
1617
public const REFUND = 'cancel-charge';

src/Constants/WebhookEvents.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,15 @@ class WebhookEvents
2222
public const AUTHORIZE_RESUMED = 'authorize.resumed';
2323
public const AUTHORIZE_SUCCEEDED = 'authorize.succeeded';
2424

25+
// preauthorize events
26+
public const PREAUTHORIZE = 'preauthorize';
27+
public const PREAUTHORIZE_CANCELED = 'preauthorize.canceled';
28+
public const PREAUTHORIZE_EXPIRED = 'preauthorize.expired';
29+
public const PREAUTHORIZE_FAILED = 'preauthorize.failed';
30+
public const PREAUTHORIZE_PENDING = 'preauthorize.pending';
31+
public const PREAUTHORIZE_RESUMED = 'preauthorize.resumed';
32+
public const PREAUTHORIZE_SUCCEEDED = 'preauthorize.succeeded';
33+
2534
// charge events
2635
public const CHARGE = 'charge';
2736
public const CHARGE_CANCELED = 'charge.canceled';
@@ -69,6 +78,13 @@ class WebhookEvents
6978
self::AUTHORIZE_PENDING,
7079
self::AUTHORIZE_RESUMED,
7180
self::AUTHORIZE_SUCCEEDED,
81+
self::PREAUTHORIZE,
82+
self::PREAUTHORIZE_CANCELED,
83+
self::PREAUTHORIZE_EXPIRED,
84+
self::PREAUTHORIZE_FAILED,
85+
self::PREAUTHORIZE_PENDING,
86+
self::PREAUTHORIZE_RESUMED,
87+
self::PREAUTHORIZE_SUCCEEDED,
7288
self::CHARGE,
7389
self::CHARGE_CANCELED,
7490
self::CHARGE_EXPIRED,

src/Resources/Payment.php

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
namespace UnzerSDK\Resources;
44

5+
use RuntimeException;
6+
use stdClass;
57
use UnzerSDK\Adapter\HttpAdapterInterface;
68
use UnzerSDK\Constants\CancelReasonCodes;
79
use UnzerSDK\Constants\IdStrings;
@@ -16,15 +18,13 @@
1618
use UnzerSDK\Resources\TransactionTypes\Charge;
1719
use UnzerSDK\Resources\TransactionTypes\Chargeback;
1820
use UnzerSDK\Resources\TransactionTypes\Payout;
21+
use UnzerSDK\Resources\TransactionTypes\PreAuthorization;
1922
use UnzerSDK\Resources\TransactionTypes\Shipment;
2023
use UnzerSDK\Services\IdService;
2124
use UnzerSDK\Traits\HasInvoiceId;
2225
use UnzerSDK\Traits\HasOrderId;
2326
use UnzerSDK\Traits\HasPaymentState;
2427
use UnzerSDK\Traits\HasTraceId;
25-
use RuntimeException;
26-
use stdClass;
27-
2828
use function is_string;
2929

3030
/**
@@ -903,6 +903,9 @@ private function updateResponseTransactions(array $transactions = []): void
903903
case TransactionTypes::AUTHORIZATION:
904904
$this->updateAuthorizationTransaction($transaction);
905905
break;
906+
case TransactionTypes::PREAUTHORIZATION:
907+
$this->updatePreAuthorizationTransaction($transaction);
908+
break;
906909
case TransactionTypes::CHARGE:
907910
$this->updateChargeTransaction($transaction);
908911
break;
@@ -994,6 +997,27 @@ private function updateAuthorizationTransaction(stdClass $transaction): void
994997
$authorization->handleResponse($transaction);
995998
}
996999

1000+
/**
1001+
* This updates the local Authorization object referenced by this Payment with the given Authorization transaction
1002+
* from the Payment response.
1003+
*
1004+
* @param stdClass $transaction The transaction from the Payment response containing the Authorization data.
1005+
*
1006+
* @throws UnzerApiException An UnzerApiException is thrown if there is an error returned on API-request.
1007+
* @throws RuntimeException A RuntimeException is thrown when there is an error while using the SDK.
1008+
*/
1009+
private function updatePreAuthorizationTransaction(stdClass $transaction): void
1010+
{
1011+
$transactionId = IdService::getResourceIdFromUrl($transaction->url, IdStrings::PREAUTHORIZE);
1012+
$authorization = $this->getAuthorization(true);
1013+
if (!$authorization instanceof Authorization) {
1014+
$authorization = (new PreAuthorization())->setPayment($this)->setId($transactionId);
1015+
$this->setAuthorization($authorization);
1016+
}
1017+
1018+
$authorization->handleResponse($transaction);
1019+
}
1020+
9971021
/**
9981022
* This updates the local Charge object referenced by this Payment with the given Charge transaction from the
9991023
* Payment response.
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php
2+
3+
namespace UnzerSDK\Resources\TransactionTypes;
4+
5+
use UnzerSDK\Adapter\HttpAdapterInterface;
6+
7+
/**
8+
* This represents the pre-authorization transaction.
9+
*
10+
* @link https://docs.unzer.com/
11+
*
12+
*/
13+
class PreAuthorization extends Authorization
14+
{
15+
/**
16+
* {@inheritDoc}
17+
*/
18+
protected function getResourcePath(string $httpMethod = HttpAdapterInterface::REQUEST_GET): string
19+
{
20+
return 'preauthorize';
21+
}
22+
}

src/Services/PaymentService.php

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,27 +3,27 @@
33
namespace UnzerSDK\Services;
44

55
use DateTime;
6+
use RuntimeException;
67
use UnzerSDK\Constants\TransactionTypes;
78
use UnzerSDK\Exceptions\UnzerApiException;
8-
use UnzerSDK\Resources\EmbeddedResources\Paylater\InstallmentPlansQuery;
9-
use UnzerSDK\Resources\PaylaterInstallmentPlans;
10-
use UnzerSDK\Resources\PaymentTypes\PaylaterInstallment;
11-
use UnzerSDK\Unzer;
129
use UnzerSDK\Interfaces\PaymentServiceInterface;
1310
use UnzerSDK\Resources\AbstractUnzerResource;
1411
use UnzerSDK\Resources\Basket;
1512
use UnzerSDK\Resources\Customer;
13+
use UnzerSDK\Resources\EmbeddedResources\Paylater\InstallmentPlansQuery;
1614
use UnzerSDK\Resources\InstalmentPlans;
1715
use UnzerSDK\Resources\Metadata;
16+
use UnzerSDK\Resources\PaylaterInstallmentPlans;
1817
use UnzerSDK\Resources\Payment;
1918
use UnzerSDK\Resources\PaymentTypes\BasePaymentType;
2019
use UnzerSDK\Resources\PaymentTypes\InstallmentSecured;
20+
use UnzerSDK\Resources\PaymentTypes\PaylaterInstallment;
2121
use UnzerSDK\Resources\PaymentTypes\Paypage;
2222
use UnzerSDK\Resources\TransactionTypes\Authorization;
2323
use UnzerSDK\Resources\TransactionTypes\Charge;
2424
use UnzerSDK\Resources\TransactionTypes\Payout;
2525
use UnzerSDK\Resources\TransactionTypes\Shipment;
26-
use RuntimeException;
26+
use UnzerSDK\Unzer;
2727

2828
/**
2929
* This service provides for functionalities concerning payment transactions.

src/Services/ResourceService.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,9 @@ public function fetchResourceByUrl($url)
170170
case $resourceType === IdStrings::AUTHORIZE:
171171
$resource = $unzer->fetchAuthorization(IdService::getResourceIdFromUrl($url, IdStrings::PAYMENT));
172172
break;
173+
case $resourceType === IdStrings::PREAUTHORIZE:
174+
$resource = $unzer->fetchAuthorization(IdService::getResourceIdFromUrl($url, IdStrings::PAYMENT));
175+
break;
173176
case $resourceType === IdStrings::CHARGE:
174177
$resource = $unzer->fetchChargeById(
175178
IdService::getResourceIdFromUrl($url, IdStrings::PAYMENT),

src/Unzer.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ class Unzer implements
5757
public const BASE_URL = 'api.unzer.com';
5858
public const API_VERSION = 'v1';
5959
public const SDK_TYPE = 'UnzerPHP';
60-
public const SDK_VERSION = '3.10.0';
60+
public const SDK_VERSION = '3.11.0';
6161

6262
/** @var string $key */
6363
private $key;
@@ -685,6 +685,9 @@ public function performAuthorization(
685685
return $this->paymentService->performAuthorization($authorization, $paymentType, $customer, $metadata, $basket);
686686
}
687687

688+
/**
689+
* {@inheritDoc}
690+
*/
688691
public function updateAuthorization($payment, Authorization $authorization): Authorization
689692
{
690693
return $this->paymentService->updateAuthorization($payment, $authorization);
Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
<?php
2+
3+
/** @noinspection PhpUnhandledExceptionInspection */
4+
/** @noinspection PhpDocMissingThrowsInspection */
5+
/**
6+
* This class defines integration tests to verify interface and
7+
* functionality of the authorization transaction type.
8+
*
9+
* @link https://docs.unzer.com/
10+
*
11+
*/
12+
13+
namespace UnzerSDK\test\integration\TransactionTypes;
14+
15+
use UnzerSDK\Constants\RecurrenceTypes;
16+
use UnzerSDK\Resources\Metadata;
17+
use UnzerSDK\Resources\PaymentTypes\Card;
18+
use UnzerSDK\Resources\PaymentTypes\Paypal;
19+
use UnzerSDK\Resources\TransactionTypes\Authorization;
20+
use UnzerSDK\Resources\TransactionTypes\PreAuthorization;
21+
use UnzerSDK\test\BaseIntegrationTest;
22+
23+
/**
24+
* @group CC-1576
25+
*/
26+
class PreAuthorizationTest extends BaseIntegrationTest
27+
{
28+
/**
29+
* Verify Unzer object can perform an authorization based on the paymentTypeId.
30+
*
31+
* @test
32+
*/
33+
public function authorizeWithTypeId(): void
34+
{
35+
$paymentType = $this->unzer->createPaymentType($this->createCardObject());
36+
$preauth = new PreAuthorization(100.0, 'EUR', self::RETURN_URL);
37+
$this->unzer->performAuthorization($preauth, $paymentType->getId());
38+
$this->assertNotNull($preauth);
39+
$this->assertNotEmpty($preauth->getId());
40+
$this->assertNotEmpty($preauth->getUniqueId());
41+
$this->assertNotEmpty($preauth->getShortId());
42+
43+
$traceId = $preauth->getTraceId();
44+
$this->assertNotEmpty($traceId);
45+
$this->assertSame($traceId, $preauth->getPayment()->getTraceId());
46+
$this->assertPending($preauth);
47+
}
48+
49+
/**
50+
* Verify authorization produces Payment and Customer.
51+
*
52+
* @test
53+
*/
54+
public function authorizationProducesPaymentAndCustomer(): void
55+
{
56+
$paymentType = $this->unzer->createPaymentType($this->createCardObject());
57+
58+
$customer = $this->getMinimalCustomer();
59+
$this->assertNull($customer->getId());
60+
61+
$preauth = new PreAuthorization(100.0, 'EUR', self::RETURN_URL);
62+
$this->unzer->performAuthorization($preauth, $paymentType, $customer);
63+
$payment = $preauth->getPayment();
64+
$this->assertNotNull($payment);
65+
$this->assertNotNull($payment->getId());
66+
67+
$newCustomer = $payment->getCustomer();
68+
$this->assertNotNull($newCustomer);
69+
$this->assertNotNull($newCustomer->getId());
70+
}
71+
72+
/**
73+
* Verify authorization with customer Id.
74+
*
75+
* @test
76+
*
77+
* @return Authorization
78+
*/
79+
public function authorizationWithCustomerId(): Authorization
80+
{
81+
$paymentType = $this->unzer->createPaymentType($this->createCardObject());
82+
83+
$customerId = $this->unzer->createCustomer($this->getMinimalCustomer())->getId();
84+
$orderId = microtime(true);
85+
$preauth = (new PreAuthorization(100.0, 'EUR', self::RETURN_URL))->setOrderId($orderId);
86+
$this->unzer->performAuthorization($preauth, $paymentType, $customerId);
87+
$payment = $preauth->getPayment();
88+
$this->assertNotNull($payment);
89+
$this->assertNotNull($payment->getId());
90+
91+
$newCustomer = $payment->getCustomer();
92+
$this->assertNotNull($newCustomer);
93+
$this->assertNotNull($newCustomer->getId());
94+
95+
return $preauth;
96+
}
97+
98+
/**
99+
* Verify authorization can be fetched.
100+
*
101+
* @depends authorizationWithCustomerId
102+
*
103+
* @test
104+
*
105+
* @param Authorization $authorization
106+
*/
107+
public function authorizationCanBeFetched(Authorization $authorization): void
108+
{
109+
$fetchedAuthorization = $this->unzer->fetchAuthorization($authorization->getPaymentId());
110+
$this->assertInstanceOf(PreAuthorization::class, $fetchedAuthorization);
111+
$this->assertEquals($authorization->setCard3ds(true)->expose(), $fetchedAuthorization->expose());
112+
}
113+
114+
115+
/**
116+
* Verify authorize accepts all parameters.
117+
*
118+
* @test
119+
*/
120+
public function requestAuthorizationShouldAcceptAllParameters(): void
121+
{
122+
/** @var Card $card */
123+
$card = $this->unzer->createPaymentType($this->createCardObject());
124+
$customer = $this->getMinimalCustomer();
125+
$orderId = 'o' . self::generateRandomId();
126+
$metadata = (new Metadata())->addMetadata('key', 'value');
127+
$basket = $this->createBasket();
128+
$invoiceId = 'i' . self::generateRandomId();
129+
$paymentReference = 'paymentReference';
130+
131+
$preauth = new PreAuthorization(119.0, 'EUR', self::RETURN_URL);
132+
$preauth->setRecurrenceType(RecurrenceTypes::ONE_CLICK, $card)
133+
->setOrderId($orderId)
134+
->setInvoiceId($invoiceId)
135+
->setPaymentReference($paymentReference);
136+
137+
$preauth = $this->unzer->performAuthorization($preauth, $card, $customer, $metadata, $basket);
138+
$payment = $preauth->getPayment();
139+
140+
$this->assertSame($card, $payment->getPaymentType());
141+
$this->assertEquals(119.0, $preauth->getAmount());
142+
$this->assertEquals('EUR', $preauth->getCurrency());
143+
$this->assertEquals(self::RETURN_URL, $preauth->getReturnUrl());
144+
$this->assertSame($customer, $payment->getCustomer());
145+
$this->assertEquals($orderId, $preauth->getOrderId());
146+
$this->assertSame($metadata, $payment->getMetadata());
147+
$this->assertSame($basket, $payment->getBasket());
148+
$this->assertTrue($preauth->isCard3ds());
149+
$this->assertEquals($invoiceId, $preauth->getInvoiceId());
150+
$this->assertEquals($paymentReference, $preauth->getPaymentReference());
151+
152+
$fetchedAuthorize = $this->unzer->fetchAuthorization($preauth->getPaymentId());
153+
$fetchedPayment = $fetchedAuthorize->getPayment();
154+
155+
$this->assertEquals($payment->getPaymentType()->expose(), $fetchedPayment->getPaymentType()->expose());
156+
$this->assertEquals($preauth->getAmount(), $fetchedAuthorize->getAmount());
157+
$this->assertEquals($preauth->getCurrency(), $fetchedAuthorize->getCurrency());
158+
$this->assertEquals($preauth->getReturnUrl(), $fetchedAuthorize->getReturnUrl());
159+
$this->assertEquals($payment->getCustomer()->expose(), $fetchedPayment->getCustomer()->expose());
160+
$this->assertEquals($preauth->getOrderId(), $fetchedAuthorize->getOrderId());
161+
$this->assertEquals($payment->getMetadata()->expose(), $fetchedPayment->getMetadata()->expose());
162+
$this->assertEquals($payment->getBasket()->expose(), $fetchedPayment->getBasket()->expose());
163+
$this->assertEquals($preauth->isCard3ds(), $fetchedAuthorize->isCard3ds());
164+
$this->assertEquals($preauth->getInvoiceId(), $fetchedAuthorize->getInvoiceId());
165+
$this->assertEquals($preauth->getPaymentReference(), $fetchedAuthorize->getPaymentReference());
166+
}
167+
168+
//<editor-fold desc="Data Providers">
169+
170+
/**
171+
* @return array
172+
*/
173+
public function authorizeHasExpectedStatesDP(): array
174+
{
175+
return [
176+
'card' => [$this->createCardObject(), 'pending'],
177+
'paypal' => [new Paypal(), 'pending']
178+
];
179+
}
180+
181+
//</editor-fold>
182+
}

0 commit comments

Comments
 (0)