Menu
Back to Blog
2 min read
SaaS

SaaS Pricing Strategies

SaaS Pricing Modelle implementieren. Freemium, Tiered Pricing, Usage-Based und Value-Based Pricing mit Stripe Integration.

SaaS PricingFreemiumTiered PricingUsage-BasedValue PricingStripe
SaaS Pricing Strategies

SaaS Pricing Strategies

Meta-Description: SaaS Pricing Modelle implementieren. Freemium, Tiered Pricing, Usage-Based und Value-Based Pricing mit Stripe Integration.

Keywords: SaaS Pricing, Freemium, Tiered Pricing, Usage-Based, Value Pricing, Stripe, Subscription, Revenue Model


Einführung

Pricing ist einer der wichtigsten Hebel für SaaS-Wachstum. Das richtige Pricing-Modell kann Revenue verdoppeln ohne zusätzliche Kunden. Dieser Guide zeigt verschiedene Strategien und deren technische Implementation.


Pricing Models Overview

┌─────────────────────────────────────────────────────────────┐
│              SAAS PRICING MODELS                             │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  1. FLAT-RATE PRICING:                                     │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  $49/month - All features included                  │   │
│  │  ✓ Simple to understand                            │   │
│  │  ✗ Limited upsell potential                        │   │
│  │  Example: Basecamp                                  │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  2. TIERED PRICING:                                        │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  Starter    Pro        Enterprise                   │   │
│  │  $19/mo     $49/mo     $199/mo                     │   │
│  │  5 users    25 users   Unlimited                    │   │
│  │  ✓ Clear upgrade path                              │   │
│  │  ✓ Captures different segments                     │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  3. USAGE-BASED (Pay-as-you-go):                           │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  $0.01 per API call / $0.10 per GB                 │   │
│  │  ✓ Low barrier to entry                            │   │
│  │  ✓ Revenue scales with customer success            │   │
│  │  ✗ Unpredictable revenue                           │   │
│  │  Example: AWS, Twilio                               │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  4. FREEMIUM:                                              │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  Free tier + Premium tiers                          │   │
│  │  ✓ Viral growth potential                          │   │
│  │  ✓ Low friction acquisition                        │   │
│  │  ✗ High support costs                              │   │
│  │  Example: Slack, Dropbox                            │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  5. PER-SEAT PRICING:                                      │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  $10/user/month                                     │   │
│  │  ✓ Predictable, easy to understand                 │   │
│  │  ✓ Grows with customer's team                      │   │
│  │  Example: Notion, Linear                            │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
└─────────────────────────────────────────────────────────────┘

Pricing Configuration

// lib/pricing/config.ts
export interface PricingTier {
  id: string;
  name: string;
  description: string;
  monthlyPrice: number;
  yearlyPrice: number;
  features: Feature[];
  limits: TierLimits;
  highlighted?: boolean;
  ctaText: string;
  stripeMonthlyPriceId: string;
  stripeYearlyPriceId: string;
}

export interface Feature {
  name: string;
  included: boolean;
  limit?: string;
  tooltip?: string;
}

export interface TierLimits {
  users: number;
  projects: number;
  storage: number; // in GB
  apiCalls: number;
  customDomains: number;
  support: 'community' | 'email' | 'priority' | 'dedicated';
}

