From ab772885ab777ba3a4d4875441c1c53acca997bd Mon Sep 17 00:00:00 2001 From: Stefan Adrian Danaita Date: Tue, 22 Oct 2024 21:07:21 +0100 Subject: [PATCH 1/9] [ACL-80] Signup Plus authentication uri generation --- config/bindings.php | 4 + src/Constants/Endpoints.php | 1 + src/Entities/Entity.php | 2 +- .../SignupPlus/SignupPlusAuthUriCreated.php | 25 ++++++ .../SignupPlus/SignupPlusAuthUriRequest.php | 79 +++++++++++++++++++ src/Entities/SignupPlus/SignupPlusBuilder.php | 23 ++++++ src/Factories/ApiFactory.php | 10 +++ src/Factories/EntityFactory.php | 2 +- src/Interfaces/Api/SignupPlusApiInterface.php | 23 ++++++ src/Interfaces/Client/ClientInterface.php | 6 ++ .../Factories/ApiFactoryInterface.php | 6 ++ .../SignupPlusAuthUriCreatedInterface.php | 15 ++++ .../SignupPlusAuthUriRequestInterface.php | 29 +++++++ .../SignupPlus/SignupPlusBuilderInterface.php | 10 +++ src/Services/Api/SignupPlusApi.php | 31 ++++++++ src/Services/Client/Client.php | 13 ++- tests/integration/CastsAttributesTest.php | 2 +- 17 files changed, 277 insertions(+), 4 deletions(-) create mode 100644 src/Entities/SignupPlus/SignupPlusAuthUriCreated.php create mode 100644 src/Entities/SignupPlus/SignupPlusAuthUriRequest.php create mode 100644 src/Entities/SignupPlus/SignupPlusBuilder.php create mode 100644 src/Interfaces/Api/SignupPlusApiInterface.php create mode 100644 src/Interfaces/SignupPlus/SignupPlusAuthUriCreatedInterface.php create mode 100644 src/Interfaces/SignupPlus/SignupPlusAuthUriRequestInterface.php create mode 100644 src/Interfaces/SignupPlus/SignupPlusBuilderInterface.php create mode 100644 src/Services/Api/SignupPlusApi.php diff --git a/config/bindings.php b/config/bindings.php index 21bd5d97..ef836d13 100644 --- a/config/bindings.php +++ b/config/bindings.php @@ -97,5 +97,9 @@ Interfaces\Webhook\Beneficiary\BusinessAccountBeneficiaryInterface::class => Entities\Webhook\Beneficiary\BusinessAccountBeneficiary::class, Interfaces\Webhook\Beneficiary\PaymentSourceBeneficiaryInterface::class => Entities\Webhook\Beneficiary\PaymentSourceBeneficiary::class, + Interfaces\SignupPlus\SignupPlusBuilderInterface::class => Entities\SignupPlus\SignupPlusBuilder::class, + Interfaces\SignupPlus\SignupPlusAuthUriRequestInterface::class => Entities\SignupPlus\SignupPlusAuthUriRequest::class, + Interfaces\SignupPlus\SignupPlusAuthUriCreatedInterface::class => Entities\SignupPlus\SignupPlusAuthUriCreated::class, + Interfaces\RequestOptionsInterface::class => Entities\RequestOptions::class, ]; diff --git a/src/Constants/Endpoints.php b/src/Constants/Endpoints.php index 49048e83..ef7d9a02 100644 --- a/src/Constants/Endpoints.php +++ b/src/Constants/Endpoints.php @@ -35,4 +35,5 @@ class Endpoints public const PAYOUTS_CREATE = '/v3/payouts'; public const PAYOUTS_RETRIEVE = '/v3/payouts/{id}'; + public const SIGNUP_PLUS_AUTH = '/signup-plus/authuri'; } diff --git a/src/Entities/Entity.php b/src/Entities/Entity.php index a85b599f..6a0bf282 100644 --- a/src/Entities/Entity.php +++ b/src/Entities/Entity.php @@ -25,7 +25,7 @@ abstract class Entity implements ArrayableInterface, HasAttributesInterface * @param EntityFactoryInterface $entityFactory */ public function __construct( - EntityFactoryInterface $entityFactory + EntityFactoryInterface $entityFactory, ) { $this->entityFactory = $entityFactory; } diff --git a/src/Entities/SignupPlus/SignupPlusAuthUriCreated.php b/src/Entities/SignupPlus/SignupPlusAuthUriCreated.php new file mode 100644 index 00000000..10151fef --- /dev/null +++ b/src/Entities/SignupPlus/SignupPlusAuthUriCreated.php @@ -0,0 +1,25 @@ +authUri; + } +} diff --git a/src/Entities/SignupPlus/SignupPlusAuthUriRequest.php b/src/Entities/SignupPlus/SignupPlusAuthUriRequest.php new file mode 100644 index 00000000..4dfce419 --- /dev/null +++ b/src/Entities/SignupPlus/SignupPlusAuthUriRequest.php @@ -0,0 +1,79 @@ +paymentId = $paymentId; + + return $this; + } + + /** + * @param string $state + * + * @return SignupPlusAuthUriRequestInterface + */ + public function state(string $state): SignupPlusAuthUriRequestInterface + { + $this->state = $state; + + return $this; + } + + /** + * @throws ApiResponseUnsuccessfulException + * @throws ApiRequestJsonSerializationException + * @throws SignerException + * @throws InvalidArgumentException + * + * @return SignupPlusAuthUriCreatedInterface + */ + public function create(): SignupPlusAuthUriCreatedInterface + { + $data = $this->getApiFactory()->signupPlusApi()->createAuthUri( + $this->toArray(), + ); + + return $this->make(SignupPlusAuthUriCreatedInterface::class, $data); + } +} diff --git a/src/Entities/SignupPlus/SignupPlusBuilder.php b/src/Entities/SignupPlus/SignupPlusBuilder.php new file mode 100644 index 00000000..b55d592d --- /dev/null +++ b/src/Entities/SignupPlus/SignupPlusBuilder.php @@ -0,0 +1,23 @@ +entityFactory->make(SignupPlusAuthUriRequestInterface::class); + } +} diff --git a/src/Factories/ApiFactory.php b/src/Factories/ApiFactory.php index 9ca51662..b9cde3c8 100644 --- a/src/Factories/ApiFactory.php +++ b/src/Factories/ApiFactory.php @@ -7,11 +7,13 @@ use TrueLayer\Interfaces\Api\MerchantAccountsApiInterface; use TrueLayer\Interfaces\Api\PaymentsApiInterface; use TrueLayer\Interfaces\Api\PayoutsApiInterface; +use TrueLayer\Interfaces\Api\SignupPlusApiInterface; use TrueLayer\Interfaces\ApiClient\ApiClientInterface; use TrueLayer\Interfaces\Factories\ApiFactoryInterface; use TrueLayer\Services\Api\MerchantAccountsApi; use TrueLayer\Services\Api\PaymentsApi; use TrueLayer\Services\Api\PayoutsApi; +use TrueLayer\Services\Api\SignupPlusApi; final class ApiFactory implements ApiFactoryInterface { @@ -51,4 +53,12 @@ public function payoutsApi(): PayoutsApiInterface { return new PayoutsApi($this->apiClient); } + + /** + * @return SignupPlusApiInterface + */ + public function signupPlusApi(): SignupPlusApiInterface + { + return new SignupPlusApi($this->apiClient); + } } diff --git a/src/Factories/EntityFactory.php b/src/Factories/EntityFactory.php index 5a4c74b6..218b0ba4 100644 --- a/src/Factories/EntityFactory.php +++ b/src/Factories/EntityFactory.php @@ -39,7 +39,7 @@ final class EntityFactory implements Interfaces\Factories\EntityFactoryInterface */ public function __construct( Interfaces\Configuration\ConfigInterface $sdkConfig, - ?Interfaces\Factories\ApiFactoryInterface $apiFactory = null + ?Interfaces\Factories\ApiFactoryInterface $apiFactory = null, ) { $this->sdkConfig = $sdkConfig; $this->apiFactory = $apiFactory; diff --git a/src/Interfaces/Api/SignupPlusApiInterface.php b/src/Interfaces/Api/SignupPlusApiInterface.php new file mode 100644 index 00000000..f6b73bfa --- /dev/null +++ b/src/Interfaces/Api/SignupPlusApiInterface.php @@ -0,0 +1,23 @@ +request() + ->uri(Endpoints::SIGNUP_PLUS_AUTH) + ->payload($authUriRequest) + ->post(); + } +} diff --git a/src/Services/Client/Client.php b/src/Services/Client/Client.php index 621c0138..d21d5df3 100644 --- a/src/Services/Client/Client.php +++ b/src/Services/Client/Client.php @@ -37,6 +37,7 @@ use TrueLayer\Interfaces\Remitter\RemitterVerification\RemitterVerificationBuilderInterface; use TrueLayer\Interfaces\RequestOptionsInterface; use TrueLayer\Interfaces\Scheme\SchemeSelectionBuilderInterface; +use TrueLayer\Interfaces\SignupPlus\SignupPlusBuilderInterface; use TrueLayer\Interfaces\UserInterface; use TrueLayer\Interfaces\Webhook\WebhookInterface; use TrueLayer\Services\Util\PaymentId; @@ -73,7 +74,7 @@ public function __construct( ApiClientInterface $apiClient, ApiFactoryInterface $apiFactory, EntityFactoryInterface $entityFactory, - ClientConfigInterface $config + ClientConfigInterface $config, ) { $this->apiClient = $apiClient; $this->apiFactory = $apiFactory; @@ -419,4 +420,14 @@ public function requestOptions(): RequestOptionsInterface { return $this->entityFactory->make(RequestOptionsInterface::class); } + + /** + * @throws InvalidArgumentException + * + * @return SignupPlusBuilderInterface + */ + public function signupPlus(): SignupPlusBuilderInterface + { + return $this->entityFactory->make(SignupPlusBuilderInterface::class); + } } diff --git a/tests/integration/CastsAttributesTest.php b/tests/integration/CastsAttributesTest.php index 1896d151..bd76440e 100644 --- a/tests/integration/CastsAttributesTest.php +++ b/tests/integration/CastsAttributesTest.php @@ -5,7 +5,7 @@ use TrueLayer\Constants\DateTime; use TrueLayer\Traits\CastsAttributes; -$trait = new class() { +$trait = new class { use CastsAttributes; protected function make(string $abstract, ?array $data = null) From 141ccc925bd7282c4960317367a353f3d1a1f862 Mon Sep 17 00:00:00 2001 From: Stefan Adrian Danaita Date: Tue, 22 Oct 2024 21:10:29 +0100 Subject: [PATCH 2/9] [ACL-80] PHPDoc --- src/Entities/SignupPlus/SignupPlusAuthUriCreated.php | 6 ++++++ src/Interfaces/SignupPlus/SignupPlusBuilderInterface.php | 3 +++ 2 files changed, 9 insertions(+) diff --git a/src/Entities/SignupPlus/SignupPlusAuthUriCreated.php b/src/Entities/SignupPlus/SignupPlusAuthUriCreated.php index 10151fef..64ce0ffe 100644 --- a/src/Entities/SignupPlus/SignupPlusAuthUriCreated.php +++ b/src/Entities/SignupPlus/SignupPlusAuthUriCreated.php @@ -9,6 +9,9 @@ class SignupPlusAuthUriCreated extends Entity implements SignupPlusAuthUriCreatedInterface { + /** + * @var string + */ protected string $authUri; /** @@ -18,6 +21,9 @@ class SignupPlusAuthUriCreated extends Entity implements SignupPlusAuthUriCreate 'auth_uri', ]; + /** + * @return string + */ public function getAuthUri(): string { return $this->authUri; diff --git a/src/Interfaces/SignupPlus/SignupPlusBuilderInterface.php b/src/Interfaces/SignupPlus/SignupPlusBuilderInterface.php index a437f901..bf3c943c 100644 --- a/src/Interfaces/SignupPlus/SignupPlusBuilderInterface.php +++ b/src/Interfaces/SignupPlus/SignupPlusBuilderInterface.php @@ -6,5 +6,8 @@ interface SignupPlusBuilderInterface { + /** + * @return SignupPlusAuthUriRequestInterface + */ public function authUri(): SignupPlusAuthUriRequestInterface; } From 5737e4cf21b5187b0b8e36db90a5f7e6356e775f Mon Sep 17 00:00:00 2001 From: Stefan Adrian Danaita Date: Thu, 24 Oct 2024 13:52:33 +0100 Subject: [PATCH 3/9] [ACL-80] Integration tests --- .../integration/Mocks/SignupPlusResponse.php | 15 +++++++ tests/integration/SignupPlusTest.php | 41 +++++++++++++++++++ 2 files changed, 56 insertions(+) create mode 100644 tests/integration/Mocks/SignupPlusResponse.php create mode 100644 tests/integration/SignupPlusTest.php diff --git a/tests/integration/Mocks/SignupPlusResponse.php b/tests/integration/Mocks/SignupPlusResponse.php new file mode 100644 index 00000000..3552abce --- /dev/null +++ b/tests/integration/Mocks/SignupPlusResponse.php @@ -0,0 +1,15 @@ +signupPlus() + ->authUri() + ->paymentId($paymentId); + + if (!empty($state)) { + $request->state($state); + } + + $request->create(); + + \expect(\getRequestPayload(1))->toMatchArray([ + 'payment_id' => $paymentId, + 'state' => $state, + ]); +})->with([ + 'no state' => ['fake_payment_id', null], + 'with state' => ['fake_payment_id', 'fake_state'], +]); + +\it('parses the signup plus auth uri creation response correctly', function () { + $client = \client(SignupPlusResponse::authUriCreated()); + + $response = $client->signupPlus() + ->authUri() + ->paymentId('fake_payment_id') + ->state('fake_state') + ->create(); + + \expect($response)->toBeInstanceOf(SignupPlusAuthUriCreatedInterface::class); + \expect($response->getAuthUri())->toBeString(); +}); From 249fd16a5ed88b382c8dbd5c251499cdf5a5bc39 Mon Sep 17 00:00:00 2001 From: Stefan Adrian Danaita Date: Thu, 24 Oct 2024 19:37:56 +0100 Subject: [PATCH 4/9] [ACL-77] Signup Plus user data endpoints implementation --- config/bindings.php | 4 + src/Constants/Endpoints.php | 2 + src/Entities/Address.php | 92 +------------- src/Entities/AddressRetrieved.php | 100 +++++++++++++++ .../SignupPlus/SignupPlusAccountDetails.php | 73 +++++++++++ src/Entities/SignupPlus/SignupPlusBuilder.php | 11 ++ .../SignupPlus/SignupPlusUserDataRequest.php | 75 ++++++++++++ .../SignupPlusUserDataRetrieved.php | 115 ++++++++++++++++++ src/Interfaces/AddressInterface.php | 32 +---- src/Interfaces/AddressRetrievedInterface.php | 38 ++++++ src/Interfaces/Api/SignupPlusApiInterface.php | 22 ++++ .../SignupPlusAccountDetailsInterface.php | 30 +++++ .../SignupPlus/SignupPlusBuilderInterface.php | 5 + .../SignupPlusUserDataRequestInterface.php | 27 ++++ .../SignupPlusUserDataRetrievedInterface.php | 51 ++++++++ src/Services/Api/SignupPlusApi.php | 36 ++++++ 16 files changed, 591 insertions(+), 122 deletions(-) create mode 100644 src/Entities/AddressRetrieved.php create mode 100644 src/Entities/SignupPlus/SignupPlusAccountDetails.php create mode 100644 src/Entities/SignupPlus/SignupPlusUserDataRequest.php create mode 100644 src/Entities/SignupPlus/SignupPlusUserDataRetrieved.php create mode 100644 src/Interfaces/AddressRetrievedInterface.php create mode 100644 src/Interfaces/SignupPlus/SignupPlusAccountDetailsInterface.php create mode 100644 src/Interfaces/SignupPlus/SignupPlusUserDataRequestInterface.php create mode 100644 src/Interfaces/SignupPlus/SignupPlusUserDataRetrievedInterface.php diff --git a/config/bindings.php b/config/bindings.php index ef836d13..4fd2422c 100644 --- a/config/bindings.php +++ b/config/bindings.php @@ -9,6 +9,7 @@ Interfaces\HppInterface::class => 'makeHpp', Interfaces\AddressInterface::class => Entities\Address::class, + Interfaces\AddressRetrievedInterface::class => Entities\AddressRetrieved::class, Interfaces\UserInterface::class => Entities\User::class, Interfaces\Beneficiary\BeneficiaryBuilderInterface::class => Entities\Beneficiary\BeneficiaryBuilder::class, @@ -100,6 +101,9 @@ Interfaces\SignupPlus\SignupPlusBuilderInterface::class => Entities\SignupPlus\SignupPlusBuilder::class, Interfaces\SignupPlus\SignupPlusAuthUriRequestInterface::class => Entities\SignupPlus\SignupPlusAuthUriRequest::class, Interfaces\SignupPlus\SignupPlusAuthUriCreatedInterface::class => Entities\SignupPlus\SignupPlusAuthUriCreated::class, + Interfaces\SignupPlus\SignupPlusAccountDetailsInterface::class => Entities\SignupPlus\SignupPlusAccountDetails::class, + Interfaces\SignupPlus\SignupPlusUserDataRequestInterface::class => Entities\SignupPlus\SignupPlusUserDataRequest::class, + Interfaces\SignupPlus\SignupPlusUserDataRetrievedInterface::class => Entities\SignupPlus\SignupPlusUserDataRetrieved::class, Interfaces\RequestOptionsInterface::class => Entities\RequestOptions::class, ]; diff --git a/src/Constants/Endpoints.php b/src/Constants/Endpoints.php index ef7d9a02..adc713a1 100644 --- a/src/Constants/Endpoints.php +++ b/src/Constants/Endpoints.php @@ -36,4 +36,6 @@ class Endpoints public const PAYOUTS_CREATE = '/v3/payouts'; public const PAYOUTS_RETRIEVE = '/v3/payouts/{id}'; public const SIGNUP_PLUS_AUTH = '/signup-plus/authuri'; + public const SIGNUP_PLUS_PAYMENTS = '/signup-plus/payments?payment_id={id}'; + public const SIGNUP_PLUS_MANDATES = '/signup-plus/mandates?mandate_id={id}'; } diff --git a/src/Entities/Address.php b/src/Entities/Address.php index 6c20f51c..b4f1d9ad 100644 --- a/src/Entities/Address.php +++ b/src/Entities/Address.php @@ -6,58 +6,8 @@ use TrueLayer\Interfaces\AddressInterface; -class Address extends Entity implements AddressInterface +class Address extends AddressRetrieved implements AddressInterface { - /** - * @var string - */ - protected string $addressLine1; - - /** - * @var string - */ - protected string $addressLine2; - - /** - * @var string - */ - protected string $city; - - /** - * @var string - */ - protected string $state; - - /** - * @var string - */ - protected string $zip; - - /** - * @var string - */ - protected string $countryCode; - - /** - * @var string[] - */ - protected array $arrayFields = [ - 'address_line1', - 'address_line2', - 'city', - 'state', - 'zip', - 'country_code', - ]; - - /** - * @return string|null - */ - public function getAddressLine1(): ?string - { - return $this->addressLine1 ?? null; - } - /** * @param string $addressLine1 * @@ -70,14 +20,6 @@ public function addressLine1(string $addressLine1): AddressInterface return $this; } - /** - * @return string|null - */ - public function getAddressLine2(): ?string - { - return $this->addressLine2 ?? null; - } - /** * @param string $addressLine2 * @@ -90,14 +32,6 @@ public function addressLine2(string $addressLine2): AddressInterface return $this; } - /** - * @return string|null - */ - public function getCity(): ?string - { - return $this->city ?? null; - } - /** * @param string $city * @@ -110,14 +44,6 @@ public function city(string $city): AddressInterface return $this; } - /** - * @return string|null - */ - public function getState(): ?string - { - return $this->state ?? null; - } - /** * @param string $state * @@ -130,14 +56,6 @@ public function state(string $state): AddressInterface return $this; } - /** - * @return string|null - */ - public function getZip(): ?string - { - return $this->zip ?? null; - } - /** * @param string $zip * @@ -150,14 +68,6 @@ public function zip(string $zip): AddressInterface return $this; } - /** - * @return string|null - */ - public function getCountryCode(): ?string - { - return $this->countryCode ?? null; - } - /** * @param string $countryCode * diff --git a/src/Entities/AddressRetrieved.php b/src/Entities/AddressRetrieved.php new file mode 100644 index 00000000..e875e411 --- /dev/null +++ b/src/Entities/AddressRetrieved.php @@ -0,0 +1,100 @@ +addressLine1 ?? null; + } + + /** + * @return string|null + */ + public function getAddressLine2(): ?string + { + return $this->addressLine2 ?? null; + } + + /** + * @return string|null + */ + public function getCity(): ?string + { + return $this->city ?? null; + } + + /** + * @return string|null + */ + public function getState(): ?string + { + return $this->state ?? null; + } + + /** + * @return string|null + */ + public function getZip(): ?string + { + return $this->zip ?? null; + } + + /** + * @return string|null + */ + public function getCountryCode(): ?string + { + return $this->countryCode ?? null; + } +} diff --git a/src/Entities/SignupPlus/SignupPlusAccountDetails.php b/src/Entities/SignupPlus/SignupPlusAccountDetails.php new file mode 100644 index 00000000..0408c0c7 --- /dev/null +++ b/src/Entities/SignupPlus/SignupPlusAccountDetails.php @@ -0,0 +1,73 @@ +accountNumber; + } + + /** + * @return string + */ + public function getSortCode(): string + { + return $this->sortCode; + } + + /** + * @return string + */ + public function getIban(): string + { + return $this->iban; + } + + /** + * @return string + */ + public function getProviderId(): string + { + return $this->providerId; + } +} diff --git a/src/Entities/SignupPlus/SignupPlusBuilder.php b/src/Entities/SignupPlus/SignupPlusBuilder.php index b55d592d..d714497d 100644 --- a/src/Entities/SignupPlus/SignupPlusBuilder.php +++ b/src/Entities/SignupPlus/SignupPlusBuilder.php @@ -8,6 +8,7 @@ use TrueLayer\Exceptions\InvalidArgumentException; use TrueLayer\Interfaces\SignupPlus\SignupPlusAuthUriRequestInterface; use TrueLayer\Interfaces\SignupPlus\SignupPlusBuilderInterface; +use TrueLayer\Interfaces\SignupPlus\SignupPlusUserDataRequestInterface; final class SignupPlusBuilder extends EntityBuilder implements SignupPlusBuilderInterface { @@ -20,4 +21,14 @@ public function authUri(): SignupPlusAuthUriRequestInterface { return $this->entityFactory->make(SignupPlusAuthUriRequestInterface::class); } + + /** + * @throws InvalidArgumentException + * + * @return SignupPlusUserDataRequestInterface + */ + public function userData(): SignupPlusUserDataRequestInterface + { + return $this->entityFactory->make(SignupPlusUserDataRequestInterface::class); + } } diff --git a/src/Entities/SignupPlus/SignupPlusUserDataRequest.php b/src/Entities/SignupPlus/SignupPlusUserDataRequest.php new file mode 100644 index 00000000..24b770b4 --- /dev/null +++ b/src/Entities/SignupPlus/SignupPlusUserDataRequest.php @@ -0,0 +1,75 @@ +paymentId = $paymentId; + + return $this; + } + + /** + * @param string $mandateId + * + * @return SignupPlusUserDataRequestInterface + */ + public function mandateId(string $mandateId): SignupPlusUserDataRequestInterface + { + $this->mandateId = $mandateId; + + return $this; + } + + /** + * @throws ApiRequestJsonSerializationException + * @throws ApiResponseUnsuccessfulException + * @throws InvalidArgumentException + * @throws SignerException + * + * @return SignupPlusUserDataRetrievedInterface + */ + public function retrieve(): SignupPlusUserDataRetrievedInterface + { + if (empty($this->paymentId) && empty($this->mandateId)) { + throw new ApiRequestJsonSerializationException('You need to pass a payment or mandate id'); + } + + $data = !empty($this->paymentId) + ? $this->getApiFactory()->signupPlusApi()->retrieveUserDataByPaymentId($this->paymentId) + : $this->getApiFactory()->signupPlusApi()->retrieveUserDataByMandateId($this->mandateId); + + return $this->make(SignupPlusUserDataRetrievedInterface::class, $data); + } +} diff --git a/src/Entities/SignupPlus/SignupPlusUserDataRetrieved.php b/src/Entities/SignupPlus/SignupPlusUserDataRetrieved.php new file mode 100644 index 00000000..a32ac9d0 --- /dev/null +++ b/src/Entities/SignupPlus/SignupPlusUserDataRetrieved.php @@ -0,0 +1,115 @@ + AddressRetrievedInterface::class, + 'account_details' => SignupPlusAccountDetailsInterface::class, + ]; + + /** + * @var string[] + */ + protected array $arrayFields = [ + 'title', + 'first_name', + 'last_name', + 'date_of_birth', + 'address', + 'national_identification_number', + 'sex', + 'account_details', + ]; + + public function getTitle(): string + { + return $this->title; + } + + public function getFirstName(): string + { + return $this->firstName; + } + + public function getLastName(): string + { + return $this->lastName; + } + + public function getDateOfBirth(): string + { + return $this->dateOfBirth; + } + + public function getAddress(): AddressRetrievedInterface + { + return $this->address; + } + + public function getNationalIdentificationNumber(): string + { + return $this->nationalIdentificationNumber; + } + + public function getSex(): string + { + return $this->sex; + } + + public function getAccountDetails(): SignupPlusAccountDetailsInterface + { + return $this->accountDetails; + } +} diff --git a/src/Interfaces/AddressInterface.php b/src/Interfaces/AddressInterface.php index dfc301da..9219e83c 100644 --- a/src/Interfaces/AddressInterface.php +++ b/src/Interfaces/AddressInterface.php @@ -4,13 +4,8 @@ namespace TrueLayer\Interfaces; -interface AddressInterface extends ArrayableInterface, HasAttributesInterface +interface AddressInterface extends AddressRetrievedInterface { - /** - * @return string|null - */ - public function getAddressLine1(): ?string; - /** * @param string $addressLine1 * @@ -18,11 +13,6 @@ public function getAddressLine1(): ?string; */ public function addressLine1(string $addressLine1): AddressInterface; - /** - * @return string|null - */ - public function getAddressLine2(): ?string; - /** * @param string $addressLine2 * @@ -30,11 +20,6 @@ public function getAddressLine2(): ?string; */ public function addressLine2(string $addressLine2): AddressInterface; - /** - * @return string|null - */ - public function getCity(): ?string; - /** * @param string $city * @@ -42,11 +27,6 @@ public function getCity(): ?string; */ public function city(string $city): AddressInterface; - /** - * @return string|null - */ - public function getState(): ?string; - /** * @param string $state * @@ -54,11 +34,6 @@ public function getState(): ?string; */ public function state(string $state): AddressInterface; - /** - * @return string|null - */ - public function getZip(): ?string; - /** * @param string $zip * @@ -66,11 +41,6 @@ public function getZip(): ?string; */ public function zip(string $zip): AddressInterface; - /** - * @return string|null - */ - public function getCountryCode(): ?string; - /** * @param string $countryCode * diff --git a/src/Interfaces/AddressRetrievedInterface.php b/src/Interfaces/AddressRetrievedInterface.php new file mode 100644 index 00000000..d80d0939 --- /dev/null +++ b/src/Interfaces/AddressRetrievedInterface.php @@ -0,0 +1,38 @@ +payload($authUriRequest) ->post(); } + + /** + * @param string $paymentId + * + * @throws SignerException + * @throws ApiResponseUnsuccessfulException + * @throws ApiRequestJsonSerializationException + * + * @return mixed[] + */ + public function retrieveUserDataByPaymentId(string $paymentId): array + { + $uri = \str_replace('{id}', $paymentId, Endpoints::SIGNUP_PLUS_PAYMENTS); + + return (array) $this->request() + ->uri($uri) + ->get(); + } + + /** + * @param string $mandateId + * + * @throws ApiRequestJsonSerializationException + * @throws ApiResponseUnsuccessfulException + * @throws SignerException + * + * @return array|mixed[] + */ + public function retrieveUserDataByMandateId(string $mandateId): array + { + $uri = \str_replace('{id}', $mandateId, Endpoints::SIGNUP_PLUS_MANDATES); + + return (array) $this->request() + ->uri($uri) + ->get(); + } } From 339004c5ee64b6fea49e1def4f13c24888e4ed8c Mon Sep 17 00:00:00 2001 From: Stefan Adrian Danaita Date: Thu, 24 Oct 2024 21:07:05 +0100 Subject: [PATCH 5/9] [ACL-80] Better typings --- src/Entities/SignupPlus/SignupPlusAuthUriRequest.php | 7 ++++--- src/Interfaces/Api/SignupPlusApiInterface.php | 2 +- src/Services/Api/SignupPlusApi.php | 7 +++++-- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/Entities/SignupPlus/SignupPlusAuthUriRequest.php b/src/Entities/SignupPlus/SignupPlusAuthUriRequest.php index 4dfce419..805aaaba 100644 --- a/src/Entities/SignupPlus/SignupPlusAuthUriRequest.php +++ b/src/Entities/SignupPlus/SignupPlusAuthUriRequest.php @@ -24,9 +24,9 @@ class SignupPlusAuthUriRequest extends Entity implements SignupPlusAuthUriReques protected string $paymentId; /** - * @var string + * @var string|null */ - protected string $state; + protected ?string $state = null; /** * @var string[] @@ -71,7 +71,8 @@ public function state(string $state): SignupPlusAuthUriRequestInterface public function create(): SignupPlusAuthUriCreatedInterface { $data = $this->getApiFactory()->signupPlusApi()->createAuthUri( - $this->toArray(), + $this->paymentId, + $this->state, ); return $this->make(SignupPlusAuthUriCreatedInterface::class, $data); diff --git a/src/Interfaces/Api/SignupPlusApiInterface.php b/src/Interfaces/Api/SignupPlusApiInterface.php index c817add8..7513919a 100644 --- a/src/Interfaces/Api/SignupPlusApiInterface.php +++ b/src/Interfaces/Api/SignupPlusApiInterface.php @@ -19,7 +19,7 @@ interface SignupPlusApiInterface * * @return mixed[] */ - public function createAuthUri(array $authUriRequest): array; + public function createAuthUri(string $paymentId, ?string $state): array; /** * @param string $paymentId diff --git a/src/Services/Api/SignupPlusApi.php b/src/Services/Api/SignupPlusApi.php index 210e7436..bffb55ae 100644 --- a/src/Services/Api/SignupPlusApi.php +++ b/src/Services/Api/SignupPlusApi.php @@ -21,11 +21,14 @@ class SignupPlusApi extends Api implements SignupPlusApiInterface * * @return mixed[] */ - public function createAuthUri(array $authUriRequest): array + public function createAuthUri(string $paymentId, ?string $state): array { return (array) $this->request() ->uri(Endpoints::SIGNUP_PLUS_AUTH) - ->payload($authUriRequest) + ->payload([ + 'payment_id' => $paymentId, + 'state' => $state, + ]) ->post(); } From a9a16183d97d7fea05a0a897f1478561fa6b04fe Mon Sep 17 00:00:00 2001 From: Stefan Adrian Danaita Date: Thu, 9 Jan 2025 19:24:23 +0000 Subject: [PATCH 6/9] [ACL-80] Gitignore .phpunit.cache. Fix phpdoc comment --- .gitignore | 1 + src/Services/Api/SignupPlusApi.php | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index a6d27108..3dc61b62 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ .idea +.phpunit.cache .phpunit.result.cache .php-cs-fixer.cache vendor/ diff --git a/src/Services/Api/SignupPlusApi.php b/src/Services/Api/SignupPlusApi.php index bffb55ae..83717ac2 100644 --- a/src/Services/Api/SignupPlusApi.php +++ b/src/Services/Api/SignupPlusApi.php @@ -13,11 +13,12 @@ class SignupPlusApi extends Api implements SignupPlusApiInterface { /** - * @param mixed[] $authUriRequest + * @param string $paymentId + * @param string|null $state * + * @throws ApiRequestJsonSerializationException * @throws ApiResponseUnsuccessfulException * @throws SignerException - * @throws ApiRequestJsonSerializationException * * @return mixed[] */ From c7d30bf8aef8f38fda55b537d451c6ad7933fbc9 Mon Sep 17 00:00:00 2001 From: Stefan Adrian Danaita Date: Thu, 9 Jan 2025 19:26:17 +0000 Subject: [PATCH 7/9] [ACL-80] Fix phpdoc --- src/Interfaces/Api/SignupPlusApiInterface.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Interfaces/Api/SignupPlusApiInterface.php b/src/Interfaces/Api/SignupPlusApiInterface.php index 7513919a..dfb5d8d5 100644 --- a/src/Interfaces/Api/SignupPlusApiInterface.php +++ b/src/Interfaces/Api/SignupPlusApiInterface.php @@ -11,7 +11,8 @@ interface SignupPlusApiInterface { /** - * @param mixed[] $authUriRequest + * @param string $paymentId + * @param string|null $state * * @throws ApiResponseUnsuccessfulException * @throws SignerException From 55a7884645c886d7284a0cdee28a345754ae1887 Mon Sep 17 00:00:00 2001 From: Stefan Adrian Danaita Date: Tue, 14 Jan 2025 12:48:09 +0000 Subject: [PATCH 8/9] [ACL-80] Integration tests and user retrieval improvements --- .../SignupPlus/SignupPlusAccountDetails.php | 18 +++--- .../SignupPlusUserDataRetrieved.php | 21 +++++-- .../SignupPlusAccountDetailsInterface.php | 12 ++-- .../SignupPlusUserDataRetrievedInterface.php | 12 ++-- .../integration/Mocks/SignupPlusResponse.php | 10 +++ tests/integration/SignupPlusTest.php | 62 +++++++++++++++++++ 6 files changed, 108 insertions(+), 27 deletions(-) diff --git a/src/Entities/SignupPlus/SignupPlusAccountDetails.php b/src/Entities/SignupPlus/SignupPlusAccountDetails.php index 0408c0c7..20d42989 100644 --- a/src/Entities/SignupPlus/SignupPlusAccountDetails.php +++ b/src/Entities/SignupPlus/SignupPlusAccountDetails.php @@ -40,27 +40,27 @@ class SignupPlusAccountDetails extends Entity implements SignupPlusAccountDetail ]; /** - * @return string + * @return string|null */ - public function getAccountNumber(): string + public function getAccountNumber(): ?string { - return $this->accountNumber; + return $this->accountNumber ?? null; } /** - * @return string + * @return string|null */ - public function getSortCode(): string + public function getSortCode(): ?string { - return $this->sortCode; + return $this->sortCode ?? null; } /** - * @return string + * @return string|null */ - public function getIban(): string + public function getIban(): ?string { - return $this->iban; + return $this->iban ?? null; } /** diff --git a/src/Entities/SignupPlus/SignupPlusUserDataRetrieved.php b/src/Entities/SignupPlus/SignupPlusUserDataRetrieved.php index a32ac9d0..22c05a8e 100644 --- a/src/Entities/SignupPlus/SignupPlusUserDataRetrieved.php +++ b/src/Entities/SignupPlus/SignupPlusUserDataRetrieved.php @@ -73,9 +73,12 @@ class SignupPlusUserDataRetrieved extends Entity implements SignupPlusUserDataRe 'account_details', ]; - public function getTitle(): string + /** + * @return string|null + */ + public function getTitle(): ?string { - return $this->title; + return $this->title ?? null; } public function getFirstName(): string @@ -98,14 +101,20 @@ public function getAddress(): AddressRetrievedInterface return $this->address; } - public function getNationalIdentificationNumber(): string + /** + * @return string|null + */ + public function getNationalIdentificationNumber(): ?string { - return $this->nationalIdentificationNumber; + return $this->nationalIdentificationNumber ?? null; } - public function getSex(): string + /** + * @return string|null + */ + public function getSex(): ?string { - return $this->sex; + return $this->sex ?? null; } public function getAccountDetails(): SignupPlusAccountDetailsInterface diff --git a/src/Interfaces/SignupPlus/SignupPlusAccountDetailsInterface.php b/src/Interfaces/SignupPlus/SignupPlusAccountDetailsInterface.php index 1bff54a8..cabdfc12 100644 --- a/src/Interfaces/SignupPlus/SignupPlusAccountDetailsInterface.php +++ b/src/Interfaces/SignupPlus/SignupPlusAccountDetailsInterface.php @@ -9,19 +9,19 @@ interface SignupPlusAccountDetailsInterface extends HasAttributesInterface { /** - * @return string + * @return string|null */ - public function getAccountNumber(): string; + public function getAccountNumber(): ?string; /** - * @return string + * @return string|null */ - public function getSortCode(): string; + public function getSortCode(): ?string; /** - * @return string + * @return string|null */ - public function getIban(): string; + public function getIban(): ?string; /** * @return string diff --git a/src/Interfaces/SignupPlus/SignupPlusUserDataRetrievedInterface.php b/src/Interfaces/SignupPlus/SignupPlusUserDataRetrievedInterface.php index fc1d254a..dfbc5e35 100644 --- a/src/Interfaces/SignupPlus/SignupPlusUserDataRetrievedInterface.php +++ b/src/Interfaces/SignupPlus/SignupPlusUserDataRetrievedInterface.php @@ -10,9 +10,9 @@ interface SignupPlusUserDataRetrievedInterface extends HasAttributesInterface { /** - * @return string + * @return string|null */ - public function getTitle(): string; + public function getTitle(): ?string; /** * @return string @@ -35,14 +35,14 @@ public function getDateOfBirth(): string; public function getAddress(): AddressRetrievedInterface; /** - * @return string + * @return string|null */ - public function getNationalIdentificationNumber(): string; + public function getNationalIdentificationNumber(): ?string; /** - * @return string + * @return string|null */ - public function getSex(): string; + public function getSex(): ?string; /** * @return SignupPlusAccountDetailsInterface diff --git a/tests/integration/Mocks/SignupPlusResponse.php b/tests/integration/Mocks/SignupPlusResponse.php index 3552abce..c8b2300e 100644 --- a/tests/integration/Mocks/SignupPlusResponse.php +++ b/tests/integration/Mocks/SignupPlusResponse.php @@ -12,4 +12,14 @@ public static function authUriCreated(): Response { return new Response(201, [], '{"auth_uri":"https://demo-api.gii.cloud/api/oauth/auth_proxy?id=863619242079485953&uuid=b912cc0d-149b-40a2-8a24-79a9d1f0913e"}'); } + + public static function userDataRetrievedFinland(): Response + { + return new Response(200, [], '{"first_name": "Tero Testi","last_name": "Äyrämö","date_of_birth": "1970-01-01","national_identification_number": "010170-1234","sex": "M","address": {"address_line1": "Kauppa Puistikko 6 B 15","city": "VAASA","zip": "65100","country": "FI"},"account_details": {"iban": "FI53CLRB04066200002723","provider_id": "fi-op"}}'); + } + + public static function userDataRetrievedUk(): Response + { + return new Response(200, [], '{"title": "Mr","first_name": "Sherlock","last_name": "Holmes","date_of_birth": "1854-01-06","address": {"address_line1": "221B Baker St","address_line2": "Flat 2","city": "London","state": "Greater London","zip": "NW1 6XE","country": "GB"},"account_details": {"account_number": "41921234","sort_code": "04-01-02","iban": "GB71MONZ04435141923452","provider_id": "ob-monzo"}}'); + } } diff --git a/tests/integration/SignupPlusTest.php b/tests/integration/SignupPlusTest.php index 16564905..49ee1ce1 100644 --- a/tests/integration/SignupPlusTest.php +++ b/tests/integration/SignupPlusTest.php @@ -2,7 +2,10 @@ declare(strict_types=1); +use TrueLayer\Interfaces\AddressRetrievedInterface; +use TrueLayer\Interfaces\SignupPlus\SignupPlusAccountDetailsInterface; use TrueLayer\Interfaces\SignupPlus\SignupPlusAuthUriCreatedInterface; +use TrueLayer\Interfaces\SignupPlus\SignupPlusUserDataRetrievedInterface; use TrueLayer\Tests\Integration\Mocks\SignupPlusResponse; \it('sends correct payload on signup plus auth link creation', function (string $paymentId, ?string $state) { @@ -39,3 +42,62 @@ \expect($response)->toBeInstanceOf(SignupPlusAuthUriCreatedInterface::class); \expect($response->getAuthUri())->toBeString(); }); + +\it('retrieves the user data by payment id', function () { + $client = \client(SignupPlusResponse::userDataRetrievedFinland()); + + $response = $client->signupPlus() + ->userData() + ->paymentId('fake_payment_id') + ->retrieve(); + + \expect($response)->toBeInstanceOf(SignupPlusUserDataRetrievedInterface::class); + + \expect($response->getTitle())->toBeNull(); + \expect($response->getFirstName())->toBe('Tero Testi'); + \expect($response->getLastName())->toBe('Äyrämö'); + \expect($response->getNationalIdentificationNumber())->toBe('010170-1234'); + \expect($response->getSex())->toBe('M'); + + $address = $response->getAddress(); + \expect($address)->toBeInstanceOf(AddressRetrievedInterface::class); + \expect($address->getAddressLine1())->toBe('Kauppa Puistikko 6 B 15'); + \expect($address->getCity())->toBe('VAASA'); + \expect($address->getZip())->toBe('65100'); + + $accountDetails = $response->getAccountDetails(); + \expect($accountDetails)->toBeInstanceOf(SignupPlusAccountDetailsInterface::class); + \expect($accountDetails->getIban())->toBe('FI53CLRB04066200002723'); + \expect($accountDetails->getProviderId())->toBe('fi-op'); +}); + +\it('retrieves the user data by mandate id', function () { + $client = \client(SignupPlusResponse::userDataRetrievedUk()); + + $response = $client->signupPlus() + ->userData() + ->mandateId('fake_mandate_id') + ->retrieve(); + + \expect($response)->toBeInstanceOf(SignupPlusUserDataRetrievedInterface::class); + + \expect($response->getTitle())->toBe('Mr'); + \expect($response->getFirstName())->toBe('Sherlock'); + \expect($response->getLastName())->toBe('Holmes'); + \expect($response->getDateOfBirth())->toBe('1854-01-06'); + + $address = $response->getAddress(); + \expect($address)->toBeInstanceOf(AddressRetrievedInterface::class); + \expect($address->getAddressLine1())->toBe('221B Baker St'); + \expect($address->getAddressLine2())->toBe('Flat 2'); + \expect($address->getCity())->toBe('London'); + \expect($address->getState())->toBe('Greater London'); + \expect($address->getZip())->toBe('NW1 6XE'); + + $accountDetails = $response->getAccountDetails(); + \expect($accountDetails)->toBeInstanceOf(SignupPlusAccountDetailsInterface::class); + \expect($accountDetails->getAccountNumber())->toBe('41921234'); + \expect($accountDetails->getSortCode())->toBe('04-01-02'); + \expect($accountDetails->getIban())->toBe('GB71MONZ04435141923452'); + \expect($accountDetails->getProviderId())->toBe('ob-monzo'); +}); From 7599a35bdcaca9d62c182cc15ad041b77a001b7b Mon Sep 17 00:00:00 2001 From: Stefan Adrian Danaita Date: Thu, 27 Feb 2025 12:50:32 +0000 Subject: [PATCH 9/9] [ACL-80] Changelog and readme changes for SignUp+ endpoints --- CHANGELOG.md | 7 +++++++ README.md | 32 ++++++++++++++++++++++++++++---- 2 files changed, 35 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 04eb81b8..78dcadb0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,13 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [3.0.2] - 2025-02-27 + +### Added + +- Support for SignUp Plus authentication link creation +- Support for SignUp Plus user data retrieval + ## [3.0.1] - 2025-01-09 ### Changed diff --git a/README.md b/README.md index 01ead03e..d49499f0 100644 --- a/README.md +++ b/README.md @@ -34,10 +34,11 @@ 10. [Payouts](#payouts) 11. [Merchant accounts](#merchant-accounts) 12. [Account identifiers](#account-identifiers) -13. [Receiving webhook notifications](#webhooks) -14. [Custom idempotency keys](#idempotency) -15. [Custom API calls](#custom-api-calls) -16. [Error Handling](#error-handling) +13. [SignUp Plus](#signup-plus) +14. [Receiving webhook notifications](#webhooks) +15. [Custom idempotency keys](#idempotency) +16. [Custom API calls](#custom-api-calls) +17. [Error Handling](#error-handling) @@ -1011,6 +1012,29 @@ foreach ($merchantAccount->getAccountIdentifiers() as $accountIdentifier) { } ``` + + +# SignUp Plus + +Generating a SignUp Plus authentication link: + +```php +$client->signupPlus() + ->authUri() + ->paymentId('some_payment_id') + ->state('some_state') + ->create(); +``` + +Retrieving user data: + +```php +$response = $client->signupPlus() + ->userData() + ->paymentId('fake_payment_id') + ->retrieve(); // SignupPlusUserDataRetrievedInterface +``` + # Receiving webhook notifications