
Indie Monetization Strategist
- 313 installs
- 178 repo stars
- Updated July 14, 2026
- erichowens/some_claude_skills
indie-monetization-strategist is a Claude Code skill that helps developers choose pricing, tiers, and a revenue model for a software product before committing to a large build.
About
indie-monetization-strategist is a product economics skill for structuring how a software product will make money before engineering spend ramps up. It guides decisions on pricing tiers, packaging, and revenue models such as subscriptions, usage-based billing, or one-time licenses so technical teams align build scope with monetization assumptions. Developers and technical founders reach for it when a feature roadmap exists but billing logic, tier boundaries, and upgrade paths are still undefined. The skill frames tradeoffs between simplicity and expansion revenue without prescribing a specific payment processor, keeping focus on model design rather than checkout implementation.
- Tier and packaging recommendations
- Freemium vs paid tradeoffs
- Solo-founder revenue targets
- Competitive price anchoring
- Launch discount and upgrade paths
Indie Monetization Strategist by the numbers
- 313 all-time installs (skills.sh)
- Ranked #873 of 3,282 Productivity & Planning skills by installs in the Skillselion catalog
- Data as of Aug 4, 2026 (Skillselion catalog sync)
npx skills add https://github.com/erichowens/some_claude_skills --skill indie-monetization-strategistAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 313 |
|---|---|
| repo stars | ★ 178 |
| Last updated | July 14, 2026 |
| Repository | erichowens/some_claude_skills ↗ |
How do you choose SaaS pricing tiers before building?
Choose pricing, tiers, and revenue model for a product before heavy build investment.
Who is it for?
Technical founders or lead developers who must lock pricing structure and tier boundaries before scaling backend billing and feature gating work.
Skip if: Teams with finalized pricing and billing already implemented in Stripe or another processor who only need integration code.
When should I use this skill?
A developer asks to choose pricing tiers, subscription vs usage billing, or revenue model before building payment features.
What you get
Documented pricing tiers, revenue model choice, and packaging rationale tied to product scope.
Files
Indie Monetization Strategist
Turn side projects into sustainable income. Battle-tested strategies for indie developers and solopreneurs.
Quick Start
1. Build audience first - Email list is your foundation 2. Start with validation - If people won't use it free, they won't pay 3. Stack revenue streams - Multiple small wins beats one moonshot 4. Price on value, not cost - Premium pricing attracts premium customers 5. Play the long game - Most "overnight" successes took 3-5 years
When to Use
Use for:
- Choosing monetization models for dev tools
- Setting up freemium/premium tiers
- Pricing strategy decisions
- Email list building for launches
- Sponsorship and donation systems
NOT for:
- Enterprise B2B sales (use sales skills)
- VC fundraising/pitch decks
- Large-scale advertising campaigns
The Indie Monetization Stack
┌─────────────────────────────────────────────┐
│ PREMIUM PRODUCTS │
│ SaaS subscriptions, one-time purchases │
│ → Highest revenue, requires product-market │
├─────────────────────────────────────────────┤
│ SERVICES & CONSULTING │
│ Custom work, implementation, training │
│ → Trade time for money, but validates │
├─────────────────────────────────────────────┤
│ PASSIVE/SEMI-PASSIVE │
│ Sponsorships, donations, affiliates │
│ → Lower friction, good for content/tools │
├─────────────────────────────────────────────┤
│ LIST BUILDING │
│ Email subscribers, community members │
│ → Foundation for all monetization │
└─────────────────────────────────────────────┘Monetization Decision Tree
Is your project...
A DEVELOPER TOOL?
├── Open source? → Sponsorships + Premium features/hosting
├── Closed source? → Freemium SaaS or one-time purchase
└── CLI tool? → Pay-what-you-want + Pro tier
AN EDUCATIONAL RESOURCE?
├── Course/tutorial? → One-time purchase or membership
├── Reference site? → Sponsorships + Premium content
└── Interactive app? → Freemium with advanced features
A CONTENT SITE?
├── Technical blog? → Sponsorships + Newsletter premium tier
├── Showcase/portfolio? → Consulting leads + Sponsorships
└── Community site? → Membership + SponsorshipsModel Quick Reference
Freemium SaaS (80/20 Rule)
| Tier | Price | What to Include |
|---|---|---|
| Free | $0 | Core functionality, usage limits, goal: get users hooked |
| Pro | $9-29/mo | Higher limits, no branding, priority support |
| Team | $49-199/mo | Admin controls, SSO, SLA guarantees |
Gate these: Usage volume, team features, white-labeling, advanced analytics Never gate: Core functionality, security features, basics competitors offer free
Sponsorship Pricing Formula
Monthly visitors × $0.01-0.05 = Base sponsorship rate
Multipliers:
+ Developer audience (2-3x)
+ Niche focus (1.5-2x)
+ High engagement (1.5x)Donation Platforms
| Platform | Best For | Notes |
|---|---|---|
| GitHub Sponsors | Developers | Best for OSS |
| Buy Me a Coffee | Low friction | Quick setup |
| Ko-fi | Creators | No platform cut |
| Stripe Links | Direct | Lowest fees |
Pricing Psychology Essentials
The Decoy Effect:
BASIC: $9 PRO: $29 (target) ENTERPRISE: $99 (decoy)Price Anchoring:
❌ "Only $29/month!"
✅ "$49/month → $29/month (save 40%)"Annual vs Monthly:
Monthly: $29/month | Annual: $19/month (billed $228/year)
Annual subscribers have 5x lower churn.Anti-Patterns (10 Critical Mistakes)
1. Premature Monetization
Symptom: Adding payments before product-market fit Fix: Validate with free users first
2. Race to the Bottom Pricing
Symptom: Pricing way below competitors Fix: Price on value delivered, not competitor copying
3. Feature Bloat to Justify Price
Symptom: Adding features nobody asked for Fix: Charge more for LESS but BETTER
4. Ignoring Existing Monetization
Symptom: Building new revenue streams instead of optimizing existing Fix: 2x conversion rate before adding new streams
5. Crippled Free Tier
Symptom: Free tier so limited it's useless Fix: Users who never experience value never convert
6. No Email List
Symptom: Relying only on organic traffic Fix: Build list before you need it - foundation for everything
7. One-Size-Fits-All Pricing
Symptom: Same price for hobbyists and enterprises Fix: Segment pricing by use case and value
8. Hidden Costs
Symptom: Surprise fees after signup Fix: Transparent pricing builds trust
9. Ignoring Churn
Symptom: Focus on acquisition, not retention Fix: Reducing churn 5% can increase profits 25-95%
10. Pricing Too Low
Symptom: Undervaluing your work Fix: Higher prices = better customers, higher expectations
Revenue Benchmarks (Indie Scale)
| Stage | Monthly Revenue | Meaning |
|---|---|---|
| Ramen Profitable | $2-5k | Can quit day job (barely) |
| Comfortable | $10-20k | Good indie income |
| Scaling | $50k+ | Time to consider hiring |
Reality check: Most indie projects earn $0-500/month. $2k/month = top 10%.
Quick Implementation
Add Payments (5 min with Stripe)
// See references/stripe-integration.md for complete guide
const session = await stripe.checkout.sessions.create({
mode: 'subscription',
line_items: [{ price: 'price_xxx', quantity: 1 }],
success_url: 'https://yoursite.com/success',
});Add Sponsorship Button
<a href="https://github.com/sponsors/yourusername">
<img src="https://img.shields.io/badge/Sponsor-💖-ea4aaa">
</a>Launch Email Sequence
Day 0: Deliver lead magnet + welcome
Day 3: Best content piece
Day 7: Your story/why you built this
Day 14: Soft pitch
Day 21: Social proof
Day 30: Direct pitch with deadlineReference Files
| File | Contents |
|---|---|
references/pricing-templates.md | HTML/CSS pricing page templates |
references/email-sequences.md | Complete email sequence examples |
references/stripe-integration.md | Full Stripe implementation guide |
---
Covers: Monetization Strategy | Pricing Psychology | Freemium | Sponsorships | Email Marketing
Use with: content-marketer (distribution) | web-design-expert (pricing pages) | product-strategist (positioning)
Changelog
All notable changes to the indie-monetization-strategist skill will be documented in this file.
[2.0.0] - 2024-12-13
Changed
- BREAKING: Restructured SKILL.md from 403 lines to ~180 lines for progressive disclosure
- Moved large code examples and templates to
./references/directory - Expanded anti-patterns section to 10 patterns
Added
references/pricing-templates.md- Copy-paste pricing page templates (HTML, CSS, markdown)references/email-sequences.md- Complete email sequence templates (welcome, launch, re-engagement)references/stripe-integration.md- Full Stripe implementation guide with webhooks- Version field in frontmatter for skill tracking
Improved
- Monetization decision tree now covers more scenarios
- Clearer tier structure explanations
- Better cross-references to detailed implementations
[1.0.0] - 2024-11-30
Added
- Initial indie-monetization-strategist skill
- Indie monetization stack diagram
- Monetization decision tree
- Freemium SaaS tier guidance
- Sponsorship pricing formulas
- Email list building strategies
- Pricing psychology techniques
- Platform-specific strategies
- Quick implementation guides
Email Sequence Templates
Battle-tested email sequences for indie product launches and monetization.
Welcome Sequence (After Lead Magnet Signup)
Email 1: Immediate Delivery (Day 0)
Subject: Your [Resource Name] is ready 📦
Hey {first_name},
Thanks for grabbing [Resource Name]!
Here's your download link:
[BUTTON: Download Now]
Quick tip: The section on [specific tip] saved me 5 hours last week. Start there.
Over the next few days, I'll share some bonus tips that didn't make it into the guide.
Talk soon,
[Your name]
P.S. Hit reply if you have any questions. I read every email.Email 2: Your Best Content (Day 3)
Subject: The mistake I see 90% of developers make
Hey {first_name},
After helping [X] developers with [problem], I've noticed a pattern.
Most people [common mistake].
Here's what actually works:
[3-5 actionable bullet points from your best content]
I wrote a deep dive on this here: [Link to pillar content]
What's the biggest challenge you're facing with [topic]?
[Your name]Email 3: Your Story (Day 7)
Subject: Why I built [product/resource]
Hey {first_name},
Let me tell you why [Resource Name] exists.
[2-3 paragraphs about your journey:]
- The problem you faced
- What you tried that didn't work
- The breakthrough moment
- How you want to help others
This is why I'm so passionate about helping developers [outcome].
If you ever want to chat about [topic], just hit reply.
[Your name]Email 4: Soft Pitch (Day 14)
Subject: A question for you
Hey {first_name},
Quick question: Have you had a chance to use [Resource Name]?
If you've implemented any of the strategies, I'd love to hear what's working.
And if you want to go deeper, I've been working on something that might help...
[1-2 sentences about your paid product]
I'll share more details soon, but wanted to give you early heads up.
What would be most valuable for you right now?
[Your name]Email 5: Social Proof (Day 21)
Subject: "This changed everything for me" - \{Customer Name\}
Hey {first_name},
Wanted to share some wins from the community:
"[Testimonial 1]"
— {Name}, {Role at Company}
"[Testimonial 2]"
— {Name}, {Role at Company}
These developers went from [before state] to [after state].
The common thread? They all [action related to your product].
If you're ready to [achieve outcome], here's how:
[Link to product]
[Your name]Email 6: Direct Pitch with Deadline (Day 30)
Subject: \{first_name\}, decision time
Hey {first_name},
I've been sharing tips about [topic] for the past month.
Now I want to make you an offer:
[Product Name] gives you:
- [Benefit 1]
- [Benefit 2]
- [Benefit 3]
Regular price: $[X]
Your price (subscribers only): $[Y]
This offer expires [specific deadline].
[BUTTON: Get [Product Name] Now]
Here's what happens after you join:
1. Instant access to [main deliverable]
2. [Bonus 1]
3. [Bonus 2]
30-day money-back guarantee. No risk.
[BUTTON: Join Now]
Questions? Hit reply.
[Your name]
P.S. After [deadline], the price goes back to $[X]. Lock in your discount today.---
Launch Sequence (Product Launch)
Pre-Launch Email 1: Announcement (5 days before)
Subject: Something new is coming...
Hey {first_name},
For the past 6 months, I've been building something I'm really excited about.
It's called [Product Name], and it [one sentence value prop].
I'll be launching it on [date].
As a subscriber, you'll get:
- Early access (24 hours before public)
- Launch discount (save [X]%)
- Exclusive bonuses
Want to be first in line?
[BUTTON: Yes, Notify Me]
More details coming soon.
[Your name]Pre-Launch Email 2: Sneak Peek (2 days before)
Subject: First look at [Product Name]
Hey {first_name},
Here's your sneak peek at [Product Name]:
[Screenshot or GIF]
Here's what you'll get:
✓ [Feature/benefit 1]
✓ [Feature/benefit 2]
✓ [Feature/benefit 3]
✓ [Feature/benefit 4]
Plus these launch-only bonuses:
🎁 [Bonus 1] ($[value])
🎁 [Bonus 2] ($[value])
Total value: $[X]
Launch price: $[Y]
Launching [day] at [time] [timezone].
Reply if you have any questions!
[Your name]Launch Email 1: Doors Open (Launch day, morning)
Subject: [Product Name] is LIVE 🚀
Hey {first_name},
It's finally here.
[Product Name] is now available.
[BUTTON: Get [Product Name] Now - Launch Price]
Quick recap of what you get:
📦 [Main deliverable]
- [Detail 1]
- [Detail 2]
- [Detail 3]
🎁 Exclusive bonuses (launch only):
- [Bonus 1]
- [Bonus 2]
💰 Price:
- Regular: $[X]
- Launch price: $[Y] (save [Z]%)
⏰ Launch pricing ends [deadline].
[BUTTON: Get Instant Access]
30-day money-back guarantee. No risk.
[Your name]Launch Email 2: FAQ/Objections (Launch day, evening)
Subject: Your questions about [Product Name], answered
Hey {first_name},
Since launching this morning, I've gotten a lot of questions.
Here are the most common:
**Q: Is this right for me if I'm [situation]?**
A: Yes, because [reason].
**Q: What if I don't have time?**
A: [Answer addressing time concern].
**Q: How is this different from [alternative]?**
A: [Differentiation answer].
**Q: What if it doesn't work for me?**
A: 30-day money-back guarantee. No questions asked.
Still have questions? Reply to this email.
Ready to join?
[BUTTON: Get [Product Name] Now]
[Your name]Launch Email 3: Case Study (Day 2)
Subject: How \{Customer\} achieved [result]
Hey {first_name},
Quick story:
{Customer Name} came to me with [problem].
They had tried [previous attempts] but nothing worked.
Then they [action with your product].
The result? [Specific outcome with numbers if possible].
Here's what {Customer} said:
"[Testimonial quote]"
Want similar results?
[BUTTON: Get [Product Name] Now]
Launch pricing ends [deadline].
[Your name]Launch Email 4: Last Call (Final day, morning)
Subject: Last chance: [Product Name] launch price ends tonight
Hey {first_name},
This is it.
Tonight at midnight [timezone], the launch price for [Product Name] ends.
After that, the price goes from $[Y] to $[X].
The launch bonuses disappear too.
If you've been on the fence, now's the time.
[BUTTON: Get [Product Name] - Final Hours]
What you get:
✓ [Main deliverable]
✓ [Bonus 1] (disappears tonight)
✓ [Bonus 2] (disappears tonight)
✓ 30-day money-back guarantee
[BUTTON: Join Now]
[Your name]Launch Email 5: Final Hours (Final day, evening)
Subject: 3 hours left...
{first_name},
3 hours left to get [Product Name] at launch price.
At midnight, two things happen:
1. Price goes from $[Y] to $[X]
2. Bonuses disappear
This is your last chance.
[BUTTON: Get [Product Name] Now]
[Your name]
P.S. 30-day guarantee. If it's not for you, get a full refund.---
Re-engagement Sequence (Cold Subscribers)
Email 1: We Miss You
Subject: Are you still there, \{first_name\}?
Hey {first_name},
I noticed you haven't opened my emails in a while.
No hard feelings—inboxes are overwhelming.
I just want to make sure I'm sending you stuff you actually want.
Quick question: What's your biggest challenge with [topic] right now?
Hit reply and let me know. I read every response.
[Your name]
P.S. If you'd rather not hear from me, no worries. [Unsubscribe link]Email 2: Best Content Roundup
Subject: The 5 things I'd read if I were starting over
Hey {first_name},
In case you missed them, here are my 5 most popular posts:
1. [Title] - [One sentence description]
2. [Title] - [One sentence description]
3. [Title] - [One sentence description]
4. [Title] - [One sentence description]
5. [Title] - [One sentence description]
Which one interests you most?
[Your name]Email 3: Final Goodbye
Subject: Should I remove you?
Hey {first_name},
I want to respect your time.
If you're no longer interested in [topic], I'll remove you from this list.
But if you want to stay, just click below:
[BUTTON: Keep me on the list!]
If I don't hear from you, I'll clean up the list next week.
No hard feelings either way.
[Your name]---
Subject Line Formulas
Curiosity
- "The [X] I wish I knew sooner"
- "This changes everything about [topic]"
- "Why [common belief] is wrong"
Direct Benefit
- "How to [achieve outcome] in [timeframe]"
- "The [X]-step system for [result]"
- "[X] ways to [benefit] (starting today)"
Personal
- "\{first_name\}, quick question"
- "I made a mistake..."
- "The story I've never told"
Urgency
- "Last chance: [offer] ends tonight"
- "[X] hours left to save [Y]%"
- "This disappears tomorrow"
Social Proof
- "How \{Customer\} achieved [result]"
- "[X] developers are already doing this"
- "Join [X]+ people who [outcome]"
Pricing Page Templates
Copy-paste templates for common indie pricing scenarios.
SaaS Pricing Table (3 Tiers)
<div class="pricing-container">
<div class="pricing-card">
<h3>Free</h3>
<p class="price">$0<span>/month</span></p>
<ul>
<li>✓ Core features</li>
<li>✓ 1,000 API calls/month</li>
<li>✓ Community support</li>
<li class="muted">✗ Custom branding</li>
<li class="muted">✗ Priority support</li>
</ul>
<button class="btn-secondary">Get Started</button>
</div>
<div class="pricing-card featured">
<span class="badge">Most Popular</span>
<h3>Pro</h3>
<p class="price">$29<span>/month</span></p>
<p class="annual-price">$19/month billed annually</p>
<ul>
<li>✓ Everything in Free</li>
<li>✓ 50,000 API calls/month</li>
<li>✓ Custom branding</li>
<li>✓ Priority email support</li>
<li>✓ Advanced analytics</li>
</ul>
<button class="btn-primary">Start Free Trial</button>
</div>
<div class="pricing-card">
<h3>Team</h3>
<p class="price">$99<span>/month</span></p>
<ul>
<li>✓ Everything in Pro</li>
<li>✓ Unlimited API calls</li>
<li>✓ 5 team members</li>
<li>✓ SSO/SAML</li>
<li>✓ Dedicated support</li>
</ul>
<button class="btn-secondary">Contact Sales</button>
</div>
</div>CSS for Pricing Table
.pricing-container {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 2rem;
max-width: 1000px;
margin: 0 auto;
}
.pricing-card {
background: #fff;
border: 1px solid #e0e0e0;
border-radius: 12px;
padding: 2rem;
text-align: center;
position: relative;
}
.pricing-card.featured {
border: 2px solid #007bff;
transform: scale(1.05);
}
.badge {
position: absolute;
top: -12px;
left: 50%;
transform: translateX(-50%);
background: #007bff;
color: white;
padding: 4px 12px;
border-radius: 20px;
font-size: 0.8rem;
}
.price {
font-size: 3rem;
font-weight: bold;
margin: 1rem 0;
}
.price span {
font-size: 1rem;
color: #666;
}
.annual-price {
color: #28a745;
font-size: 0.9rem;
}
.pricing-card ul {
list-style: none;
padding: 0;
text-align: left;
}
.pricing-card li {
padding: 0.5rem 0;
border-bottom: 1px solid #f0f0f0;
}
.pricing-card li.muted {
color: #999;
}Pay-What-You-Want Template
<div class="pwyw-container">
<h2>Support This Project</h2>
<p>This tool is free, but your support keeps it alive.</p>
<div class="amount-options">
<button class="amount-btn" data-amount="5">$5</button>
<button class="amount-btn selected" data-amount="15">$15</button>
<button class="amount-btn" data-amount="50">$50</button>
<input type="number" class="custom-amount" placeholder="Custom" min="1">
</div>
<div class="impact-message">
<span class="impact-text">$15 funds 3 hours of development</span>
</div>
<button class="btn-primary btn-large">Support with Stripe</button>
<div class="social-proof">
<p>💖 Supported by 234 developers</p>
<div class="supporter-avatars">
<!-- Avatar images -->
</div>
</div>
</div>Sponsorship Tiers (README)
## Sponsors
### 💎 Diamond ($2,500/month)
- Logo on homepage hero section
- Dedicated integration example
- Co-branded content
- Direct Slack access
- **[Your company here]**
### 🥇 Gold ($1,000/month)
- Logo in documentation sidebar
- Logo in all videos
- Monthly newsletter feature
- **[Your company here]**
### 🥈 Silver ($500/month)
- Logo on website footer
- Monthly newsletter mention
- **[Your company here]**
### 🥉 Bronze ($100/month)
- Logo in README sponsors section
- Thank you tweet
<a href="https://yoursite.com/sponsor">
Become a sponsor →
</a>One-Time Purchase Template
<div class="product-card">
<span class="sale-badge">Launch Price - Save 40%</span>
<h2>The Complete Package</h2>
<p class="tagline">Everything you need to master [skill]</p>
<div class="price-container">
<span class="original-price">$149</span>
<span class="current-price">$89</span>
<span class="price-note">one-time payment</span>
</div>
<ul class="features">
<li>✓ 50+ video lessons (12 hours)</li>
<li>✓ Downloadable source code</li>
<li>✓ Lifetime updates</li>
<li>✓ Private Discord community</li>
<li>✓ Certificate of completion</li>
</ul>
<button class="btn-primary btn-large">Get Instant Access</button>
<p class="guarantee">
<img src="/icons/shield.svg" alt="">
30-day money-back guarantee. No questions asked.
</p>
</div>Pricing Comparison Table
| Feature | Free | Pro ($29/mo) | Team ($99/mo) |
|---------|------|--------------|---------------|
| Projects | 3 | Unlimited | Unlimited |
| API calls | 1,000/mo | 50,000/mo | Unlimited |
| Collaborators | - | 1 | 10 |
| Custom domains | - | ✓ | ✓ |
| Remove branding | - | ✓ | ✓ |
| Priority support | - | Email | Dedicated |
| SSO/SAML | - | - | ✓ |
| SLA | - | - | 99.9% |Feature Comparison Cards
<div class="comparison-grid">
<div class="comparison-card free">
<h3>Perfect for trying out</h3>
<p>Get started with the basics</p>
<div class="included">
<h4>Includes:</h4>
<ul>
<li>Core editor</li>
<li>3 projects</li>
<li>Community support</li>
</ul>
</div>
</div>
<div class="comparison-card pro">
<h3>Perfect for professionals</h3>
<p>Everything you need to ship</p>
<div class="included">
<h4>Everything in Free, plus:</h4>
<ul>
<li>Unlimited projects</li>
<li>Custom domains</li>
<li>Remove branding</li>
<li>Priority support</li>
<li>Advanced analytics</li>
</ul>
</div>
</div>
</div>FAQ Section Template
## Pricing FAQ
### Can I switch plans later?
Yes! Upgrade or downgrade anytime. When upgrading, you pay the prorated difference. When downgrading, the credit applies to future bills.
### Do you offer refunds?
Yes, we offer a 30-day money-back guarantee on all paid plans. No questions asked.
### What happens when I hit my limits?
We'll notify you at 80% and 100% usage. You can upgrade anytime, or we'll pause your service until the next billing cycle.
### Do you offer discounts?
- **Annual billing**: Save 35% vs monthly
- **Students**: 50% off (verify with .edu email)
- **Open source**: Free Pro tier for qualifying projects
- **Startups**: Special pricing for early-stage companies
### Can I pay annually?
Yes! Annual billing saves 35% and ensures uninterrupted service.Upgrade CTA Patterns
<!-- Inline upgrade prompt -->
<div class="upgrade-prompt">
<span class="icon">⚡</span>
<div class="content">
<strong>Unlock this feature</strong>
<p>Upgrade to Pro for unlimited exports</p>
</div>
<button class="btn-primary btn-small">Upgrade</button>
</div>
<!-- Limit reached modal -->
<div class="limit-modal">
<h3>You've reached your limit</h3>
<p>You've used 3 of 3 projects this month.</p>
<div class="options">
<div class="option current">
<strong>Free</strong>
<p>3 projects/month</p>
</div>
<div class="option recommended">
<span class="badge">Recommended</span>
<strong>Pro - $29/month</strong>
<p>Unlimited projects</p>
<button class="btn-primary">Upgrade Now</button>
</div>
</div>
<button class="btn-link">Remind me later</button>
</div>Complete Stripe Integration Guide
Step-by-step guide for adding payments to indie projects.
Quick Start Checklist
- [ ] Create Stripe account (stripe.com)
- [ ] Get API keys (Dashboard → Developers → API keys)
- [ ] Create products and prices (Dashboard → Products)
- [ ] Implement checkout flow
- [ ] Set up webhooks for fulfillment
- [ ] Test with test mode keys
- [ ] Switch to live keys when ready
1. Environment Setup
Install Dependencies
# Node.js
npm install stripe @stripe/stripe-js
# Python
pip install stripe
# Ruby
gem install stripeEnvironment Variables
# .env.local (never commit this!)
STRIPE_SECRET_KEY=sk_test_xxxxx
STRIPE_PUBLISHABLE_KEY=pk_test_xxxxx
STRIPE_WEBHOOK_SECRET=whsec_xxxxx
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_xxxxx2. One-Time Payment (Checkout Sessions)
Create Checkout Session (Next.js API Route)
// pages/api/checkout.ts
import Stripe from 'stripe';
import { NextApiRequest, NextApiResponse } from 'next';
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, {
apiVersion: '2024-04-10',
});
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
if (req.method !== 'POST') {
return res.status(405).json({ error: 'Method not allowed' });
}
const { priceId, email } = req.body;
try {
const session = await stripe.checkout.sessions.create({
mode: 'payment',
payment_method_types: ['card'],
line_items: [
{
price: priceId,
quantity: 1,
},
],
customer_email: email,
success_url: `${process.env.NEXT_PUBLIC_URL}/success?session_id={CHECKOUT_SESSION_ID}`,
cancel_url: `${process.env.NEXT_PUBLIC_URL}/pricing`,
metadata: {
// Add custom data here for webhook processing
userId: req.body.userId,
},
});
res.json({ url: session.url });
} catch (error) {
console.error('Stripe checkout error:', error);
res.status(500).json({ error: 'Failed to create checkout session' });
}
}Frontend Checkout Button
// components/CheckoutButton.tsx
'use client';
import { useState } from 'react';
import { loadStripe } from '@stripe/stripe-js';
const stripePromise = loadStripe(
process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY!
);
export function CheckoutButton({ priceId }: { priceId: string }) {
const [loading, setLoading] = useState(false);
const handleCheckout = async () => {
setLoading(true);
try {
const response = await fetch('/api/checkout', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ priceId }),
});
const { url } = await response.json();
window.location.href = url;
} catch (error) {
console.error('Checkout error:', error);
setLoading(false);
}
};
return (
<button
onClick={handleCheckout}
disabled={loading}
className="btn-primary"
>
{loading ? 'Loading...' : 'Buy Now'}
</button>
);
}3. Subscription Payments
Create Subscription Checkout
// pages/api/subscribe.ts
import Stripe from 'stripe';
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);
export default async function handler(req, res) {
const { priceId, email, customerId } = req.body;
try {
const session = await stripe.checkout.sessions.create({
mode: 'subscription',
payment_method_types: ['card'],
line_items: [
{
price: priceId,
quantity: 1,
},
],
customer: customerId, // Existing customer
customer_email: customerId ? undefined : email, // Or new customer
success_url: `${process.env.NEXT_PUBLIC_URL}/dashboard?session_id={CHECKOUT_SESSION_ID}`,
cancel_url: `${process.env.NEXT_PUBLIC_URL}/pricing`,
subscription_data: {
trial_period_days: 14, // Optional free trial
metadata: {
userId: req.body.userId,
},
},
});
res.json({ url: session.url });
} catch (error) {
res.status(500).json({ error: error.message });
}
}Customer Portal (Manage Subscriptions)
// pages/api/portal.ts
import Stripe from 'stripe';
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);
export default async function handler(req, res) {
const { customerId } = req.body;
try {
const session = await stripe.billingPortal.sessions.create({
customer: customerId,
return_url: `${process.env.NEXT_PUBLIC_URL}/dashboard`,
});
res.json({ url: session.url });
} catch (error) {
res.status(500).json({ error: error.message });
}
}4. Webhook Handler
Set Up Webhooks
// pages/api/webhooks/stripe.ts
import Stripe from 'stripe';
import { buffer } from 'micro';
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);
export const config = {
api: {
bodyParser: false, // Required for webhook signature verification
},
};
export default async function handler(req, res) {
if (req.method !== 'POST') {
return res.status(405).json({ error: 'Method not allowed' });
}
const buf = await buffer(req);
const sig = req.headers['stripe-signature']!;
let event: Stripe.Event;
try {
event = stripe.webhooks.constructEvent(
buf,
sig,
process.env.STRIPE_WEBHOOK_SECRET!
);
} catch (err) {
console.error('Webhook signature verification failed:', err);
return res.status(400).send(`Webhook Error: ${err.message}`);
}
// Handle specific events
switch (event.type) {
case 'checkout.session.completed': {
const session = event.data.object as Stripe.Checkout.Session;
await handleCheckoutComplete(session);
break;
}
case 'customer.subscription.created': {
const subscription = event.data.object as Stripe.Subscription;
await handleSubscriptionCreated(subscription);
break;
}
case 'customer.subscription.updated': {
const subscription = event.data.object as Stripe.Subscription;
await handleSubscriptionUpdated(subscription);
break;
}
case 'customer.subscription.deleted': {
const subscription = event.data.object as Stripe.Subscription;
await handleSubscriptionCanceled(subscription);
break;
}
case 'invoice.payment_succeeded': {
const invoice = event.data.object as Stripe.Invoice;
await handlePaymentSucceeded(invoice);
break;
}
case 'invoice.payment_failed': {
const invoice = event.data.object as Stripe.Invoice;
await handlePaymentFailed(invoice);
break;
}
default:
console.log(`Unhandled event type: ${event.type}`);
}
res.json({ received: true });
}
// Handler functions
async function handleCheckoutComplete(session: Stripe.Checkout.Session) {
const userId = session.metadata?.userId;
const customerId = session.customer as string;
// Grant access to purchased product
await db.users.update({
where: { id: userId },
data: {
stripeCustomerId: customerId,
hasPurchased: true,
purchasedAt: new Date(),
},
});
// Send confirmation email
await sendEmail({
to: session.customer_email!,
template: 'purchase-confirmation',
data: { session },
});
}
async function handleSubscriptionCreated(subscription: Stripe.Subscription) {
const customerId = subscription.customer as string;
const priceId = subscription.items.data[0].price.id;
await db.subscriptions.create({
data: {
stripeCustomerId: customerId,
stripeSubscriptionId: subscription.id,
stripePriceId: priceId,
status: subscription.status,
currentPeriodEnd: new Date(subscription.current_period_end * 1000),
},
});
}
async function handleSubscriptionUpdated(subscription: Stripe.Subscription) {
await db.subscriptions.update({
where: { stripeSubscriptionId: subscription.id },
data: {
status: subscription.status,
stripePriceId: subscription.items.data[0].price.id,
currentPeriodEnd: new Date(subscription.current_period_end * 1000),
},
});
}
async function handleSubscriptionCanceled(subscription: Stripe.Subscription) {
await db.subscriptions.update({
where: { stripeSubscriptionId: subscription.id },
data: {
status: 'canceled',
canceledAt: new Date(),
},
});
// Send cancellation email
const customer = await stripe.customers.retrieve(
subscription.customer as string
);
if (customer.email) {
await sendEmail({
to: customer.email,
template: 'subscription-canceled',
});
}
}
async function handlePaymentFailed(invoice: Stripe.Invoice) {
const subscription = await stripe.subscriptions.retrieve(
invoice.subscription as string
);
// Send payment failed email
await sendEmail({
to: invoice.customer_email!,
template: 'payment-failed',
data: {
amountDue: invoice.amount_due / 100,
updatePaymentUrl: `${process.env.NEXT_PUBLIC_URL}/update-payment`,
},
});
}5. Testing
Test Mode
Always use test keys during development:
sk_test_...for secret keypk_test_...for publishable key
Test Card Numbers
# Successful payments
4242424242424242 - Visa (success)
5555555555554444 - Mastercard (success)
# Declined payments
4000000000000002 - Card declined
4000000000009995 - Insufficient funds
# 3D Secure
4000002500003155 - Requires authentication
# Use any future expiry date
# Use any 3-digit CVCTest Webhooks Locally
# Install Stripe CLI
brew install stripe/stripe-cli/stripe
# Login
stripe login
# Forward webhooks to local server
stripe listen --forward-to localhost:3000/api/webhooks/stripe
# Copy the webhook secret (whsec_xxx) to .env.local6. Production Checklist
Before Going Live
- [ ] Switch to live API keys
- [ ] Set up production webhook endpoint
- [ ] Test complete purchase flow
- [ ] Verify webhook handling for all event types
- [ ] Set up error alerting (Sentry, etc.)
- [ ] Configure Stripe receipts and invoices
- [ ] Add refund handling logic
- [ ] Set up tax collection if needed (Stripe Tax)
Security
- [ ] Never expose secret key to client
- [ ] Verify webhook signatures
- [ ] Use environment variables for all keys
- [ ] Enable 3D Secure for fraud prevention
- [ ] Set up Radar rules for fraud detection
Legal
- [ ] Display clear pricing (including taxes)
- [ ] Add terms of service
- [ ] Add privacy policy
- [ ] Add refund policy
- [ ] Display cancellation terms for subscriptions
Common Patterns
Check Subscription Status
async function checkAccess(userId: string): Promise<boolean> {
const user = await db.users.findUnique({
where: { id: userId },
include: { subscription: true },
});
if (!user?.subscription) return false;
return (
user.subscription.status === 'active' ||
user.subscription.status === 'trialing'
);
}Usage-Based Billing
// Record usage
await stripe.subscriptionItems.createUsageRecord(
subscriptionItemId,
{
quantity: apiCallCount,
timestamp: Math.floor(Date.now() / 1000),
action: 'increment',
}
);Proration on Plan Changes
// Upgrade subscription
await stripe.subscriptions.update(subscriptionId, {
items: [
{
id: existingItemId,
price: newPriceId,
},
],
proration_behavior: 'create_prorations',
});Related skills
How it compares
Use indie-monetization-strategist for pre-build pricing design rather than payment SDK integration skills.
FAQ
Does indie-monetization-strategist implement Stripe billing?
indie-monetization-strategist focuses on pricing tiers and revenue model design before heavy build work. Payment processor integration and checkout code are out of scope; those come after monetization decisions are set.
When should developers run indie-monetization-strategist?
indie-monetization-strategist fits early validation when pricing, tiers, or billing model are undefined but a product direction exists. Teams with finalized pricing should skip it and implement billing directly.