export const pricingTiers: PricingTier[] = [
  {
    id: 'free',
    name: 'Free',
    description: 'For individuals getting started',
    monthlyPrice: 0,
    yearlyPrice: 0,
    features: [
      { name: 'Up to 3 projects', included: true },
      { name: 'Basic analytics', included: true },
      { name: 'Community support', included: true },
      { name: 'API access', included: false },
      { name: 'Custom domains', included: false },
      { name: 'Team collaboration', included: false }
    ],
    limits: {
      users: 1,
      projects: 3,
      storage: 1,
      apiCalls: 1000,
      customDomains: 0,
      support: 'community'
    },
    ctaText: 'Get Started Free',
    stripeMonthlyPriceId: '',
    stripeYearlyPriceId: ''
  },
  {
    id: 'pro',
    name: 'Pro',
    description: 'For professionals and small teams',
    monthlyPrice: 29,
    yearlyPrice: 290, // 2 months free
    features: [
      { name: 'Unlimited projects', included: true },
      { name: 'Advanced analytics', included: true },
      { name: 'Email support', included: true },
      { name: 'API access', included: true, limit: '10k calls/mo' },
      { name: 'Custom domains', included: true, limit: '3 domains' },
      { name: 'Team collaboration', included: true, limit: '5 members' }
    ],
    limits: {
      users: 5,
      projects: -1, // unlimited
      storage: 50,
      apiCalls: 10000,
      customDomains: 3,
      support: 'email'
    },
    highlighted: true,
    ctaText: 'Start Free Trial',
    stripeMonthlyPriceId: 'price_pro_monthly',
    stripeYearlyPriceId: 'price_pro_yearly'
  },
  {
    id: 'enterprise',
    name: 'Enterprise',
    description: 'For large teams with advanced needs',
    monthlyPrice: 99,
    yearlyPrice: 990,
    features: [
      { name: 'Unlimited everything', included: true },
      { name: 'Custom analytics', included: true },
      { name: 'Dedicated support', included: true },
      { name: 'API access', included: true, limit: 'Unlimited' },
      { name: 'Custom domains', included: true, limit: 'Unlimited' },
      { name: 'Team collaboration', included: true, limit: 'Unlimited' },
      { name: 'SSO/SAML', included: true },
      { name: 'SLA guarantee', included: true }
    ],
    limits: {
      users: -1,
      projects: -1,
      storage: -1,
      apiCalls: -1,
      customDomains: -1,
      support: 'dedicated'
    },
    ctaText: 'Contact Sales',
    stripeMonthlyPriceId: 'price_enterprise_monthly',
    stripeYearlyPriceId: 'price_enterprise_yearly'
  }
];

// Check if feature is available
export function hasFeature(
  tierId: string,
  featureName: string
): boolean {
  const tier = pricingTiers.find(t => t.id === tierId);
  if (!tier) return false;

  const feature = tier.features.find(f => f.name === featureName);
  return feature?.included ?? false;
}

// Check limits
export function checkLimit(
  tierId: string,
  limitKey: keyof TierLimits,
  currentUsage: number
): { allowed: boolean; limit: number; usage: number } {
  const tier = pricingTiers.find(t => t.id === tierId);
  if (!tier) return { allowed: false, limit: 0, usage: currentUsage };

  const limit = tier.limits[limitKey];

  return {
    allowed: limit === -1 || currentUsage < limit,
    limit,
    usage: currentUsage
  };
}

Pricing Component

// components/pricing/PricingTable.tsx
'use client';

import { useState } from 'react';
import { pricingTiers, PricingTier } from '@/lib/pricing/config';
import { CheckIcon, XIcon } from 'lucide-react';
import { track } from '@/lib/analytics';

export function PricingTable() {
  const [billingPeriod, setBillingPeriod] = useState<'monthly' | 'yearly'>('monthly');

  const handleToggle = () => {
    const newPeriod = billingPeriod === 'monthly' ? 'yearly' : 'monthly';
    setBillingPeriod(newPeriod);
    track('pricing_period_changed', { period: newPeriod });
  };

  return (
    <section className="py-20">
      <div className="container mx-auto px-4">
        {/* Header */}
        <div className="text-center mb-12">
          <h2 className="text-4xl font-bold mb-4">
            Simple, transparent pricing
          </h2>
          <p className="text-xl text-gray-600">
            No hidden fees. Cancel anytime.
          </p>

          {/* Billing Toggle */}
          <div className="flex items-center justify-center gap-4 mt-8">
            <span className={billingPeriod === 'monthly' ? 'font-semibold' : 'text-gray-500'}>
              Monthly
            </span>
            <button
              onClick={handleToggle}
              className="relative w-14 h-7 bg-gray-200 rounded-full transition"
              aria-label="Toggle billing period"
            >
              <span
                className={`absolute w-5 h-5 bg-blue-600 rounded-full top-1 transition-all ${
                  billingPeriod === 'yearly' ? 'left-8' : 'left-1'
                }`}
              />
            </button>
            <span className={billingPeriod === 'yearly' ? 'font-semibold' : 'text-gray-500'}>
              Yearly
              <span className="ml-2 text-green-600 text-sm">Save 17%</span>
            </span>
          </div>
        </div>

        {/* Pricing Cards */}
        <div className="grid md:grid-cols-3 gap-8 max-w-5xl mx-auto">
          {pricingTiers.map((tier) => (
            <PricingCard
              key={tier.id}
              tier={tier}
              billingPeriod={billingPeriod}
            />
          ))}
        </div>

        {/* FAQ Link */}
        <div className="text-center mt-12">
          <p className="text-gray-600">
            Have questions?{' '}
            <a href="/faq" className="text-blue-600 hover:underline">
              Check our FAQ
            </a>{' '}
            or{' '}
            <a href="/contact" className="text-blue-600 hover:underline">
              contact us
            </a>
          </p>
        </div>
      </div>
    </section>
  );
}

