@funnelsgrove/cli 0.1.68 → 0.1.73
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/funnel-contract-compatibility.json +58 -58
- package/package.json +4 -4
- package/template_docs/.funnelsgrove-docs.json +9 -9
- package/template_docs/docs/funnelsgrove/contracts/analytics-events.md +2 -1
- package/template_docs/docs/funnelsgrove/contracts/payments.md +6 -1
- package/template_docs/docs/funnelsgrove/migrations/step-contract-v3.md +1 -1
- package/template_docs/docs/funnelsgrove/qa/analytics.md +4 -0
- package/template_docs/docs/funnelsgrove/qa/paywall-checkout.md +3 -0
- package/template_docs/docs/funnelsgrove/steps/paywall_offer.md +4 -2
- package/template_docs/docs/funnelsgrove/steps/upsell_offer.md +3 -2
- package/template_docs/funnel-docs.config.json +1 -1
- package/template_scaffold/.funnelsgrove-docs.json +9 -9
- package/template_scaffold/.funnelsgrove-scaffold.json +16 -16
- package/template_scaffold/docs/funnelsgrove/contracts/analytics-events.md +2 -1
- package/template_scaffold/docs/funnelsgrove/contracts/payments.md +6 -1
- package/template_scaffold/docs/funnelsgrove/migrations/step-contract-v3.md +1 -1
- package/template_scaffold/docs/funnelsgrove/qa/analytics.md +4 -0
- package/template_scaffold/docs/funnelsgrove/qa/paywall-checkout.md +3 -0
- package/template_scaffold/docs/funnelsgrove/steps/paywall_offer.md +4 -2
- package/template_scaffold/docs/funnelsgrove/steps/upsell_offer.md +3 -2
- package/template_scaffold/funnel-docs.config.json +1 -1
- package/template_scaffold/package-lock.json +15 -15
- package/template_scaffold/package.json +3 -3
- package/template_scaffold/src/steps/step-32-paywall.tsx +89 -106
- package/template_scaffold/tests/funnel-agent-docs.test.ts +1 -1
- package/template_scaffold/tests/src/steps/step-32-paywall.test.ts +37 -51
|
@@ -14,7 +14,6 @@ import {
|
|
|
14
14
|
} from '@/config/billing.plans';
|
|
15
15
|
import { stepPaywallContent } from '@/steps/content/step-32-paywall.content';
|
|
16
16
|
import { useClaimbeeStepContent } from '@/steps/use-claimbee-step-content';
|
|
17
|
-
import { publicAnalyticsSdk } from '@funnelsgrove/analytics';
|
|
18
17
|
import {
|
|
19
18
|
PaymentRuntimeBoundary,
|
|
20
19
|
apiService,
|
|
@@ -31,10 +30,9 @@ import {
|
|
|
31
30
|
} from '@funnelsgrove/runtime';
|
|
32
31
|
import { getPathForStep } from '@/runtime/funnel-runtime';
|
|
33
32
|
import {
|
|
34
|
-
ApplePaySubscriptionCheckoutSlot,
|
|
35
|
-
GooglePaySubscriptionCheckoutSlot,
|
|
36
33
|
SharedCheckoutSpecialOfferDialog,
|
|
37
34
|
SharedStripeCheckoutV2Dialog,
|
|
35
|
+
StripeSubscriptionWalletSurface,
|
|
38
36
|
activateSecondPaywallDiscount,
|
|
39
37
|
advancePaywallDiscountState,
|
|
40
38
|
buildBillingDiscountCatalog,
|
|
@@ -45,12 +43,13 @@ import {
|
|
|
45
43
|
getPaywallPromoDisplayName,
|
|
46
44
|
resolvePaywallDiscountState,
|
|
47
45
|
serializePaywallDiscountState,
|
|
48
|
-
usePlatformWalletPaymentMethods,
|
|
49
46
|
useStripeSubscriptionCheckoutSession,
|
|
50
47
|
type BillingFallbackPlanConfig,
|
|
51
48
|
type BillingPlanCatalog,
|
|
52
49
|
type PaywallDiscountState,
|
|
53
50
|
type PlatformWalletPaymentMethod,
|
|
51
|
+
type StripeSubscriptionWalletSurfaceAvailability,
|
|
52
|
+
type StripeSubscriptionWalletSurfaceCheckoutInput,
|
|
54
53
|
} from '@funnelsgrove/payments';
|
|
55
54
|
import { stepSubscriptionStartedId } from '@/steps/step-33-subscription-started';
|
|
56
55
|
import { funnelManifest } from '@/config/funnel.manifest';
|
|
@@ -76,9 +75,18 @@ type StoryOption = {
|
|
|
76
75
|
backgroundColor: string;
|
|
77
76
|
};
|
|
78
77
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
78
|
+
const PAYWALL_WALLET_SURFACE_ID = 'paywall-primary';
|
|
79
|
+
|
|
80
|
+
type PaywallWalletAvailabilityState = Partial<Record<string, boolean>>;
|
|
81
|
+
|
|
82
|
+
const paywallWalletMethodClassNames: Record<PlatformWalletPaymentMethod, string> = {
|
|
83
|
+
applePay: 'paywall-v2-apple-pay-button',
|
|
84
|
+
googlePay: 'paywall-v2-google-pay-button',
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
const paywallWalletMethodWrapperClassNames: Record<PlatformWalletPaymentMethod, string> = {
|
|
88
|
+
applePay: 'paywall-v2-apple-pay-shell',
|
|
89
|
+
googlePay: 'paywall-v2-google-pay-shell',
|
|
82
90
|
};
|
|
83
91
|
|
|
84
92
|
const claimbeeStoryBackgroundColors = ['var(--color-secondary-text)', 'var(--color-primary)', 'var(--color-secondary)'] as const;
|
|
@@ -141,7 +149,6 @@ function StepPaywallRuntime({
|
|
|
141
149
|
const publishedRuntime = useFunnelRuntimeConfig();
|
|
142
150
|
const content = useClaimbeeStepContent(stepPaywallId, stepPaywallContent);
|
|
143
151
|
const supportEmail = runtimePublicConfig.supportEmail;
|
|
144
|
-
const walletPaymentMethods = usePlatformWalletPaymentMethods();
|
|
145
152
|
const publishedOfferSetRuntime = useMemo(
|
|
146
153
|
() => publishedRuntime.config && publishedRuntime.config.offerSets.length > 0
|
|
147
154
|
? resolveGeneratedOfferSetRuntime<BillingFallbackPlanConfig>({
|
|
@@ -208,10 +215,8 @@ function StepPaywallRuntime({
|
|
|
208
215
|
const [activeStoryIndex, setActiveStoryIndex] = useState(0);
|
|
209
216
|
const [openFaqIds, setOpenFaqIds] = useState<string[]>([]);
|
|
210
217
|
const [checkoutEmail, setCheckoutEmail] = useState(user.email);
|
|
211
|
-
const [paywallWalletAvailability, setPaywallWalletAvailability] =
|
|
212
|
-
|
|
213
|
-
methods: {},
|
|
214
|
-
});
|
|
218
|
+
const [paywallWalletAvailability, setPaywallWalletAvailability] =
|
|
219
|
+
useState<PaywallWalletAvailabilityState>({});
|
|
215
220
|
const paywallStepRef = useRef<HTMLDivElement | null>(null);
|
|
216
221
|
|
|
217
222
|
const activeCouponId = discountState.couponId;
|
|
@@ -363,21 +368,40 @@ function StepPaywallRuntime({
|
|
|
363
368
|
runtimeConfig: checkoutRuntimeConfig,
|
|
364
369
|
userId: user.id,
|
|
365
370
|
});
|
|
371
|
+
const walletCheckoutInput = useMemo<StripeSubscriptionWalletSurfaceCheckoutInput>(
|
|
372
|
+
() => ({
|
|
373
|
+
analyticsMetadata: checkoutAnalyticsMetadata,
|
|
374
|
+
checkoutMode,
|
|
375
|
+
couponId: checkoutPromoActive ? selectedDisplayPlan?.couponId ?? null : null,
|
|
376
|
+
customerEmail: checkoutEmail || user.email,
|
|
377
|
+
customerName: user.name,
|
|
378
|
+
displayPlan: selectedDisplayPlan,
|
|
379
|
+
plan: selectedPlan,
|
|
380
|
+
returnUrl: successReturnUrl,
|
|
381
|
+
runtimeConfig: checkoutRuntimeConfig,
|
|
382
|
+
userId: user.id,
|
|
383
|
+
}),
|
|
384
|
+
[
|
|
385
|
+
checkoutAnalyticsMetadata,
|
|
386
|
+
checkoutEmail,
|
|
387
|
+
checkoutMode,
|
|
388
|
+
checkoutPromoActive,
|
|
389
|
+
selectedDisplayPlan,
|
|
390
|
+
selectedPlan,
|
|
391
|
+
successReturnUrl,
|
|
392
|
+
user.email,
|
|
393
|
+
user.id,
|
|
394
|
+
user.name,
|
|
395
|
+
],
|
|
396
|
+
);
|
|
366
397
|
const checkoutDisabled = !checkoutSession.configured || !selectedPlan || !selectedDisplayPlan;
|
|
367
398
|
const checkoutDialogReady =
|
|
368
399
|
checkoutDialogOpen &&
|
|
369
400
|
Boolean(checkoutSession.activeClientSecret) &&
|
|
370
401
|
Boolean(checkoutSession.stripePromise) &&
|
|
371
402
|
Boolean(selectedDisplayPlan);
|
|
372
|
-
const
|
|
373
|
-
paywallWalletAvailability
|
|
374
|
-
? paywallWalletAvailability.methods
|
|
375
|
-
: {};
|
|
376
|
-
const checkoutInitialWalletAvailable = walletPaymentMethods.some(
|
|
377
|
-
(method) => currentPaywallWalletAvailability[method] === true,
|
|
378
|
-
)
|
|
379
|
-
? true
|
|
380
|
-
: null;
|
|
403
|
+
const checkoutInitialWalletAvailable =
|
|
404
|
+
paywallWalletAvailability[PAYWALL_WALLET_SURFACE_ID] ?? null;
|
|
381
405
|
const checkoutTotalValue = useMemo(() => {
|
|
382
406
|
if (!selectedDisplayPlan) {
|
|
383
407
|
return '';
|
|
@@ -468,31 +492,22 @@ function StepPaywallRuntime({
|
|
|
468
492
|
);
|
|
469
493
|
}, [discountStateReady, serializedDiscountSnapshot]);
|
|
470
494
|
|
|
471
|
-
const
|
|
472
|
-
|
|
473
|
-
|
|
495
|
+
const handleWalletSurfaceAvailabilityChange = (
|
|
496
|
+
surfaceId: string,
|
|
497
|
+
availability: StripeSubscriptionWalletSurfaceAvailability,
|
|
474
498
|
) => {
|
|
475
|
-
setPaywallWalletAvailability((current) =>
|
|
476
|
-
|
|
477
|
-
current
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
return {
|
|
482
|
-
intentKey: checkoutSession.intentKey,
|
|
483
|
-
methods: {
|
|
484
|
-
...currentMethods,
|
|
485
|
-
[method]: available,
|
|
486
|
-
},
|
|
487
|
-
};
|
|
488
|
-
});
|
|
499
|
+
setPaywallWalletAvailability((current) =>
|
|
500
|
+
current[surfaceId] === availability.anyAvailable
|
|
501
|
+
? current
|
|
502
|
+
: { ...current, [surfaceId]: availability.anyAvailable },
|
|
503
|
+
);
|
|
489
504
|
};
|
|
490
505
|
|
|
491
|
-
const
|
|
492
|
-
|
|
506
|
+
const resolvePaywallWalletMethodClassName = (method: PlatformWalletPaymentMethod) =>
|
|
507
|
+
paywallWalletMethodClassNames[method];
|
|
493
508
|
|
|
494
|
-
const
|
|
495
|
-
|
|
509
|
+
const resolvePaywallWalletMethodWrapperClassName = (method: PlatformWalletPaymentMethod) =>
|
|
510
|
+
paywallWalletMethodWrapperClassNames[method];
|
|
496
511
|
|
|
497
512
|
const startCheckout = async () => {
|
|
498
513
|
if (checkoutDisabled || checkoutSession.loading.card) {
|
|
@@ -510,29 +525,13 @@ function StepPaywallRuntime({
|
|
|
510
525
|
}
|
|
511
526
|
};
|
|
512
527
|
|
|
513
|
-
const
|
|
514
|
-
if (!selectedDisplayPlan
|
|
515
|
-
return;
|
|
516
|
-
}
|
|
517
|
-
|
|
518
|
-
const checkoutSessionId = checkoutSession.checkoutSessionId?.trim();
|
|
519
|
-
if (!checkoutSessionId) {
|
|
528
|
+
const handleCheckoutCompleted = () => {
|
|
529
|
+
if (!selectedDisplayPlan) {
|
|
520
530
|
return;
|
|
521
531
|
}
|
|
522
|
-
|
|
523
532
|
completeStep(analyticsStepId, {
|
|
524
533
|
planId: selectedDisplayPlan.id,
|
|
525
534
|
});
|
|
526
|
-
publicAnalyticsSdk.trackPaymentInfoSubmitted({
|
|
527
|
-
eventId: `add_payment_info:${checkoutSessionId}`,
|
|
528
|
-
stepId: analyticsStepId,
|
|
529
|
-
stepName: analyticsStepName,
|
|
530
|
-
stepType: stepPaywall.type,
|
|
531
|
-
stepContractVersion: funnelManifest.stepContractVersion,
|
|
532
|
-
featureFlags,
|
|
533
|
-
metadata: checkoutAnalyticsMetadata,
|
|
534
|
-
});
|
|
535
|
-
void publicAnalyticsSdk.flush().catch(() => 0);
|
|
536
535
|
};
|
|
537
536
|
|
|
538
537
|
const preparePaywallWalletCheckout = async (): Promise<string | null> => {
|
|
@@ -676,7 +675,7 @@ function StepPaywallRuntime({
|
|
|
676
675
|
onCustomerEmailChange={setCheckoutEmail}
|
|
677
676
|
onCustomerEmailCommit={commitCheckoutEmail}
|
|
678
677
|
onError={checkoutSession.setError}
|
|
679
|
-
|
|
678
|
+
onSuccess={handleCheckoutCompleted}
|
|
680
679
|
paymentMethodLabels={content.checkout.paymentMethodLabels}
|
|
681
680
|
paypalButtonLabel={content.checkout.paypalButtonLabel}
|
|
682
681
|
paypalLabel={content.checkout.paypalLabel}
|
|
@@ -884,51 +883,28 @@ function StepPaywallRuntime({
|
|
|
884
883
|
</section>
|
|
885
884
|
|
|
886
885
|
<section className='paywall-v2-payment-stack'>
|
|
887
|
-
{
|
|
888
|
-
<
|
|
889
|
-
{selectedDisplayPlan
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
) : null}
|
|
910
|
-
{walletPaymentMethods.includes('googlePay') ? (
|
|
911
|
-
<div className='paywall-v2-google-pay-shell'>
|
|
912
|
-
{selectedDisplayPlan ? (
|
|
913
|
-
<GooglePaySubscriptionCheckoutSlot
|
|
914
|
-
amountCents={selectedDisplayPlan.amountCents}
|
|
915
|
-
beforeConfirm={preparePaywallWalletCheckout}
|
|
916
|
-
checkoutAnalytics={checkoutAnalyticsContext}
|
|
917
|
-
className='paywall-v2-google-pay-button'
|
|
918
|
-
customerEmail={checkoutEmail || user.email}
|
|
919
|
-
customerName={user.name}
|
|
920
|
-
disabled={checkoutDisabled}
|
|
921
|
-
onAvailabilityChange={handleGooglePayAvailabilityChange}
|
|
922
|
-
onPaymentInfoSubmitted={trackPaymentInfoSubmitted}
|
|
923
|
-
onUnavailableClick={openCardCheckoutFromUnavailableWallet}
|
|
924
|
-
placeholderLabel={content.checkout.googlePayPlaceholderLabel}
|
|
925
|
-
returnUrl={successReturnUrl}
|
|
926
|
-
session={checkoutSession}
|
|
927
|
-
summaryLabel={checkoutSummaryLabel}
|
|
928
|
-
suspended={checkoutDialogReady || specialOfferDialogOpen}
|
|
929
|
-
/>
|
|
930
|
-
) : null}
|
|
931
|
-
</div>
|
|
886
|
+
{selectedDisplayPlan ? (
|
|
887
|
+
<StripeSubscriptionWalletSurface
|
|
888
|
+
amountCents={selectedDisplayPlan.amountCents}
|
|
889
|
+
applePayPlaceholderLabel={content.checkout.walletPlaceholderLabel}
|
|
890
|
+
beforeConfirm={preparePaywallWalletCheckout}
|
|
891
|
+
checkout={walletCheckoutInput}
|
|
892
|
+
checkoutAnalytics={checkoutAnalyticsContext}
|
|
893
|
+
containerClassName='paywall-v2-wallet-surface'
|
|
894
|
+
disabled={checkoutDisabled}
|
|
895
|
+
googlePayPlaceholderLabel={content.checkout.googlePayPlaceholderLabel}
|
|
896
|
+
onAvailabilityChange={(availability) =>
|
|
897
|
+
handleWalletSurfaceAvailabilityChange(PAYWALL_WALLET_SURFACE_ID, availability)
|
|
898
|
+
}
|
|
899
|
+
onError={setError}
|
|
900
|
+
onSuccess={handleCheckoutCompleted}
|
|
901
|
+
onUnavailableClick={openCardCheckoutFromUnavailableWallet}
|
|
902
|
+
resolveMethodClassName={resolvePaywallWalletMethodClassName}
|
|
903
|
+
resolveMethodWrapperClassName={resolvePaywallWalletMethodWrapperClassName}
|
|
904
|
+
summaryLabel={checkoutSummaryLabel}
|
|
905
|
+
surfaceId={PAYWALL_WALLET_SURFACE_ID}
|
|
906
|
+
suspended={checkoutDialogReady || specialOfferDialogOpen}
|
|
907
|
+
/>
|
|
932
908
|
) : null}
|
|
933
909
|
<button
|
|
934
910
|
type='button'
|
|
@@ -1721,6 +1697,13 @@ const stepPaywallStyles = `
|
|
|
1721
1697
|
width: 100%;
|
|
1722
1698
|
}
|
|
1723
1699
|
|
|
1700
|
+
.paywall-v2-wallet-surface {
|
|
1701
|
+
display: flex;
|
|
1702
|
+
flex-direction: column;
|
|
1703
|
+
gap: 12px;
|
|
1704
|
+
width: 100%;
|
|
1705
|
+
}
|
|
1706
|
+
|
|
1724
1707
|
.paywall-v2-card-button {
|
|
1725
1708
|
width: 100%;
|
|
1726
1709
|
min-height: 64px;
|
|
@@ -363,7 +363,7 @@ describe('funnel agent documentation supply', () => {
|
|
|
363
363
|
|
|
364
364
|
expect(manifest).toMatchObject({
|
|
365
365
|
schemaVersion: 1,
|
|
366
|
-
bundleVersion: '2.0.
|
|
366
|
+
bundleVersion: '2.0.62',
|
|
367
367
|
stepContractVersion: contract.stepContractVersion,
|
|
368
368
|
contractHash: contract.contractHash,
|
|
369
369
|
});
|
|
@@ -43,8 +43,7 @@ describe('ClaimBee paywall source', () => {
|
|
|
43
43
|
);
|
|
44
44
|
|
|
45
45
|
expect(stepSource).toContain('SharedCheckoutSpecialOfferDialog');
|
|
46
|
-
expect(stepSource).toContain('
|
|
47
|
-
expect(stepSource).toContain('GooglePaySubscriptionCheckoutSlot');
|
|
46
|
+
expect(stepSource).toContain('StripeSubscriptionWalletSurface');
|
|
48
47
|
expect(stepSource).toContain('SharedStripeCheckoutV2Dialog');
|
|
49
48
|
expect(stepSource).not.toContain('StripeExpressCheckoutElement');
|
|
50
49
|
expect(stepSource).not.toContain('createStripeSubscriptionCheckout');
|
|
@@ -67,8 +66,8 @@ describe('ClaimBee paywall source', () => {
|
|
|
67
66
|
expect(stepSource).toContain('imageSrc={content.checkout.specialOffer.image.src}');
|
|
68
67
|
expect(stepSource).toContain("className='paywall-v2-scroll-cta paywall-v2-vibe-cta'");
|
|
69
68
|
expect(stepSource).toContain("className='paywall-v2-card-button paywall-v2-vibe-cta'");
|
|
70
|
-
expect(stepSource).toContain("
|
|
71
|
-
expect(stepSource).toContain("
|
|
69
|
+
expect(stepSource).toContain("applePay: 'paywall-v2-apple-pay-button'");
|
|
70
|
+
expect(stepSource).toContain("googlePay: 'paywall-v2-google-pay-button'");
|
|
72
71
|
expect(stepSource).not.toContain('Continue with other payment options');
|
|
73
72
|
});
|
|
74
73
|
|
|
@@ -172,7 +171,7 @@ describe('ClaimBee paywall source', () => {
|
|
|
172
171
|
expect(stepSource).toContain('useStripeSubscriptionCheckoutSession');
|
|
173
172
|
expect(stepSource).toContain('checkoutSession.activeClientSecret');
|
|
174
173
|
expect(stepSource).toContain('<SharedStripeCheckoutV2Dialog');
|
|
175
|
-
expect(stepSource).toContain('<
|
|
174
|
+
expect(stepSource).toContain('<StripeSubscriptionWalletSurface');
|
|
176
175
|
expect(stepSource).not.toContain('<StripeExpressCheckoutElement');
|
|
177
176
|
expect(stepSource).not.toContain('createStripePaymentIntent');
|
|
178
177
|
expect(stepSource).not.toContain('createStripeSubscriptionCheckout');
|
|
@@ -183,23 +182,25 @@ describe('ClaimBee paywall source', () => {
|
|
|
183
182
|
expect(stepSource).toContain("aria-label='Loading checkout'");
|
|
184
183
|
});
|
|
185
184
|
|
|
186
|
-
it('claims paywall completion only
|
|
185
|
+
it('claims paywall completion only after verified checkout success', () => {
|
|
187
186
|
const stepSource = readFileSync(
|
|
188
187
|
path.resolve(__dirname, '../../../src/steps/step-32-paywall.tsx'),
|
|
189
188
|
'utf8',
|
|
190
189
|
);
|
|
191
|
-
const
|
|
192
|
-
stepSource.indexOf('const
|
|
190
|
+
const completionHandler = stepSource.slice(
|
|
191
|
+
stepSource.indexOf('const handleCheckoutCompleted = () => {'),
|
|
193
192
|
stepSource.indexOf('const preparePaywallWalletCheckout'),
|
|
194
193
|
);
|
|
195
194
|
const checkoutStartHandler = stepSource.slice(
|
|
196
195
|
stepSource.indexOf('const startCheckout = async () => {'),
|
|
197
|
-
stepSource.indexOf('const
|
|
196
|
+
stepSource.indexOf('const handleCheckoutCompleted'),
|
|
198
197
|
);
|
|
199
198
|
|
|
200
199
|
expect(stepSource).toContain('const { user, setUser, attributes, featureFlags, completeStep } = useFunnel();');
|
|
201
|
-
expect(
|
|
202
|
-
expect(
|
|
200
|
+
expect(completionHandler).toContain('completeStep(analyticsStepId, {');
|
|
201
|
+
expect(completionHandler).toContain('planId: selectedDisplayPlan.id');
|
|
202
|
+
expect(stepSource).toContain('onSuccess={handleCheckoutCompleted}');
|
|
203
|
+
expect(stepSource).not.toContain('onPaymentInfoSubmitted=');
|
|
203
204
|
expect(checkoutStartHandler).not.toContain('completeStep(');
|
|
204
205
|
});
|
|
205
206
|
|
|
@@ -315,8 +316,7 @@ describe('ClaimBee paywall source', () => {
|
|
|
315
316
|
expect(plansIndex).toBeGreaterThan(-1);
|
|
316
317
|
expect(paymentStackIndex).toBeGreaterThan(plansIndex);
|
|
317
318
|
expect(featureGridIndex).toBeGreaterThan(paymentStackIndex);
|
|
318
|
-
expect(stepSource).toContain('
|
|
319
|
-
expect(stepSource).toContain('GooglePaySubscriptionCheckoutSlot');
|
|
319
|
+
expect(stepSource).toContain('StripeSubscriptionWalletSurface');
|
|
320
320
|
expect(stepSource).toContain('.paywall-v2-apple-pay-button,');
|
|
321
321
|
expect(stepSource).toContain('.paywall-v2-google-pay-button {');
|
|
322
322
|
expect(stepSource).toContain('min-height: 64px;');
|
|
@@ -352,12 +352,13 @@ describe('ClaimBee paywall source', () => {
|
|
|
352
352
|
'utf8',
|
|
353
353
|
);
|
|
354
354
|
|
|
355
|
-
expect(stepSource).toContain(
|
|
356
|
-
expect(stepSource).toContain("
|
|
357
|
-
expect(stepSource).toContain('
|
|
358
|
-
expect(stepSource).toContain('
|
|
355
|
+
expect(stepSource).toContain('<StripeSubscriptionWalletSurface');
|
|
356
|
+
expect(stepSource).toContain("surfaceId={PAYWALL_WALLET_SURFACE_ID}");
|
|
357
|
+
expect(stepSource).toContain('checkout={walletCheckoutInput}');
|
|
358
|
+
expect(stepSource).toContain('resolveMethodClassName={resolvePaywallWalletMethodClassName}');
|
|
359
|
+
expect(stepSource).toContain('resolveMethodWrapperClassName={resolvePaywallWalletMethodWrapperClassName}');
|
|
359
360
|
expect(stepSource).toContain('onUnavailableClick={openCardCheckoutFromUnavailableWallet}');
|
|
360
|
-
expect(stepSource).toContain('session={checkoutSession}');
|
|
361
|
+
expect(stepSource).not.toContain('session={checkoutSession}');
|
|
361
362
|
expect(stepSource).not.toContain('onClick={() => void startWalletCheckout()}');
|
|
362
363
|
expect(stepSource).not.toContain('StripeExpressCheckoutElement');
|
|
363
364
|
expect(stepSource).not.toContain('createStripeSubscriptionCheckout');
|
|
@@ -372,14 +373,13 @@ describe('ClaimBee paywall source', () => {
|
|
|
372
373
|
'utf8',
|
|
373
374
|
);
|
|
374
375
|
|
|
375
|
-
expect(stepSource).toContain('type PaywallWalletAvailabilityState =
|
|
376
|
-
expect(stepSource).toContain('
|
|
377
|
-
expect(stepSource).toContain(
|
|
378
|
-
expect(stepSource).toContain(
|
|
379
|
-
expect(stepSource).toContain('
|
|
376
|
+
expect(stepSource).toContain('type PaywallWalletAvailabilityState = Partial<Record<string, boolean>>;');
|
|
377
|
+
expect(stepSource).toContain('useState<PaywallWalletAvailabilityState>({});');
|
|
378
|
+
expect(stepSource).toContain('const handleWalletSurfaceAvailabilityChange = (');
|
|
379
|
+
expect(stepSource).toContain('availability: StripeSubscriptionWalletSurfaceAvailability');
|
|
380
|
+
expect(stepSource).toContain('paywallWalletAvailability[PAYWALL_WALLET_SURFACE_ID] ?? null');
|
|
380
381
|
expect(stepSource).toContain('initialWalletAvailable={checkoutInitialWalletAvailable}');
|
|
381
|
-
expect(stepSource).toContain('
|
|
382
|
-
expect(stepSource).toContain('onAvailabilityChange={handleGooglePayAvailabilityChange}');
|
|
382
|
+
expect(stepSource).toContain('handleWalletSurfaceAvailabilityChange(PAYWALL_WALLET_SURFACE_ID, availability)');
|
|
383
383
|
});
|
|
384
384
|
|
|
385
385
|
it('prepares the real Apple Pay element on load instead of waiting for the placeholder tap', () => {
|
|
@@ -389,7 +389,7 @@ describe('ClaimBee paywall source', () => {
|
|
|
389
389
|
);
|
|
390
390
|
|
|
391
391
|
expect(stepSource).toContain('useStripeSubscriptionCheckoutSession');
|
|
392
|
-
expect(stepSource).toContain('
|
|
392
|
+
expect(stepSource).toContain('StripeSubscriptionWalletSurface');
|
|
393
393
|
expect(stepSource).toContain('checkoutSession.prepareCardCheckout');
|
|
394
394
|
expect(stepSource).toContain('checkoutSession.activeClientSecret');
|
|
395
395
|
expect(stepSource).not.toContain('const autoWalletIntentKeyRef = useRef<string | null>(null);');
|
|
@@ -410,8 +410,8 @@ describe('ClaimBee paywall source', () => {
|
|
|
410
410
|
expect(stepSource).toContain('couponId: checkoutPromoActive ? selectedDisplayPlan?.couponId ?? null : null');
|
|
411
411
|
expect(stepSource).toContain('summaryLabel: checkoutSummaryLabel');
|
|
412
412
|
expect(stepSource).toContain('checkoutSession.activeClientSecret');
|
|
413
|
-
expect(stepSource).toContain('<
|
|
414
|
-
expect(stepSource).toContain('<
|
|
413
|
+
expect(stepSource).toContain('<StripeSubscriptionWalletSurface');
|
|
414
|
+
expect(stepSource).toContain('const walletCheckoutInput = useMemo<StripeSubscriptionWalletSurfaceCheckoutInput>');
|
|
415
415
|
expect(stepSource).not.toContain('StripeExpressCheckoutElement');
|
|
416
416
|
expect(stepSource).not.toContain('preparePaywallWalletSubscription');
|
|
417
417
|
expect(stepSource).not.toContain('const handlePaywallApplePayPlaceholderClick = () => {');
|
|
@@ -424,9 +424,8 @@ describe('ClaimBee paywall source', () => {
|
|
|
424
424
|
'utf8',
|
|
425
425
|
);
|
|
426
426
|
|
|
427
|
-
expect(stepSource).toContain('
|
|
427
|
+
expect(stepSource).not.toContain('publicAnalyticsSdk');
|
|
428
428
|
expect(stepSource).not.toContain('publicAnalyticsSdk.trackCheckoutStarted({');
|
|
429
|
-
expect(stepSource).toContain('publicAnalyticsSdk.trackPaymentInfoSubmitted({');
|
|
430
429
|
expect(stepSource).toContain('stepType: stepPaywall.type');
|
|
431
430
|
expect(stepSource).toContain('value: selectedDisplayPlan.analyticsPurchaseValue');
|
|
432
431
|
expect(stepSource).toContain("currency: 'USD'");
|
|
@@ -434,16 +433,16 @@ describe('ClaimBee paywall source', () => {
|
|
|
434
433
|
expect(stepSource).toContain('const checkoutAnalyticsContext = useMemo(');
|
|
435
434
|
expect(stepSource).toContain('checkoutAnalytics={checkoutAnalyticsContext}');
|
|
436
435
|
expect(stepSource).toContain('checkoutSessionId={checkoutSession.checkoutSessionId}');
|
|
437
|
-
expect(stepSource).toContain('onPaymentInfoSubmitted=
|
|
436
|
+
expect(stepSource).not.toContain('onPaymentInfoSubmitted=');
|
|
438
437
|
expect(stepSource).toContain("await checkoutSession.prepareCardCheckout({ checkoutStartSource: 'checkout_render' });");
|
|
439
|
-
expect(stepSource).
|
|
438
|
+
expect(stepSource).toContain('const handleCheckoutCompleted = () => {');
|
|
440
439
|
expect(stepSource).not.toContain('publicAnalyticsSdk.trackCheckoutCompleted({');
|
|
441
|
-
expect(stepSource).
|
|
440
|
+
expect(stepSource).toContain('onSuccess={handleCheckoutCompleted}');
|
|
442
441
|
expect(stepSource).not.toContain("eventType: 'checkout_started'");
|
|
443
442
|
expect(stepSource).not.toContain("eventType: 'checkout_completed'");
|
|
444
443
|
});
|
|
445
444
|
|
|
446
|
-
it('
|
|
445
|
+
it('delegates payment-info analytics to shared checkout components with session context', () => {
|
|
447
446
|
const stepSource = readFileSync(
|
|
448
447
|
path.resolve(__dirname, '../../../src/steps/step-32-paywall.tsx'),
|
|
449
448
|
'utf8',
|
|
@@ -456,25 +455,12 @@ describe('ClaimBee paywall source', () => {
|
|
|
456
455
|
stepSource.indexOf('const checkoutAnalyticsContext = useMemo(() => {'),
|
|
457
456
|
stepSource.indexOf('const successReturnUrl = useMemo(() => {'),
|
|
458
457
|
);
|
|
459
|
-
const paymentInfoHandler = stepSource.slice(
|
|
460
|
-
stepSource.indexOf('const trackPaymentInfoSubmitted = () => {'),
|
|
461
|
-
stepSource.indexOf('const preparePaywallWalletCheckout'),
|
|
462
|
-
);
|
|
463
|
-
|
|
464
458
|
expect(analyticsMetadata).toContain("currency: 'USD'");
|
|
465
459
|
expect(checkoutAnalyticsContext).toContain('metadata: checkoutAnalyticsMetadata');
|
|
466
|
-
expect(
|
|
467
|
-
|
|
468
|
-
);
|
|
469
|
-
expect(
|
|
470
|
-
expect(paymentInfoHandler.indexOf('if (!checkoutSessionId) {')).toBeLessThan(
|
|
471
|
-
paymentInfoHandler.indexOf('completeStep(analyticsStepId, {'),
|
|
472
|
-
);
|
|
473
|
-
expect(paymentInfoHandler).toContain(
|
|
474
|
-
'eventId: `add_payment_info:${checkoutSessionId}`',
|
|
475
|
-
);
|
|
476
|
-
expect(paymentInfoHandler).toContain('metadata: checkoutAnalyticsMetadata');
|
|
477
|
-
expect(paymentInfoHandler).toContain('void publicAnalyticsSdk.flush().catch(() => 0);');
|
|
460
|
+
expect(stepSource.match(/checkoutAnalytics=\{checkoutAnalyticsContext\}/g)).toHaveLength(2);
|
|
461
|
+
expect(stepSource).toContain('checkoutSessionId={checkoutSession.checkoutSessionId}');
|
|
462
|
+
expect(stepSource).not.toContain('trackPaymentInfoSubmitted');
|
|
463
|
+
expect(stepSource).not.toContain('add_payment_info:');
|
|
478
464
|
});
|
|
479
465
|
|
|
480
466
|
it('uses a local three-plan catalog so the paywall shows weekly, monthly, and yearly offers', () => {
|