Notes
開発記録
LINE認証で「1人1回」を
実現する設計

クーポン企画の条件は、たいてい「お1人さま1回まで」です。 紙の券なら1枚配れば済みますが、デジタルだと画面は無限にコピーできます。「1人」をどう定義するかが、そのまま設計の中心になります。
なぜLINEなのか
本人確認の手段としてまず考えるのはメールアドレスですが、これは使い物になりません。 使い捨てのアドレスがいくらでも作れるからです。電話番号は強いのですが、 飲食店の店頭で「電話番号を入れてください」は心理的な抵抗が大きすぎます。
専用アプリを配る案も検討しました。これは技術的にはいちばん自由が利きますが、1回の企画のためにアプリを入れる人はほとんどいません。 店頭で「アプリをダウンロードしてください」と言われた時点で、 そこで離脱するのが普通の反応です。
結果としてLINEに落ち着きました。理由は3つあります。
- 日本ではほぼ全員が持っていて、すでにログイン済みの状態にある
- アカウントの大量作成には電話番号が要るので、簡単には増やせない
- ログインの許可を1回押すだけで済む。新規登録の画面を作らなくていい
参加者から見ると、QRを読んで、LINEの「許可する」を押して、 年齢と簡単なアンケートに答える。ここまでで登録が終わります。アプリのインストールは不要で、標準のブラウザで動きます。
止めてよいのは3つだけ
不正対策を作り込むときに最初に決めたのは、「絶対に止まらない」を最優先にするということでした。
クーポンを使う瞬間、その人は店員さんの目の前に立っています。 そこでエラーが出たら、店員さんも困るし、その人はもう使ってくれません。 店舗からのクレームにもなります。不正を1件防ぐために正規の利用を10件止めるのは、割に合わない。
なので、利用を止めてよい条件を3つに絞りました。
- LINEでの本人確認が済んでいない
- すでに上限回数まで使っている
- 年齢確認を済ませていない
いずれも判定が一意で、誤って止める余地がありません。 それ以外の「怪しさ」は、記録するだけで通します。
この方針は細部にも効いています。たとえば、どの店舗のQRから来たかが分からない状態でも、 利用そのものは通します。店舗が未設定のまま記録しておいて、 あとから管理画面で割り当てる。データが足りないことを、止める理由にしない。
ボタンを2回押されたときに何が起きるか
ここが実装でいちばん神経を使ったところです。
電波の悪い店内で「使う」ボタンを押すと、反応が返ってこないので、もう一度押します。 このとき2つのリクエストがほぼ同時にサーバーへ届きます。 素朴に「使用済みか調べてから記録する」と書くと、両方が「まだ使っていない」と判断して、2件記録されます。1人1回のはずが2回になる。
既存の行に鍵をかける方法(行ロック)では、これは防げません。 まだ1件も無い状態なので、かける相手がいないからです。
そこで、行ではなく「この人 × この企画」という組み合わせそのものに鍵をかけるようにしました。データベースが持っている、キーを指定して取れる種類のロックです。
TX1: 鍵を取る → 既存0件を確認 → 記録 → コミット(鍵が外れる)
TX2: 鍵の解放を待つ → 既存1件を確認 → 記録しない
→ 「このクーポンは ○月○日 に利用済みです」と表示注目してほしいのは最後の行です。2回目はエラーにしていません。 「すでに使用済みです」という正常な画面を返します。
連打したのは不正ではなく、通信が遅かっただけです。 そこで赤いエラー画面を出すと、その人は「壊れた」と受け取ります。 利用済みの日時を穏やかに表示するほうが、事実としても正確です。
スクリーンショットで使い回せない画面
もう1つ、店頭で起きる問題があります。 利用画面を撮影して友達に送れば、その画像を見せて何度でも使えるのではないか。
対策として、利用画面は一度きりの表示にしています。 「使う」を押した瞬間に記録が確定し、その画面は二度と同じ状態では出てきません。 再度開くと「利用済み」の画面になります。撮った画像を見せても、 店員さんの手元で同じ画面が再現できないので消し込めません。
あわせて、参加者ごとに番号を発行しています。 店員さんは番号と画面の状態を目で見るだけで済み、店舗側に端末やアプリを置く必要がありません。 店舗にお願いすることが「QRを貼る」と「画面を見る」の2つだけになるので、 店舗募集の説明が一言で終わります。これは実務上とても大きい差でした。
どこで詰まったかを、あとから見る
認証は、うまくいっているときは誰も何も言いません。 問題は、失敗したときにその人が黙って去ることです。
なので、認証の各段階を記録しています。 QRを読んだ、LINEに飛んだ、戻ってきた、年齢確認を終えた、アンケートを終えた、 クーポンを使った。どの段階で何人減ったかが管理画面に出ます。
実際、これで見つかった不具合があります。 特定の経路から来た人だけが認証に失敗していて、 問い合わせは1件も来ていませんでした。数字を見ていなければ気づかないままでした。
もう1つ、SNSアプリの中のブラウザから開かれた場合の扱いも入れています。 アプリ内ブラウザはCookieの扱いが特殊で、認証が通らないことがあります。 これを検知して、標準ブラウザで開き直す案内を出しています。 そしてどの画面でエラーが出ても「QRを読み取ってください」に戻れる導線を 全画面に置きました。復帰できることが、止まらないことと同じくらい大事です。
この設計は、クーポン以外にも同じ形で使える
ここまで書いてきたことを抽象化すると、こうなります。
QRで文脈(どの場所から来たか)を受け取り、LINEで人を1つに定める。 「この人がこの権利を1回使った」を、二重にならないように記録する。
この形は、クーポンの利用だけのものではありません。
- 入場の受付: 事前登録した人が、当日1回だけ入場する
- スタンプラリー: 1つのスポットに1回だけ到達を記録する
- 整理券・抽選: 1人1枚だけ受け取る
記録する対象が「割引の利用」か「入場」か「スポット到達」かが違うだけで、 本人確認も、二重記録の防止も、画面の使い回し対策も、そのまま同じものが使えます。 実際、いま入場受付の形での提供を準備しています。
汎用の基盤を先に作ってからクーポンを載せたわけではなく、 クーポンを作りきったら結果的に汎用だった、という順番でした。 目の前の1件をちゃんと作ると、だいたいこうなります。
ほかの開発記録

地図が真っ白のまま出ない ― ピンだけ見えるときに疑う3つ
ピンは正しい位置に出るのに、地図そのものが出ない。原因は3つあり、どれも「描画が始まらない」という同じ形をしていました。現地で使う画面なので、待ち方を変えて直した記録です。

集計から1名だけ消えていた話
同じ回答データから出した2つの集計が、5,937名と5,938名でずれました。深夜2時に回答した1名が月別のバケツから落ちて、黙って消えていた話です。期間の定義を変えたときに、どこを直し忘れるのか。

アンケートのクロス集計から報告書PDFを自動生成するまで
集計と作図に数週間かかっていた報告書を、管理画面のボタン1つで出せるようにしました。表の組み立て方、紙のレイアウトでつまずいた点、そして67MBのPDFが出てしまった原因の話。

不正利用の監査を、どうやって「無かった」と証明するか
公費や協賛金が原資の企画では、終了後に必ず「不正はなかったのか」と聞かれます。自動でブロックしないと決めたうえで、端末の指紋・時間帯・エリア横断など4つの見方を重ねて、記録から答えるための設計です。