@appfunnel-dev/sdk 0.5.0 → 0.6.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/index.cjs CHANGED
@@ -1,9 +1,10 @@
1
1
  'use strict';
2
2
 
3
+ var chunkBUF5FDKC_cjs = require('./chunk-BUF5FDKC.cjs');
3
4
  var react = require('react');
4
- var jsxRuntime = require('react/jsx-runtime');
5
5
  var stripeJs = require('@stripe/stripe-js');
6
6
  var reactStripeJs = require('@stripe/react-stripe-js');
7
+ var jsxRuntime = require('react/jsx-runtime');
7
8
 
8
9
  // src/config.ts
9
10
  function defineConfig(config) {
@@ -12,23 +13,11 @@ function defineConfig(config) {
12
13
  function definePage(definition) {
13
14
  return definition;
14
15
  }
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
16
  function useVariable(id) {
28
- const { variableStore } = useFunnelContext();
17
+ const { variableStore } = chunkBUF5FDKC_cjs.useFunnelContext();
29
18
  const subscribe = react.useCallback(
30
- (callback) => variableStore.subscribe(callback),
31
- [variableStore]
19
+ (callback) => variableStore.subscribe(callback, { keys: [id] }),
20
+ [variableStore, id]
32
21
  );
33
22
  const getSnapshot = react.useCallback(
34
23
  () => variableStore.get(id),
@@ -42,7 +31,7 @@ function useVariable(id) {
42
31
  return [value, setValue];
43
32
  }
44
33
  function useVariables() {
45
- const { variableStore } = useFunnelContext();
34
+ const { variableStore } = chunkBUF5FDKC_cjs.useFunnelContext();
46
35
  const subscribe = react.useCallback(
47
36
  (callback) => variableStore.subscribe(callback),
48
37
  [variableStore]
@@ -54,9 +43,9 @@ function useVariables() {
54
43
  return react.useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
55
44
  }
56
45
  function useUser() {
57
- const { variableStore, tracker } = useFunnelContext();
46
+ const { variableStore } = chunkBUF5FDKC_cjs.useFunnelContext();
58
47
  const subscribe = react.useCallback(
59
- (cb) => variableStore.subscribe(cb),
48
+ (cb) => variableStore.subscribe(cb, { prefix: "user." }),
60
49
  [variableStore]
61
50
  );
62
51
  const getSnapshot = react.useCallback(
@@ -73,7 +62,6 @@ function useUser() {
73
62
  dateOfBirth: variables["user.dateOfBirth"] || "",
74
63
  setEmail(email) {
75
64
  variableStore.set("user.email", email);
76
- tracker.identify(email);
77
65
  },
78
66
  setName(name) {
79
67
  variableStore.set("user.name", name);
@@ -82,15 +70,15 @@ function useUser() {
82
70
  variableStore.set("user.dateOfBirth", dateOfBirth);
83
71
  }
84
72
  }),
85
- [variables, variableStore, tracker]
73
+ [variables, variableStore]
86
74
  );
87
75
  }
88
76
  function useUserProperty(field) {
89
- const { variableStore } = useFunnelContext();
77
+ const { variableStore } = chunkBUF5FDKC_cjs.useFunnelContext();
90
78
  const key = `user.${field}`;
91
79
  const subscribe = react.useCallback(
92
- (cb) => variableStore.subscribe(cb),
93
- [variableStore]
80
+ (cb) => variableStore.subscribe(cb, { keys: [key] }),
81
+ [variableStore, key]
94
82
  );
95
83
  const getSnapshot = react.useCallback(
96
84
  () => variableStore.get(key) || "",
@@ -104,11 +92,11 @@ function useUserProperty(field) {
104
92
  return [value, setValue];
105
93
  }
106
94
  function useResponse(key) {
107
- const { variableStore } = useFunnelContext();
95
+ const { variableStore } = chunkBUF5FDKC_cjs.useFunnelContext();
108
96
  const prefixedKey = `answers.${key}`;
109
97
  const subscribe = react.useCallback(
110
- (cb) => variableStore.subscribe(cb),
111
- [variableStore]
98
+ (cb) => variableStore.subscribe(cb, { keys: [prefixedKey] }),
99
+ [variableStore, prefixedKey]
112
100
  );
113
101
  const getSnapshot = react.useCallback(
114
102
  () => variableStore.get(prefixedKey),
@@ -122,9 +110,9 @@ function useResponse(key) {
122
110
  return [value, setValue];
123
111
  }
124
112
  function useResponses() {
125
- const { variableStore } = useFunnelContext();
113
+ const { variableStore } = chunkBUF5FDKC_cjs.useFunnelContext();
126
114
  const subscribe = react.useCallback(
127
- (cb) => variableStore.subscribe(cb),
115
+ (cb) => variableStore.subscribe(cb, { prefix: "answers." }),
128
116
  [variableStore]
129
117
  );
130
118
  const getSnapshot = react.useCallback(
@@ -143,9 +131,9 @@ function useResponses() {
143
131
  }, [variables]);
144
132
  }
145
133
  function useQueryParams() {
146
- const { variableStore } = useFunnelContext();
134
+ const { variableStore } = chunkBUF5FDKC_cjs.useFunnelContext();
147
135
  const subscribe = react.useCallback(
148
- (cb) => variableStore.subscribe(cb),
136
+ (cb) => variableStore.subscribe(cb, { prefix: "query." }),
149
137
  [variableStore]
150
138
  );
151
139
  const getSnapshot = react.useCallback(
@@ -164,11 +152,11 @@ function useQueryParams() {
164
152
  }, [variables]);
165
153
  }
166
154
  function useQueryParam(key) {
167
- const { variableStore } = useFunnelContext();
155
+ const { variableStore } = chunkBUF5FDKC_cjs.useFunnelContext();
168
156
  const prefixedKey = `query.${key}`;
169
157
  const subscribe = react.useCallback(
170
- (cb) => variableStore.subscribe(cb),
171
- [variableStore]
158
+ (cb) => variableStore.subscribe(cb, { keys: [prefixedKey] }),
159
+ [variableStore, prefixedKey]
172
160
  );
173
161
  const getSnapshot = react.useCallback(
174
162
  () => variableStore.get(prefixedKey) || "",
@@ -177,11 +165,11 @@ function useQueryParam(key) {
177
165
  return react.useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
178
166
  }
179
167
  function useData(key) {
180
- const { variableStore } = useFunnelContext();
168
+ const { variableStore } = chunkBUF5FDKC_cjs.useFunnelContext();
181
169
  const prefixedKey = `data.${key}`;
182
170
  const subscribe = react.useCallback(
183
- (cb) => variableStore.subscribe(cb),
184
- [variableStore]
171
+ (cb) => variableStore.subscribe(cb, { keys: [prefixedKey] }),
172
+ [variableStore, prefixedKey]
185
173
  );
186
174
  const getSnapshot = react.useCallback(
187
175
  () => variableStore.get(prefixedKey),
@@ -230,7 +218,7 @@ function detect24Hour(locale) {
230
218
  }
231
219
  }
232
220
  function useTranslation() {
233
- const { i18n } = useFunnelContext();
221
+ const { i18n } = chunkBUF5FDKC_cjs.useFunnelContext();
234
222
  const subscribe = react.useCallback(
235
223
  (cb) => i18n.subscribe(cb),
236
224
  [i18n]
@@ -253,85 +241,54 @@ function useTranslation() {
253
241
  return { t, locale, setLocale, availableLocales };
254
242
  }
255
243
  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);
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]);
266
267
  const goToNextPage = react.useCallback(() => {
267
268
  const previousPage = router.getCurrentPage();
268
269
  if (previousPage) {
269
270
  tracker.stopPageTracking();
270
271
  }
272
+ const variables = variableStore.getState();
271
273
  const nextKey = router.goToNextPage(variables);
272
274
  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
- });
275
+ afterNavigate(nextKey);
289
276
  }
290
- }, [router, variables, tracker, variableStore]);
277
+ }, [router, tracker, variableStore, afterNavigate]);
291
278
  const goBack = react.useCallback(() => {
292
279
  tracker.stopPageTracking();
293
280
  const prevKey = router.goBack();
294
281
  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
- });
282
+ afterNavigate(prevKey);
311
283
  }
312
- }, [router, tracker, variableStore]);
284
+ }, [router, tracker, afterNavigate]);
313
285
  const goToPage = react.useCallback((pageKey) => {
314
286
  tracker.stopPageTracking();
315
287
  const key = router.goToPage(pageKey);
316
288
  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
- });
289
+ afterNavigate(key);
333
290
  }
334
- }, [router, tracker, variableStore]);
291
+ }, [router, tracker, afterNavigate]);
335
292
  return {
336
293
  goToNextPage,
337
294
  goBack,
@@ -342,9 +299,9 @@ function useNavigation() {
342
299
  };
343
300
  }
