# Dodo Payments Webhooks Setup Guide

This guide explains how to configure Dodo Payments webhooks for the Shopify AI App Builder to handle payment events automatically, including failed payments, disputes, refunds, and subscription cancellations.

## Overview

The webhook system handles these events:
- **payment_succeeded**: Process successful payments (top-ups, PAYG)
- **payment_failed**: Cancel subscription on failed payments
- **payment_cancelled**: Handle cancelled payments
- **payment_processing**: Handle processing payments
- **payment_dispute_created**: Cancel subscription when a dispute is opened
- **dispute_accepted**: Handle accepted disputes
- **dispute_cancelled**: Handle cancelled disputes
- **dispute_challenged**: Handle challenged disputes
- **dispute_expired**: Handle expired disputes
- **dispute_lost**: Handle lost disputes
- **dispute_won**: Handle won disputes
- **charge_refunded**: Log refund events
- **refund_failed**: Handle failed refunds
- **subscription_canceled**: Downgrade user to hobby plan
- **subscription_payment_failed**: Cancel subscription on recurring payment failures
- **subscription_active**: Activate subscription
- **subscription_expired**: Handle expired subscriptions
- **subscription_on_hold**: Handle subscriptions on hold
- **subscription_plan_changed**: Handle plan changes
- **subscription_renewed**: Handle subscription renewals
- **subscription_updated**: Handle subscription updates

## Prerequisites

Before setting up webhooks, ensure you have:
1. Dodo Payments account configured (see `dodo.md`)
2. Your application deployed and accessible via public URL
3. Dodo API key and environment configured

## Step 1: Generate a Webhook Secret Key

1. Log in to your Dodo Payments dashboard
2. Navigate to **Developer → Webhooks**
3. Click **Create Webhook Secret**
4. Copy the generated webhook secret key (starts with `whsec_`)

Example webhook secret: `whsec_abc123def456ghi789jkl012mno345pqr`

## Step 2: Configure Environment Variable

Add the webhook secret to your environment configuration:

```bash
# Add to your .env file or deployment environment variables
DODO_PAYMENTS_WEBHOOK_KEY=whsec_abc123def456ghi789jkl012mno345pqr
```

**Important**: Restart your application after adding this variable.

## Step 3: Determine Your Webhook URL

Your webhook URL will be:
```
https://your-domain.com/webhooks/dodo
```

Examples:
- Production: `https://plugincompass.ai/webhooks/dodo`
- Staging: `https://staging.plugincompass.ai/webhooks/dodo`
- Local development: Use a tunneling service like ngrok

### Local Development with ngrok

If testing locally, use ngrok to expose your local server:

```bash
# Install ngrok if needed: https://ngrok.com/download

# Start ngrok (assuming your app runs on port 4000)
ngrok http 4000

# This will give you a URL like: https://abc123.ngrok.io
# Your webhook URL will be: https://abc123.ngrok.io/webhooks/dodo
```

## Step 4: Register Webhook in Dodo Dashboard

1. Navigate to **Developer → Webhooks** in Dodo Payments dashboard
2. Click **Add Webhook** or **Create Webhook**
3. Fill in the webhook details:
   - **Webhook URL**: `https://your-domain.com/webhooks/dodo`
   - **Events to send**: Select all events:
     - ✅ `payment.succeeded`
     - ✅ `payment.failed`
     - ✅ `payment.cancelled`
     - ✅ `payment.processing`
     - ✅ `payment.dispute.created`
     - ✅ `dispute.accepted`
     - ✅ `dispute.cancelled`
     - ✅ `dispute.challenged`
     - ✅ `dispute.expired`
     - ✅ `dispute.lost`
     - ✅ `dispute.won`
     - ✅ `charge.refunded`
     - ✅ `refund.failed`
     - ✅ `subscription.canceled`
     - ✅ `subscription.payment.failed`
     - ✅ `subscription.active`
     - ✅ `subscription.expired`
     - ✅ `subscription.on_hold`
     - ✅ `subscription.plan_changed`
     - ✅ `subscription.renewed`
     - ✅ `subscription.updated`
   - **Secret**: Paste your webhook secret key (from Step 1)
