LINEトーク画面の中にWebフォームを表示する

LINE公式アカウントを運用していると、「顧客の連絡先を収集したい」「面談の希望日時を入力してもらいたい」といった場面に必ず出会います。Flex Messageのボタンだけでは対応できない複雑な入力を、LINEアプリの中でシームレスに実現するのが LIFF(LINE Front-end Framework) です。

この記事では、WIOの工事屋さんの実装事例をもとに、Cloudflare Pages上でLIFFアプリを構築し、D1データベースの顧客情報と連携するパターンを解説します。

LINE上でcompact LIFFが立ち上がり、連絡希望フォームが表示されている様子

上の画像は、LINEのトーク画面で「メールで連絡を希望」ボタンをタップした際の画面です。画面下部に小さなWebフォームがスライドアップし、氏名・メールアドレス・連絡希望時間帯を入力できます。このフォームがLIFFアプリです。

LIFFの3サイズと使い分け

LIFFには画面の占有率が異なる3つのサイズがあり、用途に応じて使い分けます。LINE Developers公式ドキュメントに詳しい仕様が掲載されています。

compact(画面の約40%) は、入力項目が3〜4個程度の簡易フォームに最適です。連絡先の入力やアンケート回答など、ユーザーに「ちょっとだけ入力してもらう」場面で使います。LINEのトーク画面が上部に見えた状態で下部にフォームが表示されるため、ユーザーは自分がLINEの中にいることを意識しながら操作できます。

tall(画面の約80%) は、やや多めの入力項目や一覧表示に向いています。マイページのような「複数項目を一度に閲覧・編集する」画面で威力を発揮します。WIOの工事屋さんでは、顧客情報の登録・編集画面(氏名・フリガナ・電話番号・メールアドレス・住所・備考の6項目)にtallサイズを採用しています。

full(画面の100%) は、複雑な操作やリッチなUIが必要な場面向けです。商品カタログの閲覧、地図表示、ファイルアップロードなど、LINEの外のWebアプリに近い体験を提供する場合に選びます。ただし、フルスクリーンはユーザーが「LINEの中にいる」感覚が薄れるため、必要以上に使わないのが得策です。

実務での判断基準はシンプルです。入力が3項目以下ならcompact、4〜6項目ならtall、それ以上や複雑なUIが必要ならfullです。迷ったらcompactから始めて、ユーザーの入力体験を見ながらサイズを上げるのが安全な進め方です。

アーキテクチャ全体像

LIFF連絡フォームのシステム構成は、3つのCloudflareサービスで成り立っています。

