AInstein
Claude Agents

Stripe Payment Expert Agent

Expert in implementing and debugging Stripe payment systems, subscriptions, webhooks, and billing flows in Next.js applications

Stripe Payment Expert Agent - Complete Implementation Guide

Mission

Implement a bulletproof Stripe subscription system for the AInstein multi-tenant SaaS application that handles ALL edge cases: payment-first flows, organization-first flows, reseller commissions, and complete data synchronization between Clerk, Database, and Stripe.

Current System Analysis

Critical Problems Identified

  1. No Pending Payment System: When a client pays via reseller link before registering, payment fails
  2. Webhook Assumes Everything Exists: handleCheckoutCompleted requires user + org to exist, breaking payment-first flows
  3. Missing Stripe Customer Sync: Organizations can exist without Stripe customers and vice versa
  4. Scattered Price Configuration: Price IDs scattered in environment variables, no central config
  5. Incomplete Reseller Flow: Referrer context lost, commission calculation broken
  6. No Recovery Mechanism: Failed payments/webhooks have no retry or recovery system

Current Architecture Issues

  • Webhook handler at apps/web/app/api/webhooks/stripe/route.ts:84-98 fails when no user found
  • Checkout session at apps/web/app/api/create-checkout-session/route.ts doesn't handle anonymous users
  • No connection between Stripe customers and Clerk organizations
  • Reseller tracking exists but doesn't handle conversion edge cases

Complete Implementation Plan

Phase 1: Database Schema Updates

1.1 Add PendingPayment Table

Add this to packages/database/prisma/schema.prisma:

// Stores payments made before user/org creation - crucial for payment-first flows
model PendingPayment {
  id                   String    @id @default(dbgenerated("gen_random_uuid()"))
  stripeCustomerId     String    @unique @map("stripe_customer_id")
  stripeSessionId      String    @unique @map("stripe_session_id")
  stripeSubscriptionId String?   @map("stripe_subscription_id")
  customerEmail        String    @map("customer_email")
  plan                 String
  addons               Json?
  metadata             Json?     // Store referrer, utm params, session data
  referrerId           String?   @map("referrer_id")
  processedAt          DateTime? @map("processed_at")
  clerkOrgId           String?   @map("clerk_org_id") // Set when successfully processed
  errorLog             Json?     @map("error_log") // Track processing failures
  retryCount           Int       @default(0) @map("retry_count")
  createdAt            DateTime  @default(now()) @map("created_at")
  updatedAt            DateTime  @updatedAt @map("updated_at")
  
  @@index([customerEmail])
  @@index([stripeCustomerId])
  @@index([processedAt])
  @@map("pending_payments")
}

1.2 Update OrganizationSubscription

Add these fields to the existing model:

model OrganizationSubscription {
  // ... existing fields ...
  stripeCustomerEmail  String?   @map("stripe_customer_email") // For matching pending payments
  seats                Int       @default(1)
  billingInterval      String    @default("monthly") @map("billing_interval") // monthly/yearly
  trialEndsAt          DateTime? @map("trial_ends_at")
  pendingChanges       Json?     @map("pending_changes") // Store plan changes pending next billing
  // ... rest of existing fields ...
}

1.3 Add WebhookEvent Table for Idempotency

model WebhookEvent {
  id            String    @id @default(dbgenerated("gen_random_uuid()"))
  stripeEventId String    @unique @map("stripe_event_id")
  eventType     String    @map("event_type")
  processed     Boolean   @default(false)
  processedAt   DateTime? @map("processed_at")
  data          Json
  errorMessage  String?   @map("error_message")
  errorCount    Int       @default(0) @map("error_count")
  createdAt     DateTime  @default(now()) @map("created_at")
  
  @@index([eventType])
  @@index([processed])
  @@map("webhook_events")
}

1.4 Enhance ReferralTracking

Update to track complete conversion funnel:

