Skip to content
Merged
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 `payment_disputed`, `payment_reversed` and `payment_funds_received` webhooks
- Support for `settlement_stalled_at`, `reversed_at` and `reversal_reason` on a failed payment, exposing `getSettlementStalledAt()`, `getReversedAt()` and `getReversalReason()` on `PaymentFailedInterface`
- Support for `failed_attempts` on a retrieved payment, exposing `getFailedAttempts()` on all payment statuses

Expand Down
21 changes: 21 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1184,6 +1184,9 @@ This library supports handlers for the following event types:
- payment_failed
- payment_creditable
- payment_settlement_stalled
- payment_disputed
- payment_reversed
- payment_funds_received
- refund_executed
- refund_failed
- payout_executed
Expand Down Expand Up @@ -1260,6 +1263,24 @@ $client->webhook()
// Inherits from PaymentEventInterface so provides same methods plus:
$event->getSettlementStalledAt();
})
->handler(function (Webhook\PaymentDisputedEventInterface $event) {
// Handle payment disputed
// Sent when an indemnity claim has been made against a payment. Currently only applies to payments made via direct debit mandates.
// Inherits from PaymentEventInterface so provides same methods plus:
$event->getDisputedAt();
$event->getDisputedReason();
$event->getExpiresAt(); // The date and time the dispute will expire and, by default, be resolved in favour of the end-user.
})
->handler(function (Webhook\PaymentReversedEventInterface $event) {
// Handle payment reversed
$event->getReversedAt();
$event->getReversalReason(); // One of `corrected`, `failed` or `disputed`.
})
->handler(function (Webhook\PaymentFundsReceivedEventInterface $event) {
// Handle payment funds received
// Sent when a Direct Debit payment's funds arrive in the merchant account. The payment can still be reversed at this stage.
$event->getFundsReceivedAt();
})
->handler(function (Webhook\RefundEventInterface $event) {
// Handle any refund event
$event->getPaymentId();
Expand Down
3 changes: 3 additions & 0 deletions config/bindings.php
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,9 @@
Interfaces\Webhook\PaymentSettledEventInterface::class => Entities\Webhook\PaymentSettledEvent::class,
Interfaces\Webhook\PaymentCreditableEventInterface::class => Entities\Webhook\PaymentCreditableEvent::class,
Interfaces\Webhook\PaymentSettlementStalledEventInterface::class => Entities\Webhook\PaymentSettlementStalledEvent::class,
Interfaces\Webhook\PaymentDisputedEventInterface::class => Entities\Webhook\PaymentDisputedEvent::class,
Interfaces\Webhook\PaymentReversedEventInterface::class => Entities\Webhook\PaymentReversedEvent::class,
Interfaces\Webhook\PaymentFundsReceivedEventInterface::class => Entities\Webhook\PaymentFundsReceivedEvent::class,
Interfaces\Webhook\RefundExecutedEventInterface::class => Entities\Webhook\RefundExecutedEvent::class,
Interfaces\Webhook\RefundFailedEventInterface::class => Entities\Webhook\RefundFailedEvent::class,
Interfaces\Webhook\PayoutExecutedEventInterface::class => Entities\Webhook\PayoutExecutedEvent::class,
Expand Down
3 changes: 3 additions & 0 deletions config/discriminations.php
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,9 @@
WebhookEventTypes::PAYMENT_SETTLED => Interfaces\Webhook\PaymentSettledEventInterface::class,
WebhookEventTypes::PAYMENT_CREDITABLE => Interfaces\Webhook\PaymentCreditableEventInterface::class,
WebhookEventTypes::PAYMENT_SETTLEMENT_STALLED => Interfaces\Webhook\PaymentSettlementStalledEventInterface::class,
WebhookEventTypes::PAYMENT_DISPUTED => Interfaces\Webhook\PaymentDisputedEventInterface::class,
WebhookEventTypes::PAYMENT_REVERSED => Interfaces\Webhook\PaymentReversedEventInterface::class,
WebhookEventTypes::PAYMENT_FUNDS_RECEIVED => Interfaces\Webhook\PaymentFundsReceivedEventInterface::class,
WebhookEventTypes::REFUND_EXECUTED => Interfaces\Webhook\RefundExecutedEventInterface::class,
WebhookEventTypes::REFUND_FAILED => Interfaces\Webhook\RefundFailedEventInterface::class,
WebhookEventTypes::PAYOUT_EXECUTED => Interfaces\Webhook\PayoutExecutedEventInterface::class,
Expand Down
3 changes: 3 additions & 0 deletions src/Constants/WebhookEventTypes.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ class WebhookEventTypes
public const PAYMENT_SETTLED = 'payment_settled';
public const PAYMENT_CREDITABLE = 'payment_creditable';
public const PAYMENT_SETTLEMENT_STALLED = 'payment_settlement_stalled';
public const PAYMENT_DISPUTED = 'payment_disputed';
public const PAYMENT_REVERSED = 'payment_reversed';
public const PAYMENT_FUNDS_RECEIVED = 'payment_funds_received';
public const EXTERNAL_PAYMENT_RECEIVED = 'external_payment_received';

public const REFUND_EXECUTED = 'refund_executed';
Expand Down
72 changes: 72 additions & 0 deletions src/Entities/Webhook/PaymentDisputedEvent.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
<?php

declare(strict_types=1);

namespace TrueLayer\Entities\Webhook;

use TrueLayer\Interfaces\Webhook\PaymentDisputedEventInterface;

class PaymentDisputedEvent extends PaymentEvent implements PaymentDisputedEventInterface
{
/**
* @var \DateTimeInterface
*/
protected \DateTimeInterface $disputedAt;

/**
* @var string
*/
protected string $disputedReason;

/**
* @var \DateTimeInterface
*/
protected \DateTimeInterface $expiresAt;

/**
* @return mixed[]
*/
protected function casts(): array
{
return \array_merge(parent::casts(), [
'disputed_at' => \DateTimeInterface::class,
'expires_at' => \DateTimeInterface::class,
]);
}

/**
* @return mixed[]
*/
protected function arrayFields(): array
{
return \array_merge(parent::arrayFields(), [
'disputed_at',
'disputed_reason',
'expires_at',
]);
}

/**
* @return \DateTimeInterface
*/
public function getDisputedAt(): \DateTimeInterface
{
return $this->disputedAt;
}

/**
* @return string|null
*/
public function getDisputedReason(): ?string
{
return $this->disputedReason ?? null;
}

/**
* @return \DateTimeInterface
*/
public function getExpiresAt(): \DateTimeInterface
{
return $this->expiresAt;
}
}
43 changes: 43 additions & 0 deletions src/Entities/Webhook/PaymentFundsReceivedEvent.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?php

declare(strict_types=1);

namespace TrueLayer\Entities\Webhook;

use TrueLayer\Interfaces\Webhook\PaymentFundsReceivedEventInterface;

class PaymentFundsReceivedEvent extends PaymentEvent implements PaymentFundsReceivedEventInterface
{
/**
* @var \DateTimeInterface
*/
protected \DateTimeInterface $fundsReceivedAt;

/**
* @return mixed[]
*/
protected function casts(): array
{
return \array_merge(parent::casts(), [
'funds_received_at' => \DateTimeInterface::class,
]);
}

/**
* @return mixed[]
*/
protected function arrayFields(): array
{
return \array_merge(parent::arrayFields(), [
'funds_received_at',
]);
}

/**
* @return \DateTimeInterface
*/
public function getFundsReceivedAt(): \DateTimeInterface
{
return $this->fundsReceivedAt;
}
}
57 changes: 57 additions & 0 deletions src/Entities/Webhook/PaymentReversedEvent.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<?php

declare(strict_types=1);

namespace TrueLayer\Entities\Webhook;

use TrueLayer\Interfaces\Webhook\PaymentReversedEventInterface;

class PaymentReversedEvent extends PaymentEvent implements PaymentReversedEventInterface
{
/**
* @var \DateTimeInterface
*/
protected \DateTimeInterface $reversedAt;

/**
* @var string
*/
protected string $reversalReason;

/**
* @return mixed[]
*/
protected function casts(): array
{
return \array_merge(parent::casts(), [
'reversed_at' => \DateTimeInterface::class,
]);
}

/**
* @return mixed[]
*/
protected function arrayFields(): array
{
return \array_merge(parent::arrayFields(), [
'reversed_at',
'reversal_reason',
]);
}

/**
* @return \DateTimeInterface
*/
public function getReversedAt(): \DateTimeInterface
{
return $this->reversedAt;
}

/**
* @return string
*/
public function getReversalReason(): string
{
return $this->reversalReason;
}
}
29 changes: 29 additions & 0 deletions src/Interfaces/Webhook/PaymentDisputedEventInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

declare(strict_types=1);

namespace TrueLayer\Interfaces\Webhook;

interface PaymentDisputedEventInterface extends PaymentEventInterface
{
/**
* Get the date and time that TrueLayer received an indemnity claim for the payment.
*
* @return \DateTimeInterface
*/
public function getDisputedAt(): \DateTimeInterface;

/**
* Get the reason that the indemnity claim was made against the payment.
*
* @return string|null
*/
public function getDisputedReason(): ?string;

/**
* Get the date and time that the dispute will expire and, by default, be resolved in favour of the end-user.
*
* @return \DateTimeInterface
*/
public function getExpiresAt(): \DateTimeInterface;
}
16 changes: 16 additions & 0 deletions src/Interfaces/Webhook/PaymentFundsReceivedEventInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

declare(strict_types=1);

namespace TrueLayer\Interfaces\Webhook;

interface PaymentFundsReceivedEventInterface extends PaymentEventInterface
{
/**
* Get the date and time that the Direct Debit payment arrived in the merchant account.
* The payment can still be reversed at this stage.
*
* @return \DateTimeInterface
*/
public function getFundsReceivedAt(): \DateTimeInterface;
}
23 changes: 23 additions & 0 deletions src/Interfaces/Webhook/PaymentReversedEventInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

declare(strict_types=1);

namespace TrueLayer\Interfaces\Webhook;

interface PaymentReversedEventInterface extends PaymentEventInterface
{
/**
* Get the date and time the payment was reversed.
*
* @return \DateTimeInterface
*/
public function getReversedAt(): \DateTimeInterface;

/**
* Get the reason for the payment reversal.
* Can be one of "corrected", "failed", "disputed".
*
* @return string
*/
public function getReversalReason(): string;
}
64 changes: 64 additions & 0 deletions tests/integration/Mocks/WebhookPayload.php
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,70 @@ public static function paymentSettlementStalled(): string
}';
}

