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 b780017..ec80e8a 100644 --- a/examples/Notifications/AllNotificationsExample.php +++ b/examples/Notifications/AllNotificationsExample.php @@ -11,6 +11,7 @@ use Tpay\OpenApi\Model\Objects\NotificationBody\BlikAliasUnregister; use Tpay\OpenApi\Model\Objects\NotificationBody\BlikAliasUpdated; 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; @@ -130,6 +131,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('{"result":true}'); + } + // Ignore and silence other notification types if not expected http_response_code(404); exit('FALSE'); diff --git a/src/Api/ApiAction.php b/src/Api/ApiAction.php index a82e70d..e5706d7 100644 --- a/src/Api/ApiAction.php +++ b/src/Api/ApiAction.php @@ -225,6 +225,11 @@ protected function addQueryFields($requestUrl, $queryFields) return $requestUrl; } + protected function isProductionMode() + { + return true === $this->productionMode; + } + private function checkResponse() { $responseCode = $this->getHttpResponseCode(); diff --git a/src/Api/Recurring/RecurringApi.php b/src/Api/Recurring/RecurringApi.php new file mode 100644 index 0000000..43313dd --- /dev/null +++ b/src/Api/Recurring/RecurringApi.php @@ -0,0 +1,87 @@ +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) + { + $this->validateProductionPaymentType($fields); + + return $this->run(static::POST, '/recurring', $fields, new Recurring()); + } + + /** @param string $recurringId */ + public function cancelRecurring($recurringId) + { + return $this->run(static::POST, sprintf('/recurring/%s/cancel', $recurringId)); + } + + /** @param string $recurringId */ + public function retryRecurring($recurringId) + { + return $this->run(static::POST, sprintf('/recurring/%s/retry', $recurringId)); + } + + /** @param string $recurringId */ + public function updatePaymentInstrument($fields, $recurringId) + { + return $this->run( + static::POST, + sprintf('/recurring/%s/payment_instrument', $recurringId), + $fields, + new UpdatePaymentInstrument() + ); + } + + /** @param array $fields */ + private function validateProductionPaymentType($fields) + { + if ( + !$this->isProductionMode() + || !isset($fields[self::PAYMENT_INSTRUMENT_FIELD][self::PAYMENT_TYPE_FIELD]) + || PaymentType::TEST !== $fields[self::PAYMENT_INSTRUMENT_FIELD][self::PAYMENT_TYPE_FIELD] + ) { + return; + } + + throw new UnexpectedValueException( + sprintf('paymentType "%s" is not allowed in production mode', PaymentType::TEST) + ); + } +} diff --git a/src/Api/TpayApi.php b/src/Api/TpayApi.php index a4d353c..39158de 100644 --- a/src/Api/TpayApi.php +++ b/src/Api/TpayApi.php @@ -7,6 +7,7 @@ use Tpay\OpenApi\Api\Authorization\AuthorizationApi; use Tpay\OpenApi\Api\Blik\BlikApi; 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; @@ -28,6 +29,9 @@ class TpayApi /** @var null|CollectApi */ private $collect; + /** @var null|RecurringApi */ + private $recurring; + /** @var null|RefundsApi */ private $refunds; @@ -147,6 +151,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/Factory/ArrayObjectFactory.php b/src/Factory/ArrayObjectFactory.php index 983d97c..33676db 100644 --- a/src/Factory/ArrayObjectFactory.php +++ b/src/Factory/ArrayObjectFactory.php @@ -10,6 +10,8 @@ use Tpay\OpenApi\Model\Objects\Merchant\ContactPerson; use Tpay\OpenApi\Model\Objects\Merchant\PointOfSale as MerchantPointOfSale; use Tpay\OpenApi\Model\Objects\Objects; +use Tpay\OpenApi\Model\Objects\Recurring\RetryInterval; +use Tpay\OpenApi\Model\Objects\Recurring\Schedule; use Tpay\OpenApi\Model\Objects\RequestBody\Account; use Tpay\OpenApi\Model\Objects\RequestBody\Merchant; @@ -51,6 +53,15 @@ public function create($fieldName, $parentObject) } } + if ($parentObject instanceof Schedule) { + switch ($fieldName) { + case 'retryIntervals': + return new RetryInterval(); + default: + throw new InvalidArgumentException(sprintf('Unsupported field "%s" in %s', $fieldName, $parentObject->getName())); + } + } + throw new InvalidArgumentException(sprintf('Field %s as array is not supported in %s object', $fieldName, $parentObject->getName())); } } diff --git a/src/Manager/Manager.php b/src/Manager/Manager.php index 751b551..436a7ab 100644 --- a/src/Manager/Manager.php +++ b/src/Manager/Manager.php @@ -29,6 +29,7 @@ public function setFields($fields, $strictCheck = true) $this->requestBody->strictCheck = $strictCheck; $this->requestBody->setObjectValues($this->requestBody, $fields); $this->ObjectsValidator->isSetRequiredFields($this->requestBody); + $this->ObjectsValidator->validate($this->requestBody); $this->ObjectsValidator->checkUniqueFields($this->requestBody); return $this; 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/Objects.php b/src/Model/Objects/Objects.php index 67d2464..6662862 100644 --- a/src/Model/Objects/Objects.php +++ b/src/Model/Objects/Objects.php @@ -11,9 +11,13 @@ class Objects implements ObjectsInterface { const OBJECT_FIELDS = []; const UNIQUE_FIELDS = []; + private const PROVIDED_FIELDS_PROPERTY = 'providedFields'; public $strictCheck = true; + /** @var array */ + protected $providedFields = []; + /** @var ArrayObjectFactory */ private $factory; @@ -29,6 +33,17 @@ public function getRequiredFields() return []; } + public function validate() + { + return true; + } + + /** @param string $fieldName */ + public function wasFieldProvided($fieldName) + { + return isset($this->providedFields[$fieldName]); + } + /** @return string */ public function getName() { @@ -44,22 +59,56 @@ public function getName() public function setObjectValues(&$object, $values) { foreach ($values as $fieldName => $fieldValue) { - if (is_array($fieldValue) && property_exists($object, $fieldName)) { - $this->setObjectsInArray($object, $fieldValue, $fieldName); + if (!is_object($object)) { continue; } - if (property_exists($object, $fieldName) && $this->isField($object->{$fieldName})) { - $object->{$fieldName}->setValue($fieldValue); - } elseif (property_exists($object, $fieldName) && $this->isObject($object->{$fieldName})) { - $this->setObjectValues($object->{$fieldName}, $fieldValue); - } else { - $errorField = $fieldName; - if (0 === $errorField) { - $errorField = $fieldValue; + + if ($object instanceof self) { + $object->markFieldProvided($fieldName); + } + + if (!property_exists($object, $fieldName)) { + if (true === $this->strictCheck) { + throw new InvalidArgumentException( + sprintf('Field %s is not supported', $fieldName) + ); } + + continue; + } + + if (self::PROVIDED_FIELDS_PROPERTY === $fieldName) { if (true === $this->strictCheck) { - throw new InvalidArgumentException(sprintf('Field %s is not supported', $errorField)); + throw new InvalidArgumentException( + sprintf('Field %s is not supported', $fieldName) + ); } + + continue; + } + + if (is_array($fieldValue) && is_array($object->{$fieldName})) { + $this->setObjectsInArray($object, $fieldValue, $fieldName); + + continue; + } + + if (is_array($fieldValue) && $this->isObject($object->{$fieldName})) { + $this->setObjectValues($object->{$fieldName}, $fieldValue); + + continue; + } + + if ($this->isField($object->{$fieldName})) { + $object->{$fieldName}->setValue($fieldValue); + + continue; + } + + if (true === $this->strictCheck) { + throw new InvalidArgumentException( + sprintf('Field %s is not supported', $fieldName) + ); } } @@ -72,12 +121,19 @@ protected function injectObjectFields($objectFields) foreach ($objectFields as $objectVar => $fieldClass) { if (is_array($fieldClass)) { $this->{$objectVar}[] = new $fieldClass[0](); + continue; } $this->{$objectVar} = new $fieldClass(); } } + /** @param string $fieldName */ + protected function markFieldProvided($fieldName) + { + $this->providedFields[$fieldName] = true; + } + /** * @param object $object * @param array $fieldValue @@ -86,25 +142,33 @@ protected function injectObjectFields($objectFields) private function setObjectsInArray($object, $fieldValue, $fieldName) { foreach ($fieldValue as $field => $value) { + if (is_array($object->{$fieldName})) { + if (!isset($object->{$fieldName}[$field])) { + $object->{$fieldName}[$field] = $this->factory->create($fieldName, $object); + } + $this->setObjectValues($object->{$fieldName}[$field], $value); + + continue; + } + if (is_array($value)) { if (isset($object->{$fieldName}->{$field})) { $this->setObjectValues($object->{$fieldName}->{$field}, $value); } - if (is_array($object->{$fieldName})) { - if (!isset($object->{$fieldName}[$field])) { - $object->{$fieldName}[] = $this->factory->create($fieldName, $object); - } - $this->setObjectValues($object->{$fieldName}[$field], $value); - } - } else { - if (isset($object->{$fieldName}->{$field}) && $this->isField($object->{$fieldName}->{$field})) { - $object->{$fieldName}->{$field}->setValue($value); - } else { - if (true === $this->strictCheck) { - throw new InvalidArgumentException(sprintf('Field %s is not supported', $field)); - } - } + continue; + } + + if (isset($object->{$fieldName}->{$field}) && $this->isField($object->{$fieldName}->{$field})) { + $object->{$fieldName}->{$field}->setValue($value); + + continue; + } + + if (true === $this->strictCheck) { + throw new InvalidArgumentException( + sprintf('Field %s is not supported', $field) + ); } } } diff --git a/src/Model/Objects/ObjectsValidator.php b/src/Model/Objects/ObjectsValidator.php index e773cee..40874d7 100644 --- a/src/Model/Objects/ObjectsValidator.php +++ b/src/Model/Objects/ObjectsValidator.php @@ -51,6 +51,33 @@ public function isSetRequiredFields($objectClass) return true; } + /** + * @param Objects $objectClass + * + * @return bool + */ + public function validate($objectClass) + { + $objectClass->validate(); + + foreach (get_object_vars($objectClass) as $field) { + if ($field instanceof Objects) { + $this->validate($field); + continue; + } + + if (is_array($field)) { + foreach ($field as $value) { + if ($value instanceof Objects) { + $this->validate($value); + } + } + } + } + + return true; + } + /** * @param array $objects * @param mixed $fieldName diff --git a/src/Model/Objects/Recurring/BlikPaymentDetails.php b/src/Model/Objects/Recurring/BlikPaymentDetails.php new file mode 100644 index 0000000..cd21bed --- /dev/null +++ b/src/Model/Objects/Recurring/BlikPaymentDetails.php @@ -0,0 +1,35 @@ + BlikModel::class, + 'noDelay' => Boolean::class, + 'recommendedAuthLevel' => RecommendedAuthLevel::class, + ]; + + /** @var BlikModel */ + public $model; + + /** @var \Tpay\OpenApi\Model\Fields\Boolean */ + public $noDelay; + + /** @var RecommendedAuthLevel */ + public $recommendedAuthLevel; + + public function getRequiredFields() + { + return [ + $this->model, + ]; + } +} 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..8241f0f --- /dev/null +++ b/src/Model/Objects/Recurring/PaymentInstrument.php @@ -0,0 +1,57 @@ + PaymentType::class, + 'value' => Value::class, + 'blik' => BlikPaymentDetails::class, + ]; + + /** @var PaymentType */ + public $paymentType; + + /** @var Value */ + public $value; + + /** @var BlikPaymentDetails */ + public $blik; + + public function getRequiredFields() + { + $requiredFields = [ + $this->paymentType, + $this->value, + ]; + + if (PaymentType::BLIK_PAYID === $this->paymentType->getValue() && $this->wasFieldProvided('blik')) { + $requiredFields[] = $this->blik; + } + + return $requiredFields; + } + + public function validate() + { + if (PaymentType::CARD_TOKEN === $this->paymentType->getValue() && $this->wasFieldProvided('blik')) { + throw new UnexpectedValueException( + sprintf('Field "blik" is not allowed with paymentType "%s"', PaymentType::CARD_TOKEN) + ); + } + + if (PaymentType::BLIK_PAYID === $this->paymentType->getValue() && !$this->wasFieldProvided('blik')) { + throw new UnexpectedValueException( + sprintf('Field "blik" is required when paymentType is "%s"', PaymentType::BLIK_PAYID) + ); + } + + return true; + } +} diff --git a/src/Model/Objects/Recurring/RetryInterval.php b/src/Model/Objects/Recurring/RetryInterval.php new file mode 100644 index 0000000..1d94307 --- /dev/null +++ b/src/Model/Objects/Recurring/RetryInterval.php @@ -0,0 +1,31 @@ + Interval::class, + 'unit' => IntervalType::class, + ]; + + /** @var Interval */ + public $value; + + /** @var IntervalType */ + public $unit; + + public function getRequiredFields() + { + return [ + $this->value, + $this->unit, + ]; + } +} diff --git a/src/Model/Objects/Recurring/Schedule.php b/src/Model/Objects/Recurring/Schedule.php new file mode 100644 index 0000000..9f0d14c --- /dev/null +++ b/src/Model/Objects/Recurring/Schedule.php @@ -0,0 +1,56 @@ + Amount::class, + 'currency' => Currency::class, + 'firstChargeDate' => FirstChargeDate::class, + 'interval' => Interval::class, + 'intervalType' => IntervalType::class, + 'chargeCount' => ChargeCount::class, + 'retryIntervals' => [RetryInterval::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; + + /** @var RetryInterval */ + public $retryIntervals; + + 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 23578b8..3f58b04 100644 --- a/src/Webhook/JWSVerifiedPaymentNotification.php +++ b/src/Webhook/JWSVerifiedPaymentNotification.php @@ -9,6 +9,7 @@ use Tpay\OpenApi\Model\Objects\NotificationBody\BlikAliasUnregister; use Tpay\OpenApi\Model\Objects\NotificationBody\BlikAliasUpdated; 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; @@ -218,6 +219,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/Api/Recurring/RecurringApiTest.php b/tests/Api/Recurring/RecurringApiTest.php new file mode 100644 index 0000000..b841bdb --- /dev/null +++ b/tests/Api/Recurring/RecurringApiTest.php @@ -0,0 +1,105 @@ +expectException(UnexpectedValueException::class); + $this->expectExceptionMessage('Field "blik" is not allowed with paymentType "card_token"'); + + $this->createRecurringApi(false)->updatePaymentInstrument([ + 'paymentInstrument' => [ + 'paymentType' => 'card_token', + 'value' => 'card-token-value', + 'blik' => [ + 'model' => 'A', + ], + ], + ], 'recurring-id'); + } + + public function testUpdatePaymentInstrumentWithBlikPayidRequiresBlik() + { + CurlMock::expectNoCurlExecCall(); + + $this->expectException(UnexpectedValueException::class); + $this->expectExceptionMessage('Field "blik" is required when paymentType is "blik_payid"'); + + $this->createRecurringApi(false)->updatePaymentInstrument([ + 'paymentInstrument' => [ + 'paymentType' => 'blik_payid', + 'value' => 'blik-payid-value', + ], + ], 'recurring-id'); + } + + public function testUpdatePaymentInstrumentWithBlikPayidAllowsBlik() + { + CurlMock::setConsecutiveReturnedTransfers('"ok"'); + + $result = $this->createRecurringApi(false)->updatePaymentInstrument([ + 'paymentInstrument' => [ + 'paymentType' => 'blik_payid', + 'value' => 'blik-payid-value', + 'blik' => [ + 'model' => 'A', + ], + ], + ], 'recurring-id'); + + self::assertSame('ok', $result); + } + + public function testCreateRecurringInProductionModeDoesNotAllowTestPaymentType() + { + CurlMock::expectNoCurlExecCall(); + + $this->expectException(UnexpectedValueException::class); + $this->expectExceptionMessage('paymentType "test" is not allowed in production mode'); + + $this->createRecurringApi(true)->createRecurring([ + 'paymentInstrument' => [ + 'paymentType' => 'test', + 'value' => 'test-value', + ], + ]); + } + + public function testUpdatePaymentInstrumentInProductionModeAllowsTestPaymentType() + { + CurlMock::setConsecutiveReturnedTransfers('"ok"'); + + $result = $this->createRecurringApi(true)->updatePaymentInstrument([ + 'paymentInstrument' => [ + 'paymentType' => 'test', + 'value' => 'test-value', + ], + ], 'recurring-id'); + + self::assertSame('ok', $result); + } + + private function createRecurringApi($productionMode) + { + $accessToken = $this->createMock(AccessToken::class); + + $token = $this->createMock(Token::class); + $token->access_token = $accessToken; + + return new RecurringApi($token, $productionMode); + } +} diff --git a/tests/Factory/ArrayObjectFactoryTest.php b/tests/Factory/ArrayObjectFactoryTest.php index 2c958a0..b764ae8 100644 --- a/tests/Factory/ArrayObjectFactoryTest.php +++ b/tests/Factory/ArrayObjectFactoryTest.php @@ -12,6 +12,8 @@ use Tpay\OpenApi\Model\Objects\Merchant\Address as MerchantAddress; use Tpay\OpenApi\Model\Objects\Merchant\ContactPerson; use Tpay\OpenApi\Model\Objects\Merchant\PointOfSale as MerchantPointOfSale; +use Tpay\OpenApi\Model\Objects\Recurring\RetryInterval; +use Tpay\OpenApi\Model\Objects\Recurring\Schedule; use Tpay\OpenApi\Model\Objects\RequestBody\Account; use Tpay\OpenApi\Model\Objects\RequestBody\Merchant; use Tpay\OpenApi\Model\Objects\RequestBody\Refund; @@ -75,6 +77,12 @@ public function validObjectProvider() 'object' => new Account(), 'expectedResult' => Person::class, ]; + + yield 'recurring retryIntervals' => [ + 'fieldName' => 'retryIntervals', + 'object' => new Schedule(), + 'expectedResult' => RetryInterval::class, + ]; } public function invalidObjectProvider() 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; }