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