From e9f1fd92533de527eb02064335c368bc41d938a7 Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Sun, 3 Aug 2025 04:38:09 +0000 Subject: [PATCH 1/3] Feat: update payment --- src/Pay/Adapter.php | 22 +++++++++++++++++----- src/Pay/Adapter/Stripe.php | 30 ++++++++++++++++++++++++++++++ src/Pay/Pay.php | 15 +++++++++++++++ 3 files changed, 62 insertions(+), 5 deletions(-) diff --git a/src/Pay/Adapter.php b/src/Pay/Adapter.php index f9685dc..6679782 100644 --- a/src/Pay/Adapter.php +++ b/src/Pay/Adapter.php @@ -72,14 +72,26 @@ public function getCurrency(): string /** * Make a purchase request * - * @param int $amount - * @param string $customerId - * @param string|null $paymentMethodId - * @param array $additionalParams - * @return array + * @param int $amount Amount to charge + * @param string $customerId Customer ID + * @param string|null $paymentMethodId Payment method ID (optional) + * @param array $additionalParams Additional parameters (optional) + * @return array 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 $additionalParams Additional parameters (optional) + * @return array 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 * diff --git a/src/Pay/Adapter/Stripe.php b/src/Pay/Adapter/Stripe.php index c42d936..3ad3e91 100644 --- a/src/Pay/Adapter/Stripe.php +++ b/src/Pay/Adapter/Stripe.php @@ -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 $additionalParams Additional parameters (optional) + * @return array 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 */ diff --git a/src/Pay/Pay.php b/src/Pay/Pay.php index 333b072..a6fedac 100644 --- a/src/Pay/Pay.php +++ b/src/Pay/Pay.php @@ -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 $additionalParams Additional parameters (optional) + * @return array 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 * From b031ef01978b09dfa8bb5dd8516594c8d59e1457 Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Sun, 3 Aug 2025 04:46:09 +0000 Subject: [PATCH 2/3] add relevant test --- tests/Pay/Adapter/StripeTest.php | 54 ++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/tests/Pay/Adapter/StripeTest.php b/tests/Pay/Adapter/StripeTest.php index 9cfb2df..3fdff32 100644 --- a/tests/Pay/Adapter/StripeTest.php +++ b/tests/Pay/Adapter/StripeTest.php @@ -535,4 +535,58 @@ public function testErrorHandling(): void $this->assertInstanceOf(Exception::class, $e); } } + + /** + * 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 $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']); + } } From cf6a766054ef7874e698f915f1a6eb7af9d45a9d Mon Sep 17 00:00:00 2001 From: Damodar Lohani Date: Sun, 3 Aug 2025 04:48:31 +0000 Subject: [PATCH 3/3] re-organize --- tests/Pay/Adapter/StripeTest.php | 108 +++++++++++++++---------------- 1 file changed, 54 insertions(+), 54 deletions(-) diff --git a/tests/Pay/Adapter/StripeTest.php b/tests/Pay/Adapter/StripeTest.php index 3fdff32..843ab0b 100644 --- a/tests/Pay/Adapter/StripeTest.php +++ b/tests/Pay/Adapter/StripeTest.php @@ -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 $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 * @@ -535,58 +589,4 @@ public function testErrorHandling(): void $this->assertInstanceOf(Exception::class, $e); } } - - /** - * 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 $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']); - } }