HonoとChrome内蔵AIを使ったWebアプリにPAY.JPでサブスク決済機能をつけたい！！！

こちらの記事は [オンライン決済サービスPAY.JPを使ってみた情報をシェアしよう！ by PAY Advent Calendar 2024](https://qiita.com/advent-calendar/2024/payjp-01) の18日目の記事です。

## はじめに

はじめましての人ははじめまして、こんにちは！フロントエンドエンジニアのがっちゃん（ [@gatchan0807](https://x.com/gatchan0807) ）です。
ちょっと調べてみたら、前回Qiitaに記事を書いたのは2020年1月だったので、実に4年ぶりぐらいの記事投稿です。下書きをみたら8年前の記事とかもゴロゴロ残っていて、そんなに時間が経ったのか…と思いつつ、ずっと運営されているQiitaさんすげぇなぁとも思ったりしました。

せっかく久々に戻ってきたのでしっかり誰かの役に立つようにナレッジを残しておきます。

今回わたしは、 **生成AIを使ったWebアプリにオンライン決済サービスのPAY.JPを使ったサブスクリプション機能を搭載する方法を調べた（というか実装した）時の知見** を記事にしようと思います。

昨今 流行っている生成AIを個人開発Webアプリに使っていい感じのサービス作ってみたいけど、トークン単位の課金でいくらかかるか読めなくて、クラウド破産以上に破産が近い感じがしてずっと二の足を踏んでいました。
そこにこのカレンダーを見つけて「そうだ。サブスク決済機能をつけておけばドメイン代と数人〜数十人のユーザーのAI利用料ぐらいは賄えるかも」と思い、筆（VSCode）を取った次第です。

そんな形で作ったWebアプリケーションについての紹介をご覧ください。

## つくったもの

今回作ったのは、**シンプルなAI献立提案アプリ**です。
ボタンを押すと、生成AIが今日の晩御飯にちょうどいい料理を提案してくれます。

https://www.youtube.com/watch?v=3NDPQaW0Wtk

URL: https://gatchan-pay-jp-hono-ai-sandbox.pages.dev/

特徴としては、**無料でオフラインでも使える「機能限定版AI」と有料サブスクリプションを登録していないと使えない「プレミアム版AI」の2つがある**ところです

これを実現するためにPAY.JPを使っているのですが、決済画面のUI実装はPAY.JPのCheckout機能に任せて楽できたし、顧客管理やサブスクリプション管理はPAY.JPのAPIの先に保存ができているので、私が作ったWebアプリの手元に保存されているのはPAY.JPのAPIから発行された `customer_id` / `subscription_id` / `plan_id` だけにできて、こちらで管理しているデータがめちゃめちゃシンプルになりました。

PAY.JP自体は「テストモード」というので使っていて、実際にカード会社やPAY社に申請して決済できるようにする「本番申請」というのをやっていない状態なのですが、テストモードでもすでにデータの流れと実際のAPI仕様を触れているので、あとはビジネス面での審査を通るように利用規約を作ったり諸々するだけでいい状態まで持っていけてるのがとても良いなと思っています👏

## 技術構成

今回のアプリケーションの技術構成は以下のとおりです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/108554/166b5ac8-3bc6-f938-023a-1ce765446900.png)


このWebアプリを作る上でやっぱり個人開発なので初めて使うものも混ぜなきゃね！というテンションで半分は新しい技術スタックを入れて実装してみました。

【初めて使ったもの】
- Hono
- Cloudflare（Workers | Pages）
- PAY.JP（API | Checkout）

【使ったことがあるもの】
- Gemini Nano in Chrome（Chrome組み込みのAI。詳しくは[公式Docs](https://developer.chrome.com/docs/ai?hl=ja)に記載）
- OpenAI API（過去はGPT-3.5-turboを使ってたのでGPT-4o-miniははじめて）
- TailwindCSS

所感としては、
- Hono & Cloudflare: 
  - 便利だし書き味がシンプルで使いやすい。Next.jsもRemixも触ってきたけど、個人開発のアプリケーション開発これで全然こと足りる…！こりゃ流行るわ…👏
- PAY.JP: 
  - API仕様書もWebで見やすく公開されてるので、読み落としがあってハマることはあっても困ることなかった
  - 今回はCloudflareからリクエストするので意図的にシンプルなHTTP Requestで実装したけど、色んなSDKもあるからもっと楽できそう
  - 今回はCheckoutでモーダル埋め込んだけど、自分でCSS当ててフォームだけiframeを埋め込む形でPAY.JP。みたいなこともできるみたいなのでカスタマイズ性高いし良い👏

という感じです。どれも今後の個人開発やお仕事を受けた時とかに使えそうだな！という感覚を持てたので触ってよかった技術でした。

<details>
<summary>余談）AIに与えたプロンプトの話</summary>

AIに与えたプロンプト自体は何の変哲もないもので、プロンプトで指示しているポイントとしては以下の2つぐらいです

- 日本の食卓で出すのに向いたディナーの献立を出してください
- 人気・有名なものを5種類ピックアップしてください

個人的にChat用AIの性能評価に献立提案を使うことが多いのでこのWebアプリになりました。

**献立提案は生成AIにとって意外とむずかしい**ようで、AIモデル内にある日本食の知識、世界の食事の知識、かんたんに作れる料理とは？の定義がうまくできているかどうかで出力が変わるし、毎回同じ料理を提案してしまうか？現実的に作れそうな料理を提案するか？料理の説明（特に日本食）が間違っていないか？も、**一般人の私にも感覚があって判断しやすい**です。

また、その前段でそもそも日本語出力でぶっ壊れないか？（ハングルやアラビア語の文字が混ざらないか？）なども見られて、個人的に気に入っているAI評価観点です。

**今回使ったGemini Nano in Chromeはオンデバイスモデルとしてはまあ、許容できる生成結果が出ていますし、プレミアム用に利用しているGPT-4o-miniは一発でかなり精度の良いものを出してきたので即決定**しました。

プレミアム用のAIモデルに関しては、最初はCloudflare AIで利用できるモデルをPlaygloundで試していましたが、どれも日本語出力の点で微妙で、llama、mistral、gemmaあたりはまだまだ日本語出力を単体で現実的なレベルのクオリティを出せないんだなぁ…。と感じました。

今回機能限定版の方のGemini Nano in Chromeでやったように「英語で出力→翻訳APIを噛ませる」という形ならある程度クオリティを出せそうなのは見えましたが、今回はそこを選択しませんでした。（プレミアム側で使うクラウド上のAI側の出力結果を改善するためにプロンプトをめちゃこだわるつもりも、マルチLLM構成にするつもりもなかった）
</details>

### リポジトリ

「技術者なら言葉じゃなくて、作ったもので語りなさい。」って田舎のばっちゃんが言ってたので以下のGitHubリポジトリでコードは公開してます🙋（コミットログ上の試行錯誤も込みで見られてしまうのは恥ずかしい）

https://github.com/gatchan0807/pay-jp-hono-ai-sandbox

いずれ自分が個人開発で生成AIを使ったWebアプリを本格的に作る時にこれベースで作ればいいか〜とか考えていたりするので、一応、最低限再利用しやすいように作っています。

（エラーハンドリングの甘さやCookieに値を入れてるところとかはご愛嬌🙏 本リリースじゃないので…という言い訳🙏）

## こだわり・伝えておきたいポイント

### サブスクアプリに必要そうな課金状態の検証処理を作る

これがこのWebアプリを作る中で一番試したかったもので、 **決済情報の取得とサブスクリプションの登録をどうやるのか？登録されているのを検証した上でどう有料機能にアクセス許可を行うのか？** というのをHonoのAPI上に実装して試していました。

実際にはログイン機構を作ってそのアカウント情報と紐付けたり、サブスクプランの更新前や決済時にSendGrid等を使ってメール連絡をしたり、顧客データの保存とそれに基づいた体験の実装が別途必要ではありますが、 **決済周りのベースになるのは「登録」と「検証」の機能だったので、それらが重厚な審査などもなく手軽に試せてよかった** です。

詳細は以下のGitHub上でコードを読んでみてもらえればと思います。

https://github.com/gatchan0807/pay-jp-hono-ai-sandbox/blob/master/src/hooks/fetchPayJp.ts

また、API仕様を調べている時に気づいたのですが PAY.JPは定期課金の決済が成功した時のWebhook送信（Webアプリが受信側）にも対応しているようなので、気が向いたらWebhookを受け取るエンドポイントを立ててSendGridでメール送信する機能も試しに作ってみようかなと思っています。

[https://pay.jp/docs/webhook](https://pay.jp/docs/webhook)

### Chromeに搭載されたAIを使うことで無料お試しプランを作る

:::note warn
Chrome搭載のAI（Gemini Nano in Chrome）は最新版のChromeを使っていて、なおかつChromeの設定から実験的機能をONにしないと使えないのでご注意ください
:::

具体的な動作はここで見てもらうとイメージしてもらいやすいかもです

https://youtu.be/24lNgyDAmEY

こちらはプロダクト価値提供的な話で、先日、私がイベント登壇で発表した「Client-side AIでお試し・無料版を作って提供する」を本当に実現できるのか？を試す意味でChromeに搭載されたAI（Gemini Nano in Chrome）を使う機能を実装していました。

https://www.docswell.com/s/gatchan0807/ZN1V6G-2024-11-23-110000#p35

現状、Gemini Nano in Chromeは設定なしで誰でも使えるわけではないですし、以下のような課題があるのですがフロントエンドエンジニア的にはすごく未来を感じているので、色々使い方を模索していたりします。

【Gemini Nano in Chromeの課題】

- **オリジントライアルというフェーズの機能で最新のChromeを使っていれば誰でもすぐ使えるわけではない**
  - [こういう設定の対応](https://qiita.com/pitao/items/f1355b8002e360f83c93#%E8%A8%AD%E5%AE%9A%E3%81%AE%E6%9C%89%E5%8A%B9%E5%8C%96)が必要なので結構手間。誰でもやれる作業ではないと思っている
  - これはドメインを申請することで設定をしなくても使えるようにもできるので、今回のWebアプリを申請しておけばよかった…と記事を書きながら後悔しています
  - https://developer.chrome.com/docs/web-platform/origin-trials?hl=ja
- **Chrome以外のブラウザで使えるようになる未来が全然見えない**
  - Chromeチームが主導していて、[こんな感じでAPI仕様を公開](https://docs.google.com/document/d/1VG8HIyz361zGduWgNG7R_R8Xkv0OOJ8b5C9QKeCjU0c/edit?tab=t.0)してくれているもののSafariやFirefoxで同様のAPIで同レベルのモデルをブラウザに搭載するのは現実的じゃないよな…と思いながら使っています
  - とはいえ、PWAに関わっていた時に学んだ「プログレッシブエンハンスメント」の考え方で使えるようにするのもいいのかな？と思っていたりします
- **生成AIモデル自体の要求スペックが一定高いのでPCからのアクセスであっても誰でも満足に使えるスペックではないことがある**
  - これは登壇資料に書いたものと同じで、銀の弾丸にはならないだろうな…というやつです
  - もっと軽くて誰でも使える生成AIモデルがみんなのPCに入ったら嬉しいなぁ

### Chromeに搭載されたAIとAIベースの翻訳機能を組み合わせる

:::note warn
こちらのChrome搭載のAIベースの翻訳機能も最新版のChromeを使っていて、なおかつChromeの設定から実験的機能をONにしないと使えないのでご注意ください
:::

詳細は[こちらの公式の解説記事](https://developer.chrome.com/docs/ai/built-in?hl=ja)に譲るのですが、Chromeに搭載されるAIはプロンプトを受け取ってレスポンスするパターンと、特定タスクに特化したレスポンスをするパターンの2つがあります。

特定タスクに特化したChrome搭載のAIは **「翻訳」、「要約」、「言語判定（英語なのかフランス語なのか等の判定）」** の3つがあり、どれもプロンプトを受け取るパターンのAIよりも早くChromeに搭載されそうなフェーズだったりします。

これらのタスクをオフラインでも生成AIでいい感じにできる。というのは未来を感じられて楽しみにしてえいます。
以下の資料でも言及していますので合わせてご覧ください。

https://www.docswell.com/s/gatchan0807/ZN1V6G-2024-11-23-110000#p27

今回のWebアプリでは半ば制約の回避方法として使ったのですが、 **プロンプトを受け取ってレスポンスするAI（Prompt API）を使うには英語出力をさせるしかないので、その出力されたものをもう一度生成AIに入れて翻訳をしてもらう。というフローで機能限定版AIを実装** しています。

:::note info
プロンプトを受け取ってレスポンスするChrome搭載のAI（Prompt API）は、2024年12月時点も英語の出力しか許しておらず、それ以外文字が出そうになった場合はエラーを返す仕様になっています。

https://docs.google.com/document/d/1VG8HIyz361zGduWgNG7R_R8Xkv0OOJ8b5C9QKeCjU0c/edit?tab=t.0#heading=h.wbeunk8mizh
:::

実装の詳細はコードを見ていただければと思います。

https://github.com/gatchan0807/pay-jp-hono-ai-sandbox/blob/2b8197208f57fadde448cf21d8d0455827a34062/src/csr/pages/ai.tsx#L41-L78

この実装時に、うまくMarkdown to HTML変換ができずに2時間ハマってたのは、また別のお話。
（最初は「生成→翻訳→HTML変換」という流れで実装していたのですが、「生成→HTML変換→翻訳」という流れにしたら直りました。）

# おわりに

久々にがっつり個人開発でWebアプリを作ってみた記録をまるっと全部公開してみました。アプリケーション自体は大した機能を持っているものではないですが、技術検証をするにはとても良い題材だったなと思いました。

また、今回使った技術はどれも個人開発エンジニアに合った技術スタックだったなと感じたので、引き続き使っていければと思っています🙋

いつかPAY.JPやHono、生成AIを使ったWebアプリを本格的に作って、個人開発で副収入が得られるぐらいになれるよう頑張りたいな〜とか妄想していたりします。そんなイケてるエンジニアになれるようにがんばっていくぞ〜

記事を読んでみて、内容よかったなと思われたらぜひ「いいね」を1つ、ポチッと押しておいていただけたりすると、とても励みになります。よろしくお願いします🙏

ここまで読んでいただきありがとうございました！