プリアンブル
私の目的は、サブスクリプションを管理するために、複数のプロジェクト用の再利用可能なコードを作成する(そしてそれをgithubで公開する)ことです。ストライプおよび定期的な請求プロバイダーについては知っていますが、それはこのモジュールの目的ではありません。これは、アカウントの残高を計算するためのラッパー/ヘルパー、サブスクリプションを更新するための簡単な通知、および価格計算の処理にすぎません。
プロバイダーや支払いの可能性が不十分であるか、サポートされていないか、高すぎる(マイクロペイメント)ため、定期請求を使用できない国があります。また、定期的な請求を使用したくないが、手動で請求する場合や、年末に請求書を提出する場合もあります。そのため、Paypalの定期的な請求、定期的、または同様のサービスを提案しないでください。
状況
サブスクリプションプランにサブスクライブできるモデルがあるとしましょう(例:User
)。このモデルには、現在購読している購読プランの識別子を格納するフィールドがあります。したがって、すべての計画の変更で、変更が記録されます。
上記の変更を記録する次のフィールドを持つモデル(SubscriptionPlanChanges
など)があります。
subscriber
(この場合はUser
)from_plan
モデルが変更前に持っていたプラン識別子を定義するto_plan
モデルが現在選択しているプラン識別子を定義していますcreated_at
は、変更を格納する日時フィールドですvalid_until
は、実際のサブスクリプションが有効になるまで日付を保存しますpaid_at
は、サブスクリプションが支払われたかどうか(およびいつ支払われたか)を定義する日時フィールドでもあります。もちろん、そのレイアウトは議論の余地があります。
口座残高の質問
ユーザーがサブスクリプションプランを変更するとき、プランフィールドを比較し、料金を取得し、現在のプランのvalid_until
とその価格に基づいて新しいプランの控除を計算する必要があります。説明:1年間のプランAを定期購入しましたが、6か月後にプランBにアップグレードしたので、6か月のプランAの支払額の半分が差し引かれます。
私が不思議に思っていること:ユーザーが無料プランに切り替えると、ユーザーが再び切り替えたい場合に差し引かれるクレジットがあります。その値を追加フィールドにキャッシュしますか、それともそのユーザーに関連するすべてのレコードを毎回計算しますか?テーブルのレイアウトについて何か追加/変更しますか?
わかりやすさの質問
サブスクリプション期間の終了時に、ユーザーは通知を受け、再度支払うことによりサブスクリプションを更新することができます。最も簡単な方法は、paid_at
とvalid_until
を新しいサブスクリプションオプションで更新することです。ただし、支払い/サブスクリプションの履歴など、誰かが必要とする可能性のあるすべてのデータを保存しているかどうかはわかりません。
別のオプションは、このための追加のレコードを作成することです。ここで、from_plan
とto_plan
は同じ識別子を持っています(したがって、「変更なし」を表します)。しかし、それは何らかの方法で口座残高の計算を妨げませんか?
そのようなサブスクリプションを処理するロジックについて誰かが私に正しい方向を示すことができたら、私はそれを非常に感謝します。
[〜#〜]更新[〜#〜]
これまでに助けてくれてありがとう。私の質問は曖昧すぎると思うので、抽象化を少なくして、より正確にしようと思います。残念ながら、まだ問題は解決できていません。
ケースAUser
はSubscription Plan A
を選択できます。これは現在SubscriptionPlanChange
を保存して追跡します。例: 5か月後、User
はサブスクリプションをSubscription Plan B
にアップグレードします。そこで、彼は新しいサブスクリプションの料金を支払い、未使用の7か月分のプランaの価格を差し引きます。
ケースB
3か月後、User
は彼のSubscription Plan A
にロールバックします。彼は支払う必要はありませんが、その残高を受け取ります。そのため、サブスクリプションの終了時に、その残高が新しいサブスクリプションから差し引かれます。
ケースCUser
は、独立したサブスクリプションプランを持つサブサービスのサブスクリプションプランを選択できます。同じCase A
およびCase B
は、そのサブサービスサブスクリプションに適用できます。
_Case D_
ユーザーがサブスクリプションの1つをキャンセルしました。これは彼のバランスのトップアップをもたらします。
私の質問(現在、少なくとも)は、主にそのデータを適切に保存する方法に依存しているため、ビジネス分析のためにサブスクリプションの履歴を再現し、残高を計算し、サブスクリプションに基づいて未払い料金を取得できます。
また、残高を次の場所に保管する必要があるかどうかもわかりません。ユーザー自身のモデル、または保存されていないが、保存されたデータ/履歴に基づいていつでも計算できる場合。
注意すべき点がいくつかありますが、問題が生じるとは思いません。
User
である必要はなく、何でもかまいません。そのため、Subscriber
は多態的ですPlans
は必ずしも計画である必要はありませんが、たとえば、言及したようにMagazines
。これは、Case CおよびCase Dで説明したものです。残念ながら、複雑な問題に対する答えは通常複雑です。私へのアドバイスは、関連する情報のみを保存し、モデルを使用して全体像を構築することです。
つまり、テーブルSubscriptionPlanChangesには、そのキーに関する次の情報が含まれます。
このようにして、オーバーラップする可能性がある同じサブスクライバーに対して複数のプランを許可します。その他のフィールドは次のとおりです。
「計画開始」または「計画終了」がないことに注意してください。それらを使用することもできますが、情報は不必要であり、自分で計算することができます(このような情報を保存すると、一貫性を保つための追加のタスクが必要になります)。新しい計画が始まると、既存の計画を変更する必要はなく、そのままにして新しいレコードを追加するだけです。新しいプランの後に別の重複するプランが存在する場合は、そのプランを削除することもできます(この方法の方が直感的です)。サブスクライバーのこれらのプランをロードするときは、それらの「有効開始」日付でソートします。
これを取得すると、ユーザーのクレジットの計算は比較的簡単になります。 2つのプランが重複できない場合は、終了日を決定するために、前のプランの「有効期限」日付と現在のプランの「有効期限」日付の間の2つの日付のうち小さい方を使用するだけです。開始日は、「有効開始」日と「支払期限」日(定義されている場合)の間の2つの日付のうち大きい方です。その後、支払い(またはクレジット)は、そのプランの上記の開始日と終了日の間の時間間隔を掛けたレートで計算できます。
このようにして、理論的には、あなたが知る必要があるものは何でも計算することができます。既存のレコードが変更、追加、または削除されると変更されるため、計算値を保存しようとしないことをお勧めします。
これらの値の計算方法のバリエーションは、タイプフィールドを追加することで管理できます。コードでは、特別なハンドラーを作成して、特定のプランを計算するロジックを管理し、メインアルゴリズムを複雑な計算から比較的明確に保つことができます。型が指定されていない場合のハンドラーを作成できればさらに良いので、必要な計算を行うために、その型に応じて適切なハンドラーを呼び出すだけです。
それがあなたの質問に答えることを願っています。
上記の回答に加えて、クレジットを含むテーブルを作成します。この場合、クレジットは現在の通貨と等しくなります。ユーザーがプランをより安い代替案に切り替えるたびに、未使用の残高がクレジットとして入力されます。ユーザーが支払うものを持っているときはいつでも、最初にクレジットを使用し、クレジットがなくなるか存在しない場合にのみ支払いを要求します。この代替手段を使用する場合、紛争が発生した場合に使用シナリオを再現できるように、トランザクションリストとしてテーブルを作成します。例:
ID、UserId、TransactionDate、Credit(ユーザーにクレジットを与える場合は正、ユーザーがクレジットを使用する場合は負)
クレジットを合計して、ユーザーに残高を表示します。
これがあなたに役立つことを願っています...