I added a $9/month paywall to my Flask app last week. Here's the exact implementation — from zero to paid subscriptions in under 2 hours.
No Stripe. No US entity. No complex OAuth. Just LemonSqueezy + a webhook.
Why LemonSqueezy Over Stripe
If you're building something small and don't have a registered business:
- Stripe requires a business entity or operates under complicated personal account rules
- LemonSqueezy is a Merchant of Record — they handle sales tax, compliance, and entity requirements. You just get a payout
- Setup is 30 minutes, not 3 weeks
- No KYC headaches for sub-$1k/month volumes
Trade-off: 5% + $0.50 per transaction vs Stripe's 2.9% + $0.30. Worth it to ship today instead of waiting for Stripe Atlas.
The Architecture
User visits app →
hits paywall (>20 results) →
clicks "Upgrade" →
LemonSqueezy checkout →
webhook fires →
Flask grants access →
user sees full results
Implementation
1. LemonSqueezy Setup (15 min)
- Create account at lemonsqueezy.com
- Add a product → Subscription → $9/month
- Go to Settings → Webhooks → add
https://yourapp.com/webhook/payment - Note your product ID and webhook signing secret
2. Flask Paywall Gate
from flask import Flask, session, request, redirect
import hmac, hashlib
app = Flask(__name__)
app.secret_key = 'your-secret-key'
# Simulate your data endpoint
@app.route('/search')
def search():
results = get_all_results() # your data function
if not session.get('pro'):
# Free tier: first 20 results + upgrade CTA
limited = results[:20]
return render_template('results.html',
results=limited,
show_upgrade=True,
upgrade_url='https://your-store.lemonsqueezy.com/checkout/buy/YOUR_PRODUCT_ID')
# Pro tier: full results
return render_template('results.html', results=results)
3. Webhook Handler
@app.route('/webhook/payment', methods=['POST'])
def payment_webhook():
# Verify signature
signature = request.headers.get('X-Signature')
secret = 'your-webhook-signing-secret'
computed = hmac.new(
secret.encode(),
request.data,
hashlib.sha256
).hexdigest()
if not hmac.compare_digest(computed, signature or ''):
return 'Invalid signature', 401
data = request.json
event_type = data.get('meta', {}).get('event_name')
if event_type == 'order_created':
# Store the customer email as pro
email = data['data']['attributes']['user_email']
# In production: store in DB, not session
# For MVP: use a simple set
pro_emails.add(email)
return 'OK'
return 'OK'
4. Activate Page
After checkout, LemonSqueezy redirects to your success URL. Add a page to activate the session:
@app.route('/activate')
def activate():
email = request.args.get('email')
if email in pro_emails:
session['pro'] = True
session['email'] = email
return redirect('/')
return 'Activation failed. Please contact support.', 400
Set your LemonSqueezy product's "Redirect URL" to https://yourapp.com/activate?email={email}.
The Result
This is running live at HN Startup Hunter:
- Free: 20 startup contacts from HN hiring threads
- Pro ($9/month): 200+ contacts, CSV export, email-only filter
- Checkout: hosted by LemonSqueezy, works out of the box
Total implementation time: ~90 minutes including CSS for the upgrade banner.
What I'd Do Differently
- Store pro status in a DB, not a set — the in-memory set resets on app restart. Use SQLite or Redis.
- Add email verification on activate — currently trusting the email param; add a signed token.
-
Handle subscription cancellations — webhook also fires
subscription_cancelled. Remove from pro set.
Cost Structure
At $9/month with LemonSqueezy's 5% + $0.50 take:
- You keep: $9 × 0.95 − $0.50 = $8.05 per subscriber
- Break-even with Stripe (assuming Stripe Atlas $500): 500/0.58 = ~862 subscribers
For <100 subscribers, LemonSqueezy is cheaper when you account for time to register an entity.
I'm building HN Startup Hunter in public. Free tool to search 6 months of HN hiring threads by tech stack — useful for job seekers and founders sourcing B2B leads. Pro tier is $9/month.
Have questions about the webhook implementation? Drop them in the comments.
🔧 **Found this useful?* I build custom HN lead reports (20–50 companies with verified emails, tech stacks, 24h delivery) → Order done-for-you lead report — $75 | Got a workflow to automate? → 1-Hour Python Automation Audit — $39*
Top comments (0)