Skip to content

Commit

Permalink
Enable IntentPolling for AmazonPay and RevolutPay (#9476)
Browse files Browse the repository at this point in the history
* Enable IntentPolling for AmazonPay and RevolutPay

* Unit Tests
  • Loading branch information
randyliu-stripe authored Oct 19, 2024
1 parent 9c983c0 commit 4fa2a1a
Show file tree
Hide file tree
Showing 4 changed files with 300 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,26 @@ object PaymentMethodFactory {
)
}

fun amazonPay(): PaymentMethod {
return PaymentMethod(
id = "pm_1234",
created = 123456789L,
liveMode = false,
type = PaymentMethod.Type.AmazonPay,
code = PaymentMethod.Type.AmazonPay.code,
)
}

fun revolutPay(): PaymentMethod {
return PaymentMethod(
id = "pm_1234",
created = 123456789L,
liveMode = false,
type = PaymentMethod.Type.RevolutPay,
code = PaymentMethod.Type.RevolutPay.code,
)
}

fun convertCardToJson(paymentMethod: PaymentMethod): JSONObject {
val paymentMethodJson = convertGenericPaymentMethodToJson(paymentMethod)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,7 @@ constructor(
isVoucher = false,
requiresMandate = false,
hasDelayedSettlement = false,
afterRedirectAction = AfterRedirectAction.Poll(),
),
Sunbit(
"sunbit",
Expand Down Expand Up @@ -383,6 +384,7 @@ constructor(
isVoucher = false,
requiresMandate = false,
hasDelayedSettlement = false,
afterRedirectAction = AfterRedirectAction.Poll(),
),
Alma(
"alma",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -508,6 +508,146 @@ internal class PaymentIntentFlowResultProcessorTest {
verify(mockStripeRepository).refreshPaymentIntent(any(), any())
}

@Test
fun `Stops polling after max retries when encountering a Amazon Pay payment that still requires action`() =
runTest(testDispatcher) {
val requiresActionIntent = PaymentIntentFixtures.PI_SUCCEEDED.copy(
status = StripeIntent.Status.RequiresAction,
paymentMethod = PaymentMethodFactory.amazonPay(),
paymentMethodTypes = listOf("card", "amazon_pay"),
)

whenever(mockStripeRepository.retrievePaymentIntent(any(), any(), any())).thenReturn(
Result.success(requiresActionIntent),
)

val clientSecret = requireNotNull(requiresActionIntent.clientSecret)

val result = createProcessor().processResult(
PaymentFlowResult.Unvalidated(
clientSecret = clientSecret,
flowOutcome = StripeIntentResult.Outcome.UNKNOWN,
)
).getOrThrow()

val expectedResult = PaymentIntentResult(
intent = requiresActionIntent,
outcomeFromFlow = StripeIntentResult.Outcome.UNKNOWN,
failureMessage = "We are unable to authenticate your payment method." +
" Please choose a different payment method and try again.",
)

assertThat(result).isEqualTo(expectedResult)

// We need to retrieve the first time, before we start retries.
verify(mockStripeRepository, times(MAX_RETRIES + 1)).retrievePaymentIntent(any(), any(), any())
}

@Test
fun `Keeps retrying when encountering a Amazon Pay payment that still requires action`() =
runTest(testDispatcher) {
val requiresActionIntent = PaymentIntentFixtures.PI_SUCCEEDED.copy(
status = StripeIntent.Status.RequiresAction,
paymentMethod = PaymentMethodFactory.amazonPay(),
paymentMethodTypes = listOf("card", "amazon_pay"),
)

val succeededIntent = requiresActionIntent.copy(status = StripeIntent.Status.Succeeded)

whenever(mockStripeRepository.retrievePaymentIntent(any(), any(), any())).thenReturn(
Result.success(requiresActionIntent),
Result.success(requiresActionIntent),
Result.success(succeededIntent),
)

val clientSecret = requireNotNull(requiresActionIntent.clientSecret)

val result = createProcessor().processResult(
PaymentFlowResult.Unvalidated(
clientSecret = clientSecret,
flowOutcome = StripeIntentResult.Outcome.UNKNOWN,
)
).getOrThrow()

val expectedResult = PaymentIntentResult(
intent = succeededIntent,
outcomeFromFlow = StripeIntentResult.Outcome.SUCCEEDED,
failureMessage = null,
)

assertThat(result).isEqualTo(expectedResult)
}

@Test
fun `Stops polling after max retries when encountering a Revolut Pay payment that still requires action`() =
runTest(testDispatcher) {
val requiresActionIntent = PaymentIntentFixtures.PI_SUCCEEDED.copy(
status = StripeIntent.Status.RequiresAction,
paymentMethod = PaymentMethodFactory.revolutPay(),
paymentMethodTypes = listOf("card", "revolut_pay"),
)

whenever(mockStripeRepository.retrievePaymentIntent(any(), any(), any())).thenReturn(
Result.success(requiresActionIntent),
)

val clientSecret = requireNotNull(requiresActionIntent.clientSecret)

val result = createProcessor().processResult(
PaymentFlowResult.Unvalidated(
clientSecret = clientSecret,
flowOutcome = StripeIntentResult.Outcome.UNKNOWN,
)
).getOrThrow()

val expectedResult = PaymentIntentResult(
intent = requiresActionIntent,
outcomeFromFlow = StripeIntentResult.Outcome.UNKNOWN,
failureMessage = "We are unable to authenticate your payment method." +
" Please choose a different payment method and try again.",
)

assertThat(result).isEqualTo(expectedResult)

// We need to retrieve the first time, before we start retries.
verify(mockStripeRepository, times(MAX_RETRIES + 1)).retrievePaymentIntent(any(), any(), any())
}

@Test
fun `Keeps retrying when encountering a Revolut Pay payment that still requires action`() =
runTest(testDispatcher) {
val requiresActionIntent = PaymentIntentFixtures.PI_SUCCEEDED.copy(
status = StripeIntent.Status.RequiresAction,
paymentMethod = PaymentMethodFactory.revolutPay(),
paymentMethodTypes = listOf("card", "revolut_pay"),
)

val succeededIntent = requiresActionIntent.copy(status = StripeIntent.Status.Succeeded)

whenever(mockStripeRepository.retrievePaymentIntent(any(), any(), any())).thenReturn(
Result.success(requiresActionIntent),
Result.success(requiresActionIntent),
Result.success(succeededIntent),
)

val clientSecret = requireNotNull(requiresActionIntent.clientSecret)

val result = createProcessor().processResult(
PaymentFlowResult.Unvalidated(
clientSecret = clientSecret,
flowOutcome = StripeIntentResult.Outcome.UNKNOWN,
)
).getOrThrow()

val expectedResult = PaymentIntentResult(
intent = succeededIntent,
outcomeFromFlow = StripeIntentResult.Outcome.SUCCEEDED,
failureMessage = null,
)

assertThat(result).isEqualTo(expectedResult)
}

private suspend fun runCanceledFlow(
initialIntent: PaymentIntent,
refreshedIntent: PaymentIntent = initialIntent,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,144 @@ internal class SetupIntentFlowResultProcessorTest {
failureMessage = null,
)

assertThat(result).isEqualTo(expectedResult)
}

@Test
fun `Stops polling after max retries when encountering a Amazon Pay payment that still requires action`() =
runTest(testDispatcher) {
val requiresActionIntent = SetupIntentFixtures.SI_SUCCEEDED.copy(
status = StripeIntent.Status.RequiresAction,
paymentMethod = PaymentMethodFactory.amazonPay(),
paymentMethodTypes = listOf("card", "amazon_pay"),
)

whenever(mockStripeRepository.retrieveSetupIntent(any(), any(), any())).thenReturn(
Result.success(requiresActionIntent),
)

val clientSecret = requireNotNull(requiresActionIntent.clientSecret)

val result = processor.processResult(
PaymentFlowResult.Unvalidated(
clientSecret = clientSecret,
flowOutcome = StripeIntentResult.Outcome.UNKNOWN,
)
).getOrThrow()

val expectedResult = SetupIntentResult(
intent = requiresActionIntent,
outcomeFromFlow = StripeIntentResult.Outcome.UNKNOWN,
failureMessage = null,
)

assertThat(result).isEqualTo(expectedResult)

// We need to retrieve the first time, before we start retries.
verify(mockStripeRepository, times(MAX_RETRIES + 1)).retrieveSetupIntent(any(), any(), any())
}

@Test
fun `Keeps retrying when encountering a Amazon Pay payment that still requires action`() =
runTest(testDispatcher) {
val requiresActionIntent = SetupIntentFixtures.SI_SUCCEEDED.copy(
status = StripeIntent.Status.RequiresAction,
paymentMethod = PaymentMethodFactory.amazonPay(),
paymentMethodTypes = listOf("card", "amazon_pay"),
)

val succeededIntent = requiresActionIntent.copy(status = StripeIntent.Status.Succeeded)

whenever(mockStripeRepository.retrieveSetupIntent(any(), any(), any())).thenReturn(
Result.success(requiresActionIntent),
Result.success(requiresActionIntent),
Result.success(succeededIntent),
)

val clientSecret = requireNotNull(requiresActionIntent.clientSecret)

val result = processor.processResult(
PaymentFlowResult.Unvalidated(
clientSecret = clientSecret,
flowOutcome = StripeIntentResult.Outcome.UNKNOWN,
)
).getOrThrow()

val expectedResult = SetupIntentResult(
intent = succeededIntent,
outcomeFromFlow = StripeIntentResult.Outcome.SUCCEEDED,
failureMessage = null,
)

assertThat(result).isEqualTo(expectedResult)
}

@Test
fun `Stops polling after max retries when encountering a Revolut Pay payment that still requires action`() =
runTest(testDispatcher) {
val requiresActionIntent = SetupIntentFixtures.SI_SUCCEEDED.copy(
status = StripeIntent.Status.RequiresAction,
paymentMethod = PaymentMethodFactory.revolutPay(),
paymentMethodTypes = listOf("card", "revolut_pay"),
)

whenever(mockStripeRepository.retrieveSetupIntent(any(), any(), any())).thenReturn(
Result.success(requiresActionIntent),
)

val clientSecret = requireNotNull(requiresActionIntent.clientSecret)

val result = processor.processResult(
PaymentFlowResult.Unvalidated(
clientSecret = clientSecret,
flowOutcome = StripeIntentResult.Outcome.UNKNOWN,
)
).getOrThrow()

val expectedResult = SetupIntentResult(
intent = requiresActionIntent,
outcomeFromFlow = StripeIntentResult.Outcome.UNKNOWN,
failureMessage = null,
)

assertThat(result).isEqualTo(expectedResult)

// We need to retrieve the first time, before we start retries.
verify(mockStripeRepository, times(MAX_RETRIES + 1)).retrieveSetupIntent(any(), any(), any())
}

@Test
fun `Keeps retrying when encountering a Revolut Pay payment that still requires action`() =
runTest(testDispatcher) {
val requiresActionIntent = SetupIntentFixtures.SI_SUCCEEDED.copy(
status = StripeIntent.Status.RequiresAction,
paymentMethod = PaymentMethodFactory.revolutPay(),
paymentMethodTypes = listOf("card", "revolut_pay"),
)

val succeededIntent = requiresActionIntent.copy(status = StripeIntent.Status.Succeeded)

whenever(mockStripeRepository.retrieveSetupIntent(any(), any(), any())).thenReturn(
Result.success(requiresActionIntent),
Result.success(requiresActionIntent),
Result.success(succeededIntent),
)

val clientSecret = requireNotNull(requiresActionIntent.clientSecret)

val result = processor.processResult(
PaymentFlowResult.Unvalidated(
clientSecret = clientSecret,
flowOutcome = StripeIntentResult.Outcome.UNKNOWN,
)
).getOrThrow()

val expectedResult = SetupIntentResult(
intent = succeededIntent,
outcomeFromFlow = StripeIntentResult.Outcome.SUCCEEDED,
failureMessage = null,
)

assertThat(result).isEqualTo(expectedResult)
}
}

0 comments on commit 4fa2a1a

Please sign in to comment.