メインコンテンツへスキップ

サブスクリプションのアップグレードまたはダウングレードとは?

プランを変更することで、顧客をサブスクリプションのティアや数量の間で移動させることができます。これを使用して:
  • 使用状況や機能に合わせて価格を調整する
  • 月額から年額に移行する(またはその逆)
  • シートベースの製品の数量を調整する
プラン変更は、選択した按分モードに応じて即時請求を引き起こす可能性があります。

プラン変更を使用するタイミング

  • 顧客がより多くの機能、使用量、またはシートを必要とする場合はアップグレード
  • 使用量が減少した場合はダウングレード
  • サブスクリプションをキャンセルせずに新しい製品や価格にユーザーを移行する

前提条件

サブスクリプションプランの変更を実装する前に、次のことを確認してください:
  • アクティブなサブスクリプション製品を持つDodo Paymentsのマーチャントアカウント
  • ダッシュボードからのAPI認証情報(APIキーとWebhookシークレットキー)
  • 修正するための既存のアクティブなサブスクリプション
  • サブスクリプションイベントを処理するために設定されたWebhookエンドポイント
詳細なセットアップ手順については、統合ガイドを参照してください。

ステップバイステップの実装ガイド

アプリケーションでサブスクリプションプランの変更を実装するための包括的なガイドに従ってください:
1

プラン変更の要件を理解する

実装する前に、次のことを決定します:
  • どのサブスクリプション製品がどの他の製品に変更できるか
  • どの按分モードがビジネスモデルに適しているか
  • 失敗したプラン変更をどのように優雅に処理するか
  • 状態管理のために追跡するWebhookイベント
本番環境に実装する前に、テストモードでプラン変更を徹底的にテストしてください。
2

按分戦略を選択する

ビジネスニーズに合った請求アプローチを選択します:
最適:未使用の時間に対して公正に請求したいSaaSアプリケーション
  • 残りのサイクル時間に基づいて正確な按分額を計算します
  • サイクル内の未使用時間に基づいて按分額を請求します
  • 顧客に透明な請求を提供します
3

プラン変更APIを実装する

プラン変更APIを使用してサブスクリプションの詳細を変更します:
subscription_id
string
required
変更するアクティブなサブスクリプションのID。
product_id
string
required
サブスクリプションを変更する新しい製品ID。
quantity
integer
default:"1"
新しいプランのユニット数(シートベースの製品の場合)。
proration_billing_mode
string
required
即時請求を処理する方法:prorated_immediatelyfull_immediately、またはdifference_immediately
addons
array
新しいプランのオプションのアドオン。これを空にすると、既存のアドオンが削除されます。
4

Webhookイベントを処理する

プラン変更の結果を追跡するためにWebhook処理を設定します:
  • subscription.active: プラン変更成功、サブスクリプションが更新されました
  • subscription.plan_changed: サブスクリプションプランが変更されました(アップグレード/ダウングレード/アドオンの更新)
  • subscription.on_hold: プラン変更の請求に失敗し、サブスクリプションが一時停止されました
  • payment.succeeded: プラン変更の即時請求が成功しました
  • payment.failed: 即時請求に失敗しました
Webhookの署名を常に検証し、冪等性のあるイベント処理を実装してください。
5

アプリケーションの状態を更新する

Webhookイベントに基づいてアプリケーションを更新します:
  • 新しいプランに基づいて機能を付与/取り消す
  • 新しいプランの詳細で顧客ダッシュボードを更新する
  • プラン変更に関する確認メールを送信する
  • 監査目的のために請求の変更をログに記録する
6

テストと監視

実装を徹底的にテストします:
  • 異なるシナリオで全ての按分モードをテストする
  • Webhook処理が正しく機能することを確認する
  • プラン変更の成功率を監視する
  • 失敗したプラン変更のアラートを設定する
サブスクリプションプラン変更の実装は、今や本番環境での使用に準備が整いました。

プラン変更のプレビュー

