stripe-donation-widget
Version:
A complete, reusable donation system for React/Next.js applications with Stripe integration, real-time MRR tracking, and beautiful UI components.
475 lines (375 loc) โข 12 kB
Markdown
# ๐ฏ Stripe Donation Widget for React/Next.js
A complete, reusable donation system with Stripe integration, real-time MRR tracking, and beautiful UI components.
## โจ Features
- **๐จ Beautiful UI Components** - Pre-built donation widgets and buttons
- **๐ณ Stripe Integration** - Secure payment processing with automatic product creation
- **๐ Real-time MRR Tracking** - Live subscription analytics and goal progress
- **๐ง Configuration-driven** - Single config object controls all behavior
- **๐ฏ Multi-project Support** - Isolated configurations per project
- **๐ฑ Responsive Design** - Works perfectly on all devices
- **โก TypeScript Support** - Full type safety and IntelliSense
- **๐ One-Command Setup** - CLI tool generates everything automatically
## ๐ Quick Start (30 seconds!)
### 1. Install & Initialize
```bash
# Install the package
npm install stripe-donation-widget
# Generate all necessary files automatically
npx stripe-donation-widget init
```
That's it! The CLI will:
- โ
Create API routes (`/api/donation/*`)
- โ
Generate example support page
- โ
Set up environment variables template
- โ
Create setup documentation
- โ
Detect App Router vs Pages Router automatically
### 2. Add Your Stripe Keys
Update `.env.local` with your keys:
```bash
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_...
STRIPE_SECRET_KEY=sk_test_...
```
### 3. Customize & Launch
```tsx
// The CLI creates this file for you at app/support/page.tsx
import { DonationWidget } from 'stripe-donation-widget';
const config = {
stripePublishableKey: process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY!,
projectName: "Your Project",
projectSlug: "your-project",
monthly: { suggested: 5.00 },
annual: { suggested: 50.00 },
goal: { target: 100, showProgress: true }
};
export default function SupportPage() {
return <DonationWidget config={config} />;
}
```
## ๐ฏ What Gets Generated
The CLI creates everything you need:
```
your-project/
โโโ app/api/donation/
โ โโโ create-checkout-session/route.ts # Stripe checkout
โ โโโ stats/route.ts # MRR analytics
โ โโโ webhook/route.ts # Webhook handling
โโโ app/support/page.tsx # Example support page
โโโ .env.example # Environment template
โโโ .env.local # Your actual keys
โโโ DONATION_SETUP.md # Setup guide
```
## ๐ฆ Manual Installation (Alternative)
If you prefer manual setup:
### 1. Install the Package
```bash
npm install stripe-donation-widget
```
### 2. Create API Routes
**`app/api/donation/create-checkout-session/route.ts`**
```typescript
import { NextRequest, NextResponse } from 'next/server';
import { DonationStripeManager } from 'stripe-donation-widget/server';
export async function POST(request: NextRequest) {
try {
const sessionData = await request.json();
const manager = new DonationStripeManager(process.env.STRIPE_SECRET_KEY!);
const result = await manager.createCheckoutSession(sessionData);
return NextResponse.json(result);
} catch (error) {
return NextResponse.json({ error: 'Failed to create checkout session' }, { status: 500 });
}
}
```
**`app/api/donation/stats/route.ts`**
```typescript
import { NextRequest, NextResponse } from 'next/server';
import { DonationStripeManager } from 'stripe-donation-widget/server';
export async function GET(request: NextRequest) {
try {
const { searchParams } = new URL(request.url);
const projectSlug = searchParams.get('projectSlug');
const manager = new DonationStripeManager(process.env.STRIPE_SECRET_KEY!);
const stats = await manager.getSubscriptionSummary(projectSlug!);
return NextResponse.json(stats);
} catch (error) {
return NextResponse.json({ error: 'Failed to fetch stats' }, { status: 500 });
}
}
```
### 3. Use the Components
```tsx
import { DonationWidget, DonationButton } from 'stripe-donation-widget';
import type { DonationConfig } from 'stripe-donation-widget';
const config: DonationConfig = {
stripePublishableKey: process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY!,
projectName: "My Project",
projectSlug: "my-project",
monthly: { suggested: 5.00 },
annual: { suggested: 50.00 },
goal: {
target: 100,
showProgress: true,
description: "Help keep this project running",
calculateFromSubscriptions: true
}
};
export default function SupportPage() {
return (
<div className="max-w-2xl mx-auto p-6">
<h1 className="text-3xl font-bold mb-8">Support Our Project</h1>
{/* Full donation widget */}
<DonationWidget config={config} />
{/* Quick donation button */}
<DonationButton
config={config}
type="monthly"
amount={10}
className="mt-4"
>
Support for $10/month
</DonationButton>
</div>
);
}
```
## Components
### DonationWidget
The main donation component with slider interface and goal tracking.
```tsx
<DonationWidget
config={donationConfig}
showProgress={true}
goal={1000} // Override config goal
current={250} // Override current amount
className="max-w-md"
onSuccess={(sessionId) => console.log('Success:', sessionId)}
onError={(error) => console.error('Error:', error)}
/>
```
**Features:**
- Interactive slider for amount selection
- Preset amount buttons
- Monthly/Annual tabs
- Real-time goal progress
- Responsive design
### DonationButton
Simple button for quick donations.
```tsx
<DonationButton
config={donationConfig}
type="monthly"
amount={25}
className="w-full bg-gradient-to-r from-purple-500 to-pink-500"
>
โค๏ธ Become a Supporter
</DonationButton>
```
## Configuration Options
### DonationConfig
```typescript
interface DonationConfig {
// Required
stripePublishableKey: string;
projectName: string;
projectSlug: string; // Used for Stripe product IDs
// Amount settings
monthly: {
suggested: number;
min?: number; // Defaults to 50% of suggested, minimum $1
max?: number; // Defaults to 10x suggested
};
annual: {
suggested: number;
min?: number;
max?: number;
};
// Goal tracking
goal?: {
target: number; // Monthly goal amount
current?: number; // Static current amount
showProgress?: boolean; // Show progress bar
description?: string; // Goal description
calculateFromSubscriptions?: boolean; // Use real Stripe data
};
// Customization
theme?: 'light' | 'dark' | 'auto';
customAmounts?: boolean; // Allow custom amounts
currency?: string; // Default: 'usd'
successUrl?: string; // Post-checkout redirect
cancelUrl?: string; // Checkout cancel redirect
}
```
## Real MRR Calculation
When `calculateFromSubscriptions: true`, the library automatically:
1. **Fetches active subscriptions** from Stripe
2. **Filters by project** using `projectSlug`
3. **Calculates MRR** as: Monthly Revenue + (Annual Revenue รท 12)
4. **Updates progress** in real-time
Example calculation:
- 5 monthly subscribers at $10/month = $50 MRR
- 2 annual subscribers at $100/year = $16.67 MRR ($200 รท 12)
- **Total MRR: $66.67**
## Stripe Integration
### Automatic Product Creation
The library automatically creates Stripe products using consistent IDs:
- Monthly: `{projectSlug}-monthly-donation`
- Annual: `{projectSlug}-annual-donation`
### Dynamic Pricing
Uses `price_data` for flexible amount selection without pre-creating prices.
### Subscription Tracking
Subscriptions are tagged with `projectSlug` metadata for accurate filtering.
## Multi-Project Usage
### Project A Configuration
```typescript
const projectAConfig: DonationConfig = {
projectName: "Project Alpha",
projectSlug: "project-alpha",
monthly: { suggested: 5.00 },
annual: { suggested: 50.00 },
// ... other config
};
```
### Project B Configuration
```typescript
const projectBConfig: DonationConfig = {
projectName: "Project Beta",
projectSlug: "project-beta",
monthly: { suggested: 10.00 },
annual: { suggested: 100.00 },
// ... other config
};
```
Each project gets:
- โ
Separate Stripe products
- โ
Independent MRR tracking
- โ
Isolated subscription data
- โ
Custom branding and amounts
## Styling & Theming
### Custom Styling
```tsx
<DonationWidget
config={config}
className="border-2 border-purple-500 rounded-xl shadow-lg"
/>
<DonationButton
config={config}
type="monthly"
className="bg-gradient-to-r from-blue-500 to-green-500 hover:scale-105"
>
๐ Launch Support
</DonationButton>
```
### Theme Support
The library respects your app's theme system through CSS variables.
## Error Handling
```tsx
<DonationWidget
config={config}
onError={(error) => {
// Log to your error tracking service
console.error('Donation error:', error);
// Show user-friendly message
toast.error('Something went wrong. Please try again.');
}}
onSuccess={(sessionId) => {
// Track successful checkout initiation
analytics.track('donation_checkout_started', { sessionId });
}}
/>
```
## TypeScript Support
Full TypeScript support with comprehensive type definitions:
```typescript
import type {
DonationConfig,
DonationWidgetProps,
DonationButtonProps,
SubscriptionSummary,
CheckoutSessionData
} from '@/lib/donation';
```
## Best Practices
### 1. Configuration Management
```typescript
// config/donations.ts
export const getDonationConfig = (project: string): DonationConfig => {
const baseConfig = {
stripePublishableKey: process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY!,
currency: 'usd',
customAmounts: true,
};
switch (project) {
case 'project-a':
return { ...baseConfig, projectName: 'Project A', projectSlug: 'project-a', /* ... */ };
case 'project-b':
return { ...baseConfig, projectName: 'Project B', projectSlug: 'project-b', /* ... */ };
default:
throw new Error(`Unknown project: ${project}`);
}
};
```
### 2. Error Boundaries
```tsx
import { ErrorBoundary } from 'react-error-boundary';
<ErrorBoundary fallback={<div>Donation widget unavailable</div>}>
<DonationWidget config={config} />
</ErrorBoundary>
```
### 3. Loading States
```tsx
import { Suspense } from 'react';
<Suspense fallback={<DonationSkeleton />}>
<DonationWidget config={config} />
</Suspense>
```
## Migration Guide
### From Static Goals to Real MRR
**Before:**
```typescript
goal: {
target: 100,
current: 25, // Static value
showProgress: true
}
```
**After:**
```typescript
goal: {
target: 100,
showProgress: true,
calculateFromSubscriptions: true // Uses real Stripe data
}
```
### From Text Inputs to Sliders
The new slider interface is automatically enabled. To revert to text inputs, you would need to modify the component (not recommended).
## Troubleshooting
### Common Issues
**1. "Event handlers cannot be passed to Client Component props"**
- Remove `onSuccess` and `onError` from server components
- Move to client components if needed
**2. "Stripe product not found"**
- Ensure `projectSlug` is consistent
- Check Stripe dashboard for auto-created products
**3. "MRR calculation shows 0"**
- Verify `calculateFromSubscriptions: true`
- Check that subscriptions have correct metadata
- Ensure API route is accessible
**4. "Slider not working on mobile"**
- Ensure `@radix-ui/react-slider` is installed
- Check for CSS conflicts
### Debug Mode
```typescript
const config: DonationConfig = {
// ... your config
debug: true // Enable console logging (if implemented)
};
```
## License
MIT License - feel free to use in your projects!
## Contributing
1. Fork the repository
2. Create a feature branch
3. Add tests for new functionality
4. Submit a pull request
**Ready to accept donations in minutes!** ๐