The OnlyAutomator subscription system manages user access to premium features through a flexible, tiered pricing model powered by Stripe.

Subscription Flow

The subscription process is designed to be seamless and secure, following industry best practices for payment processing:
  • Plan Selection: Users choose from multiple subscription tiers
  • Payment Processing: Secure credit card processing through Stripe
  • Access Provisioning: Immediate activation of premium features
  • Recurring Billing: Automated rebilling on the subscriber’s cycle
  • Subscription Management: Self-service tools for upgrading, downgrading, or canceling
Subscription flow with Stripe

Subscription Tiers

OnlyAutomator offers multiple subscription tiers to accommodate different user needs:

Free Tier

  • Price: $0/month
  • Features:
    • Basic analytics dashboard
    • Limited data collection (1 OnlyFans account)
    • 7-day data retention
    • Standard support

Pro Tier

  • Price: $19.99/month
  • Features:
    • Advanced analytics dashboard
    • Enhanced data collection (up to 3 OnlyFans accounts)
    • 30-day data retention
    • Priority support
    • Basic automation tools

Business Tier

  • Price: $49.99/month
  • Features:
    • Comprehensive analytics suite
    • Full data collection (up to 10 OnlyFans accounts)
    • 90-day data retention
    • Priority support with dedicated contact
    • Advanced automation tools
    • Custom reporting

Technical Implementation

Database Schema

The subscription data is stored in the following database schema:
-- Products table
CREATE TABLE products (
  id TEXT PRIMARY KEY,
  active BOOLEAN,
  name TEXT,
  description TEXT,
  image TEXT,
  metadata JSONB
);

-- Prices table
CREATE TABLE prices (
  id TEXT PRIMARY KEY,
  product_id TEXT REFERENCES products(id),
  active BOOLEAN,
  description TEXT,
  unit_amount INTEGER,
  currency TEXT,
  type TEXT,
  interval TEXT,
  interval_count INTEGER,
  trial_period_days INTEGER,
  metadata JSONB
);

-- Subscriptions table
CREATE TABLE subscriptions (
  id TEXT PRIMARY KEY,
  user_id UUID REFERENCES auth.users(id) NOT NULL,
  status TEXT,
  metadata JSONB,
  price_id TEXT REFERENCES prices(id),
  quantity INTEGER,
  cancel_at_period_end BOOLEAN,
  created_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()) NOT NULL,
  current_period_start TIMESTAMP WITH TIME ZONE,
  current_period_end TIMESTAMP WITH TIME ZONE,
  ended_at TIMESTAMP WITH TIME ZONE,
  cancel_at TIMESTAMP WITH TIME ZONE,
  canceled_at TIMESTAMP WITH TIME ZONE,
  trial_start TIMESTAMP WITH TIME ZONE,
  trial_end TIMESTAMP WITH TIME ZONE
);

API Endpoints

The subscription system exposes the following API endpoints:
EndpointMethodDescription
/api/create-checkout-sessionPOSTCreates a Stripe checkout session
/api/create-portal-linkPOSTCreates a link to the Stripe customer portal
/api/webhooksPOSTHandles Stripe webhook events

Stripe Integration

Integration with Stripe is handled through a dedicated service:
// Service for managing Stripe interactions
import Stripe from 'stripe';
import { createClient } from '@supabase/supabase-js';
import { Database } from '../types_db';

export const stripe = new Stripe(process.env.STRIPE_SECRET_KEY as string, {
  apiVersion: '2022-11-15',
  typescript: true
});

export const supabase = createClient<Database>(
  process.env.NEXT_PUBLIC_SUPABASE_URL as string,
  process.env.SUPABASE_SERVICE_ROLE_KEY as string
);

// Example: Create a checkout session
export async function createCheckoutSession(
  userId: string,
  priceId: string,
  redirectTo: string
) {
  try {
    const customer = await getOrCreateCustomer(userId);
    
    const session = await stripe.checkout.sessions.create({
      payment_method_types: ['card'],
      billing_address_collection: 'required',
      customer: customer.id,
      line_items: [
        {
          price: priceId,
          quantity: 1
        }
      ],
      mode: 'subscription',
      allow_promotion_codes: true,
      subscription_data: {
        trial_from_plan: true,
        metadata: {}
      },
      success_url: `${redirectTo}/dashboard?session_id={CHECKOUT_SESSION_ID}`,
      cancel_url: `${redirectTo}/pricing`
    });
    
    return { sessionId: session.id };
  } catch (error) {
    console.log(error);
    throw error;
  }
}

