toC LINE ポータル — LINE ログイン × Firebase Auth で「回答する人」を「戻ってくる人」に

2026-10-30 初版 · insightinside / apps/consumer · デザインシステム: packages/ui (shadcn new-york / zinc) · ロゴ: docs/biz/betalab-shared-v2/assets/logo/Betalab_logo.svg

製品と反応の数字は ミカン下北沢 BetaLab ポップアップ (2026-08-04〜08-14) の実在 6 製品と本番 D1 の実測 (2026-08-12 取得, docs/reports/mikan-shimokita-popup-2026-08/report-data.json)。製品写真は本番アンケート画面のスクショから切り出したもの (原本は R2)。価格と提携カフェは未確定なので出していない。

開始位置は 3 パターン

開始位置最初の画面LINE ログインの区間その後
① LINE アプリからA01 トークのリッチメッセージ / L01 リッチメニューLIFF 内なので A03 同意 (初回のみ) だけLIFF 枠のまま
② ToC アンケート画面 × LINE アプリありW01 展示台 QR → A02 (ブラウザ)W03 OS の「LINE で開く」確認 → A03 (LINE アプリ内) → ブラウザに自動で戻るブラウザ枠。EC の使用感アンケートは Push から LINE 内で開く
③ ToC アンケート画面 × LINE アプリなしW01 → A02 (ブラウザ)W05 LINE のブラウザログイン → W06 メール → A03 (ブラウザ)。アカウントが無い人は匿名 (B) が受け皿ブラウザ枠。Push が届かないので W09 ポータルのお知らせで案内

アプリの有無を BetaLab 側で判定する必要は無い。LINE Login (access.line.me) がアプリ起動とブラウザログインを自動で切り替える。再訪時は ① L01 リッチメニュー、②③ W08 (ログイン済みのアンケート画面 → ポータル)。

🔺 As-Is / ✅ To-Be

同じ骨格 (入口 → 本人識別 → 属性 → 回答保存 → リワード → 再訪・回遊 → 削除) を左右に並べた。点線は「仕組みが無い」。