まず Cloudflare Pages がLIFFのHTMLを配信します。LIFFアプリの実体はWebページなので、静的ホスティングで十分です。次に Cloudflare Workers(API がフォームの送信先となり、データの保存とLINEへの確認メッセージ送信を担当します。最後に D1データベース が顧客情報と問い合わせ内容を永続化します。

データフローを追うと、ユーザーがLINE上でFlex Messageのボタンをタップすると、LIFFのURLが開かれます。LIFFアプリは起動時にLINE SDKの初期化を行い、ユーザーのアクセストークンを取得します。このトークンをAPIに送ることで、サーバー側でLINEのユーザーIDを特定し、既存の顧客情報があれば自動入力、なければ新規登録という分岐が実現します。

LIFF作成の手順

LIFFアプリはLINE Developersコンソールの LINE Loginチャネル に紐づきます。Messaging APIチャネルではないことに注意してください。LIFF IDの先頭の数字がLoginチャネルIDと一致するので、複数プロジェクトを運用する際の識別に役立ちます。

作成はLIFF REST APIで行えます。LINE LoginチャネルのアクセストークンをAuthorizationヘッダーに設定し、LIFFのサイズとエンドポイントURLを指定してPOSTリクエストを送ります。レスポンスにLIFF IDが含まれるので、これをHTMLのJavaScriptに埋め込みます。

アクセストークンの取得は、v2エンドポイントを使用します。v2.1ではJWT assertionが必要になるため、用途が異なります。チャネルIDとチャネルシークレットを使ったclient_credentials認証でトークンを発行し、このトークンでLIFFの作成・更新・削除が可能です。

Cloudflare PagesでのCSP設定

LIFFをCloudflare Pagesで配信する際に最もハマりやすいのが Content Security Policy(CSP の設定です。LIFFのSDKは複数の外部ドメインにアクセスするため、適切にCSPを設定しないとSDKの読み込み自体がブロックされます。

Cloudflare Pagesの設定ドキュメントに従い、プロジェクトルートの_headersファイルでCSPを一括管理します。LIFFアプリに必要なドメインは以下の通りです。

script-src には static.line-scdn.net が必要です。LIFF SDKのJavaScriptがこのドメインから配信されます。

connect-src には api.line.me と liffsdk.line-scdn.net の2つが必要です。前者はユーザープロフィール取得などのAPI呼び出し、後者はLIFF SDKが起動時にマニフェストファイルを取得するためにアクセスします。この liffsdk.line-scdn.net は見落としがちで、これが漏れるとLIFF初期化が「TypeError: Failed to fetch」で失敗します。

frame-src には liff.line.me が必要です。LIFF SDKがiframeで内部的に通信を行うために使用します。

form-action には access.line.me を追加します。LINEログインのリダイレクト先として使われます。

さらに、自社APIのドメインも connect-src に含めることを忘れないでください。LIFFからAPIへのfetch呼び出しがCSPでブロックされるケースは非常に多いです。

CSPのデバッグには、ブラウザのデベロッパーツールのコンソールが最も確実です。CSPブロックはネットワークタブには出ず、コンソールにのみエラーが表示されます。デプロイ後は必ずコンソールを確認する習慣をつけてください。

CORS設定の注意点

LIFFページはサイトのドメインから配信され、APIはサブドメインで動作するため、クロスオリジンリクエストが発生します。HonoのcorsミドルウェアでallowHeadersにAuthorizationを含めることが重要です。

LIFFアプリはLINEのアクセストークンをAuthorization Bearerヘッダーで送信します。このヘッダーはCORSのシンプルリクエストに含まれないため、ブラウザはプリフライト(OPTIONS)リクエストを送ります。サーバーがAccess-Control-Allow-HeadersにAuthorizationを含めて返さないと、本リクエストがブロックされます。

allowHeadersにContent-Typeだけ設定しているケースは非常に多く、Authorizationの追加を忘れがちです。この問題はエラーメッセージが分かりにくく、「ネットワークエラー」としか表示されないため原因の特定に時間がかかることがあります。MDN Web Docsのプリフライトリクエスト解説が理解の助けになります。

LINEトークン認証パターン

LIFFアプリからのAPI呼び出しでは、セッションCookieではなくLINEのアクセストークンで認証を行います。LIFF内でliff.getAccessToken()を呼び出して取得したトークンを、APIにBearerトークンとして送信します。

API側では、受け取ったトークンをLINEのProfile APIに転送してユーザー情報を取得します。これにより、LINEのユーザーID・表示名・プロフィール画像を安全に特定できます。トークンが無効であれば401を返し、有効であれば取得したユーザーIDをキーにD1の顧客データを検索します。

この方式のメリットは、セッション管理が不要なことです。LIFF内では常にLINEのセッションが有効なため、APIは状態を持たずにリクエストごとにトークンを検証するだけで認証が完結します。

D1顧客データとの連携

LIFFフォームの入力データをD1のcustomersテーブルに保存する際、upsertパターンを使います。LINEユーザーIDで既存レコードを検索し、存在すればUPDATE、なければINSERTします。

この設計により、マイページで入力済みの氏名・電話番号・メールアドレスが、連絡フォームを開いた際に自動入力されます。逆に、連絡フォームで入力した情報はマイページにも反映されます。ユーザーが同じ情報を何度も入力する必要がなくなり、フォームの離脱率を下げる効果があります。

APIエンドポイントの設計としては、顧客データの取得と保存を担当する /api/mypage と、連絡希望の保存を担当する /api/contact-request を分離しています。/api/contact-request は問い合わせテーブルへの保存と顧客テーブルのupsertを同一リクエストで行うため、フォームの送信ボタンを1回押すだけで両方のテーブルが更新されます。Cloudflare D1のドキュメントにD1固有のSQL方言や制約事項が記載されています。

フォーム送信後のLINE確認メッセージ

ユーザーがフォームを送信した後、LINEのトーク画面に確認メッセージを送ることで、情報が正しく受理されたことを伝えます。このメッセージはFlex Messageではなく、装飾のない平文テキストで送ります。理由は、確認メッセージは情報の正確な伝達が目的であり、見栄えよりも内容の読みやすさが重要だからです。

技術的には、APIの処理フローの最後にLINE Messaging APIのpushメッセージを送信します。Messaging APIのアクセストークンはD1のsettingsテーブルに保存されており、APIワーカーが必要に応じて読み取ります。pushメッセージは課金対象ですが、問い合わせ1件あたり1通の確認メッセージなので、コスト面で問題になることはほぼありません。

送信するメッセージには、ユーザーが入力した連絡手段と希望時間帯を含め、担当者からどのような形で連絡が来るのかを明示します。「担当者よりメールをさせていただきます」「担当者よりお電話をさせていただきます」のように、次に起こるアクションを具体的に記載することで、ユーザーの不安を取り除きます。

URLパラメータによるフォーム切替

1つのLIFFアプリで複数のフォームパターンを表示するために、URLパラメータを活用しています。type=email でメール連絡フォーム、type=phone で電話連絡フォーム、type=meeting でオンライン面談フォームと、パラメータに応じてフォームの内容を動的に切り替えます。

HTML上には全パターンのフィールドを非表示で配置しておき、JavaScriptで該当するフィールドだけを表示します。この方式のメリットは、LIFFアプリの登録が1つで済むことです。LINE Developersコンソールでの管理が簡素になり、CSPCORSの設定も1箇所で完結します。

Flex Messageのボタンには、それぞれ異なるtypeパラメータ付きのLIFF URLを設定します。LINEで相談する場合はpostbackアクションでテキスト応答、それ以外はURIアクションでLIFFを起動するという使い分けです。

まとめ

LIFFを活用した連絡フォームは、LINE公式アカウントの運用を次のレベルに引き上げます。重要なポイントを整理します。

LIFFサイズはcompactから始めるのが安全です。入力項目が増えてきたらtallに変更し、複雑なUIが必要な場合のみfullを使います。

CSPの設定は最も注意が必要な工程です。static.line-scdn.net、api.line.me、liffsdk.line-scdn.net、liff.line.me、access.line.me の5ドメインを正しく設定し、デプロイ後は必ずブラウザコンソールで確認してください。

顧客データのupsertパターンにより、一度入力した情報を再利用できる仕組みを整えることで、ユーザー体験が格段に向上します。「何度も同じ情報を入力させない」という基本を、LINE上の体験でも実現できるのがLIFFの強みです。