トレタ開発者ブログ

飲食店向け予約/顧客台帳サービス「トレタ」など、飲食業界のVertical SaaS企業です。

飲食店モバイルオーダー・トレタO/Xの開発反省会

こんにちは、トレタのモバイルオーダー事業でプロダクトマネージャーを行なっている北川です。

こちらはトレタアドベントカレンダー2日目の記事です。この記事では2022年の振り返りとして、今年の前半に取り組んだモバイルオーダー・トレタO/Xでの決済トラブルについてどう対応していったのかを記そうと思います。

toreta.in

決済でのトラブルというのは基本的に起こしてはならないものであり、それを記事にすることは憚られましたが、自分が苦闘しているなかで同様に決済への試みをしている記事を大いに参考にさせていただいたので、自分も誰かの助けになればと思いこの記事を公開します。

あらためて、導入店の飲食店様と来店してご利用いただいていたお客様には、多大なご迷惑をおかけしたことをこの場を借りてお詫び申し上げます。

トレタO/Xの決済の概要

まずはトレタO/Xでの決済について説明します。 トレタO/Xでは店員を介さずにユーザーが自分のスマホから飲食代金を決済することが可能です。 ユーザーは飲食後にトレタO/Xでお会計画面を開き、支払い方法として、オンライン決済オフライン決済かを選択することができます。

オフライン決済は従来の決済のように、店員を呼び出しレジでの会計となります。 オンライン決済であれば、クレジットカード情報を入力して決済を完了すればそのまま退店することができます。

会計待ちなどの煩わしさが減り、店員と来店者の両者の手間が省かれる体験となっています。

多重決済の問題

運用が少しずつ軌道にのりサーバーでの処理リクエスト数が日に日に増えていく中で、お会計が重複して行われてしまう多重決済の問題が発生しました。

調査したところ、多重決済が発生するケースには2パターンがありました。

  1. 決済が途中で失敗したケース
  2. ひとつのお会計に対して複数の決済が同時に行われたケース

まず1つ目の決済が途中で失敗したケースについてですが、これはシステムの構成が関わっています。

トレタO/Xのバックエンドはマイクロサービスの構成となっており、決済においては「注文サービス」と「決済サービス」を跨いだ処理となります。また、それらの処理をオーケストレーションするAPIのGatewayサーバーがあります。

決済の処理としては、

  1. オーダーアプリからGatewayサーバーへ決済のリクエストを送る
  2. Gatewayサーバーは、注文サービスへ会計情報を問い合わせる
  3. Gatewayサーバーは、取得した会計情報の金額で決済サービスへ決済処理をリクエストする
  4. 決済サービスは、決済プラットフォームのStripeを利用しており、Stripeへ決済をリクエストする
  5. 決済が完了したらGatewayサーバーは、注文サービスに該当の注文に対しての支払いレコードを登録し、会計を支払い済みのステータスに更新する

さて、今回発生したトラブルでは決済サービスからレスポンスが返ったあとに注文サービスへの書き込みでエラーが発生していました。

注文や決済などが重なりリクエストが集中する時間帯においてサーバーへの負荷が上がり、一部のリクエストでタイムアウトが発生したためです。

決済サービスへの書き込みに失敗するとユーザー側にはエラーの表示がされますが、決済処理をバックオフができないため、決済サービスでの決済は完了されてたままに注文サービスでの会計は未会計のステータスのまま、というデータの不整合が起きた状態となります。

そして会計が未会計のステータスなので、ユーザーから支払いを再試行することが可能であり二重の支払いが行われてしまいます。

リトライと冪等性

これを解決するには、エラーとなった箇所でのリトライをすればよいと考えられます。

ただし、サーバーからタイムアウトのレスポンスが返ってきた場合にはその処理の結果が成功/失敗かがわからないため、単純にリトライしてしまうと1つの決済に対してリトライ回数分の支払いレコードが登録されてしまうことになります。

