javascriptnode.jsstripe-paymentsinvoice3d-secure

How to handle products and subscriptions in one invoice with 3d secure flow on stripe?


guys! First time touched stripe got confused with such problem - Need to make flow for paying for combination PRODUCT + SUBSCRIPTION in one payment (with 3d secure flow if it is req) and to send one total invoice for customer. So I've made such a plan:

  1. Create stripe customer
  2. Create payment method based on card elements
  3. Attach payment method to customer
  4. Create subscriptions.
  5. Create paymentIntent (with return url prop if 3ds is required) to hold money on customer's card.
  6. Capture money from customer's card when i got info from shipping service that order status is "sent".

But when i started point 4 of my list i got confused because of subscription logic on stripe. As i got it from docs, subscription will create it's own payment intent, own invoice and will ask for own 3ds. So i'm rather confused, because it look like user will need to pass two 3ds for product and sub, will pay for both in two different payments and will get two invoices. What am i missing? Why subscription cannot be attach to "main" payment, be paid by it after 3ds passed and be activated after that? Why should i split them and make it more complex than one sigle payment/invoice?

how it is looking from code point of view (just mockup without any side operations):

  1. Create a user
const customer = await stripe.customers.create({
        email,
        address,
        name,
});
  1. create payment method
const { paymentMethod } = await stripe.createPaymentMethod({
        type: "card",
        card: cardElement,
        billing_details: {
          address: {
            city,
            country,
            line1: address1,
            line2: address2,
            postal_code: zip,
          },
          email,
          name,
        },
      });
  1. Attach payment method to customer
const paymentMethod = await stripe.paymentMethods.attach(paymentId, {
      customer,
    });
  1. create payment intent to hold money
const order = await stripe.paymentIntents.create({
      amount: sum * 100,
      currency: unit,
      description: "project name",
      customer,
      payment_method: paymentId,
      setup_future_usage: "off_session",
      confirm: true,
      capture_method: "manual", // to hold money
      receipt_email: email,
      return_url: returnUrl,   // to return after 3ds
    });
  1. Create subscriptions
const subs = await stripe.subscriptions.create({
      customer: customerId,
      items: subscriptions,
      expand: ["latest_invoice.payment_intent"], 
    });

if (subs.status === "incomplete" && subs.latest_invoice.payment_intent) {
        await stripe
          .confirmCardPayment(
            subs.latest_invoice.payment_intent.client_secret,
            {
              payment_method: {
                card: cardElement,
              },
            }
          )
      }

/* the only way i found to pass 3ds on sub without getting "incomplete" status
but it provide second 3ds for subs */
  1. redirect for 3ds
const action = order.next_action;
      if (action && action.type === "redirect_to_url") {
        window.location = action.redirect_to_url.url;
      }
  1. after got redirected after 3ds - capture money
    await stripe.paymentIntents.capture(paymentId);

So final result is - i have two payments (one - for product which im counting is total basket - subscriptions price, second - subscriptions), two 3ds for each, 1 invoice created by subscription, totally miss product invoice logic, coz i don't understand how to handle double pay for invoice and intent and it's seems like crutch.


Solution

  • You can add additional items when creating the Subscription, which is likely what you want to be doing here instead: https://stripe.com/docs/billing/invoices/subscription#first-invoice-extra