DEV Community 👩‍💻👨‍💻

Cover image for Payment Handling with Stripe, Node and Express
Abhee Hudani ☕
Abhee Hudani ☕

Posted on • Updated on

Payment Handling with Stripe, Node and Express

Why do you need a payment gateway ?

  • Let's just assume that we are creating an e-commerce shopping website. Payment handling is one of the crucial part in e-commerce site.
  • There are many other payment gateways are available like PayPal, Amazon Pay, World Pay, Stripe etc. But in this tutorial we are going to stick with Stripe.
  • And using a payment gateway is not just for transferring money, but it has other benefits as well.
  • A payment gateway focuses on creating a secure pathway between a customer and the merchant to facilitate payments securely.

In this tutorial we are going to create payment gateway service for Node JS using STRIPE.

Github Repo Link and Postman Collection: Click Here

What are we building here:

1) Saving Customer's Details to Stripe
2) Add customer's Card in Stripe.
3) View Customer's Cards.
4) Update Customer's Cards.
5) Delete Customer's Card from stripe
6) Payment Checkout with Saved Card.
7) One Time Payment Checkout without saving card info.

How we are gonna save customer sensitive card details?

Instead of saving card details directly by passing them to API, we are going to create a charge first and after that we are going to save the charge details to customer. This we are not going to handle card information directly and our production integration is developed in a PCI Compliant manner. Which is also recommended by the Stripe.

What do we need ?

  • Stripe API Key
  • Node version >= 12 installed in your system
  • Postman/ Insomnia (Or any other software for testing API Calls)

Getting the stripe API Key:

  • You can get your own stripe API key by logging into Stripe Dashboard and get the test key. It will look something like this 'sk_live_...3Vls'.
  • In this tutorial we are gonna use the default sandbox key from here which is provided by stripe.

Note:

If you are using your own API key, then make sure to turn on "TEST MODE" from your Stripe Dashboard otherwise you might face some unexpected charges during the test and you will be able to see the test transactions in your dashboard.

Getting Started:

  • Stripe Charges the amount in cents so if you want to charge $200 then your amount would be 20000 ($200 x 100 = 20000).
  • We are not using any Database here so I'm just gonna store customer ID as a Constant during this tutorial. So if you use this make sure to connect to Database if you want to store customer's stripe data.

Install the dependencies.

$ npm init
Enter fullscreen mode Exit fullscreen mode

Install Express, Stripe-Node packages

$ npm install express 
$ npm install --save stripe
Enter fullscreen mode Exit fullscreen mode

Index.js

Ok now we get to create our first file called index.js . Once created, at the top of the file we want to include all of the dependencies that we will need for our project:

const express = require("express");
const app = express();
const port = 3000;

app.use(express.json());
Enter fullscreen mode Exit fullscreen mode

When we want to test and make sure our server is working, we will run this function to listen on port 3000 and log a string if its successful:

app.get("/", (req, res) => {
  res.send("Hello World!");
});

app.listen(port, () => {
  console.log(`App listening at http://localhost:${port}`);
});
Enter fullscreen mode Exit fullscreen mode

Now if you open your browser and type: http://localhost:3000/ you will see ' Hello World!' in your browser.

At this point our index.js file should look like this:

const express = require("express");
const app = express();
const port = 3000;

app.use(express.json());

app.get("/", (req, res) => {
    res.send("Hello World!");
});

app.listen(port, () => {
    console.log(`App listening at http://localhost:${port}`);
});

Enter fullscreen mode Exit fullscreen mode

Stripe.js

Let's create a new file called strie.js which will be used for handling all stripe calls. Once created we are going to create a simple GET request and exporting our route file so index.js can access it:

const express = require("express");
const router = express.Router();

router.get("/", (req, res) => {
  res.status(200).json({
    message: "Stripe Hello World!",
  });
});

module.exports = router;
Enter fullscreen mode Exit fullscreen mode

Adding stripe.js route to index.js

