#3」が開催されました。第3回となる今回のテーマは「モバイル決済の裏側」。急速に成長するモバイル決済分野でサービスを展開する企業が一堂に会し、自社サービスの仕組みや技術スタックなど、知られざる裏側を語ります。」に登壇したのは、PayPay株式会社の正木一平氏。「100億円あげちゃうキャンペーン」をはじめとした各種キャンペーンを支えるキャッシュバックエンジンの仕組みを解説します。正木一平氏(以下、正木):PayPay株式会社の正木一平と申します。今日は弊社から2人登壇させていただきます。自分からPayPay全体の簡単な紹介と、自分が担当しているキャンペーン・キャッシュバックエンジンの裏側についてお話しさせていただければと思います。次のセッションでは、弊社のHarshから決済サービスとしてコアなプラットフォームとなります、ペイメントまわりの裏側のお話をさせていただければと思います。自己紹介をさせてください。JAPANに入社し、もともとは加盟店の管理システムや決済のハブシステムの開発や運用に携わっていました。PayPayに異動後は、最初はユーザ情報や認証認可のシステムの開発をしていました。Managementチームでキャンペーンやキャッシュバックエンジンを担当しております。プライベートでは2児の父として子育てにも奮闘中です。今日は、まずはPayPayの概要や開発スタイル・環境についてお話をして、その後キャンペーン・キャッシュバックエンジンの課題や実際にどんな技術を採用しているか、運用上の利点や「こんな課題を抱えています」といった話ができればと考えています。PayPayを利用したことがある方は手を上げてください。PayPayはQR・バーコード決済サービスです。お店でバーコードを読み取ってもらったり、お店のQRコードを自分で読み取ったりすることで、簡単に決済ができるサービスになります。今なら定常特典として最大3パーセントのPayPayボーナスが付与され、キャンペーンの対象になるとさらに戻ってきます。おかげ様で多くの加盟店様に導入いただいており、最新の情報ではユーザ登録者数が900万、加盟店数が70万を超えました。アプリのマップを開いていただくと、PayPayを使える加盟店がご覧いただけます。PayPayの概要と開発環境PayPay株式会社は、昨年の6月15日に会社を設立いたしました。昨年の10月5日にサービスを開始して、12月4日から12月13日まで「100億円あげちゃうキャンペーン」というキャンペーンを開催させていただきました。今年に入って2月12日から5月13日まで第2弾として、また100億円キャンペーンを実施し、5月からは「ワクワクペイペイ」というキャンペーンを展開しています。Backendということで、開発スタイルについてもお話ししたいと思います。PayPayではアジャイル型の開発スタイルを採用しており、非常にスピードを重視しています。週1のスプリントで開発を進めていまして、毎週社内で新機能のデモをやっています。サービスを開始してからもうすぐで10ヶ月が経過しますが、10ヶ月でアプリを50回以上リリースしております。開発環境はとてもグローバルです。外国籍のエンジニアが40パーセント以上なので、半分弱が外国籍のエンジニアとなっております。ベンチャーマインドのあるフラットな組織で、スーパーフレックスなど、自由な働き方ができる環境です。できたばかりのサービスなので、開発環境についても最新のものを使っています。こちらはこのあともう少し詳しくお話させていただきます。PayPayのマイクロサービス今日はけっこうこの話が出てきているので「また?」という感じだと思いますが、PayPayでもマイクロサービスアーキテクチャを採用しています。PayPayではだいたい60以上のサービスがあり、チームは7チーム以上で開発をしております。サービスごとに独立したリソースを持っていて、サービス間はAPIやKafkaを使った非同期のメッセージでやり取りをしています。小さい図になってしまいますが、具体的にはこのようなシステムになっています。詳細はお話できないのですが、1番上にあるのがユーザのアプリと加盟店のPOSの端末など、そしてマーチャント様が利用されるツールです。ユーザがアプリからクレジットカードを登録した場合、非常にセンシティブな情報なので、そのデータはPayPayには一切保存をしていません。JAPANのセキュアな環境に保存されています。今日は図の真ん中にあるCLMについて僕からお話をさせていただき、ペイメントのコアのシステムについてはHarshから説明させていただきます。PayPayの技術スタック技術スタックです。Bootがほとんどです。Streams、Node.jsでも実装をしています。PythonやGoで実装しているツールもあり、アプリケーションはすべてDockernizeされています。CI/CDやDevOpsについては、JenkinsとKubernetesでオーケストレーションしています。AWSの構成管理はTerraformで行っています。RDSとAurora、それから最近はTiDBの導入も進めています。キャッシングにはRedisを使っていて、Cassandraも使っています。limitにOpenResty、サーキットブレーカーにResilience4j、トレーシングにZipkin、ロギングにFluentd、ElasticSearch、Kibanaを使っています。モニタリングにはDatadog・PagerDuty・NewRelic・VividCortexですね。いったものを使っております。キャンペーン・キャッシュバックエンジンにおける課題それでは、ここから「100億を支える技術」と題しまして、僕の担当しているキャンペーン・キャッシュバックエンジンの裏側についてお話させていただきます。ここにあるものは一部で、実はもっとたくさんのキャンペーンを実施しています。今は常に10個以上のキャンペーンが動いていて、それぞれのキャンペーンには条件ごとに種類もいくつかあるので、それも考慮すると40から50程度のキャンペーンが常に動いている状態です。PayPayのキャンペーンについて、右側の図が決済完了画面です。赤い枠の部分に付与額と付与予定日を表示しております。決済完了時には、どのキャンペーンを適用していくら付与するか、自動で決める必要があります。これを実現するためには課題がいくつかあります。処理を完了させる必要があります。PayPayではスキャン画面でQRコードを読み取って金額を入力して支払いボタンを押すと、先ほどの決済完了画面に遷移します。この間にキャンペーンの適用とキャッシュバック金額の計算を行わなければいけないので、バッチ処理では対応が難しいです。障害が起きたときに復旧してまたすぐに表示できるようにするといった対応も必要です。先ほどお話した通りキャンペーンが非常に多く、それぞれのキャンペーンにもいろいろな条件があります。時間や金額、ユーザ情報、加盟店の情報・支払い方法・上限金額といった条件があります。いったものを判断するロジックによって、計算量が膨大になります。ユーザのアクションに影響を与えてはいけません。仮に、万が一キャッシュバックエンジンが障害を起こしてしまったとしても、ユーザが決済できないのはまずいので、ユーザの決済やその他のアクションに依存関係を持たせてしまってはいけません。PayPayのキャッシュバックエンジンそこでPayPayのキャッシュバックエンジンは、イベント駆動な非同期のストリームシステムとして実装しております。スライドの左側にAcquining・Payment・UserModuleと書いていますが、こういった他のサービスからのインターフェースはKafkaのみを利用しております。キャッシュバックエンジンでの処理結果のイベントも、KafkaにPublishしています。Kafkaについて簡単にご説明します。Kafkaはオープンソースの分散ストリーミングプラットフォームで、大規模なデータを分散環境で高速に処理することができます。システム間のデータの受け渡しを仲介するメッセージングシステムとして機能します。パーティションを使うことで、順序付けも保証しながら負荷分散をすることができます。クラスタにマシンを追加すれば、スケールアウトをすることもできます。具体的には下の図のようにProducersとConsumersでストリーミングもできますし、データベースにつなぐこともできます。1回PayPayのキャッシュバックエンジンに戻ります。他のサービスはKafkaにイベントをPublishすることだけを意識すればよいので、そのあとのキャッシュバックの操作については一切関心を持ちません。逆にキャッシュバックエンジンはKafkaからSubscribeしてきたイベントだけを意識するので、Kafkaから流れてくるユーザアクションを、イベント駆動なストリームシステムとして実装することが可能です。Streamsを採用しております。Akkaの特徴では、Akkaについてご説明します。Akkaはアクターモデルを取り入れた分散・並行処理のツールキットで、低レベルなマルチスレッドプログラミングを行うことなく、分散・並行処理を実装できるものです。ビジネス的にリアクティブなシステムを必要とする環境に適しています。リアクティブなシステムについては「リアクティブ宣言」というものがありまして、リアクティブ宣言にはこの4つの特性が挙げられています。システムは可能な限り速やかに応答する。ユーザが何かアクションとかをした場合にすぐに応答できる必要があります。システムは障害に直面しても即応性を保ち続ける。仮に何か障害が起きてしまったときにシステムを全断してしまうと即応性を保つことができなくなるので、こういった耐障害性を備える必要があります。システムはワークロードが変動しても即応性を保ち続ける。決済やユーザアクションが急に増えてシステムの負荷が非常に上がった場合に、処理を遅延させてしまったり、全断させてしまったりすると即応性がまた保てなくなるので、弾力性も重要になります。最後にメッセージ駆動。リアクティブシステムは非同期なメッセージパッシングによってコンポーネント間の境界を確立する。いうものを実現するための手段として、メッセージ駆動が挙げられます。結果を表示しなければいけない即応性が求められていますし、計算量が非常に膨大になってしまっても弾力性を保持しなければいけないですし、他にユーザのアクションに影響を与えてはいけないので、耐障害性も保つ必要があります。StreamsはAkkaライブラリの1つです。ストリーム処理を定式化するための直感的で安全な方法を提供しています。再利用可能なパーツを下の図のように柔軟なパイプラインでまとめて、ストリーム処理を簡単に書くことができます。イベントストリームを限られたリソース内で効率的に処理することができて、外部の非同期サービスとも柔軟に相互接続ができる仕組みになっています。Streamsの仕様に準拠しています。pressureというのはイベントをConsumeする側が自身の処理能力をPublisher側に伝えることで、無駄なくSubscriberが処理できる仕組みです。処理ができない量を受け取らないようにするものです。最後に現行のシステムの運用上の利点について紹介します。Groupを使うことで一部のイベントだけを新しい環境に流すことが可能になります。すると新しい環境で動作の確認ができるので、そこで問題がなければ古い環境のConsumerを止めるだけで新しい環境にすべてのリクエストが流れます。遅延させずにアプリケーションの環境を移行することが可能になります。他のサービスに依存していないのでメンテナンス無しでDBのマイグレーションもできます。Auroraへのマイグレーションを計画したのですが、KafkaのConsumerを一旦止めてしまえばDBへの書き込みはすべて止まります。その状態で新しいDNSへアプリケーションからの向き先を変えてConsumerを再開すれば新しいDBを使ってサービスが再開できます。しても作業中にKafka内で遅延が発生してしまいますが、Consumerの再開後にはすぐに反映されるので、とくにメンテナンスは不要になります。現行システムの課題としては、パフォーマンスの改善についてです。Streamsは非常に便利なので、それほど意識しなくてもそれなりにパフォーマンスが出てしまいます。現在ユーザ数も決済数も非常に増えてきているので、こういったチューニングが必要であると考えています。Akkaには内部のパフォーマンスを可視化する仕組みがあるので、まずは可視化をしてボトルネックを探したいと思っています。あとはAkkaの強みの1つとして分散システムを簡単に構築できるので、そういったところも検討しています。ご清聴ありがとうございました。JAPANに関する記事をまとめています。JAPANに関する新着記事が公開された際に、通知を受け取ることができます。