@moneybar.online/moneybar 3.2.0 → 3.4.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.
- package/README.md +123 -93
- package/dist/index.browser.js +82 -37
- package/dist/index.browser.js.map +1 -1
- package/dist/index.bundle.js +1 -1
- package/dist/index.bundle.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.esm.js +82 -37
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +82 -37
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +11 -7
- package/dist/types.d.ts.map +1 -1
- package/example-template.html +113 -57
- package/moneybar-config-template.js +227 -33
- package/package.json +3 -4
- package/src/index.ts +80 -37
- package/src/types.ts +11 -7
- package/dist/index.cjs.js +0 -3058
- package/dist/index.cjs.js.map +0 -1
- package/dist/index.umd.js +0 -2
- package/dist/index.umd.js.map +0 -1
package/README.md
CHANGED
|
@@ -21,25 +21,35 @@ npm install @moneybar.online/moneybar
|
|
|
21
21
|
import { MoneyBar } from '@moneybar.online/moneybar';
|
|
22
22
|
|
|
23
23
|
const moneyBar = new MoneyBar({
|
|
24
|
+
actionFunction: 'generatePDF', // Function name that gets called
|
|
24
25
|
appId: 'my-pdf-app',
|
|
25
|
-
|
|
26
|
+
freeAttemptLimit: 3,
|
|
26
27
|
supabase: {
|
|
27
28
|
url: 'your-supabase-url',
|
|
28
29
|
anonKey: 'your-anon-key'
|
|
29
30
|
},
|
|
30
|
-
payment: {
|
|
31
|
-
|
|
31
|
+
payment: [{
|
|
32
|
+
provider: 'dodo',
|
|
33
|
+
productId: 'your-product-id',
|
|
34
|
+
mode: 'test'
|
|
35
|
+
}]
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
moneyBar.attachToButton('#action-btn', (userContext) => {
|
|
39
|
+
// Call the specified action function
|
|
40
|
+
if (typeof window[moneyBar.config.actionFunction] === 'function') {
|
|
41
|
+
window[moneyBar.config.actionFunction](userContext);
|
|
32
42
|
}
|
|
33
43
|
});
|
|
34
44
|
|
|
35
|
-
|
|
45
|
+
// Define the function specified in actionFunction
|
|
46
|
+
function generatePDF(userContext) {
|
|
36
47
|
if (userContext.isPremium) {
|
|
37
|
-
|
|
48
|
+
alert('Premium PDF generated with all features!');
|
|
38
49
|
} else {
|
|
39
|
-
|
|
40
|
-
console.log(`${userContext.remaining} downloads remaining`);
|
|
50
|
+
alert(`Basic PDF generated! ${userContext.remaining} downloads remaining`);
|
|
41
51
|
}
|
|
42
|
-
}
|
|
52
|
+
}
|
|
43
53
|
```
|
|
44
54
|
|
|
45
55
|
### Option 2: CDN with Import Map (Recommended for HTML projects)
|
|
@@ -63,8 +73,9 @@ moneyBar.attachToButton('#download-btn', (userContext) => {
|
|
|
63
73
|
import { MoneyBar } from 'https://cdn.jsdelivr.net/npm/@moneybar.online/moneybar/dist/index.browser.js';
|
|
64
74
|
|
|
65
75
|
const moneyBar = new MoneyBar({
|
|
76
|
+
actionFunction: 'myAction', // Function name that gets called
|
|
66
77
|
appId: 'my-app',
|
|
67
|
-
|
|
78
|
+
freeAttemptLimit: 3,
|
|
68
79
|
supabase: {
|
|
69
80
|
url: 'your-supabase-url',
|
|
70
81
|
anonKey: 'your-anon-key'
|
|
@@ -75,12 +86,20 @@ moneyBar.attachToButton('#download-btn', (userContext) => {
|
|
|
75
86
|
});
|
|
76
87
|
|
|
77
88
|
moneyBar.attachToButton('#action-btn', (userContext) => {
|
|
89
|
+
// Call the specified action function
|
|
90
|
+
if (typeof window[moneyBar.config.actionFunction] === 'function') {
|
|
91
|
+
window[moneyBar.config.actionFunction](userContext);
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
// Define the function specified in actionFunction
|
|
96
|
+
function myAction(userContext) {
|
|
78
97
|
if (userContext.isPremium) {
|
|
79
98
|
alert('Premium features unlocked!');
|
|
80
99
|
} else {
|
|
81
100
|
alert(`Free trial: ${userContext.remaining} uses left`);
|
|
82
101
|
}
|
|
83
|
-
}
|
|
102
|
+
}
|
|
84
103
|
</script>
|
|
85
104
|
</body>
|
|
86
105
|
</html>
|
|
@@ -98,14 +117,30 @@ moneyBar.attachToButton('#download-btn', (userContext) => {
|
|
|
98
117
|
import { MoneyBar } from 'https://cdn.jsdelivr.net/npm/@moneybar.online/moneybar/dist/index.bundle.js';
|
|
99
118
|
|
|
100
119
|
const moneyBar = new MoneyBar({
|
|
120
|
+
actionFunction: 'myAction', // Function name that gets called
|
|
101
121
|
appId: 'my-app',
|
|
102
|
-
|
|
103
|
-
|
|
122
|
+
freeAttemptLimit: 3,
|
|
123
|
+
supabase: {
|
|
124
|
+
url: 'your-supabase-url',
|
|
125
|
+
anonKey: 'your-anon-key'
|
|
126
|
+
},
|
|
127
|
+
payment: {
|
|
128
|
+
productId: 'your-product-id'
|
|
129
|
+
}
|
|
104
130
|
});
|
|
105
131
|
|
|
106
132
|
moneyBar.attachToButton('#action-btn', (userContext) => {
|
|
107
|
-
//
|
|
133
|
+
// Call the specified action function
|
|
134
|
+
if (typeof window[moneyBar.config.actionFunction] === 'function') {
|
|
135
|
+
window[moneyBar.config.actionFunction](userContext);
|
|
136
|
+
}
|
|
108
137
|
});
|
|
138
|
+
|
|
139
|
+
// Define the function specified in actionFunction
|
|
140
|
+
function myAction(userContext) {
|
|
141
|
+
// Your business logic here
|
|
142
|
+
alert(`Action executed! Premium: ${userContext.isPremium}, Remaining: ${userContext.remaining}`);
|
|
143
|
+
}
|
|
109
144
|
</script>
|
|
110
145
|
</body>
|
|
111
146
|
</html>
|
|
@@ -134,81 +169,81 @@ moneyBar.attachToButton('#download-btn', (userContext) => {
|
|
|
134
169
|
### Quick Start Template
|
|
135
170
|
Copy `moneybar-config-template.js` and `example-template.html` from this package for a ready-to-use template.
|
|
136
171
|
|
|
172
|
+
## 🚀 Quick Start (2 minutes)
|
|
173
|
+
|
|
174
|
+
### Files You Need:
|
|
175
|
+
1. **`example-template.html`** - Working demo with all 3 use cases
|
|
176
|
+
2. **`moneybar-config-template.js`** - Comprehensive configuration with 3 problem-focused examples
|
|
177
|
+
|
|
178
|
+
### Try It Now:
|
|
179
|
+
```bash
|
|
180
|
+
# After npm install
|
|
181
|
+
open example-template.html
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
Then edit line 157 in `moneybar-config-template.js` to switch between use cases!
|
|
185
|
+
|
|
137
186
|
---
|
|
138
187
|
|
|
139
|
-
## 📱
|
|
188
|
+
## 📱 The 3 Money-Blocking Problems MoneyBar Solves
|
|
140
189
|
|
|
141
|
-
###
|
|
142
|
-
**
|
|
190
|
+
### Problem #1: "Forced Sign-In Before Value"
|
|
191
|
+
**Revenue Impact: 40-60% boost**
|
|
143
192
|
- ❌ **What Kills Revenue:** Users bounce before seeing your product works
|
|
144
193
|
- ✅ **MoneyBar Solution:** Value-first trials with 3 free attempts
|
|
145
|
-
-
|
|
194
|
+
- 🎯 **Perfect for:** PDF generators, image tools, converters
|
|
146
195
|
- 🎨 **Theme:** Emerald (trustworthy, fresh)
|
|
196
|
+
- **Config:** `VALUE_FIRST_CONFIG` in template
|
|
147
197
|
|
|
148
|
-
###
|
|
149
|
-
**
|
|
198
|
+
### Problem #2: "Silent Drop-Off"
|
|
199
|
+
**Revenue Impact: 20-30% boost**
|
|
150
200
|
- ❌ **What Kills Revenue:** Users try but don't buy - you don't know why
|
|
151
201
|
- ✅ **MoneyBar Solution:** Exit feedback capture at cancel moment
|
|
152
|
-
-
|
|
202
|
+
- 🎯 **Perfect for:** SaaS trials, B2B tools, complex products
|
|
153
203
|
- 🎨 **Theme:** Corporate (professional B2B)
|
|
204
|
+
- **Config:** `FEEDBACK_INTELLIGENCE_CONFIG` in template
|
|
154
205
|
|
|
155
|
-
###
|
|
156
|
-
**
|
|
206
|
+
### Problem #3: "Broken Money Flow"
|
|
207
|
+
**Revenue Impact: 25-40% boost**
|
|
157
208
|
- ❌ **What Kills Revenue:** Complex payments and broken sessions
|
|
158
|
-
- ✅ **MoneyBar Solution:** Seamless Google Auth + instant
|
|
159
|
-
-
|
|
209
|
+
- ✅ **MoneyBar Solution:** Seamless Google Auth + instant payments
|
|
210
|
+
- 🎯 **Perfect for:** Templates, premium features, instant upgrades
|
|
160
211
|
- 🎨 **Theme:** Dark (modern, premium)
|
|
161
|
-
-
|
|
162
|
-
|
|
163
|
-
---
|
|
164
|
-
|
|
165
|
-
## 🚨 The 3 Money-Blocking Stages for Indie Hackers
|
|
166
|
-
|
|
167
|
-
### Problem #1: "Forced Sign-In Before Value"
|
|
168
|
-
**❌ What Kills Revenue:** Users bounce before seeing your product works
|
|
169
|
-
- No evidence = no trust = no money
|
|
170
|
-
- Users don't want to create "yet another account"
|
|
171
|
-
- They leave before seeing what they would have paid for
|
|
172
|
-
|
|
173
|
-
**✅ MoneyBar Solution:** Value-first trials with limited attempts
|
|
174
|
-
- Give 2-3 free attempts - no sign-in required
|
|
175
|
-
- Users experience value immediately
|
|
176
|
-
- Only ask for payment when they're convinced
|
|
212
|
+
- **Config:** `INSTANT_PAYMENT_CONFIG` in template
|
|
177
213
|
|
|
178
|
-
|
|
179
|
-
**💰 Revenue Impact:** 40-60% more trial conversions
|
|
214
|
+
## 🔧 How to Use
|
|
180
215
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
### Problem #2: "Silent Drop-Off"
|
|
184
|
-
**❌ What Kills Revenue:** Users try your tool but don't buy - you don't know why
|
|
185
|
-
- User experiences tool → it works → but still doesn't pay
|
|
186
|
-
- You have no data on why they left
|
|
187
|
-
- You keep guessing instead of learning
|
|
188
|
-
|
|
189
|
-
**✅ MoneyBar Solution:** Exit feedback capture at cancel moment
|
|
190
|
-
- 1-click micro survey when users cancel upgrade
|
|
191
|
-
- Ask "What stopped you from buying?" with preset options
|
|
192
|
-
- Convert failure into product intelligence
|
|
216
|
+
### Step 1: Choose Your Use Case
|
|
217
|
+
Edit line 157 in `moneybar-config-template.js`:
|
|
193
218
|
|
|
194
|
-
|
|
195
|
-
|
|
219
|
+
```javascript
|
|
220
|
+
// Pick one:
|
|
221
|
+
window.APP_CONFIG = VALUE_FIRST_CONFIG; // PDF/Image/Converter tools
|
|
222
|
+
window.APP_CONFIG = FEEDBACK_INTELLIGENCE_CONFIG; // SaaS/B2B products
|
|
223
|
+
window.APP_CONFIG = INSTANT_PAYMENT_CONFIG; // Templates/Premium features
|
|
224
|
+
```
|
|
196
225
|
|
|
197
|
-
|
|
226
|
+
### Step 2: Update Credentials (REQUIRED!)
|
|
227
|
+
⚠️ **Before testing, you MUST replace these placeholder values or you'll get errors:**
|
|
198
228
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
-
|
|
202
|
-
-
|
|
203
|
-
|
|
229
|
+
```javascript
|
|
230
|
+
supabase: {
|
|
231
|
+
url: 'https://your-project.supabase.co', // ✅ Replace with your Supabase project URL
|
|
232
|
+
anonKey: 'your-anon-key' // ✅ Replace with your Supabase anon key
|
|
233
|
+
},
|
|
234
|
+
payment: [{
|
|
235
|
+
provider: 'dodo', // ✅ Payment provider
|
|
236
|
+
productId: 'your-product-id', // ✅ Replace with your DodoPayments product ID
|
|
237
|
+
mode: 'test' // ✅ Set to 'test' or 'live'
|
|
238
|
+
}]
|
|
239
|
+
```
|
|
204
240
|
|
|
205
|
-
|
|
206
|
-
-
|
|
207
|
-
-
|
|
208
|
-
- User returns directly to unlocked tool
|
|
241
|
+
**🚨 Common Error:** If you get "Cannot read properties of null (reading 'AuthClient')" error:
|
|
242
|
+
- ❌ You haven't updated the Supabase credentials above
|
|
243
|
+
- ✅ Replace ALL placeholder values with your actual credentials
|
|
209
244
|
|
|
210
|
-
|
|
211
|
-
|
|
245
|
+
### Step 3: Test the Demo
|
|
246
|
+
Open `example-template.html` in your browser and try the button!
|
|
212
247
|
|
|
213
248
|
---
|
|
214
249
|
|
|
@@ -237,22 +272,6 @@ function myAction(userContext) {
|
|
|
237
272
|
}
|
|
238
273
|
```
|
|
239
274
|
|
|
240
|
-
### Multiple Buttons
|
|
241
|
-
```javascript
|
|
242
|
-
moneyBar.attachToButtons({
|
|
243
|
-
buttons: [
|
|
244
|
-
{
|
|
245
|
-
selector: '#free-btn',
|
|
246
|
-
downloadCallback: (ctx) => downloadFreeTemplate(ctx)
|
|
247
|
-
},
|
|
248
|
-
{
|
|
249
|
-
selector: '#premium-btn',
|
|
250
|
-
downloadCallback: (ctx) => downloadPremiumTemplate(ctx),
|
|
251
|
-
isPremium: true
|
|
252
|
-
}
|
|
253
|
-
]
|
|
254
|
-
});
|
|
255
|
-
```
|
|
256
275
|
|
|
257
276
|
### Feedback Collection Configuration
|
|
258
277
|
```javascript
|
|
@@ -266,10 +285,19 @@ const moneyBar = new MoneyBar({
|
|
|
266
285
|
option2: 'Need more features',
|
|
267
286
|
option3: 'Just testing'
|
|
268
287
|
},
|
|
269
|
-
email:
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
288
|
+
email: [
|
|
289
|
+
{
|
|
290
|
+
provider: 'resend',
|
|
291
|
+
apiKey: 'your-resend-api-key',
|
|
292
|
+
fromEmail: 'feedback@yourapp.com'
|
|
293
|
+
}
|
|
294
|
+
// Future providers can be added here:
|
|
295
|
+
// {
|
|
296
|
+
// provider: 'sendgrid',
|
|
297
|
+
// apiKey: 'your-sendgrid-api-key',
|
|
298
|
+
// fromEmail: 'feedback@yourapp.com'
|
|
299
|
+
// }
|
|
300
|
+
]
|
|
273
301
|
}
|
|
274
302
|
});
|
|
275
303
|
```
|
|
@@ -306,7 +334,7 @@ const moneyBar = new MoneyBar({
|
|
|
306
334
|
const moneyBar = new MoneyBar({
|
|
307
335
|
// Core required config
|
|
308
336
|
appId: 'my-app', // Unique identifier for your app
|
|
309
|
-
|
|
337
|
+
freeAttemptLimit: 3, // Free attempts before paywall
|
|
310
338
|
|
|
311
339
|
// Supabase configuration (required)
|
|
312
340
|
supabase: {
|
|
@@ -315,10 +343,11 @@ const moneyBar = new MoneyBar({
|
|
|
315
343
|
},
|
|
316
344
|
|
|
317
345
|
// Payment configuration (required)
|
|
318
|
-
payment: {
|
|
346
|
+
payment: [{
|
|
347
|
+
provider: 'dodo', // Payment provider
|
|
319
348
|
productId: 'prod_xyz', // DodoPayments product ID
|
|
320
349
|
mode: 'test' // 'test' or 'live'
|
|
321
|
-
}
|
|
350
|
+
}]
|
|
322
351
|
});
|
|
323
352
|
```
|
|
324
353
|
|
|
@@ -388,10 +417,11 @@ function myAction(userContext) {
|
|
|
388
417
|
MoneyBar supports DodoPayments for seamless checkout:
|
|
389
418
|
|
|
390
419
|
```javascript
|
|
391
|
-
payment: {
|
|
420
|
+
payment: [{
|
|
421
|
+
provider: 'dodo',
|
|
392
422
|
productId: 'your-dodo-product-id',
|
|
393
423
|
mode: 'test' // or 'live'
|
|
394
|
-
}
|
|
424
|
+
}]
|
|
395
425
|
```
|
|
396
426
|
|
|
397
427
|
---
|
package/dist/index.browser.js
CHANGED
|
@@ -22,13 +22,13 @@ import { createClient } from '@supabase/supabase-js';
|
|
|
22
22
|
*
|
|
23
23
|
* const moneyBar = new MoneyBar({
|
|
24
24
|
* appId: 'my-pdf-app',
|
|
25
|
-
*
|
|
25
|
+
* freeAttemptLimit: 3,
|
|
26
26
|
* supabase: { url: 'https://xyz.supabase.co', anonKey: 'anon_key' },
|
|
27
27
|
* payment: { productId: 'prod_xyz' }
|
|
28
28
|
* });
|
|
29
29
|
*
|
|
30
30
|
* // Single button - handles all monetization automatically
|
|
31
|
-
* moneyBar.attachToButton('#
|
|
31
|
+
* moneyBar.attachToButton('#action-btn', (userContext) => {
|
|
32
32
|
* // Your action logic here - with user context for personalization
|
|
33
33
|
* if (userContext.isPremium) {
|
|
34
34
|
* generatePremiumPDF();
|
|
@@ -214,10 +214,10 @@ class MoneyBar {
|
|
|
214
214
|
}
|
|
215
215
|
const status = {
|
|
216
216
|
currentCount: count,
|
|
217
|
-
limit: this.config.
|
|
217
|
+
limit: this.config.freeAttemptLimit,
|
|
218
218
|
isPremium,
|
|
219
219
|
isAuthenticated: !!this.currentUser,
|
|
220
|
-
remaining: Math.max(0, this.config.
|
|
220
|
+
remaining: Math.max(0, this.config.freeAttemptLimit - count)
|
|
221
221
|
};
|
|
222
222
|
// Cache the status
|
|
223
223
|
this.cachedStatus = status;
|
|
@@ -229,10 +229,10 @@ class MoneyBar {
|
|
|
229
229
|
// Return safe defaults on error
|
|
230
230
|
const errorStatus = {
|
|
231
231
|
currentCount: 0,
|
|
232
|
-
limit: this.config.
|
|
232
|
+
limit: this.config.freeAttemptLimit,
|
|
233
233
|
isPremium: false,
|
|
234
234
|
isAuthenticated: false,
|
|
235
|
-
remaining: this.config.
|
|
235
|
+
remaining: this.config.freeAttemptLimit
|
|
236
236
|
};
|
|
237
237
|
// Cache error status for short time to avoid repeated failures
|
|
238
238
|
this.cachedStatus = errorStatus;
|
|
@@ -297,24 +297,35 @@ class MoneyBar {
|
|
|
297
297
|
if (!this.config.payment) {
|
|
298
298
|
throw new Error('Payment configuration not provided');
|
|
299
299
|
}
|
|
300
|
+
const payment = this.config.payment?.find(p => p.provider === 'dodo');
|
|
301
|
+
if (!payment) {
|
|
302
|
+
throw new Error('No dodo payment provider configured');
|
|
303
|
+
}
|
|
300
304
|
const requestBody = {
|
|
301
305
|
email: this.currentUser.email,
|
|
302
|
-
product_id:
|
|
303
|
-
mode:
|
|
306
|
+
product_id: payment.productId,
|
|
307
|
+
mode: payment.mode || 'test',
|
|
304
308
|
app_id: this.config.appId,
|
|
305
309
|
return_url: `${window.location.origin}${window.location.pathname}?payment=success`
|
|
306
310
|
};
|
|
307
|
-
console.log(`🔥 [
|
|
311
|
+
console.log(`🔥 [PAYMENT API] createPayment called for provider: ${payment.provider}, email: ${this.currentUser.email} | Time: ${new Date().toISOString()}`);
|
|
308
312
|
try {
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
'
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
313
|
+
// Route to appropriate payment API based on provider
|
|
314
|
+
let response;
|
|
315
|
+
if (payment.provider === 'dodo') {
|
|
316
|
+
response = await fetch(`${this.config.supabase.url}/functions/v1/create-payment`, {
|
|
317
|
+
method: 'POST',
|
|
318
|
+
headers: {
|
|
319
|
+
'Content-Type': 'application/json',
|
|
320
|
+
'Authorization': `Bearer ${this.config.supabase.anonKey}`
|
|
321
|
+
},
|
|
322
|
+
body: JSON.stringify(requestBody)
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
else {
|
|
326
|
+
throw new Error(`Payment provider '${payment.provider}' is not yet supported`);
|
|
327
|
+
}
|
|
328
|
+
console.log(`🔥 [PAYMENT API] createPayment response for ${payment.provider}: ${response.status}`);
|
|
318
329
|
if (this.config.options?.debug) {
|
|
319
330
|
//console.log('🔍 DEBUG: createPayment response status:', response.status);
|
|
320
331
|
}
|
|
@@ -517,8 +528,8 @@ class MoneyBar {
|
|
|
517
528
|
// Only increment count after successful execution - with limit check
|
|
518
529
|
const newCount = await this.incrementDownloadCount();
|
|
519
530
|
// Double-check: if server returned count exceeding limit, something went wrong
|
|
520
|
-
if (newCount > this.config.
|
|
521
|
-
console.error(`⚠️ Server returned count (${newCount}) exceeding limit (${this.config.
|
|
531
|
+
if (newCount > this.config.freeAttemptLimit) {
|
|
532
|
+
console.error(`⚠️ Server returned count (${newCount}) exceeding limit (${this.config.freeAttemptLimit})`);
|
|
522
533
|
// Don't update UI with invalid state
|
|
523
534
|
return;
|
|
524
535
|
}
|
|
@@ -2318,7 +2329,26 @@ class MoneyBar {
|
|
|
2318
2329
|
}
|
|
2319
2330
|
}
|
|
2320
2331
|
async sendFeedbackEmail(feedbackData) {
|
|
2321
|
-
|
|
2332
|
+
// Get email config - handle both new array format and legacy format
|
|
2333
|
+
let emailConfig;
|
|
2334
|
+
if (this.config.feedback?.email) {
|
|
2335
|
+
// New array format - use first provider (typically resend)
|
|
2336
|
+
if (Array.isArray(this.config.feedback.email)) {
|
|
2337
|
+
emailConfig = this.config.feedback.email[0];
|
|
2338
|
+
}
|
|
2339
|
+
else {
|
|
2340
|
+
// Legacy format fallback
|
|
2341
|
+
emailConfig = this.config.feedback.email;
|
|
2342
|
+
}
|
|
2343
|
+
}
|
|
2344
|
+
else if (this.config.email) {
|
|
2345
|
+
// Deprecated legacy email config
|
|
2346
|
+
emailConfig = {
|
|
2347
|
+
provider: 'resend',
|
|
2348
|
+
apiKey: this.config.email.resendApiKey,
|
|
2349
|
+
fromEmail: this.config.email.fromEmail
|
|
2350
|
+
};
|
|
2351
|
+
}
|
|
2322
2352
|
if (!emailConfig) {
|
|
2323
2353
|
throw new Error('Email configuration not available');
|
|
2324
2354
|
}
|
|
@@ -2331,19 +2361,26 @@ User Email: ${feedbackData.email || 'Not provided'}
|
|
|
2331
2361
|
Timestamp: ${feedbackData.timestamp}
|
|
2332
2362
|
User Agent: ${feedbackData.userAgent}
|
|
2333
2363
|
`.trim();
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
'
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2364
|
+
// Handle different email providers
|
|
2365
|
+
let response;
|
|
2366
|
+
if (emailConfig.provider === 'resend' || !emailConfig.provider) {
|
|
2367
|
+
response = await fetch('https://api.resend.com/emails', {
|
|
2368
|
+
method: 'POST',
|
|
2369
|
+
headers: {
|
|
2370
|
+
'Content-Type': 'application/json',
|
|
2371
|
+
'Authorization': `Bearer ${emailConfig.apiKey || emailConfig.resendApiKey}`
|
|
2372
|
+
},
|
|
2373
|
+
body: JSON.stringify({
|
|
2374
|
+
from: emailConfig.fromEmail,
|
|
2375
|
+
to: [emailConfig.fromEmail], // Send feedback to yourself
|
|
2376
|
+
subject: `Feedback from ${this.config.appId}`,
|
|
2377
|
+
text: emailBody
|
|
2378
|
+
})
|
|
2379
|
+
});
|
|
2380
|
+
}
|
|
2381
|
+
else {
|
|
2382
|
+
throw new Error(`Unsupported email provider: ${emailConfig.provider}`);
|
|
2383
|
+
}
|
|
2347
2384
|
if (!response.ok) {
|
|
2348
2385
|
throw new Error(`Email API error: ${response.status}`);
|
|
2349
2386
|
}
|
|
@@ -2765,8 +2802,8 @@ User Agent: ${feedbackData.userAgent}
|
|
|
2765
2802
|
throw new Error('supabase.url is required');
|
|
2766
2803
|
if (!this.config.supabase?.anonKey)
|
|
2767
2804
|
throw new Error('supabase.anonKey is required');
|
|
2768
|
-
if (typeof this.config.
|
|
2769
|
-
throw new Error('
|
|
2805
|
+
if (typeof this.config.freeAttemptLimit !== 'number' || this.config.freeAttemptLimit < 0) {
|
|
2806
|
+
throw new Error('freeAttemptLimit must be a non-negative number');
|
|
2770
2807
|
}
|
|
2771
2808
|
}
|
|
2772
2809
|
async initializeSupabase() {
|
|
@@ -2786,7 +2823,15 @@ User Agent: ${feedbackData.userAgent}
|
|
|
2786
2823
|
return;
|
|
2787
2824
|
}
|
|
2788
2825
|
// Validate product ID format
|
|
2789
|
-
const
|
|
2826
|
+
const payment = this.config.payment?.find(p => p.provider === 'dodo');
|
|
2827
|
+
if (!payment) {
|
|
2828
|
+
this.paymentConnectionFailed = true;
|
|
2829
|
+
if (this.config.options?.debug) {
|
|
2830
|
+
console.warn('No dodo payment provider configured');
|
|
2831
|
+
}
|
|
2832
|
+
return;
|
|
2833
|
+
}
|
|
2834
|
+
const productId = payment.productId;
|
|
2790
2835
|
if (!productId || !productId.startsWith('pdt_') || productId.length < 10) {
|
|
2791
2836
|
this.paymentConnectionFailed = true;
|
|
2792
2837
|
if (this.config.options?.debug) {
|