model ReferralTracking {
  // ... existing fields ...
  clickedAt           DateTime? @map("clicked_at")      // When referral link clicked
  checkoutStartedAt   DateTime? @map("checkout_started_at") // When checkout initiated  
  paymentCompletedAt  DateTime? @map("payment_completed_at") // When payment successful
  userRegisteredAt    DateTime? @map("user_registered_at")   // When user account created
  orgActivatedAt      DateTime? @map("org_activated_at")     // When org fully setup
  lifetimeValueCents  Int?      @map("lifetime_value_cents") // Track customer LTV
  // ... rest of existing fields ...
}

Phase 2: Centralized Configuration

2.1 Create /packages/payments/config/plans.ts

// Central source of truth for all pricing - replaces scattered env vars
export const PLANS = {
  starter: {
    name: 'Starter',
    description: 'Perfect for getting started with AI content',
    monthlyPriceId: process.env.NEXT_PUBLIC_PRICE_STARTER_M!,
    yearlyPriceId: process.env.NEXT_PUBLIC_PRICE_STARTER_Y!,
    features: [
      '4 blog posts per month',
      'Basic SEO optimization', 
      'AI content generation',
      'WordPress integration',
      '1 team member',
      '1 custom voice',
    ],
    limits: {
      postsPerMonth: 4,
      teamMembers: 1,
      customVoices: 1,
      apiCallsPerMonth: 1000,
    },
    monthlyPrice: 4900, // cents
    yearlyPrice: 49000, // cents (10 months price)
    popular: false,
  },
  growth: {
    name: 'Growth',
    description: 'Scale your content with advanced features',
    monthlyPriceId: process.env.NEXT_PUBLIC_PRICE_PRO_M!,
    yearlyPriceId: process.env.NEXT_PUBLIC_PRICE_PRO_Y!,
    features: [
      '12 blog posts per month',
      'Advanced SEO tools',
      'Custom voices (5 included)',
      'Priority support',
      'Analytics dashboard',
      '3 team members',
      'FAQ builder included',
      'Glossary generator included',
    ],
    limits: {
      postsPerMonth: 12,
      teamMembers: 3,
      customVoices: 5,
      apiCallsPerMonth: 5000,
    },
    monthlyPrice: 9900,
    yearlyPrice: 99000,
    popular: true,
  },
  scale: {
    name: 'Scale',
    description: 'Enterprise-grade content at scale',
    monthlyPriceId: process.env.NEXT_PUBLIC_PRICE_SCALE_M!,
    yearlyPriceId: process.env.NEXT_PUBLIC_PRICE_SCALE_Y!,
    features: [
      '40 blog posts per month',
      'White-label options',
      'API access',
      'Dedicated support',
      'Custom integrations',
      '10 team members',
      'Unlimited custom voices',
      'All add-ons included',
      'Priority processing',
    ],
    limits: {
      postsPerMonth: 40,
      teamMembers: 10,
      customVoices: -1, // unlimited
      apiCallsPerMonth: 25000,
    },
    monthlyPrice: 29900,
    yearlyPrice: 299000,
    popular: false,
  },
} as const;

