External checkout only: Better Auth uses Stripe-hosted external checkout pages. For embedded forms, guest checkout, or installments, use the Custom Boilerplate System.
Perfect for: Simple SaaS subscriptions where users create accounts before purchasing and you prefer zero configuration over advanced features.
Better Auth Stripe Plugin
The Stripe plugin is configured insrc/lib/better-auth/auth.ts and provides automatic integration:
// From src/lib/better-auth/auth.ts
import { stripe } from '@better-auth/stripe'
import { stripeClient } from '@/lib/stripe/stripe-client'
import { onStripeEvent } from '@/lib/stripe/stripe-events'
plugins: [
// ... other plugins
stripe({
stripeClient,
stripeWebhookSecret: env.STRIPE_WEBHOOK_SECRET,
createCustomerOnSignUp: true,
subscription: {
enabled: true,
plans: () => getActivePlansForBetterAuthService(),
authorizeReference: createAuthorizeReference(),
// Subscription lifecycle hooks...
},
onEvent: onStripeEvent,
}),
]
Core Plugin Features
Automatic Customer CreationWhat happens automatically:
// From src/lib/better-auth/auth.ts
stripe({
createCustomerOnSignUp: true, // Automatically create Stripe customer
stripeClient,
stripeWebhookSecret: env.STRIPE_WEBHOOK_SECRET,
})- User registers → Stripe customer created
- Customer ID stored in user profile
- Email synchronization between systems
- Customer metadata management
Seamless integration: Users don't need to know about Stripe customers - it's all handled automatically during registration.
Subscription Lifecycle Hooks
The plugin provides hooks for all subscription events with automatic notification integration:Subscription Creation
// From src/lib/better-auth/auth.ts
onSubscriptionComplete: async ({ subscription }) => {
if (!subscription.stripeCustomerId) {
return
}
const user = await getUserByStripeCustomerIdDao(
subscription.stripeCustomerId as string
)
if (user) {
await createTypedNotificationService({
userId: user.id,
type: NotificationTypeConst.subscription_created,
metadata: { subscription },
})
}
}- User completes payment
- Stripe webhook triggers completion
- User receives confirmation email
- Database updated with subscription details
Subscription Updates
// From src/lib/better-auth/auth.ts
onSubscriptionUpdate: async ({ subscription }) => {
if (!subscription.stripeCustomerId) {
return
}
const user = await getUserByStripeCustomerIdDao(
subscription.stripeCustomerId as string
)
if (user) {
await createTypedNotificationService({
userId: user.id,
type: NotificationTypeConst.subscription_updated,
metadata: { subscription },
})
}
}- Plan upgrades/downgrades
- Billing cycle changes
- Seat count modifications
- Price adjustments
Subscription Cancellation
// From src/lib/better-auth/auth.ts
onSubscriptionCancel: async ({ subscription }) => {
if (!subscription.stripeCustomerId) {
return
}
const user = await getUserByStripeCustomerIdDao(
subscription.stripeCustomerId as string
)
if (user) {
await createTypedNotificationService({
userId: user.id,
type: NotificationTypeConst.subscription_canceled,
metadata: { subscription },
})
}
}- Immediate cancellation support
- End-of-period cancellation
- Reactivation options
- Access period management
Subscription Deletion
// From src/lib/better-auth/auth.ts
onSubscriptionDeleted: async ({ subscription }) => {
if (!subscription.stripeCustomerId) {
return
}
const user = await getUserByStripeCustomerIdDao(
subscription.stripeCustomerId as string
)
if (user) {
await createTypedNotificationService({
userId: user.id,
type: NotificationTypeConst.subscription_deleted,
metadata: { subscription },
})
}
}Database Integration
The plugin automatically manages subscription data with your database:User-Customer Relationship
// From src/db/repositories/user-repository.ts
getUserByStripeCustomerIdDao(stripeCustomerId: string)- User ↔ Stripe Customer ID mapping
- Subscription status synchronization
- Payment method information
- Billing address details
Plan Management
// From src/services/facades/subscription-service-facade.ts
getActivePlansForBetterAuthService()- Active subscription plans
- Pricing information
- Feature limitations
- Plan availability rules
Custom Event Processing
The plugin delegates custom events to your handler:// From src/lib/stripe/stripe-events.ts
export const onStripeEvent = async (event: Stripe.Event) => {
// Custom business logic for Stripe events
// Handle events not covered by the plugin
console.log(`Processing Stripe event: ${event.type}`)
// Add your custom event handling here
switch (event.type) {
case 'invoice.payment_failed':
// Handle failed payments
break
case 'customer.subscription.trial_will_end':
// Send trial ending notifications
break
// Add more custom events...
}
}Security Features
Automatic VerificationSecurity measures:
// Plugin automatically verifies webhook signatures
stripe({
stripeWebhookSecret: env.STRIPE_WEBHOOK_SECRET,
})- Webhook signature verification
- Request timestamp validation
- Duplicate event prevention
- Secure endpoint protection
Plugin Benefits
✅
Zero Configuration
Automatic customer creation, webhook processing, and database synchronization
✅
Real-time Updates
Webhooks automatically update subscription status and user access
✅
Email Integration
Automatic email notifications for all subscription lifecycle events
✅
Security Built-in
Webhook verification, authorization checks, and secure data handling
Testing the Integration
1
Verify Plugin Loading
Check your application logs for successful plugin initialization:
[Better Auth] Stripe plugin loaded successfully
[Better Auth] Webhook endpoint: /api/auth/stripe/webhook
2
Test Customer Creation
- Register a new user account
- Check your Stripe dashboard for the new customer
- Verify the customer ID is stored in your database
3
Test Subscription Flow
- Complete a test subscription purchase
- Check webhook logs for event processing
- Verify subscription data in your database
- Confirm user received email notification
Better Auth Stripe integration complete! Your authentication system now seamlessly manages Stripe customers and subscriptions with zero additional configuration.