mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-15 07:21:10 +02:00
- Add uload project with apps/web structure
- Reorganize from flat to monorepo structure
- Remove PocketBase binary and local data
- Update to pnpm and @uload/web namespace
- Add picture project to monorepo
- Remove embedded git repository
- Unify all package names to @{project}/{app} schema:
- @maerchenzauber/* (was @storyteller/*)
- @manacore/* (was manacore-*, manacore)
- @manadeck/* (was web, backend, manadeck)
- @memoro/* (was memoro-web, landing, memoro)
- @picture/* (already unified)
- @uload/web
- Add convenient dev scripts for all apps:
- pnpm dev:{project}:web
- pnpm dev:{project}:landing
- pnpm dev:{project}:mobile
- pnpm dev:{project}:backend
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
5.7 KiB
5.7 KiB
Stripe Quick Start Guide
🚀 Schnellstart in 30 Minuten
Diese Anleitung bringt dich in 30 Minuten zu einer funktionierenden Stripe-Integration.
Prerequisites
- Stripe Account (stripe.com)
- SvelteKit Projekt läuft (
npm run dev) - PocketBase läuft
Step 1: Stripe Setup (5 Min)
1.1 Install Stripe Dependencies
npm install stripe @stripe/stripe-js
1.2 Get API Keys
- Login bei stripe.com/dashboard
- Kopiere Test Keys:
- Publishable Key:
pk_test_... - Secret Key:
sk_test_...
- Publishable Key:
1.3 Create Product
Im Stripe Dashboard:
- Products → Add Product
- Name: "ulo.ad Pro"
- Price: €9.99/month
- Kopiere Price ID:
price_xxx
Step 2: Environment Setup (2 Min)
# .env.local
PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_51...
STRIPE_SECRET_KEY=sk_test_51...
STRIPE_PRICE_ID=price_1...
PUBLIC_APP_URL=http://localhost:5173
Step 3: Minimal Backend (10 Min)
3.1 Stripe Client
// src/lib/server/stripe.ts
import Stripe from 'stripe';
import { STRIPE_SECRET_KEY } from '$env/static/private';
export const stripe = new Stripe(STRIPE_SECRET_KEY, {
apiVersion: '2024-11-20'
});
3.2 Checkout Endpoint
// src/routes/api/checkout/+server.ts
import { json } from '@sveltejs/kit';
import { stripe } from '$lib/server/stripe';
import { PUBLIC_APP_URL } from '$env/static/public';
import { STRIPE_PRICE_ID } from '$env/static/private';
export async function POST({ locals }) {
// Check if user is logged in
if (!locals.pb.authStore.isValid) {
return json({ error: 'Please login first' }, { status: 401 });
}
const session = await stripe.checkout.sessions.create({
payment_method_types: ['card'],
line_items: [
{
price: STRIPE_PRICE_ID,
quantity: 1
}
],
mode: 'subscription',
success_url: `${PUBLIC_APP_URL}/success`,
cancel_url: `${PUBLIC_APP_URL}/`,
client_reference_id: locals.pb.authStore.model?.id
});
return json({ url: session.url });
}
Step 4: Minimal Frontend (5 Min)
4.1 Upgrade Button
<!-- src/lib/components/QuickUpgrade.svelte -->
<script lang="ts">
let loading = false;
async function upgrade() {
loading = true;
const res = await fetch('/api/checkout', { method: 'POST' });
const { url } = await res.json();
window.location.href = url;
}
</script>
<button on:click={upgrade} disabled={loading}>
{loading ? 'Loading...' : 'Upgrade to Pro €9.99/mo'}
</button>
4.2 Success Page
<!-- src/routes/success/+page.svelte -->
<script>
import { onMount } from 'svelte';
import { goto } from '$app/navigation';
onMount(() => {
setTimeout(() => goto('/'), 3000);
});
</script>
<div>
<h1>🎉 Payment Successful!</h1>
<p>Redirecting to dashboard...</p>
</div>
Step 5: Test Payment (3 Min)
- Click "Upgrade to Pro"
- Use test card:
4242 4242 4242 4242 - Any future date, any CVC
- Complete payment
- Check Stripe Dashboard for payment
Step 6: Basic Webhook (5 Min)
6.1 Get Webhook Secret
# Install Stripe CLI
brew install stripe/stripe-cli/stripe
# Login and forward webhooks
stripe login
stripe listen --forward-to localhost:5173/api/webhook
# Copy the webhook secret displayed
6.2 Webhook Handler
// src/routes/api/webhook/+server.ts
import { stripe } from '$lib/server/stripe';
import { STRIPE_WEBHOOK_SECRET } from '$env/static/private';
export async function POST({ request, locals }) {
const body = await request.text();
const sig = request.headers.get('stripe-signature')!;
try {
const event = stripe.webhooks.constructEvent(body, sig, STRIPE_WEBHOOK_SECRET);
if (event.type === 'checkout.session.completed') {
const session = event.data.object;
const userId = session.client_reference_id;
// Update user to Pro in PocketBase
if (userId) {
await locals.pb.collection('users').update(userId, {
subscription_status: 'pro'
});
}
}
return new Response('OK');
} catch (err) {
return new Response('Webhook Error', { status: 400 });
}
}
Step 7: Add to PocketBase (2 Min)
Add field to users collection:
{
name: "subscription_status",
type: "select",
options: ["free", "pro"],
default: "free"
}
Done! 🎊
Du hast jetzt:
- ✅ Funktionierende Checkout-Seite
- ✅ Test-Zahlungen möglich
- ✅ User-Status wird aktualisiert
- ✅ Webhook-Integration
Next Steps
Enforce Limits
// In your API routes
if (user.subscription_status !== 'pro' && user.links_count >= 10) {
return json({ error: 'Upgrade to Pro for unlimited links' }, { status: 403 });
}
Add Customer Portal
const session = await stripe.billingPortal.sessions.create({
customer: user.stripe_customer_id,
return_url: `${PUBLIC_APP_URL}/account`
});
Production Checklist
- Switch to live keys
- Update webhook endpoint
- Add error handling
- Add loading states
- Test mit echten Karten
- Setup email notifications
Troubleshooting
"No such price" → Check STRIPE_PRICE_ID in .env
Webhook 400 Error → Wrong webhook secret, check stripe listen output
User not updated → Check PocketBase permissions
Checkout won't load → Check PUBLIC_STRIPE_PUBLISHABLE_KEY
Useful Commands
# Watch webhook events
stripe listen --forward-to localhost:5173/api/webhook
# Trigger test events
stripe trigger checkout.session.completed
# See recent events
stripe events list
# Create test customer
stripe customers create --email=test@example.com