🔐 Verify webhooks

Finally, if you're using NextJS, you will need to create a route specifically for our webhooks in i.e. /api/webhooks/brixpay. After that, you'll need to go back to our console and create a webhook secret in /dashboard and make sure to copy it and store it somewhere safe, because you cannot access it again. If you loose it, you need to create a new one.

Now put that secret into your .env file:

BRIX_WEBHOOK_SECRET=583a024e2f3887b04b32008b52a5a0e2fc6eadf32dfdf09b8fa68b5a2c3dd46a

Before you can start performing actions based on specific webhooks, you must verify that the incoming requests are from BrixPay. This is done by validating the X-Brix-Signature included in the request headers using the webhook secret you just set up. Here's how you do this in your NextJS API route:

import { NextResponse } from 'next/server';
import crypto from 'crypto';

export async function POST(req) {
    const webhookSecret = process.env.BRIX_WEBHOOK_SECRET;
    const signature = req.headers.get('X-Brix-Signature');
    const body = await req.text();

    const expectedSignature = createHmac(
        "sha256",
        process.env.BRIX_WEBHOOK_SECRET
    ).update(body).digest("hex");

    if (signature !== expectedSignature) {
        return NextResponse.json({ error: 'Invalid signature' }, { status: 401 });
    }

    const event = JSON.parse(body);

    // Handle the webhook event
    console.log('Received event:', event);

    return NextResponse.json({ message: "Webhook received" }, { status: 200 });
}

👂 Listen for webhooks

Now that you have your webhook endpoint set up and verified, you can listen for specific webhook events. You can handle different event types by checking the event.eventType property in the received event object. Currently, 4 typed exist:

(With example data)

{
    eventType: 'subscription.authorized',
    data: {
        merchantAddress: '0x694B21d5190000149a7016743955A49986176796',
        productId: '0',
        userId: 'user_303gIcIdVUv03NkTb3SGw5rIZ3m',
        smartWalletAddress: '0x307143747A5F3437Ec3B00f56bd2362e346328b0',
        network: 'Arbitrum Sepolia'
    },
    timestamp: '2025-10-24T17:13:56.115Z'
}
{
    eventType: 'subscription.revoked',
    data: {
        merchantAddress: '0x694B21d5190000149a7016743955A49986176796',
        productId: '0',
        smartWalletAddress: '0x307143747A5F3437Ec3B00f56bd2362e346328b0',
        network: 'Arbitrum Sepolia'
    },
    timestamp: '2025-10-24T17:54:39.303Z'
}
{
    eventType: 'payment.processed',
    data: {
        smartWalletAddress: '0x307143747A5F3437Ec3B00f56bd2362e346328b0',
        merchantAddress: '0x694B21d5190000149a7016743955A49986176796',
        productId: '0',
        network: 'Arbitrum Sepolia',
        interval: 86400,
        lastPayment: '1761328328'
    },
    timestamp: '2025-10-24T17:52:10.910Z'
}
{
    eventType: 'payment.failed',
    data: {
      reason: 'Execution reverted. Revert signature: 0x356e106d',
        smartWalletAddress: '0x307143747A5F3437Ec3B00f56bd2362e346328b0',
        merchantAddress: '0x694B21d5190000149a7016743955A49986176796',
        productId: 0,
         network: 'Arbitrum Sepolia'
    },
    timestamp: '2025-10-24T17:13:56.115Z'
}

The timestamp in the object is in ISO String format, and lastPayment in payment.processed is a UNIX timestamp.

Great! You are now set up to securely receive and handle webhooks from BrixPay and accept recurring crypto payments, for any feedback or other inquiries email us at hi@brixpay.io or join our Discord.