How to Accept Crypto Payments in a Next.js Application using Coinbase Commerce
Tags: nextjs, react, web3, node
Published: 2022-09-20 09:59:43



The adaptation of cryptocurrencies is increasing fast these digital assets can be used to settle debts, and several businesses now accept cryptocurrencies as a means of payment. The most significant advantage of cryptocurrencies is that they are decentralized, making payments easy as people can instantly transfer digital assets anywhere in the world without restrictions.

Given that cryptocurrencies are gaining popularity, you may want to accept cryptocurrencies in your web application, one way to do that is by using coinbase commerce. Coinbase commerce is a platform that allows merchants to accept digital payments through cryptocurrencies, and they support many cryptocurrencies, you can look at their list of supported cryptocurrencies here.

Next.js is a popular Frontend framework built on React.js, it supports serverless functions this will be very vital in this article, these serverless functions allow us to write backend codes which essentially eliminate the need to have two separate codebases for the frontend and backend.

## Prerequisite

You need the following to successfully follow this article.

- [Coinbase Commerce account](https://commerce.coinbase.com/)
- [Nodejs installed on your computer](https://nodejs.org/en/download/)
- [Ngrok installed on your computer](https://ngrok.com/download)
- Basic Knowledge of Next.js or React

## Project Set up

First, let’s create a new Next.js application by running the command below on the terminal, Follow the instructions.

```bash
npx create-next-app@latest
```

Next, navigate to the project directory and install the following dependencies by running the command below. 

```bash
npm install coinbase-commerce-node axios
```

`axios` is an HTTP client and `coinbase-commerce-node` is the coin base SDK for Nodejs.

Once these packages have been installed, open the project directory with your text editor, and run the following command in your terminal to start the app in development mode.

```bash
npm run dev
```

The application will be served at `[localhost:3000](http://localhost:3000)` if port 3000 is free, else another port will be chosen.

## Building the Application

In this article, we will be building a simple web-based e-commerce shop that accepts cryptocurrencies using Next.js, we will use coinbase commerce to create payment links, and we will create a webhook URL to verify transactions, and test this webhook locally using a tool called [Ngrok](https://ngrok.com/), the data source for the application will be a hard-coded array of shop items,  now open the project directory in your text editor, and let’s write some codes.

You can get the source code of this project [here](https://github.com/Joshuajee/nextjs-crypto), feel free to clone the repo.

### The data source

In a real-world application, the data would likely come from a database or a server, but in this article, it will be hard-coded. At the root directory of your project create a file name data.js this is where all the application data will come from, copy and paste the code snippet below in that file.

```jsx
export const products = [
    {
        id: 1,
        name: "Small Black Chair",
        price: 10,
        currency: "USD",
        description: "Soft Chair, Best value for Money"
    },
    {
        id: 2,
        name: "Spoon",
        price: 2,
        currency: "USDT",
        description: "Spoon gets the best value for Money"
    },
    {
        id: 3,
        name: "Small Table",
        price: 10,
        currency: "USDC",
        description: "Soft Table, Best value for Money"
    },
    {
        id: 4,
        name: "Silver",
        price: 0.2,
        currency: "ETH",
        description: "Silver spoon set"
    },
    {
        id: 5,
        name: "Silver Cup",
        price: 0.03,
        currency: "BTC",
        description: "Cup for the big men"
    },
    {
        id: 6,
        name: "Basic Cup",
        price: 2000,
        currency: "DOGE",
        description: "Best value for Money"
    }
]
```

### Building the Home Page

Next.js uses file-based routing so any react component exported as default in the `pages` directory will be rendered as the page when that route is visited, you can learn more about it [here](https://nextjs.org/docs/routing/introduction). 

Navigate to `pages/index.js` delete the boilerplate code and input the following code snippets, we will discourse the code next.

```jsx
import { useState } from 'react';
import axios from 'axios';
import { products } from '../data';

export default function Home() {
  return (
    <div className={"container"}>
      {
        products.map( (product, index) => {
          return (<Products key={index} product={product} />)
        })
      }
    </div>
  )
}

const Products = ({product}) => {
  const [loading, setLoading] =  useState(false);
  
  const coinbase = async () => {
    setLoading(true)
    try {
      const data = await axios.post('/api/init', { id: product.id })
      setLoading(false)
      window.open(data.data.hosted_url, '_blank');
    } catch (e) {
      console.error(e)
      setLoading(false)
    }
  }

  return (
    <div>
      <h4>{product.name}</h4>
      <p>{product.description}</p>
      <p>Price: {product.price} {product.currency}</p>
      <button onClick={coinbase} disabled={loading}> Pay With Crtpto </button>
    </div>
  )
}
```

The code snippet above contains two components, `Home` and `Products`, the `Home` component is exported as the default component, while the `Product` component is not exported this is because we are only using it in this file to display each product. 

The code snippet below in the `Home` component maps the `products` array to the `Product` component.

```jsx
products.map( (product, index) => {
  return (<Products key={index} product={product} />)
})      
```

Let’s go into the `Product` component, this component displays a particular product, and it contains a very important function, the `coinbase` function, this function sends a post request to this route `/api/init`, we will set up this route later if this request is successful this route will create a payment charge using the product `id` and returns the charge details, then the code will open a new tab on the browser using the payment link returned from coinbase commerce.

Let’s take a look at the `coinbase`  function below;

```jsx
  const coinbase = async () => {
    setLoading(true)
    try {
      const data = await axios.post('/api/init', { id: product.id })
      setLoading(false)
      window.open(data.data.hosted_url, '_blank');
    } catch (e) {
      console.error(e)
      setLoading(false)
    }
  }
```

This piece of code `const data = await axios.post('/api/init', { id: product.id })` send a post request to our api routes passing the product ID as the body of the request, and this line of code `window.open(data.data.hosted_url, '_blank');` opens another tab in the browser, using the link returned from our post request, next we are going to set up the `init` route.

### Styling the Application

Now let’s add some styling to give the application a better look, we are going to be using global CSS for this one, global CSS now open the styles/global.css file, delete the boilerplate CSS and paste the CSS code snippet below, you can learn more about global CSS in Next.js from [here](https://nextjs.org/docs/basic-features/built-in-css-support).

```css
body {
  background-color: whitesmoke;
}

.container {
  padding: 0 2rem;
  display: flex;
  justify-content: space-evenly;
  height: 100vh;
  flex-wrap: wrap;
}

.container > div {
  display: block;
  margin: 0.8em;
  padding: 4em;
  border: 2px solid black;
  flex-grow: 1;
}
```


### Building the payment route handler

Next.js allows us to create serverless functions so that we can create backend API route handlers. You can learn more about them [here](https://nextjs.org/docs/api-routes/introduction). Next, create a file `pages/api/init.js` and paste the following code snippets. we will discourse the code next.

```jsx
import { Client, resources } from 'coinbase-commerce-node';
import { products } from '../../data';

Client.init(String(process.env.COINBASE_API));
const { Charge } = resources;

const coinInitRoute = async(req, res) => {

  const { id } = req.body

  const product = products.find(product => product.id === id)

  try {
          
    const chargeData = {
      name: product.name,
      description: product.description,
      pricing_type: "fixed_price",
      local_price: {
        amount: product.price,
        currency: product.currency,
      },
      metadata: {
        id: product.id,
        userID: 1
      },
    };
  
    const charge = await Charge.create(chargeData);

    res.send(charge);
  
  } catch (e) {
    res.status(500).send({ error:e });
  }

}

export default coinInitRoute
```

We start by importing the modules needed to create a charge, we will be using `Client` and `resources` from the `coinbase-commerce-node` package, next we import our product data,

this line of code `Client.init(process.env.COINBASE_API)` initiates the coinbase commerce client using our coinbase commerce API key we will talk about how to get this later.

```jsx
import { Client, resources } from 'coinbase-commerce-node';
import { products } from '../../data';

Client.init(process.env.COINBASE_API);
const { Charge } = resources;
```

The flow of this route handler is simple, we retrieve the product id from the `req.body` and use it to search for the correct product. 

```jsx
  const product = products.find(product => product.id === id)
```

Next, we create a charge using the product data, we set the `pricing_type` to `fixed_price` so the user can’t pay any amount, the pricing type can be set to `no_price` this will allow the user to pay any amount, `metadata` field is used to send additional data to coinbase commerce for identifying the user, this `metadata` is useful in identifying the user and the product they paid for, in our use case we set the `userID` to 1.

```jsx
const chargeData = {
      name: product.name,
      description: product.description,
      pricing_type: "fixed_price",
      local_price: {
        amount: product.price,
        currency: product.currency,
      },
      metadata: {
        id: product.id,
        userID: 1
      },
    };
```
The next two lines of codes create a charge by sending a request to coinbase commerce once this request is successful it sends the charged object to the client

```jsx
const charge = await Charge.create(chargeData);
res.send(charge);
```

You can check the coinbase commerce documentation [here](https://docs.cloud.coinbase.com/commerce/docs/webhooks-events).

### Creating Coinbase API key

We have created our application, but we can’t create charges just yet because we need an API KEY from coinbase commerce to get started, first create a `.env` file at the root of the project, and add `COINBASE_API` and `COINBASE_SECRET` to the `.env` file, the file should look like the one below.

```bash
COINBASE_API = 
COINBASE_SECRET =
```

Currently, coinbase commerce doesn’t have a sandbox or a testing environment, so we will have to allow our testing with live API keys and real cryptocurrencies. So log in to your coinbase commerce [account](https://commerce.coinbase.com/), click on the profile icon on the top right corner of the page, then click on settings.


![Create API Key](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4756lxehppfziib03anf.png)

Click on the Security tab and click on the `New API key` button to create an API key, then copy the API key, open the `.env` file and paste the keys to the `COINBASE_API` key.

![Coinbase Api](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5xvr4q7o957c929k0bxp.png)

### Test Running the Application

Run the application with this command `npm run dev` on the terminal then view the app on your browser and click on `Pay With Crypto` button on any of the items, this will open a new tab to make payments.

![Homepage](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1j1klzpnps44hiex4wxo.png)

You will have just one hour to make a payment, by choosing any of the methods below, you can choose to pay with coinbase or any of the cryptocurrencies listed below.

![Coinbase Pay](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8oxx1krxjq3z0zkkyhi4.png)

## Verifying Transactions

Now users can buy items from your application using cryptocurrencies but can’t verify who made the transaction or what they bought. To verify transactions you need to set up a webhook URL for coinbase commerce to send an HTTP post request to that route when a user makes a payment.

### Setting up the webhook route

Next, create a file `pages/api/verify.js` and paste the following code snippets, we will discourse the code next.

```bash
import { Client, Webhook } from 'coinbase-commerce-node';

Client.init(process.env.COINBASE_API);

export default async function coinVerifyRoute(req, res) {

    try {

        const rawBody = JSON.stringify(req.body)
        const signature = String(req.headers['x-cc-webhook-signature']);
        const webhookSecret = String(process.env.COINBASE_SECRET);
        const event = Webhook.verifyEventBody(rawBody, signature, webhookSecret);

        console.log(event)

        if (event.type === 'charge:pending') {
            // TODO
            // user paid, but transaction not confirm on blockchain 
            console.log("pending")
        }

        if (event.type === 'charge:confirmed') {
            // TODO
            // all good, charge confirmed
            console.log("confirmed")
        }

        if (event.type === 'charge:failed') {
            // TODO
            // charge failed or expired
            console.log("failed")
        }

    } catch (e) {
        res.status(500).send("error"); 
    }

    res.send(`success`); 

};
```

We start by importing the modules needed to make our webhook work, we will be using `Client` and `Webhook` from the `coinbase-commerce-node` package.

```jsx
import { Client, Webhook } from 'coinbase-commerce-node';
```

Next, we verify that the post request is coming from coinbase commerce and not a malicious person, you can notice that we are using an environment variable `COINBASE_SECRET` this variable will be gotten later from coinbase commerce when we try to test our webhook. This piece of code `Webhook.verifyEventBody(rawBody, signature, webhookSecret);` verifies the message sender, if the message is not coming from coinbase commerce it throws an error.

```jsx
const rawBody = JSON.stringify(req.body)
const signature = String(req.headers['x-cc-webhook-signature']);
const webhookSecret = String(process.env.COINBASE_SECRET);
const event = Webhook.verifyEventBody(rawBody, signature, webhookSecret);
```

Next let’s talk about events that can be sent by coinbase commerce, in our code we had just three events, which are `charge:pending`, `'charge:confirmed`, and `'charge:failed`, the `charge:pending` event is fired when the user have made payment but the transaction has not been confirmed on the blockchain, the `'charge:confirmed` as the name implies is sent when the transaction has been confirmed on the blockchain, you should confirm the price before you give value and `'charge:failed'` when the transaction failed.

```jsx
        if (event.type === 'charge:pending') {
            // TODO
            // user paid, but transaction not confirm on blockchain 
            console.log("pending")
        }

        if (event.type === 'charge:confirmed') {
            // TODO
            // all good, charge confirmed
            console.log("confirmed")
        }

        if (event.type === 'charge:failed') {
            // TODO
            // charge failed or expired
            console.log("failed")
        }
```

Before you give value to your customer, first confirm the amount paid and the currency, and use the information on the `metadata` field, to identify customer and products, you can learn more about the coinbase commerce webhooks [here](https://docs.cloud.coinbase.com/commerce/docs/webhooks-events).

### Testing the webhook Url

To test our webhook URL we will make use of a tool called Ngrok, if you don’t have it you can install it [here](https://ngrok.com/download), once installed run the following commands

```bash
npm run dev
```

The above command will run our app on port 3000, next run the command below to connect your application to the internet using ngrok, make sure your application port is the same as the one on the command.

```bash
ngrok HTTP 3000
```
![Ngrok](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pk9v9vo9n8005j2w63cc.png)

Copy the second Forwarding URL we are going to use this as our domain for the coinbase commerce URL

### Setting the webhook Url

Login to your coinbase commerce account, navigate settings, and click on the notification tab, then click on the `Add Endpoint` past the Forwarding URL gotten from ngrok, and add the `/api/verify` route as shown in the image below.

> Note on a live server this webhook URL should be https://your-domain/api/verify
> 

![Webhook Url](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5rqngcq4pb9boopbx2yg.png)

Next get your shared secret by clicking the `Show shared secret` button on the bottom right corner, copy the shared secret, open the `.env` file and assign it to the `COINBASE_SECRET` .

![Shared Secret](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/c4ri5b2m71cpbu5m8b66.png)

Now let’s manually test our webhook by sending it fake requests, First, click on the edit button on the right of the webhook endpoint.

![Click Edit](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ruf3q4dggrkblwk5ec9k.png)

Click on the `Send Test` button and choose any event of you choice, let’s start we confirm as shown in the photo below.

![Send Test](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/s0ym10ud2gr1r8cv02iw.png)

You should get a response like this

![Test Response](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8yk01klprgq57wsrxj2v.png)

Look at your terminal, to see the dummy data sent by coinbase commerce, feel free to test as many events as you want.

![Webhook Response](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/as6vjij9t31ifdarlzjo.png)

## Conclusion

In this article, we built a simple web-based ecommerce shop that accepts cryptocurrencies using Next.js, we talked about `coinbase-commerce-node`, which is the official nodejs SDK for coinbase commerce, we created API keys in coinbase commerce, and retrieved our coinbase commerce “shared secret”, we created a payment link with coinbase-commerce using this SDK, we created a webhook URL to verify transactions, and we manually tested our webhook endpoint locally from our coinbase commerce account, using a tool named ngrok, you can get the source code of this project [here](https://github.com/Joshuajee/nextjs-crypto), feel free to clone the repo.