プラン変更を確定する前に、プレビューAPIを使用して顧客に正確に請求される金額を表示します:
const preview = await client.subscriptions.previewChangePlan('sub_123', {
  product_id: 'prod_pro',
  quantity: 1,
  proration_billing_mode: 'prorated_immediately'
});

// Show customer the charge before confirming
console.log('Immediate charge:', preview.immediate_charge.summary);
console.log('New plan details:', preview.new_plan);
プレビューAPIを使用して、顧客がプラン変更を確認する前に正確な請求額を表示する確認ダイアログを構築します。

プラン変更API

プラン変更APIを使用して、アクティブなサブスクリプションの製品、数量、および按分の動作を変更します。

クイックスタート例

import DodoPayments from 'dodopayments';

const client = new DodoPayments({
  bearerToken: process.env.DODO_PAYMENTS_API_KEY,
  environment: 'test_mode', // defaults to 'live_mode'
});

async function changePlan() {
  const result = await client.subscriptions.changePlan('sub_123', {
    product_id: 'prod_new',
    quantity: 3,
    proration_billing_mode: 'prorated_immediately',
  });
  console.log(result.status, result.invoice_id, result.payment_id);
}

changePlan();
Success
{
  "status": "processing",
  "subscription_id": "sub_123",
  "invoice_id": "inv_789",
  "payment_id": "pay_456",
  "proration_billing_mode": "prorated_immediately"
}
invoice_idpayment_idのようなフィールドは、プラン変更中に即時請求および/または請求書が作成された場合にのみ返されます。結果を確認するには、常にWebhookイベント(例:payment.succeededsubscription.plan_changed)に依存してください。
即時請求が失敗した場合、サブスクリプションはsubscription.on_holdに移行する可能性があります。支払いが成功するまで。

アドオンの管理

サブスクリプションプランを変更する際に、アドオンも変更できます:
// Add addons to the new plan
await client.subscriptions.changePlan('sub_123', {
  product_id: 'prod_new',
  quantity: 1,
  proration_billing_mode: 'difference_immediately',
  addons: [
    { addon_id: 'addon_123', quantity: 2 }
  ]
});

// Remove all existing addons
await client.subscriptions.changePlan('sub_123', {
  product_id: 'prod_new',
  quantity: 1,
  proration_billing_mode: 'difference_immediately',
  addons: [] // Empty array removes all existing addons
});
アドオンは按分計算に含まれ、選択した按分モードに従って請求されます。

按分モード

プラン変更時に顧客に請求する方法を選択します:

prorated_immediately

  • 現在のサイクルの部分的な差額を請求します
  • トライアル中の場合、即時に請求し、今すぐ新しいプランに切り替えます
  • ダウングレード:将来の更新に適用される按分クレジットを生成する可能性があります

full_immediately

  • 新しいプランの全額を即時に請求します
  • 古いプランの残りの時間を無視します
difference_immediatelyを使用して生成されたダウングレードによるクレジットは、サブスクリプションスコープであり、顧客クレジットとは異なります。これらは同じサブスクリプションの将来の更新に自動的に適用され、サブスクリプション間で移転可能ではありません。

difference_immediately

  • アップグレード:古いプランと新しいプランの価格差を即時に請求します
  • ダウングレード:残りの価値をサブスクリプションに内部クレジットとして追加し、更新時に自動的に適用します

例シナリオ

await client.subscriptions.changePlan('sub_123', {
  product_id: 'prod_pro',
  quantity: 1,
  proration_billing_mode: 'difference_immediately'
})
// Immediate charge: $50
await client.subscriptions.changePlan('sub_123', {
  product_id: 'prod_starter',
  quantity: 1,
  proration_billing_mode: 'difference_immediately'
})
// Credit added: $30 (auto-applied to future renewals for this subscription)
await client.subscriptions.changePlan('sub_123', {
  product_id: 'prod_new',
  quantity: 1,
  proration_billing_mode: 'prorated_immediately'
})
// Immediate prorated charge based on remaining days in cycle
await client.subscriptions.changePlan('sub_123', {
  product_id: 'prod_new',
  quantity: 1,
  proration_billing_mode: 'full_immediately'
})
// Immediate full charge for new plan; no credits calculated
公正な時間会計のためにprorated_immediatelyを選択し、請求をリセットするためにfull_immediatelyを選択し、シンプルなアップグレードとダウングレード時の自動クレジットのためにdifference_immediatelyを使用してください。