function PricingCard({
  tier,
  billingPeriod
}: {
  tier: PricingTier;
  billingPeriod: 'monthly' | 'yearly';
}) {
  const price = billingPeriod === 'monthly' ? tier.monthlyPrice : tier.yearlyPrice;
  const pricePerMonth = billingPeriod === 'yearly' ? Math.round(price / 12) : price;

  const handleCTAClick = () => {
    track('pricing_cta_clicked', {
      tier: tier.id,
      billingPeriod,
      price
    });
  };

  return (
    <div
      className={`relative rounded-2xl p-8 ${
        tier.highlighted
          ? 'bg-blue-600 text-white ring-4 ring-blue-600 ring-offset-2'
          : 'bg-white border border-gray-200'
      }`}
    >
      {tier.highlighted && (
        <span className="absolute -top-4 left-1/2 -translate-x-1/2 bg-yellow-400 text-yellow-900 px-4 py-1 rounded-full text-sm font-semibold">
          Most Popular
        </span>
      )}

      <h3 className="text-2xl font-bold">{tier.name}</h3>
      <p className={`mt-2 ${tier.highlighted ? 'text-blue-100' : 'text-gray-600'}`}>
        {tier.description}
      </p>

      {/* Price */}
      <div className="mt-6">
        <span className="text-5xl font-bold">
          ${pricePerMonth}
        </span>
        <span className={tier.highlighted ? 'text-blue-100' : 'text-gray-500'}>
          /month
        </span>
        {billingPeriod === 'yearly' && price > 0 && (
          <p className={`text-sm mt-1 ${tier.highlighted ? 'text-blue-100' : 'text-gray-500'}`}>
            Billed ${price} annually
          </p>
        )}
      </div>

      {/* CTA */}
      <button
        onClick={handleCTAClick}
        className={`w-full mt-8 py-3 rounded-lg font-semibold transition ${
          tier.highlighted
            ? 'bg-white text-blue-600 hover:bg-blue-50'
            : 'bg-blue-600 text-white hover:bg-blue-700'
        }`}
      >
        {tier.ctaText}
      </button>

      {/* Features */}
      <ul className="mt-8 space-y-4">
        {tier.features.map((feature, index) => (
          <li key={index} className="flex items-start gap-3">
            {feature.included ? (
              <CheckIcon className={`w-5 h-5 flex-shrink-0 ${
                tier.highlighted ? 'text-blue-200' : 'text-green-500'
              }`} />
            ) : (
              <XIcon className="w-5 h-5 flex-shrink-0 text-gray-400" />
            )}
            <span className={!feature.included ? 'text-gray-400' : ''}>
              {feature.name}
              {feature.limit && (
                <span className={`text-sm ${tier.highlighted ? 'text-blue-200' : 'text-gray-500'}`}>
                  {' '}({feature.limit})
                </span>
              )}
            </span>
          </li>
        ))}
      </ul>
    </div>
  );
}

Usage-Based Pricing

// lib/pricing/usage.ts
export interface UsageMetric {
  id: string;
  name: string;
  unit: string;
  pricePerUnit: number;
  freeAllowance: number;
  tiers?: UsageTier[];
}

export interface UsageTier {
  upTo: number;
  pricePerUnit: number;
}

export const usageMetrics: UsageMetric[] = [
  {
    id: 'api_calls',
    name: 'API Calls',
    unit: 'request',
    pricePerUnit: 0.0001, // $0.0001 per call
    freeAllowance: 10000,
    tiers: [
      { upTo: 100000, pricePerUnit: 0.0001 },
      { upTo: 1000000, pricePerUnit: 0.00008 },
      { upTo: Infinity, pricePerUnit: 0.00005 }
    ]
  },
  {
    id: 'storage',
    name: 'Storage',
    unit: 'GB',
    pricePerUnit: 0.10,
    freeAllowance: 5,
    tiers: [
      { upTo: 100, pricePerUnit: 0.10 },
      { upTo: 1000, pricePerUnit: 0.08 },
      { upTo: Infinity, pricePerUnit: 0.05 }
    ]
  },
  {
    id: 'bandwidth',
    name: 'Bandwidth',
    unit: 'GB',
    pricePerUnit: 0.05,
    freeAllowance: 10
  }
];

