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
22 changes: 17 additions & 5 deletions src/Pay/Adapter.php
Original file line number Diff line number Diff line change
Expand Up @@ -72,14 +72,26 @@ public function getCurrency(): string
/**
* Make a purchase request
*
* @param int $amount
* @param string $customerId
* @param string|null $paymentMethodId
* @param array<mixed> $additionalParams
* @return array<mixed>
* @param int $amount Amount to charge
* @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
*/
abstract public function purchase(int $amount, string $customerId, ?string $paymentMethodId = null, array $additionalParams = []): array;

/**
* Update a payment intent
*
* @param string $paymentId Payment intent ID
* @param string|null $paymentMethodId Payment method ID (optional)
* @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
*/
abstract public function updatePayment(string $paymentId, ?string $paymentMethodId = null, ?int $amount = null, string $currency = null, array $additionalParams = []): array;

/**
* Retry a purchase for a payment intent
*
Expand Down
30 changes: 30 additions & 0 deletions src/Pay/Adapter/Stripe.php
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,36 @@ public function getPayment(string $paymentId): array
return $this->execute(self::METHOD_GET, $path);
}

/**
* Update a payment intent
*
* @param string $paymentId Payment intent ID
* @param string|null $paymentMethodId Payment method ID (optional)
* @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
*/
public function updatePayment(string $paymentId, ?string $paymentMethodId = null, ?int $amount = null, string $currency = null, array $additionalParams = []): array
{
$path = '/payment_intents/'.$paymentId;
$requestBody = [];
if ($paymentMethodId != null) {
$requestBody['payment_method'] = $paymentMethodId;
}
if ($amount != null) {
$requestBody['amount'] = $amount;
}

if ($currency != null) {
$requestBody['currency'] = $currency;
}

$requestBody = array_merge($requestBody, $additionalParams);

return $this->execute(self::METHOD_POST, $path, $requestBody);
}

/**
* Add a credit card for customer
*/
Expand Down
15 changes: 15 additions & 0 deletions src/Pay/Pay.php
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,21 @@ public function getPayment(string $paymentId): array
return $this->adapter->getPayment($paymentId);
}

/**
* Update a payment intent
*
* @param string $paymentId Payment intent ID
* @param string|null $paymentMethodId Payment method ID (optional)
* @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
*/
public function updatePayment(string $paymentId, ?string $paymentMethodId = null, ?int $amount = null, string $currency = null, array $additionalParams = []): array
{
return $this->adapter->updatePayment($paymentId, $paymentMethodId, $amount, $currency, $additionalParams);
}

/**
* Delete Payment Method
*
Expand Down
54 changes: 54 additions & 0 deletions tests/Pay/Adapter/StripeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,60 @@ public function testGetPayment(array $data): array
return $data;
}

/**
* Test updatePayment: create a payment intent in a non-succeeded state, update its payment method and amount, and assert the update.
*
* @depends testCreateCustomer
*
* @param array<mixed> $data
* @return void
*/
public function testUpdatePayment(array $data): void
{
$customerId = $data['customerId'];
// Create a payment method that will fail (card_declined)
$failingPm = $this->stripe->createPaymentMethod($customerId, 'card', [
'number' => '4000000000000341',
'exp_month' => 8,
'exp_year' => 2030,
'cvc' => 123,
]);
$this->assertNotEmpty($failingPm['id']);
$failingPmId = $failingPm['id'];

// Create a payment intent with the failing payment method
$paymentIntentId = null;
try {
$this->stripe->purchase(5000, $customerId, $failingPmId);
$this->fail('Expected payment to fail');
} catch (Exception $e) {
$this->assertEquals(Exception::GENERIC_DECLINE, $e->getType());
$this->assertEquals(402, $e->getCode());
$paymentIntentMeta = $e->getMetadata()['payment_intent'] ?? null;
$paymentIntentId = is_array($paymentIntentMeta) && isset($paymentIntentMeta['id']) ? $paymentIntentMeta['id'] : $paymentIntentMeta;
$this->assertNotEmpty($paymentIntentId);
}

// Create a succeeding payment method
$succeedingPm = $this->stripe->createPaymentMethod($customerId, 'card', [
'number' => '4242424242424242',
'exp_month' => 8,
'exp_year' => 2030,
'cvc' => 123,
]);
$this->assertNotEmpty($succeedingPm['id']);
$succeedingPmId = $succeedingPm['id'];

// Update the payment intent with the new payment method and amount
$newAmount = 6000;
$updated = $this->stripe->updatePayment((string) $paymentIntentId, $succeedingPmId, $newAmount);
$this->assertNotEmpty($updated['id']);
$this->assertEquals($paymentIntentId, $updated['id']);
$this->assertEquals('payment_intent', $updated['object']);
$this->assertEquals($newAmount, $updated['amount']);
$this->assertEquals($succeedingPmId, $updated['payment_method']);
}

/**
* @depends testPurchase
*
Expand Down