419 lines
11 KiB
Markdown
419 lines
11 KiB
Markdown
# 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 |