I have the following problem, processing payment with 3ds card. My app uses Jetpack compose I have a payment screen. So I create a payment intent with new or saved card. Then I check if I need 3ds and start 3ds processing the following way:
@Composable
fun ThreeDSScreen(
publishableKey: String,
secret: String,
onSuccess: () -> Unit,
onCancel: () -> Unit,
onFail: () -> Unit
) {
val paymentLauncher = rememberPaymentLauncher(
publishableKey = publishableKey,
stripeAccountId = null,
callback = PaymentResultCallback(
onSuccess = onSuccess,
onCancel = onCancel,
onFail = onFail
)
)
val needLaunch = remember { mutableStateOf(true) }
LaunchedEffect(secret) {
if (needLaunch.value) {
needLaunch.value = false
delay(200)
paymentLauncher.confirm(
ConfirmPaymentIntentParams.create(
clientSecret = secret,
paymentMethodType = PaymentMethod.Type.CardPresent
)
)
}
}
}
class PaymentResultCallback(
private val onSuccess: () -> Unit,
private val onCancel: () -> Unit,
private val onFail: () -> Unit
) : PaymentLauncher.PaymentResultCallback {
override fun onPaymentResult(paymentResult: PaymentResult) {
when (paymentResult) {
is PaymentResult.Completed -> { onSuccess() }
is PaymentResult.Canceled -> { onCancel() }
is PaymentResult.Failed -> { onFail() }
}
}
}
I also save client secret in encrypted shared prefs. And it works fine whether a user fails it or cancel or confirm it. But let's imagine I stopped app. And I want in my app on start to check if pending intent exists. So I check it. And if I see a pending intent I try to confirm it the same way. Passing client secret to this composable. I use Stripe test VISA card with 3ds in order to check. So I start this function and see the Stripe 3ds test screen is starting and just after that Stripe behaves as if a user pressed fail. What am I doing wrong? I checked that my secret key is ok.
UPD: After running Stripe confirm I get Fail. So I'm trying to cancel pending payment intent. If I didn't succeed, I try to confirm again and from the second time it runs successfully. It seems that there's some problem with running this confirmation.
I have the following problem, processing payment with 3ds card. My app uses Jetpack compose I have a payment screen. So I create a payment intent with new or saved card. Then I check if I need 3ds and start 3ds processing the following way:
@Composable
fun ThreeDSScreen(
publishableKey: String,
secret: String,
onSuccess: () -> Unit,
onCancel: () -> Unit,
onFail: () -> Unit
) {
val paymentLauncher = rememberPaymentLauncher(
publishableKey = publishableKey,
stripeAccountId = null,
callback = PaymentResultCallback(
onSuccess = onSuccess,
onCancel = onCancel,
onFail = onFail
)
)
val needLaunch = remember { mutableStateOf(true) }
LaunchedEffect(secret) {
if (needLaunch.value) {
needLaunch.value = false
delay(200)
paymentLauncher.confirm(
ConfirmPaymentIntentParams.create(
clientSecret = secret,
paymentMethodType = PaymentMethod.Type.CardPresent
)
)
}
}
}
class PaymentResultCallback(
private val onSuccess: () -> Unit,
private val onCancel: () -> Unit,
private val onFail: () -> Unit
) : PaymentLauncher.PaymentResultCallback {
override fun onPaymentResult(paymentResult: PaymentResult) {
when (paymentResult) {
is PaymentResult.Completed -> { onSuccess() }
is PaymentResult.Canceled -> { onCancel() }
is PaymentResult.Failed -> { onFail() }
}
}
}
I also save client secret in encrypted shared prefs. And it works fine whether a user fails it or cancel or confirm it. But let's imagine I stopped app. And I want in my app on start to check if pending intent exists. So I check it. And if I see a pending intent I try to confirm it the same way. Passing client secret to this composable. I use Stripe test VISA card with 3ds in order to check. So I start this function and see the Stripe 3ds test screen is starting and just after that Stripe behaves as if a user pressed fail. What am I doing wrong? I checked that my secret key is ok.
UPD: After running Stripe confirm I get Fail. So I'm trying to cancel pending payment intent. If I didn't succeed, I try to confirm again and from the second time it runs successfully. It seems that there's some problem with running this confirmation.
Share Improve this question edited Nov 18, 2024 at 23:04 Al Sh asked Nov 18, 2024 at 15:42 Al ShAl Sh 511 silver badge4 bronze badges2 Answers
Reset to default 0It sounds like you're running into an issue where the 3D Secure (3DS) flow is prematurely failing when you try to confirm the payment intent after the app is restarted, even though the clientSecret and setup seem correct. The issue could be related to how the payment confirmation process is triggered after the app restarts, as well as how the payment state is handled in your app during the reinitialization.
Here's the code : first pic of the code : [1]: https://i.sstatic/nzkDItPN.png
It appears you are mistakenly indicating CardPresent
(which is for use with Stripe Terminal and reading cards in person), instead of Card
for online cards.
I would recommend you revise this line and retest everything:
- paymentMethodType = PaymentMethod.Type.CardPresent
+ paymentMethodType = PaymentMethod.Type.Card