【究極版】LINEチャットボットでサブスクの作り方
GPT-4と2人で力を合わせて実現した軌跡です。GPTを使ってもてこずったこと中心にまとめてあるので、素人でGPTを使ってやろうという方には役に立つのではないかと思います。
目次を見ていただくとわかりますが、記憶も入っているし、system promptも入れてあるので、ちゃんと動くところまでコピーできたら、キャラ付けなどもアレンジできると思います。
全般的なコツ
Herokuでディプロイすると次のような画面になることがあります。
「なにー!!!」とビビるんだけど、たいていは、コメントアウト戻すときに戻しちゃいけないところまでもとしちゃったとか、修正するときに "," や "}" をつけ忘れたという単純なミスなので、まずはそれを確認すべき。色々とガチャガチャ変更した後にそれに気づくと悲しくなる。
ディプロイはこまめに
上と同じですが、たくさん変更した後にディプロイでうまくいかないと、どこが引っかかってるのかわからなくなるので、こまめにした方がいいです。
特に、ひと段落ついて、いらないコメントアウトを複数のファイルで削除したりして、動かなくなると絶望です。コードの整理なら、ファイル1つずつで、念のため動くか確認しましょう。
古いコードは残しておく
ディプロイをこまめにもそうですが、ちょっと前にまともに動いた古いコードをちゃんと残しておくことも重要です。ガチャガチャやっているうちになぜか動かなくなるので、そんな時は、振り出しに戻します。特にGPTは(GPT-4でも)、その場のノリで適当なことを言うので、うまくいくときもあれば、全然めちゃくちゃになるときもあります。なんか変なことを言っているなと思ったら言うことを聞かない方がいいし、反論した方がいいです。変なことに気づけたらいいんですが、気づけない時もあるので、古いコードを取っておいて、そこからやり直すとうまくいくこともあります。
上手くいかなかったらログを出しまくる
とにかく、loggingで、いろんな部分のログを出して確認すると、GPTが直し方を教えてくれます。
ウェブアプリは、ブラウザのコンソールログも確認「F12」
少なくともChromeだとF12でブラウザのコンソールのログが確認できるので、そこでエラーが見つかることもあります。
それでは、さっそく中身に入っていきましょう!
キャッシュには気を付けて
キャッシュのせいで、コード変えたのに反映されないとかよくある。それに気づかずどんどんコードをいじると大変なことになる。キャッシュ削除し辺り、新しくブラウザを開いてやる方がいいことが多い。
1. LIFFとstripeを連携する
アカウントの準備とローカルへのクローン
基本はこちらのブログにある通りです。
GithubのREADMEのが分かりやすいかも。
基本はこれをローカルにクローンして、Herokuにあげるだけです。
LIFFアプリ、stripeサブスク商品も作ります(まずはテストモードで)。
URLの設定がわかりにくいですね。LIFFアプリのエンドポイントURLはHerokuのアプリの公開URLです。
Herokuの環境変数に入れるDOMAINは、決済後に飛ぶ先を指定するserver.jsのstripe.checkoutの下記のコードで使われます。
success_url: `${domainURL}/success.html?session_id={CHECKOUT_SESSION_ID}`,
cancel_url: `${domainURL}/cancel.html`,
つまり、自分の管理しているウェブサイトに例えば、https://example.com/stripe-success/やhttps://example.com/stripe-cancel/というページを作って、環境変数のDOMAINにはhttps://example.comを登録するという感じです。さらにこの場合は、server.jsを次のように変更する必要がありますね。(Githubにあげた後の方がやりやすいかも)
success_url: `${domainURL}/stripe-success/?session_id={CHECKOUT_SESSION_ID}`,
cancel_url: `${domainURL}/stripe-cancel/`,
clientフォルダにsucsess.htmlとcansel.htmlがあるので、DOMAINは使わず、そちらにつなぐという手もあります。私はこちらで設定。
※app-nameは、Herokuのアプリの名前、つまり、前半はHerokuのアプリのURLです。LINEのエンドポイントに設定したのと一緒
success_url: `https://app-name.herokuapp.com/success.html?session_id={CHECKOUT_SESSION_ID}`,
cancel_url: `https://app-name.herokuapp.com/cancel.html`,
stripeの審査がなかなか通らなかったのですが、概要に「決済は即時です」と入れて、ウェブサイトに「特定商取引法に基づく表記」を入れたらすぐに通りました。
.envの編集は私はnanoを使いましたが、他でもいいと思います。
私はローカルで試験するのが面倒だったので、すぐにHerokuに挙げてしまいました。そうすると開発中にHerokuを回しまくるのでお金がかかります。私はログインまでで0.3ドルかかりました。
Herokuにpush
Herokuに挙げる手順としては、ローカルからHerokuにプッシュして、次に、Githubのリポジトリを作ってそこにもプッシュして、HerokuアプリをGithubのリポジトリに同期してという流れです。
ローカルのHeroku CLIからHerokuへの接続はIDとトークンで行う方法とSSHを使う方法で2種類あります。途中、トークンで接続がうまくいかなくなり、SSHに切り替えたらすぐにできました。
Herokuへのプッシュの仕方はこちらにありますね。
githubへのプッシュは先にリポジトリを作る必要がある。コマンドからはうまくいかず、ブラウザでからのリポジトリを作って、そこにプッシュした。
ローカルからHerokuへの接続は、複数の端末を使っていたりすると頻繁に使うので、こちらにも掲載しておきます。
【Ubuntu】
アプリの作成:
heroku create -a アプリ名
接続の確認:
※Heroku ~ と出てくればOK
git remote -v
すでにあるアプリへの接続:
※HerokuにSSH接続していないと、権限の問題を指摘されるかも
heroku git:remote -a アプリ名
Hirokuのアプリの管理画面のDeploy→Deployment methodでgithubを選び、githubに上げたリポジトリを選択する。
Github上で調整
あとはHerokuのDeployのDeploy Branchして、エラーを消していく。
なんとなくポートを3000に変えたりしています。絶対に必要なのは、.envをHerokuの環境設定に移行することです。
一度あげてしまえば、Github上でserver.js や client/index.jsを編集できるので楽です。編集→ディプロイの繰り返しです。実際に決済を行う必要があるので、stripeはテストモードで。
Deployタブの下部、Deploy Branchを押すとすぐDeployできます。これをしないとコードを編集して登録しないと反映されないので、初めは引っかかってました。
とりあえず、server.js にget-liff-idのエンドポイントを追加する必要がありますね。
app.get("/get-liff-id", (req, res) => {
res.send({
liffId: process.env.LIFF_ID,
});
});
決済時にstripeにLINEのアカウント情報を送る
ログインの設定で一番大変だったのは、LINEのIDとstripeのアカウントの接続です。stripe.checkout からmetadataとして送ることになっていますが、stripeの決済に反映されません。
始めはまったく理由がわからなかったんですが、"xxx"を飛ばしたり、userId+"xxx"を飛ばしたりして、stripeのイベントデータの部分にはmetadata: ["line_user": "~"]として飛んでいることがわかりました!
ただ、内容は、undefined!
Ubuntuのコマンドからheroku logs --app アプリ名 でログを確認して(ログの出力の仕方はGPTが教えてくれます)、id tokenが期限切れになっていることがわかりました。
そこで、再ログインをさせたのですが、今度は、購入→再ログインのループに。
一応、再ログイン後にキャッシュを削除させるようにしたけど、だめ。
// 再ログイン後に古いトークンを使わないようにキャッシュの回避
app.use(function(req, res, next) {
res.setHeader('Cache-Control', 'no-cache, no-store, must-revalidate');
res.setHeader('Pragma', 'no-cache');
res.setHeader('Expires', '0');
next();
});
client/index.jsをこんな感じに変更して、再ログイン後に新しいトークンを取得しろと明記したがだめ。
.then(function (response) {
if (response.status === 401) { // 401 is the status code for unauthorized
return response.json().then(data => {
if (data.action === "relogin") {
alert("セッションがタイムアウトしました。再度ログインしてください。");
liff.login({ redirectUri: location.href }).then(() => {
if (liff.isLoggedIn()) {
// 新しいIdTokenをサーバーから取得する
fetch("/get-new-idtoken")
.then(response => response.json())
.then(data => {
if (data.token) {
lineIdToken = data.token;
}
});
}
});
}
throw new Error(data.error);
});
}
return response.json();
});
server.js に次の通りエンドポイントを追加。
app.get("/get-new-idtoken", (req, res) => {
// LINE APIを使用して新しいIdTokenを取得するロジックをここに書きます。
res.json({ token: idToken });
});
なぜかここで突然うまくいくが、後日試したらダメ。
※何らかの操作で、ブラウザが切り替わったことで、トークンがリフレッシュされたよう。
結局、これはダメみたい。頑張ったけど、直らないので、あきらめる…もしかするといろいろ変更する必要なく、元のclient/index.js のままでよかったのかも。
↓のやり取りによると、「一度getIDTokenを実行すると、localstorageにidTokenが保持され、次回以降のgetIDTokenではlocalstorageからidTokenを取得しているようです。」とのこと。LINEの仕様なのかな。
アプリからやるとか、ブラウザを開きなおして再度ログインすれば問題ないです。
とりあえず、ログインアプリ完成!
(同)実践サイコロジー研究所は、心理学サービスの国内での普及を目指しています! 『適切な支援をそれを求めるすべての人へ』