Introduction to Stripe Payments in Node.js Using Express

Published on July 21, 2019

Joshua Hall

Introduction to Stripe Payments in Node.js Using Express

While we believe that this content benefits our community, we have not yet thoroughly reviewed it. If you have any suggestions for improvements, please let us know by clicking the “report an issue“ button at the bottom of the tutorial.

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.

Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.

Learn more about our products

About the authors
Default avatar
Joshua Hall


Still looking for an answer?

Ask a questionSearch for more help

Was this helpful?

This textbox defaults to using Markdown to format your answer.

You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!

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 ?

Try DigitalOcean for free

Click below to sign up and get $200 of credit to try our products over 60 days!

Sign up

Join the Tech Talk
Success! Thank you! Please check your email for further details.

Please complete your information!

Featured on Community

Get our biweekly newsletter

Sign up for Infrastructure as a Newsletter.

Hollie's Hub for Good

Working on improving health and education, reducing inequality, and spurring economic growth? We'd like to help.

Become a contributor

Get paid to write technical tutorials and select a tech-focused charity to receive a matching donation.

Welcome to the developer cloud

DigitalOcean makes it simple to launch in the cloud and scale up as you grow — whether you're running one virtual machine or ten thousand.

Learn more
DigitalOcean Cloud Control Panel