344
301
  function useProducts() {
345
- const { products, variableStore, selectProduct: ctxSelect } = useFunnelContext();
302
+ const { products, variableStore, selectProduct: ctxSelect } = chunkBUF5FDKC_cjs.useFunnelContext();
346
303
  const subscribe = react.useCallback(
347
- (cb) => variableStore.subscribe(cb),
304
+ (cb) => variableStore.subscribe(cb, { keys: ["products.selectedProductId"] }),
348
305
  [variableStore]
349
306
  );
350
307
  const getSnapshot = react.useCallback(
@@ -359,7 +316,7 @@ function useProducts() {
359
316
  return { products, selected, select };
360
317
  }
361
318
  function useTracking() {
362
- const { tracker } = useFunnelContext();
319
+ const { tracker } = chunkBUF5FDKC_cjs.useFunnelContext();
363
320
  const track = react.useCallback(
364
321
  (eventName, data) => {
365
322
  tracker.track(eventName, data);
@@ -374,10 +331,19 @@ function useTracking() {
374
331
  );
375
332
  return { track, identify };
376
333
  }
334
+ var PAYMENT_KEYS = [
335
+ "card.last4",
336
+ "card.brand",
337
+ "card.expMonth",
338
+ "card.expYear",
339
+ "payment.loading",
340
+ "payment.error",
341
+ "payment.customerId"
342
+ ];
377
343
  function usePayment() {
378
- const { variableStore, tracker } = useFunnelContext();
344
+ const { variableStore } = chunkBUF5FDKC_cjs.useFunnelContext();
379
345
  const subscribe = react.useCallback(
380
- (cb) => variableStore.subscribe(cb),
346
+ (cb) => variableStore.subscribe(cb, { keys: PAYMENT_KEYS }),
381
347
  [variableStore]
382
348
  );
383
349
  const getSnapshot = react.useCallback(
@@ -390,19 +356,104 @@ function usePayment() {
390
356
  const brand = variables["card.brand"] || "";
391
357
  const expMonth = variables["card.expMonth"] || 0;
392
358
  const expYear = variables["card.expYear"] || 0;
359
+ const customerId = variables["payment.customerId"] || null;
393
360
  return {
394
- customerId: tracker.getCustomerId(),
361
+ customerId,
395
362
  isAuthorized: !!last4,
396
363
  loading: !!variables["payment.loading"],
397
364
  error: variables["payment.error"] || null,
398
365
  cardDetails: last4 ? { last4, brand, expMonth, expYear } : null
399
366
  };
400
- }, [variables, tracker]);
367
+ }, [variables]);
368
+ }
369
+ var DEVICE_KEYS = [
370
+ "os.name",
371
+ "os.timezone",
372
+ "device.type",
373
+ "device.isMobile",
374
+ "device.isTablet",
375
+ "device.screenWidth",
376
+ "device.screenHeight",
377
+ "device.viewportWidth",
378
+ "device.viewportHeight",
379
+ "device.colorDepth",
380
+ "device.pixelRatio",
381
+ "browser.name",
382
+ "browser.version",
383
+ "browser.userAgent",
384
+ "browser.cookieEnabled",
385
+ "browser.online",
386
+ "browser.language"
387
+ ];
388
+ function useDeviceInfo() {
389
+ const { variableStore } = chunkBUF5FDKC_cjs.useFunnelContext();
390
+ const subscribe = react.useCallback(
391
+ (cb) => variableStore.subscribe(cb, { keys: DEVICE_KEYS }),
392
+ [variableStore]
393
+ );
394
+ const getSnapshot = react.useCallback(
395
+ () => variableStore.getState(),
396
+ [variableStore]
397
+ );
398
+ const variables = react.useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
399
+ return react.useMemo(() => ({
400
+ os: {
401
+ name: variables["os.name"] || "",
402
+ timezone: variables["os.timezone"] || ""
403
+ },
404
+ device: {
405
+ type: variables["device.type"] || "desktop",
406
+ isMobile: !!variables["device.isMobile"],
407
+ isTablet: !!variables["device.isTablet"],
408
+ screenWidth: variables["device.screenWidth"] || 0,
409
+ screenHeight: variables["device.screenHeight"] || 0,
410
+ viewportWidth: variables["device.viewportWidth"] || 0,
411
+ viewportHeight: variables["device.viewportHeight"] || 0,
412
+ colorDepth: variables["device.colorDepth"] || 24,
413
+ pixelRatio: variables["device.pixelRatio"] || 1
414
+ },
415
+ browser: {
416
+ name: variables["browser.name"] || "",
417
+ version: variables["browser.version"] || "",
418
+ userAgent: variables["browser.userAgent"] || "",
419
+ cookieEnabled: variables["browser.cookieEnabled"] !== false,
420
+ online: variables["browser.online"] !== false,
421
+ language: variables["browser.language"] || "en"
422
+ }
423
+ }), [variables]);
424
+ }
425
+ var PAGE_KEYS = [
426
+ "page.currentId",
427
+ "page.currentIndex",
428
+ "page.current",
429
+ "page.total",
430
+ "page.progressPercentage",
431
+ "page.startedAt"
432
+ ];
433
+ function usePageData() {
434
+ const { variableStore } = chunkBUF5FDKC_cjs.useFunnelContext();
435
+ const subscribe = react.useCallback(
436
+ (cb) => variableStore.subscribe(cb, { keys: PAGE_KEYS }),
437
+ [variableStore]
438
+ );
439
+ const getSnapshot = react.useCallback(
440
+ () => variableStore.getState(),
441
+ [variableStore]
442
+ );
443
+ const variables = react.useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
444
+ return react.useMemo(() => ({
445
+ currentId: variables["page.currentId"] || "",
446
+ currentIndex: variables["page.currentIndex"] || 0,
447
+ current: variables["page.current"] || 1,
448
+ total: variables["page.total"] || 0,
449
+ progressPercentage: variables["page.progressPercentage"] || 0,
450
+ startedAt: variables["page.startedAt"] || 0
451
+ }), [variables]);
401
452
  }
402
453
 
403
454
  // src/hooks/useFunnel.ts
404
455
  function useFunnel() {
405
- const { funnelId, campaignId, tracker } = useFunnelContext();
456
+ const { funnelId, campaignId, tracker } = chunkBUF5FDKC_cjs.useFunnelContext();
406
457
  return {
407
458
  funnelId,
408
459
  campaignId,
@@ -417,242 +468,282 @@ function useFunnel() {
417
468
  payment: usePayment()
418
469
  };
419
470
  }
420
- function InnerPaymentForm({
421
- paymentMode,
422
- validateOnly,
423
- onSuccess,
424
- onError
425
- }) {
426
- const stripe = reactStripeJs.useStripe();
427
- const elements = reactStripeJs.useElements();
428
- const [error, setError] = react.useState(null);
429
- const { variableStore, campaignId, tracker, apiBaseUrl, products } = useFunnelContext();
430
- const handleSubmit = react.useCallback(async () => {
431
- if (!stripe || !elements) {
432
- const msg = "Stripe not loaded";
433
- setError(msg);
434
- onError?.(msg);
435
- return;
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";
471
+ var API_BASE_URL = "https://api.appfunnel.net";
472
+ var InnerPaymentForm = react.forwardRef(
473
+ function InnerPaymentForm2({ paymentMode, validateOnly, onSuccess, onError, onReady, layout }, ref) {
474
+ const stripe = reactStripeJs.useStripe();
475
+ const elements = reactStripeJs.useElements();
476
+ const [error, setError] = react.useState(null);
477
+ const { variableStore, campaignId, tracker, products } = chunkBUF5FDKC_cjs.useFunnelContext();
478
+ const readyFired = react.useRef(false);
479
+ react.useEffect(() => {
480
+ if (stripe && elements && !readyFired.current) {
481
+ readyFired.current = true;
482
+ onReady?.();
483
+ }
484
+ }, [stripe, elements, onReady]);
485
+ const handleSubmit = react.useCallback(async () => {
486
+ if (!stripe || !elements) {
487
+ const msg = "Stripe not loaded";
448
488
  setError(msg);
449
- variableStore.set("payment.error", msg);
450
489
  onError?.(msg);
451
490
  return;
452
491
  }
453
- tracker.track("checkout.payment_added");
454
- if (validateOnly) {
455
- const piId = "paymentIntent" in confirmResult ? confirmResult.paymentIntent : void 0;
456
- if (!piId?.id) {
457
- const msg = "PaymentIntent not found after confirmation";
492
+ setError(null);
493
+ variableStore.set("payment.loading", true);
494
+ try {
495
+ const confirmFn = paymentMode === "setup" ? stripe.confirmSetup : stripe.confirmPayment;
496
+ const confirmResult = await confirmFn({
497
+ elements,
498
+ redirect: "if_required",
499
+ confirmParams: { return_url: window.location.href }
500
+ });
501
+ if (confirmResult.error) {
502
+ const msg = confirmResult.error.message || "Payment failed";
458
503
  setError(msg);
459
504
  variableStore.set("payment.error", msg);
460
505
  onError?.(msg);
461
506
  return;
462
507
  }
463
- const response2 = await fetch(
464
- `${apiBaseUrl}/campaign/${campaignId}/stripe/validate-card`,
508
+ tracker.track("checkout.payment_added");
509
+ if (validateOnly) {
510
+ const piId = "paymentIntent" in confirmResult ? confirmResult.paymentIntent : void 0;
511
+ if (!piId?.id) {
512
+ const msg = "PaymentIntent not found after confirmation";
513
+ setError(msg);
514
+ variableStore.set("payment.error", msg);
515
+ onError?.(msg);
516
+ return;
517
+ }
518
+ const response2 = await fetch(
519
+ `${API_BASE_URL}/campaign/${campaignId}/stripe/validate-card`,
520
+ {
521
+ method: "POST",
522
+ headers: { "Content-Type": "application/json" },
523
+ body: JSON.stringify({
524
+ campaignId,
525
+ sessionId: tracker.getSessionId(),
526
+ paymentIntentId: piId.id
527
+ })
528
+ }
529
+ );
530
+ const result2 = await response2.json();
531
+ if (!result2.success) {
532
+ const msg = result2.error || "Card validation failed";
533
+ setError(msg);
534
+ variableStore.set("payment.error", msg);
535
+ onError?.(msg);
536
+ return;
537
+ }
538
+ variableStore.setMany({
539
+ "card.last4": result2.card.last4,
540
+ "card.brand": result2.card.brand,
541
+ "card.expMonth": result2.card.expMonth,
542
+ "card.expYear": result2.card.expYear,
543
+ "card.funding": result2.card.funding,
544
+ "payment.error": ""
545
+ });
546
+ onSuccess?.();
547
+ return;
548
+ }
549
+ const paymentIntentId = "paymentIntent" in confirmResult ? confirmResult.paymentIntent?.id : void 0;
550
+ const selectedId = variableStore.get("products.selectedProductId");
551
+ const product = products.find((p) => p.id === selectedId);
552
+ if (!product?.stripePriceId) {
553
+ const msg = "No product selected or missing Stripe price";
554
+ setError(msg);
555
+ variableStore.set("payment.error", msg);
556
+ onError?.(msg);
557
+ return;
558
+ }
559
+ const response = await fetch(
560
+ `${API_BASE_URL}/campaign/${campaignId}/stripe/purchase`,
465
561
  {
466
562
  method: "POST",
467
563
  headers: { "Content-Type": "application/json" },
468
564
  body: JSON.stringify({
469
565
  campaignId,
470
566
  sessionId: tracker.getSessionId(),
471
- paymentIntentId: piId.id
567
+ stripePriceId: product.stripePriceId,
568
+ trialPeriodDays: product.hasTrial ? product.trialDays : void 0,
569
+ onSessionPiId: paymentMode === "payment" ? paymentIntentId : void 0
472
570
  })
473
571
  }
474
572
  );
475
- const result2 = await response2.json();
476
- if (!result2.success) {
477
- const msg = result2.error || "Card validation failed";
573
+ const result = await response.json();
574
+ if (result.success) {
575
+ variableStore.set("payment.error", "");
576
+ if (result.eventId || result.eventIds?.firstPeriod) {
577
+ tracker.track("purchase.complete", {
578
+ amount: product.rawPrice ? product.rawPrice / 100 : 0,
579
+ currency: product.currencyCode || "USD"
580
+ });
581
+ }
582
+ onSuccess?.();
583
+ } else {
584
+ const msg = result.error || "Failed to process payment";
478
585
  setError(msg);
479
586
  variableStore.set("payment.error", msg);
480
587
  onError?.(msg);
481
- return;
482
588
  }
483
- variableStore.setMany({
484
- "card.last4": result2.card.last4,
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";
589
+ } catch (err) {
590
+ const msg = err instanceof Error ? err.message : "An error occurred";
499
591
  setError(msg);
500
592
  variableStore.set("payment.error", msg);
501
593
  onError?.(msg);
502
- return;
594
+ } finally {
595
+ variableStore.set("payment.loading", false);
503
596
  }
504
- const variables = variableStore.getState();
505
- await tracker.updateUserData(variables);
506
- const response = await fetch(
507
- `${apiBaseUrl}/campaign/${campaignId}/stripe/purchase`,
508
- {
509
- method: "POST",
510
- headers: { "Content-Type": "application/json" },
511
- body: JSON.stringify({
512
- campaignId,
513
- sessionId: tracker.getSessionId(),
514
- stripePriceId: product.stripePriceId,
515
- trialPeriodDays: product.hasTrial ? product.trialDays : void 0,
516
- onSessionPiId: paymentMode === "payment" ? paymentIntentId : void 0
517
- })
518
- }
519
- );
520
- const result = await response.json();
521
- if (result.success) {
522
- variableStore.set("payment.error", "");
523
- if (result.eventId || result.eventIds?.firstPeriod) {
524
- tracker.track("purchase.complete", {
525
- amount: product.rawPrice ? product.rawPrice / 100 : 0,
526
- currency: product.currencyCode || "USD"
597
+ }, [stripe, elements, paymentMode, validateOnly, variableStore, campaignId, tracker, products, onSuccess, onError]);
598
+ react.useImperativeHandle(ref, () => ({ submit: handleSubmit }), [handleSubmit]);
599
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
600
+ /* @__PURE__ */ jsxRuntime.jsx(reactStripeJs.PaymentElement, { options: { layout: layout || "tabs" } }),
601
+ error && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { color: "#ef4444", fontSize: "14px", marginTop: "12px" }, children: error })
602
+ ] });
603
+ }
604
+ );
605
+ var StripePaymentForm = react.forwardRef(
606
+ function StripePaymentForm2({
607
+ productId,
608
+ mode = "checkout",
609
+ variant = "elements",
610
+ onSuccess,
611
+ onError,
612
+ onReady,
613
+ className,
614
+ appearance,
615
+ layout
616
+ }, ref) {
617
+ const { campaignId, tracker, variableStore, products } = chunkBUF5FDKC_cjs.useFunnelContext();
618
+ const [email] = useVariable("user.email");
619
+ const validateOnly = mode === "validate-only";
620
+ const product = react.useMemo(() => {
621
+ if (productId) return products.find((p) => p.id === productId) || null;
622
+ const selectedId = variableStore.get("products.selectedProductId");
623
+ return products.find((p) => p.id === selectedId) || null;
624
+ }, [productId, products, variableStore]);
625
+ const paymentMode = react.useMemo(() => {
626
+ if (validateOnly) return "payment";
627
+ if (product?.hasTrial && !product.paidTrial) return "setup";
628
+ return "payment";
629
+ }, [product, validateOnly]);
630
+ const [stripePromise, setStripePromise] = react.useState(null);
631
+ const [clientSecret, setClientSecret] = react.useState(null);
632
+ const [error, setError] = react.useState(null);
633
+ const [isLoading, setIsLoading] = react.useState(false);
634
+ const hasInitialized = react.useRef(false);
635
+ react.useEffect(() => {
636
+ if (!email || !campaignId || hasInitialized.current) return;
637
+ if (!product?.storePriceId) return;
638
+ hasInitialized.current = true;
639
+ setIsLoading(true);
640
+ variableStore.set("payment.loading", true);
641
+ const createIntent = async () => {
642
+ try {
643
+ if (variant === "embedded") {
644
+ const response = await fetch(
645
+ `${API_BASE_URL}/campaign/${campaignId}/stripe/checkout-session`,
646
+ {
647
+ method: "POST",
648
+ headers: { "Content-Type": "application/json" },
649
+ body: JSON.stringify({
650
+ campaignId,
651
+ sessionId: tracker.getSessionId(),
652
+ customerEmail: email,
653
+ priceId: product.storePriceId,
654
+ trialPeriodDays: product.hasTrial ? product.trialDays : void 0
655
+ })
656
+ }
657
+ );
658
+ const result = await response.json();
659
+ if (!result.success) throw new Error(result.error || "Failed to create checkout session");
660
+ setStripePromise(stripeJs.loadStripe(result.publishableKey));
661
+ setClientSecret(result.clientSecret);
662
+ } else {
663
+ const endpoint = paymentMode === "setup" ? `/campaign/${campaignId}/stripe/setup-intent` : `/campaign/${campaignId}/stripe/payment-intent`;
664
+ const body = {
665
+ campaignId,
666
+ sessionId: tracker.getSessionId(),
667
+ customerEmail: email,
668
+ priceId: product.storePriceId
669
+ };
670
+ if (validateOnly) body.validateOnly = true;
671
+ const response = await fetch(`${API_BASE_URL}${endpoint}`, {
672
+ method: "POST",
673
+ headers: { "Content-Type": "application/json" },
674
+ body: JSON.stringify(body)
675
+ });
676
+ const result = await response.json();
677
+ if (!result.success) throw new Error(result.error || "Failed to create intent");
678
+ setStripePromise(stripeJs.loadStripe(result.publishableKey));
679
+ setClientSecret(result.clientSecret);
680
+ variableStore.set("user.stripeCustomerId", result.customerId);
681
+ }
682
+ tracker.track("checkout.start", {
683
+ productId: product.id,
684
+ priceId: product.stripePriceId || product.storePriceId,
685
+ productName: product.displayName
527
686
  });
687
+ } catch (err) {
688
+ const msg = err instanceof Error ? err.message : "Failed to initialize payment";
689
+ setError(msg);
690
+ variableStore.set("payment.error", msg);
691
+ } finally {
692
+ setIsLoading(false);
693
+ variableStore.set("payment.loading", false);
528
694
  }
529
- onSuccess?.();
530
- } else {
531
- const msg = result.error || "Failed to process payment";
532
- setError(msg);
533
- variableStore.set("payment.error", msg);
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);
695
+ };
696
+ createIntent();
697
+ }, [email, campaignId, product, paymentMode, validateOnly, variant, tracker, variableStore]);
698
+ if (isLoading) {
699
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className, style: { padding: "20px", textAlign: "center", color: "#6b7280" }, children: "Loading payment form..." });
543
700
  }
544
- }, [stripe, elements, paymentMode, validateOnly, variableStore, campaignId, tracker, apiBaseUrl, products, onSuccess, onError]);
545
- react.useEffect(() => {
546
- if (typeof window !== "undefined") {
547
- window.__paymentElementSubmit = handleSubmit;
701
+ if (!email) {
702
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className, style: { padding: "20px", textAlign: "center", color: "#ef4444", fontSize: "14px" }, children: "Email is required to initialize payment" });
548
703
  }
549
- return () => {
550
- if (typeof window !== "undefined") {
551
- delete window.__paymentElementSubmit;
552
- }
553
- };
554
- }, [handleSubmit]);
555
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
556
- /* @__PURE__ */ jsxRuntime.jsx(reactStripeJs.PaymentElement, { options: { layout: "tabs" } }),
557
- error && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { color: "#ef4444", fontSize: "14px", marginTop: "12px" }, children: error })
558
- ] });
559
- }
560
- function PaymentForm({
561
- productId,
562
- mode = "checkout",
563
- onSuccess,
564
- onError,
565
- className,
566
- appearance
567
- }) {
568
- const { campaignId, tracker, variableStore, apiBaseUrl, products } = useFunnelContext();
569
- const [email] = useVariable("user.email");
570
- const validateOnly = mode === "validate-only";
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");
704
+ if (error) {
705
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className, style: { padding: "20px", textAlign: "center", color: "#ef4444", fontSize: "14px" }, children: error });
706
+ }
707
+ if (!stripePromise || !clientSecret) {
708
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className, style: { padding: "20px", textAlign: "center", color: "#6b7280" }, children: "Initializing payment..." });
709
+ }
710
+ if (variant === "embedded") {
711
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className, children: /* @__PURE__ */ jsxRuntime.jsx(
712
+ reactStripeJs.EmbeddedCheckoutProvider,
713
+ {
714
+ stripe: stripePromise,
715
+ options: {
716
+ clientSecret,
717
+ onComplete: () => {
718
+ tracker.track("purchase.complete", {
719
+ amount: product?.rawPrice ? product.rawPrice / 100 : 0,
720
+ currency: product?.currencyCode || "USD"
721
+ });
722
+ onSuccess?.();
723
+ }
724
+ },
725
+ children: /* @__PURE__ */ jsxRuntime.jsx(reactStripeJs.EmbeddedCheckout, {})
610
726
  }
611
- setStripePromise(stripeJs.loadStripe(result.publishableKey));
612
- setClientSecret(result.clientSecret);
613
- variableStore.set("user.stripeCustomerId", result.customerId);
614
- tracker.track("checkout.start", {
615
- productId: product.id,
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
- }
727
+ ) });
728
+ }
729
+ const defaultAppearance = {
730
+ theme: "stripe",
731
+ variables: { colorPrimary: "#3b82f6", borderRadius: "8px" }
627
732
  };