Webhookの処理

Webhookを通じてサブスクリプションの状態を追跡し、プラン変更と支払いを確認します。

処理するイベントタイプ

  • subscription.active: サブスクリプションがアクティブ化されました
  • subscription.plan_changed: サブスクリプションプランが変更されました(アップグレード/ダウングレード/アドオンの変更)
  • subscription.on_hold: 請求に失敗し、サブスクリプションが一時停止されました
  • subscription.renewed: 更新が成功しました
  • payment.succeeded: プラン変更または更新のための支払いが成功しました
  • payment.failed: 支払いに失敗しました
ビジネスロジックをサブスクリプションイベントから駆動し、確認と調整のために支払いイベントを使用することをお勧めします。

署名を検証し、意図を処理する

import { NextRequest, NextResponse } from 'next/server';

export async function POST(req) {
  const webhookId = req.headers.get('webhook-id');
  const webhookSignature = req.headers.get('webhook-signature');
  const webhookTimestamp = req.headers.get('webhook-timestamp');
  const secret = process.env.DODO_WEBHOOK_SECRET;

  const payload = await req.text();
  // verifySignature is a placeholder – in production, use a Standard Webhooks library
  const { valid, event } = await verifySignature(
    payload,
    { id: webhookId, signature: webhookSignature, timestamp: webhookTimestamp },
    secret
  );
  if (!valid) return NextResponse.json({ error: 'Invalid signature' }, { status: 400 });

  switch (event.type) {
    case 'subscription.active':
      // mark subscription active in your DB
      break;
    case 'subscription.plan_changed':
      // refresh entitlements and reflect the new plan in your UI
      break;
    case 'subscription.on_hold':
      // notify user to update payment method
      break;
    case 'subscription.renewed':
      // extend access window
      break;
    case 'payment.succeeded':
      // reconcile payment for plan change
      break;
    case 'payment.failed':
      // log and alert
      break;
    default:
      // ignore unknown events
      break;
  }

  return NextResponse.json({ received: true });
}
詳細なペイロードスキーマについては、サブスクリプションWebhookペイロード支払いWebhookペイロードを参照してください。

ベストプラクティス

信頼性の高いサブスクリプションプラン変更のために、次の推奨事項に従ってください:

プラン変更戦略

  • 徹底的にテストする:本番環境の前に常にテストモードでプラン変更をテストしてください
  • 按分を慎重に選択する:ビジネスモデルに合った按分モードを選択してください
  • 失敗を優雅に処理する:適切なエラーハンドリングと再試行ロジックを実装してください
  • 成功率を監視する:プラン変更の成功/失敗率を追跡し、問題を調査してください

Webhook実装

  • 署名を検証する:Webhookの署名を常に検証して、信頼性を確保してください
  • 冪等性を実装する:重複したWebhookイベントを優雅に処理してください
  • 非同期で処理する:重い操作でWebhookの応答をブロックしないでください
  • すべてをログに記録する:デバッグと監査目的のために詳細なログを維持してください

ユーザーエクスペリエンス

  • 明確にコミュニケーションする:顧客に請求の変更とタイミングについて通知してください
  • 確認を提供する:成功したプラン変更のために確認メールを送信してください
  • エッジケースを処理する:トライアル期間、按分、失敗した支払いを考慮してください
  • UIを即座に更新する:アプリケーションインターフェースにプラン変更を反映させてください

一般的な問題と解決策