const stripe = require("./stripe");
app.use("/api/", stripe);
Enter fullscreen mode Exit fullscreen mode

Now the index.js will something similar to this:

const express = require("express");
const app = express();
const stripe = require("./stripe");
const port = 3000;

app.use(express.json());

app.use("/api/", stripe);

app.get("/", (req, res) => {
  res.send("Hello World!");
});

app.listen(port, () => {
  console.log(`App listening at http://localhost:${port}`);
});
Enter fullscreen mode Exit fullscreen mode

Now if you open your browser and type: http://localhost:3000/api/ you will see ' Stripe Hello World!' in your browser.


Creating a new Stripe Customer


In Stripe.js file let's add the stripe key as a constant and adding stripe module:

const Stripe_Key = 'sk_test_....jQb';
const stripe = require("stripe")(Stripe_Key);
Enter fullscreen mode Exit fullscreen mode

and let's create an api which will create a new customer id from user's email address
Creating a new Stripe Customer Code Snippet

Note
If you are using your own API key then you can add your organization account Secret Key. So it will link the customer with your organization.

You can add some extra fileds during creating the new customer using stripe API which you can found here on Stripe Documents
Currently we are not using any Database so we have to note down the customer id somewhere from our response message.

Response :

New Customer Response

  • Let's Note Down Our Customer ID here: 'cus_IDxxDETTAorXTO'

Adding a card to the existing customer


For testing purposes we don't need to use real card data. Stripe has given some list of the card for the testing purposes and we are going to use it in this. You can find it here Stripe Test Cards

Card Number, Expiry Month, Year, CVC these are the required parameters for adding a new card. So if those parameters are not passed in the body then we are going throw a bad request error as the response.
Add Card Code Snippet

Response :

Add Card Response

View Customer's All Cards


  • To get the list of customer's saved cards we need to just pass the customerID which was generated by the stripe earlier.
  • In this case we only need cardId, Card Type, Expiry Details and Last 4 digit of the saved card. But if you need more data of the saved card you can find it here on Stripe Docs for View saved Cards

View Customer's All Cards Code Snippet

Response :

View Cards Response

Update Card Details


  • From customer's saved card details we will get the cardID and from cardID we can update the saved card details like card holder name, expiry month, expiry year, address details etc. All the details except cardId is optional for update operation.
  • If you need to update more fields then you can find it here on Stripe Docs for Updating Card Details

Update Card Details Code Snippet

Response :

Update Card Details Response


Delete Saved Card


To delete a saved card we need to pass the ID of the savedCard
Delete Saved Card Code Snippet

Response :

Delete Saved Card Response


Creating A Payment Charge


  • We have two choices over here -- Customer can pay from existing card. -- Customer can pay without saving the new card or without existing cards.
  • So we are going to use 'oneTime' parameter from request.body as a toggle switch between them.
One Time Payment with new Card

const { amount, email } = req.body;
    const {
      cardNumber,
      cardExpMonth,
      cardExpYear,
      cardCVC,
      country,
      postalCode,
    } = req.body;

    if (!cardNumber || !cardExpMonth || !cardExpYear || !cardCVC) {
      return res.status(400).send({
        Error: "Necessary Card Details are required for One Time Payment",
      });
    }
    try {
      const cardToken = await stripe.tokens.create({
        card: {
          number: cardNumber,
          exp_month: cardExpMonth,
          exp_year: cardExpYear,
          cvc: cardCVC,
          address_state: country,
          address_zip: postalCode,
        },
      });

      const charge = await stripe.charges.create({
        amount: amount,
        currency: "usd",
        source: cardToken.id,
        receipt_email: email,
        description: `Stripe Charge Of Amount ${amount} for One Time Payment`,
      });

      if (charge.status === "succeeded") {
        return res.status(200).send({ Success: charge });
      } else {
        return res
          .status(400)
          .send({ Error: "Please try again later for One Time Payment" });
      }
    } catch (error) {
      return res.status(400).send({
        Error: error.raw.message,
      });
    }