628
- createIntent();
629
- }, [email, campaignId, product, paymentMode, validateOnly, tracker, variableStore, apiBaseUrl]);
630
- if (isLoading) {
631
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className, style: { padding: "20px", textAlign: "center", color: "#6b7280" }, children: "Loading payment form..." });
632
- }
633
- if (!email) {
634
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className, style: { padding: "20px", textAlign: "center", color: "#ef4444", fontSize: "14px" }, children: "Email is required to initialize payment" });
635
- }
636
- if (error) {
637
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className, style: { padding: "20px", textAlign: "center", color: "#ef4444", fontSize: "14px" }, children: error });
638
- }
639
- if (!stripePromise || !clientSecret) {
640
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className, style: { padding: "20px", textAlign: "center", color: "#6b7280" }, children: "Initializing payment..." });
733
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className, children: /* @__PURE__ */ jsxRuntime.jsx(reactStripeJs.Elements, { stripe: stripePromise, options: { clientSecret, appearance: appearance || defaultAppearance }, children: /* @__PURE__ */ jsxRuntime.jsx(
734
+ InnerPaymentForm,
735
+ {
736
+ ref,
737
+ paymentMode,
738
+ validateOnly,
739
+ onSuccess,
740
+ onError,
741
+ onReady,
742
+ layout
743
+ }
744
+ ) }) });
641
745
  }