// Helper: Get or create a Stripe customer for a user
async function getOrCreateCustomer(userId: string) {
  const { data: customerData } = await supabase
    .from('customers')
    .select('stripe_customer_id')
    .eq('id', userId)
    .single();
  
  if (customerData?.stripe_customer_id) {
    return { id: customerData.stripe_customer_id };
  }
  
  // Retrieve user data from Supabase
  const { data: userData } = await supabase
    .from('users')
    .select('email')
    .eq('id', userId)
    .single();
  
  // Create a new customer in Stripe
  const customer = await stripe.customers.create({
    email: userData?.email,
    metadata: {
      supabaseUUID: userId
    }
  });
  
  // Store the customer ID in Supabase
  await supabase
    .from('customers')
    .insert([{ id: userId, stripe_customer_id: customer.id }]);
  
  return customer;
}

Webhook Processing

Stripe webhooks are used to keep subscription status in sync:
export async function processStripeWebhook(event: Stripe.Event) {
  switch (event.type) {
    case 'customer.subscription.created':
    case 'customer.subscription.updated':
      const subscription = event.data.object as Stripe.Subscription;
      await updateSubscriptionInDatabase(subscription);
      break;
    case 'customer.subscription.deleted':
      const deletedSubscription = event.data.object as Stripe.Subscription;
      await markSubscriptionAsDeleted(deletedSubscription);
      break;
    case 'invoice.paid':
      const invoice = event.data.object as Stripe.Invoice;
      await handleSuccessfulPayment(invoice);
      break;
    case 'invoice.payment_failed':
      const failedInvoice = event.data.object as Stripe.Invoice;
      await handleFailedPayment(failedInvoice);
      break;
    default:
      console.log(`Unhandled event type: ${event.type}`);
  }
}

User Subscription Management

Users can manage their subscriptions through a dedicated portal:
export async function createPortalLink(customerId: string, returnUrl: string) {
  try {
    const portalSession = await stripe.billingPortal.sessions.create({
      customer: customerId,
      return_url: returnUrl
    });
    
    return { url: portalSession.url };
  } catch (error) {
    console.log(error);
    throw error;
  }
}

Subscription Status Checking

A custom hook provides easy access to subscription status:
// Custom hook for subscription status
import { useEffect, useState } from 'react';
import { useUser } from '@/utils/useUser';

export const useSubscriptionStatus = () => {
  const { user } = useUser();
  const [subscription, setSubscription] = useState<any>(null);
  const [loading, setLoading] = useState<boolean>(true);
  
  useEffect(() => {
    if (!user) {
      setLoading(false);
      return;
    }
    
    async function fetchSubscription() {
      try {
        const res = await fetch('/api/subscription-status');
        const data = await res.json();
        setSubscription(data);
      } catch (error) {
        console.error('Error fetching subscription:', error);
      } finally {
        setLoading(false);
      }
    }
    
    fetchSubscription();
  }, [user]);
  
  return {
    subscription,
    loading,
    isPro: subscription?.plan === 'pro',
    isBusiness: subscription?.plan === 'business',
    isActive: subscription?.status === 'active' || subscription?.status === 'trialing'
  };
};

Trial Periods

The subscription system supports trial periods to allow users to experience premium features:
  • Trial Duration: 7 days for all paid plans
  • Credit Card Required: Credit card is collected upfront but not charged until the trial ends
  • Automatic Conversion: Trial automatically converts to a paid subscription unless canceled
  • Full Access: Trial users receive full access to all plan features

Handling Subscription Changes

The system includes logic for handling various subscription events:
  • Upgrades: Immediate upgrade with prorated charges
  • Downgrades: Applied at the end of the billing cycle
  • Cancellations: Access continues until the end of the paid period
  • Reactivation: Simple process to restore a canceled subscription

Configuration Settings

The subscription system is configured through environment variables:
# Stripe API configuration
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_xxxxxxxxxxxxxxxxxxxxxxxxxx
STRIPE_SECRET_KEY=sk_test_xxxxxxxxxxxxxxxxxxxxxxxxxx
STRIPE_WEBHOOK_SECRET=whsec_xxxxxxxxxxxxxxxxxxxxxxxxxx

# Product and price IDs
NEXT_PUBLIC_STRIPE_PRO_PRICE_ID=price_xxxxxxxxxxxxxxxxxx
NEXT_PUBLIC_STRIPE_BUSINESS_PRICE_ID=price_xxxxxxxxxxxxxxxxxx

Troubleshooting

Common subscription issues and their resolutions:
IssuePossible CausesResolution
Failed paymentExpired card, insufficient fundsPrompt user to update payment method
Webhook errorsMisconfigured webhook, server issuesCheck Stripe dashboard, verify endpoint URL
Access discrepancyDatabase sync issuesForce sync subscription status from Stripe
Unexpected cancellationFailed payment, user actionCheck Stripe logs for cancellation reason