From c8300c71a92a7f965af7e5c78fdfa173bfd94e92 Mon Sep 17 00:00:00 2001 From: Krishan Koenig Date: Thu, 18 Jul 2024 17:10:25 +0200 Subject: [PATCH 1/2] wip --- src/Contracts/HasBody.php | 13 ++ src/Contracts/IsIteratable.php | 10 ++ src/Http/BaseEndpointCollection.php | 119 ++++++++++++++++++ .../BalanceEndpointCollection.php | 20 +++ .../PaymentEndpointCollection.php | 77 ++++++++++++ .../PaymentRefundEndpointCollection.php | 25 ++++ src/Http/Request.php | 53 ++++++++ .../Requests/GetPaginatedBalancesRequest.php | 34 +++++ .../GetPaginatedPaymentRefundsRequest.php | 40 ++++++ .../Requests/GetPaginatedPaymentsRequest.php | 36 ++++++ src/Http/Requests/GetPaymentRequest.php | 52 ++++++++ src/Http/Requests/HasJsonBody.php | 13 ++ src/Http/Requests/IsIteratableRequest.php | 34 +++++ src/Http/Requests/IsPaginatedRequest.php | 30 +++++ src/Http/Requests/RefundPaymentRequest.php | 38 ++++++ src/MollieApiClient.php | 15 ++- 16 files changed, 606 insertions(+), 3 deletions(-) create mode 100644 src/Contracts/HasBody.php create mode 100644 src/Contracts/IsIteratable.php create mode 100644 src/Http/BaseEndpointCollection.php create mode 100644 src/Http/EndpointCollection/BalanceEndpointCollection.php create mode 100644 src/Http/EndpointCollection/PaymentEndpointCollection.php create mode 100644 src/Http/EndpointCollection/PaymentRefundEndpointCollection.php create mode 100644 src/Http/Request.php create mode 100644 src/Http/Requests/GetPaginatedBalancesRequest.php create mode 100644 src/Http/Requests/GetPaginatedPaymentRefundsRequest.php create mode 100644 src/Http/Requests/GetPaginatedPaymentsRequest.php create mode 100644 src/Http/Requests/GetPaymentRequest.php create mode 100644 src/Http/Requests/HasJsonBody.php create mode 100644 src/Http/Requests/IsIteratableRequest.php create mode 100644 src/Http/Requests/IsPaginatedRequest.php create mode 100644 src/Http/Requests/RefundPaymentRequest.php diff --git a/src/Contracts/HasBody.php b/src/Contracts/HasBody.php new file mode 100644 index 000000000..41def4b49 --- /dev/null +++ b/src/Contracts/HasBody.php @@ -0,0 +1,13 @@ +client = $client; + } + + public function send(Request $request): mixed + { + $path = $request->resolveResourcePath() + . $this->buildQueryString($request->getQuery()); + + $body = $request instanceof HasBody + ? $request->getBody() + : null; + + $result = $this->client->performHttpCall( + $request->getMethod(), + $path, + $body + ); + + if ($result->isEmpty()) { + return null; + } + + $targetResourceClass = $request->getTargetResourceClass(); + + if (is_subclass_of($targetResourceClass, BaseCollection::class)) { + $collection = $this->buildResultCollection($result->decode(), $targetResourceClass); + + if ($request instanceof IsIteratable && $request->iteratorEnabled()) { + /** @var CursorCollection $collection */ + return $collection->getAutoIterator($request->iteratesBackwards()); + } + + return $collection; + } + + if (is_subclass_of($targetResourceClass, BaseResource::class)) { + return ResourceFactory::createFromApiResult($this->client, $result->decode(), $targetResourceClass); + } + + return null; + } + + protected function buildResultCollection(object $result, string $targetCollectionClass): BaseCollection + { + return ResourceFactory::createBaseResourceCollection( + $this->client, + ($targetCollectionClass)::getResourceClass(), + $result->_embedded->{$targetCollectionClass::getCollectionResourceName()}, + $result->_links, + $targetCollectionClass + ); + } + + /** + * Create a generator for iterating over a resource's collection using REST API calls. + * + * This function fetches paginated data from a RESTful resource endpoint and returns a generator + * that allows you to iterate through the items in the collection one by one. It supports forward + * and backward iteration, pagination, and filtering. + * + * @param string $from The first resource ID you want to include in your list. + * @param int $limit + * @param array $filters + * @param bool $iterateBackwards Set to true for reverse order iteration (default is false). + * @return LazyCollection + */ + protected function createIterator(?string $from = null, ?int $limit = null, array $filters = [], bool $iterateBackwards = false): LazyCollection + { + /** @var CursorCollection $page */ + $page = $this->fetchCollection($from, $limit, $filters); + + return $page->getAutoIterator($iterateBackwards); + } + + /** + * @param array $filters + * @return string + */ + protected function buildQueryString(array $filters): string + { + if (empty($filters)) { + return ""; + } + + foreach ($filters as $key => $value) { + if ($value === true) { + $filters[$key] = "true"; + } + + if ($value === false) { + $filters[$key] = "false"; + } + } + + return "?" . http_build_query($filters, "", "&"); + } +} diff --git a/src/Http/EndpointCollection/BalanceEndpointCollection.php b/src/Http/EndpointCollection/BalanceEndpointCollection.php new file mode 100644 index 000000000..288f17bad --- /dev/null +++ b/src/Http/EndpointCollection/BalanceEndpointCollection.php @@ -0,0 +1,20 @@ +send(new GetPaginatedBalancesRequest($from, $limit, $parameters)); + } +} diff --git a/src/Http/EndpointCollection/PaymentEndpointCollection.php b/src/Http/EndpointCollection/PaymentEndpointCollection.php new file mode 100644 index 000000000..fec8d3102 --- /dev/null +++ b/src/Http/EndpointCollection/PaymentEndpointCollection.php @@ -0,0 +1,77 @@ +send(new GetPaginatedPaymentsRequest($from, $limit, $filters)); + } + + /** + * Retrieve a single payment from Mollie. + * + * Will throw a ApiException if the payment id is invalid or the resource cannot be found. + * + * @param string $paymentId + * @param array $filters + * + * @return Payment + * @throws ApiException + */ + public function get(string $paymentId, array $filters = []): Payment + { + return $this->send(new GetPaymentRequest($paymentId, $filters)); + } + + /** + * Issue a refund for the given payment. + * + * The $data parameter may either be an array of endpoint parameters, a float value to + * initiate a partial refund, or empty to do a full refund. + * + * @param Payment $payment + * @param array|float|null $data + * + * @return Refund + * @throws ApiException + */ + public function refund(Payment $payment, $data = []): Refund + { + return $this->send(new RefundPaymentRequest($payment->id, $data)); + } + + /** + * Create an iterator for iterating over payments retrieved from Mollie. + * + * @param string $from The first resource ID you want to include in your list. + * @param int $limit + * @param array $filters + * @param bool $iterateBackwards Set to true for reverse order iteration (default is false). + * + * @return LazyCollection + */ + public function iterator(?string $from = null, ?int $limit = null, array $filters = [], bool $iterateBackwards = false): LazyCollection + { + return $this->send( + (new GetPaginatedPaymentsRequest($from, $limit, $filters)) + ->useIterator() + ->setIterationDirection($iterateBackwards) + ); + } +} diff --git a/src/Http/EndpointCollection/PaymentRefundEndpointCollection.php b/src/Http/EndpointCollection/PaymentRefundEndpointCollection.php new file mode 100644 index 000000000..b74aa3750 --- /dev/null +++ b/src/Http/EndpointCollection/PaymentRefundEndpointCollection.php @@ -0,0 +1,25 @@ +send(new GetPaginatedPaymentRefundsRequest( + $paymentId, + $filters + )); + } +} diff --git a/src/Http/Request.php b/src/Http/Request.php new file mode 100644 index 000000000..b0dfe4a90 --- /dev/null +++ b/src/Http/Request.php @@ -0,0 +1,53 @@ +method)) { + throw new LogicException('Your request is missing a HTTP method. You must add a method property like [protected Method $method = Method::GET]'); + } + + return $this->method; + } + + public function getQuery(): array + { + return []; + } + + public static function getTargetResourceClass(): string + { + if (empty(static::$targetResourceClass)) { + throw new \RuntimeException('Resource class is not set.'); + } + + return static::$targetResourceClass; + } + + /** + * Resolve the resource path. + * + * @return string + */ + abstract public function resolveResourcePath(): string; +} diff --git a/src/Http/Requests/GetPaginatedBalancesRequest.php b/src/Http/Requests/GetPaginatedBalancesRequest.php new file mode 100644 index 000000000..9339e2291 --- /dev/null +++ b/src/Http/Requests/GetPaginatedBalancesRequest.php @@ -0,0 +1,34 @@ +paymentId = $paymentId; + $this->filters = $filters; + } + + public function resolveResourcePath(): string + { + $id = urlencode($this->paymentId); + + return "payments/{$id}/refunds"; + } +} diff --git a/src/Http/Requests/GetPaginatedPaymentsRequest.php b/src/Http/Requests/GetPaginatedPaymentsRequest.php new file mode 100644 index 000000000..03e2dc075 --- /dev/null +++ b/src/Http/Requests/GetPaginatedPaymentsRequest.php @@ -0,0 +1,36 @@ +paymentId = $paymentId; + $this->filters = $filters; + } + + /** + * Resolve the resource path. + * + * @return string + */ + public function resolveResourcePath(): string + { + $id = urlencode($this->paymentId); + + return "payments/{$id}"; + } + + public function getQuery(): array + { + return $this->filters; + } +} diff --git a/src/Http/Requests/HasJsonBody.php b/src/Http/Requests/HasJsonBody.php new file mode 100644 index 000000000..684abe73e --- /dev/null +++ b/src/Http/Requests/HasJsonBody.php @@ -0,0 +1,13 @@ +body); + } +} diff --git a/src/Http/Requests/IsIteratableRequest.php b/src/Http/Requests/IsIteratableRequest.php new file mode 100644 index 000000000..e428021ba --- /dev/null +++ b/src/Http/Requests/IsIteratableRequest.php @@ -0,0 +1,34 @@ +iteratorEnabled; + } + + public function iteratesBackwards(): bool + { + return $this->iterateBackwards; + } + + public function useIterator(): self + { + $this->iteratorEnabled = true; + + return $this; + } + + public function setIterationDirection(bool $iterateBackwards = false): self + { + $this->iterateBackwards = $iterateBackwards; + + return $this; + } +} diff --git a/src/Http/Requests/IsPaginatedRequest.php b/src/Http/Requests/IsPaginatedRequest.php new file mode 100644 index 000000000..b3d5c02ad --- /dev/null +++ b/src/Http/Requests/IsPaginatedRequest.php @@ -0,0 +1,30 @@ +from = $from; + $this->limit = $limit; + $this->filters = $filters; + } + + public function getQuery(): array + { + return array_merge([ + 'from' => $this->from, + 'limit' => $this->limit, + ], $this->filters); + } +} diff --git a/src/Http/Requests/RefundPaymentRequest.php b/src/Http/Requests/RefundPaymentRequest.php new file mode 100644 index 000000000..d45e19c31 --- /dev/null +++ b/src/Http/Requests/RefundPaymentRequest.php @@ -0,0 +1,38 @@ +paymentId = $paymentId; + $this->body = $data; + } + + public function resolveResourcePath(): string + { + $id = urlencode($this->paymentId); + + return "payments/{$id}/refunds"; + } +} diff --git a/src/MollieApiClient.php b/src/MollieApiClient.php index f5dd3db07..cf9ce6bd3 100644 --- a/src/MollieApiClient.php +++ b/src/MollieApiClient.php @@ -48,7 +48,12 @@ use Mollie\Api\Exceptions\ApiException; use Mollie\Api\Exceptions\IncompatiblePlatform; use Mollie\Api\Http\Adapter\MollieHttpAdapterPicker; +use Mollie\Api\Http\EndpointCollection\BalanceEndpointCollection; +use Mollie\Api\Http\EndpointCollection\PaymentEndpointCollection; +use Mollie\Api\Contracts\HasBody; +use Mollie\Api\Http\EndpointCollection\PaymentRefundEndpointCollection; use Mollie\Api\Idempotency\IdempotencyKeyGeneratorContract; +use Mollie\Api\Http\Request; /** * @property BalanceEndpoint $balances @@ -177,7 +182,11 @@ public function __construct( private function initializeEndpoints(): void { $endpointClasses = [ - 'balances' => BalanceEndpoint::class, + 'balances' => BalanceEndpointCollection::class, + 'payments' => PaymentEndpointCollection::class, + 'paymentRefunds' => PaymentRefundEndpointCollection::class, + + // 'balances' => BalanceEndpoint::class, 'balanceReports' => BalanceReportEndpoint::class, 'balanceTransactions' => BalanceTransactionEndpoint::class, 'chargebacks' => ChargebackEndpoint::class, @@ -200,9 +209,9 @@ private function initializeEndpoints(): void 'paymentChargebacks' => PaymentChargebackEndpoint::class, 'paymentLinks' => PaymentLinkEndpoint::class, 'paymentLinkPayments' => PaymentLinkPaymentEndpoint::class, - 'paymentRefunds' => PaymentRefundEndpoint::class, + // 'paymentRefunds' => PaymentRefundEndpoint::class, 'paymentRoutes' => PaymentRouteEndpoint::class, - 'payments' => PaymentEndpoint::class, + // 'payments' => PaymentEndpoint::class, 'permissions' => PermissionEndpoint::class, 'profiles' => ProfileEndpoint::class, 'profileMethods' => ProfileMethodEndpoint::class, From 6f004b8c062138f27319f9453b40dc2c17f3e983 Mon Sep 17 00:00:00 2001 From: Krishan Koenig Date: Thu, 18 Jul 2024 17:14:22 +0200 Subject: [PATCH 2/2] wip --- src/Http/BaseEndpointCollection.php | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/src/Http/BaseEndpointCollection.php b/src/Http/BaseEndpointCollection.php index 232a7fd9a..24004ca14 100644 --- a/src/Http/BaseEndpointCollection.php +++ b/src/Http/BaseEndpointCollection.php @@ -73,27 +73,6 @@ protected function buildResultCollection(object $result, string $targetCollectio ); } - /** - * Create a generator for iterating over a resource's collection using REST API calls. - * - * This function fetches paginated data from a RESTful resource endpoint and returns a generator - * that allows you to iterate through the items in the collection one by one. It supports forward - * and backward iteration, pagination, and filtering. - * - * @param string $from The first resource ID you want to include in your list. - * @param int $limit - * @param array $filters - * @param bool $iterateBackwards Set to true for reverse order iteration (default is false). - * @return LazyCollection - */ - protected function createIterator(?string $from = null, ?int $limit = null, array $filters = [], bool $iterateBackwards = false): LazyCollection - { - /** @var CursorCollection $page */ - $page = $this->fetchCollection($from, $limit, $filters); - - return $page->getAutoIterator($iterateBackwards); - } - /** * @param array $filters * @return string