2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【個人開発】Stripe 連携で「買ってもらう」機能を3時間で実装した全手順

2
Posted at

個人開発で最大の関門の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時間あれば、個人開発に決済機能を後付けできる。「お金をもらう」体験ができると、開発のモチベーションが一段上がるので、ぜひ一度通してみてほしい。

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?