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
- Nodejs installed on your computer
- Ngrok installed on your computer
- 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.
npx create-next-app@latest
Next, navigate to the project directory and install the following dependencies by running the command below.
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.
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, 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, 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.
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.
Navigate to pages/index.js
delete the boilerplate code and input the following code snippets, we will discourse the code next.
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.
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;
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.
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. Next, create a file pages/api/init.js
and paste the following code snippets. we will discourse the code next.
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.
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.
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.
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
const charge = await Charge.create(chargeData);
res.send(charge);
You can check the coinbase commerce documentation here.
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.
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, click on the profile icon on the top right corner of the page, then click on settings.
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.
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.
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.
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.
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.
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.
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.
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.
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, once installed run the following commands
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.
ngrok HTTP 3000
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
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
.
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 on the Send Test
button and choose any event of you choice, let’s start we confirm as shown in the photo below.
You should get a response like this
Look at your terminal, to see the dummy data sent by coinbase commerce, feel free to test as many events as you want.
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, feel free to clone the repo.
Top comments (1)
Thanks, that was very informative. Modern crypto payment technologies are beginning to be actively used where natural financial instruments are limited. I read that Russia plans to introduce such a payment system to circumvent sanctions. It is very interesting that crypto technologies do not have an official legislative base there. In general, you can read about the participation in the project of such platforms as Bitcoin Apex. I'm very interested in how it will eventually be implemented. It is also necessary to take into account the fact that there are countries where crypto falls under direct state control (for example, the USA)