はじめに
こんにちは。メルペイのPayment PlatformチームでPaymentServiceの開発を担当するエンジニアの @foghost です。この記事は、Merpay Advent Calendar 2019 の21日目の記事です。
3年前、ソーシャルゲーム業界からメルカリに転職してから、幸運なことにゼロイチで決済サービスの開発に関わることができて、エンジニアとしても人生としてもとても充実の3年間を送りました。
そして、今、日本ではすでにキャッシュレスブームが始まっていて、これからFinTechの領域で様々な革命が起きていくと思っています。この記事では私がエンジニアとしてこれまでに決済サービスの開発に関わってきたことを振り返り、これからFinTechに関わりたい方への参考になればと思います。
社内向け決済基盤の検討
メルカリに入社してすぐ、当時社内ID基盤を開発していたPlatformチームに配属され、その後は子会社のSouzohに出向して新規事業向けの連携機能の開発に関わってきました。
いくつかのプロジェクトをやってみて、共通の決済基盤に対するニーズが割と高いことがわかりました。ただ、決済基盤を作るのにそれなりに時間がかかり、かつ新規サービスにとってはMVPの検証が優先されがちのため、当時モノリシックなmercari-api(PHP)の中で連携機能の開発を続けてきました。
これだといつまで経っても決済基盤が作れないという課題感が出てきて、チーム内で先行投資として先に作っていくことが決まって(最初はエンジニア3人が通常開発を兼務しながら)、2017年の3月ごろから共通決済基盤開発プロジェクトが始まりました。
残念ながら、その頃プロジェクトメンバーが誰も決済システムを開発したことがなかったのです。自分はメルカリ内で利用されているクレジットカード決済やコンビニ決済が実際にどう動いているのかすらまだ理解していなかった頃です。そんな中、ドメイン知識の不足による不安感も色々ありましたが、新しい領域にこれからチャレンジしていくワクワク感は今でも覚えています。
決済ドメイン知識の吸収
決済基盤を検討し始めた頃、チーム内で考えた問題設定は大体このようになっていました。
- (Must) メルカリ内の売上金が新規サービスでも利用できる
- (Better to have)メルカリ内で利用される他の決済手段が新規サービスにも気軽に利用できる
- (May)将来発展させて、外部サービスも利用できる決済システムに進化したい
新しいシステムを設計及び開発するには、システムにおけるドメインモデルの抽象化を適切に行うために、与えられた問題に関するドメイン知識の習得を確実に行う必要がありました。そこで、まずは決済についてのドメイン知識をひたすら勉強していきました。
学習内容として、既存のクレジットカード決済、キャリア決済、コンビニ決済の決済フロー、そしてメルカリ内での売上金管理の仕組みについてのキャッチアップから始めました。
- ドメイン概念の理解
- 各決済手段に関連する新しいキーワードを見つけたらひたすら調べて理解する
- 既存のソースコードを理解ながら、各決済フロー図を書き出す
- 既存の実装を理解することで頭のなかで決済フローの絵を書けるようになる
- 既存の実装に対して、先輩からひたすら利点、欠点の意見を聞く
- 巨人の肩の上に立ったほうが、よりよいシステムができるので、新しいシステムの設計及び開発時に既存の知恵を活かしたい
そして、将来外部サービスにも利用できる決済システムへの進化について、将来像をまとめるためには、この頃から中国のAlipayなどの決済システム設計についても本やネット上の記事なども情報収集しながら勉強してきました。
- 先行しているサービスの姿を見ながらで、将来像を描くことができる
- 先進している決済サービスのアーキテクチャはどうなっているか
- 中国で実践されてきた決済サービスの開発、設計の実践内容
一定のドメイン知識が吸収できた後に、まずMust要件となるmercari-apiから共通決済基盤に売上金管理の実装を切り出すところから、ユースケースの整理、必要な決済APIの仕様の検討、移行プランの整理、既存コードのリファクタリングなど諸々やり始めました。そして、既存及び新しいサービスから利用されるときのユースケースから必要そうなAPIが3つが見えてきました。
- Add 売上金の付与
- Consume 売上金の消費
- Refund 売上金の返金
一見良さそうに見えるAPI群ですが、この後の問題設定の変更によって、大きく生まれ変わりました。
この時期にあった課題
- 決済についてドメイン知識まったくなかった
- ソースコード、ドキュメント、他のエンジニアから必死に吸収するしかない
- 一定のドメイン知識を吸収できたとしても実運用の経験がなかったため、自信のあるシステム設計ができなかった
- 当時自分がまとめたドメインモデルの設計図を今振り返ってみたら割と突っ込みたくなる。そのときの設計は大きなドメイン概念が見えていたが、統一される体系的なドメイン設計ができてなかった
- 完璧のものができなかったが、実際に設計を考えてみるだけでも結構ためになった(設計時のフィーリングが少しずつ変わってくる)
- 最強の残高管理のスキーマがなんだろうとずっと悩んでいた
- 既存のテーブルスキーマが歴史経緯で破綻しそうになってきている
- 最強ではないが、とりあえず手を動かして既存のユースケースと会計要件を踏まえて再設計してみた
- このあたりの設計経験がその後でポイント・残高管理サービスの設計時にも活かせた
- Monolithicなシステムから機能を切り出してマイクロサービス化する経験がなくて、どうやって移行できるのだろうと色々大変だった
- 既存の機能フローを正しく理解することがスタート
- 既存の設計の利点、欠点を活かしながら新しいシステムを設計してゴールとする
- スタートからゴールまで辿るプランを整理する。
- 今振り返ると簡単そうな気持ちになるが、当時は色々苦労してた。この時期の苦労がその後メルペイのリリースに必要になるポイント移行や、エスクロー決済フローの移行を考えるときに役に立てた
メルペイへの道
2017年中旬頃、突然外部加盟店で利用できる決済サービスをこれから作っていくぞの温度感が社内から高まり、決済業界に経験のある方もその後入社されたことによって、「社内向けの決済基盤」という問題設定から急に「外部加盟店向けの決済サービス」の問題設定に切り替わり、今まで考えたユースケース、API設計をすべてひっくり返して考え直すことになりました。
しかもメルペイが実際に立ち上がる前の話なので、提供する決済手段、ユースケースが何もなかった状態で、システム開発を先行しようとしていました。普通だと、アンチパターンと言われそうなことをやってきました。
唯一わかるのが将来像として「中国で先行していたAlipay、Wechat Pay、日本だとLINE Payなどの決済サービスと近い何か」という点だったので、そこからまず世の中によく知られている決済サービスが提供している決済APIを調査して、できているものからドメイン知識及びユースケースをキャッチアップしてきました。この頃からサービス開発のエンジニア3人に加えて、社内に決済業界に経験がある人が参加してくれて、加盟店向けに決済サービスを提供し始めたら、絶対必要そうな決済ユースケースの仮説を立てながら数週間の議論を続けてきました。
色々調べて見えてきた一部のユースケース
- 加盟店側から決済できる
- 決済時に、仮売上、実売上、仮売上状態のキャンセル処理がある
- 決済手段が複数ある可能性がある
- 加盟店側から決済後の返金ができる
- 全額の払い戻し、一部払い戻し処理がある
- 加盟店の売上管理及び精算ができる
どれも今の自分だったら、当たり前のケースとしか思っていませんが、当時は本当に頭の中になにもなかったのです。そして目指すものが変わってくると、一気に知らないドメイン知識が増えてきて、またひたすら新しいキーワードを調べるのを繰り返してました。何回調べても正確に理解できなかったキーワードも少なくありません。
そして、gRPC APIの設計について、今までは既存決済機能を切り出すことしか考えてなくて、API設計時に機能ベースで考えて、ドメインモデルは深く抽象化せずに、何かできる機能をAPIとして作る素のRPC形式でした(例: ポイント消費して決済する Consume )が、StripeのRESTful API の設計を参考にして、Googleも推奨されている Resource Oriented API の思想を受けて、決済トランザクションのドメインモデルをリソースとして定義してから、リソースを操作するAPIを設計するように変わりました(例: CreateCharge)。ちなみに、この頃設計した主な決済APIはその後メルペイリリース時にも大きく変わっていません。
Resource Oriented APIを設計するには通常より抽象化の工夫が必要になりますが、
- 自然にドメインの抽象化から設計を考え始める
- 細かいDesign Guidelineがなくても、形が揃う
- 実際にはGoogleのGuildlineを使っているが、一度経験あれば、細かいGuidlineの確認がなくても一貫したAPIが設計ができる
- 決済トランザクションのリソース種別に統一される体系的な決済API群が作れる、運用面にとっても、利用側にとってもわかりやすくなる
などの利点があるため、その後メルペイにおけるほとんどのマイクロサービスもこの思想に従ってAPI設計を行っています。
この時期にあった課題
- メルペイがまだ始まってないので、ユースケースの整理は仮説を立てるしかなかった
- 個人的に仮説を立てるために、他の決済サービスの研究を一通り行い、将来ありそうなユースケース、決済関連のドメイン概念、APIの抽象化に対する理解が深まった
- 加盟店と通常のお客様の概念の抽象化、残高管理のスキーマ
- 決済の相手をどう抽象化してアカウント管理するか
- 将来想定される加盟店側のビジネススキーマ(B2C、B2B、B2B2C)に柔軟に対応できるようにどうすればよいか
- 想定していたユースケースに対応する決済APIのリソース抽象化
- 決済APIの設計思想が完全に変わった
- 決済手段の抽象化
- 決済に利用できる各種の決済手段をどう抽象化して、将来も拡張できるようにするか
- 残高とクレジットカードなどの外部決済手段を同時に利用する複合決済が将来必要な場合の分散トランザクション管理の課題
- 複合決済が発生すると、一部決済成功して一部成功しないときの整合性担保の難度が上がるので、仕組みを考えないといけない
- トランザクション処理を細かく分割してステートマシンでトランザクションの整合性を担保する仕組みを検討
- 冪等性
- 分散トランザクションの担保には命となるもの
- セキュリティ及び認証・認可の課題
- 会計知識の不足
- 決済サービスとはシステム上お金を動かすだけではなく、会計上に必要なデータも100%正確に記録しないといけない
- この時期から、経理の担当に会計の勉強会を開いてもらったり、既存の会計レポートを勉強したりして会計観点でもお金の動きを考えられるようになった
いずれも将来メルペイがどういう決済手段を加盟店向けに提供するにもかかわらず、絶対一度ぶつかる課題だったので、この段階で議論できて先にシステム設計が考えられたのはその後メルペイの開発も非常に役に立ちました。そして、色々な議論を踏まえて、当初考えたAPIのスペックを全般的に見直してから、PaymentServiceの開発を再開しました。
(開発の途中で別プロジェクトの支援が入って、開発が一度停止になった話はここで省略)
メルペイ時代
2017年11月頃メルペイが正式に立ち上がり、Platformチームが先行部隊としてそのまま出向することになり、それからいよいよ本格的に決済サービスの開発をはじめました。
メルペイのプロダクトサイドの開発は実際に動き始めたのが、社内公募などで人員を調達した後(2018年2月頃)ですが、PaymentServiceの開発は2017年10月ごろ再開されました。
これまで少しずつ作り上げてきたPaymentServiceのシステム(分散トランザクション管理など)を一度検証するためでもあるのですが、メルカリ内で利用されるエスクローの決済フローを抽象化したAPIを新規開発して、社内の新規サービスに先行導入してもらい、2018年4月に一度Beta版として本番リリースしました。ただし、当時はCloud SQLを利用しており、社内負荷テスト検証でスケーラビリティ及び可用性上の懸念があったため、その後Spannerに移行するというGo Boldな決断があり、2ヶ月ほどかかってSpannerへの移行開発を行いました。
また、この時期にPaymentServiceの開発と並行して、会計及び法的要件を満たせるポイントなどの残高管理のサービスも開発し始めました。残高の管理は簡単に思うかもしれませんが、会計、法的要件を満たせるテーブル設計、上位決済サービス向けの残高決済APIの提供など色々考えると結構細かい工夫が必要になります。テーブルもAPI設計も2, 3回ブラッシュアップしてやっと 現在の形 になってきました。
そして、メルペイが単独の決済サービスではなく、メルカリから生まれた決済サービスですので、メルカリのお客様が持ってるポイントをメルペイでも使えるように、メルペイで新規で作られる残高アカウントがメルカリの購入にも使えるようにしないとならないため、2018年中頃から、Tech LeadとしてPayment Platformチームでメルペイのリリース向けに並行で三大プロジェクトの開発を推進してきました。
- メルカリ内決済におけるすべての決済手段(クレジットカード、キャリア決済、コンビニ払い、ポイント)をPaymentServiceに移行して、メルペイの残高でも決済できるようにする
- メルカリポイントのシステム移行
- メルペイの決済に必要な機能開発、iD、CodePayment、NetPaymentに必要な要件を満たす決済APIの開発及び改修
それぞれ依存関係にもなっているので、メルペイのリリースを実現するには一つ一つ確実にリリースしていかないと会社全体のブロッカーになるため、チーム全員でAll For Oneで毎日お祭り状態で開発してきました。詳細は割愛しますが、結果として、メルカリ内決済の移行が12月から開始、1月中にすべて完了して、ポイント移行がメルペイリリースの2週間前に完了して、2019年2月中旬のメルペイ社内向けリリースに奇跡的に間に合いました。
リリース後も新規機能の開発が次々と続いてますが、ひとまずゼロイチの話としてここまで紹介しました。ちなみにリリース後の話について、弊社VPoEの@hidekさんも赤裸々な話を書いてくれたので、ぜひ読んでみてください。
この時期に決済基盤の開発にあった課題
- メルペイが正式開発スタートしてからリリースされるまで1年ぐらいしかなかったので、スケジュールが相当厳しかった
- 今までの先行開発がなければ、この時期から色々試行錯誤しても良いものが作れなかった気がする
- プロダクトサイドの要件を満たしつつ、法的観点の要件も満たさないといけない
- 法務関連のドメイン知識も理解しながら、機能設計の見直しが必要になった
- 会計、不正検知システムとのデータ連携はどうするか
- 加盟店の売上管理及び精算はどうするか
- いかに無事故でリリースするか
- Production Readyまでサービス実装の品質を高めたり、ログを見直したり、監視などの運用の仕組みを整備したりしていた
- 決済機能移行、ポイント移行時、影響範囲を抑えながら段階開放
- メルカリ内決済機能の移行が終わった後に、メルカリの購入時のすべてのトラフィックがPaymentService経由するようになり、無事にさばけていたので、落ち着いてメルペイのリリースを迎えられた
おわりに
以上で、FinTech業界の初心者としてゼロイチで経験してきた決済サービス開発の話でした。
最後に、新しい領域に挑戦するときに、個人的に大切なことをまとめて終わりにしたいと思います。
- 広くドメイン知識を吸収して、その領域の全体像が描けるようにする
- ドメイン知識の吸収手段として
- 類似サービスの機能、APIの研究
- 本、技術ブログなどでの情報収集
- その領域に詳しい方に直接ヒアリング
- エンジニアとして、将来向けにもある程度拡張性を持つシステムを設計するには、PdMよりも先読みできるのが大事、ドメイン知識の体系化がその鍵になる
- ドメイン知識の吸収手段として
- 問題設定が大事(できる限りのGo Boldな問題設計)
- 社内向けの決済基盤とメルペイが求める決済基盤の問題設定の違いでドメインの領域も、API、システムの設計も大きく変わる
- 巨人の肩の上に立つ
- その領域にすでに先行者がいる場合、彼らが実践してきたBest Practisesを活かしたほうがよりよいものができる
- 例えば、ドメインの抽象化、API、アーキテクチャ設計時に先行しているサービスなども研究しながら取り組んだほうが、よりよい設計ができる
- その領域にすでに先行者がいる場合、彼らが実践してきたBest Practisesを活かしたほうがよりよいものができる
- 良いシステムは進化で生まれるもの、一発では作れない
- 一発で完璧に作ろうとしない
- 細かい改善、試行錯誤を繰り返してシステムを進化させていくことが大事
- 決済サービスのような基盤サービスの開発に関しては、一歩先のことを考えることに損はない(開発は必要な分)
- 一歩先のこと含めて本来あるべき設計を考えてから、現在どこまで作るかを決めたほうがシステムの拡張性が残せる。
- 言われたら作るのでは遅い(試行錯誤する余裕がなくなるので、設計上の実装上の妥協が発生しがち)
明日のMerpay Advent Calendar は、メルペイスマート払いチーム/Backend Engineer の @oinume さんによる、コードレビューについての記事です。引き続きお楽しみください。