最新消息:Welcome to the puzzle paradise for programmers! Here, a well-designed puzzle awaits you. From code logic puzzles to algorithmic challenges, each level is closely centered on the programmer's expertise and skills. Whether you're a novice programmer or an experienced tech guru, you'll find your own challenges on this site. In the process of solving puzzles, you can not only exercise your thinking skills, but also deepen your understanding and application of programming knowledge. Come to start this puzzle journey full of wisdom and challenges, with many programmers to compete with each other and show your programming wisdom! Translated with DeepL.com (free version)

kotlin - Stripe processing 3ds payment on Android - Stack Overflow

matteradmin6PV0评论

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 badges
Add a comment  | 

2 Answers 2

Reset to default 0

It 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
Post a comment

comment list (0)

  1. No comments so far