From 42013a49c65ee34991dd191f7b20b593ebf5dc67 Mon Sep 17 00:00:00 2001 From: Stefan Adrian Danaita Date: Tue, 6 May 2025 20:22:45 +0300 Subject: [PATCH 1/7] [ACL-69] Merchant account transactions foundation --- config/bindings.php | 7 + config/discriminations.php | 14 ++ .../MerchantAccountPayoutStatuses.php | 11 ++ .../MerchantAccountTransactionTypes.php | 13 ++ .../MerchantAccount/MerchantAccount.php | 22 ++- .../MerchantAccountExecutedPayout.php | 36 +++++ .../MerchantAccountExternalPayment.php | 37 +++++ .../Transactions/MerchantAccountPayment.php | 63 +++++++++ .../Transactions/MerchantAccountPayout.php | 76 +++++++++++ .../MerchantAccountPendingPayout.php | 12 ++ .../Transactions/MerchantAccountRefund.php | 128 ++++++++++++++++++ .../MerchantAccountTransactionRetrieved.php | 76 +++++++++++ .../MerchantAccountExternalPaymentReturn.php | 23 ++++ ...AccountExternalPaymentReturnIdentified.php | 23 ++++ ...antAccountExternalPaymentReturnUnknown.php | 12 ++ .../Api/MerchantAccountsApiInterface.php | 10 ++ .../MerchantAccountInterface.php | 6 + ...MerchantAccountExecutedPayoutInterface.php | 18 +++ ...erchantAccountExternalPaymentInterface.php | 20 +++ .../MerchantAccountPaymentInterface.php | 31 +++++ .../MerchantAccountPayoutInterface.php | 35 +++++ .../MerchantAccountPendingPayoutInterface.php | 10 ++ .../MerchantAccountRefundInterface.php | 55 ++++++++ ...ntAccountTransactionRetrievedInterface.php | 33 +++++ ...ternalPaymentReturnIdentifiedInterface.php | 13 ++ ...tAccountExternalPaymentReturnInterface.php | 13 ++ ...tExternalPaymentReturnUnknownInterface.php | 10 ++ src/Services/Api/MerchantAccountsApi.php | 25 ++++ src/Services/Client/Client.php | 1 - 29 files changed, 831 insertions(+), 2 deletions(-) create mode 100644 src/Constants/MerchantAccountPayoutStatuses.php create mode 100644 src/Constants/MerchantAccountTransactionTypes.php create mode 100644 src/Entities/MerchantAccount/Transactions/MerchantAccountExecutedPayout.php create mode 100644 src/Entities/MerchantAccount/Transactions/MerchantAccountExternalPayment.php create mode 100644 src/Entities/MerchantAccount/Transactions/MerchantAccountPayment.php create mode 100644 src/Entities/MerchantAccount/Transactions/MerchantAccountPayout.php create mode 100644 src/Entities/MerchantAccount/Transactions/MerchantAccountPendingPayout.php create mode 100644 src/Entities/MerchantAccount/Transactions/MerchantAccountRefund.php create mode 100644 src/Entities/MerchantAccount/Transactions/MerchantAccountTransactionRetrieved.php create mode 100644 src/Entities/MerchantAccount/Transactions/Returns/MerchantAccountExternalPaymentReturn.php create mode 100644 src/Entities/MerchantAccount/Transactions/Returns/MerchantAccountExternalPaymentReturnIdentified.php create mode 100644 src/Entities/MerchantAccount/Transactions/Returns/MerchantAccountExternalPaymentReturnUnknown.php create mode 100644 src/Interfaces/MerchantAccount/Transactions/MerchantAccountExecutedPayoutInterface.php create mode 100644 src/Interfaces/MerchantAccount/Transactions/MerchantAccountExternalPaymentInterface.php create mode 100644 src/Interfaces/MerchantAccount/Transactions/MerchantAccountPaymentInterface.php create mode 100644 src/Interfaces/MerchantAccount/Transactions/MerchantAccountPayoutInterface.php create mode 100644 src/Interfaces/MerchantAccount/Transactions/MerchantAccountPendingPayoutInterface.php create mode 100644 src/Interfaces/MerchantAccount/Transactions/MerchantAccountRefundInterface.php create mode 100644 src/Interfaces/MerchantAccount/Transactions/MerchantAccountTransactionRetrievedInterface.php create mode 100644 src/Interfaces/MerchantAccount/Transactions/Returns/MerchantAccountExternalPaymentReturnIdentifiedInterface.php create mode 100644 src/Interfaces/MerchantAccount/Transactions/Returns/MerchantAccountExternalPaymentReturnInterface.php create mode 100644 src/Interfaces/MerchantAccount/Transactions/Returns/MerchantAccountExternalPaymentReturnUnknownInterface.php diff --git a/config/bindings.php b/config/bindings.php index 9af12f58..34721f3a 100644 --- a/config/bindings.php +++ b/config/bindings.php @@ -92,6 +92,13 @@ Interfaces\Remitter\RemitterVerification\AutomatedRemitterVerificationInterface::class => Entities\Remitter\RemitterVerification\AutomatedRemitterVerification::class, Interfaces\MerchantAccount\MerchantAccountInterface::class => Entities\MerchantAccount\MerchantAccount::class, + Interfaces\MerchantAccount\Transactions\MerchantAccountPaymentInterface::class => Entities\MerchantAccount\Transactions\MerchantAccountPayment::class, + Interfaces\MerchantAccount\Transactions\MerchantAccountExternalPaymentInterface::class => Entities\MerchantAccount\Transactions\MerchantAccountExternalPayment::class, + Interfaces\MerchantAccount\Transactions\MerchantAccountPendingPayoutInterface::class => Entities\MerchantAccount\Transactions\MerchantAccountPendingPayout::class, + Interfaces\MerchantAccount\Transactions\MerchantAccountExecutedPayoutInterface::class => Entities\MerchantAccount\Transactions\MerchantAccountExecutedPayout::class, + Interfaces\MerchantAccount\Transactions\MerchantAccountRefundInterface::class => Entities\MerchantAccount\Transactions\MerchantAccountRefund::class, + Interfaces\MerchantAccount\Transactions\Returns\MerchantAccountExternalPaymentReturnIdentifiedInterface::class => Entities\MerchantAccount\Transactions\Returns\MerchantAccountExternalPaymentReturnIdentified::class, + Interfaces\MerchantAccount\Transactions\Returns\MerchantAccountExternalPaymentReturnUnknownInterface::class => Entities\MerchantAccount\Transactions\Returns\MerchantAccountExternalPaymentReturnUnknown::class, Interfaces\Webhook\PaymentAuthorizedEventInterface::class => Entities\Webhook\PaymentAuthorizedEvent::class, Interfaces\Webhook\PaymentExecutedEventInterface::class => Entities\Webhook\PaymentExecutedEvent::class, diff --git a/config/discriminations.php b/config/discriminations.php index f5cc23ce..14955c48 100644 --- a/config/discriminations.php +++ b/config/discriminations.php @@ -6,6 +6,8 @@ use TrueLayer\Constants\AuthorizationFlowActionTypes; use TrueLayer\Constants\AuthorizationFlowStatusTypes; use TrueLayer\Constants\BeneficiaryTypes; +use TrueLayer\Constants\MerchantAccountPayoutStatuses; +use TrueLayer\Constants\MerchantAccountTransactionTypes; use TrueLayer\Constants\PaymentMethods; use TrueLayer\Constants\PaymentStatus; use TrueLayer\Constants\PayoutStatus; @@ -119,4 +121,16 @@ 'discriminate_on' => 'type', RemitterVerificationTypes::AUTOMATED => Interfaces\Remitter\RemitterVerification\AutomatedRemitterVerificationInterface::class, ], + Interfaces\MerchantAccount\Transactions\MerchantAccountTransactionRetrievedInterface::class => [ + 'discriminate_on' => 'type', + MerchantAccountTransactionTypes::EXTERNAL_PAYMENT => Interfaces\MerchantAccount\Transactions\MerchantAccountExternalPaymentInterface::class, + MerchantAccountTransactionTypes::PAYOUT => Interfaces\MerchantAccount\Transactions\MerchantAccountPayoutInterface::class, + MerchantAccountTransactionTypes::REFUND => Interfaces\MerchantAccount\Transactions\MerchantAccountRefundInterface::class, + MerchantAccountTransactionTypes::MERCHANT_ACCOUNT_PAYMENT => Interfaces\MerchantAccount\Transactions\MerchantAccountPaymentInterface::class, + ], + Interfaces\MerchantAccount\Transactions\MerchantAccountPayoutInterface::class => [ + 'discriminate_on' => 'status', + MerchantAccountPayoutStatuses::EXECUTED_PAYOUT => Interfaces\MerchantAccount\Transactions\MerchantAccountExecutedPayoutInterface::class, + MerchantAccountPayoutStatuses::PENDING_PAYOUT => Interfaces\MerchantAccount\Transactions\MerchantAccountPendingPayoutInterface::class, + ], ]; diff --git a/src/Constants/MerchantAccountPayoutStatuses.php b/src/Constants/MerchantAccountPayoutStatuses.php new file mode 100644 index 00000000..c243f2c0 --- /dev/null +++ b/src/Constants/MerchantAccountPayoutStatuses.php @@ -0,0 +1,11 @@ +accountHolderName; } + + /** + * @param \DateTimeInterface $from + * @param \DateTimeInterface $to + * + * @return array|MerchantAccountTransactionRetrievedInterface[] + * @throws InvalidArgumentException + */ + public function getTransactions(\DateTimeInterface $from, \DateTimeInterface $to): array { + $data = $this->getApiFactory()->merchantAccountsApi() + ->listTransactions($this->id, $from, $to); + + return $this->entityFactory->makeMany(MerchantAccountTransactionRetrievedInterface::class, $data); + } } diff --git a/src/Entities/MerchantAccount/Transactions/MerchantAccountExecutedPayout.php b/src/Entities/MerchantAccount/Transactions/MerchantAccountExecutedPayout.php new file mode 100644 index 00000000..7b648df4 --- /dev/null +++ b/src/Entities/MerchantAccount/Transactions/MerchantAccountExecutedPayout.php @@ -0,0 +1,36 @@ +returnedBy ?? null; + } + + /** + * @return string|null + */ + public function getSchemeId(): ?string + { + return $this->schemeId ?? null; + } +} diff --git a/src/Entities/MerchantAccount/Transactions/MerchantAccountExternalPayment.php b/src/Entities/MerchantAccount/Transactions/MerchantAccountExternalPayment.php new file mode 100644 index 00000000..d5787cdc --- /dev/null +++ b/src/Entities/MerchantAccount/Transactions/MerchantAccountExternalPayment.php @@ -0,0 +1,37 @@ +settledAt; + } + + /** + * @return MerchantAccountExternalPaymentReturnInterface|null + */ + public function getRefundFor(): ?MerchantAccountExternalPaymentReturnInterface + { + return $this->returnedFor ?? null; + } +} diff --git a/src/Entities/MerchantAccount/Transactions/MerchantAccountPayment.php b/src/Entities/MerchantAccount/Transactions/MerchantAccountPayment.php new file mode 100644 index 00000000..c6421871 --- /dev/null +++ b/src/Entities/MerchantAccount/Transactions/MerchantAccountPayment.php @@ -0,0 +1,63 @@ + + */ + protected array $metadata; + + /** + * @return \DateTimeInterface + */ + public function getSettledAt(): \DateTimeInterface + { + return $this->settledAt; + } + + /** + * @return PaymentSourceBeneficiaryInterface + */ + public function getPaymentSource(): PaymentSourceBeneficiaryInterface + { + return $this->paymentSource; + } + + /** + * @return string + */ + public function getPaymentId(): string + { + return $this->paymentId; + } + + /** + * @return string[] + */ + public function getMetadata(): array + { + return $this->metadata ?? []; + } +} diff --git a/src/Entities/MerchantAccount/Transactions/MerchantAccountPayout.php b/src/Entities/MerchantAccount/Transactions/MerchantAccountPayout.php new file mode 100644 index 00000000..ca389fb3 --- /dev/null +++ b/src/Entities/MerchantAccount/Transactions/MerchantAccountPayout.php @@ -0,0 +1,76 @@ + + */ + protected array $metadata; + + /** + * @return \DateTimeInterface + */ + public function getCreatedAt(): \DateTimeInterface + { + return $this->createdAt; + } + + /** + * @return string + */ + public function getContextCode(): string + { + return $this->contextCode; + } + + /** + * @return string + */ + public function getPayoutId(): string + { + return $this->payoutId; + } + + /** + * @return BeneficiaryInterface + */ + public function getBeneficiary(): BeneficiaryInterface + { + return $this->beneficiary; + } + + /** + * @return array + */ + public function getMetadata(): array + { + return $this->metadata ?? []; + } +} diff --git a/src/Entities/MerchantAccount/Transactions/MerchantAccountPendingPayout.php b/src/Entities/MerchantAccount/Transactions/MerchantAccountPendingPayout.php new file mode 100644 index 00000000..3d647232 --- /dev/null +++ b/src/Entities/MerchantAccount/Transactions/MerchantAccountPendingPayout.php @@ -0,0 +1,12 @@ + + */ + protected array $metadata; + + /** + * @return \DateTimeInterface + */ + public function getCreatedAt(): \DateTimeInterface + { + return $this->createdAt; + } + + /** + * @return \DateTimeInterface|null + */ + public function getExecutedAt(): ?\DateTimeInterface + { + return $this->executedAt ?? null; + } + + /** + * @return PaymentSourceBeneficiaryInterface + */ + public function getBeneficiary(): PaymentSourceBeneficiaryInterface + { + return $this->beneficiary; + } + + /** + * @return string + */ + public function getContextCode(): string + { + return $this->contextCode; + } + + /** + * @return string + */ + public function getRefundId(): string + { + return $this->refundId; + } + + /** + * @return string + */ + public function getPaymentId(): string + { + return $this->paymentId; + } + + /** + * @return string|null + */ + public function getReturnedBy(): ?string + { + return $this->returnedBy ?? null; + } + + /** + * @return string|null + */ + public function getSchemeId(): ?string + { + return $this->schemeId ?? null; + } + + /** + * @return array + */ + public function getMetadata(): array + { + return $this->metadata ?? []; + } +} diff --git a/src/Entities/MerchantAccount/Transactions/MerchantAccountTransactionRetrieved.php b/src/Entities/MerchantAccount/Transactions/MerchantAccountTransactionRetrieved.php new file mode 100644 index 00000000..d276b52f --- /dev/null +++ b/src/Entities/MerchantAccount/Transactions/MerchantAccountTransactionRetrieved.php @@ -0,0 +1,76 @@ +type; + } + + /** + * @return string + */ + public function getId(): string + { + return $this->id; + } + + /** + * @return int + */ + public function getAmountInMinor(): int + { + return $this->amountInMinor; + } + + /** + * @return string + */ + public function getCurrency(): string + { + return $this->currency; + } + + /** + * @return string + */ + public function getStatus(): string + { + return $this->status; + } +} diff --git a/src/Entities/MerchantAccount/Transactions/Returns/MerchantAccountExternalPaymentReturn.php b/src/Entities/MerchantAccount/Transactions/Returns/MerchantAccountExternalPaymentReturn.php new file mode 100644 index 00000000..09b5365b --- /dev/null +++ b/src/Entities/MerchantAccount/Transactions/Returns/MerchantAccountExternalPaymentReturn.php @@ -0,0 +1,23 @@ +type; + } +} diff --git a/src/Entities/MerchantAccount/Transactions/Returns/MerchantAccountExternalPaymentReturnIdentified.php b/src/Entities/MerchantAccount/Transactions/Returns/MerchantAccountExternalPaymentReturnIdentified.php new file mode 100644 index 00000000..239cdaaf --- /dev/null +++ b/src/Entities/MerchantAccount/Transactions/Returns/MerchantAccountExternalPaymentReturnIdentified.php @@ -0,0 +1,23 @@ +returnId; + } +} diff --git a/src/Entities/MerchantAccount/Transactions/Returns/MerchantAccountExternalPaymentReturnUnknown.php b/src/Entities/MerchantAccount/Transactions/Returns/MerchantAccountExternalPaymentReturnUnknown.php new file mode 100644 index 00000000..7d867edd --- /dev/null +++ b/src/Entities/MerchantAccount/Transactions/Returns/MerchantAccountExternalPaymentReturnUnknown.php @@ -0,0 +1,12 @@ + + */ + public function getMetadata(): array; +} diff --git a/src/Interfaces/MerchantAccount/Transactions/MerchantAccountPayoutInterface.php b/src/Interfaces/MerchantAccount/Transactions/MerchantAccountPayoutInterface.php new file mode 100644 index 00000000..c53b4797 --- /dev/null +++ b/src/Interfaces/MerchantAccount/Transactions/MerchantAccountPayoutInterface.php @@ -0,0 +1,35 @@ + + */ + public function getMetadata(): array; +} diff --git a/src/Interfaces/MerchantAccount/Transactions/MerchantAccountPendingPayoutInterface.php b/src/Interfaces/MerchantAccount/Transactions/MerchantAccountPendingPayoutInterface.php new file mode 100644 index 00000000..76e47924 --- /dev/null +++ b/src/Interfaces/MerchantAccount/Transactions/MerchantAccountPendingPayoutInterface.php @@ -0,0 +1,10 @@ + + */ + public function getMetadata(): array; +} diff --git a/src/Interfaces/MerchantAccount/Transactions/MerchantAccountTransactionRetrievedInterface.php b/src/Interfaces/MerchantAccount/Transactions/MerchantAccountTransactionRetrievedInterface.php new file mode 100644 index 00000000..30177d93 --- /dev/null +++ b/src/Interfaces/MerchantAccount/Transactions/MerchantAccountTransactionRetrievedInterface.php @@ -0,0 +1,33 @@ +uri(Endpoints::MERCHANT_ACCOUNTS . '/' . $id) ->get(); } + + /** + * @param string $merchantAccountId + * @param \DateTimeInterface $from + * @param \DateTimeInterface $to + * + * @return MerchantAccountTransactionRetrievedInterface[] + * + * @throws ApiResponseUnsuccessfulException + * @throws SignerException + * @throws ApiRequestJsonSerializationException + */ + public function listTransactions(string $merchantAccountId, \DateTimeInterface $from, \DateTimeInterface $to): array + { + $uri = "{$merchantAccountId}/transactions?from={$from->format(\DateTimeInterface::ATOM)}&to={$to->format(\DateTimeInterface::ATOM)}"; + + $response = (array) $this->request() + ->uri(Endpoints::MERCHANT_ACCOUNTS . '/' . $uri) + ->get(); + + return isset($response['items']) && \is_array($response['items']) + ? $response['items'] + : []; + } } diff --git a/src/Services/Client/Client.php b/src/Services/Client/Client.php index 5d80644e..f451cb00 100644 --- a/src/Services/Client/Client.php +++ b/src/Services/Client/Client.php @@ -373,7 +373,6 @@ public function getPayout(string $id): PayoutRetrievedInterface } /** - * @throws InvalidArgumentException * @throws InvalidArgumentException * @throws SignerException * @throws ApiRequestJsonSerializationException From 5acbe57918c5266ae64e2c2d8b138f3b00aabf03 Mon Sep 17 00:00:00 2001 From: Stefan Adrian Danaita Date: Tue, 6 May 2025 20:57:16 +0300 Subject: [PATCH 2/7] [ACL-69] Add arrayFields and casts to the entities --- .../MerchantAccountExecutedPayout.php | 11 ++ .../MerchantAccountExternalPayment.php | 18 ++ .../Transactions/MerchantAccountPayment.php | 21 ++ .../Transactions/MerchantAccountPayout.php | 22 +++ .../Transactions/MerchantAccountRefund.php | 27 +++ .../MerchantAccountTransactionRetrieved.php | 11 ++ .../MerchantAccountExternalPaymentReturn.php | 7 + ...AccountExternalPaymentReturnIdentified.php | 10 + tests/integration/MerchantAccountTest.php | 13 ++ .../Mocks/MerchantAccountResponse.php | 186 ++++++++++++++++++ 10 files changed, 326 insertions(+) diff --git a/src/Entities/MerchantAccount/Transactions/MerchantAccountExecutedPayout.php b/src/Entities/MerchantAccount/Transactions/MerchantAccountExecutedPayout.php index 7b648df4..aa1aa27f 100644 --- a/src/Entities/MerchantAccount/Transactions/MerchantAccountExecutedPayout.php +++ b/src/Entities/MerchantAccount/Transactions/MerchantAccountExecutedPayout.php @@ -18,6 +18,17 @@ class MerchantAccountExecutedPayout extends MerchantAccountPayout implements Mer */ protected string $schemeId; + /** + * @return string[] + */ + protected function arrayFields(): array + { + return \array_merge(parent::arrayFields(), [ + 'returned_by', + 'scheme_id', + ]); + } + /** * @return string|null */ diff --git a/src/Entities/MerchantAccount/Transactions/MerchantAccountExternalPayment.php b/src/Entities/MerchantAccount/Transactions/MerchantAccountExternalPayment.php index d5787cdc..778be2ad 100644 --- a/src/Entities/MerchantAccount/Transactions/MerchantAccountExternalPayment.php +++ b/src/Entities/MerchantAccount/Transactions/MerchantAccountExternalPayment.php @@ -19,6 +19,24 @@ class MerchantAccountExternalPayment extends MerchantAccountTransactionRetrieved */ protected MerchantAccountExternalPaymentReturnInterface $returnedFor; + /** + * @var array|string[] + */ + protected array $casts = [ + 'settled_at' => \DateTimeInterface::class, + ]; + + /** + * @return string[] + */ + protected function arrayFields(): array + { + return \array_merge(parent::arrayFields(), [ + 'settled_at', + 'returned_for', + ]); + } + /** * @return \DateTimeInterface */ diff --git a/src/Entities/MerchantAccount/Transactions/MerchantAccountPayment.php b/src/Entities/MerchantAccount/Transactions/MerchantAccountPayment.php index c6421871..d0dca903 100644 --- a/src/Entities/MerchantAccount/Transactions/MerchantAccountPayment.php +++ b/src/Entities/MerchantAccount/Transactions/MerchantAccountPayment.php @@ -29,6 +29,27 @@ class MerchantAccountPayment extends MerchantAccountTransactionRetrieved impleme */ protected array $metadata; + /** + * @var array|string[] + */ + protected array $casts = [ + 'settled_at' => \DateTimeInterface::class, + 'payment_source' => PaymentSourceBeneficiaryInterface::class, + ]; + + /** + * @return string[] + */ + protected function arrayFields(): array + { + return \array_merge(parent::arrayFields(), [ + 'settled_at', + 'payment_id', + 'payment_source', + 'metadata', + ]); + } + /** * @return \DateTimeInterface */ diff --git a/src/Entities/MerchantAccount/Transactions/MerchantAccountPayout.php b/src/Entities/MerchantAccount/Transactions/MerchantAccountPayout.php index ca389fb3..48dd8b17 100644 --- a/src/Entities/MerchantAccount/Transactions/MerchantAccountPayout.php +++ b/src/Entities/MerchantAccount/Transactions/MerchantAccountPayout.php @@ -34,6 +34,28 @@ abstract class MerchantAccountPayout extends MerchantAccountTransactionRetrieved */ protected array $metadata; + /** + * @var array|string[] + */ + protected array $casts = [ + 'created_at' => \DateTimeInterface::class, + 'beneficiary' => BeneficiaryInterface::class, + ]; + + /** + * @return string[] + */ + protected function arrayFields(): array + { + return \array_merge(parent::arrayFields(), [ + 'created_at', + 'context_code', + 'payout_id', + 'beneficiary', + 'metadata', + ]); + } + /** * @return \DateTimeInterface */ diff --git a/src/Entities/MerchantAccount/Transactions/MerchantAccountRefund.php b/src/Entities/MerchantAccount/Transactions/MerchantAccountRefund.php index 2774512c..2a95d39f 100644 --- a/src/Entities/MerchantAccount/Transactions/MerchantAccountRefund.php +++ b/src/Entities/MerchantAccount/Transactions/MerchantAccountRefund.php @@ -54,6 +54,33 @@ class MerchantAccountRefund extends MerchantAccountTransactionRetrieved implemen */ protected array $metadata; + /** + * @var array|string[] + */ + protected array $casts = [ + 'created_at' => \DateTimeInterface::class, + 'executed_at' => \DateTimeInterface::class, + 'beneficiary' => PaymentSourceBeneficiaryInterface::class, + ]; + + /** + * @return string[] + */ + protected function arrayFields(): array + { + return \array_merge(parent::arrayFields(), [ + 'created_at', + 'executed_at', + 'beneficiary', + 'context_code', + 'refund_id', + 'payment_id', + 'returned_by', + 'scheme_id', + 'metadata', + ]); + } + /** * @return \DateTimeInterface */ diff --git a/src/Entities/MerchantAccount/Transactions/MerchantAccountTransactionRetrieved.php b/src/Entities/MerchantAccount/Transactions/MerchantAccountTransactionRetrieved.php index d276b52f..d7b1ea1f 100644 --- a/src/Entities/MerchantAccount/Transactions/MerchantAccountTransactionRetrieved.php +++ b/src/Entities/MerchantAccount/Transactions/MerchantAccountTransactionRetrieved.php @@ -34,6 +34,17 @@ abstract class MerchantAccountTransactionRetrieved extends Entity implements Mer */ protected string $status; + /** + * @var array + */ + protected array $arrayFields = [ + 'type', + 'id', + 'amount_in_minor', + 'currency', + 'status', + ]; + /** * @return string */ diff --git a/src/Entities/MerchantAccount/Transactions/Returns/MerchantAccountExternalPaymentReturn.php b/src/Entities/MerchantAccount/Transactions/Returns/MerchantAccountExternalPaymentReturn.php index 09b5365b..03346351 100644 --- a/src/Entities/MerchantAccount/Transactions/Returns/MerchantAccountExternalPaymentReturn.php +++ b/src/Entities/MerchantAccount/Transactions/Returns/MerchantAccountExternalPaymentReturn.php @@ -14,6 +14,13 @@ abstract class MerchantAccountExternalPaymentReturn extends Entity implements Me */ protected string $type; + /** + * @var array|string[] + */ + protected array $arrayFields = [ + 'type', + ]; + /** * @return string */ diff --git a/src/Entities/MerchantAccount/Transactions/Returns/MerchantAccountExternalPaymentReturnIdentified.php b/src/Entities/MerchantAccount/Transactions/Returns/MerchantAccountExternalPaymentReturnIdentified.php index 239cdaaf..db099a60 100644 --- a/src/Entities/MerchantAccount/Transactions/Returns/MerchantAccountExternalPaymentReturnIdentified.php +++ b/src/Entities/MerchantAccount/Transactions/Returns/MerchantAccountExternalPaymentReturnIdentified.php @@ -13,6 +13,16 @@ class MerchantAccountExternalPaymentReturnIdentified extends MerchantAccountExte */ protected string $returnId; + /** + * @return string[] + */ + protected function arrayFields(): array + { + return \array_merge(parent::arrayFields(), [ + 'return_id', + ]); + } + /** * @return string */ diff --git a/tests/integration/MerchantAccountTest.php b/tests/integration/MerchantAccountTest.php index 77b52e74..3d8c5f2c 100644 --- a/tests/integration/MerchantAccountTest.php +++ b/tests/integration/MerchantAccountTest.php @@ -46,3 +46,16 @@ \expect($account->getAccountHolderName())->toBe('John Doe'); \expect($account->getAccountIdentifiers()[0])->toBeInstanceOf(IbanDetailsInterface::class); }); + +\it('retrieves a merchant account\'s transactions', function () { + $client = \client([MerchantAccountResponse::account(), MerchantAccountResponse::transactions()]); + + $account = $client->getMerchantAccount('1'); + + $from = DateTime::createFromFormat(DateTime::ATOM, '2025-05-01T00:00:00Z'); + $to = DateTime::createFromFormat(DateTime::ATOM, '2025-05-30T00:00:00Z'); + + $transactions = $account->getTransactions($from, $to); + + \expect(count($transactions))->toBe(5); +}); diff --git a/tests/integration/Mocks/MerchantAccountResponse.php b/tests/integration/Mocks/MerchantAccountResponse.php index d097b6f8..8a5be3ad 100644 --- a/tests/integration/Mocks/MerchantAccountResponse.php +++ b/tests/integration/Mocks/MerchantAccountResponse.php @@ -17,4 +17,190 @@ public static function account(): Response { return new Response(200, [], '{"id":"a2dcee6d-7a00-414d-a1e6-8a2b23169e00","currency":"EUR","account_identifiers":[{"type":"iban","iban":"GB38CLRB04066200003769"}],"available_balance_in_minor":100000,"current_balance_in_minor":100000,"account_holder_name":"John Doe"}'); } + + public static function transactions(): Response + { + return new Response(200, [], '{ + "items": [ + { + "type": "merchant_account_payment", + "id": "string", + "currency": "GBP", + "amount_in_minor": 0, + "status": "settled", + "settled_at": "2025-05-03T10:23:00Z", + "payment_source": { + "id": "e2b41c9d-176k-67aa-b2da-fe1a2b97253c", + "account_holder_name": "string", + "account_identifiers": [ + { + "type": "sort_code_account_number", + "sort_code": 560029, + "account_number": 26207729 + }, + { + "type": "iban", + "iban": "GB32CLRB04066800012315" + }, + { + "type": "nrb", + "nrb": "string" + } + ] + }, + "payment_id": "0afd1f6a-f611-48ce-9488-321129bb3a70", + "metadata": { + "prop1": "value1", + "prop2": "value2" + } + }, + { + "type": "external_payment", + "id": "string", + "currency": "GBP", + "amount_in_minor": 0, + "status": "settled", + "settled_at": "2025-05-03T14:37:00Z", + "remitter": { + "account_holder_name": "string", + "reference": "string", + "account_identifiers": [ + { + "type": "sort_code_account_number", + "sort_code": 560029, + "account_number": 26207729 + }, + { + "type": "iban", + "iban": "GB32CLRB04066800012315" + }, + { + "type": "nrb", + "nrb": "string" + } + ] + }, + "return_for": { + "type": "identified", + "returned_id": "0cd1b0f7-71bc-4d24-b209-95259dadcc20" + } + }, + { + "type": "payout", + "id": "string", + "currency": "GBP", + "amount_in_minor": 0, + "status": "pending", + "created_at": "2025-05-02T11:11:11Z", + "beneficiary": { + "type": "external_account", + "reference": "string", + "account_holder_name": "string", + "account_identifiers": [ + { + "type": "sort_code_account_number", + "sort_code": 560029, + "account_number": 26207729 + }, + { + "type": "iban", + "iban": "GB32CLRB04066800012315" + }, + { + "type": "nrb", + "nrb": "string" + } + ] + }, + "context_code": "withdrawal", + "payout_id": "0cd1b0f7-71bc-4d24-b209-95259dadcc20", + "metadata": { + "prop1": "value1", + "prop2": "value2" + } + }, + { + "type": "payout", + "id": "string", + "currency": "GBP", + "amount_in_minor": 0, + "status": "executed", + "created_at": "2025-05-10T12:32:12Z", + "executed_at": "2025-05-10T13:32:12Z", + "beneficiary": { + "type": "external_account", + "reference": "string", + "account_holder_name": "string", + "account_identifiers": [ + { + "type": "sort_code_account_number", + "sort_code": 560029, + "account_number": 26207729 + }, + { + "type": "iban", + "iban": "GB32CLRB04066800012315" + }, + { + "type": "nrb", + "nrb": "string" + } + ] + }, + "context_code": "withdrawal", + "payout_id": "0cd1b0f7-71bc-4d24-b209-95259dadcc20", + "returned_by": "0cd1b0f7-71bc-4d24-b209-95259dadcc20", + "scheme_id": "faster_payments_service", + "metadata": { + "prop1": "value1", + "prop2": "value2" + } + }, + { + "type": "refund", + "id": "string", + "currency": "GBP", + "amount_in_minor": 0, + "status": "pending", + "created_at": "2025-05-14T12:32:12Z", + "executed_at": "2025-05-15T10:32:12Z", + "beneficiary": { + "type": "payment_source", + "payment_source_id": "string", + "user_id": "string", + "reference": "string", + "account_holder_name": "string", + "account_identifiers": [ + { + "type": "sort_code_account_number", + "sort_code": 560029, + "account_number": 26207729 + }, + { + "type": "iban", + "iban": "GB32CLRB04066800012315" + }, + { + "type": "nrb", + "nrb": "string" + } + ] + }, + "context_code": "withdrawal", + "refund_id": "43d12d0f-d775-410f-aaff-482200c17017", + "payment_id": "0afd1f6a-f611-48ce-9488-321129bb3a70", + "returned_by": "0cd1b0f7-71bc-4d24-b209-95259dadcc20", + "scheme_id": "faster_payments_service", + "metadata": { + "prop1": "value1", + "prop2": "value2" + } + } + ], + "pagination": { + "next_cursor": "bWFuZGF0ZXM6MmUwNDk0MTMK" + } + }' + ); + } } From c891eacede1ae944ffef37f5d60bb370ed21d7d3 Mon Sep 17 00:00:00 2001 From: Stefan Adrian Danaita Date: Tue, 6 May 2025 21:01:11 +0300 Subject: [PATCH 3/7] [ACL-69] Make integration test more specific --- tests/integration/MerchantAccountTest.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/integration/MerchantAccountTest.php b/tests/integration/MerchantAccountTest.php index 3d8c5f2c..3e618d8c 100644 --- a/tests/integration/MerchantAccountTest.php +++ b/tests/integration/MerchantAccountTest.php @@ -58,4 +58,9 @@ $transactions = $account->getTransactions($from, $to); \expect(count($transactions))->toBe(5); + \expect($transactions[0])->toBeInstanceOf(\TrueLayer\Interfaces\MerchantAccount\Transactions\MerchantAccountPaymentInterface::class); + \expect($transactions[1])->toBeInstanceOf(\TrueLayer\Interfaces\MerchantAccount\Transactions\MerchantAccountExternalPaymentInterface::class); + \expect($transactions[2])->toBeInstanceOf(\TrueLayer\Interfaces\MerchantAccount\Transactions\MerchantAccountPendingPayoutInterface::class); + \expect($transactions[3])->toBeInstanceOf(\TrueLayer\Interfaces\MerchantAccount\Transactions\MerchantAccountExecutedPayoutInterface::class); + \expect($transactions[4])->toBeInstanceOf(\TrueLayer\Interfaces\MerchantAccount\Transactions\MerchantAccountRefundInterface::class); }); From 46473ba37c87ded716f9c8079df3235e5227cfc8 Mon Sep 17 00:00:00 2001 From: Stefan Adrian Danaita Date: Tue, 6 May 2025 21:02:37 +0300 Subject: [PATCH 4/7] [ACL-69] PHPStan fix --- .../Transactions/MerchantAccountTransactionRetrieved.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Entities/MerchantAccount/Transactions/MerchantAccountTransactionRetrieved.php b/src/Entities/MerchantAccount/Transactions/MerchantAccountTransactionRetrieved.php index d7b1ea1f..140d7864 100644 --- a/src/Entities/MerchantAccount/Transactions/MerchantAccountTransactionRetrieved.php +++ b/src/Entities/MerchantAccount/Transactions/MerchantAccountTransactionRetrieved.php @@ -35,7 +35,7 @@ abstract class MerchantAccountTransactionRetrieved extends Entity implements Mer protected string $status; /** - * @var array + * @var array */ protected array $arrayFields = [ 'type', From a127916dfbc45eee997cb3ddaa20db84a81b535a Mon Sep 17 00:00:00 2001 From: Stefan Adrian Danaita Date: Tue, 6 May 2025 21:37:38 +0300 Subject: [PATCH 5/7] [ACL-69] Acceptance test --- tests/acceptance/MerchantAccountsTest.php | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tests/acceptance/MerchantAccountsTest.php b/tests/acceptance/MerchantAccountsTest.php index 824d0b1d..1aeadae3 100644 --- a/tests/acceptance/MerchantAccountsTest.php +++ b/tests/acceptance/MerchantAccountsTest.php @@ -35,3 +35,16 @@ \expect(\count($account->getAccountIdentifiers()))->toBeGreaterThan(0); \expect($account->getAccountIdentifiers()[0])->toBeInstanceOf(AccountIdentifierInterface::class); }); + +\it('retrieves merchant account transactions', function () { + $accounts = \client()->getMerchantAccounts(); + $account = \client()->getMerchantAccount($accounts[0]->getId()); + + $from = (new DateTime())->sub(new DateInterval("PT5H")); + $to = new DateTime(); + + $transactions = $account->getTransactions($from, $to); + \expect($transactions)->toBeArray(); + \expect(count($transactions))->toBeGreaterThan(0); + \expect($transactions[0])->toBeInstanceOf(\TrueLayer\Interfaces\MerchantAccount\Transactions\MerchantAccountTransactionRetrievedInterface::class); +}); From 3ab7cc7a86c5f16746c105f74d71768bad507ee5 Mon Sep 17 00:00:00 2001 From: Stefan Adrian Danaita Date: Tue, 6 May 2025 21:55:15 +0300 Subject: [PATCH 6/7] [ACL-69] Readme and Changelog --- CHANGELOG.md | 1 + README.md | 16 ++++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f732f47b..6f6556b1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ### Added +- Support for retrieving merchant account transactions ([#81](https://github.com/TrueLayer/truelayer-php/pull/81)) - Support for `creditable_at` in Authorised, Executed, Settled, Failed payments ([#82](https://github.com/TrueLayer/truelayer-php/pull/82)) ## [3.1.0] - 2025-03-07 diff --git a/README.md b/README.md index e18a2586..f818d158 100644 --- a/README.md +++ b/README.md @@ -1020,6 +1020,22 @@ foreach ($merchantAccount->getAccountIdentifiers() as $accountIdentifier) { } ``` +Retrieving a merchant account's list of transactions: + +```php +$account = $client->getMerchantAccount('a2dcee6d-7a00-414d-a1e6-8a2b23169e00'); + +$from = (new DateTime())->sub(new DateInterval("PT6H")); +$to = new DateTime(); + +$transactions = $account->getTransactions($from, $to); + +foreach ($transactions as $transaction) { + // See MerchantAccountRetrievedTransactionInterface and + // the interfaces that extend it for available methods. +} +``` + # SignUp Plus From 0d8ebd7984cf52720d8406de12d4fa45e336a1a6 Mon Sep 17 00:00:00 2001 From: Stefan Adrian Danaita Date: Fri, 9 May 2025 13:33:21 +0300 Subject: [PATCH 7/7] [ACL-69] Bind MerchantAccountTransactionRetrieved base class --- config/bindings.php | 1 + 1 file changed, 1 insertion(+) diff --git a/config/bindings.php b/config/bindings.php index 34721f3a..6e8c9146 100644 --- a/config/bindings.php +++ b/config/bindings.php @@ -92,6 +92,7 @@ Interfaces\Remitter\RemitterVerification\AutomatedRemitterVerificationInterface::class => Entities\Remitter\RemitterVerification\AutomatedRemitterVerification::class, Interfaces\MerchantAccount\MerchantAccountInterface::class => Entities\MerchantAccount\MerchantAccount::class, + Interfaces\MerchantAccount\Transactions\MerchantAccountTransactionRetrievedInterface::class => Entities\MerchantAccount\Transactions\MerchantAccountTransactionRetrieved::class, Interfaces\MerchantAccount\Transactions\MerchantAccountPaymentInterface::class => Entities\MerchantAccount\Transactions\MerchantAccountPayment::class, Interfaces\MerchantAccount\Transactions\MerchantAccountExternalPaymentInterface::class => Entities\MerchantAccount\Transactions\MerchantAccountExternalPayment::class, Interfaces\MerchantAccount\Transactions\MerchantAccountPendingPayoutInterface::class => Entities\MerchantAccount\Transactions\MerchantAccountPendingPayout::class,