stripe-stack

v2026.09.24

Stripe integration patterns for Next.js + Supabase - payments, subscriptions, webhooks, credit systems, checkout. Use when: stripe, payments, billing, subscription, webhook, checkout, add payments to project.

GitHub
安装命令
npx skhub add scientiacapital/stripe-stack
Markdown
SKILL.md
<objective> Guide Stripe payment integration for Next.js + Supabase projects with production-ready patterns for webhooks, subscriptions, and credit systems. Ensures idempotency, proper test/live mode separation, and secure key handling. </objective>

<quick_start> Add payments to a Next.js + Supabase project:

  1. Install Stripe: npm install stripe @stripe/stripe-js
  2. Add env vars (see quick_reference below)
  3. Create idempotency table (see schema below)
  4. 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;
}

</quick_start>

<success_criteria> 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 </success_criteria>

<essential_principles>

Core Principles

  1. 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
  2. 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
  3. 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
  4. 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

</essential_principles>

<intake>

What Are You Building?

Before proceeding, identify your use case:

Use CaseWorkflowDescription
New projectsetup-new-project.mdFresh Stripe integration from scratch
Add webhooksadd-webhook-handler.mdAdd webhook handler to existing project
Subscriptionsimplement-subscriptions.mdRecurring billing with plans
Credit systemadd-credit-system.mdPay-as-you-go credits
Go livego-live-checklist.mdTest → Production migration
</intake> <routing>

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

</routing>

<quick_reference>

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

EventWhen It FiresAction
checkout.session.completedCustomer completes checkoutCreate subscription record
customer.subscription.createdNew subscription startsInitialize user limits
customer.subscription.updatedPlan change, renewalUpdate plan/limits
customer.subscription.deletedCancellationDowngrade to free
invoice.paidMonthly renewal successReset usage counters
invoice.payment_failedPayment failedMark 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 });
}

</quick_reference>

<integration_notes>

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

Related Skills

  • supabase-sql-skill - For database migrations
  • create-hooks-skill - For post-deployment notifications

GitHub Repository

Private templates and examples available at: github.com/ScientiaCapital/stripe-stack

</integration_notes>

<reference_index>

Reference Files

FilePurpose
reference/webhook-patterns.mdIdempotency, event handling, error recovery
reference/pricing-models.mdPlans vs Credits vs Usage-based billing
reference/environment-vars.mdStandard env var conventions
reference/common-errors.mdTroubleshooting guide

Template Files

FilePurpose
templates/webhook-handler-nextjs.tsComplete webhook route (copy-paste)
templates/stripe-client.tsLazy-loaded client factory
templates/plans-config.tsSubscription plan definitions
templates/idempotency-migration.sqlSupabase migration
templates/webhook-handler.test.tsTest template
templates/env-example.txtStandard .env template

Workflow Files

FilePurpose
workflows/setup-new-project.mdFresh Stripe integration
workflows/add-webhook-handler.mdAdd webhook to existing project
workflows/implement-subscriptions.mdSubscription billing
workflows/add-credit-system.mdPay-as-you-go credits
workflows/go-live-checklist.mdTest → Production migration

</reference_index>

Emit Outcome Sidecar

As the final step, write to ~/.claude/skill-analytics/last-outcome-stripe-stack.json:

{"ts":"[UTC ISO8601]","skill":"stripe-stack","version":"1.0.0","variant":"default",
 "status":"[success|partial|error]","runtime_ms":[estimated ms from start],
 "metrics":{"webhooks_configured":[n],"products_created":[n],"checkout_flows_built":[n]},
 "error":null,"session_id":"[YYYY-MM-DD]"}

Use status "partial" if some stages failed but results were produced. Use "error" only if no output was generated.

发现
标签

此技能尚未发布标签。

版本
最新版本元数据

版本

v2026.09.24

发布时间

2026年9月24日

分类

未分类

许可证

未指定

源路径

active/stripe-stack-skill

默认分支

main

最新提交

9e03af3

Tree SHA

5b28287