LINEの通知が同じ内容で2回来る。しかも変なタイミングで。「さっきも受け取ったのに、なんでまた?」という状態が、ある案件で起きていました。
サービスの内容はLINEを使ったサブスク型のボット。ユーザーがフォームで情報を入力すると、AIが応答し、決済はStripeで管理する構成です。LINE、AI、Stripe、全部GAS1本でつないでいました。しばらく動いていたのに、あるタイミングからLINEに二重送信が出始めた。
原因の仮説
GASには doPost という、外から叩かれたときに動く関数があります。LINEもStripeも、イベントが起きると「外から叩く」仕組み(Webhook)を使います。友だち追加があればLINEが叩く、決済が完了すればStripeが叩く。それを同じ1つの doPost で受けていた。
問題はここです。GASは「誰が叩いてきたか」を自分では判定しません。どちらのWebhookが来ても、同じ関数が走る。LINEのイベントのつもりで処理が始まったのに、Stripeのリクエストだったり、その逆だったり。タイミングが重なると、片方のメッセージをもう一度送ってしまう。
二重送信の犯人はGASのコードのバグではなく、エンドポイントを共用していたことでした。
AIへの頼み方
私が自分でゴリゴリ書いたわけではなく、AIに日本語で渡して作ってもらいました。出した依頼はこんな内容です。
このGASを分割したい。LINEBOTにメッセージを送る機能と、STRIPEとやり取りする機能をそれぞれ別にしてほしい。
LINEとSTRIPEのWebhookが競合していると思う。変なタイミングで以前受信済みのメッセージをもう一回受け取ることがある。おそらくフックのトリガーがどちらか判別できないから、メッセージが2回送られる。エンドポイントを分ければ解決するんじゃないかと思っている。
「なぜ分割したいか」の理由を入れたのがポイントです。「Webhookが競合しているから」という仮説ごと渡すと、分割の仕方も適切なものが返ってきます。ただコードを2つに切り分けるだけでなく、それぞれのデプロイ後のURLをどこに登録し直すかまで一緒に整理してもらえました。
分割後の構成
分割前は1つのGASプロジェクトに全部が同居していました。分割後はざっくりこうなります。
LINE公式アカウント
↓ Webhook(友だち追加・メッセージ)
LINE専用GAS(独自URL)
↓ AIへ処理を投げる
Dify(AI応答)
Stripe
↓ Webhook(決済完了・サブスク更新)
Stripe専用GAS(独自URL)
↓ スプレッドシートに会員情報を書き込む
それぞれ別のGASプロジェクトとしてデプロイするので、URLが別になります。LINE Developersの管理画面にはLINE専用のURLを、StripeのWebhook設定にはStripe専用のURLを入れる。これで2つのサービスが同じ関数を奪い合うことがなくなります。
分割だけでは済まなかった部分
分割すること自体は単純でした。ただ、コードを切り出した後に整合が取れていない箇所が出てきました。
スプレッドシートのユーザー情報を参照する処理が、LINE側のGASとStripe側のGASの両方から使われていたので、共通で読むための仕組みをどちらに置くか決める必要がありました。片方に持たせて、もう片方からAPIで呼ぶ形にしましたが、GASはデフォルトで外からのCORSリクエストを弾く設定になっているため、JSONPで回避する必要が出てきました。
「分割したら全部解決」とはいかなかった。でも、二重送信のトラブルはそこで消えました。他の詰まりは分割後の調整作業の話であって、競合の話とは別です。
同じ構成になりがちなケース
LINEとStripe、両方をGASで動かしているシステムは思ったより多い印象です。1つのスクリプトで全部賄おうとするのは最初は楽なのですが、Webhookが複数になった瞬間にこの問題の候補になります。
判断の目安は「外から叩いてくるサービスが2つ以上あるか」。あるなら、最初からGASのプロジェクトを分けておく方が後で楽です。後から分割するのはできますが、共通部分の整理が要るので地味にコストがかかります。
最初に全部1本でつなぐこと自体は悪くないと思っています。動くことを確認してから分割する、でも間に合います。ただ分割の判断は「二重送信が出てから」より「Webhookが2種類になった時点」でやった方が、後始末が少なくて済みます。
