// Tutorial //

Introduction to Stripe Payments in Node.js Using Express

Published on July 21, 2019
Default avatar
By Joshua Hall
Developer and author at DigitalOcean.
Introduction to Stripe Payments in Node.js Using Express

In this article we’ll be looking into using Stripe’s Charges API to make a basic donation app where we can create customers and submit payments.


Understanding basic promises and how to set up an Express server are necessary.


We’re going to need quite a few things to get this set up. Body-parser will allow us to convert our form data into something more useful, ejs will let us render our success page from our server, express for the server itself, nodemon to reload our server on save, and finally stripe to give us access to all the more interesting functionality we want with the Stripe API.

$ npm i body-parser ejs express nodemon stripe

Now over on the Stripe website you’ll need to create an account. When on the Dashboard, the API Keys under Developers, you’ll find both a Publishable key and a Secret Key. Those are what we’ll need to have access the data later. You’ll want to use the test keys at first.

File and Directory Setup

Here’s how the files for our simple project will be organized:

* views 📂  - Handles our front-end code, must be named views for `ejs` to work.
  * card.js 
  * completed.html
  * index.html
* server.js

Server Setup

Just a basic Express server, we’ll require everything we installed, add our Stripe Secret Key when requiring stripe, and we’ll use express.static to render our views folder.

const express = require('express');
const bodyParser = require('body-parser');
const path = require('path');
const stripe = require('stripe')('SECRET_KEY'); // Add your Secret Key Here

const app = express();

// This will make our form data much more useful
app.use(bodyParser.urlencoded({ extended: true }));

// This will set express to render our views folder, then to render the files as normal html
app.set('view engine', 'ejs');
app.engine('html', require('ejs').renderFile);

app.use(express.static(path.join(__dirname, './views')));

// Future Code Goes Here

const port = process.env.PORT || 3000;
app.listen(port, () => console.log('Server is running...'));

When moving any app to production never put your API keys directly into the code. With whatever hosting service your using, remember to setup environment variables for secure information passing instead. You’ll likely want to setup environment variables right away, so that your secret keys don’t get committed to a repo who’s code could be exposed accidentally.

UI Setup

For our UI and front-end, I’ll be using Tailwind CSS and we’ll also need to get the front-end script from Stripe just before our own front-end code.

At the very bottom of our form we need two empty div’s with the id’s of card-element and card-error for Stripe to display its card inputs and error messages.

<!DOCTYPE html>
<html lang="en">

  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <link href="https://unpkg.com/tailwindcss@^1.0/dist/tailwind.min.css" rel="stylesheet">
  <title>Donation App</title>

<body class="bg-gray-900">
  <nav class="bg-purple-900 h-20 flex justify-center">
    <h1 class="text-white text-5xl">Donation App</h1>

  <div class="flex justify-center mt-32">
    <form action="/charge" method="POST" class="flex flex-col w-1/3">
      <input class="bg-transparent text-white p-2 h-10 mb-4" type="text" name="name" placeholder="Name">
      <input type="email" class="bg-transparent text-white p-2 h-10 mb-4" name="email" placeholder="Email">
      <input class="bg-transparent text-white p-2 h-10 mb-4" type="text" name="amount" placeholder="Amount">

      <div id="card-element" class="bg-transparent text-white p-2 h-10 mb-4"></div>
      <div id="card-errors" role="alert"></div>
      <button class="text-white bg-purple-900 p-4 rounded">Submit Payment</button>

<script src="https://js.stripe.com/v3/"></script>
<script src="card.js"></script>


Front-End Code

First, we need to set up our card inputs and validation. Luckily for us, Stripe will help us with that too. This is going to look a bit complicated so let’s break it down a bit.

  • We need to create our card inputs using Stripe’s elements function, adding some styles to match the rest of the app, and adding it to our card-element id so it’ll be rendered on our front-end.
  • Create a function that will add an invisible input to our form with the value of our argument just before the form is submitted.
  • Add an on submit event listener to our form to create a new Stripe token, which will encrypt our user’s card data, and pass it to our function so it’s submitted in our hidden input.

