とある日の昼下がり。今日もLIGではエンジニア談義に花が咲いている。
づや:最近の生成AIの進化はほんとすごいよね。これからは、生成AIでWebサービスを作るのがコモディティ化していくんじゃないかと思うよ。
づや(高遠 和也)株式会社LIGの取締役兼CTO 1983年生まれ。SIerとしてのキャリアをスタートし、JavaやC#を中心に多岐にわたる開発プロジェクトにエンジニアとして参加。その経験を活かし、LIGを創業。バックエンドおよびフロントエンドエンジニアとしての深い知識と経験をもとに、多様なプロジェクトに従事。社内の体制やルールの最適化、AI技術の推進など、経営戦略の一翼を担う。 |
ありさん:いままでは、設計からデザイン、コードを書いて、テストして……みたいな流れで、少なくとも数週間〜数ヶ月はかかってたのに、今は自然言語で指示するだけで動くものが作れちゃうらしいじゃないですか。
ありさん(村田 ありさ)武蔵野美術大学卒業後に起業。その後、株式会社LIGに入社しデザインやプロジェクトマネジメントの実務経験を積む。2022年、BASE株式会社に「Pay ID」デザイナーとして入社。オンライン決済の分野で知識を深め、2024年からはPAY株式会社にてオンライン決済サービス「PAY.JP」のマーケティングとデザインを担当している。 |
づや:そうそう、あのスピード感はすごい! 簡単にプロトタイプを作れるし、アイデアさえあれば、誰でもサービスを立ち上げられる時代になった。
ありさん:そうなると、Webサービス開発の敷居が劇的に下がる分、次の課題って「どうやって収益化するか」だと思うんですよね。
づや:本当にそう。APIがシンプルで誰もが扱いやすくて生成AIとの相性がいい決済サービスがあったら、それこそすぐにでも収益化できるよね。
ありさん:ですよね。あっ! それなら、今私が勤めているPAY株式会社の決済サービス『PAY.JP』を使ってみてくださいよ!
PAY.JPとは?
- PAY株式会社が提供するオンライン決済サービス。シンプルなAPIと豊富なライブラリにより、クレジットカード決済を簡単に導入することができます。クレジットカード決済、定期課金、Apple Pay、プラットフォーム決済、通知機能など、必要な機能を1つのアカウントで全て利用が可能。料金体系は、スタートアップ・ベンチャー企業向け、 Web・アプリ制作会社向け、旅行業・旅行代理店業を営む会社向け、NPO・寄付決済向けと柔軟なプランが用意されており、自社のビジネスに沿って選択できます。
づや:そういや、転職したって言ってたな。
ありさん:はい! APIがシンプルで扱いやすいので、生成AIで作ったWebサービスにも簡単に決済機能を追加できますよ。
づや:でも、月額利用料金とか手数料とかお高いんでしょ?(笑)
ありさん:それが、月額無料で手数料も3.3%で導入できるんですよ。せっかくなんで、「PAY.JP」を実装したWebサービスを、これから作ってみませんか? 私も手伝いますよ。
づや:えっ、本当? でも、そんな時間ないしなー。
ありさん:生成AIでコンテンツの作成から決済サービスの組み込みまでやれば、短時間でできるんじゃないですか?
づや:確かに。それは試してみる価値あるかも……。じゃあ、作ってみようかな(……収益化できるいいチャンスだし)。
目次
生成AIでWebサービスを作ってみる!
ありさん:それで、づやさん、何を作成するんですか?
づや:実は前から考えていたことがあって、『LIGのハロウィン物語』を有料記事にしたいなって。
【LIGのハロウィン物語とは】
- 1年に1回、ハロウィンシーズンに突如更新される脱力系コンテンツ。仕事に疲弊した社員がカボチャのかぶり物をしてコンプライアンス無視で演じるジャンル不明の読み物。なぜだか2012年から10年以上も継続している(100年やっても完結するか疑問)。読んでも何の役にも立たないが、LIGの社風はよく分かるかもしれない。ちなみに課金せずとも上記のリンクから全話無料で読めます。
ありさん:……(えっ、あれを売るんか?)
づや:LIGブログの隠れ人気コンテンツだからね。これを書籍のように読めるコンテンツにしたいんだ。本の体裁だったら、お金を払う人もいそうでしょ?
ありさん:まあ、なんでもいいです。とりあえず取りかかりましょう。
づや:あ、はい……。よろしくお願いします!
書籍UIのコンテンツを制作する
づや:最初のプロンプトはこれで行こう。「本を読んでるみたいなUIできますか?」
ありさん:ずいぶんシンプルなプロンプトですね。
づや:これには理由があってね、多くの条件を羅列した長いプロンプトを入れるとAIは全ての要件を満たそうとしてしまい、あまり良い結果にならないんだよ。ざっくりしたイメージから始めるほうがいいね。
ありさん:なるほどー。あっ、さっそくイメージが表示されましたね。
づや:微妙だな。全然本っぽくないのでイメージ画像とプロンプトを追加しよう。
ありさん:だんだん、本っぽいレイアウトになってきましたね。
づや:よし、ここに『LIGのハロウィン物語』の画像とテキストを流し込んでみよう。
ありさん:あれ? 画像が表示されませんね?
づや:こうなったときも、原因は何だろう? とあれこれ悩まなくていい。
- 生成AI
- 申し訳ありません。画像が表示されない問題に対処しましょう。画像の配置と参照方法を修正します
ありさん:あっ! 表示されましたよ! プロンプトで「画像が見れません」って伝えるだけでコードを修正してくれるんですね! これ自分で直すってなったらすごい大変なことなんですよね。
づや:だねえ。細かいデザインもどんどん指示していくよ。
ありさん:どんどん指示が反映されて、イメージ通りのUIになっていきます。AI速い!
づや:ただ、全部のページの素材をAIに入れて作らせるのではなく、ある程度機能やフォーマットが完成したらダウンロードして、ローカル環境で整えた方がいいね。生成AIってずっと覚えてられる会話の量に制限があるので、すべてを作らせるのは逆に非効率なんだ。
ありさん:なるほど、どこでエラーが起きているのかが複雑だとより分からなくなってしまいますもんね。
(……スタートから50分ほどが経過)
づや:できたーーーー! イメージ通りの見た目と動きになったよ。
・
・
ありさん:1時間もかかりませんでしたね! 普通にコードを書いていたら、どのぐらいかかりましたか?
づや:普通の人なら2日はかかるかな。僕だったら8時間ぐらいだけど。いや、つまずいたら1日かかっちゃうかな……。
ありさん:だんだん自信なくなってきてる。
づや:さて、いよいよ決済機能の「PAY.JP」を組み込んでいくとしよう!
ありさん:はい!
いざ「PAY.JP」を実装!!
どうもづやです。ここまでまったく技術的なことを話してなかったのですが、状況を説明しますと、実は「Next.js」を用いてサイトを作っていて、ローカルにて動作するようになった状態です。
「PAY.JP」には、決済を実装する仕組みが色々とあるみたいなのですが、とりあえず、まずはアカウント登録を行います。
さくっと管理画面に入れるようになりました。これにてテストできる状態になった模様。
おそらく、API設定にあるこの辺りのAPIキーを使って実装していく感じですね。
さて「PAY.JP」の準備も整ったみたいですし、早速組み込みを開始したいと思います。ここからは集中力が必要そうですので、づやの一人称にてお届けいたします!
ドキュメントを見ていると何個か方法があるみたいですが、まずは、1番お手軽そうな「チェックアウト機能」を試したいと思います
以下のようなJSを読み込むだけでいいみたいですね。
<script type="text/javascript" src="https://checkout.pay.jp/" class="payjp-button" data-key="pk_test_0383a1b8f91e8a6e3ea0e2a9" data-submit-text="トークンを作成する" data-partial="true">
</script>
だがしかし、「dangerouslySetInnerHTMLで読み込むだけで動くのかなぁ」と思っていたら、やっぱりちゃんと動かない(笑)。
調べてみるとReact系で使うには、ちょっと一手間かかりそうだったため、それならきちんとAPIを呼ぶほうが早いし、汎用性があるなと思ったので、方向転換します。
ただ、これだけでセキュアな決済機能を付けられるのは、実にありがたい機能だと思います!
改めて、APIを使い、いざ「PAY.JP」を実装!!
というわけでAPIを使った実装に切り替えます(キリッ!)。
公式のガイドをみると、payjp.jsを読み込んで、APIを叩いていく感じですね
以下が、ざっくりとした流れ。
- https://pay.jp/docs/payjs-guidanceを読み込む
- クライアントサイドでトークンを生成する
- サーバサイドでトークンを用いて、決済APIをたたく
それでは、早速取り掛かります!
悪戦苦闘しながらも、とりあえず疎通できるところまでできました。
クライアントサイド Formコンポーネント
import { useEffect, useRef } from "react";
const PayForm = () => {
const payjpRef = useRef(null); // Payjpオブジェクト
const cardRef = useRef(null); // cardインスタンス
useEffect(() => {
const script = document.createElement("script");
script.src = "https://pay.jp/docs/payjs-guidance";
script.async = true;
script.onload = () => {
const payjp = (window as any).Payjp("ここに公開餓を。envからとるのがいんじゃないかな"); // 公開鍵
payjpRef.current = payjp;
const elements = payjp.elements();
const card = elements.create("card");
card.mount("#card-element"); // カード情報入力フィールドをマウント
cardRef.current = card; // cardインスタンスをrefに保存
};
document.body.appendChild(script);
return () => {
const existingScript = document.querySelector("script[src='https://js.pay.jp/v2/']");
if (existingScript) {
document.body.removeChild(existingScript);
}
};
}, []);
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
if (!payjpRef.current || !cardRef.current) {
alert("Pay.jsが初期化されていません。");
return;
}
try {
const payjp = payjpRef.current; // refからPay.jpオブジェクトを取得
const card = cardRef.current;
// トークンを生成
const result = await payjp.createToken(card);
console.log("Token generation result:", result); // トークン生成結果をログ出力
if (result.error) {
console.error("Token generation error:", result.error);
alert("エラーが発生しました:" + result.error.message);
return;
}
const token = result.id; // トークンIDを取得
if (!token) {
console.error("Token is undefined");
alert("トークンの生成に失敗しました。");
return;
}
console.log("Token generated:", token);
// サーバーにトークンを送信
const response = await fetch("/api/charge", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ token }),
});
if (!response.ok) {
console.error("Server error:", await response.text());
alert("サーバーエラーが発生しました。");
return;
}
alert("決済が完了しました!");
} catch (err) {
console.error("Submission error:", err);
alert("予期せぬエラーが発生しました。");
}
};
return (
<form>
<div id="card-element">{/* Pay.jsがここに入力フィールドを挿入する */}</div>
<button type="submit">支払う</button>
</form>
); }; export default PayForm;
サーバサイド API通信コンポーネント
import { NextResponse } from "next/server";
const PAYJP_SECRET_KEY = "ここにシークレットキーを。envからとるといいよ";
const PAYJP = require("payjp")(PAYJP_SECRET_KEY);
export async function POST(request: Request) {
try {
const body = await request.json();
const { token } = body;
if (!token) {
return NextResponse.json({ message: "Token is required" }, { status: 400 });
}
// 支払い処理
const charge = await PAYJP.charges.create({
amount: 5000, // 支払額(例: 5000円)
currency: "jpy",
card: token, // 生成されたトークンを使用
});
return NextResponse.json(charge, { status: 200 });
} catch (error: any) {
console.error("Error processing payment:", error);
return NextResponse.json({ message: "Error processing payment", error: error.message }, { status: 500 });
}
}
何点かハマったポイントとしては、最初Form内のinput要素は事前に用意していたのですが、トークンの発行が上手くいきませんでした。
渡している値をpayjs側から吐き出してもらうことで疎通できるようになりました。
あと環境の問題なのか、トークンの発行ができた後、ローカルホストにてローカルマシンで開発をしていたのですが、トークンの発行処理がタイムアウトしてしまい処理が進まない自体に。
AIに相談したりしたのですが、何をしても解決せず(もしかしたらローカル環境でHTTP通信をしてたのかも……?)。諦めかけたその時、サーバにおいて疎通したら、あっさりと解決できました(笑)。
とりあえず疎通はできましたが、金額固定とかなので実際に使う際はもう少し改修が必要ですね(笑)。
実際決済はできるけど、連絡先もわからないのでは使い物にならないため、以下のように変更を加えました。
・メールアドレスを入力してもらい、そのメールアドレスに情報を送れるようにする
・販売の時に金額がわかるようにし、製本希望というオプションをつけて金額を変更する
これで決済データにメールアドレスが加わるので、連絡もとれるしPDFでデータを送れるのでサービスとして成り立つはず!
※ほんとに販売する場合は、個人情報保護方針とか必要ですが今回はサンプルなので割愛
コードの大きな変更点はこちらです。
まずFormはメールアドレスとチェックボックスが追加されて、それをAPIに渡すようにしています。
【Form側の変更】
メールアドレスと金額をAPIに渡します。
// サーバーにトークンを送信
const response = await fetch("/api/charge", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ token: token, email, amount }),
});
本題からずれますが、生成されるFormのstyleはcreateの引数で渡せます。
const card = elements.create("card", {
style: {
base: {
color: "#000000", // テキストカラー
fontFamily: "inherit", // フォントを継承
fontSize: "14px", // サイズ(h-8に合わせる)
padding: "0", // デフォルトのパディングをリセット
border: "1px solid #EFEEF1", // メールアドレスと同じボーダー
borderRadius: "8px", // メールアドレスと同じ角丸
backgroundColor: "#ffffff", // 背景色
"::placeholder": {
color: "#9CA3AF", // プレースホルダの色
},
},
invalid: {
color: "#EF4444", // エラーカラー
iconColor: "#EF4444", // エラーアイコンカラー
},
},
});
【API側の変更】
メールアドレスと金額をうけとります。
支払い処理の前にcustomerを作成し、その情報を支払い処理に渡します。
const body = await request.json();
const { token, email, amount } = body;
/** ここでバリデーションしてます */
// カスタマーを作成
const customer = await PAYJP.customers.create({
email, // メールアドレスを登録
card: token, // カードトークンを登録
});
// 支払い処理
const charge = await PAYJP.charges.create({
amount,
currency: "jpy",
customer: customer.id,
});
さて、テスト用カードを入れて決済してみると……
成功時に表示するよう設定した画面が表示されました!!
管理画面でも売上になっていることを確認!!
売上詳細に顧客情報が表示されています!
顧客詳細を見ると、入力したメールアドレスが保存されました!
やったーー!!! テスト環境で完成だーーーー!!!
生成AIのみで作ったWebサービスが完成!
ありさん:すごーい! 完成しましたよー!
づや:本当に、ほとんど生成AIだけで作れたよ! やばい、今、俺、ちょっと感動してる……。
▼完成したWebサービスがこちら!
https://halloween.liginc.co.jp/
※テスト購入可能ですが、入力されたメールアドレスがテスト環境に保存されますので、そちらに同意の上お触りください。
ありさん:わずか数時間で作ったとは思えない完成度ですよ。すごい!
づや:『LIGのハロウィン物語』が、Webサービスになって、さらに収益化も実現できた。ありさん本当にありがとう!
ありさん:こちらこそありがとうございました!
づや:ちなみにだけど、今回は「PAY.JP」の口座をLIGで作ったけど、これ個人でもいけるのかな?
ありさん:はい、個人でも使えます。なので、クリエイターなどの皆さんにも、どんどん使っていただきたいですね。
づや:そうだったのか! だったら個人でやれば良かった。社員にはユーザーとしての体験が必要だと言って強制的に買わせればフトコロも潤う。
ありさん:なかなか、あくどいこと考えますね。さすが、づやさん、AIにはなしえない発想です。まあ、実際に売れるかは別問題ですけどね。
づや:それな。
「PAY.JP」への疑問を直接ありさんにぶつけてみた!
づや:そういえば、「PAY.JP」を触ってて気になったことがいくつかあったんだけど、聞いていい?
ありさん:はい、なんでも聞いてください!
づや:今回、テストモードで作ったけど、実際に売る場合にやらなければいけないことってあるの?
ありさん:テストモードでの動作確認が終わったら、本番環境のAPIキーを取得するために、本番利用申請を行うんですが、そのためには事業者情報や入金先の銀行口座情報、ウェブサイトや販売内容の確認資料が必要ですね。
づや:組み込みからどのぐらいの時間で本番運用できるの?
ありさん:申請から最短3〜4営業日で本番利用が可能です。
づや:ちなみに審査が落ちやすいものってあるの?
ありさん:そうですね。購入者にとってリスクが高いと判断される商材やサービスの場合、審査が厳しくなることがあります。例えば、情報商材や前払い制の予約販売など、購入者保護の観点から慎重に審査されることが多いですね。
ですので、スタートアップや新規事業でまだ市場に出ていない新しい商材を扱う場合は、事業計画や商材の信頼性を明確に示すことが重要です。
もし申請内容に不明点があったり、手続きに不安がある場合は、「PAY.JP」のサポートチームが適切なアドバイスやサポートを提供しますので、安心してください。
づや:それって組み込み時もサポートしてくれるの?
ありさん:はい! ヘルプページ、チャットbot、メールでのサポートに対応しています。
「PAY.JP」 を利用して支払いシステムを組み込む方法については、さまざまなドキュメントをご用意していますし、また、さまざまな言語やプラットフォームに対応したライブラリもご提供しています。
づや:(ご用意? ご提供?? ……なんか急に営業トークっぽくなってきたな)あ、そうそう、ぶっちゃけ「PAY.JP」ってセキュリティはどうなの?
ありさん:はい! セキュリティについてですが、購入者のクレジット情報の不正利用を防止するため、国際基準に準拠したセキュリティで安全なシステムを提供しています。「PAY.JP」がすべてのカード番号を保護するとともに、インフラシステムは、機能・要件ごとに厳格なファイアウォールとアクセス制御の元、運用されています。
づや:ふーん。あっ、料金体系とか聞いてなかった!
ありさん:月商に応じて3つのプランがございます。
スタンダードプラン | ビジネスプラン | エンタープライズプラン |
・月額費用無料 ・決済手数料3.3% 月商400万円未満の事業者向け。 |
・月額費用20,000円(税込) ・決済手数料2.78% 月商400万〜2,000万円未満の事業者向け。 |
・月額費用50,000円(税込) ・決済手数料2.59%(Visa/Mastercard)、2.7%(JCB/American Express/Diners/ ClubDiscover) 月商2,000万円以上の事業者向け。 |
づやさまには、スタンダードプランがおすすめかと。
づや:(づやさまって言っちゃってるじゃん)確かに安いかも。ちょっと考えてみようかなぁ〜。
ありさん:ぜひ! アカウントを作成いただければすぐに始められますので!
づや:(な、なんか、怖いな……)あ、ありさん、今日はありがとう。
ありさん:こちらこそありがとうございました! 引き続きご検討のほど、よろしくお願いいたします!
まとめ
最後、少しありさんが怖かったですが、生成AIと「PAY.JP」のおかげで、忙しいエンジニアでも短時間で収益化が可能なWebサービスを作れることがわかりました。とても有意義な1日だったと思います。
それにしても、これまでも多くの決済サービスを実際に使ってきましたが、「PAY.JP」は、登録してから開発できるまでのスピードが早いというのが、まず最初に感じたメリットですね。
思いついたアイデアから、決済サービスの実装までをスムーズに進められますし、本番利用の申請から最短3〜4営業日で決済が可能になる。このスピード感はサービスの開発者として実にありがたいです。
また、契約書をはじめ、あらゆるドキュメントが日本語で用意されているのも使いやすい点ですね。海外のサービスで日本語のドキュメントがある場合もありますが、翻訳されたものだから、訴訟などに発展したケースを考えると不安が残るのは事実。さらに、セキュリティ体制も万全で、セキュリティを第1に考える大企業でも安心して導入できるのではないでしょうか。
月商400万円以上であっても、スタンダードプランであれば、月額費用が無料というのは、Webでの収益化を考えている個人事業主やクリエイターに最もオススメできるポイントです。会社員の副業にも最適でしょうね。
まとめると、「PAY.JP」はこんな方達におすすめです。
- セキュリティを第一に考えている企業担当者
- Webでの収益化を考えている個人事業主やクリエイター
- 副業を考えている会社員
とにかく、生成AIと「PAY.JP」を使えば、アイデアから収益化までが一気に実現可能。こうした流れは今後加速していくでしょうし、さらにアイデア勝負の時代になりそうです。
「PAY.JP」は無料でアカウントが作成できるので、気になった方はぜひサイトにアクセスしてみてはいかがでしょうか。