Implementing a crypto payment gateway using NodeJS

Kevin Rasmusson
6 min readDec 27, 2021


Considering the growth of the general interest in cryptocurrencies over the past few years, I wasn’t surprised when I one day decided to implement a crypto (specifically Bitcoin, Ether, and Litecoin — more on that later) paywall on one of my websites. Whether you believe it to be the “new big thing” or “just a fad”, learning how to work with and implement new technologies is always interesting (and necessary to stay relevant as a developer). The code is available at GitHub.

Coinbase Commerce API integration

In this article I will walk through the necessary steps to implement your crypto payment gateway, thus enabling you to take payments for your products/services in your preferred cryptocurrencies. This will be done using the Coinbase Commerce API — however, this article is in no way, shape, or form sponsored by them (I wish though, haha). Let’s move on.

Step 1: Create a Coinbase Commerce account

The first step is creating a Coinbase Commerce account, which can be done here. After you’ve created your account, head over to Settings > API Keys (scroll down), and generate an API key. We will need it for later integration. After you’re done it should look something like this:

Step 2: Initialize back-end

In this project, I will use NodeJS as the backend. Navigate to your programming folder and start your project using npm init:

mkdir crypto-checkoutcd crypto-checkoutnpm init

After you’ve initialized the project, create an index.js file — given that this is a rather small project, the majority of our code will go here.

Step 3: Installing the necessary packages

We will need a few packages to start. Install them using the following commands:

npm install expressnpm install coinbase-commerce-nodenpm install dotenvnpm install body-parser

Step 4: Set up environment variables

Before we jump into the index.js file, let’s set up our environment variables. Create a .env file in the root directory using the following command:

nano .env

You’ll enter an editor within your terminal window, where you should type the following:


Insert the API key you generated in step (1) and then exit the editor using ctrl+x and then press the ‘y’-key.

Step 5: Requiring the dependencies

Navigate over to your index.js file and open it using your IDE of choice. Firstly, require the packages we installed before:

// Importsconst express = require('express');const coinbase = require('coinbase-commerce-node');const dotenv = require('dotenv').config();const bodyParser = require('body-parser');

Before we continue: all this code can be found on GitHub. Moving along, let’s initialize the app instance to get the server running:

// App setupconst app = express();let server = app.listen(process.env.PORT, () => {    console.log('Listening on port ' + process.env.PORT);});app.use(bodyParser.json());

If you’ve used express before this is hopefully familiar. If not, what this code does is serve our application on localhost:3000 (the ‘process.env.PORT’ carries the value 3000, defined in step (4)).

Lastly, we need to initialize the Coinbase Commerce client:

// Coinbase setupconst Client = coinbase.Client;Client.init(process.env.API_KEY);const Charge = coinbase.resources.Charge;

Like the port number, process.env.API_KEY carries the API key entered in step (4). I will get into the specifics of what the Client and Charge will be used for in later steps.

Step 6: Create charge route

We need to implement an endpoint, a URL, where we can create a Charge, a thing sort of equivalent to a “payment request.” Using the npm package making a charge request is quite easy:'/charge', (req, res) => {    let chargeData = {        name:,        description: req.body.decription,        local_price: {            amount: req.body.amount,            currency: 'USD'        },        pricing_type: 'fixed_price'    }    Charge.create(chargeData, (err, response) => {        if (err) {            res.status(400).send({message: err.message});        } else {            res.status(200).send(response);        }    });});

Note: This endpoint is just to show what is possible, and not meant for in production use (e.g., you should validate incoming data).

Firstly, the chargeData variable. This object contains fields that you should retrieve from the user on the front-end. The name, description, and amount should be changed.

After that, we create the charge using the chargeData variable, and the response from this is where the interesting data lie:

  • response[‘id’], the id of the charge, essential to retrieve info about payments later on. Should be saved in a database.
  • response[‘hosted_url’], a link to a payment gateway hosted by Coinbase that looks like this:

I recommend printing the response, using


to see more information you can print to the user. You’ll find the relevant BTC, ETH, BCH, LTC addresses if they want to make a manual payment, and more.

Now, if you create a POST request at localhost:3000/charge, you will create a charge.

Step 7: Check the status of the payment

Having created the charge, we now need to check if the user has paid. This can be done by using the id of the charge (as referred to in step (6)). This code is an example of the checks you can do:'/status', (req, res) => {  let id =  Charge.retrieve(id, (err, charge) => {    if(charge['timeline'][0]['status'] == 'NEW') {      try {        if (charge['timeline'][1]['status'] == 'PEDNING' &&     charge['timeline'].length == 2) {          return res.status(200).send({message: 'Payment pending, awaiting confirmations.'});        } else if (charge['timeline'][1]['status'] == 'EXPIRED') {          return res.status(400).send({message: 'Payment expired'});        } else if(charge['timeline'][2]['status'] == 'COMPLETED') {          return res.status(200).send({message: 'Payment completed.'});        }      } catch(err) {        return res.status(200).send({message: 'No payment detected'});        }    } else {      return res.status(400).send({message: 'Charge not found.'});    }  });});

A lot going on here. First of all, the id is all that’s needed to check the status.

The charge[‘timeline’] object is simply an array that gets an item pushed as something happens with the transaction.

The charge[‘timeline’] will always have a 0 element, with the status “NEW”. However, the 1st and 2nd elements don’t always exist, thus we wrap our if statement in a try-catch block.

The first element is either pending or expired. If pending, check for a 2nd element — the payment might already have been confirmed.

Step 8: Testing our routes using Postman

First of all, we’ll test creating a charge:

POST request @ localhost:3000/charge using Postman

Our request received a 200 OK status code, meaning the charge was created. As we can see in the body we received, we have the hosted_url and the important id property.

Let’s test the /status endpoint:

POST request @ localhost:3000/status using Postman

The body we sent was the id found in the response to the previous request. Since no payment has been made, we received the expected message “No payment detected.”

It all seems to be working!

Final thoughts

A few thoughts before I let you get along with working on your website.

(1) This is a “bare bone” implementation. For production, add validation, csrf protection, configure headers, test sending money to your own Coinbase commerce account, etc.

(2) To avoid making this an extremely long post, I have skipped the front-end part altogether. You’ll need to implement a form on the front-end, taking the name, description, and dollar amount as parameters.

I hope it helped! If you feel it did, consider following me on Medium — they no longer pay creators with < 100 followers :(



Kevin Rasmusson

22 year old web developer. Write tutorial articles on tech (mainly for myself to refer back to), general life reflections & entrepreneurship.