ai-startup
CHAPTER 52 / 100
읽기 약 2분
FUNCTION
Stripe 구독 결제 완전 구현
핵심 개념
Checkout·Customer Portal·Webhook·Proration — 자동 결제 + 셀프 서비스.
본문
Stripe Subscription 흐름
1. 사용자 → 가격 페이지
2. "구독" 클릭 → Stripe Checkout (호스팅 페이지)
3. 결제 완료 → success_url 리다이렉트
4. Stripe webhook → 우리 DB 업데이트
5. 사용자가 Pro 기능 사용
6. 매월 자동 결제
7. 해지 시 — Customer Portal에서 셀프Stripe 셋업
pnpm add stripe @stripe/stripe-js// .env
STRIPE_SECRET_KEY=sk_test_...
STRIPE_PUBLISHABLE_KEY=pk_test_...
STRIPE_WEBHOOK_SECRET=whsec_...
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_...
// Stripe Dashboard:
// 1. Products 생성
// - Pro Monthly: $19/mo
// - Pro Annual: $190/yr (2개월 할인)
// - Business: $99/mo
// 2. Price ID 복사 (price_xxx)Checkout Session 생성
// app/api/checkout/route.ts
import Stripe from 'stripe';
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);
export async function POST(req: Request) {
const { priceId } = await req.json();
const supabase = await createClient();
const { data: { user } } = await supabase.auth.getUser();
if (!user) return new Response('Unauthorized', { status: 401 });
// Stripe Customer 생성/조회
let customerId = user.user_metadata.stripe_customer_id;
if (!customerId) {
const customer = await stripe.customers.create({
email: user.email,
metadata: { user_id: user.id },
});
customerId = customer.id;
await supabase.auth.updateUser({
data: { stripe_customer_id: customerId },
});
}
const session = await stripe.checkout.sessions.create({
customer: customerId,
mode: 'subscription',
line_items: [{ price: priceId, quantity: 1 }],
success_url: `${process.env.NEXT_PUBLIC_URL}/billing?success=true`,
cancel_url: `${process.env.NEXT_PUBLIC_URL}/pricing`,
allow_promotion_codes: true,
subscription_data: {
trial_period_days: 14,
metadata: { user_id: user.id },
},
});
return Response.json({ url: session.url });
}Webhook 처리
// app/api/webhooks/stripe/route.ts
import { headers } from 'next/headers';
export async function POST(req: Request) {
const body = await req.text();
const sig = (await headers()).get('stripe-signature')!;
let event: Stripe.Event;
try {
event = stripe.webhooks.constructEvent(body, sig, process.env.STRIPE_WEBHOOK_SECRET!);
} catch {
return new Response('Invalid signature', { status: 400 });
}
switch (event.type) {
case 'customer.subscription.created':
case 'customer.subscription.updated': {
const sub = event.data.object;
await db.user_plans.upsert({
where: { user_id: sub.metadata.user_id },
create: {
user_id: sub.metadata.user_id,
plan: priceIdToPlan(sub.items.data[0].price.id),
stripe_subscription_id: sub.id,
status: sub.status,
current_period_end: new Date(sub.current_period_end * 1000),
},
update: {
plan: priceIdToPlan(sub.items.data[0].price.id),
status: sub.status,
current_period_end: new Date(sub.current_period_end * 1000),
},
});
break;
}
case 'customer.subscription.deleted': {
const sub = event.data.object;
await db.user_plans.update({
where: { stripe_subscription_id: sub.id },
data: { plan: 'free', status: 'canceled' },
});
break;
}
case 'invoice.payment_failed': {
// 이메일 알림 + 재시도 안내
const invoice = event.data.object;
await sendPaymentFailedEmail(invoice);
break;
}
}
return Response.json({ received: true });
}
// Next.js — body raw 보장
export const runtime = 'nodejs';Customer Portal
// app/api/billing/portal/route.ts
export async function POST(req: Request) {
const { user } = await getUser(req);
const customerId = user.user_metadata.stripe_customer_id;
const session = await stripe.billingPortal.sessions.create({
customer: customerId,
return_url: `${process.env.NEXT_PUBLIC_URL}/billing`,
});
return Response.json({ url: session.url });
}
// 사용자가 셀프로:
// - 플랜 변경
// - 결제수단 변경
// - 인보이스 다운로드
// - 구독 해지Stripe Tax (자동 세금)
const session = await stripe.checkout.sessions.create({
// ...
automatic_tax: { enabled: true },
customer_update: {
address: 'auto', // 주소 → 세금 자동
},
});
// → 미국 sales tax, EU VAT 등 자동
// → Stripe Tax 활성화 ($0.5 per transaction)결제 실패 자동 처리 (Smart Retries)
Stripe Dashboard → Subscriptions → Settings:
- Smart Retries 활성
- 4번까지 자동 재시도
- 실패 시 자동 cancel
또는 Dunning email:
- 1일 후: "결제 실패, 카드 확인"
- 3일 후: "곧 서비스 중단"
- 7일 후: "구독 취소됨"다음 챕터
CH.53 "사용량 기반 과금: AI API 호출 단위".
AI 프롬프트
🤖 AI에게 잘 물어보는 법 — 모델·전략별 프롬프트
무료
월 $0 — 검증·시작 단계
Stripe 구독을 무료 도구만으로 시작하는 방법을 알려줘.
소자본
월 $20~50 — MVP·초기 운영
월 $20~50 예산으로 Stripe 구독을 검증·MVP 단계까지 진행하는 전략은?
프로덕션
월 $200~500 — 성장 단계
Stripe 구독을 프로덕션 단계로 확장할 때 필요한 도구·운영 체계는?
스택
풀스택 — 도구 조합 분석
2026년 Stripe 구독 관련 도구 5개를 조합한 추천 스택을 알려줘.
⭐ 이것만 기억하세요
Stripe 구독 결제 완전 구현은 이 3가지만 확실히 잡으세요
1.Stripe Checkout + Webhook + Portal = 완전 자동 결제
2.Webhook이 진실 — DB는 Stripe 따라감
3.Customer Portal로 셀프 서비스 — CS 부담 90% 감소
공유하기
진행도 52 / 100