Overview
PDF AI integrates with Stripe to handle subscription payments, allowing users to upgrade to a Pro plan for enhanced features. The integration manages customer creation, subscription lifecycle, and payment verification.Configuration
Environment Variables
Add your Stripe credentials to.env:
Get your API keys from the Stripe Dashboard
Webhook Configuration
- Set up a webhook endpoint at
/api/webhookin your Stripe Dashboard - Subscribe to these events:
checkout.session.completedinvoice.payment_succeededcustomer.subscription.deleted
Implementation
Stripe Client
The Stripe client is initialized insrc/lib/stripe.ts:1-7:
The API version is pinned to
2023-10-16 to ensure consistent behavior. Update this when you’re ready to adopt new Stripe features.Subscription Management
Checking Subscription Status
ThecheckSubscription() function verifies if a user has an active Pro subscription (src/lib/subscription.ts:6-20):
How It Works
- Get User ID: Retrieves the authenticated user from Clerk
- Query Database: Looks up subscription record in the database
- Validate Subscription: Checks if:
- A price ID exists (subscription was created)
- Current period hasn’t expired (plus 24-hour grace period)
- Return Status: Returns
truefor active Pro users,falseotherwise
The 24-hour grace period (
+ 1000 * 60 * 60 * 24) allows users to continue using Pro features for a day after their subscription ends, accommodating payment processing delays.Database Schema
Subscription data is stored in theuser_subscriptions table (src/lib/db/schema.ts:25-36):
Schema Fields
The Clerk user ID, linking the subscription to the authenticated user
The Stripe customer ID for managing billing
The Stripe subscription ID for the active subscription
The Stripe price ID indicating the selected plan
When the current billing period ends
Usage in Components
Display subscription status and upgrade options (src/app/page.tsx:16-43):
Subscription Flow
- User Clicks Upgrade:
SubscriptionButtoninitiates checkout - Create Checkout Session: API route creates a Stripe Checkout session
- Redirect to Stripe: User is sent to Stripe-hosted payment page
- Payment Completion: Stripe processes payment and redirects back
- Webhook Received: Your app receives
checkout.session.completedevent - Update Database: Store subscription details in
user_subscriptionstable - Grant Access:
checkSubscription()now returnstrue
Creating a Checkout Session
Typical implementation for creating a checkout session:Webhook Handler
Process Stripe events in/api/webhook/route.ts:
Dependencies
Best Practices
Security
- Verify Webhooks: Always validate webhook signatures
- Server-Side Only: Never expose
STRIPE_API_KEYto the client - Idempotency: Handle duplicate webhook events gracefully
User Experience
- Grace Period: Allow 24-hour access after subscription expiration
- Clear Messaging: Show subscription status clearly in the UI
- Easy Upgrades: Make the upgrade button prominent for free users
Error Handling
Troubleshooting
Subscription Not Recognized
- Check that webhook events are being received and processed
- Verify
user_subscriptionstable contains the subscription record - Ensure
stripeCurrentPeriodEndis in the future
Webhook Failures
- Confirm webhook endpoint is publicly accessible
- Verify
STRIPE_WEBHOOK_SECRETmatches the Dashboard - Check webhook logs in Stripe Dashboard for error details
Payment Issues
- Test with Stripe’s test card numbers:
4242 4242 4242 4242 - Ensure you’re using the correct API keys (test vs. production)
- Check that the price ID exists in your Stripe account