そのため注文サービスの支払いレコード登録APIには冪等性の担保が必要となります。

冪等性対応の仕様としては、The Idempotency-Key HTTP Header Fielddraft-ietf-httpapi-idempotency-key-header-02を基に一部簡略化して実装しました。

  • HTTPリクエストのリクエストヘッダーにIdempotency-Keyを設定する
  • 過去に同一のIdempotency-Keyの値でのリクエストがあれば、処理は行わずにステータスコードを409(Conflicted)でレスポンスを返す

また、StripeのAPIにも同様に冪等キーを付与する仕組みがあるので決済サーバー側もこちらを用いて冪等対応をします。

では冪等キーには何を使用するのが妥当でしょうか。

注文をとりまとめた会計レコードがもつ会計IDがあるので、一見すると会計IDを使うと会計に対して一回以上支払いができないように制限でれるように考えられます。

しかし、支払いは会計に対して本当に一度だけでしょうか。

オンライン決済やオフライン決済が複合して行われることを想定すると、会計に対して複数回の支払いを行うケースはいくつか考えられます。

  • クーポン(金券)で一部を払い、残りをクレジットカードで払う
  • クレジットカードで一部を払い、残りを現金で払う
  • グループ内で割り勘し、それぞれがクレジットカードで払う※

※ 現在は機能として未実装

会計に対して支払いは一回とは限らず、複数回行えるとした方がよさそうです。 そのため冪等キーは支払いに対して毎回ユニークなキー(UUIDなど)を発行することになりました。

同時支払いの問題

次に、2つめの問題である同時支払いについて考えていきます。

同時支払いはいわゆるECサイトなどでの決済では起きづらいですが、複数人の会計がひとつにまとめられている飲食店での決済においては発生する頻度が高まります。

同時支払いが起こるシチュエーションは主に2パターンあります。

複数のユーザーが同時に払うケース

飲食店における会計はグループ内で共有しているため、グループ内の誰でも会計を行うことが可能です。 そのため一人が支払いを開始して完了する前に別の人が支払いを開始してしまうと、両者が決済してしまうことにます。

なお、前述の通り1会計につき1支払いとは限らないので支払いを1回だけに絞ることはできません。

オンライン会計とオフライン会計が同時に行われてしまうケース

複数人の同時支払いと同様に、オンライン会計とレジでの現金払いが同時に行われてしまうケースも可能性としてあります。

オフライン決済だと現金以外にクーポン利用もあり、クーポンであれば過払いも許容されます。(例. 1,000円のお会計に3,000円のクーポンで支払う)

注文の排他制御

この問題を解決するには、支払いに対しての排他制御が考えられます。

誰かが支払い行為を開始した時点で支払い枠にロックをかけ、他の支払いは通さないようにさせます。他の人がロックを取得している状態で新しくロックの取得をしようとすると失敗するため、支払いは常に1つずつ処理されることになります。

排他制御を加えた決済の処理としては、

  1. オーダーアプリからGatewayサーバーへ決済のリクエストを送る
  2. Gatewayサーバーは、注文サービスへ会計情報を問い合わせ、ロックを取得する
  3. Gatewayサーバーは、取得した会計情報の金額で決済サービスへ決済処理をリクエストする
  4. 決済サービスは、Stripeへ決済をリクエストする
  5. 決済が完了したらGatewayサーバーは、注文サービスに該当の注文に対しての支払いレコードを登録し、会計を支払い済みのステータスに更新する

対応方針のまとめ

2つの多重決済の問題に対して、以下の対応方針で解決できそうなことがわかりました。

  • 支払いの排他制御を行う
  • 各処理で失敗したら冪等性を担保しつつリトライを行う

補償トランザクション

ではリトライを何度も行っても解決しない場合はどうすればよいでしょうか。

決済サービスへの決済処理が完了するまであればバックオフが可能です。 その場合、取得したロックを解除してユーザーからの再試行を行える状態にします。

