@appfunnel-dev/sdk 0.5.0 → 0.7.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/dist/chunk-EVUYCLVY.cjs +1326 -0
- package/dist/chunk-EVUYCLVY.cjs.map +1 -0
- package/dist/chunk-H3KHXZSI.js +1321 -0
- package/dist/chunk-H3KHXZSI.js.map +1 -0
- package/dist/chunk-P4SLDMWY.js +104 -0
- package/dist/chunk-P4SLDMWY.js.map +1 -0
- package/dist/chunk-XP44I2MU.cjs +108 -0
- package/dist/chunk-XP44I2MU.cjs.map +1 -0
- package/dist/elements/index.cjs +12172 -0
- package/dist/elements/index.cjs.map +1 -0
- package/dist/elements/index.d.cts +602 -0
- package/dist/elements/index.d.ts +602 -0
- package/dist/elements/index.js +12137 -0
- package/dist/elements/index.js.map +1 -0
- package/dist/index.cjs +590 -371
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +132 -14
- package/dist/index.d.ts +132 -14
- package/dist/index.js +555 -355
- package/dist/index.js.map +1 -1
- package/dist/internal-C7seLJBr.d.cts +516 -0
- package/dist/internal-C7seLJBr.d.ts +516 -0
- package/dist/internal.cjs +5 -1196
- package/dist/internal.cjs.map +1 -1
- package/dist/internal.d.cts +2 -204
- package/dist/internal.d.ts +2 -204
- package/dist/internal.js +1 -1199
- package/dist/internal.js.map +1 -1
- package/package.json +17 -2
- package/dist/types-ChorYUCl.d.cts +0 -255
- package/dist/types-ChorYUCl.d.ts +0 -255
package/dist/index.cjs
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
var chunkXP44I2MU_cjs = require('./chunk-XP44I2MU.cjs');
|
|
4
|
+
var chunkEVUYCLVY_cjs = require('./chunk-EVUYCLVY.cjs');
|
|
3
5
|
var react = require('react');
|
|
4
|
-
var jsxRuntime = require('react/jsx-runtime');
|
|
5
6
|
var stripeJs = require('@stripe/stripe-js');
|
|
6
7
|
var reactStripeJs = require('@stripe/react-stripe-js');
|
|
8
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
7
9
|
|
|
8
10
|
// src/config.ts
|
|
9
11
|
function defineConfig(config) {
|
|
@@ -12,23 +14,11 @@ function defineConfig(config) {
|
|
|
12
14
|
function definePage(definition) {
|
|
13
15
|
return definition;
|
|
14
16
|
}
|
|
15
|
-
function registerIntegration(id, loader) {
|
|
16
|
-
}
|
|
17
|
-
var FunnelContext = react.createContext(null);
|
|
18
|
-
function useFunnelContext() {
|
|
19
|
-
const ctx = react.useContext(FunnelContext);
|
|
20
|
-
if (!ctx) {
|
|
21
|
-
throw new Error("useFunnelContext must be used within a <FunnelProvider>");
|
|
22
|
-
}
|
|
23
|
-
return ctx;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
// src/hooks/useVariable.ts
|
|
27
17
|
function useVariable(id) {
|
|
28
|
-
const { variableStore } = useFunnelContext();
|
|
18
|
+
const { variableStore } = chunkEVUYCLVY_cjs.useFunnelContext();
|
|
29
19
|
const subscribe = react.useCallback(
|
|
30
|
-
(callback) => variableStore.subscribe(callback),
|
|
31
|
-
[variableStore]
|
|
20
|
+
(callback) => variableStore.subscribe(callback, { keys: [id] }),
|
|
21
|
+
[variableStore, id]
|
|
32
22
|
);
|
|
33
23
|
const getSnapshot = react.useCallback(
|
|
34
24
|
() => variableStore.get(id),
|
|
@@ -42,7 +32,7 @@ function useVariable(id) {
|
|
|
42
32
|
return [value, setValue];
|
|
43
33
|
}
|
|
44
34
|
function useVariables() {
|
|
45
|
-
const { variableStore } = useFunnelContext();
|
|
35
|
+
const { variableStore } = chunkEVUYCLVY_cjs.useFunnelContext();
|
|
46
36
|
const subscribe = react.useCallback(
|
|
47
37
|
(callback) => variableStore.subscribe(callback),
|
|
48
38
|
[variableStore]
|
|
@@ -53,10 +43,106 @@ function useVariables() {
|
|
|
53
43
|
);
|
|
54
44
|
return react.useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
|
|
55
45
|
}
|
|
46
|
+
|
|
47
|
+
// src/utils/date.ts
|
|
48
|
+
function toISODate(input) {
|
|
49
|
+
if (!input || !input.trim()) return "";
|
|
50
|
+
const s = input.trim();
|
|
51
|
+
if (/^\d{4}-\d{2}-\d{2}$/.test(s)) return s;
|
|
52
|
+
if (/^\d{4}-\d{2}-\d{2}T/.test(s)) return s.slice(0, 10);
|
|
53
|
+
const sepMatch = s.match(/^(\d{1,2})[/\-.](\d{1,2})[/\-.](\d{4})$/);
|
|
54
|
+
if (sepMatch) {
|
|
55
|
+
const a = parseInt(sepMatch[1], 10);
|
|
56
|
+
const b = parseInt(sepMatch[2], 10);
|
|
57
|
+
const year = sepMatch[3];
|
|
58
|
+
let month;
|
|
59
|
+
let day;
|
|
60
|
+
if (a > 12 && b <= 12) {
|
|
61
|
+
day = a;
|
|
62
|
+
month = b;
|
|
63
|
+
} else if (b > 12 && a <= 12) {
|
|
64
|
+
month = a;
|
|
65
|
+
day = b;
|
|
66
|
+
} else {
|
|
67
|
+
month = a;
|
|
68
|
+
day = b;
|
|
69
|
+
}
|
|
70
|
+
if (month >= 1 && month <= 12 && day >= 1 && day <= 31) {
|
|
71
|
+
return `${year}-${String(month).padStart(2, "0")}-${String(day).padStart(2, "0")}`;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
const ymdSlash = s.match(/^(\d{4})[/\-.](\d{1,2})[/\-.](\d{1,2})$/);
|
|
75
|
+
if (ymdSlash) {
|
|
76
|
+
const year = ymdSlash[1];
|
|
77
|
+
const month = parseInt(ymdSlash[2], 10);
|
|
78
|
+
const day = parseInt(ymdSlash[3], 10);
|
|
79
|
+
if (month >= 1 && month <= 12 && day >= 1 && day <= 31) {
|
|
80
|
+
return `${year}-${String(month).padStart(2, "0")}-${String(day).padStart(2, "0")}`;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
if (/^\d{8}$/.test(s)) {
|
|
84
|
+
const a = parseInt(s.slice(0, 2), 10);
|
|
85
|
+
const b = parseInt(s.slice(2, 4), 10);
|
|
86
|
+
const year = s.slice(4, 8);
|
|
87
|
+
let month;
|
|
88
|
+
let day;
|
|
89
|
+
if (a > 12 && b <= 12) {
|
|
90
|
+
day = a;
|
|
91
|
+
month = b;
|
|
92
|
+
} else {
|
|
93
|
+
month = a;
|
|
94
|
+
day = b;
|
|
95
|
+
}
|
|
96
|
+
if (month >= 1 && month <= 12 && day >= 1 && day <= 31) {
|
|
97
|
+
return `${year}-${String(month).padStart(2, "0")}-${String(day).padStart(2, "0")}`;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
throw new Error(
|
|
101
|
+
`[AppFunnel] Invalid date format: "${input}". Expected a date string like MM/DD/YYYY, DD/MM/YYYY, YYYY-MM-DD, or MMDDYYYY.`
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
function toISODateWithFormat(input, format) {
|
|
105
|
+
if (!input || !input.trim()) return "";
|
|
106
|
+
const s = input.trim();
|
|
107
|
+
if (/^\d{4}-\d{2}-\d{2}$/.test(s) || /^\d{4}-\d{2}-\d{2}T/.test(s)) {
|
|
108
|
+
return s.slice(0, 10);
|
|
109
|
+
}
|
|
110
|
+
const digits = s.replace(/[^\d]/g, "");
|
|
111
|
+
if (format === "YYYY-MM-DD") {
|
|
112
|
+
const m = s.match(/^(\d{4})[/\-.](\d{1,2})[/\-.](\d{1,2})$/);
|
|
113
|
+
if (m) return `${m[1]}-${m[2].padStart(2, "0")}-${m[3].padStart(2, "0")}`;
|
|
114
|
+
if (digits.length === 8) {
|
|
115
|
+
return `${digits.slice(0, 4)}-${digits.slice(4, 6)}-${digits.slice(6, 8)}`;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
if (format === "MM/DD/YYYY") {
|
|
119
|
+
const m = s.match(/^(\d{1,2})[/\-.](\d{1,2})[/\-.](\d{4})$/);
|
|
120
|
+
if (m) return `${m[3]}-${m[1].padStart(2, "0")}-${m[2].padStart(2, "0")}`;
|
|
121
|
+
if (digits.length === 8) {
|
|
122
|
+
return `${digits.slice(4, 8)}-${digits.slice(0, 2)}-${digits.slice(2, 4)}`;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
if (format === "DD/MM/YYYY") {
|
|
126
|
+
const m = s.match(/^(\d{1,2})[/\-.](\d{1,2})[/\-.](\d{4})$/);
|
|
127
|
+
if (m) return `${m[3]}-${m[2].padStart(2, "0")}-${m[1].padStart(2, "0")}`;
|
|
128
|
+
if (digits.length === 8) {
|
|
129
|
+
return `${digits.slice(4, 8)}-${digits.slice(2, 4)}-${digits.slice(0, 2)}`;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
throw new Error(
|
|
133
|
+
`[AppFunnel] Invalid date format: "${input}". Expected format ${format} (e.g. ${{
|
|
134
|
+
"MM/DD/YYYY": "03/15/1990 or 03151990",
|
|
135
|
+
"DD/MM/YYYY": "15/03/1990 or 15031990",
|
|
136
|
+
"YYYY-MM-DD": "1990-03-15 or 19900315"
|
|
137
|
+
}[format]}).`
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// src/hooks/useUser.ts
|
|
56
142
|
function useUser() {
|
|
57
|
-
const { variableStore
|
|
143
|
+
const { variableStore } = chunkEVUYCLVY_cjs.useFunnelContext();
|
|
58
144
|
const subscribe = react.useCallback(
|
|
59
|
-
(cb) => variableStore.subscribe(cb),
|
|
145
|
+
(cb) => variableStore.subscribe(cb, { prefix: "user." }),
|
|
60
146
|
[variableStore]
|
|
61
147
|
);
|
|
62
148
|
const getSnapshot = react.useCallback(
|
|
@@ -73,24 +159,23 @@ function useUser() {
|
|
|
73
159
|
dateOfBirth: variables["user.dateOfBirth"] || "",
|
|
74
160
|
setEmail(email) {
|
|
75
161
|
variableStore.set("user.email", email);
|
|
76
|
-
tracker.identify(email);
|
|
77
162
|
},
|
|
78
163
|
setName(name) {
|
|
79
164
|
variableStore.set("user.name", name);
|
|
80
165
|
},
|
|
81
166
|
setDateOfBirth(dateOfBirth) {
|
|
82
|
-
variableStore.set("user.dateOfBirth", dateOfBirth);
|
|
167
|
+
variableStore.set("user.dateOfBirth", toISODate(dateOfBirth));
|
|
83
168
|
}
|
|
84
169
|
}),
|
|
85
|
-
[variables, variableStore
|
|
170
|
+
[variables, variableStore]
|
|
86
171
|
);
|
|
87
172
|
}
|
|
88
173
|
function useUserProperty(field) {
|
|
89
|
-
const { variableStore } = useFunnelContext();
|
|
174
|
+
const { variableStore } = chunkEVUYCLVY_cjs.useFunnelContext();
|
|
90
175
|
const key = `user.${field}`;
|
|
91
176
|
const subscribe = react.useCallback(
|
|
92
|
-
(cb) => variableStore.subscribe(cb),
|
|
93
|
-
[variableStore]
|
|
177
|
+
(cb) => variableStore.subscribe(cb, { keys: [key] }),
|
|
178
|
+
[variableStore, key]
|
|
94
179
|
);
|
|
95
180
|
const getSnapshot = react.useCallback(
|
|
96
181
|
() => variableStore.get(key) || "",
|
|
@@ -103,49 +188,28 @@ function useUserProperty(field) {
|
|
|
103
188
|
);
|
|
104
189
|
return [value, setValue];
|
|
105
190
|
}
|
|
106
|
-
function
|
|
107
|
-
const { variableStore } = useFunnelContext();
|
|
108
|
-
const
|
|
191
|
+
function useDateOfBirth(format = "MM/DD/YYYY") {
|
|
192
|
+
const { variableStore } = chunkEVUYCLVY_cjs.useFunnelContext();
|
|
193
|
+
const key = "user.dateOfBirth";
|
|
109
194
|
const subscribe = react.useCallback(
|
|
110
|
-
(cb) => variableStore.subscribe(cb),
|
|
195
|
+
(cb) => variableStore.subscribe(cb, { keys: [key] }),
|
|
111
196
|
[variableStore]
|
|
112
197
|
);
|
|
113
198
|
const getSnapshot = react.useCallback(
|
|
114
|
-
() => variableStore.get(
|
|
115
|
-
[variableStore
|
|
199
|
+
() => variableStore.get(key) || "",
|
|
200
|
+
[variableStore]
|
|
116
201
|
);
|
|
117
202
|
const value = react.useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
|
|
118
203
|
const setValue = react.useCallback(
|
|
119
|
-
(v) => variableStore.set(
|
|
120
|
-
[variableStore,
|
|
204
|
+
(v) => variableStore.set(key, toISODateWithFormat(v, format)),
|
|
205
|
+
[variableStore, format]
|
|
121
206
|
);
|
|
122
207
|
return [value, setValue];
|
|
123
208
|
}
|
|
124
|
-
function useResponses() {
|
|
125
|
-
const { variableStore } = useFunnelContext();
|
|
126
|
-
const subscribe = react.useCallback(
|
|
127
|
-
(cb) => variableStore.subscribe(cb),
|
|
128
|
-
[variableStore]
|
|
129
|
-
);
|
|
130
|
-
const getSnapshot = react.useCallback(
|
|
131
|
-
() => variableStore.getState(),
|
|
132
|
-
[variableStore]
|
|
133
|
-
);
|
|
134
|
-
const variables = react.useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
|
|
135
|
-
return react.useMemo(() => {
|
|
136
|
-
const result = {};
|
|
137
|
-
for (const [key, value] of Object.entries(variables)) {
|
|
138
|
-
if (key.startsWith("answers.")) {
|
|
139
|
-
result[key.slice(8)] = value;
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
return result;
|
|
143
|
-
}, [variables]);
|
|
144
|
-
}
|
|
145
209
|
function useQueryParams() {
|
|
146
|
-
const { variableStore } = useFunnelContext();
|
|
210
|
+
const { variableStore } = chunkEVUYCLVY_cjs.useFunnelContext();
|
|
147
211
|
const subscribe = react.useCallback(
|
|
148
|
-
(cb) => variableStore.subscribe(cb),
|
|
212
|
+
(cb) => variableStore.subscribe(cb, { prefix: "query." }),
|
|
149
213
|
[variableStore]
|
|
150
214
|
);
|
|
151
215
|
const getSnapshot = react.useCallback(
|
|
@@ -164,11 +228,11 @@ function useQueryParams() {
|
|
|
164
228
|
}, [variables]);
|
|
165
229
|
}
|
|
166
230
|
function useQueryParam(key) {
|
|
167
|
-
const { variableStore } = useFunnelContext();
|
|
231
|
+
const { variableStore } = chunkEVUYCLVY_cjs.useFunnelContext();
|
|
168
232
|
const prefixedKey = `query.${key}`;
|
|
169
233
|
const subscribe = react.useCallback(
|
|
170
|
-
(cb) => variableStore.subscribe(cb),
|
|
171
|
-
[variableStore]
|
|
234
|
+
(cb) => variableStore.subscribe(cb, { keys: [prefixedKey] }),
|
|
235
|
+
[variableStore, prefixedKey]
|
|
172
236
|
);
|
|
173
237
|
const getSnapshot = react.useCallback(
|
|
174
238
|
() => variableStore.get(prefixedKey) || "",
|
|
@@ -177,11 +241,11 @@ function useQueryParam(key) {
|
|
|
177
241
|
return react.useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
|
|
178
242
|
}
|
|
179
243
|
function useData(key) {
|
|
180
|
-
const { variableStore } = useFunnelContext();
|
|
244
|
+
const { variableStore } = chunkEVUYCLVY_cjs.useFunnelContext();
|
|
181
245
|
const prefixedKey = `data.${key}`;
|
|
182
246
|
const subscribe = react.useCallback(
|
|
183
|
-
(cb) => variableStore.subscribe(cb),
|
|
184
|
-
[variableStore]
|
|
247
|
+
(cb) => variableStore.subscribe(cb, { keys: [prefixedKey] }),
|
|
248
|
+
[variableStore, prefixedKey]
|
|
185
249
|
);
|
|
186
250
|
const getSnapshot = react.useCallback(
|
|
187
251
|
() => variableStore.get(prefixedKey),
|
|
@@ -230,7 +294,7 @@ function detect24Hour(locale) {
|
|
|
230
294
|
}
|
|
231
295
|
}
|
|
232
296
|
function useTranslation() {
|
|
233
|
-
const { i18n } = useFunnelContext();
|
|
297
|
+
const { i18n } = chunkEVUYCLVY_cjs.useFunnelContext();
|
|
234
298
|
const subscribe = react.useCallback(
|
|
235
299
|
(cb) => i18n.subscribe(cb),
|
|
236
300
|
[i18n]
|
|
@@ -252,99 +316,10 @@ function useTranslation() {
|
|
|
252
316
|
const availableLocales = i18n.getAvailableLocales();
|
|
253
317
|
return { t, locale, setLocale, availableLocales };
|
|
254
318
|
}
|
|
255
|
-
function useNavigation() {
|
|
256
|
-
const { router, variableStore, tracker } = useFunnelContext();
|
|
257
|
-
const subscribe = react.useCallback(
|
|
258
|
-
(cb) => variableStore.subscribe(cb),
|
|
259
|
-
[variableStore]
|
|
260
|
-
);
|
|
261
|
-
const getSnapshot = react.useCallback(
|
|
262
|
-
() => variableStore.getState(),
|
|
263
|
-
[variableStore]
|
|
264
|
-
);
|
|
265
|
-
const variables = react.useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
|
|
266
|
-
const goToNextPage = react.useCallback(() => {
|
|
267
|
-
const previousPage = router.getCurrentPage();
|
|
268
|
-
if (previousPage) {
|
|
269
|
-
tracker.stopPageTracking();
|
|
270
|
-
}
|
|
271
|
-
const nextKey = router.goToNextPage(variables);
|
|
272
|
-
if (nextKey) {
|
|
273
|
-
const nextPage = router.getCurrentPage();
|
|
274
|
-
if (nextPage) {
|
|
275
|
-
tracker.track("page.view", {
|
|
276
|
-
pageId: nextPage.key,
|
|
277
|
-
pageKey: nextPage.key,
|
|
278
|
-
pageName: nextPage.name
|
|
279
|
-
});
|
|
280
|
-
tracker.startPageTracking(nextPage.key);
|
|
281
|
-
}
|
|
282
|
-
variableStore.setMany({
|
|
283
|
-
"page.currentId": nextKey,
|
|
284
|
-
"page.currentIndex": router.getPageHistory().length,
|
|
285
|
-
"page.current": router.getPageHistory().length + 1,
|
|
286
|
-
"page.startedAt": Date.now(),
|
|
287
|
-
"page.timeOnCurrent": 0
|
|
288
|
-
});
|
|
289
|
-
}
|
|
290
|
-
}, [router, variables, tracker, variableStore]);
|
|
291
|
-
const goBack = react.useCallback(() => {
|
|
292
|
-
tracker.stopPageTracking();
|
|
293
|
-
const prevKey = router.goBack();
|
|
294
|
-
if (prevKey) {
|
|
295
|
-
const page = router.getCurrentPage();
|
|
296
|
-
if (page) {
|
|
297
|
-
tracker.track("page.view", {
|
|
298
|
-
pageId: page.key,
|
|
299
|
-
pageKey: page.key,
|
|
300
|
-
pageName: page.name
|
|
301
|
-
});
|
|
302
|
-
tracker.startPageTracking(page.key);
|
|
303
|
-
}
|
|
304
|
-
variableStore.setMany({
|
|
305
|
-
"page.currentId": prevKey,
|
|
306
|
-
"page.currentIndex": router.getPageHistory().length,
|
|
307
|
-
"page.current": router.getPageHistory().length + 1,
|
|
308
|
-
"page.startedAt": Date.now(),
|
|
309
|
-
"page.timeOnCurrent": 0
|
|
310
|
-
});
|
|
311
|
-
}
|
|
312
|
-
}, [router, tracker, variableStore]);
|
|
313
|
-
const goToPage = react.useCallback((pageKey) => {
|
|
314
|
-
tracker.stopPageTracking();
|
|
315
|
-
const key = router.goToPage(pageKey);
|
|
316
|
-
if (key) {
|
|
317
|
-
const page = router.getCurrentPage();
|
|
318
|
-
if (page) {
|
|
319
|
-
tracker.track("page.view", {
|
|
320
|
-
pageId: page.key,
|
|
321
|
-
pageKey: page.key,
|
|
322
|
-
pageName: page.name
|
|
323
|
-
});
|
|
324
|
-
tracker.startPageTracking(page.key);
|
|
325
|
-
}
|
|
326
|
-
variableStore.setMany({
|
|
327
|
-
"page.currentId": key,
|
|
328
|
-
"page.currentIndex": router.getPageHistory().length,
|
|
329
|
-
"page.current": router.getPageHistory().length + 1,
|
|
330
|
-
"page.startedAt": Date.now(),
|
|
331
|
-
"page.timeOnCurrent": 0
|
|
332
|
-
});
|
|
333
|
-
}
|
|
334
|
-
}, [router, tracker, variableStore]);
|
|
335
|
-
return {
|
|
336
|
-
goToNextPage,
|
|
337
|
-
goBack,
|
|
338
|
-
goToPage,
|
|
339
|
-
currentPage: router.getCurrentPage(),
|
|
340
|
-
pageHistory: router.getPageHistory(),
|
|
341
|
-
progress: router.getProgress()
|
|
342
|
-
};
|
|
343
|
-
}
|
|
344
319
|
function useProducts() {
|
|
345
|
-
const { products, variableStore, selectProduct: ctxSelect } = useFunnelContext();
|
|
320
|
+
const { products, variableStore, selectProduct: ctxSelect } = chunkEVUYCLVY_cjs.useFunnelContext();
|
|
346
321
|
const subscribe = react.useCallback(
|
|
347
|
-
(cb) => variableStore.subscribe(cb),
|
|
322
|
+
(cb) => variableStore.subscribe(cb, { keys: ["products.selectedProductId"] }),
|
|
348
323
|
[variableStore]
|
|
349
324
|
);
|
|
350
325
|
const getSnapshot = react.useCallback(
|
|
@@ -359,7 +334,7 @@ function useProducts() {
|
|
|
359
334
|
return { products, selected, select };
|
|
360
335
|
}
|
|
361
336
|
function useTracking() {
|
|
362
|
-
const { tracker } = useFunnelContext();
|
|
337
|
+
const { tracker } = chunkEVUYCLVY_cjs.useFunnelContext();
|
|
363
338
|
const track = react.useCallback(
|
|
364
339
|
(eventName, data) => {
|
|
365
340
|
tracker.track(eventName, data);
|
|
@@ -374,10 +349,19 @@ function useTracking() {
|
|
|
374
349
|
);
|
|
375
350
|
return { track, identify };
|
|
376
351
|
}
|
|
352
|
+
var PAYMENT_KEYS = [
|
|
353
|
+
"card.last4",
|
|
354
|
+
"card.brand",
|
|
355
|
+
"card.expMonth",
|
|
356
|
+
"card.expYear",
|
|
357
|
+
"payment.loading",
|
|
358
|
+
"payment.error",
|
|
359
|
+
"payment.customerId"
|
|
360
|
+
];
|
|
377
361
|
function usePayment() {
|
|
378
|
-
const { variableStore
|
|
362
|
+
const { variableStore } = chunkEVUYCLVY_cjs.useFunnelContext();
|
|
379
363
|
const subscribe = react.useCallback(
|
|
380
|
-
(cb) => variableStore.subscribe(cb),
|
|
364
|
+
(cb) => variableStore.subscribe(cb, { keys: PAYMENT_KEYS }),
|
|
381
365
|
[variableStore]
|
|
382
366
|
);
|
|
383
367
|
const getSnapshot = react.useCallback(
|
|
@@ -390,269 +374,483 @@ function usePayment() {
|
|
|
390
374
|
const brand = variables["card.brand"] || "";
|
|
391
375
|
const expMonth = variables["card.expMonth"] || 0;
|
|
392
376
|
const expYear = variables["card.expYear"] || 0;
|
|
377
|
+
const customerId = variables["payment.customerId"] || null;
|
|
393
378
|
return {
|
|
394
|
-
customerId
|
|
379
|
+
customerId,
|
|
395
380
|
isAuthorized: !!last4,
|
|
396
381
|
loading: !!variables["payment.loading"],
|
|
397
382
|
error: variables["payment.error"] || null,
|
|
398
383
|
cardDetails: last4 ? { last4, brand, expMonth, expYear } : null
|
|
399
384
|
};
|
|
400
|
-
}, [variables
|
|
385
|
+
}, [variables]);
|
|
386
|
+
}
|
|
387
|
+
var DEVICE_KEYS = [
|
|
388
|
+
"os.name",
|
|
389
|
+
"os.timezone",
|
|
390
|
+
"device.type",
|
|
391
|
+
"device.isMobile",
|
|
392
|
+
"device.isTablet",
|
|
393
|
+
"device.screenWidth",
|
|
394
|
+
"device.screenHeight",
|
|
395
|
+
"device.viewportWidth",
|
|
396
|
+
"device.viewportHeight",
|
|
397
|
+
"device.colorDepth",
|
|
398
|
+
"device.pixelRatio",
|
|
399
|
+
"browser.name",
|
|
400
|
+
"browser.version",
|
|
401
|
+
"browser.userAgent",
|
|
402
|
+
"browser.cookieEnabled",
|
|
403
|
+
"browser.online",
|
|
404
|
+
"browser.language"
|
|
405
|
+
];
|
|
406
|
+
function useDeviceInfo() {
|
|
407
|
+
const { variableStore } = chunkEVUYCLVY_cjs.useFunnelContext();
|
|
408
|
+
const subscribe = react.useCallback(
|
|
409
|
+
(cb) => variableStore.subscribe(cb, { keys: DEVICE_KEYS }),
|
|
410
|
+
[variableStore]
|
|
411
|
+
);
|
|
412
|
+
const getSnapshot = react.useCallback(
|
|
413
|
+
() => variableStore.getState(),
|
|
414
|
+
[variableStore]
|
|
415
|
+
);
|
|
416
|
+
const variables = react.useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
|
|
417
|
+
return react.useMemo(() => ({
|
|
418
|
+
os: {
|
|
419
|
+
name: variables["os.name"] || "",
|
|
420
|
+
timezone: variables["os.timezone"] || ""
|
|
421
|
+
},
|
|
422
|
+
device: {
|
|
423
|
+
type: variables["device.type"] || "desktop",
|
|
424
|
+
isMobile: !!variables["device.isMobile"],
|
|
425
|
+
isTablet: !!variables["device.isTablet"],
|
|
426
|
+
screenWidth: variables["device.screenWidth"] || 0,
|
|
427
|
+
screenHeight: variables["device.screenHeight"] || 0,
|
|
428
|
+
viewportWidth: variables["device.viewportWidth"] || 0,
|
|
429
|
+
viewportHeight: variables["device.viewportHeight"] || 0,
|
|
430
|
+
colorDepth: variables["device.colorDepth"] || 24,
|
|
431
|
+
pixelRatio: variables["device.pixelRatio"] || 1
|
|
432
|
+
},
|
|
433
|
+
browser: {
|
|
434
|
+
name: variables["browser.name"] || "",
|
|
435
|
+
version: variables["browser.version"] || "",
|
|
436
|
+
userAgent: variables["browser.userAgent"] || "",
|
|
437
|
+
cookieEnabled: variables["browser.cookieEnabled"] !== false,
|
|
438
|
+
online: variables["browser.online"] !== false,
|
|
439
|
+
language: variables["browser.language"] || "en"
|
|
440
|
+
}
|
|
441
|
+
}), [variables]);
|
|
442
|
+
}
|
|
443
|
+
function useSafeArea() {
|
|
444
|
+
const [insets, setInsets] = react.useState({ top: 0, right: 0, bottom: 0, left: 0 });
|
|
445
|
+
react.useEffect(() => {
|
|
446
|
+
if (typeof window === "undefined") return;
|
|
447
|
+
const el = document.createElement("div");
|
|
448
|
+
el.style.cssText = [
|
|
449
|
+
"position:fixed",
|
|
450
|
+
"top:env(safe-area-inset-top,0px)",
|
|
451
|
+
"right:env(safe-area-inset-right,0px)",
|
|
452
|
+
"bottom:env(safe-area-inset-bottom,0px)",
|
|
453
|
+
"left:env(safe-area-inset-left,0px)",
|
|
454
|
+
"pointer-events:none",
|
|
455
|
+
"visibility:hidden",
|
|
456
|
+
"z-index:-1"
|
|
457
|
+
].join(";");
|
|
458
|
+
document.body.appendChild(el);
|
|
459
|
+
function read() {
|
|
460
|
+
const style = getComputedStyle(el);
|
|
461
|
+
setInsets({
|
|
462
|
+
top: parseFloat(style.top) || 0,
|
|
463
|
+
right: parseFloat(style.right) || 0,
|
|
464
|
+
bottom: parseFloat(style.bottom) || 0,
|
|
465
|
+
left: parseFloat(style.left) || 0
|
|
466
|
+
});
|
|
467
|
+
}
|
|
468
|
+
read();
|
|
469
|
+
const observer = new ResizeObserver(read);
|
|
470
|
+
observer.observe(el);
|
|
471
|
+
window.addEventListener("resize", read);
|
|
472
|
+
return () => {
|
|
473
|
+
observer.disconnect();
|
|
474
|
+
window.removeEventListener("resize", read);
|
|
475
|
+
el.remove();
|
|
476
|
+
};
|
|
477
|
+
}, []);
|
|
478
|
+
return insets;
|
|
479
|
+
}
|
|
480
|
+
function useKeyboard() {
|
|
481
|
+
const [state, setState] = react.useState({ isOpen: false, height: 0 });
|
|
482
|
+
const timeoutRef = react.useRef();
|
|
483
|
+
react.useEffect(() => {
|
|
484
|
+
if (typeof window === "undefined") return;
|
|
485
|
+
if ("virtualKeyboard" in navigator) {
|
|
486
|
+
const vk = navigator.virtualKeyboard;
|
|
487
|
+
vk.overlaysContent = true;
|
|
488
|
+
const handler = () => {
|
|
489
|
+
const h = vk.boundingRect.height;
|
|
490
|
+
setState((prev) => {
|
|
491
|
+
if (prev.height === h && prev.isOpen === h > 0) return prev;
|
|
492
|
+
return { isOpen: h > 0, height: h };
|
|
493
|
+
});
|
|
494
|
+
};
|
|
495
|
+
vk.addEventListener("geometrychange", handler);
|
|
496
|
+
handler();
|
|
497
|
+
return () => vk.removeEventListener("geometrychange", handler);
|
|
498
|
+
}
|
|
499
|
+
const vv = window.visualViewport;
|
|
500
|
+
if (!vv) return;
|
|
501
|
+
let layoutHeight = window.innerHeight;
|
|
502
|
+
function compute() {
|
|
503
|
+
const kbHeight = Math.max(0, layoutHeight - vv.height);
|
|
504
|
+
const h = kbHeight > 40 ? Math.round(kbHeight) : 0;
|
|
505
|
+
setState((prev) => {
|
|
506
|
+
if (prev.height === h && prev.isOpen === h > 0) return prev;
|
|
507
|
+
return { isOpen: h > 0, height: h };
|
|
508
|
+
});
|
|
509
|
+
}
|
|
510
|
+
function onViewportResize() {
|
|
511
|
+
compute();
|
|
512
|
+
clearTimeout(timeoutRef.current);
|
|
513
|
+
timeoutRef.current = setTimeout(compute, 1e3);
|
|
514
|
+
}
|
|
515
|
+
function onWindowResize() {
|
|
516
|
+
layoutHeight = window.innerHeight;
|
|
517
|
+
compute();
|
|
518
|
+
}
|
|
519
|
+
vv.addEventListener("resize", onViewportResize);
|
|
520
|
+
window.addEventListener("resize", onWindowResize);
|
|
521
|
+
window.addEventListener("orientationchange", onWindowResize);
|
|
522
|
+
compute();
|
|
523
|
+
return () => {
|
|
524
|
+
clearTimeout(timeoutRef.current);
|
|
525
|
+
vv.removeEventListener("resize", onViewportResize);
|
|
526
|
+
window.removeEventListener("resize", onWindowResize);
|
|
527
|
+
window.removeEventListener("orientationchange", onWindowResize);
|
|
528
|
+
};
|
|
529
|
+
}, []);
|
|
530
|
+
return state;
|
|
531
|
+
}
|
|
532
|
+
var PAGE_KEYS = [
|
|
533
|
+
"page.currentId",
|
|
534
|
+
"page.currentIndex",
|
|
535
|
+
"page.current",
|
|
536
|
+
"page.total",
|
|
537
|
+
"page.progressPercentage",
|
|
538
|
+
"page.startedAt"
|
|
539
|
+
];
|
|
540
|
+
function usePageData() {
|
|
541
|
+
const { variableStore } = chunkEVUYCLVY_cjs.useFunnelContext();
|
|
542
|
+
const subscribe = react.useCallback(
|
|
543
|
+
(cb) => variableStore.subscribe(cb, { keys: PAGE_KEYS }),
|
|
544
|
+
[variableStore]
|
|
545
|
+
);
|
|
546
|
+
const getSnapshot = react.useCallback(
|
|
547
|
+
() => variableStore.getState(),
|
|
548
|
+
[variableStore]
|
|
549
|
+
);
|
|
550
|
+
const variables = react.useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
|
|
551
|
+
return react.useMemo(() => ({
|
|
552
|
+
currentId: variables["page.currentId"] || "",
|
|
553
|
+
currentIndex: variables["page.currentIndex"] || 0,
|
|
554
|
+
current: variables["page.current"] || 1,
|
|
555
|
+
total: variables["page.total"] || 0,
|
|
556
|
+
progressPercentage: variables["page.progressPercentage"] || 0,
|
|
557
|
+
startedAt: variables["page.startedAt"] || 0
|
|
558
|
+
}), [variables]);
|
|
401
559
|
}
|
|
402
560
|
|
|
403
561
|
// src/hooks/useFunnel.ts
|
|
404
562
|
function useFunnel() {
|
|
405
|
-
const { funnelId, campaignId, tracker } = useFunnelContext();
|
|
563
|
+
const { funnelId, campaignId, tracker } = chunkEVUYCLVY_cjs.useFunnelContext();
|
|
406
564
|
return {
|
|
407
565
|
funnelId,
|
|
408
566
|
campaignId,
|
|
409
567
|
sessionId: tracker.getSessionId(),
|
|
410
568
|
variables: useVariables(),
|
|
411
569
|
user: useUser(),
|
|
412
|
-
responses: useResponses(),
|
|
570
|
+
responses: chunkXP44I2MU_cjs.useResponses(),
|
|
413
571
|
queryParams: useQueryParams(),
|
|
414
|
-
navigation: useNavigation(),
|
|
572
|
+
navigation: chunkXP44I2MU_cjs.useNavigation(),
|
|
415
573
|
products: useProducts(),
|
|
416
574
|
tracking: useTracking(),
|
|
417
575
|
payment: usePayment()
|
|
418
576
|
};
|
|
419
577
|
}
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
validateOnly,
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
setError(null);
|
|
438
|
-
variableStore.set("payment.loading", true);
|
|
439
|
-
try {
|
|
440
|
-
const confirmFn = paymentMode === "setup" ? stripe.confirmSetup : stripe.confirmPayment;
|
|
441
|
-
const confirmResult = await confirmFn({
|
|
442
|
-
elements,
|
|
443
|
-
redirect: "if_required",
|
|
444
|
-
confirmParams: { return_url: window.location.href }
|
|
445
|
-
});
|
|
446
|
-
if (confirmResult.error) {
|
|
447
|
-
const msg = confirmResult.error.message || "Payment failed";
|
|
578
|
+
var API_BASE_URL = "https://api.appfunnel.net";
|
|
579
|
+
var InnerPaymentForm = react.forwardRef(
|
|
580
|
+
function InnerPaymentForm2({ paymentMode, validateOnly, onSuccess, onError, onReady, layout }, ref) {
|
|
581
|
+
const stripe = reactStripeJs.useStripe();
|
|
582
|
+
const elements = reactStripeJs.useElements();
|
|
583
|
+
const [error, setError] = react.useState(null);
|
|
584
|
+
const { variableStore, campaignId, tracker, products } = chunkEVUYCLVY_cjs.useFunnelContext();
|
|
585
|
+
const readyFired = react.useRef(false);
|
|
586
|
+
react.useEffect(() => {
|
|
587
|
+
if (stripe && elements && !readyFired.current) {
|
|
588
|
+
readyFired.current = true;
|
|
589
|
+
onReady?.();
|
|
590
|
+
}
|
|
591
|
+
}, [stripe, elements, onReady]);
|
|
592
|
+
const handleSubmit = react.useCallback(async () => {
|
|
593
|
+
if (!stripe || !elements) {
|
|
594
|
+
const msg = "Stripe not loaded";
|
|
448
595
|
setError(msg);
|
|
449
|
-
variableStore.set("payment.error", msg);
|
|
450
596
|
onError?.(msg);
|
|
451
597
|
return;
|
|
452
598
|
}
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
599
|
+
setError(null);
|
|
600
|
+
variableStore.set("payment.loading", true);
|
|
601
|
+
try {
|
|
602
|
+
const confirmFn = paymentMode === "setup" ? stripe.confirmSetup : stripe.confirmPayment;
|
|
603
|
+
const confirmResult = await confirmFn({
|
|
604
|
+
elements,
|
|
605
|
+
redirect: "if_required",
|
|
606
|
+
confirmParams: { return_url: window.location.href }
|
|
607
|
+
});
|
|
608
|
+
if (confirmResult.error) {
|
|
609
|
+
const msg = confirmResult.error.message || "Payment failed";
|
|
458
610
|
setError(msg);
|
|
459
611
|
variableStore.set("payment.error", msg);
|
|
460
612
|
onError?.(msg);
|
|
461
613
|
return;
|
|
462
614
|
}
|
|
463
|
-
|
|
464
|
-
|
|
615
|
+
tracker.track("checkout.payment_added");
|
|
616
|
+
if (validateOnly) {
|
|
617
|
+
const piId = "paymentIntent" in confirmResult ? confirmResult.paymentIntent : void 0;
|
|
618
|
+
if (!piId?.id) {
|
|
619
|
+
const msg = "PaymentIntent not found after confirmation";
|
|
620
|
+
setError(msg);
|
|
621
|
+
variableStore.set("payment.error", msg);
|
|
622
|
+
onError?.(msg);
|
|
623
|
+
return;
|
|
624
|
+
}
|
|
625
|
+
const response2 = await fetch(
|
|
626
|
+
`${API_BASE_URL}/campaign/${campaignId}/stripe/validate-card`,
|
|
627
|
+
{
|
|
628
|
+
method: "POST",
|
|
629
|
+
headers: { "Content-Type": "application/json" },
|
|
630
|
+
body: JSON.stringify({
|
|
631
|
+
campaignId,
|
|
632
|
+
sessionId: tracker.getSessionId(),
|
|
633
|
+
paymentIntentId: piId.id
|
|
634
|
+
})
|
|
635
|
+
}
|
|
636
|
+
);
|
|
637
|
+
const result2 = await response2.json();
|
|
638
|
+
if (!result2.success) {
|
|
639
|
+
const msg = result2.error || "Card validation failed";
|
|
640
|
+
setError(msg);
|
|
641
|
+
variableStore.set("payment.error", msg);
|
|
642
|
+
onError?.(msg);
|
|
643
|
+
return;
|
|
644
|
+
}
|
|
645
|
+
variableStore.setMany({
|
|
646
|
+
"card.last4": result2.card.last4,
|
|
647
|
+
"card.brand": result2.card.brand,
|
|
648
|
+
"card.expMonth": result2.card.expMonth,
|
|
649
|
+
"card.expYear": result2.card.expYear,
|
|
650
|
+
"card.funding": result2.card.funding,
|
|
651
|
+
"payment.error": ""
|
|
652
|
+
});
|
|
653
|
+
onSuccess?.();
|
|
654
|
+
return;
|
|
655
|
+
}
|
|
656
|
+
const paymentIntentId = "paymentIntent" in confirmResult ? confirmResult.paymentIntent?.id : void 0;
|
|
657
|
+
const selectedId = variableStore.get("products.selectedProductId");
|
|
658
|
+
const product = products.find((p) => p.id === selectedId);
|
|
659
|
+
if (!product?.stripePriceId) {
|
|
660
|
+
const msg = "No product selected or missing Stripe price";
|
|
661
|
+
setError(msg);
|
|
662
|
+
variableStore.set("payment.error", msg);
|
|
663
|
+
onError?.(msg);
|
|
664
|
+
return;
|
|
665
|
+
}
|
|
666
|
+
const response = await fetch(
|
|
667
|
+
`${API_BASE_URL}/campaign/${campaignId}/stripe/purchase`,
|
|
465
668
|
{
|
|
466
669
|
method: "POST",
|
|
467
670
|
headers: { "Content-Type": "application/json" },
|
|
468
671
|
body: JSON.stringify({
|
|
469
672
|
campaignId,
|
|
470
673
|
sessionId: tracker.getSessionId(),
|
|
471
|
-
|
|
674
|
+
stripePriceId: product.stripePriceId,
|
|
675
|
+
trialPeriodDays: product.hasTrial ? product.trialDays : void 0,
|
|
676
|
+
onSessionPiId: paymentMode === "payment" ? paymentIntentId : void 0
|
|
472
677
|
})
|
|
473
678
|
}
|
|
474
679
|
);
|
|
475
|
-
const
|
|
476
|
-
if (
|
|
477
|
-
|
|
680
|
+
const result = await response.json();
|
|
681
|
+
if (result.success) {
|
|
682
|
+
variableStore.set("payment.error", "");
|
|
683
|
+
if (result.eventId || result.eventIds?.firstPeriod) {
|
|
684
|
+
tracker.track("purchase.complete", {
|
|
685
|
+
amount: product.rawPrice ? product.rawPrice / 100 : 0,
|
|
686
|
+
currency: product.currencyCode || "USD"
|
|
687
|
+
});
|
|
688
|
+
}
|
|
689
|
+
onSuccess?.();
|
|
690
|
+
} else {
|
|
691
|
+
const msg = result.error || "Failed to process payment";
|
|
478
692
|
setError(msg);
|
|
479
693
|
variableStore.set("payment.error", msg);
|
|
480
694
|
onError?.(msg);
|
|
481
|
-
return;
|
|
482
695
|
}
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
"card.brand": result2.card.brand,
|
|
486
|
-
"card.expMonth": result2.card.expMonth,
|
|
487
|
-
"card.expYear": result2.card.expYear,
|
|
488
|
-
"card.funding": result2.card.funding,
|
|
489
|
-
"payment.error": ""
|
|
490
|
-
});
|
|
491
|
-
onSuccess?.();
|
|
492
|
-
return;
|
|
493
|
-
}
|
|
494
|
-
const paymentIntentId = "paymentIntent" in confirmResult ? confirmResult.paymentIntent?.id : void 0;
|
|
495
|
-
const selectedId = variableStore.get("products.selectedProductId");
|
|
496
|
-
const product = products.find((p) => p.id === selectedId);
|
|
497
|
-
if (!product?.stripePriceId) {
|
|
498
|
-
const msg = "No product selected or missing Stripe price";
|
|
696
|
+
} catch (err) {
|
|
697
|
+
const msg = err instanceof Error ? err.message : "An error occurred";
|
|
499
698
|
setError(msg);
|
|
500
699
|
variableStore.set("payment.error", msg);
|
|
501
700
|
onError?.(msg);
|
|
502
|
-
|
|
701
|
+
} finally {
|
|
702
|
+
variableStore.set("payment.loading", false);
|
|
503
703
|
}
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
704
|
+
}, [stripe, elements, paymentMode, validateOnly, variableStore, campaignId, tracker, products, onSuccess, onError]);
|
|
705
|
+
react.useImperativeHandle(ref, () => ({ submit: handleSubmit }), [handleSubmit]);
|
|
706
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
707
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactStripeJs.PaymentElement, { options: { layout: layout || "tabs" } }),
|
|
708
|
+
error && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { color: "#ef4444", fontSize: "14px", marginTop: "12px" }, children: error })
|
|
709
|
+
] });
|
|
710
|
+
}
|
|
711
|
+
);
|
|
712
|
+
var StripePaymentForm = react.forwardRef(
|
|
713
|
+
function StripePaymentForm2({
|
|
714
|
+
productId,
|
|
715
|
+
mode = "checkout",
|
|
716
|
+
variant = "elements",
|
|
717
|
+
onSuccess,
|
|
718
|
+
onError,
|
|
719
|
+
onReady,
|
|
720
|
+
className,
|
|
721
|
+
appearance,
|
|
722
|
+
layout
|
|
723
|
+
}, ref) {
|
|
724
|
+
const { campaignId, tracker, variableStore, products } = chunkEVUYCLVY_cjs.useFunnelContext();
|
|
725
|
+
const [email] = useVariable("user.email");
|
|
726
|
+
const validateOnly = mode === "validate-only";
|
|
727
|
+
const product = react.useMemo(() => {
|
|
728
|
+
if (productId) return products.find((p) => p.id === productId) || null;
|
|
729
|
+
const selectedId = variableStore.get("products.selectedProductId");
|
|
730
|
+
return products.find((p) => p.id === selectedId) || null;
|
|
731
|
+
}, [productId, products, variableStore]);
|
|
732
|
+
const paymentMode = react.useMemo(() => {
|
|
733
|
+
if (validateOnly) return "payment";
|
|
734
|
+
if (product?.hasTrial && !product.paidTrial) return "setup";
|
|
735
|
+
return "payment";
|
|
736
|
+
}, [product, validateOnly]);
|
|
737
|
+
const [stripePromise, setStripePromise] = react.useState(null);
|
|
738
|
+
const [clientSecret, setClientSecret] = react.useState(null);
|
|
739
|
+
const [error, setError] = react.useState(null);
|
|
740
|
+
const [isLoading, setIsLoading] = react.useState(false);
|
|
741
|
+
const hasInitialized = react.useRef(false);
|
|
742
|
+
react.useEffect(() => {
|
|
743
|
+
if (!email || !campaignId || hasInitialized.current) return;
|
|
744
|
+
if (!product?.storePriceId) return;
|
|
745
|
+
hasInitialized.current = true;
|
|
746
|
+
setIsLoading(true);
|
|
747
|
+
variableStore.set("payment.loading", true);
|
|
748
|
+
const createIntent = async () => {
|
|
749
|
+
try {
|
|
750
|
+
if (variant === "embedded") {
|
|
751
|
+
const response = await fetch(
|
|
752
|
+
`${API_BASE_URL}/campaign/${campaignId}/stripe/checkout-session`,
|
|
753
|
+
{
|
|
754
|
+
method: "POST",
|
|
755
|
+
headers: { "Content-Type": "application/json" },
|
|
756
|
+
body: JSON.stringify({
|
|
757
|
+
campaignId,
|
|
758
|
+
sessionId: tracker.getSessionId(),
|
|
759
|
+
customerEmail: email,
|
|
760
|
+
priceId: product.storePriceId,
|
|
761
|
+
trialPeriodDays: product.hasTrial ? product.trialDays : void 0
|
|
762
|
+
})
|
|
763
|
+
}
|
|
764
|
+
);
|
|
765
|
+
const result = await response.json();
|
|
766
|
+
if (!result.success) throw new Error(result.error || "Failed to create checkout session");
|
|
767
|
+
setStripePromise(stripeJs.loadStripe(result.publishableKey));
|
|
768
|
+
setClientSecret(result.clientSecret);
|
|
769
|
+
} else {
|
|
770
|
+
const endpoint = paymentMode === "setup" ? `/campaign/${campaignId}/stripe/setup-intent` : `/campaign/${campaignId}/stripe/payment-intent`;
|
|
771
|
+
const body = {
|
|
772
|
+
campaignId,
|
|
773
|
+
sessionId: tracker.getSessionId(),
|
|
774
|
+
customerEmail: email,
|
|
775
|
+
priceId: product.storePriceId
|
|
776
|
+
};
|
|
777
|
+
if (validateOnly) body.validateOnly = true;
|
|
778
|
+
const response = await fetch(`${API_BASE_URL}${endpoint}`, {
|
|
779
|
+
method: "POST",
|
|
780
|
+
headers: { "Content-Type": "application/json" },
|
|
781
|
+
body: JSON.stringify(body)
|
|
782
|
+
});
|
|
783
|
+
const result = await response.json();
|
|
784
|
+
if (!result.success) throw new Error(result.error || "Failed to create intent");
|
|
785
|
+
setStripePromise(stripeJs.loadStripe(result.publishableKey));
|
|
786
|
+
setClientSecret(result.clientSecret);
|
|
787
|
+
variableStore.set("user.stripeCustomerId", result.customerId);
|
|
788
|
+
}
|
|
789
|
+
tracker.track("checkout.start", {
|
|
790
|
+
productId: product.id,
|
|
791
|
+
priceId: product.stripePriceId || product.storePriceId,
|
|
792
|
+
productName: product.displayName
|
|
527
793
|
});
|
|
794
|
+
} catch (err) {
|
|
795
|
+
const msg = err instanceof Error ? err.message : "Failed to initialize payment";
|
|
796
|
+
setError(msg);
|
|
797
|
+
variableStore.set("payment.error", msg);
|
|
798
|
+
} finally {
|
|
799
|
+
setIsLoading(false);
|
|
800
|
+
variableStore.set("payment.loading", false);
|
|
528
801
|
}
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
onError?.(msg);
|
|
535
|
-
}
|
|
536
|
-
} catch (err) {
|
|
537
|
-
const msg = err instanceof Error ? err.message : "An error occurred";
|
|
538
|
-
setError(msg);
|
|
539
|
-
variableStore.set("payment.error", msg);
|
|
540
|
-
onError?.(msg);
|
|
541
|
-
} finally {
|
|
542
|
-
variableStore.set("payment.loading", false);
|
|
802
|
+
};
|
|
803
|
+
createIntent();
|
|
804
|
+
}, [email, campaignId, product, paymentMode, validateOnly, variant, tracker, variableStore]);
|
|
805
|
+
if (isLoading) {
|
|
806
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className, style: { padding: "20px", textAlign: "center", color: "#6b7280" }, children: "Loading payment form..." });
|
|
543
807
|
}
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
if (typeof window !== "undefined") {
|
|
547
|
-
window.__paymentElementSubmit = handleSubmit;
|
|
808
|
+
if (!email) {
|
|
809
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className, style: { padding: "20px", textAlign: "center", color: "#ef4444", fontSize: "14px" }, children: "Email is required to initialize payment" });
|
|
548
810
|
}
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
const product = react.useMemo(() => {
|
|
572
|
-
if (productId) return products.find((p) => p.id === productId) || null;
|
|
573
|
-
const selectedId = variableStore.get("products.selectedProductId");
|
|
574
|
-
return products.find((p) => p.id === selectedId) || null;
|
|
575
|
-
}, [productId, products, variableStore]);
|
|
576
|
-
const paymentMode = react.useMemo(() => {
|
|
577
|
-
if (validateOnly) return "payment";
|
|
578
|
-
if (product?.hasTrial && !product.paidTrial) return "setup";
|
|
579
|
-
return "payment";
|
|
580
|
-
}, [product, validateOnly]);
|
|
581
|
-
const [stripePromise, setStripePromise] = react.useState(null);
|
|
582
|
-
const [clientSecret, setClientSecret] = react.useState(null);
|
|
583
|
-
const [error, setError] = react.useState(null);
|
|
584
|
-
const [isLoading, setIsLoading] = react.useState(false);
|
|
585
|
-
const hasInitialized = react.useRef(false);
|
|
586
|
-
react.useEffect(() => {
|
|
587
|
-
if (!email || !campaignId || hasInitialized.current) return;
|
|
588
|
-
if (!product?.storePriceId) return;
|
|
589
|
-
hasInitialized.current = true;
|
|
590
|
-
setIsLoading(true);
|
|
591
|
-
variableStore.set("payment.loading", true);
|
|
592
|
-
const createIntent = async () => {
|
|
593
|
-
try {
|
|
594
|
-
const endpoint = paymentMode === "setup" ? `/campaign/${campaignId}/stripe/setup-intent` : `/campaign/${campaignId}/stripe/payment-intent`;
|
|
595
|
-
const body = {
|
|
596
|
-
campaignId,
|
|
597
|
-
sessionId: tracker.getSessionId(),
|
|
598
|
-
customerEmail: email,
|
|
599
|
-
priceId: product.storePriceId
|
|
600
|
-
};
|
|
601
|
-
if (validateOnly) body.validateOnly = true;
|
|
602
|
-
const response = await fetch(`${apiBaseUrl}${endpoint}`, {
|
|
603
|
-
method: "POST",
|
|
604
|
-
headers: { "Content-Type": "application/json" },
|
|
605
|
-
body: JSON.stringify(body)
|
|
606
|
-
});
|
|
607
|
-
const result = await response.json();
|
|
608
|
-
if (!result.success) {
|
|
609
|
-
throw new Error(result.error || "Failed to create intent");
|
|
811
|
+
if (error) {
|
|
812
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className, style: { padding: "20px", textAlign: "center", color: "#ef4444", fontSize: "14px" }, children: error });
|
|
813
|
+
}
|
|
814
|
+
if (!stripePromise || !clientSecret) {
|
|
815
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className, style: { padding: "20px", textAlign: "center", color: "#6b7280" }, children: "Initializing payment..." });
|
|
816
|
+
}
|
|
817
|
+
if (variant === "embedded") {
|
|
818
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
819
|
+
reactStripeJs.EmbeddedCheckoutProvider,
|
|
820
|
+
{
|
|
821
|
+
stripe: stripePromise,
|
|
822
|
+
options: {
|
|
823
|
+
clientSecret,
|
|
824
|
+
onComplete: () => {
|
|
825
|
+
tracker.track("purchase.complete", {
|
|
826
|
+
amount: product?.rawPrice ? product.rawPrice / 100 : 0,
|
|
827
|
+
currency: product?.currencyCode || "USD"
|
|
828
|
+
});
|
|
829
|
+
onSuccess?.();
|
|
830
|
+
}
|
|
831
|
+
},
|
|
832
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(reactStripeJs.EmbeddedCheckout, {})
|
|
610
833
|
}
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
priceId: product.stripePriceId || product.storePriceId,
|
|
617
|
-
productName: product.displayName
|
|
618
|
-
});
|
|
619
|
-
} catch (err) {
|
|
620
|
-
const msg = err instanceof Error ? err.message : "Failed to initialize payment";
|
|
621
|
-
setError(msg);
|
|
622
|
-
variableStore.set("payment.error", msg);
|
|
623
|
-
} finally {
|
|
624
|
-
setIsLoading(false);
|
|
625
|
-
variableStore.set("payment.loading", false);
|
|
626
|
-
}
|
|
834
|
+
) });
|
|
835
|
+
}
|
|
836
|
+
const defaultAppearance = {
|
|
837
|
+
theme: "stripe",
|
|
838
|
+
variables: { colorPrimary: "#3b82f6", borderRadius: "8px" }
|
|
627
839
|
};
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
return /* @__PURE__ */ jsxRuntime.jsx("div", { className, style: { padding: "20px", textAlign: "center", color: "#6b7280" }, children: "Initializing payment..." });
|
|
840
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className, children: /* @__PURE__ */ jsxRuntime.jsx(reactStripeJs.Elements, { stripe: stripePromise, options: { clientSecret, appearance: appearance || defaultAppearance }, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
841
|
+
InnerPaymentForm,
|
|
842
|
+
{
|
|
843
|
+
ref,
|
|
844
|
+
paymentMode,
|
|
845
|
+
validateOnly,
|
|
846
|
+
onSuccess,
|
|
847
|
+
onError,
|
|
848
|
+
onReady,
|
|
849
|
+
layout
|
|
850
|
+
}
|
|
851
|
+
) }) });
|
|
641
852
|
}
|
|
642
|
-
|
|
643
|
-
theme: "stripe",
|
|
644
|
-
variables: { colorPrimary: "#3b82f6", borderRadius: "8px" }
|
|
645
|
-
};
|
|
646
|
-
return /* @__PURE__ */ jsxRuntime.jsx("div", { className, children: /* @__PURE__ */ jsxRuntime.jsx(reactStripeJs.Elements, { stripe: stripePromise, options: { clientSecret, appearance: appearance || defaultAppearance }, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
647
|
-
InnerPaymentForm,
|
|
648
|
-
{
|
|
649
|
-
paymentMode,
|
|
650
|
-
validateOnly,
|
|
651
|
-
onSuccess,
|
|
652
|
-
onError
|
|
653
|
-
}
|
|
654
|
-
) }) });
|
|
655
|
-
}
|
|
853
|
+
);
|
|
656
854
|
function PaddleCheckout({
|
|
657
855
|
productId,
|
|
658
856
|
mode = "overlay",
|
|
@@ -660,7 +858,7 @@ function PaddleCheckout({
|
|
|
660
858
|
onError,
|
|
661
859
|
className
|
|
662
860
|
}) {
|
|
663
|
-
const { variableStore, tracker, products } = useFunnelContext();
|
|
861
|
+
const { variableStore, tracker, products } = chunkEVUYCLVY_cjs.useFunnelContext();
|
|
664
862
|
const containerRef = react.useRef(null);
|
|
665
863
|
const initializedRef = react.useRef(false);
|
|
666
864
|
const product = productId ? products.find((p) => p.id === productId) : products.find((p) => p.id === variableStore.get("products.selectedProductId"));
|
|
@@ -717,21 +915,42 @@ function PaddleCheckout({
|
|
|
717
915
|
return null;
|
|
718
916
|
}
|
|
719
917
|
|
|
918
|
+
Object.defineProperty(exports, "useNavigation", {
|
|
919
|
+
enumerable: true,
|
|
920
|
+
get: function () { return chunkXP44I2MU_cjs.useNavigation; }
|
|
921
|
+
});
|
|
922
|
+
Object.defineProperty(exports, "useResponse", {
|
|
923
|
+
enumerable: true,
|
|
924
|
+
get: function () { return chunkXP44I2MU_cjs.useResponse; }
|
|
925
|
+
});
|
|
926
|
+
Object.defineProperty(exports, "useResponses", {
|
|
927
|
+
enumerable: true,
|
|
928
|
+
get: function () { return chunkXP44I2MU_cjs.useResponses; }
|
|
929
|
+
});
|
|
930
|
+
Object.defineProperty(exports, "FunnelProvider", {
|
|
931
|
+
enumerable: true,
|
|
932
|
+
get: function () { return chunkEVUYCLVY_cjs.FunnelProvider; }
|
|
933
|
+
});
|
|
934
|
+
Object.defineProperty(exports, "registerIntegration", {
|
|
935
|
+
enumerable: true,
|
|
936
|
+
get: function () { return chunkEVUYCLVY_cjs.registerIntegration; }
|
|
937
|
+
});
|
|
720
938
|
exports.PaddleCheckout = PaddleCheckout;
|
|
721
|
-
exports.
|
|
939
|
+
exports.StripePaymentForm = StripePaymentForm;
|
|
722
940
|
exports.defineConfig = defineConfig;
|
|
723
941
|
exports.definePage = definePage;
|
|
724
|
-
exports.registerIntegration = registerIntegration;
|
|
725
942
|
exports.useData = useData;
|
|
943
|
+
exports.useDateOfBirth = useDateOfBirth;
|
|
944
|
+
exports.useDeviceInfo = useDeviceInfo;
|
|
726
945
|
exports.useFunnel = useFunnel;
|
|
946
|
+
exports.useKeyboard = useKeyboard;
|
|
727
947
|
exports.useLocale = useLocale;
|
|
728
|
-
exports.
|
|
948
|
+
exports.usePageData = usePageData;
|
|
729
949
|
exports.usePayment = usePayment;
|
|
730
950
|
exports.useProducts = useProducts;
|
|
731
951
|
exports.useQueryParam = useQueryParam;
|
|
732
952
|
exports.useQueryParams = useQueryParams;
|
|
733
|
-
exports.
|
|
734
|
-
exports.useResponses = useResponses;
|
|
953
|
+
exports.useSafeArea = useSafeArea;
|
|
735
954
|
exports.useTracking = useTracking;
|
|
736
955
|
exports.useTranslation = useTranslation;
|
|
737
956
|
exports.useUser = useUser;
|