mehdibe
mehdibe

Reputation: 337

Stripe - Save customer payment method only if user ask for it

I use stripe payment element in client side. It sends a request to the server to retrieve the payment intent ID and the client secret.

I would like to be able to save the customer's payment method only if the customer has checked the box save this payment method for future purchases

But I would like the method to register only after I pass my complete flow in server side and be successful.

1- For this I set the capture method to "manual" when I create the payment intent and I capture and update the payment intent when all the server-side instructions have succeeded like this :

if (isWantSaveMethod) {
    await stripe.paymentIntents.update(paymentIntent.id, { setup_future_usage: 'off_session' })
}

await stripe.paymentIntents.capture(paymentIntent.id)

=> The problem is that if I update the payment intent before it is captured, I get the error: You cannot provide a new payment method to a PaymentIntent when it has a status of requires_capture. You should either capture the remaining funds, or cancel this PaymentIntent and try again with a new PaymentIntent.

2- Then I tried to use an intent setup, which apparently allows us to link a payment method to a customer

if (isWantSaveMethod) {
    await stripe.setupIntents.create({
        payment_method: paymentIntent.payment_method,
        confirm: true,
        customer: paymentIntent.customer,
    })
}

await stripe.paymentIntents.capture(paymentIntent.id)

=> But : The provided PaymentMethod was previously used with a PaymentIntent without Customer attachment, shared with a connected account without Customer attachment, or was detached from a Customer. It may not be used again. To use a PaymentMethod multiple times, you must attach it to a Customer first.

3- Then I tried to attach the payment intent to a customer like this :

if (isWantSaveMethod) {
    await stripe.paymentMethods.attach(paymentIntent.payment_method, {
        customer: paymentIntent.customer,
    })

    await stripe.setupIntents.create({
        payment_method: paymentIntent.payment_method,
        confirm: true,
        customer: paymentIntent.customer,
    })
}

await stripe.paymentIntents.capture(paymentIntent.id)

=> But I remember that the documentation say : To attach a new PaymentMethod to a customer for future payments, we recommend you use a SetupIntent or a PaymentIntent with setup_future_usage. These approaches will perform any necessary steps to ensure that the PaymentMethod can be used in a future payment.

So finally I'm here to know what's the good way to do that ? How popular websites do this thing ?

I just want to make sure that all my instructions in the server side executed successfully before save the payment method and I want to be able to use this method later

How can I do that while respecting the recommendations that stripe gave in relation to the method attachment ?

Upvotes: 1

Views: 3079

Answers (1)

karbi
karbi

Reputation: 2163

When using Payment Element the intended user flow is that you would pass in setup_future_usage when the Payment Intent is created. Stripe wants to know if you intend to use setup_future_usage ahead of time so they can choose which Payment Method types to in the Payment Element (since not all of them can be used with setup_future_usage. You should either change your checkout flow to ask whether your customer wants to save their payment method before showing the Payment Element, or you can use the individual elements like Card element instead (each of theses elements should support passing in setup_future_usage with the client-side confirmation call - example here).

Alternatively (I really wouldn't recommend this), you can look into doing the following:

  1. Make sure you haven't enabled any Payment Method types that do not support setup_future_usage (you can find the list here).
  2. Change your client-side code to first send a request to your server to update the Payment Intent to set setup_future_usage, and wait for the response to be successful before continuing to call confirmPayment client-side.

Upvotes: 2

Related Questions