@funnelsgrove/payments 0.11.1 → 0.11.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/providers/stripe/components/CheckoutBrandAssets.js +2 -2
- package/dist/providers/stripe/components/SharedStripeCheckoutV2Dialog.js +11 -3
- package/dist/providers/stripe/components/StripeCheckoutExpressCheckoutButton.js +15 -4
- package/dist/providers/stripe/services/checkoutCompletionAnalytics.service.d.ts +1 -1
- package/dist/services/paywallOffer.service.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -108,11 +108,11 @@ Funnels should call `resolvePublishedBillingRuntime` once at their runtime-provi
|
|
|
108
108
|
- Stripe owns real card fields and wallet controls. Local wallet buttons are placeholders until Stripe confirms availability.
|
|
109
109
|
- Keep `planId` as the funnel plan key and send Stripe's price id only as `providerPlanId`; the API uses both fields to resolve the active test/live offer mapping.
|
|
110
110
|
- Wallet unavailable state must fall back to card/manual checkout or disappear.
|
|
111
|
-
- Count `checkout_started` only at shared UI boundaries: when a shared card dialog opens or when Stripe reports Apple Pay / Google Pay intent. Checkout Sessions use confirmation as a fallback because Stripe omits their click callback.
|
|
111
|
+
- Count `checkout_started` only at shared UI boundaries: when a shared card dialog opens or when Stripe reports Apple Pay / Google Pay intent. Checkout Sessions use confirmation or cancellation as a fallback because Stripe omits their click callback. Cancellation recovers the start with `checkout_start_source: wallet_cancel`; its timestamp is dismissal time, not wallet-open time. Closing the browser before either callback is still unobservable. Cancel has no payment-method field, so the method is included only when the element enables exactly one method.
|
|
112
112
|
- Pass `checkoutAnalytics` to every current shared dialog and wallet surface. The package then owns `checkout_started`, `add_payment_info`, and verified `checkout_completed`, all deduplicated by the actual Checkout Session.
|
|
113
113
|
- Call funnel lifecycle `completeStep` only from the shared checkout `onSuccess` callback. Payment-info submission is not step completion.
|
|
114
114
|
- Wallet slots may create render-only Checkout Sessions on mount so Apple Pay and Google Pay render before the visitor taps. Session creation is technical preparation and must never emit `checkout_started`.
|
|
115
|
-
- Shared checkout analytics deduplicates `checkout_started` by Checkout Session id. Wallet confirmation may retry the start signal when the click callback is unavailable, without creating a duplicate.
|
|
115
|
+
- Shared checkout analytics deduplicates `checkout_started` by Checkout Session id. Wallet confirmation and cancellation may retry the start signal when the click callback is unavailable, without creating a duplicate. Cancellation never emits payment-info or purchase completion events.
|
|
116
116
|
- Do not call `publicAnalyticsSdk.trackCheckoutStarted`, `trackPaymentInfoSubmitted`, or `trackCheckoutCompleted` directly from funnel checkout components.
|
|
117
117
|
- Funnel paywalls should style wallet buttons through the shared slot API: `className` for the Stripe button/placeholder, `slotClassName` for the outer slot, `appearance` for Stripe Elements appearance, and `options` for Stripe Express Checkout options.
|
|
118
118
|
- Keep discount math reusable here, but keep offer activation timing in funnel code.
|
|
@@ -3,10 +3,10 @@ const normalizePaymentBrandLabel = (label) => {
|
|
|
3
3
|
return label.trim().toLowerCase().replace(/[^a-z0-9]+/g, '');
|
|
4
4
|
};
|
|
5
5
|
function VisaMark() {
|
|
6
|
-
return (_jsxs("svg", { viewBox: '0 0 32 20', fill: 'none', "aria-hidden": 'true', children: [_jsx("path", { d: 'M30 0.325H2C1.07492 0.325 0.325 1.07492 0.325 2V18C0.325 18.9251 1.07492 19.675 2 19.675H30C30.9251 19.675 31.675 18.9251 31.675 18V2C31.675 1.07492 30.9251 0.325 30 0.325Z', fill: 'white' }), _jsx("path", { fillRule: 'evenodd', clipRule: 'evenodd', d: 'M11.1253 13.2159H9.40884L8.12171 8.16105C8.06062 7.92853 7.9309 7.72296 7.74009 7.62607C7.2639 7.3826 6.73918 7.18883 6.16675 7.0911V6.89648H8.93182C9.31343 6.89648 9.59965 7.18883 9.64735 7.52835L10.3152 11.1746L12.0308 6.89648H13.6995L11.1253 13.2159ZM14.654 13.2159H13.033L14.3678 6.89648H15.9888L14.654 13.2159ZM18.0854 8.64643C18.1331 8.30606 18.4193 8.11145 18.7532 8.11145C19.2779 8.06259 19.8495 8.16032 20.3265 8.40295L20.6127 7.04234C20.1357 6.84773 19.611 6.75 19.1348 6.75C17.5615 6.75 16.4166 7.62534 16.4166 8.8402C16.4166 9.7644 17.2275 10.2497 17.8 10.542C18.4193 10.8335 18.6578 11.0281 18.6101 11.3196C18.6101 11.7569 18.1331 11.9515 17.6569 11.9515C17.0844 11.9515 16.512 11.8057 15.9881 11.5623L15.7019 12.9237C16.2743 13.1663 16.8936 13.2641 17.4661 13.2641C19.2302 13.3121 20.3265 12.4376 20.3265 11.125C20.3265 9.47206 18.0854 9.37517 18.0854 8.64643ZM26 13.2159L24.7129 6.89648H23.3303C23.0441 6.89648 22.7579 7.0911 22.6625 7.3826L20.2791 13.2159H21.9478L22.2809 12.2926H24.3313L24.5221 13.2159H26ZM23.5693 8.59766L24.0455 10.9794H22.7107L23.5693 8.59766Z', fill: '#172B85' }), _jsx("path", { d: 'M30 0.325H2C1.07492 0.325 0.325 1.07492 0.325 2V18C0.325 18.9251 1.07492 19.675 2 19.675H30C30.9251 19.675 31.675 18.9251 31.675 18V2C31.675 1.07492 30.9251 0.325 30 0.325Z', stroke: '
|
|
6
|
+
return (_jsxs("svg", { viewBox: '0 0 32 20', fill: 'none', "aria-hidden": 'true', children: [_jsx("path", { d: 'M30 0.325H2C1.07492 0.325 0.325 1.07492 0.325 2V18C0.325 18.9251 1.07492 19.675 2 19.675H30C30.9251 19.675 31.675 18.9251 31.675 18V2C31.675 1.07492 30.9251 0.325 30 0.325Z', fill: 'white' }), _jsx("path", { fillRule: 'evenodd', clipRule: 'evenodd', d: 'M11.1253 13.2159H9.40884L8.12171 8.16105C8.06062 7.92853 7.9309 7.72296 7.74009 7.62607C7.2639 7.3826 6.73918 7.18883 6.16675 7.0911V6.89648H8.93182C9.31343 6.89648 9.59965 7.18883 9.64735 7.52835L10.3152 11.1746L12.0308 6.89648H13.6995L11.1253 13.2159ZM14.654 13.2159H13.033L14.3678 6.89648H15.9888L14.654 13.2159ZM18.0854 8.64643C18.1331 8.30606 18.4193 8.11145 18.7532 8.11145C19.2779 8.06259 19.8495 8.16032 20.3265 8.40295L20.6127 7.04234C20.1357 6.84773 19.611 6.75 19.1348 6.75C17.5615 6.75 16.4166 7.62534 16.4166 8.8402C16.4166 9.7644 17.2275 10.2497 17.8 10.542C18.4193 10.8335 18.6578 11.0281 18.6101 11.3196C18.6101 11.7569 18.1331 11.9515 17.6569 11.9515C17.0844 11.9515 16.512 11.8057 15.9881 11.5623L15.7019 12.9237C16.2743 13.1663 16.8936 13.2641 17.4661 13.2641C19.2302 13.3121 20.3265 12.4376 20.3265 11.125C20.3265 9.47206 18.0854 9.37517 18.0854 8.64643ZM26 13.2159L24.7129 6.89648H23.3303C23.0441 6.89648 22.7579 7.0911 22.6625 7.3826L20.2791 13.2159H21.9478L22.2809 12.2926H24.3313L24.5221 13.2159H26ZM23.5693 8.59766L24.0455 10.9794H22.7107L23.5693 8.59766Z', fill: '#172B85' }), _jsx("path", { d: 'M30 0.325H2C1.07492 0.325 0.325 1.07492 0.325 2V18C0.325 18.9251 1.07492 19.675 2 19.675H30C30.9251 19.675 31.675 18.9251 31.675 18V2C31.675 1.07492 30.9251 0.325 30 0.325Z', stroke: '#E3E6EE', strokeWidth: '0.65' })] }));
|
|
7
7
|
}
|
|
8
8
|
function MastercardMark() {
|
|
9
|
-
return (_jsxs("svg", { viewBox: '0 0 32 20', fill: 'none', "aria-hidden": 'true', children: [_jsx("path", { d: 'M30 0.325H2C1.07492 0.325 0.325 1.07492 0.325 2V18C0.325 18.9251 1.07492 19.675 2 19.675H30C30.9251 19.675 31.675 18.9251 31.675 18V2C31.675 1.07492 30.9251 0.325 30 0.325Z', fill: 'white' }), _jsx("path", { fillRule: 'evenodd', clipRule: 'evenodd', d: 'M16.3156 14.0245C15.3289 14.8561 14.0491 15.3581 12.6506 15.3581C9.52985 15.3581 7 12.8583 7 9.77474C7 6.69115 9.52985 4.19141 12.6506 4.19141C14.0491 4.19141 15.3289 4.69342 16.3156 5.52502C17.3023 4.69342 18.5821 4.19141 19.9807 4.19141C23.1014 4.19141 25.6312 6.69115 25.6312 9.77474C25.6312 12.8583 23.1014 15.3581 19.9807 15.3581C18.5821 15.3581 17.3023 14.8561 16.3156 14.0245Z', fill: '#ED0006' }), _jsx("path", { fillRule: 'evenodd', clipRule: 'evenodd', d: 'M16.3157 14.0245C17.5307 13.0004 18.3012 11.4765 18.3012 9.77474C18.3012 8.07302 17.5307 6.54911 16.3157 5.52502C17.3024 4.69342 18.5822 4.19141 19.9807 4.19141C23.1014 4.19141 25.6313 6.69115 25.6313 9.77474C25.6313 12.8583 23.1014 15.3581 19.9807 15.3581C18.5822 15.3581 17.3024 14.8561 16.3157 14.0245Z', fill: '#F9A000' }), _jsx("path", { fillRule: 'evenodd', clipRule: 'evenodd', d: 'M16.3156 14.0245C17.5307 13.0004 18.3012 11.4765 18.3012 9.77474C18.3012 8.07302 17.5307 6.54911 16.3156 5.52502C15.1006 6.54911 14.3301 8.07302 14.3301 9.77474C14.3301 11.4765 15.1006 13.0004 16.3156 14.0245Z', fill: '#FF5E00' }), _jsx("path", { d: 'M30 0.325H2C1.07492 0.325 0.325 1.07492 0.325 2V18C0.325 18.9251 1.07492 19.675 2 19.675H30C30.9251 19.675 31.675 18.9251 31.675 18V2C31.675 1.07492 30.9251 0.325 30 0.325Z', stroke: '
|
|
9
|
+
return (_jsxs("svg", { viewBox: '0 0 32 20', fill: 'none', "aria-hidden": 'true', children: [_jsx("path", { d: 'M30 0.325H2C1.07492 0.325 0.325 1.07492 0.325 2V18C0.325 18.9251 1.07492 19.675 2 19.675H30C30.9251 19.675 31.675 18.9251 31.675 18V2C31.675 1.07492 30.9251 0.325 30 0.325Z', fill: 'white' }), _jsx("path", { fillRule: 'evenodd', clipRule: 'evenodd', d: 'M16.3156 14.0245C15.3289 14.8561 14.0491 15.3581 12.6506 15.3581C9.52985 15.3581 7 12.8583 7 9.77474C7 6.69115 9.52985 4.19141 12.6506 4.19141C14.0491 4.19141 15.3289 4.69342 16.3156 5.52502C17.3023 4.69342 18.5821 4.19141 19.9807 4.19141C23.1014 4.19141 25.6312 6.69115 25.6312 9.77474C25.6312 12.8583 23.1014 15.3581 19.9807 15.3581C18.5821 15.3581 17.3023 14.8561 16.3156 14.0245Z', fill: '#ED0006' }), _jsx("path", { fillRule: 'evenodd', clipRule: 'evenodd', d: 'M16.3157 14.0245C17.5307 13.0004 18.3012 11.4765 18.3012 9.77474C18.3012 8.07302 17.5307 6.54911 16.3157 5.52502C17.3024 4.69342 18.5822 4.19141 19.9807 4.19141C23.1014 4.19141 25.6313 6.69115 25.6313 9.77474C25.6313 12.8583 23.1014 15.3581 19.9807 15.3581C18.5822 15.3581 17.3024 14.8561 16.3157 14.0245Z', fill: '#F9A000' }), _jsx("path", { fillRule: 'evenodd', clipRule: 'evenodd', d: 'M16.3156 14.0245C17.5307 13.0004 18.3012 11.4765 18.3012 9.77474C18.3012 8.07302 17.5307 6.54911 16.3156 5.52502C15.1006 6.54911 14.3301 8.07302 14.3301 9.77474C14.3301 11.4765 15.1006 13.0004 16.3156 14.0245Z', fill: '#FF5E00' }), _jsx("path", { d: 'M30 0.325H2C1.07492 0.325 0.325 1.07492 0.325 2V18C0.325 18.9251 1.07492 19.675 2 19.675H30C30.9251 19.675 31.675 18.9251 31.675 18V2C31.675 1.07492 30.9251 0.325 30 0.325Z', stroke: '#E3E6EE', strokeWidth: '0.65' })] }));
|
|
10
10
|
}
|
|
11
11
|
function PayPalMark() {
|
|
12
12
|
return (_jsxs("svg", { viewBox: '0 0 34 24', fill: 'none', "aria-hidden": 'true', children: [_jsx("path", { d: 'M30 0.5H4C2.067 0.5 0.5 2.06701 0.5 4V20C0.5 21.933 2.067 23.5 4 23.5H30C31.933 23.5 33.5 21.933 33.5 20V4C33.5 2.06701 31.933 0.5 30 0.5Z', fill: 'white', stroke: '#D9D9D9' }), _jsx("path", { fillRule: 'evenodd', clipRule: 'evenodd', d: 'M14.615 18.4493L14.836 17.0002L14.344 16.9883H11.996L13.628 6.29468C13.633 6.2623 13.649 6.23224 13.673 6.21085C13.697 6.18947 13.728 6.17774 13.76 6.17774H17.719C19.034 6.17774 19.941 6.46037 20.414 7.01832C20.636 7.28006 20.778 7.55365 20.846 7.85462C20.918 8.17049 20.919 8.54783 20.849 9.00813L20.844 9.04161V9.33662L21.066 9.46662C21.253 9.56912 21.402 9.68642 21.515 9.82072C21.705 10.0446 21.828 10.3291 21.88 10.6662C21.934 11.013 21.916 11.4258 21.828 11.893C21.727 12.4305 21.563 12.8986 21.342 13.2815C21.139 13.6344 20.879 13.9272 20.572 14.154C20.278 14.3696 19.929 14.5332 19.534 14.6379C19.152 14.7408 18.716 14.7927 18.238 14.7927H17.929C17.709 14.7927 17.495 14.8747 17.327 15.0217C17.159 15.1718 17.047 15.3768 17.013 15.601L16.99 15.7315L16.6 18.2858L16.582 18.3795C16.578 18.4092 16.57 18.424 16.558 18.434C16.547 18.4432 16.532 18.4493 16.517 18.4493H14.615Z', fill: '#28356A' }), _jsx("path", { fillRule: 'evenodd', clipRule: 'evenodd', d: 'M21.276 9.07618C21.264 9.15426 21.251 9.23405 21.236 9.31602C20.714 12.0866 18.927 13.0438 16.646 13.0438H15.484C15.205 13.0438 14.97 13.2531 14.927 13.5376L14.164 18.5408C14.136 18.7277 14.275 18.8959 14.457 18.8959H16.517C16.761 18.8959 16.968 18.7127 17.007 18.4641L17.027 18.3559L17.415 15.8117L17.44 15.6722C17.478 15.4227 17.686 15.2394 17.929 15.2394H18.238C20.234 15.2394 21.796 14.4018 22.253 11.978C22.443 10.9655 22.345 10.12 21.84 9.52552C21.687 9.34622 21.498 9.19739 21.276 9.07618Z', fill: '#298FC2' }), _jsx("path", { fillRule: 'evenodd', clipRule: 'evenodd', d: 'M20.729 8.84798C20.649 8.82391 20.567 8.80216 20.482 8.78248C20.397 8.7633 20.31 8.74631 20.221 8.73141C19.908 8.67911 19.565 8.6543 19.197 8.6543H16.094C16.018 8.6543 15.945 8.67214 15.88 8.7044C15.737 8.77552 15.631 8.91555 15.605 9.08699L14.945 13.4085L14.926 13.5345C14.969 13.25 15.204 13.0407 15.483 13.0407H16.645C18.926 13.0407 20.712 12.083 21.234 9.31292C21.25 9.23094 21.263 9.15115 21.275 9.07306C21.143 9.0006 21 8.93865 20.846 8.88586C20.808 8.87279 20.768 8.8602 20.729 8.84798Z', fill: '#22284F' }), _jsx("path", { fillRule: 'evenodd', clipRule: 'evenodd', d: 'M15.605 9.08716C15.63 8.91572 15.737 8.77569 15.88 8.70506C15.945 8.67268 16.017 8.65484 16.094 8.65484H19.197C19.564 8.65484 19.908 8.67977 20.221 8.73207C20.31 8.74685 20.397 8.76396 20.482 8.78314C20.566 8.80269 20.649 8.82457 20.729 8.84852C20.768 8.86074 20.807 8.87344 20.846 8.88603C21 8.93882 21.143 9.00126 21.275 9.07323C21.43 8.04936 21.274 7.35224 20.738 6.72099C20.148 6.02594 19.082 5.72852 17.718 5.72852H13.759C13.481 5.72852 13.243 5.93784 13.2 6.2228L11.551 17.0264C11.519 17.2401 11.678 17.4329 11.887 17.4329H14.331L15.605 9.08716Z', fill: '#28356A' })] }));
|
|
@@ -734,12 +734,20 @@ const sharedStripeCheckoutV2Styles = `
|
|
|
734
734
|
display: flex;
|
|
735
735
|
align-items: center;
|
|
736
736
|
justify-content: center;
|
|
737
|
-
|
|
737
|
+
max-width: 100%;
|
|
738
|
+
gap: 4px;
|
|
738
739
|
}
|
|
739
740
|
|
|
740
741
|
.shared-checkout-v2-tab-brand {
|
|
741
|
-
width:
|
|
742
|
-
|
|
742
|
+
min-width: 0;
|
|
743
|
+
width: 24px;
|
|
744
|
+
height: 15px;
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
.shared-checkout-v2-tab-brand svg {
|
|
748
|
+
display: block;
|
|
749
|
+
width: 100%;
|
|
750
|
+
height: 100%;
|
|
743
751
|
}
|
|
744
752
|
|
|
745
753
|
.shared-checkout-v2-wallet-panel,
|
|
@@ -95,16 +95,18 @@ export function StripeCheckoutExpressCheckoutButton({ beforeConfirm, checkoutAna
|
|
|
95
95
|
}
|
|
96
96
|
await (onSuccess === null || onSuccess === void 0 ? void 0 : onSuccess());
|
|
97
97
|
};
|
|
98
|
-
const queueCheckoutStarted = (paymentMethod) => {
|
|
98
|
+
const queueCheckoutStarted = (paymentMethod, checkoutStartSource = 'wallet_click') => {
|
|
99
99
|
if (!checkoutAnalytics) {
|
|
100
100
|
return;
|
|
101
101
|
}
|
|
102
|
-
queueStripeSubscriptionCheckoutStarted(Object.assign(Object.assign({}, checkoutAnalytics), { checkoutSessionId,
|
|
102
|
+
queueStripeSubscriptionCheckoutStarted(Object.assign(Object.assign({}, checkoutAnalytics), { checkoutSessionId,
|
|
103
|
+
checkoutStartSource,
|
|
104
|
+
paymentMethod }));
|
|
103
105
|
};
|
|
104
106
|
const handleConfirm = async (event) => {
|
|
105
107
|
var _a, _b, _c;
|
|
106
108
|
// Checkout Sessions omits the Express Checkout onClick callback. Confirmation
|
|
107
|
-
//
|
|
109
|
+
// and cancellation are the reliable wallet-intent boundaries for this provider.
|
|
108
110
|
// Session-based analytics deduplication keeps this idempotent if onClick fires.
|
|
109
111
|
queueCheckoutStarted(event.expressPaymentType);
|
|
110
112
|
if (checkoutState.type !== 'success') {
|
|
@@ -176,13 +178,22 @@ export function StripeCheckoutExpressCheckoutButton({ beforeConfirm, checkoutAna
|
|
|
176
178
|
queueCheckoutStarted(event.expressPaymentType);
|
|
177
179
|
event.resolve();
|
|
178
180
|
};
|
|
181
|
+
const handleCancel = () => {
|
|
182
|
+
var _a;
|
|
183
|
+
// Dismissal proves the wallet was opened, even when Stripe omitted onClick.
|
|
184
|
+
// Cancel has no method field; only single-method placements can identify it.
|
|
185
|
+
const enabledMethods = Object.entries((_a = resolvedOptions.paymentMethods) !== null && _a !== void 0 ? _a : {})
|
|
186
|
+
.filter(([, availability]) => availability !== 'never');
|
|
187
|
+
queueCheckoutStarted(enabledMethods.length === 1 ? enabledMethods[0][0] : undefined, 'wallet_cancel');
|
|
188
|
+
onCancel === null || onCancel === void 0 ? void 0 : onCancel();
|
|
189
|
+
};
|
|
179
190
|
return (_jsxs(_Fragment, { children: [_jsx("div", { className: [
|
|
180
191
|
'stripe-express-checkout-button',
|
|
181
192
|
className !== null && className !== void 0 ? className : '',
|
|
182
193
|
effectiveWalletAvailable === false ? 'is-hidden' : '',
|
|
183
194
|
]
|
|
184
195
|
.filter(Boolean)
|
|
185
|
-
.join(' '), children: _jsx(ExpressCheckoutElementWithCancel, { options: resolvedOptions, onCancel:
|
|
196
|
+
.join(' '), children: _jsx(ExpressCheckoutElementWithCancel, { options: resolvedOptions, onCancel: handleCancel, onClick: handleClick, onConfirm: (event) => void handleConfirm(event), onLoadError: ({ error }) => {
|
|
186
197
|
setWalletAvailable(false);
|
|
187
198
|
onAvailabilityChange === null || onAvailabilityChange === void 0 ? void 0 : onAvailabilityChange(false);
|
|
188
199
|
onError === null || onError === void 0 ? void 0 : onError(error.message || 'Unable to load wallet checkout.');
|
|
@@ -12,7 +12,7 @@ export type StripeSubscriptionCheckoutAnalyticsContext = {
|
|
|
12
12
|
};
|
|
13
13
|
export type TrackStripeSubscriptionCheckoutStartedInput = StripeSubscriptionCheckoutAnalyticsContext & {
|
|
14
14
|
checkoutSessionId?: string | null;
|
|
15
|
-
checkoutStartSource?: 'card_form_open' | 'wallet_click';
|
|
15
|
+
checkoutStartSource?: 'card_form_open' | 'wallet_click' | 'wallet_cancel';
|
|
16
16
|
paymentMethod?: unknown;
|
|
17
17
|
};
|
|
18
18
|
export type TrackPaidStripeSubscriptionCheckoutCompletedInput = StripeSubscriptionCheckoutAnalyticsContext & {
|
|
@@ -319,5 +319,5 @@ export const buildPaywallRenewalDisclaimer = (input) => {
|
|
|
319
319
|
var _a;
|
|
320
320
|
const introLabel = input.hasActiveDiscount ? 'discounted intro plan' : 'selected plan';
|
|
321
321
|
const productPrefix = ((_a = input.productName) === null || _a === void 0 ? void 0 : _a.trim()) ? `${input.productName.trim()} ` : '';
|
|
322
|
-
return `Without cancellation, before the
|
|
322
|
+
return `Without cancellation, before the ${introLabel} ends, I accept that ${productPrefix}will automatically charge ${input.plan.priceLabel} ${formatRecurringCadence(input.plan)} until I cancel. Cancel online via the profile on the website or app.`;
|
|
323
323
|
};
|