642
- const defaultAppearance = {
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
- }
746
+ );
656
747
  function PaddleCheckout({
657
748
  productId,
658
749
  mode = "overlay",
@@ -660,7 +751,7 @@ function PaddleCheckout({
660
751
  onError,
661
752
  className
662
753
  }) {
663
- const { variableStore, tracker, products } = useFunnelContext();
754
+ const { variableStore, tracker, products } = chunkBUF5FDKC_cjs.useFunnelContext();
664
755
  const containerRef = react.useRef(null);
665
756
  const initializedRef = react.useRef(false);
666
757
  const product = productId ? products.find((p) => p.id === productId) : products.find((p) => p.id === variableStore.get("products.selectedProductId"));
@@ -717,15 +808,24 @@ function PaddleCheckout({
717
808
  return null;
718
809
  }
719
810
 
811
+ Object.defineProperty(exports, "FunnelProvider", {
812
+ enumerable: true,
813
+ get: function () { return chunkBUF5FDKC_cjs.FunnelProvider; }
814
+ });
815
+ Object.defineProperty(exports, "registerIntegration", {
816
+ enumerable: true,
817
+ get: function () { return chunkBUF5FDKC_cjs.registerIntegration; }
818
+ });
720
819
  exports.PaddleCheckout = PaddleCheckout;
721
- exports.PaymentForm = PaymentForm;
820
+ exports.StripePaymentForm = StripePaymentForm;
722
821
  exports.defineConfig = defineConfig;
723
822
  exports.definePage = definePage;
724
- exports.registerIntegration = registerIntegration;
725
823
  exports.useData = useData;
824
+ exports.useDeviceInfo = useDeviceInfo;
726
825
  exports.useFunnel = useFunnel;
727
826
  exports.useLocale = useLocale;
728
827
  exports.useNavigation = useNavigation;
828
+ exports.usePageData = usePageData;
729
829
  exports.usePayment = usePayment;
730
830
  exports.useProducts = useProducts;
731
831
  exports.useQueryParam = useQueryParam;