初回無料プラン登録
説明
初回無料プラン登録機能は、アクティブなサブスクリプションプランを持たないユーザーまたはグループが無料サービスプランに登録できるようにします。自動登録とは異なり、システムはユーザーがログインした際に無料プランへの登録を提案するモーダルを表示します。このプロセスはStripeと統合され、顧客情報とサブスクリプションを管理し、内部データベースで正確な状態を維持します。
この機能は、新規ユーザーがスムーズにシステムの利用を開始できるようにするために特に重要であり、支払い情報を必要とせず、システムの基本機能へのアクセスをすぐに提供します。
前提条件:
- ユーザーがシステムに正常にログインしている。
- ユーザーに関連するグループにアクティブなサブスクリプションがない。
- ユーザーがグループのcreator(所有者)である。
プロセスフロー図
---
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"]
LoginController[LoginController]
SubscriptionController[SubscriptionController]
WebhookController[WebhookController]
end
%% API Service Layer
subgraph ApiServiceLayer["API Service Layer"]
AuthService(AuthService)
SubscriptionService(SubscriptionService)
end
%% Business Logic Services
subgraph BusinessServices["Business Logic Services"]
StripeService(StripeService)
end
%% Database Layer
subgraph DatabaseLayer["Database Layer"]
UsersDB[(users)]
GroupsDB[(groups)]
SubscriptionDB[(subscriptions)]
HistoryDB[(subscription_histories)]
PackageDB[(packages)]
PackagePlanDB[(package_plans)]
WebhookEventsDB[(stripe_webhook_events)]
end
%% External Services
subgraph ExternalServices["External Services"]
StripeAPI((Stripe API))
end
%% Login Flow
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 --> LoginController
LoginController --- 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 --> AuthService
AuthService --- 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'>グループとサブスクリプション確認</p>
</div>
]
Step3 --> UsersDB
Step3 --> GroupsDB
Step3 --> SubscriptionDB
AuthService --- Step4[
<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'>4</span>
<p style='margin-top: 8px'>無料プランモーダル表示フラグ設定</p>
</div>
]
Step4 --> LoginController
LoginController --- Step5[
<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'>5</span>
<p style='margin-top: 8px'>ログイン結果を返す</p>
</div>
]
Step5 --> Client
%% Registration Flow
Client --- Step6[
<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'>6</span>
<p style='margin-top: 8px'>無料プラン登録</p>
</div>
]
Step6 --> SubscriptionController
SubscriptionController --- Step7[
<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'>7</span>
<p style='margin-top: 8px'>ユーザー権限確認</p>
</div>
]
Step7 --> UsersDB
Step7 --> GroupsDB
SubscriptionController --- Step8A[
<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'>8A</span>
<p style='margin-top: 8px'>Stripe顧客確認/作成</p>
</div>
]
Step8A --> StripeService
StripeService --> StripeAPI
SubscriptionController --- Step8B[
<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'>8B</span>
<p style='margin-top: 8px'>無料プラン情報取得</p>
</div>
]
Step8B --> PackageDB
Step8B --> PackagePlanDB
SubscriptionController --- Step9[
<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'>9</span>
<p style='margin-top: 8px'>サブスクリプションレコード作成</p>
</div>
]
Step9 --> SubscriptionService
SubscriptionService --- Step10[
<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'>10</span>
<p style='margin-top: 8px'>サブスクリプション情報保存</p>
</div>
]
Step10 --> SubscriptionDB
Step10 --> HistoryDB
SubscriptionService --- Step11[
<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'>11</span>
<p style='margin-top: 8px'>Stripeでサブスクリプション作成</p>
</div>
]
Step11 --> StripeService
StripeService --> StripeAPI
SubscriptionController --- Step12[
<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'>12</span>
<p style='margin-top: 8px'>登録結果を返す</p>
</div>
]
Step12 --> Client
%% Webhook Flow
StripeAPI --- StepW1[
<div style='text-align: center'>
<span style='display: inline-block; background-color: #ff9966 !important; color:white; width: 28px; height: 28px; line-height: 28px; border-radius: 50%; font-weight: bold'>W1</span>
<p style='margin-top: 8px'>webhookイベント送信</p>
</div>
]
StepW1 --> WebhookController
WebhookController --- StepW2[
<div style='text-align: center'>
<span style='display: inline-block; background-color: #ff9966 !important; color:white; width: 28px; height: 28px; line-height: 28px; border-radius: 50%; font-weight: bold'>W2</span>
<p style='margin-top: 8px'>webhook認証</p>
</div>
]
StepW2 --> StripeService
WebhookController --- StepW3[
<div style='text-align: center'>
<span style='display: inline-block; background-color: #ff9966 !important; color:white; width: 28px; height: 28px; line-height: 28px; border-radius: 50%; font-weight: bold'>W3</span>
<p style='margin-top: 8px'>webhookイベント保存</p>
</div>
]
StepW3 --> WebhookEventsDB
WebhookController --- StepW4[
<div style='text-align: center'>
<span style='display: inline-block; background-color: #ff9966 !important; color:white; width: 28px; height: 28px; line-height: 28px; border-radius: 50%; font-weight: bold'>W4</span>
<p style='margin-top: 8px'>イベント処理</p>
</div>
]
StepW4 --> SubscriptionService
SubscriptionService --- StepW5[
<div style='text-align: center'>
<span style='display: inline-block; background-color: #ff9966 !important; color:white; width: 28px; height: 28px; line-height: 28px; border-radius: 50%; font-weight: bold'>W5</span>
<p style='margin-top: 8px'>サブスクリプション状態更新</p>
</div>
]
StepW5 --> SubscriptionDB
StepW5 --> HistoryDB
%% 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 Step3 fill:transparent,stroke:transparent,stroke-width:1px
style Step4 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 Step7 fill:transparent,stroke:transparent,stroke-width:1px
style Step8A fill:transparent,stroke:transparent,stroke-width:1px
style Step8B fill:transparent,stroke:transparent,stroke-width:1px
style Step9 fill:transparent,stroke:transparent,stroke-width:1px
style Step10 fill:transparent,stroke:transparent,stroke-width:1px
style Step11 fill:transparent,stroke:transparent,stroke-width:1px
style Step12 fill:transparent,stroke:transparent,stroke-width:1px
style StepW1 fill:transparent,stroke:transparent,stroke-width:1px
style StepW2 fill:transparent,stroke:transparent,stroke-width:1px
style StepW3 fill:transparent,stroke:transparent,stroke-width:1px
style StepW4 fill:transparent,stroke:transparent,stroke-width:1px
style StepW5 fill:transparent,stroke:transparent,stroke-width:1px
ユースケース
ケース1: ログインと無料プランモーダル表示
説明
ユーザーがログインすると、システムはユーザーのグループにアクティブなサブスクリプションがなく、ユーザーがcreatorである場合、無料プランへの登録を提案するモーダルを表示します。
シーケンス図
sequenceDiagram
participant User as ユーザー
participant LC as LoginController
participant AS as AuthService
participant DB as データベース
Note over User,DB: ログインと無料プランモーダル表示
rect rgb(200, 255, 200)
Note right of User: Happy Case Flow
User->>LC: ログイン情報送信
rect rgb(200, 230, 255)
Note right of LC: ユーザー認証
LC->>AS: ユーザー認証処理
AS->>DB: ユーザー情報照会
DB-->>AS: ユーザーデータ返却
end
rect rgb(200, 255, 255)
Note right of AS: グループとサブスクリプション確認
AS->>DB: グループとサブスクリプション照会
DB-->>AS: グループ・サブスクリプションデータ
Note over AS: ユーザーがcreator かつ<br/>アクティブなサブスクリプションなし
AS->>AS: show_free_plan_modal = true
end
AS-->>LC: 認証結果返却
LC-->>User: 認証成功レスポンス<br/>(show_free_plan_modal=true)
end
rect rgb(255, 200, 200)
Note right of User: エラー処理
rect rgb(255, 230, 230)
alt ログイン情報が無効
AS-->>LC: 認証失敗
LC-->>User: 401 Unauthorized
else ユーザーが存在しない
DB-->>AS: ユーザーが見つからない
AS-->>LC: ユーザーが見つからない
LC-->>User: 401 Unauthorized
end
end
end
ステップ
ステップ1: ログイン
- 説明: ユーザーがログイン情報を送信
- 認証: データベースでログイン情報を確認
- 可能性のあるエラー: ログイン情報が正しくない
ステップ2: グループとサブスクリプション状態の確認
- 説明: システムがユーザーのグループとサブスクリプション状態を確認
- アクション:
- ユーザーのmembershipsからグループ情報を取得
- グループにアクティブなサブスクリプションがあるか確認
- ユーザーがcreatorであるか確認
ステップ3: モーダル表示フラグの設定
- 説明: アクティブなサブスクリプションがなく、ユーザーがcreatorの場合
- アクション: ログイン応答に
show_free_plan_modal = trueフラグを設定
ステップ4: ログイン結果の返却
- 説明: モーダル表示フラグ付きの成功ログイン結果を返す
- レスポンス:
- 成功: ユーザー情報と
show_free_plan_modalフラグを含む200 OK - エラー: 適切なエラーコードとメッセージ
- 成功: ユーザー情報と
ケース2: 無料プラン登録
説明
ユーザーがモーダルまたはサービスリストページから無料プランを登録します。
シーケンス図
sequenceDiagram
participant User as ユーザー
participant SC as SubscriptionController
participant SS as SubscriptionService
participant StS as StripeService
participant DB as データベース
participant StripeAPI as Stripe API
Note over User,StripeAPI: 無料プラン登録プロセス
rect rgb(200, 255, 200)
Note right of User: Happy Case Flow
User->>SC: POST /api/v1/general/subscription/free-plan
rect rgb(255, 255, 200)
Note right of SC: ユーザー権限確認
SC->>DB: ユーザー権限確認
DB-->>SC: ユーザー/グループデータ
end
alt ユーザーがcreatorでない
SC-->>User: 403エラー (権限なし)
else creatorである
rect rgb(255, 230, 200)
Note right of SC: Stripe顧客処理
SC->>SS: firstRegisterFreePlanSubscription()
alt ユーザーにStripe顧客IDがない
SS->>StS: Stripe顧客ID同期
StS->>StripeAPI: 顧客作成
StripeAPI-->>StS: 顧客データ
StS-->>SS: 更新されたユーザー
end
SS->>StripeAPI: アクティブなサブスクリプション確認
StripeAPI-->>SS: サブスクリプションリスト
end
alt Stripeにアクティブなサブスクリプションが存在
SS-->>SC: エラーレスポンス
SC-->>User: 409エラー (競合)
else サブスクリプションなし
rect rgb(230, 200, 255)
Note right of SS: プラン情報取得
SS->>DB: 無料プラン情報取得
DB-->>SS: パッケージ/プランデータ
end
rect rgb(200, 255, 255)
Note right of SS: サブスクリプション作成
SS->>DB: トランザクション開始
SS->>DB: サブスクリプションレコード作成
SS->>DB: サブスクリプション履歴作成
DB-->>SS: 作成結果
SS->>StS: Stripeサブスクリプション作成
StS->>StripeAPI: サブスクリプションAPI呼び出し
StripeAPI-->>StS: サブスクリプションデータ
StS-->>SS: Stripe処理結果
alt Stripe処理失敗
SS->>DB: トランザクションロールバック
SS-->>SC: エラーレスポンス
SC-->>User: 500エラー
else 成功
SS->>DB: トランザクションコミット
SS-->>SC: 成功レスポンス
SC-->>User: 200 OK (サブスクリプションデータ)
end
end
end
end
end
rect rgb(255, 200, 200)
Note right of User: エラー処理
rect rgb(255, 230, 230)
alt 無料プランが見つからない
DB-->>SS: プラン情報なし
SS-->>SC: プラン情報なし
SC-->>User: 404 Not Found
else 既にアクティブなサブスクリプションあり
DB-->>SS: アクティブなサブスクリプション
SS-->>SC: サブスクリプション競合
SC-->>User: 409 Conflict
else Stripe APIエラー
StripeAPI-->>StS: APIエラー
StS-->>SS: Stripeエラー
SS-->>SC: APIエラー
SC-->>User: 500 Internal Server Error
end
end
end
ステップ
ステップ1: 登録リクエスト送信
- 説明: ユーザーが無料プラン登録リクエストを送信
- リクエスト:
POST /api/v1/general/subscription/free-plan - 認証:
- ユーザーがログインしていることを確認
- ユーザーがグループのcreatorであることを確認
ステップ2: Stripe顧客の確認/作成
- 説明: システムがStripe顧客を確認または新規作成
- アクション:
- ユーザーがすでにStripe顧客IDを持っている場合、アクティブなサブスクリプションを確認
- 持っていない場合、新しいStripe顧客を作成し、ユーザープロフィールを更新
- Stripeにアクティブなサブスクリプションがある場合、システムにログを記録しSlackに通知を送信する可能性がある
ステップ3: 無料プラン情報の取得
- 説明: システムがデータベースから無料プラン情報を取得
- アクション:
- slugで無料プランを検索
- 関連するpackage_planとprovider情報を取得
ステップ4: サブスクリプションレコードの作成
- 説明: データベースに新しいサブスクリプションレコードを作成
- アクション:
- データの一貫性を確保するためのトランザクション開始
- subscriptionsテーブルにレコードを作成
- subscription_historiesテーブルにレコードを作成
- ユーザーとグループ情報を更新
ステップ5: Stripeでのサブスクリプション作成
- 説明: Stripeで無料プランサブスクリプションを作成
- アクション:
- 必要なパラメータでStripe APIを呼び出して無料サブスクリプションを作成
- Stripeからのレスポンスを確認し、返されたステータスに応じて処理
- 作成に失敗した場合、エラーをログに記録しSlackに通知を送信する可能性がある
ステップ6: 結果の返却
- 説明: システムが登録成功結果を返す
- レスポンス:
- 成功: サブスクリプション情報を含む
200 OK - エラー: 適切なエラーコードとメッセージ
- 成功: サブスクリプション情報を含む
ケース3: Stripeからのウェブフック処理
説明
無料プラン登録後、Stripeはサブスクリプション状態についてのウェブフックイベントを送信します。システムはこれらのイベントを処理して、データベース内のサブスクリプション状態を更新する必要があります。
シーケンス図
sequenceDiagram
participant StripeAPI as Stripe API
participant WC as WebhookController
participant StS as StripeService
participant SS as SubscriptionService
participant DB as データベース
Note over StripeAPI,DB: Stripeウェブフック処理
rect rgb(200, 255, 200)
Note right of StripeAPI: Happy Case Flow
StripeAPI->>WC: POST /api/v1/admin/stripe/webhook
Note over StripeAPI,WC: イベントデータ (customer.subscription.created など)
rect rgb(255, 255, 200)
Note right of WC: ウェブフック認証
WC->>StS: webhook署名検証
StS-->>WC: 検証結果
end
alt 署名無効
WC-->>StripeAPI: 403エラー
else 署名有効
rect rgb(230, 200, 255)
Note right of WC: イベント保存
WC->>DB: webhookイベント保存 (pending状態)
DB-->>WC: 保存結果
WC->>WC: イベントタイプ判別
end
rect rgb(200, 255, 255)
Note right of WC: イベント処理
alt subscription.created / subscription.updated
WC->>SS: サブスクリプション処理
SS->>DB: サブスクリプション照会
DB-->>SS: サブスクリプションデータ
SS->>DB: サブスクリプション状態更新
SS->>DB: 履歴レコード追加
DB-->>SS: 更新結果
SS-->>WC: 処理結果
else invoice.paid
WC->>SS: インボイス処理
SS->>DB: サブスクリプション照会
DB-->>SS: サブスクリプションデータ
SS->>DB: サブスクリプション支払い状態更新
SS->>DB: 履歴レコード更新
DB-->>SS: 更新結果
SS-->>WC: 処理結果
end
end
rect rgb(255, 230, 200)
Note right of WC: 完了処理
WC->>DB: webhookイベント状態更新 (completed)
DB-->>WC: 更新結果
WC-->>StripeAPI: 200 OK
end
end
end
rect rgb(255, 200, 200)
Note right of StripeAPI: エラー処理
rect rgb(255, 230, 230)
alt 既に処理済みイベント
DB-->>WC: イベント既存
WC-->>StripeAPI: 409 Conflict
else サブスクリプションが見つからない
DB-->>SS: サブスクリプションなし
SS-->>WC: 検索エラー
WC->>DB: webhookイベント状態更新 (failed)
WC-->>StripeAPI: 404 Not Found
else データベースエラー
DB-->>SS: データベースエラー
SS-->>WC: エラー結果
WC->>DB: webhookイベント状態更新 (failed, エラーメッセージ)
WC-->>StripeAPI: 500 Internal Server Error
end
end
end
ステップ
ステップ1: ウェブフックイベントの受信
- 説明: Stripeが設定されたエンドポイントにウェブフックイベントを送信
- リクエスト:
POST /api/v1/admin/stripe/webhook - データ: Stripeからのイベント情報(event_type, data)
ステップ2: ウェブフック認証
- 説明: ウェブフックの有効性を確認
- アクション:
- Stripeシークレットでウェブフック署名を検証
- イベントタイプ(event_type)を確認
- 重複処理を防止
ステップ3: ウェブフックイベントの保存
- 説明: イベント情報をデータベースに保存
- アクション:
- stripe_webhook_eventsテーブルにレコードを作成
- 初期状態を'pending'に設定
ステップ4: サブスクリプションイベントの処理
- 説明: イベントタイプに基づいて処理
- アクション:
customer.subscription.createdイベント: 新規サブスクリプションを確認customer.subscription.updatedイベント: サブスクリプション情報を更新customer.subscription.deletedイベント: サブスクリプションをキャンセル済みとマーク
ステップ5: サブスクリプション状態の更新
- 説明: データベース内のサブスクリプション情報を更新
- アクション:
- subscriptionsテーブルでサブスクリプション状態を更新
- subscription_historiesテーブルに新しいレコードを作成
- ウェブフックイベントを正常に処理済みとマーク
関連データベース
erDiagram
users {
bigint id PK "主キー"
string name "ユーザー名"
string email "ユーザーメール (unique)"
string uid "ユーザーUID"
string payment_provider_customer_id "Stripe顧客ID (nullable)"
integer status "ユーザー状態"
boolean is_first_login "初回ログインフラグ"
timestamp created_at "作成日時"
timestamp updated_at "更新日時"
timestamp deleted_at "ソフト削除日時 (nullable)"
}
groups {
bigint id PK "主キー"
string name "グループ名"
bigint created_by FK "作成者ID (users)"
integer status "グループ状態"
timestamp created_at "作成日時"
timestamp updated_at "更新日時"
}
group_roles {
bigint id PK "主キー"
string name "ロール名"
string slug "ロールスラッグ (unique)"
timestamp created_at "作成日時"
timestamp updated_at "更新日時"
}
group_members {
bigint id PK "主キー"
bigint user_id FK "usersテーブルへの関連付け"
bigint group_id FK "groupsテーブルへの関連付け"
bigint group_role_id FK "group_rolesテーブルへの関連付け"
boolean is_creator "グループ作成者フラグ"
timestamp joined_at "参加日時"
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{ group_members : has
groups ||--o{ group_members : has
group_roles ||--o{ group_members : has
users ||--o{ subscriptions : registers
groups ||--o{ subscriptions : has
subscriptions ||--o{ subscription_histories : has
関連APIエンドポイント
| Method | Endpoint | Controller | 説明 |
|---|---|---|---|
| POST | /api/v1/general/auth/login | AuthController@login | ログインとサブスクリプション状態の確認 |
| POST | /api/v1/general/subscription/free-plan | SubscriptionController@registerFreePlan | 無料プランの登録 |
| GET | /api/v1/general/subscription/active | SubscriptionController@getActiveSubscription | アクティブなサブスクリプションの確認 |
| POST | /api/v1/admin/stripe/webhook | WebhookController@handleWebhook | Stripeからのウェブフックイベントを処理 |
| GET | /api/v1/general/packages/free-plan | PackageController@getFreePlan | 無料プラン情報の取得 |
| GET | /api/v1/general/subscription/status | SubscriptionController@getCurrentStatus | 現在のサブスクリプション状態の取得 |
エラー処理
-
ログ:
- すべての登録エラーはアプリケーションログに記録される
- Stripe APIエラーは詳細情報と共にログに記録される
- Webhookエラーはstripe_webhook_eventsテーブルのerrorフィールドに保存される
- 重大なケースでは、Slackに通知が送信される
-
エラー情報:
| エラーコード | エラーメッセージ | 説明 |
|---|---|---|
| 400 | "グループには既にアクティブなサブスクリプションがあります。" | 無料プラン登録の必要なし |
| 403 | "ユーザーはグループのcreatorではありません。" | プラン登録権限なし |
| 404 | "無料プランが見つかりません。" | システムに無料プランが存在しない |
| 409 | "Stripeにアクティブなサブスクリプションが既に存在します。" | Stripeで既存のサブスクリプションを検出 |
| 409 | "Webhookイベントは既に処理されています。" | 重複Webhookイベント |
| 422 | "無効なデータ: ..." | 入力データ検証エラー |
| 500 | "Stripe APIエラー: ..." | Stripe API呼び出し時のエラー |
| 500 | "データベースエラー: ..." | データベース操作時のエラー |
追加注意事項
- 無料プラン登録はグループにアクティブなサブスクリプションがない場合にのみ実行できます。
- ユーザーは無料プランを登録するためにグループのcreatorである必要があります(group_membersテーブルのis_creator = true)。
- すべてのStripeおよびデータベース操作は、一貫性を確保するためにトランザクションとして実行されます。
- 無料プラン登録プロセスでは、ユーザーからの支払い情報は不要です。
- 無料プランには通常、メンバー数、製品数、利用可能な機能に制限があります。
- システムは内部データベースとStripeの両方をチェックすることで、重複登録がないことを確認します。
- Stripeからのwebhookはstripe_webhook_eventsテーブルを通じて重複処理を防止するメカニズムで処理されます。
- 無料プランの自動更新はデフォルトで有効になっていますが、無料プランのため実際の影響はありません。