Stripe Connect(Custom)×DirectChargeというアンチパターンからの大脱走

Yuki Matsuyama
mento-techblog
Published in
Dec 24, 2020

初めに

この記事は、JP_Stripes Advent Calendar 2020 24日目の記事です。

mentoでCTOをしているmatsumatsu20です。年末にかけて大きな技術負債の解消に取り組んでいたのでつらつらと経緯を書き起こしてみました。

mentoではコーチングのマッチングプラットフォームを運営しており、クライアント(コーチングを受ける人)がコーチに支払いをする際の決済システムにStripeを採用しています。

StripeにはConnectアカウントという仕組みがあり、それを用いることで我々のようなtwo-sideプラットフォームの決済フローを簡単に実装することが可能です。

(Connectアカウントについての細かい説明はここでは省きますが、上記のような関係性を実現できます)

また、更にStripeでは決済方法にいくつか種類があるのですが、その中でも一番シンプルなDirect Chargeというものを採用しておりました。

ただ実は上記の構成はかなりアンチパターンだということが発覚し、その構成から脱却することを決めたので、何が問題だったのか、どういったソリューションを選択したのか、その時何を考えたのかをまとめていきます。

何が問題だったのか

まずConnectアカウントには3種類存在し、我々はその中でCustomアカウントというものを採用しております。

このCustomアカウントでは、アカウントを発行するもののそのアカウントの所有者(我々でいうとコーチ)は、Stripeの存在を意識することなく、我々がAPIや各種統合を用いて管理する前提のアカウントとなってます。(Standardアカウントを選択すると、アカウント生成後ログイン、セットアップしてもらって売上などもStripeにログインして確認してもらう形になります)

このCustomアカウントに対してDirect Chargeをするといくつか問題や悩ましい事態にぶつかることになります。

返金が厄介

Direct Chargeはあくまでもそのアカウントに対して直接決済する方法であるため、決済対象はConnectアカウントになり、まずはコーチに売上として決済金額が計上されます。そこからプラットフォームアカウントにFeeの支払いと、StripeにFeeの支払いを行い残ったのが売上という作りです。(金額はすべてダミーです。またあくまで概念図なので実際の処理の順序の正しさは保証してません)

この場合返金しようとすると以下のフローとなります。

  1. コーチからクライアントに10000円返金
  2. プラットフォームアカウントからコーチに2000円返金(手数料売上を返す)

しかし、Stripeへ支払いしたFeeは当然帰って来ませんので、コーチが500円売上毀損することになります。

プラットフォームビジネスの提供者は我々なので、返金に際して手数料の毀損が発生する場合は当然我々に毀損を発生させるべきです。しかし上記構成の場合はそれが難しいので、現在は他の手数料売上から該当コーチに返金という形で補填を行うということをしています。ただその手数料売上も返金の対象になることもあるので綱渡り感は否めないですし、データがどんどん汚くなっていくので、なんとかしたい状況になっていました。

チャージバック請求されたときの対応が難しい

クレジットカードにはチャージバックという消費者保護の仕組みがあり、Stripeもそれをサポートしています。端的に言うと、カードの不正利用などがあった場合にそれを報告するとクレジットカード会社がその決済を帳消しにする、といった仕組みです。

ただこれはサービス提供者の立場からすると、例えば勘違いなどでサービス提供しているにも関わらず、チャージバックが成立すると返金することになり毀損が発生しまうため、チャージバックの請求が来た際に事実確認やエビデンスの用意、決済者とのコミュニケーションなどをする必要があります。

ここで問題なのが、上記のDirectChargeで直接Connectアカウントに決済をしているため、チャージバックもConnectアカウントに申請されることになります。

ConnectアカウントにEmailは登録されているので、メールが飛んでいるはずではあるのですが、そもそも前述の通りコーチたちはStripeアカウントの存在を知らないので、チャージバックの申請がきたところでなんのこっちゃとなるわけです。

しかもStripeのチャージバックは、一定期間こちらからエビデンスを提出しないと、勝手に全額返金し、不審請求手数料として1500円も取られることになります。もちろん、これもDirectChageでConnectアカウント上の決済の出来事なので、全部コーチの売上が毀損します。

これを防ぐために弊社では、定期的に全Connectアカウント上で不審請求が発生していないかチェックするスクリプトを実行していますが、本来プラットフォームアカウントに請求が来るべきです。

誰がその決済に責任を持つのか

他にも領収書発行機能が使えなかったり、shared_customerを作ることによって実装が複雑化したりと、デメリットはあるのですが、大きくは上記の2点(とそれに付随するシステムの複雑化+運用コスト)だと思っています。

上記2点も、結局は誰がその決済に責任を持つのかということであり、プラットフォームなのであればプラットフォームアカウント上で決済させるべきであり、Connectアカウント(我々で言うコーチ)なのであればStandardアカウントで決済システムや仕組みを理解させた上で、Connectアカウント上で決済させるべき、ということだと理解しています。

