reserve-optimizer
整骨院向けLINE予約管理Bot。LINE Bot + GASGoogle Apps Script(グーグル・アップス・スクリプト)の略。Googleが提供する、Gmailやスプレッドシート等のGoogleサービスを自動化するための簡易プログラミング環境 + Cloudflare Workers世界中に分散したサーバー上で小さなプログラムを動かす仕組み(Cloudflare社のサービス)。使った分だけの課金なので、アクセスが少ないサービスなら実質無料で運用できる + Stripe決済の サーバーレス構成で、24時間自動受付・決済・AIチャット対応を実現。
なぜ作ったか
整骨院の予約管理は電話・紙ベースが多く、スタッフの負担が大きいという課題がありました。LINE Botで24時間自動受付・Stripe決済・AIチャット対応を実現し、予約業務をゼロにするために開発しました。
アーキテクチャ
レイヤー構成
[LINEユーザー] ←→ [LINE Messaging API]
↓ Webhook
[Cloudflare Worker] ← 署名検証 + 即座に200 OK返却
↓ waitUntil で転送
[GAS Web App] ← 会話ステートマシン実行
↓
[Google Spreadsheets] ← データ保存
↓
[Stripe Checkout] ← デポジット決済(1,000円)
設計上の判断と理由
Q: なぜCloudflare Workerを挟んでいるのか?GASに直接Webhookウェブフック。「イベントが起きたら指定URLに自動で通知を送る」仕組み。GitHubで更新があればDiscordに通知が飛ぶ、等の連携に使うしない理由
LINEのWebhookは5秒以内に200 OKを返さないとタイムアウトします。しかしGASの起動は遅く(コールドスタートしばらく使われていなかったプログラムを、次に利用が来た時にゼロから起動すること。この起動時間が長いと利用者が待たされる。Cloudflare Workersはこの起動が速いとされるで3〜5秒)、処理も含めるとタイムアウトする可能性があります。そこで:
- Cloudflare Workerが即座に200 OKを返す(LINEタイムアウト回避)
waitUntilで非同期1つの処理の完了を待たずに、他の処理を同時に進める方式。ファストフードで注文を受けて番号で呼ぶイメージ。Webサーバーが多数の利用者を同時に捌く時に効くにGASへ転送- WorkerでHMAC-SHA256署名検証も行う(セキュリティ層)
Q: なぜデータベースにGoogle Spreadsheetsを選んだのか?
- 運用者が非エンジニア(整骨院スタッフ)のため、スプレッドシートなら直接確認・修正できる
- 無料枠で十分に運用可能
- GASと同じGoogleエコシステムある技術の周りに自然に集まった、関連道具・教材・コミュニティの総体。「エコシステムが豊富」=困った時に道具や情報がすぐ見つかる状態で連携が簡単
- 将来的にFirestore等に移行する場合も、データアクセス層を変更するだけで対応可能
Q: なぜデポジット制(前払い1,000円)なのか?
- 無料予約ではキャンセルやドタキャンの問題が頻発する
- 1,000円の心理的ハードルで、本気で予約する人だけに絞れる
- Stripe Checkoutを使うことで、PCIContinuous Integration(シーアイ・継続的インテグレーション)の略。コードを変更するたびに自動でテストとチェックを回し、壊れていないかを常に確認する仕組み DSS準拠の安全な決済を簡単に実装
Q: なぜGAS + Workersという構成を選んだのか(長期的視点)
3つの軸で判断しました。
- コスト: 整骨院は零細事業者でランニングコストを極小にしたかった。GASもWorkersも無料枠で動き、月額0円で本番稼働できる
- セキュリティ: LINEとStripeのWebhookをインターネットに晒す必要があるが、GASのWeb Appは認証が弱い。だからWorkersを前段に置いて署名検証をエッジ(高速)で通し、検証済みリクエストだけを独自トークンAIにとっての「文字数」のようなもの。AIは文章をトークンという小片に区切って処理する。課金や入力上限はこの単位で数えられる付きでGASに転送し、GASのエンドポイント自体は隠す二段構えにした
- 移行前提: GASは「現在の小規模(1日数十件)に対する選択」であって永遠の答えではない。Phase 2でスケール・SaaS化する段階で
Next.js + Vercel + Supabaseに移す前提と割り切っている
💡 一言で: 「安く済ませる部分(中身)」と「絶対に手を抜けない部分(入り口の守り)」を分けて考え、大きくなったら移る、という規模に応じた段階的な選択です。
会話ステートマシン
LINE Botの会話フローを状態管理する仕組みです:
初期状態 → 日付選択 → 時間枠選択 → 予約確認 → Stripe決済 → 予約完了
↓ キャンセル
予約取消
各状態でQuickReply(選択肢ボタン)を表示し、ユーザーが迷わないように設計しています。
AIチャット(代替プロバイダー M2.7)
整骨院に関する質問に回答するAIチャット機能:
- トピックを「整骨院関連」に限定(関係ない質問には答えない)
- 既知のFAQはテンプレート回答、未知の質問のみAI生成
重視したテスト戦略
「壊れたら最悪(お金の消失・個人情報・二重処理)の箇所」と「一番複雑でバグが出やすい箇所」に、テストのリソースを集中させました。
① Webhook署名検証(なりすまし防止の生命線)
LINE / Stripe 両方のWebhookで HMAC-SHA256 による署名検証をしています。偽の通知を送りつけられて虚偽の予約や「決済完了」を捏ねじ曲げられるのを防ぐためです。比較にはタイミングセーフ比較を採用し、比較にかかる時間の差から正解を推測する「タイミング攻撃」も塞いでいます。
② 会話ステートマシンの遷移網羅
LINE Botの会話は15以上の状態を行き来します(初期状態 → 日付選択 → 時間枠 → 予約確認 → 決済 → 完了、加えてキャンセル・変更・待機リストの分岐)。ここが一番ビジネスロジックが詰まっていてバグが出やすいため、入力と状態遷移の組み合わせを網羅するテストを多数用意しました。
③ 決済の冪等性(リプレイ攻撃対策)
同じWebhookが複数回送られても二重決済・二重予約にならないよう、処理済みのリクエストは20分間キャッシュで記憶して重ねて処理しない仕組み(冪等性)を持たせています。
④ 複数LLMクロスレビュー
単一のLLMは自分の出力に甘くなる(同じ盲点を共有する)ため、2機の異なるLLMで独立にコードレビュー書かれたコードを、書いた本人以外の視点で読んで問題を指摘する作業。バグ・読みにくさ・危険な書き方を第三者が見つけるのに有効させ、出た指摘を優先度順に潰す運用にしています。
💡 一言で: 「AIが書いたから正しい」ではなく、独立した検証層を何重に挟むかが品質を決める、というのが実感です。
面接で聞かれそうなポイント
「なぜサーバーレス構成?EC2等のサーバーではない理由」
- 予約システムは1日のアクセスが少なく(数十件)、常にサーバーを立てるのはコストの無駄
- Cloudflare Workersは無料枠(10万リクエスト/日)で十分
- GASも無料枠で運用可能
- 結果的に月額0円で本番稼働
「Stripe Checkoutの選定理由」
- 自分で決済フォームを実装するとPCI DSS(クレジットカード情報の保護基準)への対応が必要
- Stripe Checkoutを使えば、カード情報はStripeのサーバーで処理され、こちらには届かない
- セキュリティリスクを最小化できる
「本番運用していて不安な点・技術的負債は」
正直に3つあります。いずれもレビュー議事録に文書化済みで、改善案を把握しているものです。
-
Workers → GAS 間の通信がGASの302リダイレクト仕様に依存している点。GASのWeb AppはPOSTを受けると302を返してPOSTをGETに変換する仕様で、それをWorkers側で吸収する工夫をしています。Google側の仕様変更の影響を受ける可能性があり、Apps Script APIApplication Programming Interface(エーピーアイ)の略。あるプログラムの機能を、別のプログラムから呼び出せるようにした「窓口」。予約システムがLINE社の機能とやり取りするのもAPI経由(実行可能API)化すれば依存を外せる認識で移行を検討中。※署名検証・認証・冪等性などセキュリティの核はWorkers内で別途堅牢に実装済みで、ここが壊れてもデータ漏洩・不正アクセスには直結せず、予約機能の停止(可用性問題)に留まります。
-
顧客の個人情報(氏名・電話番号)がスプレッドシートに平文で保存されている点。現在はGoogleアカウントの権限管理で守っていますが、Phase 2のSupabase移行で暗号化と細かなアクセス権限(RBAC)を整える前提です。
-
3件の失敗テストが未解明の点(回数券の有効期限・セグメント配信の非アクティブ判定)。実GASの挙動とすり合わせる残課題として認識しています。
💡 ポイント: 自分で弱みを挙げられる=技術的負債を把握・管理している証拠。隠す欠陥ではなく、改善案付きの「見える化された課題」として扱っています。
技術スタック
- TypeScript / Google Apps Script (JavaScript)
- Cloudflare Workers / LINE Messaging API
- Stripe API / Google Spreadsheets
- 代替プロバイダー M2.7(AIチャット)
用語の平易な解説(専門用語を読み解く)
☁️ サーバーレス・EC2
一言で: 「サーバーを常に立てず、必要な時だけ動かす仕組み」と「常に動くサーバー」。
- サーバーレス: 「サーバーがない」のではなく「サーバーの管理が不要」な仕組み。アクセスが来た時だけ処理が動き、使わない時は課金されない
- EC2(イーシーツー): Amazon AWSが提供する「常に動く仮想サーバー」。使っていなくも時間課金される
- 本章では「1日のアクセスが数十件なので、常に立てるEC2よりサーバーレスがお得」と判断
🔔 Webhook(ウェブフック)
一言で: 「イベントが起きた時に、別のサービスへ自動で連絡する仕組み」。
- ユーザーがLINEでメッセージを送った瞬間に、LINE側からこちらのシステムへ「メッセージ来ましたよ」と連絡が来る。この連絡の仕組みがWebhook
- 電話でいう「呼び出しベル」のようなもの
⚡ Cloudflare Workers(クラウドフレアワーカーズ)・コールドスタート・waitUntil
一言で: 「世界中に散らばるサーバーで高速に処理を動かす仕組み」と「初回起動のもたつき」。
- Cloudflare Workers: 世界中のサーバー網(エッジ)でプログラムを動かす仕組み。非常に高速
- コールドスタート: しばらく動いていなかったプログラムを再び動かす時の、初回のもたつき。GAS(Google Apps Script)はこれが遅い
- waitUntil(ウェイトユンティル): 「これが終わるまで待って」と処理を延命する仕組み。即座に応答を返しつつ、裏で別処理を回せる
🔐 HMAC-SHA256・署名検証(しょめいかんしょう)
一言で: 「メッセージが本当に正規の送り主からかを確かめる仕組み」。
- 署名検証: 「この連絡、本当にLINEから来たもの? 偽物じゃない?」を確かめること
- HMAC-SHA256: 署名を作る・確認する際の計算方式の名前。改ざんを見抜くのに使う
⏱️ タイミング攻撃(タイミングこうげき)・タイミングセーフ比較
一言で: 「処理にかかる時間の差で正解を当てる攻撃」と、それを防ぐ比較方法。
- タイミング攻撃: 例えば「先頭1文字だけ合っていれば速く返す」ような比較だと、文字が合うほど処理が長くなる。その時間の差を測って、正しい署名を少しずつ総当たりで特定する攻撃
- タイミングセーフ比較: どの文字が合っていても必ず同じ時間で比較する方式。本章では署名検証にこれを採用し、時間差から正解を推測されるのを防いでいる
🔁 冪等性(べきとうせい)
一言で: 「同じ操作を何度繰り返しても、結果が同じになる性質」。
- 決済のWebhookが「ネットワークの調子で2回送られる」ことは普通に起きる。この時、毎回処理してしまうと二重引き落としになる
- 冪等性とは「2回目以降の同じ要求は無視する」仕組み。本章では処理済みの要求を20分間キャッシュで記憶し、重ねて処理しないようにしている
📊 GAS(Google Apps Script)・Web App
一言で: 「Googleのサービスを自動化するプログラム」と「Webから呼べる形にしたもの」。
- GAS(Google Apps Script): スプレッドシート等のGoogleサービスを操作できるプログラム環境
- Web App: GASのプログラムを「Webから呼べるサービス」として公開したもの。Webhookの受け口になる
💳 Stripe Checkout(ストライプチェックアウト)・デポジット・PCI DSS
一言で: 「安全な決済を簡単に組み込む仕組み」と「カード情報保護の国際基準」。
- Stripe(ストライプ): オンライン決済を代行するサービス。自社で決済システムを作らずに済む
- Stripe Checkout: Stripeが用意した完成品の決済画面。カード入力欄等が最初から用意されている
- デポジット(前払金): 予約時に前払いで預かるお金。キャンセル防止の心理的ハードル
- PCI DSS(ピーシーアイディーエスエス): クレジットカード情報を安全に扱うための国際基準。自前でカード入力を受けると、この厳しい基準への対応が必要になる