決めたこと (設計判断)

  1. 匿名回答は残す。LINE は「主ボタン」として並べる — 回答前に登録の壁を作らない。匿名で答えた人には完了画面で初めて連携を誘い、回答を引き継いでチケットを渡す (B02→B03)。
  2. LINE ログインは LIFF の ID トークン → API で検証 → Firebase カスタムトークン — Workers 上で LINE の verify エンドポイントを叩き、uid=line:<sub> のカスタムトークンを WebCrypto で署名して返す。Identity Platform (OIDC プロバイダ) へのアップグレードは不要。既存の betalabo-channel-oauth-identity-linking-asis-tobe.html の「LINE のみ」スコープと同じ方式。
  3. 匿名回答の引き継ぎはサーバ側で付け替える — Firebase の linkWithCredential はカスタムトークンと結合できないため、匿名 uid の Firebase ID トークンを一緒に送り、API がその uid の回答を consumer_user に付け替える (冪等・他人の回答は奪わない)。
  4. 属性は連携直後に 3 タップで 1 回だけ (年代・性別・エリア)。以後の回答には自動で添付し、設問から属性質問を外せるようにする。
  5. 友だち追加は LINE の許可画面で同時に (bot_prompt=aggressive) — 製品アンケートの案内 (E04) やチケット控えを Push で送るため。Messaging API チャネルは LINE Login と同一プロバイダーに作る (userId と sub を一致させる)。
  6. EC は外部 (各ブランドの公式 EC) — BetaLab は決済しない。紹介コード付きリンク → 注文 webhook で購入確認 → 購入者限定アンケートを解放 (betalabo-ec-referral-attribution-asis-tobe.html の方式)。
  7. カフェチケットは 1 回答 = 1 枚 (冪等キー = response_id)。QR は「使う」を押してから出す。押した瞬間にチケットを提示中にロックし、1 回限りのトークンを発行。店舗が読み取ったら、お客さまが「表示をやめる」を押していても消込が優先される (下の「チケット消込の設計」)。
  8. 開始位置は 3 パターン (LINE アプリ / アンケート画面 × LINE アプリ有無)。違うのはログインの区間だけで、以降の画面は共通 (ブラウザでは枠だけ変わる)。
  9. LINE グリーン (#06C755) は LINE の操作にだけ使う。それ以外は shadcn zinc の primary / outline / ghost。

LINE ワンタップ登録のシーケンス

チケット消込の設計 (ロック・冪等)

状態

状態入る条件出る条件
issued回答保存と同時に発行 (response_id で 1 枚)お客さまが「使う」→ presenting
presenting 🔒POST /tickets/:id/present。署名付き 1 回限りトークン (nonce, exp=10 分) を発行。他の端末からは使えない店舗が読み取り → used / 期限切れ → issued に戻る
usedPOST /store/redeem {token} が CAS で成功(終端) 取り消しは管理画面から監査ログ付き
expiredチケット自体の有効期限切れ(終端)

競合と失敗の扱い

ケース結果
読み取られた直後にお客さまが「表示をやめる」消込が勝つ。cancel は「QR を隠す」だけで、トークンは期限まで有効。画面は待ち受けを続け、確定したら使用済みに切り替える
同じ QR を 2 回読んだ / 通信リトライ2 回目も同じ成功結果を返す (nonce 単位で冪等)。チケットは 1 回だけ消える
「使う」を 2 回押した / 2 台で開いた有効なトークンがあれば同じトークンを返す (チケット単位のロック)。並行して 2 つのトークンは作らない
店舗端末がオフライン署名と期限は端末で検証して「使用できます」を出し、読み取り時刻つきで保留 → 復帰時に送る。期限内に読んだものは有効
10 分間読み取られなかったpresenting → issued に戻る。もう一度「使う」で新しいトークン
スクショの使い回しトークンは 1 回限り・10 分で失効。時計が秒で動くので店員が目視でも見分けられる

整備するユーザーデータ

画面サムネイル

クリックで static 版の該当画面へ。PNG は _shots/ (Playwright で機械撮影)。

実装面の課題 (整理)

領域課題方針 / 担当 issue
認証custom トークンで role クレームが欠けると既存判定に落ちて product_owner になりうる (レビュー M1)custom / line: は role を anonymous に固定。#259 で修正
退会D1 を消しても Firebase Auth の line:<sub> ユーザーが残る (レビュー M2)Identity Toolkit accounts:delete を呼ぶ。#259 で修正
LIFFLIFF の Endpoint URL は 1 つ。PR ごとに変わる preview の URL では LIFF が開けない本番 / 検証で LINE Login チャネルと LIFF を分け、検証用は固定の preview スロットに向ける (#260)
LINE ログインID トークンを nonce なしで検証 (同じチャネルのトークンなら期限までリプレイできる)・/auth/line にレート制限が無いLIFF 方式では一般的なので許容。レート制限は公開台帳に記録済み、別途追加
匿名の引き継ぎ他人の匿名 ID トークン (1 時間) を得た人は未所有回答を付け替えられる引き継ぎを auth_time が新しいトークンに限る (#259 の残課題)
アプリなしLINE アカウントが無い人はログインできない / Push が届かない匿名 (B) を受け皿に。案内はポータルのお知らせ (W09)。#261 / #264
チケット提示中のロック・消込の CAS・オフライン読み取り・店舗スタッフの認証 (誰が読んだか)上の「チケット消込の設計」。店舗スタッフは admin の店舗ロールで発行 (#262)
PushMessaging API の無料枠は月 200 通 (日本・コミュニケーションプラン)送信数の上限と計測。プラン選定は事業判断 (#264)
製品データ写真はスクショからの切り出し (444px)。価格・提携カフェが未確定実装では R2 の原本 (product_assets) を使う。価格は各 EC で表示 (#263)

実装状況と issue

issue内容画面状態
#258[Epic] toC LINE ポータル全 22—
#260人手設定: LINE Developers / Firebase の発行と環境変数投入—人の作業待ち
#259LINE ワンタップ登録 × Firebase Auth (匿名回答の引き継ぎ・開始位置 3 パターン)W01–W06, A02–A07, B01–B03実装済み (PR #265)
#261ポータル シェル + 入口 (ホーム・アンケート一覧・マイページ・連携解除・リッチメニュー・再訪)P01, P02, M01, L01, W08, W09未着手
#262リワード: カフェチケット発行・一覧・提示ロック・店舗消込A07, E06, R01–R05未着手
#263EC 回遊: ショップ・紹介リンク・購入確認E01–E03未着手
#264製品アンケート: LINE Push 案内 + 購入者限定E04–E06未着手

依存: #260 → #259 → #261 → #262。#263 → #264。環境変数の一覧と発行元 URL は #260。