Enter fullscreen mode Exit fullscreen mode
Payment with an existing Card

const { amount, cardId,  email } = req.body;

try {
      const createCharge = await stripe.charges.create({
        amount: amount,
        currency: "usd",
        receipt_email: email,
        customer: customerId,
        card: cardId,
        description: `Stripe Charge Of Amount ${amount} for Payment`,
      });
      if (createCharge.status === "succeeded") {
        return res.status(200).send({ Success: createCharge });
      } else {
        return res
          .status(400)
          .send({ Error: "Please try again later for payment" });
      }
    } catch (error) {
      return res.status(400).send({
        Error: error.raw.message,
      });
    }
Enter fullscreen mode Exit fullscreen mode

Final Charge Payment Code


Final Charge Payment Code Snippet

One Time Payment Response :

One Time Payment Response

Saved Card Payment Response :

One Time Payment Response


Final Stripe.JS


Now the stripe.js will something similar to this:

const express = require("express");
const router = express.Router();
const Stripe_Key =
  "sk_test_....Qb";
const stripe = require("stripe")(Stripe_Key);
const customerId = "cus_IDxx....orXTO";

router.get("/", (req, res) => {
  res.status(200).json({
    message: "Stripe Hello World!",
  });
});

// Create a new customer for stripe
router.post("/newCustomer", async (req, res) => {
  console.log("\n\n Body Passed:", req.body);
  try {
    const customer = await stripe.customers.create(
      {
        email: req.body.email,
      }
      // {
      //   // If you are using your own api then you can add your organization account here. So it will link the customer with your organization
      //   stripeAccount: process.env.StripeAccountId,
      //}
    );
    return res.status(200).send({
      //   customerDetails: customer,
      customerId: customer.id,
      customerEmail: customer.email,
    });
  } catch (error) {
    return res.status(400).send({ Error: error.raw.message });
  }
});

// Add a new card of the customer
router.post("/addNewCard", async (req, res) => {
  console.log("\n\n Body Passed:", req.body);
  const {
    cardNumber,
    cardExpMonth,
    cardExpYear,
    cardCVC,
    cardName,
    country,
    postal_code,
  } = req.body;

  if (!cardNumber || !cardExpMonth || !cardExpYear || !cardCVC) {
    return res.status(400).send({
      Error: "Please Provide All Necessary Details to save the card",
    });
  }
  try {
    const cardToken = await stripe.tokens.create({
      card: {
        name: cardName,
        number: cardNumber,
        exp_month: cardExpMonth,
        exp_year: cardExpYear,
        cvc: cardCVC,
        address_country: country,
        address_zip: postal_code,
      },
      // customer: customer.stripe_id,
      // stripe_account: StripeAccountId,
    });

    const card = await stripe.customers.createSource(customerId, {
      source: `${cardToken.id}`,
    });

    return res.status(200).send({
      card: card.id,
    });
  } catch (error) {
    return res.status(400).send({
      Error: error.raw.message,
    });
  }
});

// Get List of all saved card of the customers
router.get("/viewAllCards", async (req, res) => {
  let cards = [];
  try {
    const savedCards = await stripe.customers.listSources(customerId, {
      object: "card",
    });
    const cardDetails = Object.values(savedCards.data);

    cardDetails.forEach((cardData) => {
      let obj = {
        cardId: cardData.id,
        cardType: cardData.brand,
        cardExpDetails: `${cardData.exp_month}/${cardData.exp_year}`,
        cardLast4: cardData.last4,
      };
      cards.push(obj);
    });
    return res.status(200).send({
      cardDetails: cards,
    });
  } catch (error) {
    return res.status(400).send({
      Error: error.raw.message,
    });
  }
});

