0
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?

Lemon SqueezyでFree/Proプランのサブスクリプションを個人開発アプリに実装した

0
Posted at

はじめに

個人開発のWebアプリにサブスクリプション機能を実装しました。
Stripe の代替として Lemon Squeezy を使った理由と実装を解説します。

Lemon Squeezy を選んだ理由

  1. MoR(Merchant of Record): 消費税・VAT処理を代行してくれる
  2. 日本円対応: 円建て価格設定が可能
  3. API が簡単: Stripe より学習コストが低い
  4. ダッシュボードが使いやすい

チェックアウトセッションの作成

// api/create-checkout-session.js (Vercel Serverless Function)
export default async function handler(req, res) {
  const { userId, email, plan } = req.body

  const variantId = plan === 'yearly'
    ? process.env.LS_VARIANT_YEARLY
    : process.env.LS_VARIANT_MONTHLY

  const response = await fetch('https://api.lemonsqueezy.com/v1/checkouts', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${process.env.LEMONSQUEEZY_API_KEY}`,
      'Content-Type':  'application/vnd.api+json',
    },
    body: JSON.stringify({
      data: {
        type: 'checkouts',
        attributes: {
          checkout_data: { email, custom: { user_id: userId } },
        },
        relationships: {
          store:   { data: { type: 'stores',   id: process.env.LS_STORE_ID } },
          variant: { data: { type: 'variants', id: variantId } },
        },
      },
    }),
  })

  const { data } = await response.json()
  res.json({ url: data.attributes.url })
}

Webhook でサブスクリプション状態を管理

// api/lemonsqueezy-webhook.js
export default async function handler(req, res) {
  const event = req.headers['x-event-name']
  const payload = req.body

  if (event === 'subscription_created' || event === 'subscription_updated') {
    const userId = payload.meta.custom_data?.user_id
    const status = payload.data.attributes.status

    await supabase.from('subscriptions').upsert({
      user_id: userId,
      plan: status === 'active' ? 'pro' : 'free',
      ls_subscription_id: payload.data.id,
    })
  }

  res.status(200).json({ ok: true })
}

フロントエンドでのプラン分岐

// Pro機能へのアクセス制御
const isPro = subscription?.plan === 'pro'

{isPro ? (
  <AlertSettings />
) : (
  <div>
    <p>Pro プランで利用できます</p>
    <UpgradeButton />
  </div>
)}

まとめ

Lemon Squeezy を使えば個人開発者でも簡単にサブスクリプション機能を実装できます。
MoR として税務処理を代行してくれるので、海外ユーザーへの販売も安心です。

0
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
0
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?