Stripe migrating from token based to payment intent based charges

Viewed 489

Previously I was using stripe with another third party service called Zuora. I was generating a token like so on the front end

const tokenOptions = {
  name: form.fullName,
  address_country: form.country,
  address_zip: form.zip
};

const cardElement = stripeElements.getElement(CardElement);

const { error, token } = await stripe.createToken(
  cardElement,
  tokenOptions
);

// use `token.id` on the BE to make the charge

And then passing token.id to the back end which hit Zuora's endpoint that actually made the charge using this token id.

But apparently the token approach doesn't support 3D Secure which we want to implement. The other approach of using Payment Intents does support SD secure however. So I replaced the above code with:

const getClientSecret = await fetch('http://localhost:3000/api/create-payment-intent', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json'
  }
});

const { client_secret } = await getClientSecret.json();

const paymentIntent = await stripe.confirmCardPayment(client_secret, {
  payment_method: {
    card: cardElement,
    billing_details: {
      name: 'Foo bar',
      email: 'foo@bar.com'
    }
  }
});

My create-payment-intent endpoint on the backend looks like this:

router.post("/api/create-payment-intent", async (req, res, next) => {
  try {
    const paymentIntent = await stripe.paymentIntents.create({
      amount: 100,
      currency: "usd",
      capture_method: "manual",
    });
    res.status(200).json({
      client_secret: paymentIntent.client_secret,
    });
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
});

Now this call:

const paymentIntent = await stripe.confirmCardPayment(client_secret, {
  payment_method: {
    card: cardElement,
    billing_details: {
      name: 'Foo bar',
      email: 'foo@bar.com'
    }
  }
});

returns an object like this:

paymentIntent: {
  amount: 100,
  canceled_at: null,
  cancellation_reason: null,
  capture_method: "manual",
  client_secret: "some-secret-id-here",
  confirmation_method: "automatic",
  created: 1626287910,
  currency: "usd",
  description: null,
  id: "pi_1JDCyYCmZFHcp9eoF2MoVeyg",
  last_payment_error: null,
  livemode: false,
  next_action: null,
  object: "payment_intent",
  payment_method: "pm_1JDCyYCmZFHcp9eoZHAfkoCX",
  payment_method_types: ["card"],
  receipt_email: null,
  setup_future_usage: null,
  shipping: null,
  source: null,
  status: "requires_capture"
}

My question is can I use one of the values inside the return object in place of the token id that I was using before to make the charge at a later time from Zuora?

Can I use paymentIntent.id for this?

1 Answers

Just to clarify, your code is currently creating a payment. When you call confirmCardPayment(), your code is creating a Charge under the hood.

Specifically, it has "authorized" a charge but not yet captured it, as can be seen by the status: "requires_capture" status of the PaymentIntent in your response.

If you're looking to send something to Zoura for a later charge, what you're looking for is the modern equivalent to a Token i.e. a PaymentMethod object.

In your response, the PaymentMethod in question is the payment_method: "pm_1JDCyYCmZFHcp9eoZHAfkoCX", field from the response in confirmCardPayment.

That PaymentMethod represents a card (similar to what the legacy Token did) and that is what you'd need to create a charge later.

Related