大変なのはわかった。じゃあどうするのか

やりたきことは、決済をプラットフォームアカウント上で行いたい、ということなので、決済しつつ特定のConnectアカウントに送金を実施できるDestinationChargeを採用することにしました。

これ自体は特に難しいこともなく実装していけばよいのですが、我々にはDirectChargeですでに生成された既存決済の移行という大きな課題がありました。

弊社ではすでに1年間くらいサービスを運用しており、現状数百件のサブスクリプションが現在進行系で走っております。

まずはとりあえずサポートに、簡単に移行できる方法がないか確認したところ以下のような回答をいただきました。

定期支払い情報やプランを子アカウントから親アカウントにコピーすることはできかねます。貴社側にて保存いただいたAPIの実行内容から、プランを再度作成いただくことをおすすめいたします。プランや定期支払を再作成する方法については下記リンクをご確認ください(英語):https://support.stripe.com/questions/recreate-subscriptions-and-plans-after-moving-customer-data-to-a-new-stripe-account

つまり移行はできないのでAPIを使って再生成するしかなく、請求サイクルを合わせるために、生成時にトライアル期間を設定して任意の時間に請求が開始するように調整する必要がある、という内容でした。

これを受けて影響範囲やコスト含めて検討した後、我々は既存決済を移行するのを諦めるという意思決定をしました。

移行を諦め、新規に発生する決済からDestinationChargeを使った新しいフローを導入し、1年くらいかけて緩やかに移行が完了するような方針にしております。

理由としては、再生成を実行した場合の

  • 顧客の情報を利用して親アカウント上でサブスクリプションを作り直すという運用自体が、作業にミスがあった際に顧客に不当に請求をしてしまうことになるためリスクが高いこと
  • 対象のサブスクリプションの件数が500件近い数になっており、すべてが正常に意図通りに再生成されているかの確認が難しいこと
  • 別のアカウントに再生成してることになるので、概念としては同じSubscriptionでもStripe上のログまでは引き継げず、支払データや諸々のログがConnectアカウントとプラットフォームアカウントに分散してしまうこと(調査などのコストが上がる)

というデメリットの方が、緩やかな移行方式をとった際の以下のデメリットよりも大きいと判断したためです。

  • DirectChargeでの決済と、DestinationChargeでの決済が混在した状態になるので、明細や支払い一覧などの既存の決済に関係するアプリケーションコードですべてそれを考慮する必要がある
  • 本作業時点ですでに存在するSubscriptionが終了するまで↑の状態を許容し続けることになる

具体的な対応

既存の決済を移行することを諦めたので、対応・実装としてはシンプルで

  • 新規決済をすべてDestinationChargeに変更する
  • 支払いを管理しているモデルで、その決済がDirectChargeによって作られたのか、DestinationChargeによって作られたのかを判別できるようにする
  • 明細表示や支払いの一覧を表示するロジックなど過去の決済を扱うロジックをすべて洗い出し、上記の判別を用いた分岐を入れて出し分けられるようにする
  • Subscriptionのwebhookリクエスト時にそのSubscriptionが、Connectアカウント上に存在するSubscriptionオブジェクトか、プラットフォームアカウント上にあるものかを判別し、適切な処理を行う

上記対応を影響範囲を随時調査しながら進めていきました。

後は、月1くらいで旧Subscriptionの数をモニタリングしつつ、全て新Subscriptionに移行したら分岐ロジックをすべて削除する予定です。

終わりに

移行作業のリスクやデメリットが多く、あえて緩やかな移行を選択し、アプリケーションコードに分岐を埋め込んで我慢する、といった意思決定をしましたが、あまり悲観的になりすぎず、楽しく旧Subscriptionが駆逐されていくのをモニタリングして、キレイになったら(1年後くらいかなぁ)打ち上げ🍺でもしたいと思っています。

最後に教訓をまとめておきます

  • DirectChargeとConnectのCustom Accountの組み合わせはアンチパターン。絶対辛くなるので避けたほうがよい。
  • Connectを使う場合はそのビジネスの顧客から見た決済の対象者や責任者を考えて、プラットフォーム上で決済(Custom Account × Destination Charge or Charge and Transfer)をさせるのか、Connectアカウント上で決済をさせるのか(Standard Account × DirectCharge)の選択が必要

mentoでは気持ちよく新年を迎えられる人を増やすため、年末限定価格で体験コーチングを実施しております。興味があったけどまだ受けられたことのない方など、一年を棚卸しするいい機会になるので是非受けてみてください🍦

登録はこちらから!

--

--

Yuki Matsuyama
mento-techblog
0 Followers
Editor for

パーソナルコーチングサービスmento 取締役CTO