# 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