158 lines
6.2 KiB
Markdown
158 lines
6.2 KiB
Markdown
# Dodo Payments Plan Change & Cancellation Fix
|
|
|
|
## Summary
|
|
|
|
Fixed the subscription management system to properly handle plan changes and cancellations using the correct Dodo Payments API endpoints.
|
|
|
|
## Issues Fixed
|
|
|
|
### 1. **Paid-to-Paid Plan Changes**
|
|
- **Problem**: When users switched between paid plans (e.g., Starter → Professional), the app was creating a new checkout session instead of using Dodo's Change Plan API
|
|
- **Solution**: Implemented `changeDodoSubscriptionPlan()` function that uses `POST /subscriptions/{subscription_id}/change-plan` with `difference_immediately` proration mode
|
|
- **Behavior**:
|
|
- Upgrades: Charges the price difference for the current billing period
|
|
- Downgrades: Applies remaining value as credit to future renewals
|
|
- No service interruption during the change
|
|
|
|
### 2. **Paid-to-Free Downgrades**
|
|
- **Problem**: Downgrading to the free "Hobby" plan wasn't properly cancelling the Dodo subscription
|
|
- **Solution**: Enhanced the logic to call `cancelDodoSubscription()` when switching from a paid plan to hobby
|
|
- **Behavior**: Subscription is cancelled with Dodo, user downgraded to free tier immediately
|
|
|
|
### 3. **Cancel Button**
|
|
- **Problem**: Cancel button was calling general account update endpoint which may not have properly cancelled the Dodo subscription
|
|
- **Solution**: Already uses dedicated `/api/subscription/cancel` endpoint which properly calls `cancelDodoSubscription()`
|
|
- **Behavior**: Cancels subscription at Dodo and marks billing status as cancelled
|
|
|
|
## Technical Implementation
|
|
|
|
### Server-Side Changes (`server.js`)
|
|
|
|
#### 1. New Function: `changeDodoSubscriptionPlan()`
|
|
```javascript
|
|
async function changeDodoSubscriptionPlan(user, newPlan, billingCycle, currency)
|
|
```
|
|
- Uses Dodo's official Change Plan API endpoint
|
|
- Handles proration automatically
|
|
- Proper error handling and logging
|
|
- Updates subscription product while maintaining continuity
|
|
|
|
#### 2. Updated: `handleAccountSettingsUpdate()`
|
|
Enhanced to handle three scenarios:
|
|
- **Paid → Paid**: Calls `changeDodoSubscriptionPlan()` with Dodo API
|
|
- **Paid → Free**: Calls `cancelDodoSubscription()` and downgrades to hobby
|
|
- **Free → Paid**: Returns error requiring checkout flow (correct behavior)
|
|
|
|
### Frontend Changes (`settings.html`)
|
|
|
|
#### Updated: `saveAccount()` Function
|
|
Completely restructured to handle different plan change scenarios:
|
|
|
|
1. **Paid-to-Paid Changes**:
|
|
- Calls `/api/account` which uses Change Plan API
|
|
- Shows appropriate confirmation with proration explanation
|
|
- Immediate update without checkout
|
|
|
|
2. **Paid-to-Free Downgrades**:
|
|
- Warns user about feature loss
|
|
- Cancels subscription via `/api/account`
|
|
- Immediate downgrade
|
|
|
|
3. **Free-to-Paid Upgrades**:
|
|
- Redirects to checkout flow (correct)
|
|
- Uses `/api/subscription/checkout`
|
|
|
|
4. **Other Settings**:
|
|
- Simple account updates (email, currency)
|
|
|
|
## Dodo Payments API Integration
|
|
|
|
### Change Plan API
|
|
**Endpoint**: `POST /subscriptions/{subscription_id}/change-plan`
|
|
|
|
**Request Body**:
|
|
```json
|
|
{
|
|
"product_id": "prod_xxx",
|
|
"quantity": 1,
|
|
"proration_billing_mode": "difference_immediately"
|
|
}
|
|
```
|
|
|
|
**Proration Mode**: `difference_immediately`
|
|
- **Upgrades**: Immediate charge for price difference
|
|
- **Downgrades**: Credit applied to subscription for future renewals
|
|
- **Benefits**: Simple, fair billing without complex proration calculations
|
|
|
|
### Cancel Subscription API
|
|
**Endpoints Used**:
|
|
1. `DELETE /subscriptions/{subscription_id}` (immediate cancellation)
|
|
2. `PATCH /subscriptions/{subscription_id}` with `cancel_at_next_billing_date: true` (fallback)
|
|
|
|
The implementation tries immediate cancellation first, then falls back to end-of-period cancellation if needed.
|
|
|
|
## User Experience Improvements
|
|
|
|
### Clear Messaging
|
|
- Upgrade messages explain immediate charges
|
|
- Downgrade messages explain credit application
|
|
- Free plan changes warn about feature loss
|
|
|
|
### Seamless Transitions
|
|
- Paid-to-paid changes happen instantly without checkout
|
|
- No service interruption during plan changes
|
|
- Proper proration handled by Dodo
|
|
|
|
### Confirmation Dialogs
|
|
- All plan changes require explicit confirmation
|
|
- Different messages for upgrades vs downgrades
|
|
- Warning icons for cancellations and downgrades
|
|
|
|
## Testing Checklist
|
|
|
|
- [ ] **Upgrade** (Starter → Professional): Verify immediate charge for difference
|
|
- [ ] **Downgrade** (Professional → Starter): Verify credit applied to account
|
|
- [ ] **Cancel** (any paid → hobby): Verify Dodo subscription cancelled
|
|
- [ ] **Free to Paid** (hobby → starter): Verify checkout flow triggered
|
|
- [ ] **Cancel Button**: Verify subscription cancelled at Dodo
|
|
- [ ] **Webhook Handling**: Verify `subscription.plan_changed` webhook updates user plan
|
|
|
|
## Environment Variables Required
|
|
|
|
All existing Dodo environment variables remain the same:
|
|
- `DODO_PAYMENTS_API_KEY`
|
|
- `DODO_PAYMENTS_ENV` (test/live)
|
|
- Subscription product IDs for each plan/cycle/currency combination
|
|
|
|
## API Endpoints Modified
|
|
|
|
### `POST /api/account`
|
|
Now handles three plan change scenarios differently:
|
|
1. Paid→Paid: Uses Change Plan API
|
|
2. Paid→Free: Cancels subscription
|
|
3. Free→Paid: Returns error requiring checkout
|
|
|
|
### `POST /api/subscription/cancel`
|
|
Already working correctly - cancels Dodo subscription properly
|
|
|
|
## Webhook Events
|
|
|
|
The system now properly responds to:
|
|
- `subscription.plan_changed`: Confirms plan change succeeded
|
|
- `subscription.canceled`: Confirms cancellation
|
|
- `payment.succeeded`: Confirms upgrade payment
|
|
- `subscription.on_hold`: Handles failed plan change payments
|
|
|
|
## Documentation References
|
|
|
|
- [Dodo Subscription Upgrade & Downgrade Guide](https://docs.dodopayments.com/developer-resources/subscription-upgrade-downgrade)
|
|
- [Change Plan API Reference](https://docs.dodopayments.com/api-reference/subscriptions/change-plan)
|
|
- [Update Subscription API](https://docs.dodopayments.com/api-reference/subscriptions/patch-subscriptions)
|
|
|
|
## Notes
|
|
|
|
- The `difference_immediately` proration mode is ideal for most SaaS applications
|
|
- Credits from downgrades are automatically applied by Dodo to future renewals
|
|
- The Change Plan API uses existing payment method - no new payment collection needed
|
|
- All plan changes are logged for audit purposes
|