Skip to main content
Chat with me

Saving Cards to Charge Customer Later Using Stripe


Stripe is a secure and widely-used payment processing platform that offers a convenient way to handle card information without the need to store sensitive data directly on your servers. In this guide, we'll walk you through the process of saving card information for future use and charging customers later using ExpressJS.


To charge a customer at a later time, we need to create a customer profile with a payment method and store the customer and payment method IDs securely in a database. Later, we can use these IDs to create a payment intent, allowing us to charge the customer without the need for their direct involvement. Here are the key steps involved in this process:

  1. Create a checkout session with an empty amount, which will automatically convert to a session for saving card details if no amount is defined. This API should return a Stripe web URL where the user will be redirected.

  2. Handle the successful callback redirect, during which we will store the customer and payment method IDs in a database.

  3. Handle the failure callback redirect in case something goes wrong during the process.


Before you begin, ensure you have the following prerequisites in place:

  • NodeJS version 16 or higher installed on your machine.
  • The "stripe": "^13.10.0" NPM package.
  • Stripe API version 2023-08-16.

Step 1: Initialize the Stripe Session

First, you'll need to initialize a Stripe session. It's recommended to store the Stripe object in a separate file. Here's how you can do it:

// utils/stripe.js
import Stripe from 'stripe';
import { STRIPE_SECRET_KEY } from process.env;

// Initialization
const stripe = new Stripe(STRIPE_SECRET_KEY, {
  apiVersion: '2023-08-16',

export default stripe;

Now, let's create the first API to create a checkout session and redirect the user to the Stripe page:

import stripe from '@utils/stripe';

export const checkout = async (req, res) => {
  try {
    const session = await stripe.checkout.sessions.create({
      payment_method_types: ['card'],
      mode: 'setup',
      customer_creation: 'always',
      success_url: `http://localhost:5000/api/v1/payments/save/?sessionID={CHECKOUT_SESSION_ID}`, // sessionID will be provided by Stripe once the user redirects to this URL
      cancel_url: `http://localhost:5000/api/v1/payments/failed`,

    if (session.status !== 'open') {
      return res
        .json({ success: false, message: 'Failed to create a checkout session' });

    return res.redirect(session.url);
  } catch (err) {
    // Error handling
    console.log('Error ----> ', err);
    return res.status(500).json({ success: false, message: 'Internal server error' });

Step 1 is complete. Users will be redirected to the Stripe page to enter their card information.

Step 2: Handle Successful Callback

Now, let's handle the scenario where the user redirects back to the success callback URL:

export const save = async (req, res) => {
  try {
    const { sessionID } = req.query; // sessionID will be provided by Stripe

    // Retrieve the updated checkout session
    const session = await stripe.checkout.sessions.retrieve(sessionID);

    // Check if the session status is 'complete,' indicating that the user added the card successfully
    if (session.status !== 'complete') {
      return res.status(400).send('Could not find the checkout session');

    // Stripe automatically creates a setup intent with status "succeeded" after the user adds the card. We'll need to find that to get the customer and payment method ID.
    const setupIntent = await stripe.setupIntents.retrieve(session.setup_intent);

    if (
      setupIntent.status !== 'succeeded' ||
      !setupIntent.customer ||
    ) {
      return res.status(400).send('Could not find the setup intent');

    const { customer, payment_method } = setupIntent;

    // TODO: Save customer and payment_method in a database

    return res.send('Card saved successfully');
  } catch (err) {
    // Error handling
    console.log('Error ----> ', err);
    return res.status(500).send('Server error');

Step 2 is complete. Now, you have the customer and payment_method IDs stored in the database.

Step 3: Handle Failure Callback

In case the user fails to save the card or if something goes wrong, Stripe will redirect the user back to the failure callback URL. We need to handle this scenario:

export const failed = (_req: Request, res: Response): Response => {
   return res.status(400).send('Failed to save the card information.');

With these three steps, you're all set on the user side, and you have the customer and payment_method IDs required to charge the customer later.

Charging a Customer

To charge a customer at a later time, you can use the following code in various scenarios, such as a cron job or an API call:

const paymentIntent = await stripe.paymentIntents.create({ 
  amount: 20 * 100, // Convert to cents
  currency: 'usd',
  confirm: true,
  automatic_payment_methods: { enabled: false },
  payment_method_types: ['card'],
  customer: user.customerID, // The customer ID we stored
  payment_method: user.payment_method, // The payment method we stored

if (paymentIntent.status !== 'succeeded') {
  // Handle the case where the payment does not succeed

Congratulations! You've successfully stored the customer's card information and have the ability to charge the customer as needed in the future.