モンストWebショップにおける継続決済プラットフォームの設計と実装
はじめに
モンストWebショップは、モンスターストライクのアプリ外課金を実現するためのサービスです。モンスターストライクでは月額制会員サービス「モンパス」を展開しており、2025年4月11日よりモンストWebショップ上で「モンパス」の購入が可能となりました。
本記事では、モンストWebショップ上でのモンパスの販売を実現するために構築した、継続決済プラットフォームの設計と実装について説明します。特に、設計検討段階での技術選定の判断や、実装時に直面した技術的課題とその解決策に焦点を当てて紹介します。
継続決済プラットフォームの自社構築について
モンパスの実装にあたり、以下の要件がありました。
- 複数サービスへの展開: モンストWebショップ以外にも、社内の複数サービスで継続課金機能が必要になる可能性
- 柔軟なビジネスロジック: サービスごとに異なる課金ロジックやユーザー体験を実現したい
- PCI DSS準拠: クレジットカード番号を保持して決済するため、PCI DSS準拠であるセキュアな基盤が必要
既存の決済代行サービスも検討しましたが、柔軟性の観点・リソース活用の観点などから、PCI DSS準拠済みの社内の既存決済基盤を活用することにしました。
既存サービスのサーベイと方針選択
既存サービスの調査
設計を始めるにあたり、まず主要な継続決済サービスの仕組みを調査しました。その結果、継続決済システムには大きく分けて2つの設計方針があるとわかりました。
1. 提携サービス側でサブスクリプションを管理
この方針を採用する場合、継続決済は以下のような流れで動作します。
- APIでサブスクリプションobjectを作成
- ユーザーがサブスクリプション決済に同意
- 提携サービスが定期的にバッチを実行し、決済objectを作成
- 決済結果に応じて提携サービスが契約の継続・停止を判断
メリット:
- 責務が明確に分離されている(決済代行は決済のみ、契約管理は提携サービス)
- 決済代行側の実装がシンプル
- サービス固有のビジネスロジックを柔軟に実装できる
デメリット:
- 各提携サービスで実装工数が大きい
- サブスクリプション管理のロジックが各サイトで重複する
- 提携サービスへのコンサルティングやトラブルシューティングの工数が増加
2. 決済代行側でサブスクリプションを管理
この方針を採用する場合、継続決済の流れは以下です。
- APIでサブスクリプションobjectを作成
- 決済代行が定期的にサブスクリプション決済する
- 課金結果をwebhookで提携サービスに通知
- 提携サービスは通知を受けてビジネスロジックを実行
メリット:
- 提携サービスの実装工数が少ない
- サブスクリプション管理のロジックを一元化できる
- 新しいサービスを容易に追加できる
デメリット:
- サブスクリプション管理が複雑化する
- サービス固有の要件への対応が難しくなる可能性
- 管理ダッシュボードの提供が必要になる可能性
設計方針の選択
今回は以下の理由から、「決済代行側でサブスクリプションを管理」=「決済プラットフォーム側で管理」する方針を採用しました。
1. 複数サービスへの展開を見据えている
継続決済プラットフォームは、モンストWebショップ以外にも、社内の複数サービスへの提供を計画していました。各サービスチームが独自にサブスクリプション管理を実装する場合、以下の問題が発生する懸念を持っていました。
- サブスクリプション管理のロジックが各サービスで重複
- 各サービスチームへのコンサルティング工数が増大
- トラブルシューティングやデバッグの難易度が上がる
2. 既存実装を活用できる
既に社内の別サービス[1]で残高ベースの継続課金実装があり、それを拡張する形で効率的に開発できる見込みがありました。
トレードオフの考慮
決済プラットフォーム側でサブスクリプション管理する選択にはトレードオフがあります。サブスクリプションの状態管理はビジネスロジックの一部であり、サービスごとに存在するすべての要件を満たそうとすると、過度に複雑化するリスクがあります。
そのため、以下の方針で進めることにしました。
- 最大公約数的な機能に絞る: 既存サービスの設計を参考に、共通的な機能に絞る
- Webhookで拡張可能にする: サービス固有のロジックはwebhookで各サービスに委譲(例:決済成功時に特定のゲーム内アイテムを付与するなど)
- 段階的に拡張: 初期はモンパスで必要な機能に絞り、他サービス対応時に拡張
技術的課題と解決策
実装を進める中で、いくつかの技術的課題に直面しました。ここでは主要な課題とその解決策を紹介します。
課題1: 決済失敗時のハンドリング
問題
既存の残高ベースの継続課金では、残高不足なら即座に解約という単純なロジックでした。しかし、モンパスではそのような単純なロジックを採用できない、以下のような問題がありました。
- ユーザー体験の課題: モンパスには契約を継続することによる特典があるため、クレジットカードの有効期限切れなどの一時的な問題で契約を解約すると、ユーザー体験が著しく悪化する
- 通信障害による失敗: クレジットカード決済を想定しているため、外部の決済プラットフォームとの通信状態によっては、一時的な決済失敗が起こり得る
解決策: unpaid状態と支払い猶予期間
サブスクリプションの取りうる状態として「契約継続中であるが未課金」状態(unpaidステータス)を導入しました。この状態のサブスクリプションは、「支払い猶予期間」中であるとみなされます。
支払い猶予期間の仕組み:
- 決済失敗から自動解約まで一定期間(例:3日間)の猶予を設ける
- 猶予期間中は毎日再課金を試行
- 猶予期間中に支払い方法を変更した場合、即座にオーソリを実行
- 猶予期限を過ぎても決済失敗の場合、自動解約
猶予期間中は、次回課金日を表すパラメータを翌日に設定し、翌日の継続課金試行で自動的に再課金します。この仕組みにより、クレジットカードの有効期限切れなど一時的な問題でも、ユーザーが対処する時間を確保できるようになりました。
課題2: タイムゾーンの問題
問題
継続課金は「毎月○日に課金」という日ベースの管理が必要です。既存の実装は国内ユーザーを想定していたためにJST(日本標準時)固定でしたが、海外ユーザーがいる場合、タイムゾーンごとに異なる処理が必要となる可能性があります。さらに、地域によってはサマータイムもあるため、実装が複雑化します。
以下のような選択肢を検討しました。
- タイムゾーンごとに課金処理を分ける
ユーザーごとにタイムゾーンのパラメータを持ち、そのパラメータごとに課金処理を分ける戦略です。この方針では実装の複雑化が避けられず、またタイムゾーン移行による不正(契約期間の延長など)のリスクが考えられました。
- タイムゾーン固定にする
仕様を簡略化し、課金処理を行うタイムゾーンを固定にする戦略が考えられます。当然、海外在住のユーザーにとっては時差ズレを発生させるデメリットがあります。
解決策: タイムゾーンの固定
事前の調査により、主要な継続決済サービスもタイムゾーン固定で運用し、時差ズレを仕様として許容していることがわかりました。前述した「タイムゾーンごとに課金処理を分ける」場合のデメリットも加味し、タイムゾーン固定で運用する方針としました。例えばJST 12:00に課金処理を実行すると、日本ユーザーには昼間、海外ユーザーには早朝または前日夜になりますが、許容範囲と判断しました。
課題3: 二重課金の防止
問題
モンスターストライクはモンストWebショップ以外に、アプリプラットフォームでもサブスクリプション購入を提供しています。要件上、このようなアプリプラットフォーム上における契約と、モンストWebショップでの契約の二重課金を防ぐ必要がありました。
解決策
1. 新規契約時のチェック
課金開始時にプラットフォーム契約の確認を実施し、プラットフォーム契約が残っている場合、その契約終了日までは二重課金を避ける必要があります。一方で、支払い手段の有効性は確認しておく必要があります。この時、プラットフォーム契約の終了日までを無料扱いとし、契約段階では支払い手段の有効性のみ確認[2]します。この仕組みにより、プラットフォームからの移行をスムーズに行いつつ、二重課金を防止できます。
2. アプリプラットフォーム課金検知時の自動停止
ゲームサーバー側でアプリプラットフォームでの課金を検知次第、継続決済プラットフォームに通知する仕組みを実装しています。アプリプラットフォームで課金検知された場合、モンストWebショップのモンパス契約では、次回更新を停止します。
- ゲームサーバー: アプリプラットフォーム課金を検知 → 継続決済プラットフォームに通知
- 継続決済プラットフォーム: 次回更新停止ステータスに遷移
- 契約終了日までは引き続き有効、その後自動解約
この二段階の仕組みにより、ユーザーがサブスクリプションを二重契約してしまうリスクを軽減しました。
実装アーキテクチャ
モンストWebショップでは、継続決済プラットフォームを利用し、以下の三層でモンパス購入の実装を構成しました。
各層は以下のような責務を負っています。
決済プラットフォーム層
- 汎用的な継続決済機能を提供
- クレジットカード、QRコード決済などの決済手段を統合
- 定期課金の実行
- PCI DSS準拠のセキュアな実装
バックエンド層
- モンパス固有のビジネスロジック
- ユーザー IDの紐付け管理
- ゲームサーバーとの連携(特典付与など)
- 契約状態の管理
フロントエンド層
- モンパス購入、解約、プラン変更などのUI
- React/Next.jsによるSPA実装
- 決済フローのUX
この三層構造により、決済ロジックとビジネスロジックを分離し、保守性と拡張性を確保しました。
初回契約のフロー
三層構造を前提に、初回契約のフローをシーケンス図で示します。初回契約時は、3DS認証を経て決済を確定し、サブスクリプションを有効化します。
継続課金のフロー
次は継続課金のシーケンス図です。継続課金はユーザー起点ではなく、加盟店(この場合継続課金プラットフォーム)起点となることに注意してください。継続課金は定期的に自動実行され、結果をWebhookで通知します。
その他のフロー
他にも、支払い方法変更フローや継続課金に失敗した時の再試行フローなどが存在しますが、上記二種のどちらかに近いフローになるため、ここでは割愛します。
まとめ
モンストWebショップにおける、月額会員サービス「モンパス」を支える継続決済プラットフォームの設計と実装について説明しました。
継続決済システムの構築にあたり、既存サービスの調査から始め、決済プラットフォーム側でサブスクリプションを管理する方針を選択しました。この選択により、提携サービスの実装工数を削減し、将来的な複数サービスへの展開を見据えた拡張性を確保しました。
実装では、決済失敗時の支払い猶予期間の導入、タイムゾーン固定による運用の簡素化、二重課金防止の仕組みなど、実際のユーザー体験を重視した設計判断を行いました。
決済プラットフォーム側でのサブスクリプション管理は複雑化のリスクがあり、タイムゾーン固定は海外ユーザーに時差ズレを生じさせます。しかし、これらのトレードオフを認識したうえで、最大公約数的な機能に絞りました。また、Webhookで拡張可能にするという方針により、保守性と拡張性の両立を目指しました。
決済システムの設計・実装に携わる方の参考になれば幸いです。
-
すでにサービス終了していますが、MIXI Mでプレミアム会員サービスを提供していました。 ↩︎
-
いわゆる0円オーソリ、あるいは少額オーソリと呼ばれるものです。カードの有効性を確認するために、実際には課金されないか、ごく少額の課金が行われる仕組みです。 ↩︎
Discussion