stripe-stack

安装量: 40
排名: #18120

安装

npx skills add https://github.com/scientiacapital/skills --skill stripe-stack

Add payments to a Next.js + Supabase project: Install Stripe: npm install stripe @stripe/stripe-js Add env vars (see quick_reference below) Create idempotency table (see schema below) Choose workflow: setup-new-project.md or add-webhook-handler.md // Lazy-loaded Stripe client import Stripe from 'stripe' ; let stripe : Stripe | null = null ; export function getStripe ( ) : Stripe { if ( ! _stripe ) { _stripe = new Stripe ( process . env . STRIPE_SECRET_KEY ! , { apiVersion : '2025-12-15.clover' } ) ; } return _stripe ; } Integration is successful when: Webhook handler uses database-backed idempotency (not in-memory) All keys in environment variables (never hardcoded) Test mode fully working before any live mode deployment Signature verification on all webhook endpoints Event logging before processing (insert-before-process pattern) Go-live checklist completed before production deployment Core Principles Idempotency is Non-Negotiable ALL webhook handlers MUST use database-backed idempotency Never use in-memory Sets (lost on serverless cold starts) Insert event record BEFORE processing, not after Test/Live Mode Separation Use environment variables for ALL keys (never hardcode) Test keys: sk_test , pk_test_ , whsec_test_ Live keys: sk_live_ , pk_live_ , whsec_live_ Products/prices must be recreated in live mode Shared Stripe Account All NetZero Suite projects share ONE Stripe account Same webhook secret can be used across projects Each project has its own webhook endpoint URL Lazy Client Initialization Never initialize Stripe at module level (build errors) Use factory function pattern for server-side client Check for API key before creating instance What Are You Building? Before proceeding, identify your use case: Use Case Workflow Description New project setup-new-project.md Fresh Stripe integration from scratch Add webhooks add-webhook-handler.md Add webhook handler to existing project Subscriptions implement-subscriptions.md Recurring billing with plans Credit system add-credit-system.md Pay-as-you-go credits Go live go-live-checklist.md Test → Production migration Workflow Routing If setting up Stripe in a new project: → Read workflows/setup-new-project.md → Then read reference/environment-vars.md → Use templates/stripe-client.ts and templates/env-example.txt If adding webhook handling: → Read workflows/add-webhook-handler.md → Then read reference/webhook-patterns.md → Use templates/webhook-handler-nextjs.ts and templates/idempotency-migration.sql If implementing subscription billing: → Read workflows/implement-subscriptions.md → Then read reference/pricing-models.md → Use templates/plans-config.ts If adding credit/usage-based system: → Read workflows/add-credit-system.md → Then read reference/pricing-models.md If migrating test → production: → Read workflows/go-live-checklist.md Quick Reference Environment Variables (Standard)

Server-side (never expose to client)

STRIPE_SECRET_KEY

sk_test_ .. . STRIPE_WEBHOOK_SECRET = whsec_ .. .

Client-side (safe to expose)

NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY

pk_test_ .. .

Optional: Price IDs (for test→live switching)

STRIPE_PRICE_STARTER_MONTHLY

price_
..
.
STRIPE_PRICE_PRO_MONTHLY
=
price_
..
.
Common Webhook Events
Event
When It Fires
Action
checkout.session.completed
Customer completes checkout
Create subscription record
customer.subscription.created
New subscription starts
Initialize user limits
customer.subscription.updated
Plan change, renewal
Update plan/limits
customer.subscription.deleted
Cancellation
Downgrade to free
invoice.paid
Monthly renewal success
Reset usage counters
invoice.payment_failed
Payment failed
Mark as past_due
Stripe Client Pattern
let
_stripe
:
Stripe
|
null
=
null
;
export
function
getStripe
(
)
:
Stripe
{
if
(
!
_stripe
)
{
const
key
=
process
.
env
.
STRIPE_SECRET_KEY
;
if
(
!
key
)
throw
new
Error
(
'STRIPE_SECRET_KEY not configured'
)
;
_stripe
=
new
Stripe
(
key
,
{
apiVersion
:
'2025-12-15.clover'
,
typescript
:
true
}
)
;
}
return
_stripe
;
}
Idempotency Table Schema
CREATE
TABLE
stripe_webhook_events
(
id
TEXT
PRIMARY
KEY
,
-- Use Stripe event ID directly
type
TEXT
NOT
NULL
,
-- Event type
data
JSONB
NOT
NULL
,
-- Full event payload
processed_at TIMESTAMPTZ
DEFAULT
NOW
(
)
)
;
Webhook Handler Structure
export
async
function
POST
(
request
:
NextRequest
)
{
const
body
=
await
request
.
text
(
)
;
const
signature
=
request
.
headers
.
get
(
'stripe-signature'
)
;
// 1. Verify signature
const
event
=
stripe
.
webhooks
.
constructEvent
(
body
,
signature
,
webhookSecret
)
;
// 2. Check idempotency (BEFORE processing)
const
{
data
:
existing
}
=
await
supabase
.
from
(
'stripe_webhook_events'
)
.
select
(
'id'
)
.
eq
(
'id'
,
event
.
id
)
.
single
(
)
;
if
(
existing
)
return
NextResponse
.
json
(
{
duplicate
:
true
}
)
;
// 3. Log event (INSERT before processing)
await
supabase
.
from
(
'stripe_webhook_events'
)
.
insert
(
{
id
:
event
.
id
,
type
:
event
.
type
,
data
:
event
,
}
)
;
// 4. Process event
switch
(
event
.
type
)
{
case
'checkout.session.completed'
:
await
handleCheckout
(
event
.
data
.
object
)
;
break
;
// ... other handlers
}
return
NextResponse
.
json
(
{
received
:
true
}
)
;
}
Integration Notes
Works With
Supabase
Use service role client for webhook handlers (bypasses RLS)
Prisma
Alternative to Supabase for idempotency table
Vercel
Add runtime/maxDuration config for webhook routes
Next.js App Router
Use request.text() for raw body
返回排行榜