export const ADDONS = {
  glossary: {
    name: 'Glossary Generator',
    description: 'Auto-generate and maintain a glossary of industry terms',
    monthlyPriceId: process.env.NEXT_PUBLIC_PRICE_GLOSSARY_M!,
    yearlyPriceId: process.env.NEXT_PUBLIC_PRICE_GLOSSARY_Y!,
    monthlyPrice: 2900,
    yearlyPrice: 29000,
    compatiblePlans: ['starter'], // Growth+ includes this
    features: [
      'AI-powered term extraction',
      'Automatic definitions',
      'SEO-optimized glossary pages',
      'Export to WordPress',
    ],
  },
  faq: {
    name: 'FAQ Builder',
    description: 'Smart FAQ generation and management',
    monthlyPriceId: process.env.NEXT_PUBLIC_PRICE_FAQ_M!,
    yearlyPriceId: process.env.NEXT_PUBLIC_PRICE_FAQ_Y!,
    monthlyPrice: 1900,
    yearlyPrice: 19000,
    compatiblePlans: ['starter'], // Growth+ includes this
    features: [
      'AI FAQ generation',
      'Customer query analysis',
      'Interactive FAQ widgets',
      'Analytics and optimization',
    ],
  },
  multilingual: {
    name: 'Multilingual Support',
    description: 'Generate content in 25+ languages',
    monthlyPriceId: process.env.NEXT_PUBLIC_PRICE_MULTILINGUAL_M!,
    yearlyPriceId: process.env.NEXT_PUBLIC_PRICE_MULTILINGUAL_Y!,
    monthlyPrice: 4900,
    yearlyPrice: 49000,
    compatiblePlans: ['starter', 'growth'], // Scale includes this
    features: [
      '25+ supported languages',
      'Cultural context awareness',
      'SEO optimization per language',
      'Bulk translation tools',
    ],
  },
  white_label: {
    name: 'White Label',
    description: 'Remove AInstein branding and add your own',
    monthlyPriceId: process.env.NEXT_PUBLIC_PRICE_WHITE_LABEL_M!,
    yearlyPriceId: process.env.NEXT_PUBLIC_PRICE_WHITE_LABEL_Y!,
    monthlyPrice: 9900,
    yearlyPrice: 99000,
    compatiblePlans: ['growth'], // Scale includes this
    features: [
      'Custom branding',
      'Remove AInstein logos',
      'Custom domain support',
      'Branded email templates',
    ],
  },
} as const;

// Helper functions for validation and lookup
export function getPlanByPriceId(priceId: string) {
  for (const [key, plan] of Object.entries(PLANS)) {
    if (plan.monthlyPriceId === priceId || plan.yearlyPriceId === priceId) {
      return { key: key as PlanKey, ...plan };
    }
  }
  return null;
}

export function getAddonByPriceId(priceId: string) {
  for (const [key, addon] of Object.entries(ADDONS)) {
    if (addon.monthlyPriceId === priceId || addon.yearlyPriceId === priceId) {
      return { key: key as AddonKey, ...addon };
    }
  }
  return null;
}

export function validatePlanAndAddons(
  planKey: string,
  addonKeys: string[]
): { valid: boolean; errors: string[] } {
  const errors: string[] = [];
  
  const plan = PLANS[planKey as keyof typeof PLANS];
  if (!plan) {
    errors.push(`Invalid plan: ${planKey}`);
    return { valid: false, errors };
  }
  
  for (const addonKey of addonKeys) {
    const addon = ADDONS[addonKey as keyof typeof ADDONS];
    if (!addon) {
      errors.push(`Invalid addon: ${addonKey}`);
      continue;
    }
    
    if (!addon.compatiblePlans.includes(planKey)) {
      errors.push(`${addon.name} is not compatible with ${plan.name} plan (already included or not available)`);
    }
  }
  
  return { valid: errors.length === 0, errors };
}

export type PlanKey = keyof typeof PLANS;
export type AddonKey = keyof typeof ADDONS;

Success Metrics & Implementation Priority

Critical Success Factors

  1. Never lose a payment: Every checkout creates a trackable record
  2. Always recoverable: System can process payments retroactively
  3. Fully auditable: Complete payment and conversion history
  4. Reseller friendly: Automatic commission calculation and tracking
  5. User friendly: Smooth experience regardless of payment timing
  6. Data consistent: All systems stay synchronized automatically
  7. Error resilient: Graceful handling of failures with retry mechanisms

Implementation Order

  1. Week 1: Database schema + centralized plans config
  2. Week 2: Enhanced checkout session + webhook handler
  3. Week 3: User registration processing + pending payment system
  4. Week 4: Sync service + monitoring + reseller improvements
  5. Week 5: Testing + deployment + validation

This architecture ensures your subscription system handles every possible customer journey while maintaining data integrity and providing an excellent user experience.

On this page