Core Features

Webhooks

Webhooks allow you to receive real-time notifications about important events in your MyBerryFlow integration, such as successful payments, KYC requirements, and seller status changes.

MyBerryFlow sends webhooks as HTTP POST requests to your configured endpoint. Your endpoint should return a 2xx status code to acknowledge receipt.

Setting up webhooks

Configure your webhook endpoint in the MyBerryFlow dashboard under Settings → Webhooks. You can specify which events you want to receive.

Webhook endpoint requirements

  • Must be accessible over HTTPS
  • Should respond with a 2xx status code
  • Should respond within 10 seconds
  • Should be idempotent (handle duplicate events gracefully)

Event types

Payment Events

payment.succeeded
{
  "type": "payment.succeeded",
  "data": {
    "paymentId": "pay_abc123",
    "sellerId": "seller_xyz789",
    "amount": 2999,
    "currency": "usd",
    "customerEmail": "customer@example.com",
    "metadata": {
      "productName": "Premium Service"
    }
  },
  "created": "2024-01-15T14:30:00Z"
}
payment.failed
{
  "type": "payment.failed",
  "data": {
    "paymentId": "pay_abc123",
    "sellerId": "seller_xyz789", 
    "amount": 2999,
    "currency": "usd",
    "failureReason": "card_declined",
    "failureMessage": "Your card was declined."
  },
  "created": "2024-01-15T14:30:00Z"
}

Seller Events

seller.created
{
  "type": "seller.created",
  "data": {
    "sellerId": "seller_abc123",
    "email": "seller@example.com",
    "country": "US",
    "status": "active",
    "onboardingType": "deferred"
  },
  "created": "2024-01-15T10:30:00Z"
}
seller.kyc_required
{
  "type": "seller.kyc_required",
  "data": {
    "sellerId": "seller_abc123",
    "email": "seller@example.com",
    "totalRevenue": 10000,
    "kycThreshold": 10000,
    "onboardingUrl": "https://connect.stripe.com/setup/s/...",
    "message": "Complete your account setup to continue receiving payments"
  },
  "created": "2024-01-15T12:00:00Z"
}
seller.verified
{
  "type": "seller.verified",
  "data": {
    "sellerId": "seller_abc123",
    "email": "seller@example.com",
    "kycCompletedAt": "2024-01-15T16:45:00Z",
    "canReceivePayouts": true
  },
  "created": "2024-01-15T16:45:00Z"
}
seller.kyc_failed
{
  "type": "seller.kyc_failed",
  "data": {
    "sellerId": "seller_abc123", 
    "email": "seller@example.com",
    "failureReason": "document_verification_failed",
    "requirements": [
      "identity_document",
      "address_verification"
    ]
  },
  "created": "2024-01-15T17:00:00Z"
}

Handling webhooks

Basic webhook handler

Node.js/Express Example
app.post('/webhook/myberryflow', express.raw({type: 'application/json'}), (req, res) => {
  const event = JSON.parse(req.body);
  
  // Handle the event
  switch (event.type) {
    case 'payment.succeeded':
      console.log('Payment succeeded:', event.data.paymentId);
      // Update your database, send confirmation email, etc.
      break;
      
    case 'seller.kyc_required':
      console.log('Seller needs KYC:', event.data.sellerId);
      // Send notification email to seller with onboarding link
      sendKycNotification(event.data.email, event.data.onboardingUrl);
      break;
      
    case 'seller.verified':
      console.log('Seller verified:', event.data.sellerId);
      // Enable additional features, send congratulations email
      break;
      
    default:
      console.log('Unhandled event type:', event.type);
  }
  
  // Return 200 to acknowledge receipt
  res.json({received: true});
});

Idempotency

MyBerryFlow may send the same webhook multiple times. Include an idempotency key to handle duplicates:

Idempotent Handler
const processedEvents = new Set();

app.post('/webhook/myberryflow', (req, res) => {
  const event = req.body;
  const eventId = event.id; // Use event ID for idempotency
  
  // Check if we've already processed this event
  if (processedEvents.has(eventId)) {
    return res.json({received: true});
  }
  
  // Process the event
  handleEvent(event);
  
  // Mark as processed
  processedEvents.add(eventId);
  
  res.json({received: true});
});

Webhook security

Verify webhook signatures

Always verify webhook signatures to ensure they're from MyBerryFlow and haven't been tampered with.

MyBerryFlow signs webhooks with your webhook secret. Verify the signature:

Signature Verification
const crypto = require('crypto');

function verifyWebhook(payload, signature, secret) {
  const expectedSignature = crypto
    .createHmac('sha256', secret)
    .update(payload)
    .digest('hex');
    
  return crypto.timingSafeEqual(
    Buffer.from(signature, 'hex'),
    Buffer.from(expectedSignature, 'hex')
  );
}

app.post('/webhook/myberryflow', express.raw({type: 'application/json'}), (req, res) => {
  const signature = req.headers['myberryflow-signature'];
  const payload = req.body;
  
  if (!verifyWebhook(payload, signature, process.env.WEBHOOK_SECRET)) {
    return res.status(400).send('Invalid signature');
  }
  
  // Process the verified webhook
  const event = JSON.parse(payload);
  handleEvent(event);
  
  res.json({received: true});
});

Testing webhooks

Using ngrok for local development

For local testing, use ngrok to create a public URL for your local server:

ngrok setup
# Install ngrok
npm install -g ngrok

# Start your local server
node server.js

# In another terminal, expose port 3000
ngrok http 3000

# Use the ngrok URL in MyBerryFlow dashboard
# Example: https://abc123.ngrok.io/webhook/myberryflow

Manual webhook testing

You can test webhook handling by sending POST requests to your endpoint:

Test webhook
curl -X POST https://yourapp.com/webhook/myberryflow \
  -H "Content-Type: application/json" \
  -d '{
    "type": "payment.succeeded",
    "data": {
      "paymentId": "pay_test123",
      "sellerId": "seller_test456", 
      "amount": 1000,
      "currency": "usd"
    },
    "created": "2024-01-15T14:30:00Z"
  }'