@moneybar.online/moneybar 3.2.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,75 +4,131 @@
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": {
18
- "@supabase/supabase-js": "https://cdn.jsdelivr.net/npm/@supabase/supabase-js@2.86.0/+esm"
98
+ "@supabase/supabase-js": "https://cdn.jsdelivr.net/npm/@supabase/supabase-js@2/+esm"
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@3.1.0/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
- actionFunction: 'myAction',
32
- appId: 'test-app',
33
- freeDownloadLimit: 3,
34
- supabase: {
35
- url: 'your-supabase-url',
36
- anonKey: 'your-anon-key'
37
- },
38
- payment: {
39
- productId: 'your-product-id',
40
- mode: 'test'
41
- },
42
- theme: {
43
- name: 'dark'
44
- }
45
- });
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>
46
122
 
47
- moneyBar.attachToButton('#action-btn', (userContext) => {
48
- if (userContext.isPremium) {
49
- alert('Premium features unlocked!');
50
- } else {
51
- alert(`Free trial: ${userContext.remaining} uses left`);
52
- }
53
- });
54
- </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>
55
130
 
56
- <!-- OPTION 3: UMD Script Tag (No modules) -->
57
- <!-- Uncomment this for traditional script tag usage: -->
58
- <!--
59
- <script src="https://cdn.jsdelivr.net/npm/@supabase/supabase-js@2/dist/umd/supabase.min.js"></script>
60
- <script src="https://cdn.jsdelivr.net/npm/@moneybar.online/moneybar@3.1.0/dist/index.umd.js"></script>
61
- <script>
62
- const moneyBar = new MoneyBar.MoneyBar({
63
- actionFunction: 'myAction',
64
- appId: 'test-app',
65
- freeDownloadLimit: 3,
66
- supabase: {
67
- url: 'your-supabase-url',
68
- anonKey: 'your-anon-key'
69
- },
70
- payment: {
71
- productId: 'your-product-id',
72
- mode: 'test'
73
- }
74
- });
75
- </script>
76
- -->
131
+ <p><small>See <code>moneybar-config-template.js</code> for all integration options and detailed examples!</small></p>
132
+ </div>
77
133
  </body>
78
134
  </html>
@@ -1,28 +1,172 @@
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": {
11
- "@supabase/supabase-js": "https://cdn.jsdelivr.net/npm/@supabase/supabase-js@2.86.0/+esm"
30
+ "@supabase/supabase-js": "https://cdn.jsdelivr.net/npm/@supabase/supabase-js@2/+esm"
12
31
  }
13
32
  }
14
33
  </script>
15
34
  */
16
-
17
- import { MoneyBar } from 'https://cdn.jsdelivr.net/npm/@moneybar.online/moneybar@3.1.0/dist/index.browser.js';
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)
20
- // import { MoneyBar } from 'https://cdn.jsdelivr.net/npm/@moneybar.online/moneybar@3.1.0/dist/index.bundle.js';
38
+ // import { MoneyBar } from 'https://cdn.jsdelivr.net/npm/@moneybar.online/moneybar/dist/index.bundle.js';
21
39
 
22
40
  // Option 3: NPM install usage (for projects with bundlers)
23
41
  // import { MoneyBar } from '@moneybar.online/moneybar';
24
42
 
25
- // Configuration object (matches examples structure)
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
+ /*
26
170
  window.APP_CONFIG = {
27
171
  // REQUIRED: Function name that gets called
28
172
  actionFunction: 'myAction',
@@ -31,7 +175,7 @@ window.APP_CONFIG = {
31
175
  appId: 'my-app',
32
176
 
33
177
  // REQUIRED: Number of free attempts before paywall
34
- freeDownloadLimit: 3,
178
+ freeAttemptLimit: 3,
35
179
 
36
180
  // REQUIRED: Supabase configuration
37
181
  supabase: {
@@ -68,7 +212,7 @@ window.APP_CONFIG = {
68
212
  message: 'Your action completed successfully!'
69
213
  },
70
214
 
71
- // OPTIONAL: Feedback collection
215
+ // OPTIONAL: Feedback collection (Problem #2 solution)
72
216
  feedback: {
73
217
  form: {
74
218
  title: 'Quick Feedback',
@@ -77,12 +221,20 @@ window.APP_CONFIG = {
77
221
  option2: 'Need more features',
78
222
  option3: 'Just testing'
79
223
  },
80
- email: {
81
- resendApiKey: 'your-resend-api-key',
82
- fromEmail: 'hello@yourapp.com'
83
- }
224
+ email: [
225
+ {
226
+ provider: 'resend',
227
+ apiKey: 'your-resend-api-key',
228
+ fromEmail: 'hello@yourapp.com'
229
+ }
230
+ ]
84
231
  }
85
232
  };
233
+ */
234
+
235
+ // ========================================
236
+ // INITIALIZE MONEYBAR
237
+ // ========================================
86
238
 
87
239
  // Initialize MoneyBar with the configuration
88
240
  const moneyBar = new MoneyBar(window.APP_CONFIG);
@@ -98,36 +250,74 @@ moneyBar.attachToButton('#action-btn', (userContext) => {
98
250
  }
99
251
  });
100
252
 
101
- // Global function for easy HTML usage (matches actionFunction)
102
- window.myAction = function(userContext) {
103
- console.log('User context:', userContext);
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...');
104
260
 
105
261
  if (userContext.isPremium) {
106
- alert('Premium features unlocked!');
107
- // Your premium functionality here
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
108
265
  } else {
109
- alert(`Free trial: ${userContext.remaining} uses left`);
110
- // Your free tier functionality here
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
111
269
  }
112
270
  };
113
271
 
114
- // Example alternative action functions
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
115
288
  window.createTemplate = function(userContext) {
289
+ console.log('🎯 INSTANT PAYMENT: Creating template...');
290
+
116
291
  if (userContext.isPremium) {
117
- console.log('Creating premium template...');
118
- alert('Premium template created!');
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
119
295
  } else {
120
- console.log('Creating basic template...');
121
- alert(`Basic template created! ${userContext.remaining} uses remaining`);
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
122
299
  }
123
300
  };
124
301
 
125
- window.downloadFile = function(userContext) {
302
+ // 🎯 DEFAULT: Fallback Action
303
+ window.myAction = function(userContext) {
304
+ console.log('User context:', userContext);
305
+
126
306
  if (userContext.isPremium) {
127
- console.log('Downloading premium file...');
128
- // Premium download logic
307
+ alert('Premium features unlocked!');
308
+ // Your premium functionality here
129
309
  } else {
130
- console.log('Downloading basic file...');
131
- // Basic download logic
310
+ alert(`Free trial: ${userContext.remaining} uses left`);
311
+ // Your free tier functionality here
132
312
  }
133
- };
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.2.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) */