// Update saved card details of the customer
router.post("/updateCardDetails", async (req, res) => {
  const { cardName, cardExpMonth, cardExpYear, cardId } = req.body;

  if (!cardId) {
    return res.status(400).send({
      Error: "CardID is Required to update",
    });
  }
  try {
    const card = await stripe.customers.updateSource(customerId, cardId, {
      name: cardName,
      exp_month: cardExpMonth,
      exp_year: cardExpYear,
    });
    return res.status(200).send({
      updatedCard: card,
    });
  } catch (error) {
    return res.status(400).send({
      Error: error.raw.message,
    });
  }
});

// Delete a saved card of the customer
router.post("/deleteCard", async (req, res) => {
  console.log("\n\n Body Passed:", req.body);
  const { cardId } = req.body;
  if (!cardId) {
    return res.status(400).send({
      Error: "CardId is required to delete Card",
    });
  }
  try {
    const deleteCard = await stripe.customers.deleteSource(customerId, cardId);
    return res.status(200).send(deleteCard);
  } catch (error) {
    return res.status(400).send({
      Error: error.raw.message,
    });
  }
});

// Create a payment charge
router.post("/createCharge", async (req, res) => {
  const { amount, cardId, oneTime, email } = req.body;
  if (oneTime) {
    const {
      cardNumber,
      cardExpMonth,
      cardExpYear,
      cardCVC,
      country,
      postalCode,
    } = req.body;

    if (!cardNumber || !cardExpMonth || !cardExpYear || !cardCVC) {
      return res.status(400).send({
        Error: "Necessary Card Details are required for One Time Payment",
      });
    }
    try {
      const cardToken = await stripe.tokens.create({
        card: {
          number: cardNumber,
          exp_month: cardExpMonth,
          exp_year: cardExpYear,
          cvc: cardCVC,
          address_state: country,
          address_zip: postalCode,
        },
      });

      const charge = await stripe.charges.create({
        amount: amount,
        currency: "usd",
        source: cardToken.id,
        receipt_email: email,
        description: `Stripe Charge Of Amount ${amount} for One Time Payment`,
      });

      if (charge.status === "succeeded") {
        return res.status(200).send({ Success: charge });
      } else {
        return res
          .status(400)
          .send({ Error: "Please try again later for One Time Payment" });
      }
    } catch (error) {
      return res.status(400).send({
        Error: error.raw.message,
      });
    }
  } else {
    try {
      const createCharge = await stripe.charges.create({
        amount: amount,
        currency: "usd",
        receipt_email: email,
        customer: customerId,
        card: cardId,
        description: `Stripe Charge Of Amount ${amount} for Payment`,
      });
      if (createCharge.status === "succeeded") {
        return res.status(200).send({ Success: charge });
      } else {
        return res
          .status(400)
          .send({ Error: "Please try again later for payment" });
      }
    } catch (error) {
      return res.status(400).send({
        Error: error.raw.message,
      });
    }
  }
});

module.exports = router;
Enter fullscreen mode Exit fullscreen mode

Conclusion

This is the totally working code to handle payment and its related data. All you have to do is replace the sandbox key with your Strip API key and it will work perfectly. Let me know if it is not working or anything I would really appreciate your feedbacks.

Top comments (5)

Collapse
 
tdaypreneur profile image
Ganiyu Omotayo Seedik

I really appreciate this, this is one of the best article about stripe api.

Thanks for putting this together

Collapse
 
gautham495 profile image
Gautham Vijayan

Phenomenal article about the backend Integration of Stripe. It would be nice if you gave a react frontend integration for this article

Collapse
 
hudaniabhee profile image
Abhee Hudani ☕

Thank you Gautham ! And yes, I'm planning to write an article about integration with react and going to publish it within a week or two. I'm happy that you liked this article.

Collapse
 
zykx profile image
Karan Pandya

Thank You very much my friend.. This helped me a lot.
As you're from my native, allow me to buy a you a coffee as a sweet gesture for this help...!

Collapse
 
hudaniabhee profile image
Abhee Hudani ☕

Thank you Karan, I'm really glad that you liked it and it has helped you !

Here is a post you might want to check out:

Regex for lazy developers

regex for lazy devs

Sorry for the callout 😆