Skip to main content

Overview

Webhooks are sent as POST requests to your configured endpoint with a JSON payload.

Event Types

EventDescription
payment.completedPayment successfully processed
feature.unlockedFeature unlock product purchased
access.verifiedAccess token verified via API
product.createdNew product created
product.updatedProduct settings changed
product.deletedProduct removed

payment.completed

Sent when a payment is successfully processed.

Payload

{
  "type": "payment.completed",
  "productId": "prod_abc123xyz",
  "productTitle": "Premium Article",
  "walletAddress": "7xK3abcdefghijklmnop",
  "txHash": "5defghijklmnopqrstuvwxyz123456789",
  "amount": "0.50",
  "currency": "USDC",
  "network": "mainnet-beta",
  "timestamp": "2024-01-15T10:30:00.000Z"
}

Fields

FieldTypeDescription
typestringAlways payment.completed
productIdstringProduct identifier
productTitlestringProduct name
walletAddressstringPayer’s wallet
txHashstringSolana transaction hash
amountstringPayment amount
currencystringAlways USDC
networkstringSolana network
timestampstringISO 8601 timestamp

feature.unlocked

Sent when a feature unlock product is purchased.

Payload

{
  "type": "feature.unlocked",
  "productId": "prod_feature123",
  "productTitle": "Pro Plan",
  "accessToken": "ft_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6",
  "featureIds": [
    "pro_analytics",
    "pro_export",
    "pro_themes"
  ],
  "walletAddress": "7xK3abcdefghijklmnop",
  "txHash": "5defghijklmnopqrstuvwxyz123456789",
  "timestamp": "2024-01-15T10:30:00.000Z"
}

Fields

FieldTypeDescription
typestringAlways feature.unlocked
productIdstringProduct identifier
productTitlestringProduct name
accessTokenstringGenerated access token
featureIdsstring[]Unlocked feature identifiers
walletAddressstringPurchaser’s wallet
txHashstringSolana transaction hash
timestampstringISO 8601 timestamp

product.created

Sent when a new product is created.

Payload

{
  "type": "product.created",
  "productId": "prod_new123",
  "productTitle": "New Product",
  "productType": "content",
  "price": "1.00",
  "currency": "USDC",
  "status": "active",
  "network": "mainnet-beta",
  "timestamp": "2024-01-15T10:30:00.000Z"
}

product.updated

Sent when a product is updated.

Payload

{
  "type": "product.updated",
  "productId": "prod_abc123",
  "productTitle": "Updated Product",
  "productType": "content",
  "price": "2.00",
  "currency": "USDC",
  "status": "active",
  "network": "mainnet-beta",
  "changes": ["price", "status"],
  "timestamp": "2024-01-15T10:30:00.000Z"
}

product.deleted

Sent when a product is deleted.

Payload

{
  "type": "product.deleted",
  "productId": "prod_abc123",
  "productTitle": "Deleted Product",
  "timestamp": "2024-01-15T10:30:00.000Z"
}

Handling Webhooks

Express.js

import express from 'express';

const app = express();
app.use(express.json());

app.post('/webhooks/paygate', async (req, res) => {
  const event = req.body;

  // Log for debugging
  console.log('Webhook received:', event.type);

  switch (event.type) {
    case 'payment.completed':
      await handlePaymentCompleted(event);
      break;

    case 'feature.unlocked':
      await handleFeatureUnlocked(event);
      break;

    case 'product.created':
    case 'product.updated':
    case 'product.deleted':
      await handleProductEvent(event);
      break;

    default:
      console.log('Unknown event type:', event.type);
  }

  // Always respond 200 to acknowledge
  res.json({ received: true });
});

async function handlePaymentCompleted(event) {
  // Grant access in your system
  await db.access.create({
    data: {
      productId: event.productId,
      walletAddress: event.walletAddress,
      txHash: event.txHash,
      amount: parseFloat(event.amount),
      createdAt: new Date(event.timestamp)
    }
  });
}

async function handleFeatureUnlocked(event) {
  // Store access token for user
  await db.users.upsert({
    where: { wallet: event.walletAddress },
    update: {
      accessToken: event.accessToken,
      features: event.featureIds
    },
    create: {
      wallet: event.walletAddress,
      accessToken: event.accessToken,
      features: event.featureIds
    }
  });
}

async function handleProductEvent(event) {
  console.log(`Product ${event.productId}: ${event.type}`);
}

Verifying Webhooks

To ensure webhooks are from PayGate, check the source IP or implement signature verification.

Retry Policy

Failed webhook deliveries are retried:
AttemptDelay
1Immediate
21 minute
35 minutes
430 minutes
52 hours
624 hours
After 6 failed attempts, the webhook is marked as failed.

Testing

Use the dashboard to send test webhooks:
  1. Go to Webhooks
  2. Click on your webhook
  3. Click Send Test
  4. Select event type
  5. Review the payload sent
You can also use webhook.site for testing during development.