サブスクリプションモジュール
説明
サブスクリプションモジュールは、ユーザーが無料プランと様々な機能を持つ有料プランを含む、さまざまなサービスパッケージを選択し、登録することを可能にします。このモジュールはStripeと統合されており、決済処理、サブスクリプションサイクルの管理、およびその他の支払い関連タスクを処理します。
各サービスパッケージ(package)は、異なる価格と支払いサイクルを持つ複数のパッケージプラン(package_plan)を持つことができます。ユーザーは特定のパッケージプランを選択して登録し、システムはデータベースとStripe上で対応するサブスクリプションを作成します。
主な機能
-
無料プランの登録
- ユーザーがリソース数の制限付きで無料サービスプランを登録できる
- 支払い情報は不要で、登録時に自動的に確認される
- ユースケース:基本機能でシステムを試したい新規ユーザー
-
有料プランの登録
- 様々な価格と特典を持つ複数のサービスプランをサポート
- 安全な支払い処理のためのStripe統合
- ユーザーをStripeの支払いページに自動的にリダイレクト
- ユースケース:製品やメンバーの制限を増やすなどの高度な機能が必要な企業
-
Stripeからのウェブフックの処理
- Stripeからのイベント(支払い成功、失敗など)を受信して処理
- Stripeからの情報に基づいてシステム内のサブスクリプションステータスを更新
- 支払い履歴とイベントの記録
- ユースケース:ユーザーが支払いを完了すると、システムはサービスを自動的に有効化する必要がある
-
サブスクリプション管理
- ユーザーが現在のプラン情報を確認可能
- サービスプラン間のアップグレード/ダウングレードをサポート
- 支払いサイクルの変更を処理
- ユースケース:顧客が使用制限を増やすために上位プランにアップグレードしたい
-
サブスクリプションのキャンセル
- ユーザーがサービスプランをキャンセルできる
- 即時キャンセルまたは支払いサイクル終了時のキャンセルをサポート
- 分析のためのキャンセル理由の記録
- ユースケース:サービス利用の必要がなくなった顧客
-
サブスクリプション履歴の追跡
- サブスクリプションの変更と支払いの履歴を保存
- 新規作成、アップグレード、ダウングレード、キャンセルなどのイベントの詳細を表示
- ユースケース:管理者が支払い履歴やプラン変更を確認する必要がある
-
自動更新
- 期限切れ時のサブスクリプションの自動更新をサポート
- Stripeを通じた定期支払いの処理
- ユースケース:顧客へのサービス中断を防止
-
リソース制限の管理
- 各プランはメンバー数、製品数、製品グループなどに独自の制限を持つ
- サブスクリプションプランに基づく機能へのアクセス制御
- ユースケース:顧客のニーズと予算に応じたサービスの階層化
-
支払い失敗の処理
- 支払いが失敗した場合のユーザーへの通知
- 支払いの再試行をサポート
- サービスキャンセル前の猶予期間の提供
- ユースケース:顧客のクレジットカードが期限切れまたは残高不足
-
レポートと統計
- プラン別のサブスクリプション数に関する情報の提供
- 収益とコンバージョン率の追跡
- ユースケース:ビジネスの効果と利用傾向の分析
このモジュールは、登録プロセス中のシームレスなユーザーエクスペリエンス、データベース内のサブスクリプションステータスの管理、およびStripeからのウェブフックイベントの効率的な処理を保証します。
プロセスフロー概要図
---
config:
theme: base
layout: dagre
flowchart:
curve: linear
htmlLabels: true
themeVariables:
edgeLabelBackground: "transparent"
---
flowchart TD
%% Main components
Client[ユーザー]
%% API Controller Layer
subgraph ApiControllerLayer["API Controller Layer"]
SubscriptionController[SubscriptionController]
WebhookController[WebhookController]
Middleware{Middleware}
end
%% API Service Layer
subgraph ApiServiceLayer["API Service Layer"]
SubscriptionService(SubscriptionService)
end
%% Business Logic Services
subgraph BusinessServices["Business Logic Services"]
StripeService(StripeService)
end
%% Database Layer
subgraph DatabaseLayer["Database Layer"]
SubscriptionDB[(subscriptions)]
UsersDB[(users)]
HistoryDB[(subscription_histories)]
PackageDB[(packages)]
PackagePlanDB[(package_plans)]
PackagePlanToProviderDB[(package_plan_to_providers)]
end
%% External Services
subgraph ExternalServices["External Services"]
StripeAPI((Stripe API))
end
%% Flow Steps
Client --- Step1[
<div style='text-align: center'>
<span style='display: inline-block; background-color: #6699cc !important; color:white; width: 28px; height: 28px; line-height: 28px; border-radius: 50%; font-weight: bold'>1</span>
<p style='margin-top: 8px'>サービスプラン選択</p>
</div>
]
Step1 --> SubscriptionController
SubscriptionController --- Step2[
<div style='text-align: center'>
<span style='display: inline-block; background-color: #6699cc !important; color:white; width: 28px; height: 28px; line-height: 28px; border-radius: 50%; font-weight: bold'>2</span>
<p style='margin-top: 8px'>ユーザー&データ認証</p>
</div>
]
Step2 --> Middleware
Middleware --> Step2Output[
<div style='text-align: center'>
<span style='display: inline-block; background-color: #6699cc !important; color:white; width: 28px; height: 28px; line-height: 28px; border-radius: 50%; font-weight: bold'>2'</span>
<p style='margin-top: 8px'>認証結果</p>
</div>
]
Step2Output --> SubscriptionController
SubscriptionController --- Step3[
<div style='text-align: center'>
<span style='display: inline-block; background-color: #6699cc !important; color:white; width: 28px; height: 28px; line-height: 28px; border-radius: 50%; font-weight: bold'>3</span>
<p style='margin-top: 8px'>package & package_plan情報取得</p>
</div>
]
Step3 --> PackagePlanDB
PackagePlanDB --> PackageDB
PackagePlanDB --> PackagePlanToProviderDB
SubscriptionController --- Step3b[
<div style='text-align: center'>
<span style='display: inline-block; background-color: #6699cc !important; color:white; width: 28px; height: 28px; line-height: 28px; border-radius: 50%; font-weight: bold'>3'</span>
<p style='margin-top: 8px'>Serviceにデータ転送</p>
</div>
]
Step3b --> SubscriptionService
SubscriptionService --- Step4A[
<div style='text-align: center'>
<span style='display: inline-block; background-color: #99cc66 !important; color:white; width: 28px; height: 28px; line-height: 28px; border-radius: 50%; font-weight: bold'>4A</span>
<p style='margin-top: 8px'>無料プラン権限付与</p>
</div>
]
Step4A --> SubscriptionDB
Step4A --> HistoryDB
Step4A --> UsersDB
SubscriptionService --- Step4B[
<div style='text-align: center'>
<span style='display: inline-block; background-color: #cc66cc !important; color:white; width: 28px; height: 28px; line-height: 28px; border-radius: 50%; font-weight: bold'>4B</span>
<p style='margin-top: 8px'>有料プラン処理</p>
</div>
]
Step4B --> StripeService
StripeService --- Step5[
<div style='text-align: center'>
<span style='display: inline-block; background-color: #cc66cc !important; color:white; width: 28px; height: 28px; line-height: 28px; border-radius: 50%; font-weight: bold'>5</span>
<p style='margin-top: 8px'>決済セッション作成</p>
</div>
]
Step5 --> StripeAPI
StripeAPI --- Step6[
<div style='text-align: center'>
<span style='display: inline-block; background-color: #cc66cc !important; color:white; width: 28px; height: 28px; line-height: 28px; border-radius: 50%; font-weight: bold'>6</span>
<p style='margin-top: 8px'>webhookイベント送信</p>
</div>
]
Step6 --> WebhookController
WebhookController --- Step7A[
<div style='text-align: center'>
<span style='display: inline-block; background-color: #6699cc !important; color:white; width: 28px; height: 28px; line-height: 28px; border-radius: 50%; font-weight: bold'>7A</span>
<p style='margin-top: 8px'>webhook認証</p>
</div>
]
Step7A --> StripeService
WebhookController --- Step7B[
<div style='text-align: center'>
<span style='display: inline-block; background-color: #cc66cc !important; color:white; width: 28px; height: 28px; line-height: 28px; border-radius: 50%; font-weight: bold'>7B</span>
<p style='margin-top: 8px'>webhookイベント処理</p>
</div>
]
Step7B --> SubscriptionService
SubscriptionService --- Step8[
<div style='text-align: center'>
<span style='display: inline-block; background-color: #6699cc !important; color:white; width: 28px; height: 28px; line-height: 28px; border-radius: 50%; font-weight: bold'>8</span>
<p style='margin-top: 8px'>サブスクリプション状態更新</p>
</div>
]
Step8 --> SubscriptionDB
Step8 --> HistoryDB
Client --- Step9[
<div style='text-align: center'>
<span style='display: inline-block; background-color: #6699cc !important; color:white; width: 28px; height: 28px; line-height: 28px; border-radius: 50%; font-weight: bold'>9</span>
<p style='margin-top: 8px'>サブスクリプション管理</p>
</div>
]
Step9 --> SubscriptionController
%% Styling
style Client fill:#e6f3ff,stroke:#0066cc,stroke-width:2px
style ApiControllerLayer fill:#e6f3ff,stroke:#0066cc,stroke-width:2px
style ApiServiceLayer fill:#f0f8e6,stroke:#339933,stroke-width:2px
style BusinessServices fill:#f5f0ff,stroke:#9966cc,stroke-width:2px
style DatabaseLayer fill:#ffe6cc,stroke:#ff9900,stroke-width:2px
style ExternalServices fill:#fcd9d9,stroke:#cc3333,stroke-width:2px
style Step1 fill:transparent,stroke:transparent,stroke-width:1px
style Step2 fill:transparent,stroke:transparent,stroke-width:1px
style Step2Output fill:transparent,stroke:transparent,stroke-width:1px
style Step3 fill:transparent,stroke:transparent,stroke-width:1px
style Step3b fill:transparent,stroke:transparent,stroke-width:1px
style Step4A fill:transparent,stroke:transparent,stroke-width:1px
style Step4B fill:transparent,stroke:transparent,stroke-width:1px
style Step5 fill:transparent,stroke:transparent,stroke-width:1px
style Step6 fill:transparent,stroke:transparent,stroke-width:1px
style Step7A fill:transparent,stroke:transparent,stroke-width:1px
style Step7B fill:transparent,stroke:transparent,stroke-width:1px
style Step8 fill:transparent,stroke:transparent,stroke-width:1px
style Step9 fill:transparent,stroke:transparent,stroke-width:1px
機能リスト
| 名前 | リンク | 説明 |
|---|---|---|
| サービスプラン登録 | サービスプラン登録 | 無料と有料プランを含む新しいサービスプラン登録プロセス |
| サブスクリプション管理 | サブスクリプション管理 | サービスプランの更新、アップグレード、ダウングレード、キャンセル |
| サブスクリプション履歴 | サブスクリプション履歴 | サブスクリプション変更の追跡と表示 |
| Stripeウェブフック処理 | ウェブフック処理 | サブスクリプション状態を更新するためのStripeウェブフックイベントの処理 |
関連データベース
erDiagram
users {
bigint id PK "主キー"
string name "ユーザー名"
string email "ユーザーメール (unique)"
string uid "ユーザーUID (unique)"
string payment_provider_customer_id "Stripe顧客ID (nullable)"
integer status "ユーザー状態"
string remember_token "ログイン記憶トークン (nullable)"
timestamp created_at "作成日時"
timestamp updated_at "更新日時"
timestamp deleted_at "ソフト削除日時 (nullable)"
}
packages {
bigint id PK "主キー"
string name "パッケージ名"
string slug "パッケージスラッグ (unique)"
text description "パッケージ説明 (nullable)"
text image "画像パス (nullable)"
integer schedule_id "スケジュールID"
integer schedule_priority "スケジュール優先度"
integer max_member "最大メンバー数 (nullable)"
integer max_product_group "最大製品グループ数 (nullable)"
integer max_product "最大製品数 (nullable)"
integer max_category "最大カテゴリ数 (nullable)"
integer max_search_query "最大検索クエリ数 (nullable)"
integer max_viewpoint "最大ビューポイント数 (nullable)"
string data_visible "データ表示可能性"
tinyint api_available "API利用可能性"
tinyint status "パッケージ状態"
timestamp created_at "作成日時"
timestamp updated_at "更新日時"
}
package_plans {
bigint id PK "主キー"
string name "プラン名"
string slug "プランスラッグ (unique)"
bigint package_id FK "packagesテーブルへの関連付け"
double amount "金額"
string currency "通貨"
string type "プランタイプ (recurring, one_time)"
string billing_plan "支払いサイクル"
tinyint status "プラン状態"
timestamp created_at "作成日時"
timestamp updated_at "更新日時"
}
package_plan_to_providers {
bigint id PK "主キー"
bigint package_plan_id FK "package_plansテーブルへの関連付け"
bigint provider_id FK "payment_providersテーブルへの関連付け"
string provider_price_id "プロバイダー価格ID (Stripe)"
tinyint status "状態"
timestamp created_at "作成日時"
timestamp updated_at "更新日時"
}
subscriptions {
bigint id PK "主キー"
string slug "サブスクリプションスラッグ (nullable, unique)"
bigint package_id FK "packagesテーブルへの関連付け"
bigint package_plan_id FK "package_plansテーブルへの関連付け"
bigint group_id FK "groupsテーブルへの関連付け"
bigint user_id FK "usersテーブルへの関連付け"
string email "ユーザーメール"
string payment_provider_customer_id "決済プロバイダー顧客ID"
string payment_provider_subscription_id "決済プロバイダーサブスクリプションID (nullable, unique)"
boolean auto_renew "自動更新"
string status "サブスクリプション状態"
string scheduled_plan_id "予定プラン変更ID (nullable)"
timestamp scheduled_plan_change_at "プラン変更予定日時 (nullable)"
timestamp canceled_at "キャンセル日時 (nullable)"
string canceled_reason "キャンセル理由 (nullable)"
timestamp first_register_at "初回登録日時 (nullable)"
timestamp deadline_at "期限 (nullable)"
timestamp grace_period_end_at "猶予期間終了日時 (nullable)"
timestamp created_at "作成日時"
timestamp updated_at "更新日時"
timestamp deleted_at "ソフト削除日時 (nullable)"
}
subscription_histories {
bigint id PK "主キー"
bigint subscription_id FK "subscriptionsテーブルへの関連付け"
bigint package_id FK "packagesテーブルへの関連付け"
bigint package_plan_id FK "package_plansテーブルへの関連付け"
bigint group_id FK "groupsテーブルへの関連付け"
bigint user_id FK "usersテーブルへの関連付け"
string old_plan_id "旧プランID (nullable)"
string payment_intent_id "Stripe支払いインテントID (nullable, unique)"
string invoice_id "Stripe請求書ID (nullable, unique)"
string billing_plan "支払いサイクル"
integer payment_attempt "支払い試行回数 (nullable)"
json payment_method_details "支払い方法詳細 (nullable)"
string type "サブスクリプションタイプ"
decimal amount "金額"
string currency "通貨"
integer schedule_id "スケジュールID"
integer schedule_priority "スケジュール優先度"
integer product_group_usage "使用製品グループ数"
integer product_usage "使用製品数"
integer category_usage "使用カテゴリ数"
integer search_query_usage "使用検索クエリ数"
integer max_member "最大メンバー数"
integer max_product_group "最大製品グループ数"
integer max_product "最大製品数"
integer max_category "最大カテゴリ数"
integer max_search_query "最大検索クエリ数"
integer max_viewpoint "最大ビューポイント数"
string data_visible "データ表示可能性"
tinyint api_available "API利用可能性"
tinyint status "状態"
string payment_status "支払い状態"
timestamp started_at "開始日時 (nullable)"
timestamp expires_at "期限切れ日時 (nullable)"
timestamp paid_at "支払い日時 (nullable)"
timestamp created_at "作成日時"
timestamp updated_at "更新日時"
}
stripe_webhook_events {
bigint id PK "主キー"
string stripe_event_id "Stripeイベント識別子"
string request_id "リクエストID (nullable)"
string event_type "イベントタイプ"
enum status "状態 (pending, processing, completed, failed)"
text error "エラー (nullable)"
timestamp processed_at "処理日時 (nullable)"
timestamp created_at "作成日時"
timestamp updated_at "更新日時"
}
users ||--o{ subscriptions : has
packages ||--o{ package_plans : has
package_plans ||--o{ package_plan_to_providers : has
packages ||--o{ subscriptions : associated_with
package_plans ||--o{ subscriptions : associated_with
package_plan_to_providers ||--o{ subscriptions : used_for
subscriptions ||--o{ subscription_histories : has
関連APIエンドポイント
| Method | Endpoint | Controller | 説明 |
|---|---|---|---|
| GET | /api/general/subscription | SubscriptionController@index | 現在のユーザーのサブスクリプションリストを取得 |
| POST | /api/general/subscription | SubscriptionController@store | 新しいサービスプランを登録 |
| GET | /api/general/subscription/active | SubscriptionController@getActiveSubscription | 現在のサブスクリプション情報を取得 |
| PUT | /api/general/subscription/{id} | SubscriptionController@update | サブスクリプションプランを更新/変更 |
| POST | /api/general/subscription/free-plan | SubscriptionController@registerFreePlan | 無料プランを登録 |
| GET | /api/admin/group/packages | PackageController@index | 利用可能なサービスパッケージのリストを取得 |
| GET | /api/general/package-plan | PackagePlanController@index | サービスパッケージプランのリストを取得 |
| GET | /api/general/checkout | CheckoutController@index | 支払い情報を取得 |
| POST | /api/general/checkout | CheckoutController@store | 新しい支払いセッションを作成 |
| POST | /api/general/stripe/checkout/session | StripeController@createCheckoutSession | Stripe支払いセッションを作成 |
| POST | /api/general/stripe/portal/session | StripeController@createPortalSession | Stripeポータルセッションを作成 |
| POST | /api/admin/stripe/webhook | WebhookController@handleWebhook | Stripeからのウェブフックイベントを処理 |