Use Cases

Cara Integrate Payment Systems (Stripe) dengan Claude Code

Pelajari cara integrate payment systems (stripe) menggunakan Claude Code. Dilengkapi contoh kode praktis dan panduan langkah demi langkah.

implementasiにClaude Codeを使うメリット 決済sistem

決済sistem セキュリティ dan 正確性 求めらpentingな機能.Claude Code Stripe best practices 沿ったimplementasi generateし、Webhookpemrosesanやerrorハンドリング 見落 dan し 防ぎ.

Stripe Checkoutによる決済

> Stripe Checkout 使ったsubscription決済 implementasikan.
> フリー・プロ・エンタープライズ 3プラン dengan 。
> berhasil・キャンセル時 リダイレクト konfigurasi juga.

checkoutsessionのpembuatan

// src/services/payment-service.ts
import Stripe from 'stripe';

const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, {
  apiVersion: '2024-12-18.acacia',
});

const PLANS = {
  pro: {
    priceId: process.env.STRIPE_PRO_PRICE_ID!,
    name: 'Pro',
  },
  enterprise: {
    priceId: process.env.STRIPE_ENTERPRISE_PRICE_ID!,
    name: 'Enterprise',
  },
} as const;

type PlanKey = keyof typeof PLANS;

export class PaymentService {
  async createCheckoutSession(userId: string, plan: PlanKey) {
    // 既存 Stripe顧客 pencarian、なければpembuatan
    let customer = await this.findOrCreateCustomer(userId);

    const session = await stripe.checkout.sessions.create({
      customer: customer.id,
      mode: 'subscription',
      payment_method_types: ['card'],
      line_items: [
        {
          price: PLANS[plan].priceId,
          quantity: 1,
        },
      ],
      success_url: `${process.env.APP_URL}/payment/success?session_id={CHECKOUT_SESSION_ID}`,
      cancel_url: `${process.env.APP_URL}/pricing`,
      metadata: { userId, plan },
      subscription_data: {
        trial_period_days: 14,
      },
    });

    return { url: session.url };
  }

  async createPortalSession(userId: string) {
    const customer = await this.getCustomerByUserId(userId);

    const session = await stripe.billingPortal.sessions.create({
      customer: customer.id,
      return_url: `${process.env.APP_URL}/settings/billing`,
    });

    return { url: session.url };
  }

  private async findOrCreateCustomer(userId: string) {
    const user = await prisma.user.findUnique({ where: { id: userId } });
    if (!user) throw new Error('User not found');

    if (user.stripeCustomerId) {
      return stripe.customers.retrieve(user.stripeCustomerId) as Promise<Stripe.Customer>;
    }

    const customer = await stripe.customers.create({
      email: user.email,
      metadata: { userId },
    });

    await prisma.user.update({
      where: { id: userId },
      data: { stripeCustomerId: customer.id },
    });

    return customer;
  }

  private async getCustomerByUserId(userId: string) {
    const user = await prisma.user.findUnique({ where: { id: userId } });
    if (!user?.stripeCustomerId) throw new Error('No Stripe customer found');
    return stripe.customers.retrieve(user.stripeCustomerId) as Promise<Stripe.Customer>;
  }
}

APIルートのpengaturan

// src/app/api/checkout/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { PaymentService } from '@/services/payment-service';
import { getSession } from '@/lib/auth';

const paymentService = new PaymentService();

export async function POST(req: NextRequest) {
  const session = await getSession();
  if (!session) {
    return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
  }

  const { plan } = await req.json();
  if (!['pro', 'enterprise'].includes(plan)) {
    return NextResponse.json({ error: 'Invalid plan' }, { status: 400 });
  }

  const checkout = await paymentService.createCheckoutSession(session.userId, plan);
  return NextResponse.json(checkout);
}

Penanganan Webhook

> Stripe webhookhandler implementasikan.
> subscription pembuatan・pembaruan・キャンセル pemrosesanして。
> 署名verifikasi implementasikan juga.
// src/app/api/webhooks/stripe/route.ts
import { NextRequest, NextResponse } from 'next/server';
import Stripe from 'stripe';
import { prisma } from '@/lib/db';

const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);

