Pular para o conteúdo principal

Webhook Quick Start Guide

Set up your first EdenPay webhook endpoint in under 5 minutes.

Prerequisites

  • Active BlockEden.xyz account (sign up here)
  • A publicly accessible HTTPS server endpoint
  • Basic knowledge of your backend framework

Step 1: Create a Webhook Endpoint (2 minutes)

  1. Log in to your BlockEden dashboard
  2. Navigate to Accept PaymentsWebhooks tab
  3. Click "Add Endpoint" button
  4. Configure your endpoint:
    • URL: Enter your HTTPS webhook URL (e.g., https://your-domain.com/api/webhooks)
    • Events: Select events to receive (start with payment.confirmed and payment.failed)
    • Description: Optional description for your reference
  5. Click "Create Endpoint"
  6. Copy the secret displayed in the success modal ⚠️ Save it securely - you won't see it again
Store Your Secret Securely

Save the webhook secret in your environment variables or secrets manager immediately. You'll need it to verify webhook signatures.

Step 2: Implement Your Webhook Handler (3 minutes)

Choose your backend framework:

Node.js with Express

const express = require('express');
const crypto = require('crypto');

const app = express();
const WEBHOOK_SECRET = process.env.WEBHOOK_SECRET; // Your webhook secret

// IMPORTANT: Use raw body for signature verification
app.post('/api/webhooks',
express.raw({ type: 'application/json' }),
(req, res) => {
// 1. Get signature from headers
const signature = req.headers['x-eden-signature'];

// 2. Verify signature
const expectedSignature = crypto
.createHmac('sha256', WEBHOOK_SECRET)
.update(req.body)
.digest('hex');

if (signature !== expectedSignature) {
console.error('⚠️ Invalid signature');
return res.status(401).send('Unauthorized');
}

// 3. Parse and handle the event
const event = JSON.parse(req.body.toString());
console.log('✅ Received event:', event.type);

switch (event.type) {
case 'payment.confirmed':
// Payment confirmed - safe to fulfill order
console.log('💰 Payment confirmed:', event.data.paymentId);
// TODO: Your fulfillment logic here
break;

case 'payment.failed':
// Payment failed - notify customer
console.log('❌ Payment failed:', event.data.paymentId);
// TODO: Your failure handling logic here
break;

default:
console.log('ℹ️ Unhandled event type:', event.type);
}

// 4. Return 200 to acknowledge receipt
res.status(200).json({ received: true });
}
);

app.listen(3000, () => {
console.log('🎧 Webhook server listening on port 3000');
});

Next.js App Router

// app/api/webhooks/route.ts
import { NextRequest, NextResponse } from 'next/server';
import crypto from 'crypto';

const WEBHOOK_SECRET = process.env.WEBHOOK_SECRET!;

export async function POST(req: NextRequest) {
try {
// 1. Get signature from headers
const signature = req.headers.get('x-eden-signature');
if (!signature) {
return NextResponse.json(
{ error: 'Missing signature' },
{ status: 401 }
);
}

// 2. Get raw body for signature verification
const body = await req.text();

// 3. Verify signature
const expectedSignature = crypto
.createHmac('sha256', WEBHOOK_SECRET)
.update(body)
.digest('hex');

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

// 4. Parse and handle the event
const event = JSON.parse(body);
console.log('✅ Received event:', event.type);

switch (event.type) {
case 'payment.confirmed':
console.log('💰 Payment confirmed:', event.data.paymentId);
// TODO: Your fulfillment logic here
await fulfillOrder(event.data);
break;

case 'payment.failed':
console.log('❌ Payment failed:', event.data.paymentId);
// TODO: Your failure handling logic here
await handleFailedPayment(event.data);
break;
}

// 5. Return 200 to acknowledge receipt
return NextResponse.json({ received: true }, { status: 200 });

} catch (error) {
console.error('Error processing webhook:', error);
return NextResponse.json(
{ error: 'Webhook processing failed' },
{ status: 500 }
);
}
}

// Example fulfillment function
async function fulfillOrder(paymentData: any) {
// Your business logic here
// - Update database
// - Send confirmation email
// - Grant access to purchased content
}

async function handleFailedPayment(paymentData: any) {
// Your failure handling logic here
// - Send failure notification
// - Update order status
}

Python with Flask

from flask import Flask, request, jsonify
import hmac
import hashlib
import os

app = Flask(__name__)
WEBHOOK_SECRET = os.environ.get('WEBHOOK_SECRET')

@app.route('/api/webhooks', methods=['POST'])
def webhook():
# 1. Get signature from headers
signature = request.headers.get('x-eden-signature')

# 2. Verify signature
body = request.get_data()
expected_signature = hmac.new(
WEBHOOK_SECRET.encode('utf-8'),
body,
hashlib.sha256
).hexdigest()

if signature != expected_signature:
print('⚠️ Invalid signature')
return jsonify({'error': 'Unauthorized'}), 401

# 3. Parse and handle the event
event = request.get_json()
print(f'✅ Received event: {event["type"]}')

if event['type'] == 'payment.confirmed':
print(f'💰 Payment confirmed: {event["data"]["paymentId"]}')
# TODO: Your fulfillment logic here

elif event['type'] == 'payment.failed':
print(f'❌ Payment failed: {event["data"]["paymentId"]}')
# TODO: Your failure handling logic here

# 4. Return 200 to acknowledge receipt
return jsonify({'received': True}), 200

if __name__ == '__main__':
app.run(port=3000)

Step 3: Test Your Webhook

  1. Deploy your webhook handler to your server
  2. Go back to the BlockEden dashboard → Webhooks tab
  3. Click "Test" on your webhook endpoint
  4. Check your server logs to verify:
    • ✅ Event was received
    • ✅ Signature was validated correctly
    • ✅ Event was processed successfully
Testing Locally

For local development, use tools like ngrok or localtunnel to expose your local server to the internet:

# With ngrok
ngrok http 3000

# With localtunnel
npx localtunnel --port 3000

Step 4: Handle Webhook Delivery

Return 200 Quickly

Your endpoint must return a 200 OK response within 30 seconds. If processing takes longer, acknowledge receipt immediately and process asynchronously:

app.post('/api/webhooks', async (req, res) => {
// Verify signature...

// Return 200 immediately
res.status(200).json({ received: true });

// Process asynchronously
processWebhookAsync(event).catch(console.error);
});

async function processWebhookAsync(event) {
// Your heavy processing here
// - Database operations
// - External API calls
// - Send emails
}

Monitor Delivery Attempts

View delivery attempts in the dashboard to:

  • See request/response details
  • Check error messages
  • Replay failed events
  • Monitor success rate

Security Checklist

  • Always verify signatures before processing events
  • Store secrets securely in environment variables
  • Use HTTPS only - HTTP endpoints will be rejected
  • Return 200 quickly - process heavy tasks asynchronously
  • Handle idempotency - events may be delivered more than once
  • Validate event structure before processing

Common Issues

401 Unauthorized

Cause: Signature verification is failing

Solutions:

  • Verify you're using the correct webhook secret
  • Ensure you're hashing the raw request body (before parsing)
  • Check that your HMAC implementation matches (SHA-256)

Webhook Timeout

Cause: Your endpoint took longer than 30 seconds to respond

Solutions:

  • Return 200 immediately and process asynchronously
  • Move database operations to background queue
  • Optimize slow API calls

Missing Events

Cause: Your endpoint is not publicly accessible

Solutions:

  • Verify your endpoint is reachable from the internet
  • Check firewall rules and security groups
  • Test with curl from an external server

Next Steps

Need Help?