We’re just creating our inputs, securing the data passed into it, and adding that encrypted data to our form before submitting it to our server.

const stripe = Stripe('PUBLISHABLE_KEY'); // Your Publishable Key
const elements = stripe.elements();

// Create our card inputs
var style = {
  base: {
    color: "#fff"

const card = elements.create('card', { style });

const form = document.querySelector('form');
const errorEl = document.querySelector('#card-errors');

// Give our token to our form
const stripeTokenHandler = token => {
  const hiddenInput = document.createElement('input');
  hiddenInput.setAttribute('type', 'hidden');
  hiddenInput.setAttribute('name', 'stripeToken');
  hiddenInput.setAttribute('value', token.id);


// Create token from card data
form.addEventListener('submit', e => {

  stripe.createToken(card).then(res => {
    if (res.error) errorEl.textContent = res.error.message;
    else stripeTokenHandler(res.token);

Basic Charges

Now over on our server side we’re going to create a new POST API endpoint for /charge. We’re just making a promise that creates a customer with our basic information (you can play with all the options here), most importantly, the token we created from the card input.

With our new customer, we’ll create a new charge to their card, passing in the amount in cents. If everything went well, we’ll render our completed.html page.

app.post("/charge", (req, res) => {
  try {
        name: req.body.name,
        email: req.body.email,
        source: req.body.stripeToken
      .then(customer =>
          amount: req.body.amount * 100,
          currency: "usd",
          customer: customer.id
      .then(() => res.render("completed.html"))
      .catch(err => console.log(err));
  } catch (err) {

Completed Page

When we have successfully sent the donation we can render another page with a thank you message and a button back to our homepage:

<!DOCTYPE html>
<html lang="en">

  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <link href="https://unpkg.com/tailwindcss@^1.0/dist/tailwind.min.css" rel="stylesheet">
  <title>Donation Completed</title>

<body class="bg-gray-900">
  <nav class="bg-purple-900 h-20 flex justify-center">
    <h1 class="text-white text-5xl">Donation App</h1>
  <div class="flex flex-col items-center mt-32 text-white text-2xl">
    <p>Thank you for your generous donation.</p>
    <p>Your payment has been received.</p>
    <a href="/"><button class="bg-blue-700 rounded p-4 mt-3">Return</button></a>


Now on localhost:3000 try it out, Stripe gives us a list of test card numbers. Over on our Dashboard we can see the details of any test payments.


Obviously this post only scratched the surface and more complex use cases will require that you dig into the excellent Stripe documentation. A more robust payment system would also take care of things like error handling and avoiding duplicate charges if a user accidentally tries to process the charge twice in a row. However, as you saw, Stripe makes it really easy to create payment pages without ever having to deal with sensitive data like credits card numbers. They take care of handling the sensitive data for us.

Hopefully this short intro was helpful in easing your dive into how processing payments online using Stripe and Node.js works. If you had any problems implementing the code here, feel free to check out this Github repo.

Want to learn more? Join the DigitalOcean Community!

Join our DigitalOcean community of over a million developers for free! Get help and share knowledge in our Questions & Answers section, find tutorials and tools that will help you grow as a developer and scale your project or business, and subscribe to topics of interest.

Sign up
About the authors
Default avatar
Developer and author at DigitalOcean.

Still looking for an answer?

Was this helpful?

I have tried to access stripe api usign digitalOcean Kubernetes that run container with nodejs, similar to this tutorial’s node-express server.

When I used test-mode API keys, It works and paymant was successful in test-mode. But it does not work when I used live-mode API keys.

Nginx reverse proxy shows 502 bad gateway error, when I access stripe API using stripe node module. How can I solve this issue? Is there any live-mode specific issue?

Refused to load the script ‘https://js.stripe.com/v3/’ because it violates the following Content Security Policy directive: “script-src ‘self’ http://gc.kis.v2.scr.kaspersky-labs.com ws://gc.kis.v2.scr.kaspersky-labs.com”. Note that ‘script-src-elem’ was not explicitly set, so ‘script-src’ is used as a fallback.

Can you help me with this error ?