4. Click **Save** or **Create Webhook**

## Step 5: Test Webhook Configuration

### Verify Webhook Endpoint

Check that your webhook endpoint is accessible:

```bash
curl -X POST https://your-domain.com/webhooks/dodo \
  -H "Content-Type: application/json" \
  -d '{"test": true}'
```

Expected response: `{"received": true}` (with HTTP 200 status)

### Send Test Webhook

In the Dodo dashboard:
1. Find your newly created webhook
2. Click **Send Test Event**
3. Select an event type (e.g., `payment.succeeded`)
4. Check your application logs for webhook receipt

You should see log entries like:
```
[2026-01-20T16:30:45.123Z] Dodo webhook received {"type":"payment.succeeded","id":"evt_..."}
```

## Step 6: Monitor Webhook Logs

### Application Logs

Your application logs webhook events with the following format:

```
[timestamp] Dodo webhook received {"type":"event_type","id":"event_id"}
[timestamp] payment_succeeded: top-up processed via webhook {"userId":"...","tokens":...}
[timestamp] payment_failed: subscription cancelled {"userId":"...","email":"..."}
```

### Dodo Dashboard Logs

Monitor webhook delivery in the Dodo dashboard:
1. Navigate to **Developer → Webhooks**
2. Click on your webhook
3. View delivery history and retry failed events

## Webhook Event Handlers

### payment_succeeded
- Processes successful one-time payments
- Adds tokens for top-ups
- Clears PAYG billing balance
- Logs the event
- Email sent to user with confirmation

### payment_failed
- Cancels the user's subscription
- Downgrades to hobby plan
- Logs the cancellation reason
- Email sent to user

### payment_cancelled
- Logs cancelled payments
- Email sent to user

### payment_processing
- Logs processing payments

### payment_dispute_created
- Immediately cancels the user's subscription
- Downgrades to hobby plan
- Logs the dispute ID and reason
- Email sent to user

### dispute_accepted
- Handles accepted disputes
- Email sent to user

### dispute_cancelled
- Handles cancelled disputes
- Email sent to user

### dispute_challenged
- Handles challenged disputes
- Email sent to user

### dispute_expired
- Handles expired disputes
- Email sent to user

### dispute_lost
- Handles lost disputes
- Email sent to user

### dispute_won
- Handles won disputes
- Email sent to user

### charge_refunded
- Logs refund events
- No automatic action (manual review recommended)
- Tracks refunded amounts
- Email sent to user

### refund_failed
- Handles failed refunds
- Email sent to user

### subscription_canceled
- Downgrades user to hobby plan
- Clears subscription data
- Logs the cancellation
- Email sent to user

### subscription_payment_failed
- Cancels subscription on recurring payment failure
- Downgrades to hobby plan
- Logs the failure event
- Email sent to user

### subscription_active
- Activates subscription
- Updates billing status
- Email sent to user

### subscription_expired
- Downgrades user to hobby plan
- Logs expiration
- Email sent to user

### subscription_on_hold
- Places subscription on hold
- Updates billing status
- Email sent to user

### subscription_plan_changed
- Updates user plan
- Logs plan change
- Email sent to user

### subscription_renewed
- Marks subscription as renewed
- Updates renewal date
- Email sent to user

### subscription_updated
- Updates subscription details
- Logs updates

## Error Handling

The webhook endpoint:
- Always returns HTTP 200 to acknowledge receipt
- Logs all errors for debugging
- Continues processing even if individual events fail
- Uses timing-safe signature verification

### Common Issues

**Issue**: Invalid signature error
- **Cause**: Webhook secret mismatch
- **Solution**: Verify `DODO_PAYMENTS_WEBHOOK_KEY` matches Dodo dashboard

