Abeid Ahmed
Abeid Ahmed

Reputation: 355

Stripe subscription with 3D secure is incomplete

For cards that do not require 3D security, it works flawlessly. But for those that require them, it gets stuck at incomplete status. My implementation is very simple. The frontend collects the card info and makes a request to stripe's API to get the token and to create the payment_method.

async submitForm(e) {
  e.preventDefault();

  try {
    const { token } = await this.stripe.createToken(this.card);
    const { paymentMethod } = await this.stripe.createPaymentMethod({
      type: 'card',
      card: this.card,
    });

    this.stripeTokenHandler(token.id, paymentMethod.id); // handles submission to backend
  } catch (e) {
    this.cardErrorsTarget.textContent = e.error.message;
  }
}

The backend part is written in rails.

customer = Stripe::Customer.create(
  source: params[:stripe_token],
  plan: plan_id,
  # address of the customer
)

# Creating the setup intent
intent = Stripe::PaymentIntent.create(
  amount: some_amount,
  customer: customer.id,
  payment_method_types: ["card"],
)

# After that I need to confirm the intent
confirm_intent = Stripe::PaymentIntent.confirm(intent.id, {
  payment_method: params[:pm_token],
  return_url: app_confirm_subscription_url # After 3D secure, it redirects to this URL
})

After the redirection,

def show
  intent = Stripe::PaymentIntent.retrieve(params[:payment_intent]) # stripe adds the payment_intent params
  
  raise intent
end

By raising the intent, I see that

{
  paid: true,
  status: "succeeded",
  result: "authenticated",
  // more data
}

But when I go to Stripe's dashboard, it shows that the subscription is incomplete. These are the events in descending order.

The payment pi_1I6AVKCYHrwDIsnfiGm7bWUM for $29.00 USD has succeeded

[email protected] was charged $29.00 USD

The payment pi_1I6AVKCYHrwDIsnfiGm7bWUM for $29.00 USD requires you to take action in order to complete the payment

A new payment pi_1I6AVKCYHrwDIsnfiGm7bWUM for $29.00 USD was created

[email protected] attempted to subscribe to price_1I5vshCYHrwDIsnf981UpZEc

[email protected] added a new Visa ending in 3220

A card payment method ending in 3220 was attached to customer cus_IhZezT431x2G7Z

A draft invoice for $29.00 USD to [email protected] was finalized

A draft invoice was created

[email protected] is a new customer

PS: The card that I used is 4000 0000 0000 3220, which requires 3D security. If I use the 4242 4242 4242 4242, it works without any problems.

Is there something that I'm missing?

Upvotes: 2

Views: 6678

Answers (1)

v3nkman
v3nkman

Reputation: 1179

Creating a standalone PaymentIntent and using it to drive subscription creation is in this case the wrong integration. If we make reference to the guide for starting a fixed price subscription using Elements [1], the major steps are as follows:

  1. Create a Customer
  2. Collect the card information using a card element using createPaymentMethod from Stripe.js
  3. Start the subscription by, a) attaching the payment method to the customer b) setting the payment method as the invoice settings, default payment method, c) creating the subscription
  4. Handle authentication [2]

The key difference here is step 4. When you start a subscription, a PaymentIntent is created automatically under the hood and associated with the first invoice of the subscription. This is the PaymentIntent that should be used in conjunction with confirmCardPayment in the client using Stripe.js and the PaymentIntent's client secret [2]. It can be accessed by checking the latest_invoice.payment_intent.status of the subscription after it is created i.e. after the first payment attempt.

There are quite a few other steps to handle for example when payment fails but the guide goes into details about the possible flows that you will encounter.

[1] https://stripe.com/docs/billing/subscriptions/fixed-price

[2] https://stripe.com/docs/billing/subscriptions/fixed-price#manage-payment-authentication

Upvotes: 2

Related Questions