public static function paymentDisputed(): string
{
return '{
"type": "payment_disputed",
"event_version": 1,
"event_id": "b8d4dda0-ff2c-4d77-a6da-4615e4bad941",
"payment_id": "60c0a60ed8d7-4e5b-ac79-401b1d8a8633",
"disputed_at": "2024-10-14T14:02:26.825Z",
"disputed_reason": "unauthorized_direct_debit",
"expires_at": "2024-10-21T14:02:26.825Z",
"metadata": {
"key1": "value1",
"key2": "value2"
}
}';
}

public static function paymentDisputedNoReason(): string
{
return '{
"type": "payment_disputed",
"event_version": 1,
"event_id": "b8d4dda0-ff2c-4d77-a6da-4615e4bad941",
"payment_id": "60c0a60ed8d7-4e5b-ac79-401b1d8a8633",
"disputed_at": "2024-10-14T14:02:26.825Z",
"expires_at": "2024-10-21T14:02:26.825Z",
"metadata": {
"key1": "value1",
"key2": "value2"
}
}';
}

public static function paymentReversed(): string
{
return '{
"type": "payment_reversed",
"event_version": 1,
"event_id": "b8d4dda0-ff2c-4d77-a6da-4615e4bad941",
"payment_id": "60c0a60ed8d7-4e5b-ac79-401b1d8a8633",
"reversed_at": "2024-10-14T14:02:26.825Z",
"reversal_reason": "corrected",
"metadata": {
"key1": "value1",
"key2": "value2"
}
}';
}

public static function paymentFundsReceived(): string
{
return '{
"type": "payment_funds_received",
"event_version": 1,
"event_id": "b8d4dda0-ff2c-4d77-a6da-4615e4bad941",
"payment_id": "60c0a60ed8d7-4e5b-ac79-401b1d8a8633",
"funds_received_at": "2024-10-14T14:02:26.825Z",
"metadata": {
"key1": "value1",
"key2": "value2"
}
}';
}

public static function paymentNoMetadata(): string
{
return '{
Expand Down
Loading
Loading