> ## Documentation Index
> Fetch the complete documentation index at: https://docs.mlm-platform.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Webhooks

> Receive real-time notifications for Stripe events

# Webhooks

The MLM Platform uses Stripe webhooks to receive real-time notifications about subscription and payment events.

## Overview

When events occur in Stripe (e.g., subscription created, payment succeeded), Stripe sends webhook events to the MLM Platform, which then processes them to:

* Create commission entries for purchases
* Update subscription statuses
* Track payment lifecycle events

## Setting Up Webhooks

### Step 1: Get Your Webhook Endpoint

Your Stripe webhook endpoint is:

```
https://app.mlm-platform.com/api/v1/webhooks/stripe?tenant_id=YOUR_TENANT_ID
```

For sandbox environments:

```
https://app.mlm-platform.com/api/v1/webhooks/stripe?tenant_id=YOUR_TENANT_ID&environment=sandbox
```

Your Wise webhook endpoint is:

```
https://app.mlm-platform.com/api/v1/webhooks/wise?tenant_id=YOUR_TENANT_ID
```

For sandbox environments:

```
https://app.mlm-platform.com/api/v1/webhooks/wise?tenant_id=YOUR_TENANT_ID&environment=sandbox
```

If you use Sumsub for KYC, the Sumsub webhook endpoint is:

```
https://app.mlm-platform.com/api/v1/webhooks/kyc/sumsub?tenant_id=YOUR_TENANT_ID
```

### Step 2: Configure in Stripe Dashboard

1. Go to [Stripe Dashboard](https://dashboard.stripe.com/webhooks)
2. Click **Add endpoint**
3. Enter your webhook URL
4. Select events to listen for (see below)
5. Click **Add endpoint**
6. Copy the **Signing secret**

### Step 3: Save the Webhook Secret

In the MLM Platform Admin Dashboard:

1. Navigate to **Settings** > **Integrations** > **Stripe**
2. Enter the webhook signing secret
3. Click **Save**

## Supported Events

| Event                           | Description               | Action                      |
| ------------------------------- | ------------------------- | --------------------------- |
| `checkout.session.completed`    | Checkout completed        | Creates commission entries  |
| `invoice.paid`                  | Invoice payment succeeded | Records purchase event      |
| `customer.subscription.created` | New subscription          | Creates subscription record |
| `customer.subscription.updated` | Subscription changed      | Updates subscription status |
| `customer.subscription.deleted` | Subscription cancelled    | Marks subscription inactive |

## Webhook Signature Verification

All webhook requests are verified using Stripe's signature verification:

```javascript theme={null}
import Stripe from 'stripe';

const stripe = new Stripe(process.env.STRIPE_SECRET_KEY);

async function handleWebhook(req) {
  const sig = req.headers['stripe-signature'];
  const webhookSecret = process.env.STRIPE_WEBHOOK_SECRET;
  
  let event;
  try {
    event = stripe.webhooks.constructEvent(
      req.body,
      sig,
      webhookSecret
    );
  } catch (err) {
    console.error('Webhook signature verification failed:', err.message);
    return { status: 400, body: 'Invalid signature' };
  }
  
  // Process the event
  switch (event.type) {
    case 'checkout.session.completed':
      await handleCheckoutComplete(event.data.object);
      break;
    // ... handle other events
  }
  
  return { status: 200, body: 'OK' };
}
```

<Warning>
  Always verify webhook signatures to prevent spoofed requests.
</Warning>

## Idempotent Handling

Stripe may send the same event multiple times. Handle this by:

1. Storing processed event IDs
2. Checking before processing

```javascript theme={null}
async function handleWebhook(event) {
  // Check if already processed
  const existing = await db.webhookEvents.findUnique({
    where: { stripeEventId: event.id }
  });
  
  if (existing) {
    console.log('Event already processed:', event.id);
    return { status: 200, body: 'Already processed' };
  }
  
  // Process the event
  await processEvent(event);
  
  // Mark as processed
  await db.webhookEvents.create({
    data: {
      stripeEventId: event.id,
      type: event.type,
      processedAt: new Date()
    }
  });
  
  return { status: 200, body: 'OK' };
}
```

## Retry Behavior

Stripe retries failed webhook deliveries:

| Attempt | Delay                   |
| ------- | ----------------------- |
| 1       | Immediate               |
| 2       | 5 minutes               |
| 3       | 30 minutes              |
| 4       | 2 hours                 |
| 5       | 5 hours                 |
| 6       | 10 hours                |
| 7+      | 24 hours (up to 3 days) |

<Info>
  Return a 2xx status code quickly to acknowledge receipt. Process events asynchronously if needed.
</Info>

## Testing Webhooks

### Using Stripe CLI

```bash theme={null}
# Install Stripe CLI
brew install stripe/stripe-cli/stripe

# Login
stripe login

# Forward webhooks to local server
stripe listen --forward-to localhost:3000/api/v1/webhooks/stripe?tenant_id=YOUR_TENANT_ID

# Trigger test events
stripe trigger checkout.session.completed
```

### Using Sandbox Environment

1. Use Stripe test mode
2. Use sandbox API keys
3. Create test subscriptions with test cards

Test card numbers:

* `4242 4242 4242 4242` - Succeeds
* `4000 0000 0000 0002` - Declines

## Troubleshooting

### Webhook Not Received

1. Check endpoint URL is correct
2. Verify webhook is enabled in Stripe
3. Check Stripe webhook logs for errors
4. Ensure firewall allows Stripe IPs

### Signature Verification Failed

1. Verify webhook secret is correct
2. Ensure raw request body is used (not parsed JSON)
3. Check for proxy modifications to headers

### Event Processing Failed

1. Check MLM Platform logs
2. Verify user exists for the customer
3. Check commission rules are configured
