From 3a848315fa561c32d32f74e1d0b610246adaac29 Mon Sep 17 00:00:00 2001 From: Francis Hilaire Date: Fri, 15 Dec 2023 19:37:21 +0100 Subject: [PATCH] Handle the async failed status --- src/Action/Api/StripeApiAwareTrait.php | 1 + src/Action/StatusPaymentIntentAction.php | 24 +++++++++++++++-- src/Action/StatusSetupIntentAction.php | 27 +++++++++++++++++-- .../Action/StatusPaymentIntentActionTest.php | 23 +++++++++++++++- tests/Action/StatusSetupIntentActionTest.php | 21 +++++++++++++++ 5 files changed, 91 insertions(+), 5 deletions(-) diff --git a/src/Action/Api/StripeApiAwareTrait.php b/src/Action/Api/StripeApiAwareTrait.php index 80eaa8d..cb3311f 100644 --- a/src/Action/Api/StripeApiAwareTrait.php +++ b/src/Action/Api/StripeApiAwareTrait.php @@ -9,6 +9,7 @@ /** * @property StripeClientAwareInterface $api + * @property string $apiClass */ trait StripeApiAwareTrait { diff --git a/src/Action/StatusPaymentIntentAction.php b/src/Action/StatusPaymentIntentAction.php index 8c13f93..45e71b0 100644 --- a/src/Action/StatusPaymentIntentAction.php +++ b/src/Action/StatusPaymentIntentAction.php @@ -7,6 +7,7 @@ use Payum\Core\Bridge\Spl\ArrayObject; use Payum\Core\Request\GetStatusInterface; use Stripe\PaymentIntent; +use Stripe\StripeObject; class StatusPaymentIntentAction extends AbstractStatusAction { @@ -36,7 +37,7 @@ public function isMarkedStatus(GetStatusInterface $request, ArrayObject $model): return true; } - if ($this->isCanceledStatus($status)) { + if ($this->isCanceledStatus($status) || $this->isSpecialCanceledStatus($model)) { $request->markCanceled(); return true; @@ -52,7 +53,7 @@ public function isMarkedStatus(GetStatusInterface $request, ArrayObject $model): } /** - * @see https://stripe.com/docs/payments/intents#payment-intent + * @see https://stripe.com/docs/payments/paymentintents/lifecycle */ protected function isCanceledStatus(string $status): bool { @@ -72,4 +73,23 @@ public function getSupportedObjectName(): string { return PaymentIntent::OBJECT_NAME; } + + /** + * @see https://stripe.com/docs/payments/paymentintents/lifecycle + */ + protected function isSpecialCanceledStatus(ArrayObject $model): bool + { + /** @var string|null $status */ + $status = $model->offsetGet('status'); + /** @var null|StripeObject $lastPaymentError */ + $lastPaymentError = $model->offsetGet('last_payment_error'); + + if (PaymentIntent::STATUS_REQUIRES_PAYMENT_METHOD === $status) { + if (null !== $lastPaymentError) { + return true; + } + } + + return false; + } } diff --git a/src/Action/StatusSetupIntentAction.php b/src/Action/StatusSetupIntentAction.php index 5f0e440..a9f8461 100644 --- a/src/Action/StatusSetupIntentAction.php +++ b/src/Action/StatusSetupIntentAction.php @@ -6,7 +6,9 @@ use Payum\Core\Bridge\Spl\ArrayObject; use Payum\Core\Request\GetStatusInterface; +use Stripe\PaymentIntent; use Stripe\SetupIntent; +use Stripe\StripeObject; class StatusSetupIntentAction extends AbstractStatusAction { @@ -30,7 +32,7 @@ public function isMarkedStatus(GetStatusInterface $request, ArrayObject $model): return true; } - if ($this->isCanceledStatus($status)) { + if ($this->isCanceledStatus($status) || $this->isSpecialCanceledStatus($model)) { $request->markCanceled(); return true; @@ -46,7 +48,7 @@ public function isMarkedStatus(GetStatusInterface $request, ArrayObject $model): } /** - * @see https://stripe.com/docs/payments/intents#payment-intent + * @see https://stripe.com/docs/payments/setupintents/lifecycle */ protected function isCanceledStatus(string $status): bool { @@ -62,6 +64,27 @@ protected function isNewStatus(string $status): bool ], true); } + + + /** + * @see https://stripe.com/docs/payments/setupintents/lifecycle + */ + protected function isSpecialCanceledStatus(ArrayObject $model): bool + { + /** @var string|null $status */ + $status = $model->offsetGet('status'); + /** @var null|StripeObject $lastPaymentError */ + $lastPaymentError = $model->offsetGet('last_setup_error'); + + if (SetupIntent::STATUS_REQUIRES_PAYMENT_METHOD === $status) { + if (null !== $lastPaymentError) { + return true; + } + } + + return false; + } + public function getSupportedObjectName(): string { return SetupIntent::OBJECT_NAME; diff --git a/tests/Action/StatusPaymentIntentActionTest.php b/tests/Action/StatusPaymentIntentActionTest.php index de2593e..e1db087 100644 --- a/tests/Action/StatusPaymentIntentActionTest.php +++ b/tests/Action/StatusPaymentIntentActionTest.php @@ -12,6 +12,7 @@ use Payum\Core\Request\Sync; use PHPUnit\Framework\TestCase; use Stripe\PaymentIntent; +use Stripe\StripeObject; final class StatusPaymentIntentActionTest extends TestCase { @@ -161,7 +162,27 @@ public function testShouldMarkCanceledIfIsAPaymentIntentObjectAndStatusIsCancele $this->assertTrue($request->isCanceled()); } - public function testShouldMarkAsCanceledIfIsAPaymentIntentObjectAndStatusRequiresPaymentMethod(): void + public function testShouldMarkAsCanceledIfIsAPaymentIntentObjectAndStatusRequiresPaymentMethodWithError(): void + { + $action = $this->createStatusWithGateway(); + + $model = [ + 'object' => PaymentIntent::OBJECT_NAME, + 'status' => PaymentIntent::STATUS_REQUIRES_PAYMENT_METHOD, + 'last_payment_error' => new StripeObject(), + ]; + + $request = new GetHumanStatus($model); + + $supports = $action->supports($request); + $this->assertTrue($supports); + + $action->execute($request); + + $this->assertTrue($request->isCanceled()); + } + + public function testShouldMarkAsNewIfIsAPaymentIntentObjectAndStatusRequiresPaymentMethod(): void { $action = $this->createStatusWithGateway(); diff --git a/tests/Action/StatusSetupIntentActionTest.php b/tests/Action/StatusSetupIntentActionTest.php index 546c88c..da000f4 100644 --- a/tests/Action/StatusSetupIntentActionTest.php +++ b/tests/Action/StatusSetupIntentActionTest.php @@ -12,6 +12,7 @@ use Payum\Core\Request\Sync; use PHPUnit\Framework\TestCase; use Stripe\SetupIntent; +use Stripe\StripeObject; final class StatusSetupIntentActionTest extends TestCase { @@ -161,6 +162,26 @@ public function testShouldMarkAsCanceledIfIsASetupIntentObjectAndStatusRequiresP $this->assertTrue($request->isNew()); } + public function testShouldMarkAsNewIfIsASetupIntentObjectAndStatusRequiresPaymentMethodWithError(): void + { + $action = $this->createStatusWithGateway(); + + $model = [ + 'object' => SetupIntent::OBJECT_NAME, + 'status' => SetupIntent::STATUS_REQUIRES_PAYMENT_METHOD, + 'last_setup_error' => new StripeObject(), + ]; + + $request = new GetHumanStatus($model); + + $supports = $action->supports($request); + $this->assertTrue($supports); + + $action->execute($request); + + $this->assertTrue($request->isCanceled()); + } + public function testShouldMarkAsNewIfIsASetupIntentObjectAndStatusRequiresConfirmation(): void { $action = $this->createStatusWithGateway();