読者です 読者をやめる 読者になる 読者になる

クックパッドの課金を支える技術

f:id:eisuke-oishi:20150409133149j:plain

こんにちは、技術部の大石です。開発基盤グループで課金システムの担当をしています。

インターネットサービスの決済・課金システムの開発や運用は、サービスの根幹を支えるために正確性と機能性を満たさなくてはなりません。また同時に、価格や料金体系、決済手段のバリエーションでユーザーに利便性を提供する必要もあります。「堅牢性」「信頼性」と「柔軟性」「開発スピード」という相反する要素の両立が求められます。

その結果、決済・課金システムは適切な設計や運用を意識しないと複雑になってしまいがちです。

課金システムの開発、運用でよくある問題

複数の決済方法を同じサービスの上で共存させる難しさ

例えば、最初にクレジットカード決済を導入して、その後にコンビニ決済、キャリア決済やアプリ内決済と決済方法が増えていくことはよくあることです。 最初の導入の際にクレジットカード決済への設計だけでなく、その後に増えていく決済を見据えた適切な設計を行なわないと、コンビニ決済やキャリア決済、アプリ内決済をクレジットカード決済の流れに無理矢理押し込めてしまうような実装になってしまいます。

導入当初の段階で複数の決済の流れを把握した上での設計を行なうのは、決済導入の予定があるいくつかの決済のインターフェースや仕様の知識などが必要になったりするため、過去に導入を行なったことがある経験や知見が必要となったりします。 また一度走り始めると根本的な設計を見直すというのは難しくなってしまいます。

割引キャンペーンといった施策の残骸が残ってしまう

ユーザーにサービスの魅力を伝えるために、割引キャンペーンや無料キャンペーンなどの価格面での施策は必ずといっていいほど行ないます。 期間限定であったり、納期の短かいものが多いので、長く運用されるとそのような施策の残骸が残ってしまうことが起きがちです。 そのような残骸がコードの品質を低下させていくことになります。

複数サービス間での運用

クックパッドでも、プレミアムサービスの他にも、プロのレシピ産地直送便などのユーザー課金を行なうサービスがあります。 複数のサービスで決済を導入する場合、課金機能をそれぞれの部署で開発すると外部の決済サービスとの連携などは大半が同じようなコードになったり、運用の知見が部署毎に分散してしまうことが起きてしまいます。

クックパッドの解決方法

以上のような問題を解決するために、クックパッドでは共通決済基盤を開発するというアプローチを取りました。

共通決済基盤とは

クックパッドを起点とする全てのサービスから各種決済の導入ができるようにした、社内向けの独立したWEBサービスです。主な機能としては、

  • 各種決済サービスとの連携
  • 接続先との差異を吸収し統一的なAPI
  • 決済情報、履歴の管理
  • 決済情報の突合や有効性の確認
  • 決済に必要なユーザー情報の管理
  • 経理処理における集計機能
  • カスタマーサポートが履歴を確認したりキャンセルなどを行える管理画面

があります。

なぜWEBサービスとして独立させたのか

たとえば、ロジックをgemとして切り出したり、Rails Engineを使うなど共通のライブラリを使用して管理する方法も考えられます。 しかし、なぜ私達は共通決済基盤をWEBサービスとして独立させたのでしょうか。

それは、WEBサービスとして独立させることにより、共通決済基盤を利用するサービスへの言語、環境的な依存が無くなるというのも理由の1つです。それ以上に大きな理由として、クックパッドのサービスは「プレミアムサービス」を始め、継続課金モデルを採用しているサービスが多いということが大きな動機です。

継続課金は、その名の通り課金状態が継続されていくものなので課金の状態を常に確認する必要があり、都度課金に比べ購入後の管理が複雑になります。 運用の煩雑さを軽減することを考えた場合に、ノウハウが集約されたプラットフォーム上で管理することが必要だったからです。

サービス連携の流れ

それでは、具体的に共通決済基盤が行なっている継続課金の利用開始から、決済情報の突合、サービスとの連携を追ってみることにします。

継続課金の利用開始

f:id:eisuke-oishi:20150409133625p:plain

  1. ユーザーが利用開始のアクションをとる
  2. 決済開始の手続きを決済APIへリクエストする
  3. ユーザーが決済の手続きを行う
  4. 共通決済基盤がサービスへ決済の結果をAPIコールバックする
  5. ユーザーがサービスを利用できる

サービス側での付与が失敗した場合は、適切に共通決済基盤の情報をロールバックします。

決済情報の突合

突合とは、それぞれのサービスが持つ継続決済の一覧同士を比較し差分を検出することです。

f:id:eisuke-oishi:20150409133618p:plain

  1. 共通決済基盤が決済サービスのステータスと同期する
  2. 共通決済基盤からサービスへ差分情報を送信し、サービスは利用できないユーザーの有料機能利用を解除する
  3. 共通決済基盤とサービス側での差分がないかをチェックし、差があれば検知する

上記の処理を、各種決済サービスの情報と共通決済基盤との継続契約の差と共通決済基盤とサービスとの差が無いように毎日行ないます。

以上のような処理が決済手段ごとに存在するするため、各サービスの管理の手間を省くと同時にノウハウが集積された確実な運用を行なうためWEBサービスとして独立させました。

共通決済基盤と利用サービスとの責務の分担

次に共通決済基盤とそれを利用するサービスとの責務の分担について説明します。

