Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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();
```

<a name="creating-a-payment-from-array"></a>

### 5. Creating a payment from an array
Expand Down
1 change: 1 addition & 0 deletions config/bindings.php
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
48 changes: 48 additions & 0 deletions src/Entities/Payment/PaymentRelatedProducts.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?php

declare(strict_types=1);

namespace TrueLayer\Entities\Payment;

use TrueLayer\Entities\Entity;
use TrueLayer\Interfaces\Payment\PaymentRelatedProductsInterface;

final class PaymentRelatedProducts extends Entity implements PaymentRelatedProductsInterface
{
/**
* @var \stdClass|null
*/
protected ?\stdClass $signupPlus;

/**
* @var string[]
*/
protected array $casts = [
'signup_plus' => \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);
}
}
22 changes: 22 additions & 0 deletions src/Entities/Payment/PaymentRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -63,6 +64,11 @@ final class PaymentRequest extends Entity implements PaymentRequestInterface, Ha
*/
protected PaymentHostedPageInterface $hostedPage;

/**
* @var PaymentRelatedProductsInterface
*/
protected PaymentRelatedProductsInterface $relatedProducts;

/**
* @var string[]
*/
Expand All @@ -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,
];

/**
Expand All @@ -84,6 +91,7 @@ final class PaymentRequest extends Entity implements PaymentRequestInterface, Ha
'risk_assessment',
'user',
'hosted_page',
'related_products',
];

/**
Expand Down Expand Up @@ -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
*
Expand Down
22 changes: 22 additions & 0 deletions src/Interfaces/Payment/PaymentRelatedProductsInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

declare(strict_types=1);

namespace TrueLayer\Interfaces\Payment;

use TrueLayer\Interfaces\ArrayableInterface;

interface PaymentRelatedProductsInterface extends ArrayableInterface
{
/**
* Enable the Signup+ product on the payment.
*
* @return PaymentRelatedProductsInterface
*/
public function signupPlus(): PaymentRelatedProductsInterface;

/**
* @return bool
*/
public function isSignupPlusEnabled(): bool;
}
7 changes: 7 additions & 0 deletions src/Interfaces/Payment/PaymentRequestInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,13 @@ public function user(UserInterface $user): PaymentRequestInterface;
*/
public function hostedPage(?PaymentHostedPageInterface $hostedPage = null): PaymentHostedPageInterface;

/**
* @param PaymentRelatedProductsInterface|null $relatedProducts
*
* @return PaymentRelatedProductsInterface
*/
public function relatedProducts(?PaymentRelatedProductsInterface $relatedProducts = null): PaymentRelatedProductsInterface;

/**
* @param RequestOptionsInterface $requestOptions
*
Expand Down
20 changes: 20 additions & 0 deletions tests/integration/PaymentCreateTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,26 @@
]);
});

\it('does not send related products field', function () {
$factory = CreatePayment::responses([PaymentResponse::created()]);
$factory->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()]);

Expand Down