@moneybar.online/moneybar
v11.4.0
Published
The navbar of monetization. Fix the 3 money-blocking stages: forced sign-ins, silent drop-offs, and broken payment flows. Turn browsers into buyers.
Maintainers
Readme
MoneyBar
The navbar that gets you money faster. Complete backend infrastructure included. Setup in 1 minute.
MoneyBar handles authentication, payment processing, usage limits, and subscription management - all in one navbar component. No backend coding required.
Quick Start (2 Steps)
Step 1: Copy & Customize Config
Copy moneybar-config-template.js to your project and customize these settings:
const CONFIG = {
// ========================================
// CUSTOMIZABLE SETTINGS
// ========================================
// Security key - Get yours from Supabase
// Run: SELECT generate_security_key('my-app', 'yourdomain.com', '[email protected]', 3)
security_key: 'sk_...',
// What are users doing? (shown in UI: "PDF Generations: 2/3")
actionLabel: 'PDF Generations', // Examples: 'Image Exports', 'Downloads', 'AI Requests'
// How many free attempts before paywall?
freeAttemptLimit: 3,
// Payment setup (optional - add later if needed)
payment: [{
provider: 'dodo',
productId: 'pdt_xxx', // Get from Dodo Payments dashboard
mode: 'test' // 'test' or 'live'
}],
// Feedback form (optional)
feedback: {
form: {
title: 'Quick Feedback',
description: 'What stopped you from upgrading?',
option1: 'Too expensive',
option2: 'Not enough features',
option3: 'Need more time to evaluate'
},
email: [{
provider: 'resend',
apiKey: 'your-resend-api-key',
fromEmail: '[email protected]'
}]
},
// UI Theme (optional)
theme: {
name: 'emerald', // DaisyUI theme name
primaryColor: '#059669'
},
// Title bar (optional)
titleBar: {
title: '🚀 My App',
links: [
{ text: 'Features', url: '#features', target: '_self' },
{ text: 'Pricing', url: '#pricing', target: '_self' }
]
},
// ⚠️ DO NOT MODIFY - Required for MoneyBar to work
supabase: {
url: 'https://qhlzdkwcrrjjcqvupyaa.supabase.co',
anonKey: 'eyJ...'
}
};Step 2: Add to HTML & Attach to Buttons
Add these scripts to your HTML:
<!DOCTYPE html>
<html>
<head>
<title>My App</title>
</head>
<body>
<!-- Your app content -->
<button id="my-custom-btn">Generate PDF</button>
<!-- Step 2a: Load MoneyBar config -->
<script type="module" src="moneybar-config-template.js"></script>
<!-- Step 2b: Attach to your buttons -->
<script type="module">
// Access the MoneyBar instance
window.moneyBar.attachToButton('#my-custom-btn', (userContext) => {
// Check if user is premium
if (userContext.isPremium) {
// ✅ Premium user - no limits!
generatePDF(); // Your custom function
} else {
// Free user - show remaining attempts
alert(`Free user. ${userContext.remaining} attempts remaining`);
if (userContext.remaining > 0) {
generatePDF(); // Your custom function
}
}
});
// Your custom function
function generatePDF() {
console.log('Generating PDF...');
// Your PDF generation logic here
}
</script>
</body>
</html>Understanding UserContext
When you attach to a button, you get a userContext object with all user info:
window.moneyBar.attachToButton('#my-btn', (userContext) => {
// Premium status
userContext.isPremium // true/false - Does user have premium access?
userContext.isAuthenticated // true/false - Is user signed in?
// Usage limits
userContext.currentCount // Number - How many times user has used the feature
userContext.remaining // Number - Remaining free attempts
userContext.limit // Number - Total free attempt limit
// User info
userContext.email // String - User's email (if signed in)
userContext.name // String - User's name (if available)
userContext.user // Object - Full Supabase user object
// Subscription details (only for subscription users)
userContext.subscriptionType // 'monthly', 'quarterly', 'yearly'
userContext.subscriptionId // String - Subscription ID
userContext.subscriptionExpiresAt // ISO date string - When subscription expires
userContext.subscriptionDaysRemaining // Number - Days until expiry
userContext.isSubscriptionActive // true/false - Active or cancelled
userContext.nextBillingDate // ISO date string - Next billing date
userContext.billingFrequency // String - 'monthly', 'quarterly', etc.
userContext.customerId // String - Customer ID for portal access
});UserContext for Different Payment Types
One-Time Payment:
{
isPremium: true,
subscriptionType: 'one-time', // Indicates one-time payment
// No subscription fields (expiresAt, nextBillingDate, etc.)
}Subscription Payment:
{
isPremium: true,
subscriptionType: 'monthly', // or 'quarterly', 'yearly'
subscriptionId: 'sub_xxx',
subscriptionExpiresAt: '2026-02-06',
subscriptionDaysRemaining: 31,
isSubscriptionActive: true, // false if cancelled
nextBillingDate: '2026-02-06',
billingFrequency: 'monthly',
customerId: 'cus_xxx' // For customer portal
}Note: Payment amounts, transaction IDs, and billing details are available in your Dodo Payments dashboard.
Common Patterns
Pattern 1: Premium vs Free
window.moneyBar.attachToButton('#export-btn', (userContext) => {
if (userContext.isPremium) {
// Full access
exportHighQuality();
} else {
// Limited access
exportLowQuality();
}
});Pattern 2: Check Remaining Attempts
window.moneyBar.attachToButton('#convert-btn', (userContext) => {
if (userContext.isPremium) {
convert();
} else if (userContext.remaining > 0) {
convert();
console.log(`${userContext.remaining} attempts left`);
} else {
console.log('No attempts remaining - upgrade required');
}
});Pattern 3: Multiple Buttons
// Attach to multiple buttons with the same logic
window.moneyBar.attachToButton('#btn1', handleAction);
window.moneyBar.attachToButton('#btn2', handleAction);
window.moneyBar.attachToButton('#btn3', handleAction);
function handleAction(userContext) {
if (userContext.isPremium) {
performAction();
}
}Pattern 4: Different Actions per Button
// Button 1: Export
window.moneyBar.attachToButton('#export-btn', (ctx) => {
if (ctx.isPremium) exportPDF();
});
// Button 2: Convert
window.moneyBar.attachToButton('#convert-btn', (ctx) => {
if (ctx.isPremium) convertImage();
});Integrating Your Existing Functions
MoneyBar works with your existing functions. Here's how to integrate them properly:
Basic Integration
// Your existing function
async function generatePDF(userId, template) {
const response = await fetch('/api/generate-pdf', {
method: 'POST',
body: JSON.stringify({ userId, template })
});
if (!response.ok) {
throw new Error('PDF generation failed');
}
return await response.json();
}
// Integrate with MoneyBar
window.moneyBar.attachToButton('#generate-pdf-btn', async (userContext) => {
if (userContext.isPremium) {
// Premium: Unlimited access
try {
const result = await generatePDF(userContext.email, 'premium-template');
alert('✅ PDF generated successfully!');
} catch (error) {
alert('❌ Error: ' + error.message);
throw error; // Re-throw to prevent count increment
}
} else {
// Free: Limited attempts
try {
const result = await generatePDF(userContext.email, 'basic-template');
const remaining = userContext.remaining - 1;
alert(`✅ PDF generated! ${remaining} attempts remaining.`);
} catch (error) {
alert(`❌ Error: ${error.message}\n\nYou still have ${userContext.remaining} attempts.`);
throw error; // Re-throw to prevent count increment
}
}
});How Success/Error Handling Works
Critical: Count increments ONLY if your function succeeds
window.moneyBar.attachToButton('#my-btn', async (userContext) => {
try {
// Your async function runs FIRST
await yourCustomFunction();
// ✅ SUCCESS PATH:
// - Function completed without error
// - MoneyBar increments count AFTER this
// - User consumed 1 attempt
} catch (error) {
// ❌ ERROR PATH:
// - Function threw an error
// - Re-throw the error to prevent count increment
// - User does NOT consume an attempt
console.error('Function failed:', error);
throw error; // IMPORTANT: Re-throw to prevent count increment
}
});Real-World Example: API Call with Error Handling
// Your existing API function
async function processImage(imageUrl, format) {
const response = await fetch('https://api.yourapp.com/process', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ imageUrl, format })
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.message || 'Processing failed');
}
return await response.json();
}
// Integrate with MoneyBar
window.moneyBar.attachToButton('#process-image-btn', async (userContext) => {
const imageUrl = document.getElementById('image-url').value;
const format = userContext.isPremium ? 'high-quality' : 'standard';
try {
const result = await processImage(imageUrl, format);
// Success
if (userContext.isPremium) {
alert('✅ Image processed in high quality!');
} else {
const remaining = userContext.remaining - 1;
alert(`✅ Image processed! ${remaining} attempts remaining.`);
}
} catch (error) {
// Error - API failed, network issue, etc.
if (userContext.isPremium) {
alert('❌ Processing failed: ' + error.message);
} else {
alert(`❌ Processing failed: ${error.message}\n\nNo attempt used. You still have ${userContext.remaining} attempts.`);
}
throw error; // Re-throw to prevent count increment
}
});Key Points
- Async Functions: MoneyBar automatically waits for async functions to complete
- Error Handling: If your function throws an error, count does NOT increment
- Re-throw Errors: Always
throw error;in catch blocks to prevent count increment - Success/Error Feedback: Show different messages based on outcome and user type
- userContext.isPremium: Use this to provide different features/quality for premium users
What MoneyBar Handles Automatically
✅ Authentication - Google sign-in (more providers coming) ✅ Usage Tracking - Counts attempts per user per app ✅ Payment Flow - Checkout, webhooks, confirmation ✅ Subscription Management - Auto-renewal, expiry, cancellation ✅ Customer Portal - Self-service subscription management ✅ Caching - 24h cache for premium status, 60s for usage counts ✅ Multi-app Support - Same user, different apps, separate limits
Configuration Reference
| Setting | Required | Purpose | Example |
|---------|----------|---------|---------|
| security_key | ✅ Yes | App identifier + auth | 'sk_...' |
| actionLabel | ✅ Yes | What users are doing | 'PDF Generations' |
| freeAttemptLimit | ✅ Yes | Free attempts before paywall | 3 |
| payment | ⬜ Optional | Payment provider config | Dodo Payments |
| feedback | ⬜ Optional | Collect user feedback | Email to you |
| theme | ⬜ Optional | Customize colors | DaisyUI themes |
| titleBar | ⬜ Optional | Navbar title & links | Your branding |
| supabase | ✅ Yes | Backend (DO NOT CHANGE) | Pre-configured |
Getting Your Security Key
MoneyBar is currently in private beta.
To get a security key for beta testing, please contact the authors:
- Email: [email protected]
- GitHub: Open an issue
We'll provide you with a security key and help you get set up!
Support
- Issues: GitHub Issues
- Docs: moneybar.online
License
MIT
