Skip to content
On this page

Implementing custom webhooks

CandyPay checkout allows developer to setup their own webhooks to listen events such as transaction.successful, transaction.failed and run post-checkout events such as saving the transaction details to a database.

Source Code

⚡ Full source code for the example webhook server available here

Setting up an Node.js server

We'll be using express.js to setup the webhook server. Go ahead and install express, dotenv

pnpm i express dotenv

If you're using TypeScript, you'll need to additional install the typings for the express module - @types/express

import express, { Request, Response } from "express";

const app = express();

app.use(express.urlencoded({ extended: false }));

app.get("/", (_req: Request, res: Response) => {
  return res.status(200).json({
    message: "I'm alive!",

const port = 3000 || process.env.PORT;

app.listen(3000, () => {
  console.log(`The server is running on port ${port}`);

Creating webhook endpoint

A webhook is an endpoint on your server that receives incoming requests regarding checkout session's transaction updates from CandyPay. Add a new POST endpoint to your server.

import { verifyWebhookSignature } from "@candypay/checkout-sdk";"/webhook", async (req: Request, res: Response) => {
  const headers = req.headers;
  const payload = req.body;

  try {
    await verifyWebhookSignature({
      payload: JSON.stringify(payload),
      headers: headers as Record<string, string>,
      webhook_secret: process.env.WEBHOOK_SECRET!,
  } catch (err) {
    return res.status(400).json({
      message: "Invalid webhook signature",

  return res.send();

The /webhook endpoint validates the X-CandyPay-Signature header via the webhook secret field. The webhook secret needs to be generated from the CandyPay dashboard, under the settings page

The payload objects provides you with all the payment related data that can be used to power multiple functions like NFT airdrops, SPL tokens cashback, storing payments in your custom dB, sending emails, and much more! A payload object includes all the below given fields,

customer: '882BZWTWuJehEocq3wRq2dzzTe53n51C64rcWWVkDSpS',
customer_email: '[email protected]',
event: 'transaction.successful',
items: [{
image: '';,
name: 'Clayno Plushie',
price: 0.03,
quantity: 1
metadata: {
customer_name: "John Doe"
network: 'mainnet',
order_id: 'TyAhFD63RK8U',
payment_amount: 0.03,
payment_currency: 'USD',
session_id: 'cp_pay_ef1bf155-7ae9-47e7-bbac-091e64fddeb9',
signature: 'bzX81JoktbiKBkAqoVN1EAPFF9H6YfdcqeGez4d9SD9k8F2H62yF1gLaevtmED31b7XcCpWgRJhYMxhfQSFSThc',
timestamp: '2023-04-11T18:02:08.644Z'