課金に関連するストーリーとして、クックパッドでの代表的な例を考えてみます。

  • ユーザーが280円のプレミアムサービス(商品)をクレジットカード決済(決済方法)で、継続課金を契約(注文)し、毎月280円を課金(請求)する。
  • 課金ユーザーがプレミアムサービスの利用ができるようにする(認可)
  • 毎月ユーザーに280円を請求し、結果を検証する

共通決済基盤は「注文」や「請求」の管理や外部APIとの連携を担い、サービス側は「商品」と「決済方法」の選択、サービス利用「認可」を担うように責務を分担しています。

このように、

  • 利用サービス側では商品を割引するクーポンの発行を行なうなど、商品に関する様々な施策が実施
  • 注文や請求の管理、外部の決済サービスとの連携、決済情報の突合などは共通決済基盤のノウハウが集約されたプラットフォーム上で管理

することで、明確な責務の分担を行なっています。

都度課金も対応

WEBサービスとして独立させた理由として継続課金の例を示しましたが、都度課金への対応も行っています。 これにより、サービス開発者は都度課金と継続課金のどちらにも対応でき、ユーザーは継続課金で利用しているものと同じクレジットカード情報を使って都度課金も行なうことができます。

共通決済基盤を導入した結果

各サービスから共通決済基盤を利用することで、冒頭に挙げたよくある問題も解決することができました。

「複数の決済方法を同じサービスとして共存させる難しさ」は1箇所に知見を集約することでよい設計の上で動作させることができています。また、「割引キャンペーンといった施策の残骸が残ってしまう」ことは責務の分担を明確に行なうことで利用サービスでの柔軟性と共通決済基盤の信頼性を担保できています。「複数サービス間での運用」も新規サービスなどでの決済導入コストの削減や運用の負荷軽減に繋がっています。

このように、共通決済基盤を導入することで「堅牢性」「信頼性」と「柔軟性」「開発スピード」の両立ができるようになりました。

まとめ

決済、課金に関する開発や運用は、外部環境の影響が大きく、運用コストが高くなりがちなので頭を悩ますことが多い部分かと思います。クックパッドの例がそういった開発や運用のご参考になれば幸いです。

決済に関する環境は今後テクノロジーの進化や新しい決済方法の登場などによってこれからも進化していく分野です。 そういった外部要因とあわせてクックパッドのサービスの発展に対応していくためにも、共通決済基盤をより良いものに進化させていきたいと思っています。

クックパッドでは、そのような課題に取り組んでみたいというエンジニアを募集しています。

/* */ @import "/css/theme/report/report.css"; /* */ /* */ body{ background-image: url('http://cdn-ak.f.st-hatena.com/images/fotolife/c/cookpadtech/20140527/20140527163350.png'); background-repeat: repeat-x; background-color:transparent; background-attachment: scroll; background-position: left top;} /* */ body{ border-top: 3px solid orange; color: #3c3c3c; font-family: 'Helvetica Neue', Helvetica, 'ヒラギノ角ゴ Pro W3', 'Hiragino Kaku Gothic Pro', Meiryo, Osaka, 'MS Pゴシック', sans-serif; line-height: 1.8; font-size: 16px; } a { text-decoration: underline; color: #693e1c; } a:hover { color: #80400e; text-decoration: underline; } .entry-title a{ color: rgb(176, 108, 28); cursor: auto; display: inline; font-family: 'Helvetica Neue', Helvetica, 'ヒラギノ角ゴ Pro W3', 'Hiragino Kaku Gothic Pro', Meiryo, Osaka, 'MS Pゴシック', sans-serif; font-size: 30px; font-weight: bold; height: auto; line-height: 40.5px; text-decoration: underline solid rgb(176, 108, 28); width: auto; line-height: 1.35; } .date a { color: #9b8b6c; font-size: 14px; text-decoration: none; font-weight: normal; } .urllist-title-link { font-size: 14px; } /* Recent Entries */ .recent-entries a{ color: #693e1c; } .recent-entries a:visited { color: #4d2200; text-decoration: none; } .hatena-module-recent-entries li { padding-bottom: 8px; border-bottom-width: 0px; } /*Widget*/ .hatena-module-body li { list-style-type: circle; } .hatena-module-body a{ text-decoration: none; } .hatena-module-body a:hover{ text-decoration: underline; } /* Widget name */ .hatena-module-title, .hatena-module-title a{ color: #b06c1c; margin-top: 20px; margin-bottom: 7px; } /* work frame*/ #container { width: 970px; text-align: center; margin: 0 auto; background: transparent; padding: 0 30px; } #wrapper { float: left; overflow: hidden; width: 660px; } #box2 { width: 240px; float: right; font-size: 14px; word-wrap: break-word; } /*#blog-title-inner{*/ /*margin-top: 3px;*/ /*height: 125px;*/ /*background-position: left 0px;*/ /*}*/ /*.header-image-only #blog-title-inner {*/ /*background-repeat: no-repeat;*/ /*position: relative;*/ /*height: 200px;*/ /*display: none;*/ /*}*/ /*#blog-title {*/ /*margin-top: 3px;*/ /*height: 125px;*/ /*background-image: url('http://cdn-ak.f.st-hatena.com/images/fotolife/c/cookpadtech/20140527/20140527172848.png');*/ /*background-repeat: no-repeat;*/ /*background-position: left 0px;*/ /*}*/