Change Plan API
Plan Change Preview
Integration Guide
What is a subscription upgrade or downgrade?
Changing plans lets you move a customer between subscription tiers or quantities. Use it to:- Align pricing with usage or features
- Move from monthly to annual (or vice versa)
- Adjust quantity for seat-based products
When to use plan changes
- Upgrade when a customer needs more features, usage, or seats
- Downgrade when usage decreases
- Migrate users to a new product or price without cancelling their subscription
Plan Change Flow
Prerequisites
Before implementing subscription plan changes, ensure you have:- A Dodo Payments merchant account with active subscription products
- API credentials (API key and webhook secret key) from the dashboard
- An existing active subscription to modify
- Webhook endpoint configured to handle subscription events
Step-by-Step Implementation Guide
Follow this comprehensive guide to implement subscription plan changes in your application:Understand Plan Change Requirements
- Which subscription products can be changed to which others
- What proration mode fits your business model
- How to handle failed plan changes gracefully
- Which webhook events to track for state management
Choose Your Proration Strategy
- prorated_immediately
- difference_immediately
- full_immediately
- do_not_bill
- Calculates exact prorated amount based on remaining cycle time
- Charges a prorated amount based on unused time remaining in the cycle
- Provides transparent billing to customers
Implement the Change Plan API
prorated_immediately, full_immediately, difference_immediately, or do_not_bill.prevent_change: Keep subscription on current plan until payment succeedsapply_change(default): Apply plan change immediately regardless of payment outcome
preserve_on_plan_change=true와 함께 있으면 (새 제품에 적용 가능한 경우) 기존 할인이 유지됩니다.immediately(기본값): 즉시 플랜 변경 적용next_billing_date: 다음 청구 날짜에 변경 예약. 고객은 청구 기간이 끝날 때까지 현재 플랜을 유지합니다.
next_billing_date를 다운그레이드에 사용하여 고객이 청구 기간이 끝날 때까지 현재 플랜 혜택을 유지할 수 있도록 합니다.Handle Webhook Events
subscription.active: 플랜 변경 성공, 구독 업데이트됨subscription.plan_changed: 구독 플랜 변경 (업그레이드/다운그레이드/애드온 업데이트)subscription.on_hold: 플랜 변경 요금 실패, 구독 일시 중지됨payment.succeeded: 플랜 변경 즉시 요금 성공payment.failed: 즉시 요금 실패
Update Your Application State
- 신규 플랜에 따라 기능 부여/회수
- 고객 대시보드에 신규 플랜 세부사항 업데이트
- 플랜 변경에 관한 확인 이메일 발송
- 감사 목적으로 청구 변경 사항 기록
플랜 변경 미리보기
플랜 변경을 확정하기 전에, 고객에게 정확히 청구될 금액을 보여주기 위해 Preview API를 사용하십시오.- Node.js SDK
- Python SDK
플랜 변경 API
플랜 변경 API를 사용하여 활성 구독의 제품, 수량, 비례 요금 동작을 수정합니다.빠른 시작 예제
- Node.js SDK
- Python SDK
- Go SDK
- HTTP
invoice_id와 payment_id와 같은 필드는 플랜 변경 중에 즉시 요금 청구 및/또는 송장이 생성될 때만 반환됩니다. 항상 웹훅 이벤트 (payment.succeeded, subscription.plan_changed 등)를 통해 결과를 확인하십시오.애드온 관리
구독 플랜을 변경할 때 애드온도 수정할 수 있습니다:할인 코드 적용
구독 플랜 변경 시 할인 코드를 적용할 수 있습니다. 이는 업그레이드 또는 마이그레이션에 대한 프로모션 가격을 제공하는 데 유용합니다.- Node.js SDK
- Python SDK
- HTTP
플랜 변경 시 할인 동작
| 시나리오 | 동작 |
|---|---|
discount_code 제공됨 | 새로운 플랜에 할인을 적용합니다. |
코드가 제공되지 않았으나 preserve_on_plan_change=true와 함께 기존 할인 있음 | 새로운 제품에 적용 가능한 경우, 기존 할인이 자동으로 유지됩니다. |
| 코드가 제공되지 않고, 유지 가능한 할인 없음 | 새로운 플랜에는 할인이 적용되지 않습니다. |
비례 요금 모드
플랜 변경 시 고객에게 청구할 방식을 선택하십시오:prorated_immediately
- 현재 사이클에서 부분 차이에 대한 요금을 청구합니다
- 체험 중이라면 즉시 요금을 부과하고 새 플랜으로 전환합니다
- 다운그레이드: 일부 환불을 후속 갱신에 적용할 수 있는 경우 생성됩니다
full_immediately
- 새 플랜의 전체 금액을 즉시 청구합니다
- 오래된 플랜의 남은 시간을 무시합니다
difference_immediately를 사용하여 다운그레이드할 때 생성된 크레딧은 구독에 범위가 지정되며, 크레딧 기반 청구 권리와는 다릅니다. 이 크레딧은 동일한 구독의 후속 갱신에 자동으로 적용되며, 구독 간에 이전되지 않습니다.difference_immediately
- 업그레이드: 오래된 플랜과 새로운 플랜 간의 가격 차이를 즉시 청구합니다
- 다운그레이드: 남은 가치를 내부 크레딧으로 추가하고 자동 갱신 시 적용합니다
do_not_bill
- 요금이나 크레딧이 계산되지 않습니다
- 고객은 즉시 변경된 플랜으로 전환되며 청구 조정이 없습니다
- 청구 주기는 변하지 않습니다
- 주로 무료 마이그레이션, 무료 플랜 전환, 비용 차이 흡수를 위한 것입니다
| 기능 | prorated_immediately | difference_immediately | full_immediately | do_not_bill |
|---|---|---|---|---|
| 업그레이드 요금 | 남은 일수에 대한 비례 차이 | 플랜 간의 전체 가격 차이 | 새 플랜의 전체 금액 | 요금 없음 |
| 다운그레이드 크레딧 | 남은 일수에 대한 비례 크레딧 | 전체 가격 차이를 크레딧으로 | 크레딧 없음 | 크레딧 없음 |
| 청구 주기 | 변하지 않음 | 변하지 않음 | 오늘로 초기화 | 변하지 않음 |
| 체험 동작 | 체험 종료, 즉시 요금 부과 | 체험 종료, 즉시 요금 부과 | 체험 종료, 전체 금액 부과 | 체험 종료, 요금 없음 |
| 최적사용 | 공정한 시간 기반 청구 | 간단한 업그레이드/다운그레이드 계산 | 청구 주기 초기화 | 무료 마이그레이션 또는 무료 전환 |
| 복잡도 | 중간 (일 계산) | 낮음 (간단한 뺄셈) | 낮음 (전체 청구) | 없음 |
예제 시나리오
다음 기준 숫자를 일관되게 사용하십시오:- 현재 플랜: Basic $30/월
- 업그레이드 목표: Pro $80/월
- 다운그레이드 목표 (Pro에서): Starter $20/월
- 청구 주기: 30일, 1월 1일에 시작
- 플랜 변경 시점: 1월 16일 (남은 15일, 사용한 15일)
Upgrade: Basic ($30) → Pro ($80) with prorated_immediately
Upgrade: Basic ($30) → Pro ($80) with prorated_immediately
Downgrade: Pro ($80) → Starter ($20) with prorated_immediately
Downgrade: Pro ($80) → Starter ($20) with prorated_immediately
Upgrade: Basic ($30) → Pro ($80) with difference_immediately
Upgrade: Basic ($30) → Pro ($80) with difference_immediately
Downgrade: Pro ($80) → Starter ($20) with difference_immediately
Downgrade: Pro ($80) → Starter ($20) with difference_immediately
Upgrade: Basic ($30) → Pro ($80) with full_immediately
Upgrade: Basic ($30) → Pro ($80) with full_immediately
Mid-cycle upgrade with add-ons using prorated_immediately
Mid-cycle upgrade with add-ons using prorated_immediately
각 모드가 청구를 처리하는 방식
결제 실패 처리
플랜 변경 결제가 실패했을 때의 처리를on_payment_failure 매개변수를 사용하여 제어하십시오.
결제 실패 모드
- prevent_change (Recommended for critical upgrades)
- apply_change (Default)
- 플랜 변경은 “보류 중”으로 표시됩니다
- 고객은 현재 플랜에 대한 접근을 유지합니다
- 결제 성공 후에만 구독이
active상태로 이동합니다 - 결제를 보장한 후에 업그레이드 기능을 제공하고자 할 때 유용합니다
on_payment_failure 매개변수는 대시보드에 구성된 비즈니스 수준의 기본 설정을 사용합니다.각 모드의 사용 시기
| 시나리오 | 권장 모드 | 이유 |
|---|---|---|
| 프리미엄 기능으로의 업그레이드 | prevent_change | 접근권 부여 전에 결제 보장 |
| 수량 증가 (더 많은 좌석) | prevent_change | 결제 없이 사용 방지 |
| 플랜 다운그레이드 | apply_change | 고객이 지출을 줄임 |
| 신뢰할 수 있는 기업 고객 | apply_change | 비결제에 대한 위험이 낮음 |
| 체험에서 유료 전환 | prevent_change | 중요한 결제 순간 |
웹훅 처리
웹훅을 통해 구독 상태를 추적하여 플랜 변경 및 결제를 확인합니다.처리해야 할 이벤트 유형
subscription.active: 구독 활성화됨subscription.plan_changed: 구독 플랜 변경 (업그레이드/다운그레이드/애드온 변경)subscription.on_hold: 요금 실패, 구독 일시 중지subscription.renewed: 갱신 성공payment.succeeded: 플랜 변경 또는 갱신에 대한 결제 성공payment.failed: 결제 실패
서명 확인 및 의도 처리
- Next.js Route Handler
- Express.js
모범 사례
안정적인 구독 플랜 변경을 위해 다음 권장 사항을 따르십시오:플랜 변경 전략
- 철저하게 테스트: 프로덕션 전에 항상 테스트 모드에서 플랜 변경을 테스트하세요
- 비례 요금 신중히 선택: 비즈니스 모델에 맞는 비례 요금 모드를 선택하세요
- 실패를 우아하게 처리: 적절한 오류 처리 및 재시도 로직 구현
- 성공률 모니터링: 플랜 변경 성공/실패율을 추적하고 문제를 조사하세요
웹훅 구현
- 서명 확인: 항상 웹훅 서명을 검증하여 진위성을 확인하세요
- 멱등성 구현: 중복된 웹훅 이벤트를 우아하게 처리하세요
- 비동기 처리: 무거운 작업으로 웹훅 응답을 차단하지 마세요
- 모든 것 기록: 디버깅 및 감사 목적으로 세부 로그 유지하세요
사용자 경험
- 명확한 소통: 고객에게 청구 변경 및 시기에 대해 알립니다
- 확인 제공: 성공적인 플랜 변경에 대한 이메일 확인 발송
- 경계 사례 처리: 체험 기간, 비례 요금 및 결제 실패를 고려하세요
- UI 즉시 업데이트: 애플리케이션 인터페이스에서 플랜 변경 반영하세요
일반적인 문제와 해결책
구독 플랜 변경 중에 발생하는 일반적인 문제를 해결하세요:Charge created but subscription not updated
Charge created but subscription not updated
- 웹훅 처리가 실패했거나 지연됨
- 웹훅 수신 후 애플리케이션 상태가 업데이트되지 않음
- 상태 업데이트 중 데이터베이스 트랜잭션 문제
- 견고한 웹훅 처리 및 재시도 로직 구현
- 상태 업데이트에 대한 멱등한 작업 사용
- 놓친 웹훅 이벤트를 감지하고 알림
- 웹훅 엔드포인트가 접근 가능하며 올바르게 응답하는지 확인
Credits not applied after downgrade
Credits not applied after downgrade
- 비례 요금 모드 기대: 다운그레이드는
difference_immediately로 전체 플랜 가격 차이를 크레딧으로 적용하고,prorated_immediately는 사이클에 남은 시간을 기반으로 비례 크레딧을 생성 - 크레딧은 구독에 국한되어 있으며 구독 간에 이전되지 않음
- 고객 대시보드에 크레딧 잔액 표시되지 않음
- 자동 크레딧을 원할 때 다운그레이드에
difference_immediately사용 - 크레딧이 동일한 구독의 미래 갱신에 적용된다고 고객에게 설명
- 크레딧 잔액을 보여주는 고객 포털 구현
- 다음 청구서 미리보기를 확인하여 적용된 크레딧 확인
Webhook signature verification fails
Webhook signature verification fails
- 잘못된 웹훅 시크릿 키
- JSON 파싱 미들웨어 전에 원시 요청 본문이 수정됨
- 잘못된 서명 검증 알고리즘
- 대시보드에서 올바른
DODO_WEBHOOK_SECRET사용을 확인 - 어떤 JSON 파싱 미들웨어 이전에 원시 요청 본문 읽기
- 플랫폼 표준 웹훅 검증 라이브러리 사용
- 개발 환경에서 웹훅 서명 검증 테스트
Plan change fails with 422 error
Plan change fails with 422 error
- 잘못된 구독 ID 또는 제품 ID
- 구독이 활성 상태가 아님
- 필요한 매개변수 누락
- 플랜 변경에 사용할 수 없는 제품
- 구독이 존재하고 활성 상태인지 확인
- 제품 ID가 유효하고 사용 가능한지 확인
- 모든 필요한 매개변수가 제공되었는지 확인
- 매개변수 요구 사항에 대한 API 문서 검토
Immediate charge fails during plan change
Immediate charge fails during plan change
- 고객의 결제 수단에 자금이 부족함
- 결제 수단이 만료되었거나 유효하지 않음
- 은행이 거래를 거부함
- 사기 탐지가 요금을 차단함
payment.failed웹훅 이벤트를 적절히 처리- 고객에게 결제 수단 업데이트 알림
- 일시적 실패에 대한 재시도 로직 구현
- 실패한 즉시 요금에도 플랜 변경 허용 고려
Subscription on hold after plan change
Subscription on hold after plan change
on_hold 상태로 이동발생 상황:
플랜 변경 요금이 실패하면 구독이 자동으로 on_hold 상태로 전환됩니다. 결제 수단이 업데이트될 때까지 구독은 자동으로 갱신되지 않습니다.해결책: 결제 수단을 업데이트하여 구독을 재활성화플랜 변경 실패 후 on_hold 상태에서 구독을 다시 활성화하려면:- 결제 수단 업데이트 Update Payment Method API 사용
- 자동 요금 생성: API가 자동으로 남은 금액에 대해 요금을 생성
- 청구서 생성: 요금에 대한 송장 생성
- 결제 처리: 새로운 결제 수단으로 결제를 처리
- 재활성화: 결제가 성공하면 구독이
active상태로 재활성화
subscription.on_hold: 구독 일시 정지 (플랜 변경 요금 실패 시 수신)payment.succeeded: 남은 금액에 대한 결제가 성공 (결제 수단 업데이트 후)subscription.active: 성공적인 결제 후 구독 재활성화
- 플랜 변경 요금이 실패할 때 즉시 고객에게 알림
- 결제 수단을 업데이트하는 방법에 대한 명확한 지침 제공
- 재활성화 상태를 추적하기 위해 웹훅 이벤트 모니터링
- 일시적인 결제 실패에 대한 자동 재시도 로직 구현 고려
Update Payment Method API Reference
구현 테스트
구독 플랜 변경 구현을 철저히 테스트하려면 다음 단계를 따르십시오:Test different proration modes
- 다양한 청구 주기 위치에서
prorated_immediately테스트 - 업그레이드 및 다운그레이드에
difference_immediately테스트 - 청구 주기를 재설정하기 위해
full_immediately테스트 - 요금 없음/크레딧 없음 플랜 전환에
do_not_bill테스트 - 크레딧 계산이 정확한지 확인
오류 처리
구현에서 일반적인 API 오류를 우아하게 처리하세요:HTTP 상태 코드
200 OK
200 OK
400 Bad Request
400 Bad Request
401 Unauthorized
401 Unauthorized
404 Not Found
404 Not Found
422 Unprocessable Entity
422 Unprocessable Entity
500 Internal Server Error
500 Internal Server Error