@appfunnel-dev/sdk 0.6.0 → 0.8.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 +2 -80
- package/dist/{chunk-BUF5FDKC.cjs → chunk-EVUYCLVY.cjs} +42 -19
- package/dist/chunk-EVUYCLVY.cjs.map +1 -0
- package/dist/{chunk-E6KSJ5UI.js → chunk-H3KHXZSI.js} +42 -20
- 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 +12217 -0
- package/dist/elements/index.cjs.map +1 -0
- package/dist/elements/index.d.cts +620 -0
- package/dist/elements/index.d.ts +620 -0
- package/dist/elements/index.js +12181 -0
- package/dist/elements/index.js.map +1 -0
- package/dist/index.cjs +958 -150
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +75 -5
- package/dist/index.d.ts +75 -5
- package/dist/index.js +928 -131
- package/dist/index.js.map +1 -1
- package/dist/{internal-BlgQ9C2d.d.cts → internal-C9MOEdND.d.cts} +12 -12
- package/dist/{internal-BlgQ9C2d.d.ts → internal-C9MOEdND.d.ts} +12 -12
- package/dist/internal.cjs +2 -2
- package/dist/internal.d.cts +1 -1
- package/dist/internal.d.ts +1 -1
- package/dist/internal.js +1 -1
- package/package.json +17 -2
- package/dist/chunk-BUF5FDKC.cjs.map +0 -1
- package/dist/chunk-E6KSJ5UI.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var chunkXP44I2MU_cjs = require('./chunk-XP44I2MU.cjs');
|
|
4
|
+
var chunkEVUYCLVY_cjs = require('./chunk-EVUYCLVY.cjs');
|
|
4
5
|
var react = require('react');
|
|
5
6
|
var stripeJs = require('@stripe/stripe-js');
|
|
6
7
|
var reactStripeJs = require('@stripe/react-stripe-js');
|
|
@@ -14,7 +15,7 @@ function definePage(definition) {
|
|
|
14
15
|
return definition;
|
|
15
16
|
}
|
|
16
17
|
function useVariable(id) {
|
|
17
|
-
const { variableStore } =
|
|
18
|
+
const { variableStore } = chunkEVUYCLVY_cjs.useFunnelContext();
|
|
18
19
|
const subscribe = react.useCallback(
|
|
19
20
|
(callback) => variableStore.subscribe(callback, { keys: [id] }),
|
|
20
21
|
[variableStore, id]
|
|
@@ -31,7 +32,7 @@ function useVariable(id) {
|
|
|
31
32
|
return [value, setValue];
|
|
32
33
|
}
|
|
33
34
|
function useVariables() {
|
|
34
|
-
const { variableStore } =
|
|
35
|
+
const { variableStore } = chunkEVUYCLVY_cjs.useFunnelContext();
|
|
35
36
|
const subscribe = react.useCallback(
|
|
36
37
|
(callback) => variableStore.subscribe(callback),
|
|
37
38
|
[variableStore]
|
|
@@ -42,8 +43,104 @@ function useVariables() {
|
|
|
42
43
|
);
|
|
43
44
|
return react.useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
|
|
44
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
|
|
45
142
|
function useUser() {
|
|
46
|
-
const { variableStore } =
|
|
143
|
+
const { variableStore, tracker } = chunkEVUYCLVY_cjs.useFunnelContext();
|
|
47
144
|
const subscribe = react.useCallback(
|
|
48
145
|
(cb) => variableStore.subscribe(cb, { prefix: "user." }),
|
|
49
146
|
[variableStore]
|
|
@@ -60,6 +157,7 @@ function useUser() {
|
|
|
60
157
|
stripeCustomerId: variables["user.stripeCustomerId"] || "",
|
|
61
158
|
paddleCustomerId: variables["user.paddleCustomerId"] || "",
|
|
62
159
|
dateOfBirth: variables["user.dateOfBirth"] || "",
|
|
160
|
+
marketingConsent: variables["user.marketingConsent"] === true,
|
|
63
161
|
setEmail(email) {
|
|
64
162
|
variableStore.set("user.email", email);
|
|
65
163
|
},
|
|
@@ -67,14 +165,20 @@ function useUser() {
|
|
|
67
165
|
variableStore.set("user.name", name);
|
|
68
166
|
},
|
|
69
167
|
setDateOfBirth(dateOfBirth) {
|
|
70
|
-
variableStore.set("user.dateOfBirth", dateOfBirth);
|
|
168
|
+
variableStore.set("user.dateOfBirth", toISODate(dateOfBirth));
|
|
169
|
+
},
|
|
170
|
+
setMarketingConsent(consent) {
|
|
171
|
+
variableStore.set("user.marketingConsent", consent);
|
|
172
|
+
},
|
|
173
|
+
identify(email) {
|
|
174
|
+
tracker.identify(email);
|
|
71
175
|
}
|
|
72
176
|
}),
|
|
73
|
-
[variables, variableStore]
|
|
177
|
+
[variables, variableStore, tracker]
|
|
74
178
|
);
|
|
75
179
|
}
|
|
76
180
|
function useUserProperty(field) {
|
|
77
|
-
const { variableStore } =
|
|
181
|
+
const { variableStore } = chunkEVUYCLVY_cjs.useFunnelContext();
|
|
78
182
|
const key = `user.${field}`;
|
|
79
183
|
const subscribe = react.useCallback(
|
|
80
184
|
(cb) => variableStore.subscribe(cb, { keys: [key] }),
|
|
@@ -91,47 +195,26 @@ function useUserProperty(field) {
|
|
|
91
195
|
);
|
|
92
196
|
return [value, setValue];
|
|
93
197
|
}
|
|
94
|
-
function
|
|
95
|
-
const { variableStore } =
|
|
96
|
-
const
|
|
198
|
+
function useDateOfBirth(format = "MM/DD/YYYY") {
|
|
199
|
+
const { variableStore } = chunkEVUYCLVY_cjs.useFunnelContext();
|
|
200
|
+
const key = "user.dateOfBirth";
|
|
97
201
|
const subscribe = react.useCallback(
|
|
98
|
-
(cb) => variableStore.subscribe(cb, { keys: [
|
|
99
|
-
[variableStore
|
|
202
|
+
(cb) => variableStore.subscribe(cb, { keys: [key] }),
|
|
203
|
+
[variableStore]
|
|
100
204
|
);
|
|
101
205
|
const getSnapshot = react.useCallback(
|
|
102
|
-
() => variableStore.get(
|
|
103
|
-
[variableStore
|
|
206
|
+
() => variableStore.get(key) || "",
|
|
207
|
+
[variableStore]
|
|
104
208
|
);
|
|
105
209
|
const value = react.useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
|
|
106
210
|
const setValue = react.useCallback(
|
|
107
|
-
(v) => variableStore.set(
|
|
108
|
-
[variableStore,
|
|
211
|
+
(v) => variableStore.set(key, toISODateWithFormat(v, format)),
|
|
212
|
+
[variableStore, format]
|
|
109
213
|
);
|
|
110
214
|
return [value, setValue];
|
|
111
215
|
}
|
|
112
|
-
function useResponses() {
|
|
113
|
-
const { variableStore } = chunkBUF5FDKC_cjs.useFunnelContext();
|
|
114
|
-
const subscribe = react.useCallback(
|
|
115
|
-
(cb) => variableStore.subscribe(cb, { prefix: "answers." }),
|
|
116
|
-
[variableStore]
|
|
117
|
-
);
|
|
118
|
-
const getSnapshot = react.useCallback(
|
|
119
|
-
() => variableStore.getState(),
|
|
120
|
-
[variableStore]
|
|
121
|
-
);
|
|
122
|
-
const variables = react.useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
|
|
123
|
-
return react.useMemo(() => {
|
|
124
|
-
const result = {};
|
|
125
|
-
for (const [key, value] of Object.entries(variables)) {
|
|
126
|
-
if (key.startsWith("answers.")) {
|
|
127
|
-
result[key.slice(8)] = value;
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
return result;
|
|
131
|
-
}, [variables]);
|
|
132
|
-
}
|
|
133
216
|
function useQueryParams() {
|
|
134
|
-
const { variableStore } =
|
|
217
|
+
const { variableStore } = chunkEVUYCLVY_cjs.useFunnelContext();
|
|
135
218
|
const subscribe = react.useCallback(
|
|
136
219
|
(cb) => variableStore.subscribe(cb, { prefix: "query." }),
|
|
137
220
|
[variableStore]
|
|
@@ -152,7 +235,7 @@ function useQueryParams() {
|
|
|
152
235
|
}, [variables]);
|
|
153
236
|
}
|
|
154
237
|
function useQueryParam(key) {
|
|
155
|
-
const { variableStore } =
|
|
238
|
+
const { variableStore } = chunkEVUYCLVY_cjs.useFunnelContext();
|
|
156
239
|
const prefixedKey = `query.${key}`;
|
|
157
240
|
const subscribe = react.useCallback(
|
|
158
241
|
(cb) => variableStore.subscribe(cb, { keys: [prefixedKey] }),
|
|
@@ -165,7 +248,7 @@ function useQueryParam(key) {
|
|
|
165
248
|
return react.useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
|
|
166
249
|
}
|
|
167
250
|
function useData(key) {
|
|
168
|
-
const { variableStore } =
|
|
251
|
+
const { variableStore } = chunkEVUYCLVY_cjs.useFunnelContext();
|
|
169
252
|
const prefixedKey = `data.${key}`;
|
|
170
253
|
const subscribe = react.useCallback(
|
|
171
254
|
(cb) => variableStore.subscribe(cb, { keys: [prefixedKey] }),
|
|
@@ -218,7 +301,7 @@ function detect24Hour(locale) {
|
|
|
218
301
|
}
|
|
219
302
|
}
|
|
220
303
|
function useTranslation() {
|
|
221
|
-
const { i18n } =
|
|
304
|
+
const { i18n } = chunkEVUYCLVY_cjs.useFunnelContext();
|
|
222
305
|
const subscribe = react.useCallback(
|
|
223
306
|
(cb) => i18n.subscribe(cb),
|
|
224
307
|
[i18n]
|
|
@@ -240,66 +323,8 @@ function useTranslation() {
|
|
|
240
323
|
const availableLocales = i18n.getAvailableLocales();
|
|
241
324
|
return { t, locale, setLocale, availableLocales };
|
|
242
325
|
}
|
|
243
|
-
function useNavigation() {
|
|
244
|
-
const { router, variableStore, tracker } = chunkBUF5FDKC_cjs.useFunnelContext();
|
|
245
|
-
react.useSyncExternalStore(
|
|
246
|
-
react.useCallback((cb) => router.subscribe(cb), [router]),
|
|
247
|
-
react.useCallback(() => router.getSnapshot(), [router]),
|
|
248
|
-
react.useCallback(() => router.getSnapshot(), [router])
|
|
249
|
-
);
|
|
250
|
-
const afterNavigate = react.useCallback((key) => {
|
|
251
|
-
const page = router.getCurrentPage();
|
|
252
|
-
if (page) {
|
|
253
|
-
tracker.track("page.view", {
|
|
254
|
-
pageId: page.key,
|
|
255
|
-
pageKey: page.key,
|
|
256
|
-
pageName: page.name
|
|
257
|
-
});
|
|
258
|
-
tracker.startPageTracking(page.key);
|
|
259
|
-
}
|
|
260
|
-
variableStore.setMany({
|
|
261
|
-
"page.currentId": key,
|
|
262
|
-
"page.currentIndex": router.getPageHistory().length,
|
|
263
|
-
"page.current": router.getPageHistory().length + 1,
|
|
264
|
-
"page.startedAt": Date.now()
|
|
265
|
-
});
|
|
266
|
-
}, [router, tracker, variableStore]);
|
|
267
|
-
const goToNextPage = react.useCallback(() => {
|
|
268
|
-
const previousPage = router.getCurrentPage();
|
|
269
|
-
if (previousPage) {
|
|
270
|
-
tracker.stopPageTracking();
|
|
271
|
-
}
|
|
272
|
-
const variables = variableStore.getState();
|
|
273
|
-
const nextKey = router.goToNextPage(variables);
|
|
274
|
-
if (nextKey) {
|
|
275
|
-
afterNavigate(nextKey);
|
|
276
|
-
}
|
|
277
|
-
}, [router, tracker, variableStore, afterNavigate]);
|
|
278
|
-
const goBack = react.useCallback(() => {
|
|
279
|
-
tracker.stopPageTracking();
|
|
280
|
-
const prevKey = router.goBack();
|
|
281
|
-
if (prevKey) {
|
|
282
|
-
afterNavigate(prevKey);
|
|
283
|
-
}
|
|
284
|
-
}, [router, tracker, afterNavigate]);
|
|
285
|
-
const goToPage = react.useCallback((pageKey) => {
|
|
286
|
-
tracker.stopPageTracking();
|
|
287
|
-
const key = router.goToPage(pageKey);
|
|
288
|
-
if (key) {
|
|
289
|
-
afterNavigate(key);
|
|
290
|
-
}
|
|
291
|
-
}, [router, tracker, afterNavigate]);
|
|
292
|
-
return {
|
|
293
|
-
goToNextPage,
|
|
294
|
-
goBack,
|
|
295
|
-
goToPage,
|
|
296
|
-
currentPage: router.getCurrentPage(),
|
|
297
|
-
pageHistory: router.getPageHistory(),
|
|
298
|
-
progress: router.getProgress()
|
|
299
|
-
};
|
|
300
|
-
}
|
|
301
326
|
function useProducts() {
|
|
302
|
-
const { products, variableStore, selectProduct: ctxSelect } =
|
|
327
|
+
const { products, variableStore, selectProduct: ctxSelect } = chunkEVUYCLVY_cjs.useFunnelContext();
|
|
303
328
|
const subscribe = react.useCallback(
|
|
304
329
|
(cb) => variableStore.subscribe(cb, { keys: ["products.selectedProductId"] }),
|
|
305
330
|
[variableStore]
|
|
@@ -316,32 +341,19 @@ function useProducts() {
|
|
|
316
341
|
return { products, selected, select };
|
|
317
342
|
}
|
|
318
343
|
function useTracking() {
|
|
319
|
-
const { tracker } =
|
|
344
|
+
const { tracker } = chunkEVUYCLVY_cjs.useFunnelContext();
|
|
320
345
|
const track = react.useCallback(
|
|
321
346
|
(eventName, data) => {
|
|
322
347
|
tracker.track(eventName, data);
|
|
323
348
|
},
|
|
324
349
|
[tracker]
|
|
325
350
|
);
|
|
326
|
-
|
|
327
|
-
(email) => {
|
|
328
|
-
tracker.identify(email);
|
|
329
|
-
},
|
|
330
|
-
[tracker]
|
|
331
|
-
);
|
|
332
|
-
return { track, identify };
|
|
351
|
+
return { track };
|
|
333
352
|
}
|
|
334
|
-
var
|
|
335
|
-
|
|
336
|
-
"card.brand",
|
|
337
|
-
"card.expMonth",
|
|
338
|
-
"card.expYear",
|
|
339
|
-
"payment.loading",
|
|
340
|
-
"payment.error",
|
|
341
|
-
"payment.customerId"
|
|
342
|
-
];
|
|
353
|
+
var API_BASE_URL = "https://api.appfunnel.net";
|
|
354
|
+
var PAYMENT_KEYS = ["payment.loading", "payment.error"];
|
|
343
355
|
function usePayment() {
|
|
344
|
-
const { variableStore } =
|
|
356
|
+
const { variableStore, products, campaignId, tracker } = chunkEVUYCLVY_cjs.useFunnelContext();
|
|
345
357
|
const subscribe = react.useCallback(
|
|
346
358
|
(cb) => variableStore.subscribe(cb, { keys: PAYMENT_KEYS }),
|
|
347
359
|
[variableStore]
|
|
@@ -351,20 +363,258 @@ function usePayment() {
|
|
|
351
363
|
[variableStore]
|
|
352
364
|
);
|
|
353
365
|
const variables = react.useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
366
|
+
const purchase = react.useCallback(
|
|
367
|
+
async (productId, options) => {
|
|
368
|
+
console.log("[Purchase] Starting purchase for product:", productId);
|
|
369
|
+
if (globalThis.__APPFUNNEL_DEV__) {
|
|
370
|
+
console.log(
|
|
371
|
+
"[Purchase] Dev mode \u2014 simulating success (500ms delay)"
|
|
372
|
+
);
|
|
373
|
+
variableStore.set("payment.loading", true);
|
|
374
|
+
await new Promise((r) => setTimeout(r, 500));
|
|
375
|
+
variableStore.set("payment.error", "");
|
|
376
|
+
console.log("[Purchase] Dev mode \u2014 success");
|
|
377
|
+
options?.onSuccess?.();
|
|
378
|
+
return true;
|
|
379
|
+
}
|
|
380
|
+
const customerId = variableStore.get(
|
|
381
|
+
"user.stripeCustomerId"
|
|
382
|
+
);
|
|
383
|
+
console.log("[Purchase] Customer ID:", customerId || "(none)");
|
|
384
|
+
if (!customerId) {
|
|
385
|
+
const msg = "Please complete payment authorization first";
|
|
386
|
+
console.error("[Purchase] Failed:", msg);
|
|
387
|
+
variableStore.set("payment.error", msg);
|
|
388
|
+
options?.onError?.(msg);
|
|
389
|
+
return false;
|
|
390
|
+
}
|
|
391
|
+
const product = products.find((p) => p.id === productId);
|
|
392
|
+
console.log(
|
|
393
|
+
"[Purchase] Product found:",
|
|
394
|
+
product ? {
|
|
395
|
+
id: product.id,
|
|
396
|
+
name: product.name,
|
|
397
|
+
stripePriceId: product.stripePriceId
|
|
398
|
+
} : "(not found)"
|
|
399
|
+
);
|
|
400
|
+
if (!product?.stripePriceId) {
|
|
401
|
+
const msg = "Product not found or missing Stripe price";
|
|
402
|
+
console.error("[Purchase] Failed:", msg);
|
|
403
|
+
variableStore.set("payment.error", msg);
|
|
404
|
+
options?.onError?.(msg);
|
|
405
|
+
return false;
|
|
406
|
+
}
|
|
407
|
+
const trialPeriodDays = product.hasTrial ? product.trialDays : void 0;
|
|
408
|
+
const trialChargePriceId = product.paidTrial && product.trialStorePriceId ? product.trialStorePriceId : void 0;
|
|
409
|
+
console.log("[Purchase] Trial config:", {
|
|
410
|
+
trialPeriodDays,
|
|
411
|
+
trialChargePriceId,
|
|
412
|
+
hasTrial: product.hasTrial,
|
|
413
|
+
paidTrial: product.paidTrial
|
|
414
|
+
});
|
|
415
|
+
variableStore.set("payment.loading", true);
|
|
416
|
+
variableStore.set("payment.error", "");
|
|
417
|
+
const requestBody = {
|
|
418
|
+
campaignId,
|
|
419
|
+
sessionId: tracker.getSessionId(),
|
|
420
|
+
stripePriceId: product.stripePriceId,
|
|
421
|
+
trialPeriodDays,
|
|
422
|
+
trialChargePriceId
|
|
423
|
+
};
|
|
424
|
+
console.log(
|
|
425
|
+
"[Purchase] Sending request:",
|
|
426
|
+
`${API_BASE_URL}/campaign/${campaignId}/stripe/purchase`,
|
|
427
|
+
requestBody
|
|
428
|
+
);
|
|
429
|
+
try {
|
|
430
|
+
const response = await fetch(
|
|
431
|
+
`${API_BASE_URL}/campaign/${campaignId}/stripe/purchase`,
|
|
432
|
+
{
|
|
433
|
+
method: "POST",
|
|
434
|
+
headers: { "Content-Type": "application/json" },
|
|
435
|
+
body: JSON.stringify(requestBody)
|
|
436
|
+
}
|
|
437
|
+
);
|
|
438
|
+
console.log("[Purchase] Response status:", response.status);
|
|
439
|
+
let result = await response.json();
|
|
440
|
+
console.log("[Purchase] Response body:", result);
|
|
441
|
+
if (!result.success && result.requiresAction) {
|
|
442
|
+
console.log(
|
|
443
|
+
"[Purchase] 3DS authentication required, loading Stripe..."
|
|
444
|
+
);
|
|
445
|
+
const { loadStripe: loadStripe2 } = await import('@stripe/stripe-js');
|
|
446
|
+
const stripeInstance = await loadStripe2(
|
|
447
|
+
result.publishableKey
|
|
448
|
+
);
|
|
449
|
+
if (!stripeInstance) {
|
|
450
|
+
const msg = "Failed to load payment processor";
|
|
451
|
+
console.error("[Purchase] Failed:", msg);
|
|
452
|
+
variableStore.set("payment.error", msg);
|
|
453
|
+
options?.onError?.(msg);
|
|
454
|
+
return false;
|
|
455
|
+
}
|
|
456
|
+
console.log(
|
|
457
|
+
"[Purchase] Confirming card payment with 3DS..."
|
|
458
|
+
);
|
|
459
|
+
const { error: confirmError, paymentIntent: confirmedPi } = await stripeInstance.confirmCardPayment(
|
|
460
|
+
result.clientSecret,
|
|
461
|
+
{
|
|
462
|
+
payment_method: result.paymentMethodId
|
|
463
|
+
}
|
|
464
|
+
);
|
|
465
|
+
if (confirmError || !confirmedPi || confirmedPi.status !== "succeeded") {
|
|
466
|
+
const msg = confirmError?.message || "Payment authentication failed";
|
|
467
|
+
console.error("[Purchase] 3DS failed:", msg);
|
|
468
|
+
variableStore.set("payment.error", msg);
|
|
469
|
+
options?.onError?.(msg);
|
|
470
|
+
return false;
|
|
471
|
+
}
|
|
472
|
+
console.log(
|
|
473
|
+
"[Purchase] 3DS succeeded, retrying purchase with confirmed PI:",
|
|
474
|
+
confirmedPi.id
|
|
475
|
+
);
|
|
476
|
+
const retryResponse = await fetch(
|
|
477
|
+
`${API_BASE_URL}/campaign/${campaignId}/stripe/purchase`,
|
|
478
|
+
{
|
|
479
|
+
method: "POST",
|
|
480
|
+
headers: { "Content-Type": "application/json" },
|
|
481
|
+
body: JSON.stringify({
|
|
482
|
+
campaignId,
|
|
483
|
+
sessionId: tracker.getSessionId(),
|
|
484
|
+
stripePriceId: product.stripePriceId,
|
|
485
|
+
trialPeriodDays,
|
|
486
|
+
trialChargePriceId,
|
|
487
|
+
onSessionPiId: confirmedPi.id
|
|
488
|
+
})
|
|
489
|
+
}
|
|
490
|
+
);
|
|
491
|
+
result = await retryResponse.json();
|
|
492
|
+
console.log("[Purchase] Retry response:", result);
|
|
493
|
+
}
|
|
494
|
+
if (result.success) {
|
|
495
|
+
console.log("[Purchase] Success! Type:", result.type);
|
|
496
|
+
variableStore.set("payment.error", "");
|
|
497
|
+
if (result.type === "validate_only") {
|
|
498
|
+
console.log(
|
|
499
|
+
"[Purchase] Validate-only \u2014 setting card variables"
|
|
500
|
+
);
|
|
501
|
+
if (result.card) {
|
|
502
|
+
variableStore.setMany({
|
|
503
|
+
"card.last4": result.card.last4,
|
|
504
|
+
"card.brand": result.card.brand,
|
|
505
|
+
"card.expMonth": result.card.expMonth,
|
|
506
|
+
"card.expYear": result.card.expYear,
|
|
507
|
+
"card.funding": result.card.funding
|
|
508
|
+
});
|
|
509
|
+
}
|
|
510
|
+
if (result.eventId) {
|
|
511
|
+
console.log(
|
|
512
|
+
"[Purchase] Tracking purchase.complete (validate_only), eventId:",
|
|
513
|
+
result.eventId
|
|
514
|
+
);
|
|
515
|
+
tracker.track("purchase.complete", {
|
|
516
|
+
eventId: result.eventId,
|
|
517
|
+
amount: product.rawPrice ? product.rawPrice / 100 : 0,
|
|
518
|
+
currency: product.currencyCode || "USD"
|
|
519
|
+
});
|
|
520
|
+
}
|
|
521
|
+
} else if (result.type === "one_time") {
|
|
522
|
+
console.log(
|
|
523
|
+
"[Purchase] One-time charge \u2014 paymentIntentId:",
|
|
524
|
+
result.paymentIntentId
|
|
525
|
+
);
|
|
526
|
+
variableStore.set(
|
|
527
|
+
"stripe.paymentIntentId",
|
|
528
|
+
result.paymentIntentId
|
|
529
|
+
);
|
|
530
|
+
variableStore.set("payment.status", result.status);
|
|
531
|
+
if (result.eventId) {
|
|
532
|
+
console.log(
|
|
533
|
+
"[Purchase] Tracking purchase.complete (one_time), eventId:",
|
|
534
|
+
result.eventId
|
|
535
|
+
);
|
|
536
|
+
tracker.track("purchase.complete", {
|
|
537
|
+
eventId: result.eventId,
|
|
538
|
+
amount: product.rawPrice ? product.rawPrice / 100 : 0,
|
|
539
|
+
currency: product.currencyCode || "USD"
|
|
540
|
+
});
|
|
541
|
+
}
|
|
542
|
+
} else {
|
|
543
|
+
console.log(
|
|
544
|
+
"[Purchase] Subscription \u2014 subscriptionId:",
|
|
545
|
+
result.subscriptionId,
|
|
546
|
+
"status:",
|
|
547
|
+
result.status
|
|
548
|
+
);
|
|
549
|
+
variableStore.set(
|
|
550
|
+
"stripe.subscriptionId",
|
|
551
|
+
result.subscriptionId
|
|
552
|
+
);
|
|
553
|
+
variableStore.set("subscription.status", result.status);
|
|
554
|
+
if (result.trialCharge) {
|
|
555
|
+
console.log(
|
|
556
|
+
"[Purchase] Tracking trial charge purchase.complete"
|
|
557
|
+
);
|
|
558
|
+
tracker.track("purchase.complete", {
|
|
559
|
+
eventId: result.eventIds?.trialCharge,
|
|
560
|
+
amount: result.trialCharge.amount / 100,
|
|
561
|
+
currency: "USD"
|
|
562
|
+
});
|
|
563
|
+
}
|
|
564
|
+
if (result.eventIds?.subscription) {
|
|
565
|
+
console.log(
|
|
566
|
+
"[Purchase] Tracking subscription.created, eventId:",
|
|
567
|
+
result.eventIds.subscription
|
|
568
|
+
);
|
|
569
|
+
tracker.track("subscription.created", {
|
|
570
|
+
eventId: result.eventIds.subscription,
|
|
571
|
+
subscriptionId: result.subscriptionId,
|
|
572
|
+
status: result.status
|
|
573
|
+
});
|
|
574
|
+
}
|
|
575
|
+
if (result.eventIds?.firstPeriod) {
|
|
576
|
+
console.log(
|
|
577
|
+
"[Purchase] Tracking first-period purchase.complete, eventId:",
|
|
578
|
+
result.eventIds.firstPeriod
|
|
579
|
+
);
|
|
580
|
+
tracker.track("purchase.complete", {
|
|
581
|
+
eventId: result.eventIds.firstPeriod,
|
|
582
|
+
amount: product.rawPrice ? product.rawPrice / 100 : 0,
|
|
583
|
+
currency: product.currencyCode || "USD"
|
|
584
|
+
});
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
console.log("[Purchase] Calling onSuccess callback");
|
|
588
|
+
options?.onSuccess?.();
|
|
589
|
+
return true;
|
|
590
|
+
} else {
|
|
591
|
+
const msg = result.error || "Purchase failed";
|
|
592
|
+
console.error("[Purchase] Failed:", msg);
|
|
593
|
+
variableStore.set("payment.error", msg);
|
|
594
|
+
options?.onError?.(msg);
|
|
595
|
+
return false;
|
|
596
|
+
}
|
|
597
|
+
} catch (err) {
|
|
598
|
+
const msg = err instanceof Error ? err.message : "Purchase failed";
|
|
599
|
+
console.error("[Purchase] Exception:", err);
|
|
600
|
+
variableStore.set("payment.error", msg);
|
|
601
|
+
options?.onError?.(msg);
|
|
602
|
+
return false;
|
|
603
|
+
} finally {
|
|
604
|
+
console.log("[Purchase] Done \u2014 setting loading to false");
|
|
605
|
+
variableStore.set("payment.loading", false);
|
|
606
|
+
}
|
|
607
|
+
},
|
|
608
|
+
[variableStore, products, campaignId, tracker]
|
|
609
|
+
);
|
|
610
|
+
return react.useMemo(
|
|
611
|
+
() => ({
|
|
363
612
|
loading: !!variables["payment.loading"],
|
|
364
613
|
error: variables["payment.error"] || null,
|
|
365
|
-
|
|
366
|
-
}
|
|
367
|
-
|
|
614
|
+
purchase
|
|
615
|
+
}),
|
|
616
|
+
[variables, purchase]
|
|
617
|
+
);
|
|
368
618
|
}
|
|
369
619
|
var DEVICE_KEYS = [
|
|
370
620
|
"os.name",
|
|
@@ -386,7 +636,7 @@ var DEVICE_KEYS = [
|
|
|
386
636
|
"browser.language"
|
|
387
637
|
];
|
|
388
638
|
function useDeviceInfo() {
|
|
389
|
-
const { variableStore } =
|
|
639
|
+
const { variableStore } = chunkEVUYCLVY_cjs.useFunnelContext();
|
|
390
640
|
const subscribe = react.useCallback(
|
|
391
641
|
(cb) => variableStore.subscribe(cb, { keys: DEVICE_KEYS }),
|
|
392
642
|
[variableStore]
|
|
@@ -422,6 +672,95 @@ function useDeviceInfo() {
|
|
|
422
672
|
}
|
|
423
673
|
}), [variables]);
|
|
424
674
|
}
|
|
675
|
+
function useSafeArea() {
|
|
676
|
+
const [insets, setInsets] = react.useState({ top: 0, right: 0, bottom: 0, left: 0 });
|
|
677
|
+
react.useEffect(() => {
|
|
678
|
+
if (typeof window === "undefined") return;
|
|
679
|
+
const el = document.createElement("div");
|
|
680
|
+
el.style.cssText = [
|
|
681
|
+
"position:fixed",
|
|
682
|
+
"top:env(safe-area-inset-top,0px)",
|
|
683
|
+
"right:env(safe-area-inset-right,0px)",
|
|
684
|
+
"bottom:env(safe-area-inset-bottom,0px)",
|
|
685
|
+
"left:env(safe-area-inset-left,0px)",
|
|
686
|
+
"pointer-events:none",
|
|
687
|
+
"visibility:hidden",
|
|
688
|
+
"z-index:-1"
|
|
689
|
+
].join(";");
|
|
690
|
+
document.body.appendChild(el);
|
|
691
|
+
function read() {
|
|
692
|
+
const style = getComputedStyle(el);
|
|
693
|
+
setInsets({
|
|
694
|
+
top: parseFloat(style.top) || 0,
|
|
695
|
+
right: parseFloat(style.right) || 0,
|
|
696
|
+
bottom: parseFloat(style.bottom) || 0,
|
|
697
|
+
left: parseFloat(style.left) || 0
|
|
698
|
+
});
|
|
699
|
+
}
|
|
700
|
+
read();
|
|
701
|
+
const observer = new ResizeObserver(read);
|
|
702
|
+
observer.observe(el);
|
|
703
|
+
window.addEventListener("resize", read);
|
|
704
|
+
return () => {
|
|
705
|
+
observer.disconnect();
|
|
706
|
+
window.removeEventListener("resize", read);
|
|
707
|
+
el.remove();
|
|
708
|
+
};
|
|
709
|
+
}, []);
|
|
710
|
+
return insets;
|
|
711
|
+
}
|
|
712
|
+
function useKeyboard() {
|
|
713
|
+
const [state, setState] = react.useState({ isOpen: false, height: 0 });
|
|
714
|
+
const timeoutRef = react.useRef();
|
|
715
|
+
react.useEffect(() => {
|
|
716
|
+
if (typeof window === "undefined") return;
|
|
717
|
+
if ("virtualKeyboard" in navigator) {
|
|
718
|
+
const vk = navigator.virtualKeyboard;
|
|
719
|
+
vk.overlaysContent = true;
|
|
720
|
+
const handler = () => {
|
|
721
|
+
const h = vk.boundingRect.height;
|
|
722
|
+
setState((prev) => {
|
|
723
|
+
if (prev.height === h && prev.isOpen === h > 0) return prev;
|
|
724
|
+
return { isOpen: h > 0, height: h };
|
|
725
|
+
});
|
|
726
|
+
};
|
|
727
|
+
vk.addEventListener("geometrychange", handler);
|
|
728
|
+
handler();
|
|
729
|
+
return () => vk.removeEventListener("geometrychange", handler);
|
|
730
|
+
}
|
|
731
|
+
const vv = window.visualViewport;
|
|
732
|
+
if (!vv) return;
|
|
733
|
+
let layoutHeight = window.innerHeight;
|
|
734
|
+
function compute() {
|
|
735
|
+
const kbHeight = Math.max(0, layoutHeight - vv.height);
|
|
736
|
+
const h = kbHeight > 40 ? Math.round(kbHeight) : 0;
|
|
737
|
+
setState((prev) => {
|
|
738
|
+
if (prev.height === h && prev.isOpen === h > 0) return prev;
|
|
739
|
+
return { isOpen: h > 0, height: h };
|
|
740
|
+
});
|
|
741
|
+
}
|
|
742
|
+
function onViewportResize() {
|
|
743
|
+
compute();
|
|
744
|
+
clearTimeout(timeoutRef.current);
|
|
745
|
+
timeoutRef.current = setTimeout(compute, 1e3);
|
|
746
|
+
}
|
|
747
|
+
function onWindowResize() {
|
|
748
|
+
layoutHeight = window.innerHeight;
|
|
749
|
+
compute();
|
|
750
|
+
}
|
|
751
|
+
vv.addEventListener("resize", onViewportResize);
|
|
752
|
+
window.addEventListener("resize", onWindowResize);
|
|
753
|
+
window.addEventListener("orientationchange", onWindowResize);
|
|
754
|
+
compute();
|
|
755
|
+
return () => {
|
|
756
|
+
clearTimeout(timeoutRef.current);
|
|
757
|
+
vv.removeEventListener("resize", onViewportResize);
|
|
758
|
+
window.removeEventListener("resize", onWindowResize);
|
|
759
|
+
window.removeEventListener("orientationchange", onWindowResize);
|
|
760
|
+
};
|
|
761
|
+
}, []);
|
|
762
|
+
return state;
|
|
763
|
+
}
|
|
425
764
|
var PAGE_KEYS = [
|
|
426
765
|
"page.currentId",
|
|
427
766
|
"page.currentIndex",
|
|
@@ -431,7 +770,7 @@ var PAGE_KEYS = [
|
|
|
431
770
|
"page.startedAt"
|
|
432
771
|
];
|
|
433
772
|
function usePageData() {
|
|
434
|
-
const { variableStore } =
|
|
773
|
+
const { variableStore } = chunkEVUYCLVY_cjs.useFunnelContext();
|
|
435
774
|
const subscribe = react.useCallback(
|
|
436
775
|
(cb) => variableStore.subscribe(cb, { keys: PAGE_KEYS }),
|
|
437
776
|
[variableStore]
|
|
@@ -453,28 +792,477 @@ function usePageData() {
|
|
|
453
792
|
|
|
454
793
|
// src/hooks/useFunnel.ts
|
|
455
794
|
function useFunnel() {
|
|
456
|
-
const { funnelId, campaignId, tracker } =
|
|
795
|
+
const { funnelId, campaignId, tracker } = chunkEVUYCLVY_cjs.useFunnelContext();
|
|
457
796
|
return {
|
|
458
797
|
funnelId,
|
|
459
798
|
campaignId,
|
|
460
799
|
sessionId: tracker.getSessionId(),
|
|
461
800
|
variables: useVariables(),
|
|
462
801
|
user: useUser(),
|
|
463
|
-
responses: useResponses(),
|
|
802
|
+
responses: chunkXP44I2MU_cjs.useResponses(),
|
|
464
803
|
queryParams: useQueryParams(),
|
|
465
|
-
navigation: useNavigation(),
|
|
804
|
+
navigation: chunkXP44I2MU_cjs.useNavigation(),
|
|
466
805
|
products: useProducts(),
|
|
467
806
|
tracking: useTracking(),
|
|
468
807
|
payment: usePayment()
|
|
469
808
|
};
|
|
470
809
|
}
|
|
471
|
-
var
|
|
810
|
+
var FONT = '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif';
|
|
811
|
+
var inputStyle = (focused) => ({
|
|
812
|
+
width: "100%",
|
|
813
|
+
padding: "12px",
|
|
814
|
+
border: `1px solid ${focused ? "#666" : "#e0e0e0"}`,
|
|
815
|
+
borderRadius: "6px",
|
|
816
|
+
fontSize: "14px",
|
|
817
|
+
fontFamily: FONT,
|
|
818
|
+
outline: "none",
|
|
819
|
+
backgroundColor: "#fff",
|
|
820
|
+
boxSizing: "border-box",
|
|
821
|
+
transition: "border-color 0.15s"
|
|
822
|
+
});
|
|
823
|
+
var labelStyle = {
|
|
824
|
+
display: "block",
|
|
825
|
+
fontSize: "14px",
|
|
826
|
+
fontWeight: 500,
|
|
827
|
+
color: "#333",
|
|
828
|
+
marginBottom: "6px",
|
|
829
|
+
fontFamily: FONT
|
|
830
|
+
};
|
|
831
|
+
function CardBrandBadges() {
|
|
832
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", gap: "4px", alignItems: "center" }, children: [
|
|
833
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: "10px", fontWeight: 700, color: "#1a1f71", background: "#fff", border: "1px solid #e0e0e0", borderRadius: "3px", padding: "2px 4px", fontFamily: FONT }, children: "VISA" }),
|
|
834
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: "10px", fontWeight: 700, color: "#eb001b", background: "#fff", border: "1px solid #e0e0e0", borderRadius: "3px", padding: "2px 4px", fontFamily: FONT }, children: "MC" }),
|
|
835
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: "10px", fontWeight: 700, color: "#ff6000", background: "#fff", border: "1px solid #e0e0e0", borderRadius: "3px", padding: "2px 4px", fontFamily: FONT }, children: "DISC" })
|
|
836
|
+
] });
|
|
837
|
+
}
|
|
838
|
+
function CvcIcon() {
|
|
839
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "20", height: "16", viewBox: "0 0 20 16", fill: "none", style: { opacity: 0.4 }, children: [
|
|
840
|
+
/* @__PURE__ */ jsxRuntime.jsx("rect", { x: "0.5", y: "0.5", width: "19", height: "15", rx: "2", stroke: "#888" }),
|
|
841
|
+
/* @__PURE__ */ jsxRuntime.jsx("rect", { x: "0", y: "4", width: "20", height: "3", fill: "#888" }),
|
|
842
|
+
/* @__PURE__ */ jsxRuntime.jsx("rect", { x: "3", y: "10", width: "8", height: "2", rx: "1", fill: "#ccc" }),
|
|
843
|
+
/* @__PURE__ */ jsxRuntime.jsx("text", { x: "14", y: "12", fontSize: "6", fill: "#888", fontFamily: FONT, children: "123" })
|
|
844
|
+
] });
|
|
845
|
+
}
|
|
846
|
+
function DemoElementsForm({ onReady }) {
|
|
847
|
+
const [cardNumber, setCardNumber] = react.useState("");
|
|
848
|
+
const [expiry, setExpiry] = react.useState("");
|
|
849
|
+
const [cvc, setCvc] = react.useState("");
|
|
850
|
+
const [country, setCountry] = react.useState("Denmark");
|
|
851
|
+
const [focusedField, setFocusedField] = react.useState(null);
|
|
852
|
+
const readyFired = react.useRef(false);
|
|
853
|
+
react.useEffect(() => {
|
|
854
|
+
if (!readyFired.current) {
|
|
855
|
+
readyFired.current = true;
|
|
856
|
+
onReady?.();
|
|
857
|
+
}
|
|
858
|
+
}, [onReady]);
|
|
859
|
+
const formatCardNumber = (val) => {
|
|
860
|
+
const digits = val.replace(/\D/g, "").slice(0, 16);
|
|
861
|
+
return digits.replace(/(.{4})/g, "$1 ").trim();
|
|
862
|
+
};
|
|
863
|
+
const formatExpiry = (val) => {
|
|
864
|
+
const digits = val.replace(/\D/g, "").slice(0, 4);
|
|
865
|
+
if (digits.length > 2) return digits.slice(0, 2) + " / " + digits.slice(2);
|
|
866
|
+
return digits;
|
|
867
|
+
};
|
|
868
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { fontFamily: FONT }, children: [
|
|
869
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { marginBottom: "14px" }, children: [
|
|
870
|
+
/* @__PURE__ */ jsxRuntime.jsx("label", { style: labelStyle, children: "Card number" }),
|
|
871
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { position: "relative" }, children: [
|
|
872
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
873
|
+
"input",
|
|
874
|
+
{
|
|
875
|
+
type: "text",
|
|
876
|
+
value: cardNumber,
|
|
877
|
+
onChange: (e) => setCardNumber(formatCardNumber(e.target.value)),
|
|
878
|
+
onFocus: () => setFocusedField("card"),
|
|
879
|
+
onBlur: () => setFocusedField(null),
|
|
880
|
+
placeholder: "1234 1234 1234 1234",
|
|
881
|
+
style: inputStyle(focusedField === "card")
|
|
882
|
+
}
|
|
883
|
+
),
|
|
884
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { style: { position: "absolute", right: "12px", top: "50%", transform: "translateY(-50%)" }, children: /* @__PURE__ */ jsxRuntime.jsx(CardBrandBadges, {}) })
|
|
885
|
+
] })
|
|
886
|
+
] }),
|
|
887
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "grid", gridTemplateColumns: "1fr 1fr", gap: "12px", marginBottom: "14px" }, children: [
|
|
888
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
889
|
+
/* @__PURE__ */ jsxRuntime.jsx("label", { style: labelStyle, children: "Expiry date" }),
|
|
890
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
891
|
+
"input",
|
|
892
|
+
{
|
|
893
|
+
type: "text",
|
|
894
|
+
value: expiry,
|
|
895
|
+
onChange: (e) => setExpiry(formatExpiry(e.target.value)),
|
|
896
|
+
onFocus: () => setFocusedField("expiry"),
|
|
897
|
+
onBlur: () => setFocusedField(null),
|
|
898
|
+
placeholder: "MM / YY",
|
|
899
|
+
style: inputStyle(focusedField === "expiry")
|
|
900
|
+
}
|
|
901
|
+
)
|
|
902
|
+
] }),
|
|
903
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
904
|
+
/* @__PURE__ */ jsxRuntime.jsx("label", { style: labelStyle, children: "Security code" }),
|
|
905
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { position: "relative" }, children: [
|
|
906
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
907
|
+
"input",
|
|
908
|
+
{
|
|
909
|
+
type: "text",
|
|
910
|
+
value: cvc,
|
|
911
|
+
onChange: (e) => setCvc(e.target.value.replace(/\D/g, "").slice(0, 4)),
|
|
912
|
+
onFocus: () => setFocusedField("cvc"),
|
|
913
|
+
onBlur: () => setFocusedField(null),
|
|
914
|
+
placeholder: "CVC",
|
|
915
|
+
style: inputStyle(focusedField === "cvc")
|
|
916
|
+
}
|
|
917
|
+
),
|
|
918
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { style: { position: "absolute", right: "12px", top: "50%", transform: "translateY(-50%)" }, children: /* @__PURE__ */ jsxRuntime.jsx(CvcIcon, {}) })
|
|
919
|
+
] })
|
|
920
|
+
] })
|
|
921
|
+
] }),
|
|
922
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { marginBottom: "14px" }, children: [
|
|
923
|
+
/* @__PURE__ */ jsxRuntime.jsx("label", { style: labelStyle, children: "Country" }),
|
|
924
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
925
|
+
"select",
|
|
926
|
+
{
|
|
927
|
+
value: country,
|
|
928
|
+
onChange: (e) => setCountry(e.target.value),
|
|
929
|
+
onFocus: () => setFocusedField("country"),
|
|
930
|
+
onBlur: () => setFocusedField(null),
|
|
931
|
+
style: {
|
|
932
|
+
...inputStyle(focusedField === "country"),
|
|
933
|
+
appearance: "none",
|
|
934
|
+
backgroundImage: `url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='%23666' viewBox='0 0 16 16'%3E%3Cpath d='M8 11L3 6h10z'/%3E%3C/svg%3E")`,
|
|
935
|
+
backgroundRepeat: "no-repeat",
|
|
936
|
+
backgroundPosition: "right 12px center",
|
|
937
|
+
paddingRight: "32px"
|
|
938
|
+
},
|
|
939
|
+
children: [
|
|
940
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { children: "Australia" }),
|
|
941
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { children: "Austria" }),
|
|
942
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { children: "Belgium" }),
|
|
943
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { children: "Brazil" }),
|
|
944
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { children: "Canada" }),
|
|
945
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { children: "Denmark" }),
|
|
946
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { children: "Finland" }),
|
|
947
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { children: "France" }),
|
|
948
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { children: "Germany" }),
|
|
949
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { children: "Ireland" }),
|
|
950
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { children: "Italy" }),
|
|
951
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { children: "Japan" }),
|
|
952
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { children: "Netherlands" }),
|
|
953
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { children: "New Zealand" }),
|
|
954
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { children: "Norway" }),
|
|
955
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { children: "Poland" }),
|
|
956
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { children: "Portugal" }),
|
|
957
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { children: "Spain" }),
|
|
958
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { children: "Sweden" }),
|
|
959
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { children: "Switzerland" }),
|
|
960
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { children: "United Kingdom" }),
|
|
961
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { children: "United States" })
|
|
962
|
+
]
|
|
963
|
+
}
|
|
964
|
+
)
|
|
965
|
+
] }),
|
|
966
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { style: { fontSize: "12px", color: "#6b7280", lineHeight: 1.5, margin: 0, fontFamily: FONT }, children: "By providing your card information, you allow the merchant to charge your card for future payments in accordance with their terms." })
|
|
967
|
+
] });
|
|
968
|
+
}
|
|
969
|
+
function DemoEmbeddedForm({ product, onReady }) {
|
|
970
|
+
const readyFired = react.useRef(false);
|
|
971
|
+
const [email, setEmail] = react.useState("");
|
|
972
|
+
const [cardNumber, setCardNumber] = react.useState("");
|
|
973
|
+
const [expiry, setExpiry] = react.useState("");
|
|
974
|
+
const [cvc, setCvc] = react.useState("");
|
|
975
|
+
const [name, setName] = react.useState("");
|
|
976
|
+
const [country, setCountry] = react.useState("Denmark");
|
|
977
|
+
const [focusedField, setFocusedField] = react.useState(null);
|
|
978
|
+
react.useEffect(() => {
|
|
979
|
+
if (!readyFired.current) {
|
|
980
|
+
readyFired.current = true;
|
|
981
|
+
onReady?.();
|
|
982
|
+
}
|
|
983
|
+
}, [onReady]);
|
|
984
|
+
const formatCardNumber = (val) => {
|
|
985
|
+
const digits = val.replace(/\D/g, "").slice(0, 16);
|
|
986
|
+
return digits.replace(/(.{4})/g, "$1 ").trim();
|
|
987
|
+
};
|
|
988
|
+
const formatExpiry = (val) => {
|
|
989
|
+
const digits = val.replace(/\D/g, "").slice(0, 4);
|
|
990
|
+
if (digits.length > 2) return digits.slice(0, 2) + " / " + digits.slice(2);
|
|
991
|
+
return digits;
|
|
992
|
+
};
|
|
993
|
+
const embeddedInputStyle = (focused) => ({
|
|
994
|
+
width: "100%",
|
|
995
|
+
padding: "10px 12px",
|
|
996
|
+
border: "none",
|
|
997
|
+
borderBottom: `1px solid ${focused ? "#666" : "#e0e0e0"}`,
|
|
998
|
+
fontSize: "14px",
|
|
999
|
+
fontFamily: FONT,
|
|
1000
|
+
outline: "none",
|
|
1001
|
+
backgroundColor: "#fff",
|
|
1002
|
+
boxSizing: "border-box"
|
|
1003
|
+
});
|
|
1004
|
+
const displayPrice = product?.hasTrial ? product.trialPrice : product?.price;
|
|
1005
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { fontFamily: FONT, border: "1px solid #e0e0e0", borderRadius: "12px", overflow: "hidden", background: "#fff" }, children: [
|
|
1006
|
+
product && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { padding: "24px 20px", borderBottom: "1px solid #e0e0e0" }, children: [
|
|
1007
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { fontSize: "14px", color: "#666", marginBottom: "4px" }, children: [
|
|
1008
|
+
"Pay ",
|
|
1009
|
+
product.displayName || product.name
|
|
1010
|
+
] }),
|
|
1011
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { fontSize: "28px", fontWeight: 700, color: "#333", marginBottom: "4px" }, children: [
|
|
1012
|
+
product.currencyCode?.toUpperCase(),
|
|
1013
|
+
displayPrice
|
|
1014
|
+
] }),
|
|
1015
|
+
product.hasTrial && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { fontSize: "13px", color: "#666" }, children: [
|
|
1016
|
+
"Then ",
|
|
1017
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { style: { textDecoration: "underline" }, children: [
|
|
1018
|
+
product.currencyCode?.toUpperCase(),
|
|
1019
|
+
product.price
|
|
1020
|
+
] }),
|
|
1021
|
+
" every ",
|
|
1022
|
+
product.period
|
|
1023
|
+
] }),
|
|
1024
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1025
|
+
"button",
|
|
1026
|
+
{
|
|
1027
|
+
type: "button",
|
|
1028
|
+
style: {
|
|
1029
|
+
marginTop: "12px",
|
|
1030
|
+
padding: "6px 14px",
|
|
1031
|
+
borderRadius: "6px",
|
|
1032
|
+
border: "1px solid #e0e0e0",
|
|
1033
|
+
backgroundColor: "#fff",
|
|
1034
|
+
color: "#333",
|
|
1035
|
+
fontSize: "13px",
|
|
1036
|
+
cursor: "default",
|
|
1037
|
+
fontFamily: FONT
|
|
1038
|
+
},
|
|
1039
|
+
children: "View details \u25BE"
|
|
1040
|
+
}
|
|
1041
|
+
)
|
|
1042
|
+
] }),
|
|
1043
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { padding: "20px" }, children: [
|
|
1044
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { marginBottom: "16px" }, children: [
|
|
1045
|
+
/* @__PURE__ */ jsxRuntime.jsx("label", { style: { ...labelStyle, fontSize: "13px", color: "#666" }, children: "Email" }),
|
|
1046
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1047
|
+
"input",
|
|
1048
|
+
{
|
|
1049
|
+
type: "email",
|
|
1050
|
+
value: email,
|
|
1051
|
+
onChange: (e) => setEmail(e.target.value),
|
|
1052
|
+
onFocus: () => setFocusedField("email"),
|
|
1053
|
+
onBlur: () => setFocusedField(null),
|
|
1054
|
+
placeholder: "you@example.com",
|
|
1055
|
+
style: embeddedInputStyle(focusedField === "email")
|
|
1056
|
+
}
|
|
1057
|
+
)
|
|
1058
|
+
] }),
|
|
1059
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { marginBottom: "16px" }, children: [
|
|
1060
|
+
/* @__PURE__ */ jsxRuntime.jsx("label", { style: { ...labelStyle, fontSize: "13px", color: "#666" }, children: "Card information" }),
|
|
1061
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { border: "1px solid #e0e0e0", borderRadius: "6px", overflow: "hidden" }, children: [
|
|
1062
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { position: "relative" }, children: [
|
|
1063
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1064
|
+
"input",
|
|
1065
|
+
{
|
|
1066
|
+
type: "text",
|
|
1067
|
+
value: cardNumber,
|
|
1068
|
+
onChange: (e) => setCardNumber(formatCardNumber(e.target.value)),
|
|
1069
|
+
onFocus: () => setFocusedField("card"),
|
|
1070
|
+
onBlur: () => setFocusedField(null),
|
|
1071
|
+
placeholder: "1234 1234 1234 1234",
|
|
1072
|
+
style: { ...embeddedInputStyle(focusedField === "card"), padding: "12px" }
|
|
1073
|
+
}
|
|
1074
|
+
),
|
|
1075
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { style: { position: "absolute", right: "12px", top: "50%", transform: "translateY(-50%)" }, children: /* @__PURE__ */ jsxRuntime.jsx(CardBrandBadges, {}) })
|
|
1076
|
+
] }),
|
|
1077
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "grid", gridTemplateColumns: "1fr 1fr" }, children: [
|
|
1078
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1079
|
+
"input",
|
|
1080
|
+
{
|
|
1081
|
+
type: "text",
|
|
1082
|
+
value: expiry,
|
|
1083
|
+
onChange: (e) => setExpiry(formatExpiry(e.target.value)),
|
|
1084
|
+
onFocus: () => setFocusedField("expiry"),
|
|
1085
|
+
onBlur: () => setFocusedField(null),
|
|
1086
|
+
placeholder: "MM / YY",
|
|
1087
|
+
style: { ...embeddedInputStyle(focusedField === "expiry"), borderRight: "1px solid #e0e0e0", padding: "12px" }
|
|
1088
|
+
}
|
|
1089
|
+
),
|
|
1090
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { position: "relative" }, children: [
|
|
1091
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1092
|
+
"input",
|
|
1093
|
+
{
|
|
1094
|
+
type: "text",
|
|
1095
|
+
value: cvc,
|
|
1096
|
+
onChange: (e) => setCvc(e.target.value.replace(/\D/g, "").slice(0, 4)),
|
|
1097
|
+
onFocus: () => setFocusedField("cvc"),
|
|
1098
|
+
onBlur: () => setFocusedField(null),
|
|
1099
|
+
placeholder: "CVC",
|
|
1100
|
+
style: { ...embeddedInputStyle(focusedField === "cvc"), padding: "12px" }
|
|
1101
|
+
}
|
|
1102
|
+
),
|
|
1103
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { style: { position: "absolute", right: "12px", top: "50%", transform: "translateY(-50%)" }, children: /* @__PURE__ */ jsxRuntime.jsx(CvcIcon, {}) })
|
|
1104
|
+
] })
|
|
1105
|
+
] })
|
|
1106
|
+
] })
|
|
1107
|
+
] }),
|
|
1108
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { marginBottom: "16px" }, children: [
|
|
1109
|
+
/* @__PURE__ */ jsxRuntime.jsx("label", { style: { ...labelStyle, fontSize: "13px", color: "#666" }, children: "Cardholder name" }),
|
|
1110
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1111
|
+
"input",
|
|
1112
|
+
{
|
|
1113
|
+
type: "text",
|
|
1114
|
+
value: name,
|
|
1115
|
+
onChange: (e) => setName(e.target.value),
|
|
1116
|
+
onFocus: () => setFocusedField("name"),
|
|
1117
|
+
onBlur: () => setFocusedField(null),
|
|
1118
|
+
placeholder: "Full name on card",
|
|
1119
|
+
style: inputStyle(focusedField === "name")
|
|
1120
|
+
}
|
|
1121
|
+
)
|
|
1122
|
+
] }),
|
|
1123
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { marginBottom: "20px" }, children: [
|
|
1124
|
+
/* @__PURE__ */ jsxRuntime.jsx("label", { style: { ...labelStyle, fontSize: "13px", color: "#666" }, children: "Country or region" }),
|
|
1125
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
1126
|
+
"select",
|
|
1127
|
+
{
|
|
1128
|
+
value: country,
|
|
1129
|
+
onChange: (e) => setCountry(e.target.value),
|
|
1130
|
+
onFocus: () => setFocusedField("country"),
|
|
1131
|
+
onBlur: () => setFocusedField(null),
|
|
1132
|
+
style: {
|
|
1133
|
+
...inputStyle(focusedField === "country"),
|
|
1134
|
+
appearance: "none",
|
|
1135
|
+
backgroundImage: `url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='%23666' viewBox='0 0 16 16'%3E%3Cpath d='M8 11L3 6h10z'/%3E%3C/svg%3E")`,
|
|
1136
|
+
backgroundRepeat: "no-repeat",
|
|
1137
|
+
backgroundPosition: "right 12px center",
|
|
1138
|
+
paddingRight: "32px"
|
|
1139
|
+
},
|
|
1140
|
+
children: [
|
|
1141
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { children: "Australia" }),
|
|
1142
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { children: "Austria" }),
|
|
1143
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { children: "Belgium" }),
|
|
1144
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { children: "Brazil" }),
|
|
1145
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { children: "Canada" }),
|
|
1146
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { children: "Denmark" }),
|
|
1147
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { children: "Finland" }),
|
|
1148
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { children: "France" }),
|
|
1149
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { children: "Germany" }),
|
|
1150
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { children: "Ireland" }),
|
|
1151
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { children: "Italy" }),
|
|
1152
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { children: "Japan" }),
|
|
1153
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { children: "Netherlands" }),
|
|
1154
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { children: "New Zealand" }),
|
|
1155
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { children: "Norway" }),
|
|
1156
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { children: "Poland" }),
|
|
1157
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { children: "Portugal" }),
|
|
1158
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { children: "Spain" }),
|
|
1159
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { children: "Sweden" }),
|
|
1160
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { children: "Switzerland" }),
|
|
1161
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { children: "United Kingdom" }),
|
|
1162
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { children: "United States" })
|
|
1163
|
+
]
|
|
1164
|
+
}
|
|
1165
|
+
)
|
|
1166
|
+
] }),
|
|
1167
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1168
|
+
"button",
|
|
1169
|
+
{
|
|
1170
|
+
type: "button",
|
|
1171
|
+
disabled: true,
|
|
1172
|
+
style: {
|
|
1173
|
+
width: "100%",
|
|
1174
|
+
padding: "14px",
|
|
1175
|
+
borderRadius: "6px",
|
|
1176
|
+
border: "none",
|
|
1177
|
+
backgroundColor: "#0570de",
|
|
1178
|
+
color: "#fff",
|
|
1179
|
+
fontSize: "16px",
|
|
1180
|
+
fontWeight: 600,
|
|
1181
|
+
cursor: "default",
|
|
1182
|
+
fontFamily: FONT,
|
|
1183
|
+
opacity: 0.7
|
|
1184
|
+
},
|
|
1185
|
+
children: "Subscribe"
|
|
1186
|
+
}
|
|
1187
|
+
),
|
|
1188
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { textAlign: "center", marginTop: "12px", fontSize: "12px", color: "#aaa" }, children: [
|
|
1189
|
+
"Powered by ",
|
|
1190
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontWeight: 700, letterSpacing: "-0.3px" }, children: "stripe" })
|
|
1191
|
+
] })
|
|
1192
|
+
] })
|
|
1193
|
+
] });
|
|
1194
|
+
}
|
|
1195
|
+
var DemoStripePaymentForm = react.forwardRef(
|
|
1196
|
+
function DemoStripePaymentForm2({
|
|
1197
|
+
productId,
|
|
1198
|
+
mode = "checkout",
|
|
1199
|
+
variant = "elements",
|
|
1200
|
+
onSuccess,
|
|
1201
|
+
onError,
|
|
1202
|
+
onReady,
|
|
1203
|
+
className
|
|
1204
|
+
}, ref) {
|
|
1205
|
+
const { variableStore, tracker, products } = chunkEVUYCLVY_cjs.useFunnelContext();
|
|
1206
|
+
const validateOnly = mode === "validate-only";
|
|
1207
|
+
const product = react.useMemo(() => {
|
|
1208
|
+
if (productId) return products.find((p) => p.id === productId) || null;
|
|
1209
|
+
const selectedId = variableStore.get("products.selectedProductId");
|
|
1210
|
+
return products.find((p) => p.id === selectedId) || null;
|
|
1211
|
+
}, [productId, products, variableStore]);
|
|
1212
|
+
const hasFiredStart = react.useRef(false);
|
|
1213
|
+
react.useEffect(() => {
|
|
1214
|
+
if (hasFiredStart.current) return;
|
|
1215
|
+
hasFiredStart.current = true;
|
|
1216
|
+
variableStore.set("payment.loading", false);
|
|
1217
|
+
tracker.track("checkout.start", {
|
|
1218
|
+
productId: product?.id,
|
|
1219
|
+
priceId: product?.stripePriceId || product?.storePriceId,
|
|
1220
|
+
productName: product?.displayName
|
|
1221
|
+
});
|
|
1222
|
+
}, [product, tracker, variableStore]);
|
|
1223
|
+
const handleSubmit = react.useCallback(async () => {
|
|
1224
|
+
variableStore.set("payment.loading", true);
|
|
1225
|
+
await new Promise((r) => setTimeout(r, 800));
|
|
1226
|
+
try {
|
|
1227
|
+
tracker.track("checkout.payment_added");
|
|
1228
|
+
if (validateOnly) {
|
|
1229
|
+
variableStore.setMany({
|
|
1230
|
+
"card.last4": "4242",
|
|
1231
|
+
"card.brand": "visa",
|
|
1232
|
+
"card.expMonth": 12,
|
|
1233
|
+
"card.expYear": 2030,
|
|
1234
|
+
"card.funding": "credit",
|
|
1235
|
+
"payment.error": ""
|
|
1236
|
+
});
|
|
1237
|
+
onSuccess?.();
|
|
1238
|
+
return;
|
|
1239
|
+
}
|
|
1240
|
+
variableStore.set("payment.error", "");
|
|
1241
|
+
tracker.track("purchase.complete", {
|
|
1242
|
+
amount: product?.rawPrice ? product.rawPrice / 100 : 0,
|
|
1243
|
+
currency: product?.currencyCode || "USD"
|
|
1244
|
+
});
|
|
1245
|
+
onSuccess?.();
|
|
1246
|
+
} catch (err) {
|
|
1247
|
+
const msg = err instanceof Error ? err.message : "An error occurred";
|
|
1248
|
+
variableStore.set("payment.error", msg);
|
|
1249
|
+
onError?.(msg);
|
|
1250
|
+
} finally {
|
|
1251
|
+
variableStore.set("payment.loading", false);
|
|
1252
|
+
}
|
|
1253
|
+
}, [validateOnly, variableStore, tracker, product, onSuccess, onError]);
|
|
1254
|
+
react.useImperativeHandle(ref, () => ({ submit: handleSubmit }), [handleSubmit]);
|
|
1255
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className, children: variant === "embedded" ? /* @__PURE__ */ jsxRuntime.jsx(DemoEmbeddedForm, { product, onReady }) : /* @__PURE__ */ jsxRuntime.jsx(DemoElementsForm, { onReady }) });
|
|
1256
|
+
}
|
|
1257
|
+
);
|
|
1258
|
+
var API_BASE_URL2 = "https://api.appfunnel.net";
|
|
1259
|
+
var isDevMode = () => typeof globalThis !== "undefined" && !!globalThis.__APPFUNNEL_DEV__;
|
|
472
1260
|
var InnerPaymentForm = react.forwardRef(
|
|
473
1261
|
function InnerPaymentForm2({ paymentMode, validateOnly, onSuccess, onError, onReady, layout }, ref) {
|
|
474
1262
|
const stripe = reactStripeJs.useStripe();
|
|
475
1263
|
const elements = reactStripeJs.useElements();
|
|
476
1264
|
const [error, setError] = react.useState(null);
|
|
477
|
-
const { variableStore, campaignId, tracker, products } =
|
|
1265
|
+
const { variableStore, campaignId, tracker, products } = chunkEVUYCLVY_cjs.useFunnelContext();
|
|
478
1266
|
const readyFired = react.useRef(false);
|
|
479
1267
|
react.useEffect(() => {
|
|
480
1268
|
if (stripe && elements && !readyFired.current) {
|
|
@@ -516,7 +1304,7 @@ var InnerPaymentForm = react.forwardRef(
|
|
|
516
1304
|
return;
|
|
517
1305
|
}
|
|
518
1306
|
const response2 = await fetch(
|
|
519
|
-
`${
|
|
1307
|
+
`${API_BASE_URL2}/campaign/${campaignId}/stripe/validate-card`,
|
|
520
1308
|
{
|
|
521
1309
|
method: "POST",
|
|
522
1310
|
headers: { "Content-Type": "application/json" },
|
|
@@ -557,7 +1345,7 @@ var InnerPaymentForm = react.forwardRef(
|
|
|
557
1345
|
return;
|
|
558
1346
|
}
|
|
559
1347
|
const response = await fetch(
|
|
560
|
-
`${
|
|
1348
|
+
`${API_BASE_URL2}/campaign/${campaignId}/stripe/purchase`,
|
|
561
1349
|
{
|
|
562
1350
|
method: "POST",
|
|
563
1351
|
headers: { "Content-Type": "application/json" },
|
|
@@ -602,8 +1390,8 @@ var InnerPaymentForm = react.forwardRef(
|
|
|
602
1390
|
] });
|
|
603
1391
|
}
|
|
604
1392
|
);
|
|
605
|
-
var
|
|
606
|
-
function
|
|
1393
|
+
var RealStripePaymentForm = react.forwardRef(
|
|
1394
|
+
function RealStripePaymentForm2({
|
|
607
1395
|
productId,
|
|
608
1396
|
mode = "checkout",
|
|
609
1397
|
variant = "elements",
|
|
@@ -614,7 +1402,7 @@ var StripePaymentForm = react.forwardRef(
|
|
|
614
1402
|
appearance,
|
|
615
1403
|
layout
|
|
616
1404
|
}, ref) {
|
|
617
|
-
const { campaignId, tracker, variableStore, products } =
|
|
1405
|
+
const { campaignId, tracker, variableStore, products } = chunkEVUYCLVY_cjs.useFunnelContext();
|
|
618
1406
|
const [email] = useVariable("user.email");
|
|
619
1407
|
const validateOnly = mode === "validate-only";
|
|
620
1408
|
const product = react.useMemo(() => {
|
|
@@ -642,7 +1430,7 @@ var StripePaymentForm = react.forwardRef(
|
|
|
642
1430
|
try {
|
|
643
1431
|
if (variant === "embedded") {
|
|
644
1432
|
const response = await fetch(
|
|
645
|
-
`${
|
|
1433
|
+
`${API_BASE_URL2}/campaign/${campaignId}/stripe/checkout-session`,
|
|
646
1434
|
{
|
|
647
1435
|
method: "POST",
|
|
648
1436
|
headers: { "Content-Type": "application/json" },
|
|
@@ -668,7 +1456,7 @@ var StripePaymentForm = react.forwardRef(
|
|
|
668
1456
|
priceId: product.storePriceId
|
|
669
1457
|
};
|
|
670
1458
|
if (validateOnly) body.validateOnly = true;
|
|
671
|
-
const response = await fetch(`${
|
|
1459
|
+
const response = await fetch(`${API_BASE_URL2}${endpoint}`, {
|
|
672
1460
|
method: "POST",
|
|
673
1461
|
headers: { "Content-Type": "application/json" },
|
|
674
1462
|
body: JSON.stringify(body)
|
|
@@ -744,6 +1532,14 @@ var StripePaymentForm = react.forwardRef(
|
|
|
744
1532
|
) }) });
|
|
745
1533
|
}
|
|
746
1534
|
);
|
|
1535
|
+
var StripePaymentForm = react.forwardRef(
|
|
1536
|
+
function StripePaymentForm2(props, ref) {
|
|
1537
|
+
if (isDevMode()) {
|
|
1538
|
+
return /* @__PURE__ */ jsxRuntime.jsx(DemoStripePaymentForm, { ref, ...props });
|
|
1539
|
+
}
|
|
1540
|
+
return /* @__PURE__ */ jsxRuntime.jsx(RealStripePaymentForm, { ref, ...props });
|
|
1541
|
+
}
|
|
1542
|
+
);
|
|
747
1543
|
function PaddleCheckout({
|
|
748
1544
|
productId,
|
|
749
1545
|
mode = "overlay",
|
|
@@ -751,7 +1547,7 @@ function PaddleCheckout({
|
|
|
751
1547
|
onError,
|
|
752
1548
|
className
|
|
753
1549
|
}) {
|
|
754
|
-
const { variableStore, tracker, products } =
|
|
1550
|
+
const { variableStore, tracker, products } = chunkEVUYCLVY_cjs.useFunnelContext();
|
|
755
1551
|
const containerRef = react.useRef(null);
|
|
756
1552
|
const initializedRef = react.useRef(false);
|
|
757
1553
|
const product = productId ? products.find((p) => p.id === productId) : products.find((p) => p.id === variableStore.get("products.selectedProductId"));
|
|
@@ -808,30 +1604,42 @@ function PaddleCheckout({
|
|
|
808
1604
|
return null;
|
|
809
1605
|
}
|
|
810
1606
|
|
|
1607
|
+
Object.defineProperty(exports, "useNavigation", {
|
|
1608
|
+
enumerable: true,
|
|
1609
|
+
get: function () { return chunkXP44I2MU_cjs.useNavigation; }
|
|
1610
|
+
});
|
|
1611
|
+
Object.defineProperty(exports, "useResponse", {
|
|
1612
|
+
enumerable: true,
|
|
1613
|
+
get: function () { return chunkXP44I2MU_cjs.useResponse; }
|
|
1614
|
+
});
|
|
1615
|
+
Object.defineProperty(exports, "useResponses", {
|
|
1616
|
+
enumerable: true,
|
|
1617
|
+
get: function () { return chunkXP44I2MU_cjs.useResponses; }
|
|
1618
|
+
});
|
|
811
1619
|
Object.defineProperty(exports, "FunnelProvider", {
|
|
812
1620
|
enumerable: true,
|
|
813
|
-
get: function () { return
|
|
1621
|
+
get: function () { return chunkEVUYCLVY_cjs.FunnelProvider; }
|
|
814
1622
|
});
|
|
815
1623
|
Object.defineProperty(exports, "registerIntegration", {
|
|
816
1624
|
enumerable: true,
|
|
817
|
-
get: function () { return
|
|
1625
|
+
get: function () { return chunkEVUYCLVY_cjs.registerIntegration; }
|
|
818
1626
|
});
|
|
819
1627
|
exports.PaddleCheckout = PaddleCheckout;
|
|
820
1628
|
exports.StripePaymentForm = StripePaymentForm;
|
|
821
1629
|
exports.defineConfig = defineConfig;
|
|
822
1630
|
exports.definePage = definePage;
|
|
823
1631
|
exports.useData = useData;
|
|
1632
|
+
exports.useDateOfBirth = useDateOfBirth;
|
|
824
1633
|
exports.useDeviceInfo = useDeviceInfo;
|
|
825
1634
|
exports.useFunnel = useFunnel;
|
|
1635
|
+
exports.useKeyboard = useKeyboard;
|
|
826
1636
|
exports.useLocale = useLocale;
|
|
827
|
-
exports.useNavigation = useNavigation;
|
|
828
1637
|
exports.usePageData = usePageData;
|
|
829
1638
|
exports.usePayment = usePayment;
|
|
830
1639
|
exports.useProducts = useProducts;
|
|
831
1640
|
exports.useQueryParam = useQueryParam;
|
|
832
1641
|
exports.useQueryParams = useQueryParams;
|
|
833
|
-
exports.
|
|
834
|
-
exports.useResponses = useResponses;
|
|
1642
|
+
exports.useSafeArea = useSafeArea;
|
|
835
1643
|
exports.useTracking = useTracking;
|
|
836
1644
|
exports.useTranslation = useTranslation;
|
|
837
1645
|
exports.useUser = useUser;
|