diff --git a/composer.json b/composer.json index b41ecd1..c02a05f 100644 --- a/composer.json +++ b/composer.json @@ -15,7 +15,7 @@ "docs": "https://openapi.tpay.com" }, "require": { - "php": ">=5.6.0", + "php": ">=7.1", "ext-curl": "*", "ext-fileinfo": "*", "ext-json": "*", diff --git a/examples/Notifications/AllNotificationsExample.php b/examples/Notifications/AllNotificationsExample.php index 1870fe9..4c69c52 100644 --- a/examples/Notifications/AllNotificationsExample.php +++ b/examples/Notifications/AllNotificationsExample.php @@ -9,6 +9,7 @@ use Tpay\OpenApi\Model\Objects\NotificationBody\BlikAliasRegister; use Tpay\OpenApi\Model\Objects\NotificationBody\BlikAliasUnregister; use Tpay\OpenApi\Model\Objects\NotificationBody\MarketplaceTransaction; +use Tpay\OpenApi\Model\Objects\NotificationBody\Recurring; use Tpay\OpenApi\Model\Objects\NotificationBody\Tokenization; use Tpay\OpenApi\Model\Objects\NotificationBody\TokenUpdate; use Tpay\OpenApi\Model\Objects\Objects; @@ -106,6 +107,20 @@ public function getVerifiedNotification() exit('TRUE'); } + if ($notification instanceof Recurring) { + // Notification about successful recurring registered + + $recurringId = $notification->recurringId->getValue(); + // The above example will check the notification and return the value of recurring id + + $transactionId = $notification->transactionId->getValue(); + // The above example will check the notification and return the value of received transaction id field + // You can access any notification field by $notification->fieldName + + // $recurringProcessor->process($notification) + exit('TRUE'); + } + // Ignore and silence other notification types if not expected http_response_code(404); exit('FALSE'); diff --git a/src/Api/Recurring/RecurringApi.php b/src/Api/Recurring/RecurringApi.php new file mode 100644 index 0000000..03d51f1 --- /dev/null +++ b/src/Api/Recurring/RecurringApi.php @@ -0,0 +1,58 @@ +addQueryFields('/recurring', $queryFields); + + return $this->run(static::GET, $requestUrl); + } + + /** @param string $recurringId */ + public function getRecurringById($recurringId) + { + return $this->run(static::GET, sprintf('/recurring/%s', $recurringId)); + } + + /** + * @param string $recurringId + * @param array $queryFields + */ + public function getTransactionsByRecurringId($recurringId, $queryFields = []) + { + $requestUrl = $this->addQueryFields(sprintf('/recurring/%s/transactions', $recurringId), $queryFields); + + return $this->run(static::GET, $requestUrl); + } + + /** @param array $fields */ + public function createRecurring($fields) + { + return $this->run(static::POST, '/recurring', $fields, new Recurring()); + } + + /** @param string $recurringId */ + public function cancelTransaction($recurringId) + { + return $this->run(static::POST, sprintf('/recurring/%s/cancel', $recurringId)); + } + + /** @param string $recurringId */ + public function updatePaymentInstrument($fields, $recurringId) + { + return $this->run( + static::POST, + sprintf('/recurring/%s/payment_instrument', $recurringId), + $fields, + new UpdatePaymentInstrument() + ); + } +} diff --git a/src/Api/TpayApi.php b/src/Api/TpayApi.php index 66ca600..3deab44 100644 --- a/src/Api/TpayApi.php +++ b/src/Api/TpayApi.php @@ -6,6 +6,7 @@ use Tpay\OpenApi\Api\Accounts\AccountsApi; use Tpay\OpenApi\Api\Authorization\AuthorizationApi; use Tpay\OpenApi\Api\Collect\CollectApi; +use Tpay\OpenApi\Api\Recurring\RecurringApi; use Tpay\OpenApi\Api\Refunds\RefundsApi; use Tpay\OpenApi\Api\Reports\ReportsApi; use Tpay\OpenApi\Api\Transactions\TransactionsApi; @@ -24,6 +25,9 @@ class TpayApi /** @var null|CollectApi */ private $collect; + /** @var null|RecurringApi */ + private $recurring; + /** @var null|RefundsApi */ private $refunds; @@ -125,6 +129,22 @@ public function authorization() return $this->authorization; } + /** @return RecurringApi */ + public function recurring() + { + $this->authorize(); + if (null === $this->recurring) { + $this->recurring = (new RecurringApi($this->token, $this->productionMode)) + ->overrideApiUrl($this->apiUrl); + + if ($this->clientName) { + $this->recurring->setClientName($this->clientName); + } + } + + return $this->recurring; + } + /** @return RefundsApi */ public function refunds() { diff --git a/src/Model/Fields/Notification/Recurring/IterationCount.php b/src/Model/Fields/Notification/Recurring/IterationCount.php new file mode 100644 index 0000000..bc89484 --- /dev/null +++ b/src/Model/Fields/Notification/Recurring/IterationCount.php @@ -0,0 +1,14 @@ + Id::class, + 'transactionId' => TransactionId::class, + 'hiddenDescription' => HiddenDescription::class, + 'iterationCount' => IterationCount::class, + 'iterationAttemptCount' => IterationCountAttempt::class, + 'status' => Status::class, + 'nextChargeDate' => NextChargeDate::class, + 'reason' => Reason::class, + ]; + + /** @var Id */ + public $recurringId; + + /** @var TransactionId */ + public $transactionId; + + /** @var HiddenDescription */ + public $hiddenDescription; + + /** @var IterationCount */ + public $iterationCount; + + /** @var IterationCountAttempt */ + public $iterationAttemptCount; + + /** @var Status */ + public $status; + + /** @var NextChargeDate */ + public $nextChargeDate; + + /** @var Reason */ + public $reason; + + public function getRequiredFields() + { + return [ + $this->recurringId, + $this->transactionId, + $this->hiddenDescription, + $this->iterationCount, + $this->iterationAttemptCount, + $this->status, + ]; + } +} diff --git a/src/Model/Objects/Recurring/Payer.php b/src/Model/Objects/Recurring/Payer.php new file mode 100644 index 0000000..666ff4c --- /dev/null +++ b/src/Model/Objects/Recurring/Payer.php @@ -0,0 +1,59 @@ + Email::class, + 'name' => Name::class, + 'phone' => Phone::class, + 'address' => Address::class, + 'code' => PostalCode::class, + 'city' => City::class, + 'country' => Country::class, + 'taxId' => TaxId::class, + ]; + + /** @var Email */ + public $email; + + /** @var Name */ + public $name; + + /** @var Phone */ + public $phone; + + /** @var Address */ + public $address; + + /** @var PostalCode */ + public $code; + + /** @var City */ + public $city; + + /** @var Country */ + public $country; + + /** @var TaxId */ + public $taxId; + + public function getRequiredFields() + { + return [ + $this->email, + $this->name, + ]; + } +} diff --git a/src/Model/Objects/Recurring/PaymentInstrument.php b/src/Model/Objects/Recurring/PaymentInstrument.php new file mode 100644 index 0000000..82cce19 --- /dev/null +++ b/src/Model/Objects/Recurring/PaymentInstrument.php @@ -0,0 +1,29 @@ + PaymentType::class, + 'value' => Value::class, + ]; + + /** @var PaymentType */ + public $paymentType; + + /** @var Value */ + public $value; + + public function getRequiredFields() + { + return [ + $this->paymentType, + $this->value, + ]; + } +} diff --git a/src/Model/Objects/Recurring/Schedule.php b/src/Model/Objects/Recurring/Schedule.php new file mode 100644 index 0000000..fe6293c --- /dev/null +++ b/src/Model/Objects/Recurring/Schedule.php @@ -0,0 +1,52 @@ + Amount::class, + 'currency' => Currency::class, + 'firstChargeDate' => FirstChargeDate::class, + 'interval' => Interval::class, + 'intervalType' => IntervalType::class, + 'chargeCount' => ChargeCount::class, + ]; + + /** @var Amount */ + public $amount; + + /** @var Currency */ + public $currency; + + /** @var FirstChargeDate */ + public $firstChargeDate; + + /** @var Interval */ + public $interval; + + /** @var IntervalType */ + public $intervalType; + + /** @var ChargeCount */ + public $chargeCount; + + public function getRequiredFields() + { + return [ + $this->amount, + $this->currency, + $this->firstChargeDate, + $this->interval, + $this->intervalType, + ]; + } +} diff --git a/src/Model/Objects/RequestBody/Recurring.php b/src/Model/Objects/RequestBody/Recurring.php new file mode 100644 index 0000000..04a54f5 --- /dev/null +++ b/src/Model/Objects/RequestBody/Recurring.php @@ -0,0 +1,58 @@ + Id::class, + 'description' => Description::class, + 'hiddenDescription' => HiddenDescription::class, + 'payer' => Payer::class, + 'schedule' => Schedule::class, + 'paymentInstrument' => PaymentInstrument::class, + 'callbackUrl' => CallbackUrl::class, + ]; + + /** @var Id */ + public $id; + + /** @var Description */ + public $description; + + /** @var HiddenDescription */ + public $hiddenDescription; + + /** @var Payer */ + public $payer; + + /** @var Schedule */ + public $schedule; + + /** @var PaymentInstrument */ + public $paymentInstrument; + + /** @var CallbackUrl */ + public $callbackUrl; + + public function getRequiredFields() + { + return [ + $this->id, + $this->description, + $this->payer, + $this->schedule, + $this->paymentInstrument, + $this->callbackUrl, + ]; + } +} diff --git a/src/Model/Objects/RequestBody/UpdatePaymentInstrument.php b/src/Model/Objects/RequestBody/UpdatePaymentInstrument.php new file mode 100644 index 0000000..a5c7610 --- /dev/null +++ b/src/Model/Objects/RequestBody/UpdatePaymentInstrument.php @@ -0,0 +1,23 @@ + PaymentInstrument::class, + ]; + + /** @var PaymentInstrument */ + public $paymentInstrument; + + public function getRequiredFields() + { + return [ + $this->paymentInstrument, + ]; + } +} diff --git a/src/Webhook/JWSVerifiedPaymentNotification.php b/src/Webhook/JWSVerifiedPaymentNotification.php index 98317b3..d321995 100644 --- a/src/Webhook/JWSVerifiedPaymentNotification.php +++ b/src/Webhook/JWSVerifiedPaymentNotification.php @@ -7,6 +7,7 @@ use Tpay\OpenApi\Model\Objects\NotificationBody\BlikAliasRegister; use Tpay\OpenApi\Model\Objects\NotificationBody\BlikAliasUnregister; use Tpay\OpenApi\Model\Objects\NotificationBody\MarketplaceTransaction; +use Tpay\OpenApi\Model\Objects\NotificationBody\Recurring; use Tpay\OpenApi\Model\Objects\NotificationBody\Tokenization; use Tpay\OpenApi\Model\Objects\NotificationBody\TokenUpdate; use Tpay\OpenApi\Model\Objects\Objects; @@ -210,6 +211,8 @@ private function getNotificationObject() throw new TpayException('Not recognised or invalid notification event: '.json_encode($source)); } $source = $source['msg_value']; + } elseif (isset($source['recurringId'])) { + $requestBody = new Recurring(); } else { throw new TpayException( 'Cannot determine notification type. POST payload: '.json_encode($source) diff --git a/tests/Webhook/JWSVerifiedPaymentNotificationTest.php b/tests/Webhook/JWSVerifiedPaymentNotificationTest.php index 42bbe4b..830d0e0 100644 --- a/tests/Webhook/JWSVerifiedPaymentNotificationTest.php +++ b/tests/Webhook/JWSVerifiedPaymentNotificationTest.php @@ -7,6 +7,7 @@ use Tpay\OpenApi\Model\Objects\NotificationBody\BlikAliasRegister; use Tpay\OpenApi\Model\Objects\NotificationBody\BlikAliasUnregister; use Tpay\OpenApi\Model\Objects\NotificationBody\MarketplaceTransaction; +use Tpay\OpenApi\Model\Objects\NotificationBody\Recurring; use Tpay\OpenApi\Model\Objects\NotificationBody\Tokenization; use Tpay\OpenApi\Model\Objects\NotificationBody\TokenUpdate; use Tpay\OpenApi\Utilities\TpayException; @@ -223,6 +224,62 @@ public function positiveValidationProvider() $payload = http_build_query($data); $result[] = ['application/x-www-form-urlencoded', $data, $payload, $this->sign($payload, true), 'x', true, BlikAliasUnregister::class, 'value', 'user_unique_alias_456']; + $payload = <<<'JSON' +{ + "recurringId": "01KHXNTSWXP42CYFTF5TM3FJJX", + "transactionId": "TR-1234-12012345678901234567822", + "hiddenDescription": "ORDER-123", + "iterationCount": "1", + "iterationAttemptCount": "1", + "status": "active", + "nextChargeDate": null, + "reason": null +} +JSON; + $data = json_decode($payload, true); + $result[] = ['application/json', $data, $payload, $this->sign($payload, true), 'x', true, Recurring::class, 'recurringId', '01KHXNTSWXP42CYFTF5TM3FJJX']; + + $payload = <<<'JSON' +{ + "recurringId": "01KHXNTSWXP42CYFTF5TM3FJJY", + "transactionId": "TR-1234-12012345678901234567822", + "hiddenDescription": "ORDER-123", + "iterationCount": "1", + "iterationAttemptCount": "1", + "status": "active" +} +JSON; + $data = json_decode($payload, true); + $result[] = ['application/json', $data, $payload, $this->sign($payload, true), 'x', true, Recurring::class, 'recurringId', '01KHXNTSWXP42CYFTF5TM3FJJY']; + + $payload = <<<'JSON' +{ + "recurringId": "01KHXNTSWXP42CYFTF5TM3FJJZ", + "transactionId": "TR-1234-12012345678901234567822", + "hiddenDescription": "ORDER-123", + "iterationCount": "1", + "iterationAttemptCount": "1", + "status": "active", + "nextChargeDate": "2024-12-10 09:27:59" +} +JSON; + $data = json_decode($payload, true); + $result[] = ['application/json', $data, $payload, $this->sign($payload, true), 'x', true, Recurring::class, 'recurringId', '01KHXNTSWXP42CYFTF5TM3FJJZ']; + + $payload = <<<'JSON' +{ + "recurringId": "01KHXNTSWXP42CYFTF5TM3FJJA", + "transactionId": "", + "hiddenDescription": "ORDER-123", + "iterationCount": "1", + "iterationAttemptCount": "1", + "status": "failed", + "reason": "insufficient funds" +} +JSON; + $data = json_decode($payload, true); + $result[] = ['application/json', $data, $payload, $this->sign($payload, true), 'x', true, Recurring::class, 'recurringId', '01KHXNTSWXP42CYFTF5TM3FJJA']; + return $result; }