では決済処理完了後に、注文サービスへの支払い完了登録が行えない場合はどうでしょうか。

バックオフはできませんし、エラー終了すると支払いのロックは残ったままでデッドロックとなってしまいます。 同様に、バックオフ中にロックの解除に失敗し続けた場合もデッドロックのままとなってしまいます。

現状ではこの状況に陥った場合には、お客様からは支払いを頂かずに後日トレタ側で飲食店へ補填する対応としています。 そもそも注文サービスが何度リトライをしても失敗してしまうケースとしては、サーバーがダウンしている可能性が高いので、その他の処理も続行が難しいと考えられるためです。

最善の策ではないので、発生頻度などを注視しながらあるべき対応を引き続き検討している状況です。

実装手段

これらの実装にあたって、今回はGCPのWorkflowsの利用を取り入れました。

Workflowsはタスクを登録することで外部API呼び出しなどの定義されたステップを順に実行してくれるというサービスです。 AWSであれば似たようなサービスとしてStepFunctionsがあります。

リトライの仕組みを持ち合わせているため、リトライ回数やインターバルなど柔軟なリトライ処理を容易に組むことができます。

今回はこのWorkflowsを使い、外部API呼び出しとそのレスポンスに応じたリトライ、バックオフ処理を実装しました。また、タスク登録時にタスクのIDを得られるので、冪等キーにはこれを利用しました。

ちなみにWorkflowsでの使いづらい点を挙げておくと、基本的に定義はすべてyaml形式であるため、プログラマブルな細かい制御は難しく、テストもしづらい点です。

さいごに

以上が今回行ったトレタO/Xにおける多重決済に対しての対応のアプローチでした。

分散トランザクションの難しさに改めてて対峙し、自分の未熟さを痛感しましたがここで得た教訓とノウハウを今後の開発に活かし、より安全なシステムの開発に向けて精進していこうと思います。

なお、トレタではエンジニアの募集を全方位で行なっております。

コロナ禍を乗り越えた飲食店の新しい姿を探求する仲間をお待ちしております。

corp.toreta.in

入社前不安だったことは杞憂だった話

はじめに

はじめまして。2022年の5月から株式会社トレタでフロントエンドエンジニアとして働いている武市です。

実務としては、モバイルオーダー「トレタO/X」のフロントエンドを担当しています。

詳しくは代表のnoteをご覧ください。

note.com

私は愛媛県からフルリモートで勤務しております。今回はトレタに入社する前に不安だったことをベースに記載していこうと思います。

転職時の不安を少しでも拭えたら幸いです。

簡単な経歴

元々は飲食店を経営していたのですが廃業してしまい人生どん底状態で腐っていました。このままではダメだと奮起し社会的に需要の高いプログラミングを学習。IT業界へ転職しました。エンジニア歴としては2年と長いわけではありませんが、以前の会社では良くも悪くもリソースが足りておらず既存サービスの保守・運用だけでなく新規サービスであるフィットネスクラブ向けの月額顧客管理システムのPM兼開発を、立ち上げからさせていただき貴重な経験を積むことが出来ました。直近の技術スタックはVue.js,React,PHPです。

転職へのモチベーション

飲食店を経営していたこともあり僕の友人は飲食関係の友人が多いのですが、コロナ禍で休業、廃業する店は少なくありません。今でもお店が回らないと聞けば手伝いに行ったりもしていますが、人手不足は深刻です。プログラミングを学び飲食業界に貢献できることはないのか、そう考えていた矢先見つけたのがトレタでした。

転職時に不安だったこと

地方から働いて不便はないか