サブスクリプションプラン変更中に発生する典型的な問題を解決します:
症状:API呼び出しは成功するが、サブスクリプションは古いプランのまま一般的な原因
  • Webhook処理が失敗したか遅延した
  • Webhookを受信した後にアプリケーションの状態が更新されなかった
  • 状態更新中のデータベーストランザクションの問題
解決策
  • 再試行ロジックを持つ堅牢なWebhook処理を実装する
  • 状態更新のために冪等性のある操作を使用する
  • 見逃したWebhookイベントを検出し、アラートを設定するための監視を追加する
  • Webhookエンドポイントがアクセス可能で正しく応答していることを確認する
症状:顧客がダウングレードしたが、クレジット残高が表示されない一般的な原因
  • 按分モードの期待:ダウングレードはdifference_immediatelyでプラン価格差の全額をクレジットしますが、prorated_immediatelyはサイクル内の残りの時間に基づいて按分クレジットを生成します
  • クレジットはサブスクリプション固有であり、サブスクリプション間で移転できません
  • 顧客ダッシュボードにクレジット残高が表示されない
解決策
  • 自動クレジットを希望する場合はダウングレードにdifference_immediatelyを使用してください
  • クレジットが同じサブスクリプションの将来の更新に適用されることを顧客に説明してください
  • クレジット残高を表示する顧客ポータルを実装してください
  • 次の請求書プレビューで適用されたクレジットを確認してください
症状:無効な署名のためWebhookイベントが拒否される一般的な原因
  • Webhookシークレットキーが不正確
  • 署名検証の前に生のリクエストボディが変更された
  • 不正な署名検証アルゴリズム
解決策
  • ダッシュボードから正しいDODO_WEBHOOK_SECRETを使用していることを確認してください
  • JSONパースミドルウェアの前に生のリクエストボディを読み取ってください
  • プラットフォーム用の標準Webhook検証ライブラリを使用してください
  • 開発環境でWebhook署名検証をテストしてください
症状:APIが422 Unprocessable Entityエラーを返す一般的な原因
  • 無効なサブスクリプションIDまたは製品ID
  • サブスクリプションがアクティブな状態でない
  • 必須パラメータが不足している
  • プラン変更に利用できない製品
解決策
  • サブスクリプションが存在し、アクティブであることを確認してください
  • 製品IDが有効で利用可能であることを確認してください
  • すべての必須パラメータが提供されていることを確認してください
  • パラメータ要件についてAPIドキュメントを確認してください
症状:プラン変更が開始されたが即時請求が失敗する一般的な原因
  • 顧客の支払い方法に十分な資金がない
  • 支払い方法が期限切れまたは無効
  • 銀行が取引を拒否した
  • 不正検出が請求をブロックした
解決策
  • payment.failedWebhookイベントを適切に処理してください
  • 顧客に支払い方法を更新するよう通知してください
  • 一時的な失敗のために再試行ロジックを実装してください
  • 即時請求が失敗した場合でもプラン変更を許可することを検討してください
症状:プラン変更の請求が失敗し、サブスクリプションがon_hold状態に移行する何が起こるか: プラン変更の請求が失敗すると、サブスクリプションは自動的にon_hold状態に置かれます。支払い方法が更新されるまで、サブスクリプションは自動的に更新されません。解決策:支払い方法を更新してサブスクリプションを再アクティブ化しますプラン変更が失敗した後にon_hold状態からサブスクリプションを再アクティブ化するには:
  1. 支払い方法を更新してUpdate Payment Method APIを使用します
  2. 自動請求の作成:APIが残りの未払い分の請求を自動的に作成します
  3. 請求書の生成:請求のための請求書が生成されます
  4. 支払い処理:新しい支払い方法を使用して支払いが処理されます
  5. 再アクティブ化:支払いが成功すると、サブスクリプションがactive状態に再アクティブ化されます
