Restore to commit 74e578279624c6045ca440a3459ebfa1f8d54191
This commit is contained in:
419
dodo-webhooks.md
Normal file
419
dodo-webhooks.md
Normal file
@@ -0,0 +1,419 @@
|
||||
# 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
|
||||
Reference in New Issue
Block a user