Complete technical guide to secure payment processing and subscription management
๐ Published on November 21, 2025
A Stripe Payment System is a comprehensive payment infrastructure that handles subscription billing, webhook processing, and database synchronization. It creates a seamless bridge between payment processing and application access control.
Payment Formula: Stripe API + Webhooks + Database Sync + Access Control = Complete SaaS Billing
PCI DSS Level 1 certified infrastructure
Support for 135+ currencies and payment methods
Comprehensive APIs and webhook system
Real-time payment analytics and insights
Credits: 2 stock analyses
Duration: Permanent access
Features: Basic portfolio analysis
// Free tier initialization
const freeTierAccess = {
maxAnalyses: 2,
hasAccess: true,
subscriptionType: 'free'
}Model: Credit-based recurring billing
Plans: 5, 10, 20, 50, 100 analyses/month
Billing: Monthly or yearly cycles
// Quantity subscription structure
const quantityPriceMap = {
'price_monthly_5': 5,
'price_monthly_10': 10,
'price_yearly_20': 20 * 12
}Strategy: Volume-based degressive pricing
Discounts: Yearly plans (12x monthly)
Flexibility: Upgrade/downgrade anytime
// Price calculation logic
const calculatePrice = (quantity, isYearly) => {
const basePrice = getBasePrice(quantity)
return isYearly ? basePrice * 10 : basePrice
}Handles both new subscriptions and renewals
case 'invoice.paid': {
const invoice = data.object
const subscriptionId = invoice.subscription
// Add credits to user account
await updateUserCredits(userId, addedQuantity)
// Grant access
await grantAccess(userId)
}Manages subscription cancellations and access revocation
case 'customer.subscription.deleted': {
// Check remaining credits
const remainingCredits = await getRemainingCredits(userId)
if (remainingCredits === 0) {
await revokeAccess(userId)
}
}Handles failed payments and retry logic
case 'invoice.payment_failed': {
await updateSubscriptionStatus(subscriptionId, 'past_due')
// Notify user via email
await sendPaymentFailureNotification(userEmail)
}// User Management
users: {
id, email, name,
stripeCustomerId, hasAccess,
created_at, updated_at
}
// Subscription Tracking
subscriptions: {
id, userId, customerId,
status, priceId, quantity,
currentPeriodStart, currentPeriodEnd
}
// Access Control
service_access: {
userId, resourceId,
status, start_date, end_date
}The database maintains referential integrity through foreign key constraints linking users to their subscriptions and access permissions. Each subscription tracks credit usage and billing periods, while access control tables manage feature-specific permissions.
Webhook โ Database Update โ Access Calculation โ User Experience
Stripe events trigger database updates that immediately recalculate user access permissions, ensuring real-time synchronization between payment status and application features.
System checks available credits before allowing stock analysis
const remainingCredits = await connection.execute(
'SELECT SUM(GREATEST(quantity - COALESCE(quantityused,0), 0)) as credits
FROM subscriptions WHERE userId = ? AND type = "Quantity"',
[userId]
)Monitor stocks with active analysis periods
const activeStocks = await connection.execute(
'SELECT COUNT(*) as count FROM quantity_subscription_stocks
WHERE userId = ? AND status = "online" AND
(end_date IS NULL OR end_date > NOW())',
[userId]
)Scenario: User with no subscription
Access: 2 stock analyses maximum
Restriction: Cannot add more stocks after limit
Scenario: Active subscription with 15 credits
Access: Can analyze stocks until credits depleted
Tracking: Credits decremented per analysis
Scenario: Subscription canceled, 5 active stocks, 0 credits
Access: Retains access due to active stock periods
Future: Access revoked when all stocks expire
Cryptographic signature validation for all webhook events
Separate test and production Stripe keys and endpoints
Comprehensive audit trail for all payment events
Duplicate event protection and safe retry mechanisms
// Verify webhook authenticity
const signature = headers().get('stripe-signature')
const webhookSecret = process.env.STRIPE_WEBHOOK_SECRET
try {
event = stripe.webhooks.constructEvent(
rawBody, signature, webhookSecret
)
} catch (err) {
return NextResponse.json({ error: 'Invalid signature' },
{ status: 400 })
}// Atomic database operations
const connection = await mysql.createConnection(config)
try {
await connection.beginTransaction()
await connection.execute(updateQuery, params)
await connection.execute(logQuery, logParams)
await connection.commit()
} catch (error) {
await connection.rollback()
throw error
} finally {
await connection.end()
}# Environment variables setup
STRIPE_SECRET_KEY=sk_test_[your_test_key]
STRIPE_WEBHOOK_SECRET=whsec_[webhook_secret]
STRIPE_PUBLISHABLE_KEY=pk_test_[your_public_key]
# Configure test price identifiers
PRICE_ID_MONTHLY=price_test_[monthly_id]
PRICE_ID_YEARLY=price_test_[yearly_id]npm run devStart your development environment with proper environment variables loaded
stripe listen --forward-to localhost:PORT/api/webhooks/stripeConfigure webhook forwarding to your local webhook endpoint
// Verify user access logic
const userAccess = await checkUserAccess(userId)
const remainingCredits = await getCreditBalance(userId)
const activeResources = await getActiveResources(userId)
// Test access decision matrix
const hasAccess = (remainingCredits > 0) || (activeResources.length > 0)The Stripe Payment System represents a enterprise-grade billing infrastructure that seamlessly integrates payment processing with application access control. By combining secure webhooks, robust database design, and intelligent access restrictions, it creates a reliable foundation for SaaS subscription management that scales with business growth. ๐๐ณ