@moneybar.online/moneybar 3.1.0 → 3.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -4,14 +4,94 @@
4
4
  <meta charset="UTF-8">
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
6
  <title>MoneyBar Integration Example</title>
7
+ <style>
8
+ body {
9
+ font-family: system-ui, sans-serif;
10
+ max-width: 800px;
11
+ margin: 0 auto;
12
+ padding: 2rem;
13
+ line-height: 1.6;
14
+ }
15
+ .demo-section {
16
+ background: #f8f9fa;
17
+ padding: 1.5rem;
18
+ border-radius: 8px;
19
+ margin: 1rem 0;
20
+ }
21
+ button {
22
+ background: #059669;
23
+ color: white;
24
+ border: none;
25
+ padding: 0.75rem 1.5rem;
26
+ border-radius: 6px;
27
+ cursor: pointer;
28
+ font-size: 1rem;
29
+ margin: 0.5rem 0;
30
+ }
31
+ button:hover { background: #047857; }
32
+ .config-note {
33
+ background: #fef3c7;
34
+ padding: 1rem;
35
+ border-radius: 6px;
36
+ margin: 1rem 0;
37
+ border-left: 4px solid #f59e0b;
38
+ }
39
+ </style>
7
40
  </head>
8
41
  <body>
9
- <h1>MoneyBar Integration Test</h1>
10
- <button id="action-btn">Try Action (3 free uses)</button>
42
+ <h1>🚀 MoneyBar Integration Example</h1>
43
+
44
+ <div class="config-note">
45
+ <strong>⚠️ SETUP REQUIRED:</strong> Before testing, you MUST update these credentials in <code>moneybar-config-template.js</code>:
46
+ <ul>
47
+ <li>✅ <strong>Supabase URL</strong> (line 54, 86, 128)</li>
48
+ <li>✅ <strong>Supabase anon key</strong> (line 55, 87, 129)</li>
49
+ <li>✅ <strong>DodoPayments product ID</strong> (line 58, 90, 132)</li>
50
+ <li>✅ <strong>Payment mode</strong> ('test' or 'live')</li>
51
+ </ul>
52
+ <small><strong>Current config:</strong> FEEDBACK_INTELLIGENCE_CONFIG (line 157) - Switch to VALUE_FIRST_CONFIG or INSTANT_PAYMENT_CONFIG if needed</small>
53
+ </div>
54
+
55
+ <div class="demo-section">
56
+ <h2>🎯 The 3 Money-Blocking Problems MoneyBar Solves</h2>
57
+
58
+ <h3>Problem #1: "Forced Sign-In Before Value" (40-60% boost)</h3>
59
+ <p>❌ Users bounce before seeing your product works<br>
60
+ ✅ Solution: Value-first trials - let users try before they buy</p>
61
+
62
+ <h3>Problem #2: "Silent Drop-Off" (20-30% boost)</h3>
63
+ <p>❌ Users try but don't buy - you don't know why<br>
64
+ ✅ Solution: Exit feedback capture - learn why users don't convert</p>
11
65
 
12
- <!-- OPTION 1: Browser with Import Map (Recommended) -->
13
- <!-- Copy this import map and uncomment -->
14
- <!--
66
+ <h3>Problem #3: "Broken Money Flow" (25-40% boost)</h3>
67
+ <p>❌ Complex payments and broken sessions<br>
68
+ ✅ Solution: Seamless auth + instant payments</p>
69
+ </div>
70
+
71
+ <div class="demo-section">
72
+ <h2>🎨 Try the Demo</h2>
73
+ <p>This button demonstrates the current configuration from <code>moneybar-config-template.js</code></p>
74
+ <button id="action-btn">Try Action (Free Trial)</button>
75
+
76
+ <h3>🔧 To Switch Use Cases:</h3>
77
+ <p>Edit line 157 in <code>moneybar-config-template.js</code>:</p>
78
+ <ul>
79
+ <li><code>VALUE_FIRST_CONFIG</code> - PDF generator demo (calls <code>generatePDF</code> function)</li>
80
+ <li><code>FEEDBACK_INTELLIGENCE_CONFIG</code> - SaaS analytics demo (calls <code>runAnalysis</code> function)</li>
81
+ <li><code>INSTANT_PAYMENT_CONFIG</code> - Template creator demo (calls <code>createTemplate</code> function)</li>
82
+ </ul>
83
+
84
+ <div style="background: #fee2e2; padding: 1rem; border-radius: 6px; border-left: 4px solid #ef4444; margin: 1rem 0;">
85
+ <strong>🚨 Common Error:</strong> If you get "Cannot read properties of null (reading 'AuthClient')" error:
86
+ <ul>
87
+ <li>❌ You haven't updated the Supabase credentials</li>
88
+ <li>❌ Invalid Supabase URL or anon key</li>
89
+ <li>✅ Update all placeholder values in the config first!</li>
90
+ </ul>
91
+ </div>
92
+ </div>
93
+
94
+ <!-- Import map for dependencies -->
15
95
  <script type="importmap">
16
96
  {
17
97
  "imports": {
@@ -19,51 +99,36 @@
19
99
  }
20
100
  }
21
101
  </script>
22
- <script type="module" src="moneybar-config-template.js"></script>
23
- -->
24
102
 
25
- <!-- OPTION 2: Self-contained bundle (No import map needed) -->
26
- <!-- Comment out the import map above and use this: -->
27
- <script type="module">
28
- import { MoneyBar } from 'https://cdn.jsdelivr.net/npm/@moneybar.online/moneybar/dist/index.bundle.js';
103
+ <!-- Load MoneyBar configuration and initialization -->
104
+ <script type="module" src="moneybar-config-template.js"></script>
29
105
 
30
- const moneyBar = new MoneyBar({
31
- appId: 'test-app',
32
- freeDownloadLimit: 3,
33
- supabase: {
34
- url: 'your-supabase-url',
35
- anonKey: 'your-anon-key'
36
- },
37
- payment: {
38
- productId: 'your-product-id',
39
- mode: 'test'
40
- },
41
- theme: {
42
- name: 'dark'
43
- }
44
- });
106
+ <div class="demo-section">
107
+ <h2>📚 Setup Checklist</h2>
108
+ <ol>
109
+ <li><strong>🔧 Update Credentials First!</strong>
110
+ <ul>
111
+ <li>Replace <code>your-supabase-url</code> with your actual Supabase project URL</li>
112
+ <li>Replace <code>your-anon-key</code> with your actual Supabase anon key</li>
113
+ <li>Replace <code>your-product-id</code> with your DodoPayments product ID</li>
114
+ <li>Set payment mode: <code>'test'</code> or <code>'live'</code></li>
115
+ </ul>
116
+ </li>
117
+ <li><strong>Choose</strong> your use case config (line 157 in template)</li>
118
+ <li><strong>Test</strong> the button - should work without errors now!</li>
119
+ <li><strong>Copy</strong> to your project when ready</li>
120
+ <li><strong>Add</strong> the import map to your HTML</li>
121
+ </ol>
45
122
 
46
- moneyBar.attachToButton('#action-btn', (userContext) => {
47
- if (userContext.isPremium) {
48
- alert('Premium features unlocked!');
49
- } else {
50
- alert(`Free trial: ${userContext.remaining} uses left`);
51
- }
52
- });
53
- </script>
123
+ <h3>🎛️ Integration Options:</h3>
124
+ <ul>
125
+ <li><strong>CDN + Import Map</strong> - Simple, no build step (current demo)</li>
126
+ <li><strong>Self-contained Bundle</strong> - No import map needed</li>
127
+ <li><strong>NPM Install</strong> - For projects with bundlers</li>
128
+ <li><strong>UMD Script Tags</strong> - Traditional non-module usage</li>
129
+ </ul>
54
130
 
55
- <!-- OPTION 3: UMD Script Tag (No modules) -->
56
- <!-- Uncomment this for traditional script tag usage: -->
57
- <!--
58
- <script src="https://cdn.jsdelivr.net/npm/@supabase/supabase-js@2/dist/umd/supabase.min.js"></script>
59
- <script src="https://cdn.jsdelivr.net/npm/@moneybar.online/moneybar/dist/index.umd.js"></script>
60
- <script>
61
- const moneyBar = new MoneyBar.MoneyBar({
62
- appId: 'test-app',
63
- freeDownloadLimit: 3,
64
- // ... configuration
65
- });
66
- </script>
67
- -->
131
+ <p><small>See <code>moneybar-config-template.js</code> for all integration options and detailed examples!</small></p>
132
+ </div>
68
133
  </body>
69
134
  </html>
@@ -1,10 +1,29 @@
1
1
  // MoneyBar Configuration Template
2
2
  // Copy this file and customize for your project
3
3
 
4
+ // ========================================
5
+ // THE 3 MONEY-BLOCKING STAGES FOR INDIE HACKERS
6
+ // ========================================
7
+
8
+ // PROBLEM #1: "Forced Sign-In Before Value" (40-60% conversion boost)
9
+ // Solution: Value-first trials - let users try before they buy
10
+ // Use case: PDF generators, image tools, converters
11
+
12
+ // PROBLEM #2: "Silent Drop-Off" (20-30% conversion boost)
13
+ // Solution: Exit feedback capture - learn why users don't convert
14
+ // Use case: SaaS trials, B2B tools, complex products
15
+
16
+ // PROBLEM #3: "Broken Money Flow" (25-40% conversion boost)
17
+ // Solution: Seamless auth + instant payments
18
+ // Use case: Templates, premium features, instant upgrades
19
+
20
+ // ========================================
21
+ // IMPORT OPTIONS
22
+ // ========================================
23
+
4
24
  // Option 1: Browser CDN Usage (Recommended for simple projects)
5
- // Use this import and add the import map to your HTML
25
+ // Add this import map to your HTML:
6
26
  /*
7
- Add to your HTML:
8
27
  <script type="importmap">
9
28
  {
10
29
  "imports": {
@@ -13,7 +32,6 @@ Add to your HTML:
13
32
  }
14
33
  </script>
15
34
  */
16
-
17
35
  import { MoneyBar } from 'https://cdn.jsdelivr.net/npm/@moneybar.online/moneybar/dist/index.browser.js';
18
36
 
19
37
  // Option 2: Self-contained bundle (No import map needed)
@@ -22,12 +40,142 @@ import { MoneyBar } from 'https://cdn.jsdelivr.net/npm/@moneybar.online/moneybar
22
40
  // Option 3: NPM install usage (for projects with bundlers)
23
41
  // import { MoneyBar } from '@moneybar.online/moneybar';
24
42
 
25
- const moneyBar = new MoneyBar({
43
+ // ========================================
44
+ // CONFIGURATION EXAMPLES BY USE CASE
45
+ // ========================================
46
+
47
+ // 🎯 USE CASE 1: VALUE-FIRST DEMO (Problem #1 Solution)
48
+ // Perfect for: PDF generators, image tools, converters
49
+ const VALUE_FIRST_CONFIG = {
50
+ actionFunction: 'generatePDF',
51
+ appId: 'pdf-generator',
52
+ freeAttemptLimit: 3, // Give enough attempts to prove value
53
+ supabase: {
54
+ url: 'your-supabase-url',
55
+ anonKey: 'your-anon-key'
56
+ },
57
+ payment: {
58
+ productId: 'your-product-id',
59
+ mode: 'test'
60
+ },
61
+ theme: {
62
+ name: 'emerald', // Trustworthy, fresh
63
+ primaryColor: '#059669'
64
+ },
65
+ titleBar: {
66
+ title: '💰 PDF Generator',
67
+ links: [
68
+ { text: 'How it Works', url: '#how', target: '_self' },
69
+ { text: 'Examples', url: '#examples', target: '_self' }
70
+ ]
71
+ },
72
+ successMessage: {
73
+ enabled: true,
74
+ title: 'PDF Generated!',
75
+ message: 'Your PDF has been created successfully. Try more examples!'
76
+ }
77
+ };
78
+
79
+ // 🎯 USE CASE 2: FEEDBACK INTELLIGENCE (Problem #2 Solution)
80
+ // Perfect for: SaaS trials, B2B tools, complex products
81
+ const FEEDBACK_INTELLIGENCE_CONFIG = {
82
+ actionFunction: 'runAnalysis',
83
+ appId: 'saas-analytics',
84
+ freeAttemptLimit: 3,
85
+ supabase: {
86
+ url: 'your-supabase-url',
87
+ anonKey: 'your-anon-key'
88
+ },
89
+ payment: {
90
+ productId: 'your-product-id',
91
+ mode: 'test'
92
+ },
93
+ // KEY FEATURE: Feedback collection on cancel/exit
94
+ feedback: {
95
+ form: {
96
+ title: 'Quick Feedback',
97
+ description: 'What stopped you from upgrading?',
98
+ option1: 'Too complex to implement',
99
+ option2: 'Not enough ROI for my team',
100
+ option3: 'Need more features first'
101
+ },
102
+ email: [
103
+ {
104
+ provider: 'resend',
105
+ apiKey: 'your-resend-api-key',
106
+ fromEmail: 'feedback@yourapp.com'
107
+ }
108
+ // Future providers can be added:
109
+ // {
110
+ // provider: 'sendgrid',
111
+ // apiKey: 'your-sendgrid-api-key',
112
+ // fromEmail: 'feedback@yourapp.com'
113
+ // }
114
+ ]
115
+ },
116
+ theme: {
117
+ name: 'corporate', // Professional B2B
118
+ primaryColor: '#0066cc'
119
+ },
120
+ titleBar: {
121
+ title: '📈 Analytics Tool',
122
+ links: [
123
+ { text: 'Features', url: '#features', target: '_self' },
124
+ { text: 'ROI Calculator', url: '#roi', target: '_self' }
125
+ ]
126
+ }
127
+ };
128
+
129
+ // 🎯 USE CASE 3: INSTANT PAYMENT FLOW (Problem #3 Solution)
130
+ // Perfect for: Templates, premium features, instant upgrades
131
+ const INSTANT_PAYMENT_CONFIG = {
132
+ actionFunction: 'createTemplate',
133
+ appId: 'template-creator',
134
+ freeAttemptLimit: 5, // Lower to trigger payment faster
135
+ supabase: {
136
+ url: 'your-supabase-url',
137
+ anonKey: 'your-anon-key'
138
+ },
139
+ payment: {
140
+ productId: 'your-product-id',
141
+ mode: 'test'
142
+ },
143
+ theme: {
144
+ name: 'dark', // Modern, premium feel
145
+ primaryColor: '#7c3aed'
146
+ },
147
+ titleBar: {
148
+ title: '⚡ Template Creator',
149
+ links: [
150
+ { text: 'Templates', url: '#templates', target: '_self' },
151
+ { text: 'Pricing', url: '#pricing', target: '_self' }
152
+ ]
153
+ },
154
+ successMessage: {
155
+ enabled: true,
156
+ title: 'Template Created!',
157
+ message: 'Your professional template is ready. Download and customize!'
158
+ }
159
+ };
160
+
161
+ // ========================================
162
+ // CHOOSE YOUR CONFIGURATION
163
+ // ========================================
164
+
165
+ // Pick one of the configs above or create your own:
166
+ window.APP_CONFIG = FEEDBACK_INTELLIGENCE_CONFIG; // Change this to your preferred config
167
+
168
+ // Or create a custom configuration:
169
+ /*
170
+ window.APP_CONFIG = {
171
+ // REQUIRED: Function name that gets called
172
+ actionFunction: 'myAction',
173
+
26
174
  // REQUIRED: Your unique app identifier
27
175
  appId: 'my-app',
28
176
 
29
177
  // REQUIRED: Number of free attempts before paywall
30
- freeDownloadLimit: 3,
178
+ freeAttemptLimit: 3,
31
179
 
32
180
  // REQUIRED: Supabase configuration
33
181
  supabase: {
@@ -64,7 +212,7 @@ const moneyBar = new MoneyBar({
64
212
  message: 'Your action completed successfully!'
65
213
  },
66
214
 
67
- // OPTIONAL: Feedback collection
215
+ // OPTIONAL: Feedback collection (Problem #2 solution)
68
216
  feedback: {
69
217
  form: {
70
218
  title: 'Quick Feedback',
@@ -73,37 +221,103 @@ const moneyBar = new MoneyBar({
73
221
  option2: 'Need more features',
74
222
  option3: 'Just testing'
75
223
  },
76
- email: {
77
- resendApiKey: 'your-resend-api-key',
78
- fromEmail: 'hello@yourapp.com'
79
- }
224
+ email: [
225
+ {
226
+ provider: 'resend',
227
+ apiKey: 'your-resend-api-key',
228
+ fromEmail: 'hello@yourapp.com'
229
+ }
230
+ ]
80
231
  }
81
- });
232
+ };
233
+ */
234
+
235
+ // ========================================
236
+ // INITIALIZE MONEYBAR
237
+ // ========================================
238
+
239
+ // Initialize MoneyBar with the configuration
240
+ const moneyBar = new MoneyBar(window.APP_CONFIG);
82
241
 
83
242
  // Attach to button with your business logic
84
- moneyBar.attachToButton('#your-button-id', (userContext) => {
85
- // Your app's functionality here
86
- console.log('User context:', userContext);
243
+ moneyBar.attachToButton('#action-btn', (userContext) => {
244
+ // Call the specified action function
245
+ if (typeof window[window.APP_CONFIG.actionFunction] === 'function') {
246
+ window[window.APP_CONFIG.actionFunction](userContext);
247
+ } else {
248
+ // Fallback action
249
+ myAction(userContext);
250
+ }
251
+ });
252
+
253
+ // ========================================
254
+ // ACTION FUNCTIONS BY USE CASE
255
+ // ========================================
256
+
257
+ // 🎯 USE CASE 1: Value-First Demo Actions
258
+ window.generatePDF = function(userContext) {
259
+ console.log('🎯 VALUE-FIRST: Generating PDF...');
87
260
 
88
261
  if (userContext.isPremium) {
89
- console.log('Premium user - unlock all features');
90
- // Premium functionality
262
+ console.log('Premium PDF with all features');
263
+ alert('Premium PDF generated with watermark removal, custom fonts, and high quality!');
264
+ // Your premium PDF generation logic here
91
265
  } else {
92
- console.log(`Free user - ${userContext.remaining} uses remaining`);
93
- // Free tier functionality
266
+ console.log(`📄 Basic PDF generated. ${userContext.remaining} attempts remaining`);
267
+ alert(`Basic PDF generated! ${userContext.remaining} free attempts left.`);
268
+ // Your basic PDF generation logic here
94
269
  }
270
+ };
95
271
 
96
- // Example: Your app logic
97
- // generatePDF();
98
- // downloadFile();
99
- // processData();
100
- });
272
+ // 🎯 USE CASE 2: Feedback Intelligence Actions
273
+ window.runAnalysis = function(userContext) {
274
+ console.log('🎯 FEEDBACK INTELLIGENCE: Running analysis...');
275
+
276
+ if (userContext.isPremium) {
277
+ console.log('✅ Full analytics suite access');
278
+ alert('Complete analysis ready! Access to all metrics, exports, and AI insights.');
279
+ // Your premium analytics logic here
280
+ } else {
281
+ console.log(`📊 Basic analysis complete. ${userContext.remaining} attempts remaining`);
282
+ alert(`Analysis complete! Limited data shown. ${userContext.remaining} attempts left.`);
283
+ // Your basic analytics logic here
284
+ }
285
+ };
286
+
287
+ // 🎯 USE CASE 3: Instant Payment Actions
288
+ window.createTemplate = function(userContext) {
289
+ console.log('🎯 INSTANT PAYMENT: Creating template...');
290
+
291
+ if (userContext.isPremium) {
292
+ console.log('✅ Premium template with full customization');
293
+ alert('Premium template created! Full customization, commercial license, and PSD files included.');
294
+ // Your premium template logic here
295
+ } else {
296
+ console.log(`🎨 Basic template created. ${userContext.remaining} attempts remaining`);
297
+ alert(`Template created! Basic version only. ${userContext.remaining} attempts left.`);
298
+ // Your basic template logic here
299
+ }
300
+ };
101
301
 
102
- // Global function for easy HTML usage
302
+ // 🎯 DEFAULT: Fallback Action
103
303
  window.myAction = function(userContext) {
304
+ console.log('User context:', userContext);
305
+
104
306
  if (userContext.isPremium) {
105
307
  alert('Premium features unlocked!');
308
+ // Your premium functionality here
106
309
  } else {
107
310
  alert(`Free trial: ${userContext.remaining} uses left`);
311
+ // Your free tier functionality here
108
312
  }
109
- };
313
+ };
314
+
315
+ // ========================================
316
+ // QUICK REFERENCE: Available Themes
317
+ // ========================================
318
+
319
+ // Light themes: light, cupcake, bumblebee, emerald, corporate, garden
320
+ // Dark themes: dark, synthwave, retro, cyberpunk, valentine, halloween
321
+ // Professional: corporate, business, luxury
322
+ // Creative: fantasy, cyberpunk, synthwave
323
+ // And 20+ more themes available!
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moneybar.online/moneybar",
3
- "version": "3.1.0",
3
+ "version": "3.3.0",
4
4
  "description": "The navbar of monetization. Fix the 3 money-blocking stages: forced sign-ins, silent drop-offs, and broken payment flows. Turn browsers into buyers.",
5
5
  "main": "dist/index.cjs.js",
6
6
  "module": "dist/index.esm.js",
package/src/index.ts CHANGED
@@ -23,13 +23,13 @@ import { createClient } from '@supabase/supabase-js';
23
23
  *
24
24
  * const moneyBar = new MoneyBar({
25
25
  * appId: 'my-pdf-app',
26
- * freeDownloadLimit: 3,
26
+ * freeAttemptLimit: 3,
27
27
  * supabase: { url: 'https://xyz.supabase.co', anonKey: 'anon_key' },
28
28
  * payment: { productId: 'prod_xyz' }
29
29
  * });
30
30
  *
31
31
  * // Single button - handles all monetization automatically
32
- * moneyBar.attachToButton('#download-btn', (userContext) => {
32
+ * moneyBar.attachToButton('#action-btn', (userContext) => {
33
33
  * // Your action logic here - with user context for personalization
34
34
  * if (userContext.isPremium) {
35
35
  * generatePremiumPDF();
@@ -243,10 +243,10 @@ export class MoneyBar {
243
243
 
244
244
  const status: DownloadStatus = {
245
245
  currentCount: count,
246
- limit: this.config.freeDownloadLimit,
246
+ limit: this.config.freeAttemptLimit,
247
247
  isPremium,
248
248
  isAuthenticated: !!this.currentUser,
249
- remaining: Math.max(0, this.config.freeDownloadLimit - count)
249
+ remaining: Math.max(0, this.config.freeAttemptLimit - count)
250
250
  };
251
251
 
252
252
  // Cache the status
@@ -259,10 +259,10 @@ export class MoneyBar {
259
259
  // Return safe defaults on error
260
260
  const errorStatus: DownloadStatus = {
261
261
  currentCount: 0,
262
- limit: this.config.freeDownloadLimit,
262
+ limit: this.config.freeAttemptLimit,
263
263
  isPremium: false,
264
264
  isAuthenticated: false,
265
- remaining: this.config.freeDownloadLimit
265
+ remaining: this.config.freeAttemptLimit
266
266
  };
267
267
 
268
268
  // Cache error status for short time to avoid repeated failures
@@ -595,8 +595,8 @@ export class MoneyBar {
595
595
  const newCount = await this.incrementDownloadCount();
596
596
 
597
597
  // Double-check: if server returned count exceeding limit, something went wrong
598
- if (newCount > this.config.freeDownloadLimit) {
599
- console.error(`⚠️ Server returned count (${newCount}) exceeding limit (${this.config.freeDownloadLimit})`);
598
+ if (newCount > this.config.freeAttemptLimit) {
599
+ console.error(`⚠️ Server returned count (${newCount}) exceeding limit (${this.config.freeAttemptLimit})`);
600
600
  // Don't update UI with invalid state
601
601
  return;
602
602
  }
@@ -2441,7 +2441,25 @@ export class MoneyBar {
2441
2441
  }
2442
2442
 
2443
2443
  private async sendFeedbackEmail(feedbackData: any): Promise<void> {
2444
- const emailConfig = this.config.feedback?.email || this.config.email;
2444
+ // Get email config - handle both new array format and legacy format
2445
+ let emailConfig;
2446
+ if (this.config.feedback?.email) {
2447
+ // New array format - use first provider (typically resend)
2448
+ if (Array.isArray(this.config.feedback.email)) {
2449
+ emailConfig = this.config.feedback.email[0];
2450
+ } else {
2451
+ // Legacy format fallback
2452
+ emailConfig = this.config.feedback.email as any;
2453
+ }
2454
+ } else if (this.config.email) {
2455
+ // Deprecated legacy email config
2456
+ emailConfig = {
2457
+ provider: 'resend',
2458
+ apiKey: (this.config.email as any).resendApiKey,
2459
+ fromEmail: (this.config.email as any).fromEmail
2460
+ };
2461
+ }
2462
+
2445
2463
  if (!emailConfig) {
2446
2464
  throw new Error('Email configuration not available');
2447
2465
  }
@@ -2456,19 +2474,25 @@ Timestamp: ${feedbackData.timestamp}
2456
2474
  User Agent: ${feedbackData.userAgent}
2457
2475
  `.trim();
2458
2476
 
2459
- const response = await fetch('https://api.resend.com/emails', {
2460
- method: 'POST',
2461
- headers: {
2462
- 'Content-Type': 'application/json',
2463
- 'Authorization': `Bearer ${emailConfig.resendApiKey}`
2464
- },
2465
- body: JSON.stringify({
2466
- from: emailConfig.fromEmail,
2467
- to: [emailConfig.fromEmail], // Send feedback to yourself
2468
- subject: `Feedback from ${this.config.appId}`,
2469
- text: emailBody
2470
- })
2471
- });
2477
+ // Handle different email providers
2478
+ let response;
2479
+ if (emailConfig.provider === 'resend' || !emailConfig.provider) {
2480
+ response = await fetch('https://api.resend.com/emails', {
2481
+ method: 'POST',
2482
+ headers: {
2483
+ 'Content-Type': 'application/json',
2484
+ 'Authorization': `Bearer ${emailConfig.apiKey || (emailConfig as any).resendApiKey}`
2485
+ },
2486
+ body: JSON.stringify({
2487
+ from: emailConfig.fromEmail,
2488
+ to: [emailConfig.fromEmail], // Send feedback to yourself
2489
+ subject: `Feedback from ${this.config.appId}`,
2490
+ text: emailBody
2491
+ })
2492
+ });
2493
+ } else {
2494
+ throw new Error(`Unsupported email provider: ${emailConfig.provider}`);
2495
+ }
2472
2496
 
2473
2497
  if (!response.ok) {
2474
2498
  throw new Error(`Email API error: ${response.status}`);
@@ -2959,8 +2983,8 @@ User Agent: ${feedbackData.userAgent}
2959
2983
  if (!this.config.appId) throw new Error('appId is required');
2960
2984
  if (!this.config.supabase?.url) throw new Error('supabase.url is required');
2961
2985
  if (!this.config.supabase?.anonKey) throw new Error('supabase.anonKey is required');
2962
- if (typeof this.config.freeDownloadLimit !== 'number' || this.config.freeDownloadLimit < 0) {
2963
- throw new Error('freeDownloadLimit must be a non-negative number');
2986
+ if (typeof this.config.freeAttemptLimit !== 'number' || this.config.freeAttemptLimit < 0) {
2987
+ throw new Error('freeAttemptLimit must be a non-negative number');
2964
2988
  }
2965
2989
  }
2966
2990
 
package/src/types.ts CHANGED
@@ -3,7 +3,7 @@ export interface MoneyBarConfig {
3
3
  appId: string;
4
4
 
5
5
  /** Number of free attempts allowed before paywall */
6
- freeDownloadLimit: number;
6
+ freeAttemptLimit: number;
7
7
 
8
8
  /** Supabase configuration */
9
9
  supabase: {
@@ -26,10 +26,11 @@ export interface MoneyBarConfig {
26
26
  option2: string;
27
27
  option3: string;
28
28
  };
29
- email: {
30
- resendApiKey: string;
29
+ email: Array<{
30
+ provider: 'resend' | 'sendgrid' | 'mailgun' | 'ses'; // Extensible for future providers
31
+ apiKey: string;
31
32
  fromEmail: string;
32
- };
33
+ }>;
33
34
  };
34
35
 
35
36
  /** @deprecated Use feedback.email instead - Email service configuration (for feedback and notifications) */