Discordスラッシュコマンドからクレカ決済(Stripe)し、ロールを付与するシステム概説
はじめに
- なんらかの決済サービスで有料会員になった人だけ見られるDiscordチャンネル
- Discordサーバーのメンバーだけ見られる外部コンテンツ
決済状況とロールの連動や外部アカウントとDiscordアカウントの連携などを、自動でできたら便利だろうなと思い、試作品を作ってみることにしました。
今回はその全体の流れと、主要な要素について概説しました。
なお、スラッシュコマンドを実行したユーザーに、本人にだけ見えるメッセージを送る処理については、別途以下の記事に書いています。
DiscordのSlash commands返信にて、実行したユーザーにだけ見せる「これらはあなただけに表示されています」を試す、他
そのため、メッセージやDiscordのAPIについての説明をかなり省いています。
前提
開発言語はNode.jsになります。
Discord開発について詳しくない方は先に
Getting Started - Building your first Discord app
あたりを事前にご一読いただくと良いかと思います。
Stripeについても、Stripeや、StripeのSDK自体に関する詳説はしません。
Stripeに馴染みのない人が、ピンポイントで調べやすいよう、多少参考文献の紹介など補足しますが、こちらも別途検索などしていただければと思います。
また、決済に関する法律についても扱っていません。特商法など必要な表示について各自ご確認ください。
処理概要と今回の実装概要
まず全体の流れを説明します。
未契約時(新規、解約後ユーザー)
- ユーザーがスラッシュコマンドを実行する。
- 決済前画面のURLをユーザーに返信する。
※URLは本人識別用のtokenを含み、本人にだけ見られる方法で送信します。
- 決済前画面
- プラン説明
- 特商法などの法定表記
- Stripe Checkoutへのリンク
- ユーザーがStripe Checkoutで手続き
- 決済成功
- 決済失敗・中断
- StripeからWEBサーバーへ決済成功通知
- Discordに対し、role付与処理
- Discord経由で決済完了メッセージ送信
契約中
- ユーザーがスラッシュコマンドを実行する
- StripeカスタマーポータルのURLを送信する
※URLは本人識別用のtokenを含み、本人にだけ見られる方法で送信します。 - ユーザーがStripeカスタマーポータルで手続き
- 契約プランの確認
- 解約
- Stripeから処理結果通知
- Discord APIで、role付与処理
- Discord経由で決済完了メッセージ送信
ユーザー x Discord x WEBサーバー図
上記を一部要素を省いて図解します。
ユーザー x Stripe x WEBサーバー x Discrd図
続けて、それぞの要素について説明していきます。
Stripe Checkout、カスタマーポータル
Stripe Checkout
はノーコードで決済を組み込めるサービスです。
組み込み元のサービスからSDKを使って専用URLを生成し、ユーザーはそこで決済を行います。
決済結果はwebhookなどを通じて通知されます。
Strpe側の管理画面も充実しているため、商品の作成、価格の設定、決済画面のカスタマイズ、顧客情報、決済履歴などStripe管理画面上で確認できます。
逆に、組み込み元のサービスではカード情報や連絡先など不要な個人情報を管理する必要がありません。
Checkoutは主に購入手続きに関する機能ですが、カスタマーポータルは購入後の契約プラン確認・変更・解約、購入履歴確認、カード情報変更などが可能です。
Stripe Webhook
着信する Webhook を使用してリアルタイムで更新を取得する
Webhookでは、指定したイベント(決済の完了、支払いの成功、解約etc)を受信することができます。
Stripe側は多様のイベントを用意してますが、必要なイベントのみを選択して受信し、イベント通知をトリガーに、APIで必要な情報を取得し直しています。
イベント通知の中にも必要な情報は含まれていますが、Webhookの性質上、かならずしも時系列で送信されるわけではありません。遅延、重複、なども想定し、通知をトリガーに最新の情報を取得し直し、必要な処理を実行するようにしています。
※ 夜10:00に新規契約、夜11:00に解約したが、10:00に障害が発生したため、先に解約の通知が届いてしまうということがありえます。
私が今回利用予定のeventは
customer.subscription.*
になります。
サブスクリプション契約に何らかの変更があった場合、都度APIでSubscriptionの情報を取得し、Stripe側の契約状態と手元DBの値を比較し、必要なアクションを実行します。
イベントの詳細については以下などを参考にしてください。
サブスクリプションの Webhook イベント
Stripe SDK、API
実装にあたって必要なSDKをインストールします。
今回はNode.js(サーバーサイドのみ)で利用するので以下を使います。
stripe-node
npm install stripe
今回使用したのは以下の機能になります。
-
CheckoutのURL作成
Stripe Checkout
Checkout, Create a Session -
カスタマーポータルのURL作成
カスタマーポータルを設定します
Create a portal session -
Webhookイベント通知受信
Webhook を使用してアクションをトリガーする -
最新のSubscription(継続課金契約プラン)情報の取得
Retrieve a subscription
実装にあたっては、まず概説しているドキュメントやサンプルコードで概要をつかみ、詳細をAPIドキュメントで確認すると良いでしょう。サンプルコードも言語ごとに変更することができます。
Stripe その他
今回の記事では概要のみ記載し、触れていませんが、実際にはサービス開始後の運用フローも重要です。
サブスクリプション契約の場合、契約翌月の以降の支払い、解約、契約更新失敗(与信、洗い替え)、チャージバック、不正利用対応、洗い替え(カード番号自動更新)、ユーザー問い合わせ、、、
記事ではさらっと書いてしまいましたが、検証すべきパターンは大量にあります。
Stripeでは幸いなことに、開発環境で利用できるテスト決済用のカードが多数あります。
また、時間軸のテストを可能にするテストクロックもあります。
ぜひ活用してみてください。
決済管理テーブル
続けて、StripeとDiscordを紐づけるためのデータ管理についてです。
一式作成し終わった段階で必要な項目を洗い出してみました。
有料会員数が少ない想定で、最初1テーブルで作成したのですが、途中で
- 契約状態管理
- 決済手続き管理
の2テーブルにした方が良い気がしてきました。
他にも、サービスの性質、ユーザー数、利用するDB種別などによってテーブルの構成やデータの持ち方に違いがあるかと思いますので、ご参考程度に。
私も細かいテストをしながら、作り変えていくと思います。
カラム名 | 型 | 説明 |
---|---|---|
id | string | uuid |
discord_guild_id | string | DiscordサーバーID |
discord_user_id | string | DiscordユーザーID |
discord_token | string | Followup Message用のtoken、スラッシュコマンド受信時に格納 |
stripe_user_id | string | Stripe側のユーザー識別子 |
payment_status | int | 0: 未契約 1:契約中 |
onetime_token | string | 決済前画面などアクセス制御に利用する |
expiration_time | int | onetime_tokenの有効期限を管理 |
created_at | int | レコード作成時間 |
updated_at | int | レコード更新時間 |
deleted_at | int | 論理削除管理 |
少し補足すると
- 契約・解約のたびにStripeのユーザーアカウントを作成すると、名寄せや重複契約の恐れがあるので、 discord_user_id+discord_guild_idはユニークキーにした方が良いと思います。
(1ユーザ1レコードのテーブルと、手続きごとに作成されログとして保存されるテーブル) - Checkout前に独自の商品説明ページを経由する想定ですが、これはDiscord外のサイトなので、URLにtokenを付与することでユーザーを識別します。これが外部に漏れるとなりすましができてしまいます。ユーザー本人以外にわからないように送信する想定ですが、さらにリスクを下げるために有効期限を設定します。
roleの追加・削除
roleの追加削除はAPIを使って行います。
Add Guild Member Role
Remove Guild Member Role
パラメタとして、Discordのサーバー、ユーザー、ロールのID、リクエスト送信用のトークンを利用します。
ロールはDiscordの設定で事前に作っておきます。
(APIを使ってもいいと思いますが)
他は、スラッシュコマンド実行時にエンドポイントに送られてくるリクエストから取得し、DBに保存します。
最後に
以上、かなり端折りましたが、Discordのスラッシュコマンドを使った外部決済と、決済に伴うロールの操作を行うシステムについて概要を説明しました。
Stripeについては、サンプルコードを添えて、もう少し丁寧に書こうか迷ったのですが、Stripeの資料(サンプルコード)がだいぶ充実していますし、お盆休みも残りわずか、もう少しやりたいことが残っているので、がっつり省かせていただきました。。。
しばらく自分で使ってみて、大きな改善点や役に立ちそうな知見があれば追記、または新しい記事を書きたいと思います。
またStripe以外のサービスとの連携も試す予定なので、そちらも機会があれば是非読んでいただければと思います。
Discussion