個人開発で最大の関門の1つが「決済機能をどう付けるか」だ。本稿では Next.js 製の個人サービスに Stripe を組み込み、最初の1円を受け取るところまでを 3時間で完了させた手順を、後追いできる形でまとめる。テストモードのまま動かすので、本番に出す前に必ず本番鍵に切り替える前提だ。
0. 完成イメージ
- ユーザーが「購入」ボタンを押す
- Stripe Checkout の決済画面に遷移
- カード情報を入力して決済
- 成功画面に戻ってくる
- Webhook でサーバ側に「決済完了」が通知される
- DB のユーザーレコードに
is_paid = trueを立てる
1. Stripe アカウント開設(15分)
stripe.com で無料登録。事業情報は後でも入れられる。ダッシュボードの右上が「テストモード」になっていることを確認。
「開発者」→「API キー」から、
-
pk_test_...(公開鍵) -
sk_test_...(秘密鍵)
を控える。
2. 商品と価格を作る(10分)
ダッシュボードの「商品」→「商品を追加」で、商品名と価格 (例: 1000円 / 一回払い) を作成する。生成された price_xxx という ID を控えておく。
3. 環境変数(5分)
# .env.local
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_xxxxx
STRIPE_SECRET_KEY=sk_test_xxxxx
STRIPE_PRICE_ID=price_xxxxx
STRIPE_WEBHOOK_SECRET=whsec_xxxxx
4. SDK インストール(5分)
npm install stripe @stripe/stripe-js
5. Checkout セッションを作る API(30分)
// app/api/checkout/route.ts
import Stripe from 'stripe'
import { NextResponse } from 'next/server'
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!)
export async function POST() {
const session = await stripe.checkout.sessions.create({
mode: 'payment',
payment_method_types: ['card'],
line_items: [
{ price: process.env.STRIPE_PRICE_ID!, quantity: 1 },
],
success_url: 'https://example.com/success?session_id={CHECKOUT_SESSION_ID}',
cancel_url: 'https://example.com/cancel',
})
return NextResponse.json({ url: session.url })
}
6. フロント側のボタン(15分)
'use client'
export default function BuyButton() {
const buy = async () => {
const res = await fetch('/api/checkout', { method: 'POST' })
const { url } = await res.json()
window.location.href = url
}
return <button onClick={buy}>1000円で購入</button>
}
これで、ボタンを押すと Stripe ホスティングの決済画面に飛ぶ。テスト用カード番号 4242 4242 4242 4242 で決済できる。
7. Webhook を受け取る(45分)
決済完了の確証は、必ず Webhook で取る。フロントの success_url の到達だけを根拠にしてはいけない(ユーザーがブラウザを閉じたら通知されない)。
// app/api/stripe/webhook/route.ts
import Stripe from 'stripe'
import { NextResponse } from 'next/server'
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!)
export async function POST(req: Request) {
const sig = req.headers.get('stripe-signature')!
const body = await req.text()
let event: Stripe.Event
try {
event = stripe.webhooks.constructEvent(
body,
sig,
process.env.STRIPE_WEBHOOK_SECRET!
)
} catch (e) {
return new NextResponse('invalid signature', { status: 400 })
}
if (event.type === 'checkout.session.completed') {
const session = event.data.object as Stripe.Checkout.Session
// DB を更新する
// await markUserAsPaid(session.customer_email!)
}
return NextResponse.json({ received: true })
}
ローカルで Webhook を受けるには Stripe CLI が便利。
stripe login
stripe listen --forward-to localhost:3000/api/stripe/webhook
whsec_... が表示されるので、STRIPE_WEBHOOK_SECRET に入れる。
8. 成功画面(15分)
// app/success/page.tsx
export default function SuccessPage() {
return (
<main className="p-10 text-center">
<h1 className="text-2xl font-bold">ご購入ありがとうございました</h1>
<p>すぐに有効化されます。</p>
</main>
)
}
9. テスト(30分)
- 通常決済:
4242 4242 4242 4242 - 3D セキュア:
4000 0027 6000 3184 - 失敗カード:
4000 0000 0000 0002
それぞれで挙動を確認し、Webhook が来ているか・DB が更新されているかを必ずチェックする。
まとめ
- Checkout を使えば、自前でカードフォームを書かなくて良い
- 決済の確証は必ず Webhook で取る
- 本番化する前に「本番鍵への切り替え」「事業情報の入力」「禁制商品でないか」の3点を確認
3時間あれば、個人開発に決済機能を後付けできる。「お金をもらう」体験ができると、開発のモチベーションが一段上がるので、ぜひ一度通してみてほしい。