Documentation
Developer Resources

Webhooks Integration

Learn how to robustly handle webhook events from CryptoPay in your application.

Last updated: June 23, 2023

The Basics

Webhooks are the glue that holds your integration together. They allow your backend to stay in sync with the state of payments, subscriptions, and transfers on CryptoPay without polling.

How it works

  1. 1An event occurs in your CryptoPay account (e.g., payment succeeded).
  2. 2CryptoPay creates an Event object.
  3. 3CryptoPay sends the Event object to your webhook endpoint via HTTP POST.
  4. 4Your server receives the request, verifies the signature, and processes the event.
  5. 5Your server returns a 200 OK response.
POST /webhooks200 OK
{
  "id": "evt_123...",
  "type": "payment.succeeded",
  "data": {...}
}

Typical webhook payload structure

Handling Events

Your webhook handler needs to be robust and secure. Here is a complete example of how to handle webhooks in a Node.js Express application.

server.js

const express = require('express');
const app = express();
const CryptoPay = require('CryptoPay');

// Initialize the client
const client = new CryptoPay('sk_test_...');

// Match the raw body to content type application/json
app.post('/webhook', express.raw({type: 'application/json'}), (request, response) => {
  const sig = request.headers['x-CryptoPay-signature'];
  const endpointSecret = "whsec_...";

  let event;

  try {
    // Verify signature
    event = client.webhooks.constructEvent(request.body, sig, endpointSecret);
  } catch (err) {
    console.log(`Webhook Error: ${err.message}`);
    return response.status(400).send(`Webhook Error: ${err.message}`);
  }

  // Handle the event
  switch (event.type) {
    case 'payment_intent.succeeded':
      const paymentIntent = event.data.object;
      handlePaymentSuccess(paymentIntent);
      break;
    case 'payment_intent.failed':
      const paymentFailed = event.data.object;
      handlePaymentFailure(paymentFailed);
      break;
    // ... handle other event types
    default:
      console.log(`Unhandled event type ${event.type}`);
  }

  // Return a 200 response to acknowledge receipt of the event
  response.json({received: true});
});

function handlePaymentSuccess(paymentIntent) {
  console.log('Payment succeeded:', paymentIntent.id);
  // Fulfill order, send email, etc.
}

function handlePaymentFailure(paymentIntent) {
  console.log('Payment failed:', paymentIntent.id);
  // Notify user, etc.
}

app.listen(3000, () => console.log('Running on port 3000'));

Best Practices

Verify Signatures

Always verify the webhook signature to prevent replay attacks and ensure the request came from CryptoPay.

Handle Duplicates

Webhook endpoints may occasionally receive the same event more than once. Make your event processing idempotent.

Secure Your Endpoint

Use HTTPS for your webhook endpoint and consider adding IP allowlisting if possible.

Respond Quickly

Respond with a 2xx status code quickly. Perform complex logic (like sending emails) asynchronously.