前職はオフラインでの業務でしたが、トレタではフルリモートになりました。どのようなオンボーディングになるのか不安だったのですが、業務の中でわからない部分や、設計に悩んだ際もメンター制度があるおかげで気兼ねなく相談できる環境が整っています。私は入社初日からフルリモートでは働いているのですが不便を感じたことは一度もないです。 ※メンター制度とは・・フルリモート環境下における新入社員のオンボーディングサポートの制度。新入社員には入社時に1人メンターがつき、定期的な1on1の実施や相談役としてフォローを行い、新入社員をサポートする制度。

人間関係はうまくいくのか

トレタで人間関係で不満に思っている人はいないんじゃないかと思うぐらい穏やかな人が多いです。一度、『怖い人っていないんですか?』と聞いたことがありますが『そんな人いない、みんな楽しく仕事しているから心に余裕があるんだよ』と答えが返ってきて安心しました。

思ったような仕事を担当できるか

前職ではPMとして勤務していたこともあり、開発業務に集中できる環境ではありませんでした。トレタではBizサイド、PM、バックエンド、フロントエンド、QAと業務分担がしっかりされておりフロントエンドエンジニアとして集中できる環境が整っています。

これまでの経験・スキルが通用するか

トレタではマイクロサービスを採択していることにより高い設計力が求められます。品質の高いソフトウェアを開発をする為に、求められる技術レベルが高くなったため日々勉強する毎日です。技術力の高い方からレビューしていただける環境は非常にありがたい。トレタにいればもっともっとプログラミングのレベルを高めることができるとワクワクしています!

まとめ

飲食業界をDX化することで社会貢献ができ、従業員一人一人の生活を大切にしているワークライフバランスの取れた会社です。トレタに入社して幸福度の高さは人生で一番だと思います。

興味がある方は是非カジュアル面談へお越しください! www.wantedly.com

トレタO/XのtoCアプリをリニューアルしたら諸々整理できてよかったという話

こんにちは、パパエンジニアのkentaroです.

2022年2月、トレタO/Xをご利用いただいているワンダーテーブルさまの「よなよなビアワークス」向けのモバイルオーダーアプリ(以下 toCアプリ と呼びます)をFlutter WebからReactにリニューアルしました。

なお、「トレタO/X」については代表のnoteをご覧ください。

note.com

背景

当時のtoCアプリはFlutter1系で作られており、いくつか問題点を抱えていました。代表的なことの1つはNullSafety対応されていないことです。もう1つは状態管理も現在デファクトスタンダードになっているライブラリ(Riverpod)への乗り換えが必要ということです。
(ちなみにスタッフ向けアプリはFlutter2系・NullSafety対応済み・Riverpod採用しています)
ほぼ作り直しとなることが見込まれたので機会を見計らっていましたが、iOS15でFlutter Webが動作しなくなったことがきっかけで一気に検討が進み、最終的にはReactで作り直すという意思決定をしました。

参考までにチームメンバーが作成してくれたIssueをご紹介します。当時もワークアラウンドで回避可能でしたが、これによりリニューアルの議論が進むきっかけになりました。

github.com

なぜReactなのか

当初はモバイルアプリ化する可能性があったため、Flutter Webを採用していましたが、その後の事業方針でtoCアプリはモバイルアプリ化しないことになりました。

他法人向けtoCアプリはAngularで作っていましたが正社員採用や業務委託契約による体制拡大を考えた際、より人を集めやすいReactで作り直すことになりました。

リニューアルプロジェクト

2021年9月からアプリのリニューアルプロジェクトが始動しました。

最初の1ヶ月程は開発環境の構築と並行し、Flutter版toCアプリの仕様策定や、複雑なロジックの図解をしました。
プロジェクト序盤に現状を整理して可視化したことにより、過剰に複雑になっていた仕様をできるだけシンプルにすることができました。振り返ってみた際にとても有効な取り組みだったと感じています。
例えばアプリ起動時の状態判定が複雑だったのですが、ここをある程度シンプルな形にできたため、メンテナンス性が向上しました。

