2 min read
SaaSSaaS 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
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
| Tactic | Implementation |
|---|---|
| **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:
- Know Your Customers: Segmente verstehen
- Value-Based: Preis an Kundennutzen koppeln
- Test & Iterate: A/B Testing für Pricing
- Clear Communication: Transparente Preisgestaltung
Pricing ist nie "fertig" – kontinuierlich optimieren.
Bildprompts
- "SaaS pricing page design, three-tier comparison with highlighted option"
- "Usage-based pricing calculator interface, slider and cost preview"
- "Pricing psychology infographic, anchoring and decoy effects"