Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
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
110 changes: 68 additions & 42 deletions src/Pay/Adapter.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,15 @@

namespace Utopia\Pay;

use Utopia\Pay\Customer\Customer;
use Utopia\Pay\Dispute\Dispute;
use Utopia\Pay\Mandate\Mandate;
use Utopia\Pay\Payment\Payment;
use Utopia\Pay\PaymentMethod\PaymentMethod;
use Utopia\Pay\Refund\Refund;
use Utopia\Pay\SetupIntent\SetupIntent;
use Utopia\Pay\Webhook\WebhookEvent;

abstract class Adapter
{
/**
Expand Down Expand Up @@ -58,9 +67,9 @@ public function getCurrency(): string
* @param string $customerId Customer ID
* @param string|null $paymentMethodId Payment method ID (optional)
* @param array<mixed> $additionalParams Additional parameters (optional)
* @return array<mixed> Result of the purchase
* @return Payment Result of the purchase
*/
abstract public function purchase(int $amount, string $customerId, ?string $paymentMethodId = null, array $additionalParams = []): array;
abstract public function purchase(int $amount, string $customerId, ?string $paymentMethodId = null, array $additionalParams = []): Payment;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Typed returns break compatibility

Changing the abstract adapter operations from array to concrete model return types breaks both sides of the existing public contract. Third-party adapters that still implement the documented : array signatures will fail PHP's method compatibility check when their classes load. Existing consumers that follow the README and access results through $customer['id'] or $authorization['id'] will instead receive model objects that do not implement ArrayAccess, causing runtime failures. The related conversions in Stripe also reduce listCustomers() and listPaymentMethods() to their data items, so callers can no longer recover list metadata such as has_more.

Knowledge Base Used:

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/Pay/Adapter.php
Line: 72

Comment:
**Typed returns break compatibility**

Changing the abstract adapter operations from `array` to concrete model return types breaks both sides of the existing public contract. Third-party adapters that still implement the documented `: array` signatures will fail PHP's method compatibility check when their classes load. Existing consumers that follow the README and access results through `$customer['id']` or `$authorization['id']` will instead receive model objects that do not implement `ArrayAccess`, causing runtime failures. The related conversions in `Stripe` also reduce `listCustomers()` and `listPaymentMethods()` to their `data` items, so callers can no longer recover list metadata such as `has_more`.

**Knowledge Base Used:**
- [Payment adapter contract](https://app.greptile.com/appwrite/-/custom-context/knowledge-base/utopia-php/pay/-/docs/payment-adapter-contract.md)
- [Payment client orchestration](https://app.greptile.com/appwrite/-/custom-context/knowledge-base/utopia-php/pay/-/docs/payment-client-orchestration.md)

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Fix in Claude Code Fix in Codex


/**
* Authorize a payment (hold funds without capturing)
Expand All @@ -70,9 +79,9 @@ abstract public function purchase(int $amount, string $customerId, ?string $paym
* @param string $customerId Customer ID
* @param string|null $paymentMethodId Payment method ID (optional)
* @param array<mixed> $additionalParams Additional parameters (optional)
* @return array<mixed> Result of the authorization including authorization ID
* @return Payment Result of the authorization including authorization ID
*/
abstract public function authorize(int $amount, string $customerId, ?string $paymentMethodId = null, array $additionalParams = []): array;
abstract public function authorize(int $amount, string $customerId, ?string $paymentMethodId = null, array $additionalParams = []): Payment;

/**
* Capture a previously authorized payment
Expand All @@ -81,19 +90,19 @@ abstract public function authorize(int $amount, string $customerId, ?string $pay
* @param string $paymentId The payment/authorization ID to capture
* @param int|null $amount Amount to capture (optional, defaults to full authorized amount)
* @param array<mixed> $additionalParams Additional parameters (optional)
* @return array<mixed> Result of the capture
* @return Payment Result of the capture
*/
abstract public function capture(string $paymentId, ?int $amount = null, array $additionalParams = []): array;
abstract public function capture(string $paymentId, ?int $amount = null, array $additionalParams = []): Payment;

/**
* Cancel/void a payment authorization
* Releases the hold on funds without capturing
*
* @param string $paymentId The payment/authorization ID to cancel
* @param array<mixed> $additionalParams Additional parameters (optional)
* @return array<mixed> Result of the cancellation
* @return Payment Result of the cancellation
*/
abstract public function cancelAuthorization(string $paymentId, array $additionalParams = []): array;
abstract public function cancelAuthorization(string $paymentId, array $additionalParams = []): Payment;

/**
* Update a payment intent
Expand All @@ -103,47 +112,47 @@ abstract public function cancelAuthorization(string $paymentId, array $additiona
* @param int|null $amount Amount to update (optional)
* @param string|null $currency Currency to update (optional)
* @param array<mixed> $additionalParams Additional parameters (optional)
* @return array<mixed> Result of the update
* @return Payment Result of the update
*/
abstract public function updatePayment(string $paymentId, ?string $paymentMethodId = null, ?int $amount = null, ?string $currency = null, array $additionalParams = []): array;
abstract public function updatePayment(string $paymentId, ?string $paymentMethodId = null, ?int $amount = null, ?string $currency = null, array $additionalParams = []): Payment;

/**
* Retry a purchase for a payment intent
*
* @param string $paymentId The payment intent ID to retry
* @param string|null $paymentMethodId The payment method to use (optional)
* @param array<mixed> $additionalParams Additional parameters for the retry (optional)
* @return array<mixed> The result of the retry attempt
* @return Payment The result of the retry attempt
*/
abstract public function retryPurchase(string $paymentId, ?string $paymentMethodId = null, array $additionalParams = []): array;
abstract public function retryPurchase(string $paymentId, ?string $paymentMethodId = null, array $additionalParams = []): Payment;

/**
* Refund payment
*
* @param string $paymentId
* @param int $amount
* @param string $reason
* @return array<mixed>
* @return Refund
*/
abstract public function refund(string $paymentId, ?int $amount = null, ?string $reason = null): array;
abstract public function refund(string $paymentId, ?int $amount = null, ?string $reason = null): Refund;

/**
* Get a payment details
*
* @param string $paymentId
* @return array<mixed>
* @return Payment
*/
abstract public function getPayment(string $paymentId): array;
abstract public function getPayment(string $paymentId): Payment;

/**
* Add a payment method
*
* @param string $customerId
* @param string $type
* @param array<mixed> $details
* @return array<mixed>
* @return PaymentMethod
*/
abstract public function createPaymentMethod(string $customerId, string $type, array $details): array;
abstract public function createPaymentMethod(string $customerId, string $type, array $details): PaymentMethod;

/**
* Update payment method billing details
Expand All @@ -153,25 +162,25 @@ abstract public function createPaymentMethod(string $customerId, string $type, a
* @param string|null $email
* @param string|null $phone
* @param array<mixed>|null $address
* @return array<mixed>
* @return PaymentMethod
*/
abstract public function updatePaymentMethodBillingDetails(string $paymentMethodId, ?string $name = null, ?string $email = null, ?string $phone = null, ?array $address = null): array;
abstract public function updatePaymentMethodBillingDetails(string $paymentMethodId, ?string $name = null, ?string $email = null, ?string $phone = null, ?array $address = null): PaymentMethod;

/**
* Update payment method
*
* @param string $paymentMethodId
* @param string $type
* @param array<mixed> $details
* @return array<mixed>
* @return PaymentMethod
*/
abstract public function updatePaymentMethod(string $paymentMethodId, string $type, array $details): array;
abstract public function updatePaymentMethod(string $paymentMethodId, string $type, array $details): PaymentMethod;

/**
* List payment methods
*
* @param string $customerId
* @return array<mixed>
* @return array<PaymentMethod>
*/
abstract public function listPaymentMethods(string $customerId): array;

Expand All @@ -190,24 +199,24 @@ abstract public function deletePaymentMethod(string $paymentMethodId): bool;
* @param string $email
* @param array<mixed> $address
* @param string|null $paymentMethod
* @return array<mixed>
* @return Customer
*/
abstract public function createCustomer(string $name, string $email, array $address = [], ?string $paymentMethod = null): array;
abstract public function createCustomer(string $name, string $email, array $address = [], ?string $paymentMethod = null): Customer;

/**
* List customers
*
* @return array<mixed>
* @return array<Customer>
*/
abstract public function listCustomers(): array;

/**
* Get customer details by ID
*
* @param string $customerId
* @return array<mixed>
* @return Customer
*/
abstract public function getCustomer(string $customerId): array;
abstract public function getCustomer(string $customerId): Customer;

/**
* Update customer details
Expand All @@ -217,9 +226,9 @@ abstract public function getCustomer(string $customerId): array;
* @param string $email
* @param Address|null $address
* @param string|null $paymentMethod
* @return array<mixed>
* @return Customer
*/
abstract public function updateCustomer(string $customerId, string $name, string $email, ?Address $address = null, ?string $paymentMethod = null): array;
abstract public function updateCustomer(string $customerId, string $name, string $email, ?Address $address = null, ?string $paymentMethod = null): Customer;

/**
* Delete Customer
Expand All @@ -234,9 +243,9 @@ abstract public function deleteCustomer(string $customerId): bool;
*
* @param string $customerId
* @param string $paymentMethodId
* @return array<mixed>
* @return PaymentMethod
*/
abstract public function getPaymentMethod(string $customerId, string $paymentMethodId): array;
abstract public function getPaymentMethod(string $customerId, string $paymentMethodId): PaymentMethod;

/**
* Create setup for accepting future payments
Expand All @@ -246,26 +255,26 @@ abstract public function getPaymentMethod(string $customerId, string $paymentMet
* @param array<mixed> $paymentMethodTypes
* @param array<mixed> $paymentMethodOptions
* @param ?string $paymentMethodConfiguration
* @return array<mixed>
* @return SetupIntent
*/
abstract public function createFuturePayment(string $customerId, ?string $paymentMethod = null, array $paymentMethodTypes = [], array $paymentMethodOptions = [], ?string $paymentMethodConfiguration = null): array;
abstract public function createFuturePayment(string $customerId, ?string $paymentMethod = null, array $paymentMethodTypes = [], array $paymentMethodOptions = [], ?string $paymentMethodConfiguration = null): SetupIntent;

/**
* List future payments associated with the provided customer or payment method
*
* @param string|null $customerId
* @param string|null $paymentMethodId
* @return array<mixed>
* @return array<SetupIntent>
*/
abstract public function listFuturePayments(?string $customerId = null, ?string $paymentMethodId = null): array;

/**
* Get Future payment
*
* @param string $id
* @return array<mixed>
* @return SetupIntent
*/
abstract public function getFuturePayment(string $id): array;
abstract public function getFuturePayment(string $id): SetupIntent;

/**
* Update future payment setup
Expand All @@ -275,17 +284,17 @@ abstract public function getFuturePayment(string $id): array;
* @param string|null $paymentMethod
* @param array<mixed> $paymentMethodOptions
* @param string|null $paymentMethodConfiguration
* @return array<mixed>
* @return SetupIntent
*/
abstract public function updateFuturePayment(string $id, ?string $customerId = null, ?string $paymentMethod = null, array $paymentMethodOptions = [], ?string $paymentMethodConfiguration = null): array;
abstract public function updateFuturePayment(string $id, ?string $customerId = null, ?string $paymentMethod = null, array $paymentMethodOptions = [], ?string $paymentMethodConfiguration = null): SetupIntent;

/**
* Get mandate
*
* @param string $id
* @return array<mixed>
* @return Mandate
*/
abstract public function getMandate(string $id): array;
abstract public function getMandate(string $id): Mandate;

/**
* List disputes
Expand All @@ -294,7 +303,24 @@ abstract public function getMandate(string $id): array;
* @param string|null $paymentIntentId
* @param string|null $chargeId
* @param int|null $createdAfter
* @return array
* @return array<Dispute>
*/
abstract public function listDisputes(?int $limit = null, ?string $paymentIntentId = null, ?string $chargeId = null, ?int $createdAfter = null): array;

/**
* Verify a webhook signature and decode the event
*
* @param string $payload Raw request body, exactly as received
* @param string $signatureHeader
* @param string $secret
* @param int|null $tolerance Maximum age of the signature in seconds, null to skip the check
* @return WebhookEvent
*
* @throws Exception
*/
public function constructWebhookEvent(string $payload, string $signatureHeader, string $secret, ?int $tolerance = 300): WebhookEvent
{
// Not abstract so adding it does not break third-party adapters
throw new Exception(Exception::GENERAL_UNKNOWN, $this->getName().' does not support webhooks');
}
}
Loading
Loading