そこから11月末までは実装フェーズでした。
フロントエンド開発が得意なメンバーはcomponentの量産に集中し、デザイナーと連携してUIの実装を推し進めてくれました。
自分は今回が初のWebフロントエンドの開発業務でしたが、Flutter版toCアプリの開発に携わっていたのでドメイン知識はチーム内でも豊富な部類でした。
そこでなるべくロジック寄りの実装を担当し、不慣れなWebフロントエンドの開発でも実装のスピードを落とさないように工夫しました。
メンバーそれぞれの強みを活かし、チームとしての成果を最大化できたと感じています。

12月・1月はQAとテストで出た不具合の対応をしつつ、残タスクの消化やリリース準備を行い、2月のリリースに漕ぎ着けました。

プロジェクトメンバー全員が何かしらのO/X別プロジェクトと兼任している状態ではありましたが、リリース遅延することなくやりきることができました。

React版で追加した機能

基本的にはFlutter版をReact化する、というプロジェクトだったのですが1つだけ新しい機能を追加しています。
これまでのクレジットカードによる支払いに加え、Apple Pay / Google Pay に対応しました!是非ご利用ください!

よなよなビアワークス向けトレタO/XのtoCアプリでApple Payボタンが表示されている画像

振り返ってみて

チームメンバーに「このプロジェクトを振り返ってみてどうでしたか?」というインタビューをしました。

プロジェクトマネージャー

  • PoCで手探りで作ってきた部分をきれいに作り直すいい機会だった
  • 転職市場やフリーランス市場ではFlutterエンジニアよりReactエンジニアのほうが多いので、長い目で見て保守しやすくなったと捉えている

デザイナー

  • 画面・コンポーネントの名前が人によって呼び方が人によって異なっていたが、それを統一できたためコミュニケーションがしやすくなった
    • 例: 「注文リスト」を「カート」と呼ぶ人もいたが、「注文リスト」に統一
  • 画面遷移フローの図をわかりやすくすることができた
  • コンポーネントの整理ができた

QA

  • FlutterのIntegration Testについて、当時のバージョンではWebに対応していなくて書けなかった
  • Webの多様なE2E Testing Framework使えるので、今後テストの自動化を進めていくことができる
  • 仕様整理しながらプロジェクトを進めることができた

エンジニア

  • (プロジェクトマネージャーと同じく)PoCで手探りで作ってきた部分をきれいに作り直すいい機会だった
  • O/Xを開発するにあたって理解しておくといいことをドキュメント化することができたので、今後新しいエンジニアが来てもスムーズにオンボーディングができそう

個人的な感想

  • 業務でのWebフロントエンドの開発に挑戦できた

この辺りの話は トレタO/Xの開発にジョインしてやってきたこと という記事の後半にも書いています。

tech.toreta.in

それぞれの目線でコメントもらいましたが「諸々整理できたのでよかった」というのは職種関係なく感じていたようです。

終わりに

プロダクトを作りエンハンスを続けていると、細かいところに綻びが出ていることに気がつきはするのですが、立ち止って整理するという意思決定に至らないことも多いのではないかと思います。
トレタでは新しいプロダクト開発に取り組んでいますが、今回のリニューアルプロジェクトで得た知見・ドキュメント等が非常に役に立っています。
中長期的に考えると事業をさらに発展させていくために、短期的に立ち止まり、整理したり仕切り直したりすることは必要なのだと感じています。

今回は良いきっかけに恵まれましたが、一方で施策を前に進めることも必要です。プロジェクトや企業の状況に応じて判断する必要があることなので、ステークホルダーとロードマップを握りながらリファクタリングを組み込んでいくのが良いように感じました。

お約束の

最後に、一緒に走ったり立ち止まったりしてくれる仲間を大募集中です!
とりあえず話を聞いてみたいという方、カジュアル面談もウェルカムですのでお気軽にお問い合わせください。

www.wantedly.com

© Toreta, Inc.

Powered by Hatena Blog