export function calculateUsageCost(
  metricId: string,
  usage: number
): number {
  const metric = usageMetrics.find(m => m.id === metricId);
  if (!metric) return 0;

  // Subtract free allowance
  const billableUsage = Math.max(0, usage - metric.freeAllowance);
  if (billableUsage === 0) return 0;

  // Calculate with tiers if available
  if (metric.tiers) {
    let remaining = billableUsage;
    let cost = 0;
    let previousLimit = 0;

    for (const tier of metric.tiers) {
      const tierUsage = Math.min(remaining, tier.upTo - previousLimit);
      cost += tierUsage * tier.pricePerUnit;
      remaining -= tierUsage;
      previousLimit = tier.upTo;

      if (remaining <= 0) break;
    }

    return cost;
  }

  return billableUsage * metric.pricePerUnit;
}

export function calculateTotalUsageCost(
  usage: Record<string, number>
): { breakdown: Record<string, number>; total: number } {
  const breakdown: Record<string, number> = {};
  let total = 0;

  for (const [metricId, amount] of Object.entries(usage)) {
    const cost = calculateUsageCost(metricId, amount);
    breakdown[metricId] = cost;
    total += cost;
  }

  return { breakdown, total };
}

Stripe Integration

// lib/stripe/checkout.ts
import Stripe from 'stripe';

const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, {
  apiVersion: '2024-06-20'
});

export async function createCheckoutSession(
  customerId: string,
  priceId: string,
  options?: {
    trialDays?: number;
    successUrl?: string;
    cancelUrl?: string;
    couponId?: string;
  }
): Promise<string> {
  const session = await stripe.checkout.sessions.create({
    customer: customerId,
    mode: 'subscription',
    payment_method_types: ['card'],
    line_items: [
      {
        price: priceId,
        quantity: 1
      }
    ],
    subscription_data: {
      trial_period_days: options?.trialDays || 14,
      ...(options?.couponId && {
        coupon: options.couponId
      })
    },
    success_url: options?.successUrl || `${process.env.NEXT_PUBLIC_APP_URL}/checkout/success?session_id={CHECKOUT_SESSION_ID}`,
    cancel_url: options?.cancelUrl || `${process.env.NEXT_PUBLIC_APP_URL}/pricing`,
    allow_promotion_codes: true
  });

  return session.url!;
}

export async function createBillingPortalSession(
  customerId: string
): Promise<string> {
  const session = await stripe.billingPortal.sessions.create({
    customer: customerId,
    return_url: `${process.env.NEXT_PUBLIC_APP_URL}/settings/billing`
  });

  return session.url;
}

// Usage-based billing
export async function reportUsage(
  subscriptionItemId: string,
  quantity: number,
  timestamp?: number
): Promise<void> {
  await stripe.subscriptionItems.createUsageRecord(subscriptionItemId, {
    quantity,
    timestamp: timestamp || Math.floor(Date.now() / 1000),
    action: 'increment'
  });
}

Pricing Psychology

TacticImplementation
**Anchoring**Show highest tier first or middle highlighted
**Decoy Effect**Add a tier that makes others look better
**Charm Pricing**$29 instead of $30
**Annual Discount**2 months free for yearly
**Social Proof**"Most Popular" badge
**Loss Aversion**"Limited time offer"
**Value Framing**Compare to alternatives

Fazit

SaaS Pricing Strategien erfordern:

  1. Know Your Customers: Segmente verstehen
  2. Value-Based: Preis an Kundennutzen koppeln
  3. Test & Iterate: A/B Testing für Pricing
  4. Clear Communication: Transparente Preisgestaltung

Pricing ist nie "fertig" – kontinuierlich optimieren.


Bildprompts

  1. "SaaS pricing page design, three-tier comparison with highlighted option"
  2. "Usage-based pricing calculator interface, slider and cost preview"
  3. "Pricing psychology infographic, anchoring and decoy effects"

Quellen