export async function POST(req: NextRequest) {
  const body = await req.text();
  const signature = req.headers.get('stripe-signature')!;

  let event: Stripe.Event;
  try {
    event = stripe.webhooks.constructEvent(
      body,
      signature,
      process.env.STRIPE_WEBHOOK_SECRET!
    );
  } catch (err) {
    console.error('Webhook signature verification failed');
    return NextResponse.json({ error: 'Invalid signature' }, { status: 400 });
  }

  switch (event.type) {
    case 'checkout.session.completed': {
      const session = event.data.object as Stripe.Checkout.Session;
      await handleCheckoutCompleted(session);
      break;
    }

    case 'customer.subscription.updated': {
      const subscription = event.data.object as Stripe.Subscription;
      await handleSubscriptionUpdated(subscription);
      break;
    }

    case 'customer.subscription.deleted': {
      const subscription = event.data.object as Stripe.Subscription;
      await handleSubscriptionCanceled(subscription);
      break;
    }

    case 'invoice.payment_failed': {
      const invoice = event.data.object as Stripe.Invoice;
      await handlePaymentFailed(invoice);
      break;
    }
  }

  return NextResponse.json({ received: true });
}

async function handleCheckoutCompleted(session: Stripe.Checkout.Session) {
  const userId = session.metadata?.userId;
  if (!userId) return;

  await prisma.user.update({
    where: { id: userId },
    data: {
      plan: session.metadata?.plan || 'pro',
      stripeSubscriptionId: session.subscription as string,
      subscriptionStatus: 'active',
    },
  });
}

async function handleSubscriptionUpdated(subscription: Stripe.Subscription) {
  const user = await prisma.user.findFirst({
    where: { stripeSubscriptionId: subscription.id },
  });
  if (!user) return;

  await prisma.user.update({
    where: { id: user.id },
    data: { subscriptionStatus: subscription.status },
  });
}

async function handleSubscriptionCanceled(subscription: Stripe.Subscription) {
  const user = await prisma.user.findFirst({
    where: { stripeSubscriptionId: subscription.id },
  });
  if (!user) return;

  await prisma.user.update({
    where: { id: user.id },
    data: {
      plan: 'free',
      subscriptionStatus: 'canceled',
      stripeSubscriptionId: null,
    },
  });
}

async function handlePaymentFailed(invoice: Stripe.Invoice) {
  const customerId = invoice.customer as string;
  const user = await prisma.user.findFirst({
    where: { stripeCustomerId: customerId },
  });
  if (!user) return;

  // 支払いgagal notifikasiメール pengiriman
  await sendPaymentFailedEmail(user.email);
}

料金表komponen

function PricingTable() {
  const handleCheckout = async (plan: string) => {
    const res = await fetch('/api/checkout', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ plan }),
    });
    const { url } = await res.json();
    window.location.href = url;
  };

  return (
    <div className="grid gap-6 md:grid-cols-3">
      <PricingCard
        name="Free"
        price="$0"
        features={['dasar機能', '月5proyek']}
        buttonText="現在 プラン"
        disabled
      />
      <PricingCard
        name="Pro"
        price="$1,980/月"
        features={['全機能', '無制限proyek', '優先サポート']}
        buttonText="Pro アップグレード"
        onSelect={() => handleCheckout('pro')}
        highlighted
      />
      <PricingCard
        name="Enterprise"
        price="$9,800/月"
        features={['全機能', 'SSO', '専任サポート', 'SLA保証']}
        buttonText="お問い合わせ"
        onSelect={() => handleCheckout('enterprise')}
      />
    </div>
  );
}

Summary

Dengan Claude Code, Stripe決済 integrasi checkout dari Webhookpemrosesanま amanかつefisien implementasi bisa dilakukan.決済 セキュリティ 直結 untuk 、コードレビュー 必ず行い.proyek 決済方針 CLAUDE.md 記述 こ dan お勧め.コード品質 維持 refactoring otomatisasi juga pemanfaatan .

Claude Code 詳細 Anthropic公式dokumen silakan lihat.Stripe implementasipanduan Stripe公式dokumen juga bisa dijadikan referensi.

#Claude Code #Stripe #payments #subscription #TypeScript