**Issue**: Webhook not received
- **Cause**: URL not publicly accessible or firewall blocking
- **Solution**: Check URL accessibility and firewall settings

**Issue**: User not found in webhook
- **Cause**: Missing userId in payment metadata
- **Solution**: Ensure checkout sessions include userId in metadata

## Security Best Practices

1. **Always use HTTPS**: Webhooks must use HTTPS in production
2. **Verify signatures**: The application validates webhook signatures
3. **Keep secrets secure**: Never commit webhook secrets to version control
4. **Monitor logs**: Regularly review webhook logs for suspicious activity
5. **Rate limiting**: Dodo handles webhook delivery retries automatically

## Webhook Signature Verification

The application uses HMAC-SHA256 signature verification:

```javascript
const signature = req.headers['dodo-signature'];
const expectedSignature = `sha256=${crypto.createHmac('sha256', DODO_WEBHOOK_KEY).update(rawBody).digest('hex')}`;
```

If verification fails, the webhook returns HTTP 401.

## Testing Different Scenarios

### Test Failed Payment
1. Create a test subscription
2. Use a test card that will fail (e.g., expired card)
3. Observe subscription cancellation in logs

### Test Dispute Handling
1. Create a test payment
2. Create a test dispute in Dodo dashboard
3. Observe subscription cancellation

### Test Refund
1. Create a test payment
2. Process a refund in Dodo dashboard
3. Observe refund logging

## Troubleshooting

### Check Webhook Delivery Status

In Dodo dashboard, view webhook delivery logs for:
- Delivery attempts
- Response codes
- Error messages

### Enable Debug Logging

Add to your environment temporarily:
```bash
LOG_LEVEL=debug
```

### Verify Metadata in Checkout Sessions

Ensure your checkout sessions include metadata:
```javascript
{
  metadata: {
    userId: 'user-uuid',
    type: 'subscription|topup|payg',
    tokens: 100000
  }
}
```

## Production Checklist

- [ ] Webhook secret configured in environment
- [ ] Webhook endpoint accessible via HTTPS
- [ ] All event types enabled in Dodo dashboard
- [ ] Test webhooks sent successfully
- [ ] Application logs show webhook receipt
- [ ] Failed payments trigger subscription cancellation
- [ ] Cancelled payments are logged
- [ ] Processing payments are tracked
- [ ] Disputes trigger subscription cancellation
- [ ] Dispute accepted events are handled
- [ ] Dispute cancelled events are handled
- [ ] Dispute challenged events are handled
- [ ] Dispute expired events are handled
- [ ] Dispute lost events are handled
- [ ] Dispute won events are handled
- [ ] Refunds are logged correctly
- [ ] Failed refunds are handled
- [ ] Subscription cancellations downgrade users correctly
- [ ] Subscription payment failures trigger cancellation
- [ ] Subscription active events activate subscriptions
- [ ] Subscription expired events downgrade users
- [ ] Subscription on hold events are handled
- [ ] Subscription plan changed events update plans
- [ ] Subscription renewed events update renewal dates
- [ ] Subscription updated events update subscription details
- [ ] Email notifications are sent for all appropriate events

## Webhook Reference

### Endpoint
```
POST /webhooks/dodo
```

### Headers
```
Content-Type: application/json
dodo-signature: sha256=...
```

### Event Payload Structure
```json
{
  "id": "evt_abc123",
  "type": "payment_succeeded",
  "data": {
    "id": "pay_xyz789",
    "amount": 7500,
    "currency": "usd",
    "status": "succeeded",
    "metadata": {
      "userId": "user-uuid",
      "type": "subscription"
    }
  },
  "created_at": "2026-01-20T16:30:45Z"
}
```

## Support

- **Dodo Payments Documentation**: https://docs.dodopayments.com
- **Dodo Support**: Contact via Dodo dashboard
- **Application Issues**: Check application logs first, then review this guide

## Updates

Last updated: January 20, 2026