From ce13cb8ba95181b0ddfb5dbbd83e66fb564bd64c Mon Sep 17 00:00:00 2001 From: Alexandru Lighezan Date: Fri, 7 Aug 2026 16:30:23 +0100 Subject: [PATCH] Add support for related_products --- CHANGELOG.md | 1 + README.md | 14 ++++++ config/bindings.php | 1 + .../Payment/PaymentRelatedProducts.php | 48 +++++++++++++++++++ src/Entities/Payment/PaymentRequest.php | 22 +++++++++ .../PaymentRelatedProductsInterface.php | 22 +++++++++ .../Payment/PaymentRequestInterface.php | 7 +++ tests/integration/PaymentCreateTest.php | 20 ++++++++ 8 files changed, 135 insertions(+) create mode 100644 src/Entities/Payment/PaymentRelatedProducts.php create mode 100644 src/Interfaces/Payment/PaymentRelatedProductsInterface.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 78c621b..90b42c8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ### Added - Support for `hosted_page` on payment creation (`return_uri`, `country_code`, `language_code`, `max_wait_for_result`), exposing `getHostedPageUri()` on the created payment +- Support for `related_products.signup_plus` on payment creation via `$payment->relatedProducts()->signupPlus()` ## [3.3.0] - 2025-09-17 diff --git a/README.md b/README.md index d9355a7..429224a 100644 --- a/README.md +++ b/README.md @@ -373,6 +373,20 @@ $payment = $payment->create(); $payment->getHostedPageUri(); // The URI to redirect your user to ``` +You can also enable related products on the payment, such as [Signup+](https://docs.truelayer.com/docs/signup-basics): + +```php +$payment = $client->payment() + ->user($user) + ->amountInMinor(1) + ->currency(\TrueLayer\Constants\PaymentCurrencies::GBP) + ->paymentMethod($paymentMethod); + +$payment->relatedProducts()->signupPlus(); + +$payment = $payment->create(); +``` + ### 5. Creating a payment from an array diff --git a/config/bindings.php b/config/bindings.php index 11e577a..d109d96 100644 --- a/config/bindings.php +++ b/config/bindings.php @@ -19,6 +19,7 @@ Interfaces\Payment\PaymentRequestInterface::class => Entities\Payment\PaymentRequest::class, Interfaces\Payment\PaymentRiskAssessmentInterface::class => Entities\Payment\PaymentRiskAssessment::class, Interfaces\Payment\PaymentHostedPageInterface::class => Entities\Payment\PaymentHostedPage::class, + Interfaces\Payment\PaymentRelatedProductsInterface::class => Entities\Payment\PaymentRelatedProducts::class, Interfaces\Payment\PaymentCreatedInterface::class => Entities\Payment\PaymentCreated::class, Interfaces\Payment\PaymentAuthorizationRequiredInterface::class => Entities\Payment\PaymentRetrieved\PaymentAuthorizationRequired::class, Interfaces\Payment\PaymentAuthorizingInterface::class => Entities\Payment\PaymentRetrieved\PaymentAuthorizing::class, diff --git a/src/Entities/Payment/PaymentRelatedProducts.php b/src/Entities/Payment/PaymentRelatedProducts.php new file mode 100644 index 0000000..0d59d2f --- /dev/null +++ b/src/Entities/Payment/PaymentRelatedProducts.php @@ -0,0 +1,48 @@ + \stdClass::class, + ]; + + /** + * @var string[] + */ + protected array $arrayFields = [ + 'signup_plus', + ]; + + /** + * @return PaymentRelatedProductsInterface + */ + public function signupPlus(): PaymentRelatedProductsInterface + { + $this->signupPlus = new \stdClass(); + + return $this; + } + + /** + * @return bool + */ + public function isSignupPlusEnabled(): bool + { + return (bool) ($this->signupPlus ?? null); + } +} diff --git a/src/Entities/Payment/PaymentRequest.php b/src/Entities/Payment/PaymentRequest.php index ff3e2ca..59c1b2c 100644 --- a/src/Entities/Payment/PaymentRequest.php +++ b/src/Entities/Payment/PaymentRequest.php @@ -12,6 +12,7 @@ use TrueLayer\Interfaces\HasApiFactoryInterface; use TrueLayer\Interfaces\Payment\PaymentCreatedInterface; use TrueLayer\Interfaces\Payment\PaymentHostedPageInterface; +use TrueLayer\Interfaces\Payment\PaymentRelatedProductsInterface; use TrueLayer\Interfaces\Payment\PaymentRequestInterface; use TrueLayer\Interfaces\Payment\PaymentRiskAssessmentInterface; use TrueLayer\Interfaces\PaymentMethod\PaymentMethodInterface; @@ -63,6 +64,11 @@ final class PaymentRequest extends Entity implements PaymentRequestInterface, Ha */ protected PaymentHostedPageInterface $hostedPage; + /** + * @var PaymentRelatedProductsInterface + */ + protected PaymentRelatedProductsInterface $relatedProducts; + /** * @var string[] */ @@ -71,6 +77,7 @@ final class PaymentRequest extends Entity implements PaymentRequestInterface, Ha 'risk_assessment' => PaymentRiskAssessmentInterface::class, 'user' => UserInterface::class, 'hosted_page' => PaymentHostedPageInterface::class, + 'related_products' => PaymentRelatedProductsInterface::class, ]; /** @@ -84,6 +91,7 @@ final class PaymentRequest extends Entity implements PaymentRequestInterface, Ha 'risk_assessment', 'user', 'hosted_page', + 'related_products', ]; /** @@ -176,6 +184,20 @@ public function hostedPage(?PaymentHostedPageInterface $hostedPage = null): Paym return $this->hostedPage; } + /** + * @param PaymentRelatedProductsInterface|null $relatedProducts + * + * @throws InvalidArgumentException + * + * @return PaymentRelatedProductsInterface + */ + public function relatedProducts(?PaymentRelatedProductsInterface $relatedProducts = null): PaymentRelatedProductsInterface + { + $this->relatedProducts = $relatedProducts ?: $this->entityFactory->make(PaymentRelatedProductsInterface::class); + + return $this->relatedProducts; + } + /** * @param RequestOptionsInterface $requestOptions * diff --git a/src/Interfaces/Payment/PaymentRelatedProductsInterface.php b/src/Interfaces/Payment/PaymentRelatedProductsInterface.php new file mode 100644 index 0000000..dc78989 --- /dev/null +++ b/src/Interfaces/Payment/PaymentRelatedProductsInterface.php @@ -0,0 +1,22 @@ +payment($factory->newUser(), $factory->bankTransferMethod($factory->sortCodeBeneficiary()))->create(); + + $payload = \json_decode(\getRequestPayload(1, false), false); + \expect($payload->related_products ?? null)->toBeNull(); +}); + +\it('sends related products signup plus on payment creation', function () { + $factory = CreatePayment::responses([PaymentResponse::created()]); + + $payment = $factory->payment($factory->newUser(), $factory->bankTransferMethod($factory->sortCodeBeneficiary())); + $payment->relatedProducts()->signupPlus(); + + $payment->create(); + + $payload = \json_decode(\getRequestPayload(1, false), false); + \expect($payload->related_products->signup_plus)->toBeInstanceOf(stdClass::class); +}); + \it('has no hosted page uri when the API does not return one', function () { $factory = CreatePayment::responses([PaymentResponse::created()]);