// Reactivate subscription from on_hold after failed plan change
async function reactivateAfterFailedPlanChange(subscriptionId) {
  // Update payment method - automatically creates charge for remaining dues
  const response = await client.subscriptions.updatePaymentMethod(subscriptionId, {
    type: 'new',
    return_url: 'https://example.com/return'
  });
  
  if (response.payment_id) {
    console.log('Charge created for remaining dues:', response.payment_id);
    console.log('Payment link:', response.payment_link);
    
    // Redirect customer to payment_link to complete payment
    // Monitor webhooks for:
    // 1. payment.succeeded - charge succeeded
    // 2. subscription.active - subscription reactivated
  }
  
  return response;
}

// Or use existing payment method if available
async function reactivateWithExistingPaymentMethod(subscriptionId, paymentMethodId) {
  const response = await client.subscriptions.updatePaymentMethod(subscriptionId, {
    type: 'existing',
    payment_method_id: paymentMethodId
  });
  
  // Monitor webhooks for payment.succeeded and subscription.active
  return response;
}
監視すべきWebhookイベント
  • subscription.on_hold: サブスクリプションが保留されました(プラン変更の請求が失敗したときに受信)
  • payment.succeeded: 残りの未払い分の支払いが成功しました(支払い方法を更新した後)
  • subscription.active: 支払いが成功した後にサブスクリプションが再アクティブ化されました
ベストプラクティス
  • プラン変更の請求が失敗した場合は、顧客に即座に通知してください
  • 支払い方法を更新するための明確な指示を提供してください
  • 再アクティブ化のステータスを追跡するためにWebhookイベントを監視してください
  • 一時的な支払い失敗のために自動再試行ロジックを実装することを検討してください

Update Payment Method APIリファレンス

支払い方法を更新し、サブスクリプションを再アクティブ化するための完全なAPIドキュメントを表示します。

実装のテスト

サブスクリプションプラン変更の実装を徹底的にテストするための手順:
1

テスト環境を設定する

  • テストAPIキーとテスト製品を使用する
  • 異なるプランタイプのテストサブスクリプションを作成する
  • テストWebhookエンドポイントを設定する
  • 監視とログ記録を設定する
2

異なる按分モードをテストする

  • 様々な請求サイクルの位置でprorated_immediatelyをテストする
  • アップグレードとダウングレードのためにdifference_immediatelyをテストする
  • 請求サイクルをリセットするためにfull_immediatelyをテストする
  • クレジット計算が正しいことを確認する
3

Webhook処理をテストする

  • すべての関連Webhookイベントが受信されることを確認する
  • Webhook署名検証をテストする
  • 重複したWebhookイベントを優雅に処理する
  • Webhook処理の失敗シナリオをテストする
4

エラーシナリオをテストする

  • 無効なサブスクリプションIDでテストする
  • 期限切れの支払い方法でテストする
  • ネットワークの失敗やタイムアウトでテストする
  • 不十分な資金でテストする
5

本番環境で監視する

  • 失敗したプラン変更のアラートを設定する
  • Webhook処理時間を監視する
  • プラン変更の成功率を追跡する
  • プラン変更の問題に関する顧客サポートチケットをレビューする

エラーハンドリング

一般的なAPIエラーを実装で優雅に処理します:

HTTPステータスコード

プラン変更リクエストが正常に処理されました。サブスクリプションが更新され、支払い処理が開始されました。
無効なリクエストパラメータ。すべての必須フィールドが提供され、正しくフォーマットされていることを確認してください。
無効または欠落しているAPIキー。DODO_PAYMENTS_API_KEYが正しいことを確認し、適切な権限があることを確認してください。
サブスクリプションIDが見つからないか、あなたのアカウントに属していません。
サブスクリプションを変更できません(例:すでにキャンセルされている、製品が利用できないなど)。
サーバーエラーが発生しました。短い遅延の後にリクエストを再試行してください。

エラー応答フォーマット

{
  "error": {
    "code": "subscription_not_found",
    "message": "The subscription with ID 'sub_123' was not found",
    "details": {
      "subscription_id": "sub_123"
    }
  }
}

次のステップ