@funnelsgrove/payments 0.1.0 → 0.1.2

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.
Files changed (44) hide show
  1. package/README.md +32 -3
  2. package/dist/components/shared/ApplePaySubscribeButton.d.ts +15 -0
  3. package/dist/components/shared/ApplePaySubscribeButton.js +76 -0
  4. package/dist/components/shared/CheckoutBrandAssets.d.ts +12 -0
  5. package/dist/components/shared/CheckoutBrandAssets.js +56 -0
  6. package/dist/components/shared/GooglePaySubscribeButton.d.ts +14 -0
  7. package/dist/components/shared/GooglePaySubscribeButton.js +94 -0
  8. package/dist/components/shared/SharedStripeCheckoutDialog.d.ts +44 -0
  9. package/dist/components/shared/SharedStripeCheckoutDialog.js +717 -0
  10. package/dist/components/shared/SharedStripeCheckoutV2Dialog.d.ts +60 -0
  11. package/dist/components/shared/SharedStripeCheckoutV2Dialog.js +988 -0
  12. package/dist/components/shared/StripeExpressCheckoutButton.d.ts +18 -0
  13. package/dist/components/shared/StripeExpressCheckoutButton.js +164 -0
  14. package/dist/components/shared/StripeExpressCheckoutElement.d.ts +8 -0
  15. package/dist/components/shared/StripeExpressCheckoutElement.js +19 -0
  16. package/dist/components/shared/StripePlanSelector.d.ts +5 -7
  17. package/dist/components/shared/StripePlanSelector.js +16 -57
  18. package/dist/components/shared/walletPlatform.d.ts +11 -0
  19. package/dist/components/shared/walletPlatform.js +39 -0
  20. package/dist/config/billing.config.d.ts +60 -9
  21. package/dist/config/billing.config.js +7 -2
  22. package/dist/hooks/useResolvedPaywallPlans.d.ts +15 -0
  23. package/dist/hooks/useResolvedPaywallPlans.js +68 -0
  24. package/dist/index.d.ts +21 -5
  25. package/dist/index.js +21 -5
  26. package/dist/providers/paymentProvider.types.d.ts +4 -0
  27. package/dist/providers/paymentProvider.types.js +4 -0
  28. package/dist/providers/stripe/ApplePaySubscriptionCheckoutSlot.d.ts +5 -0
  29. package/dist/providers/stripe/ApplePaySubscriptionCheckoutSlot.js +21 -0
  30. package/dist/providers/stripe/GooglePaySubscriptionCheckoutSlot.d.ts +7 -0
  31. package/dist/providers/stripe/GooglePaySubscriptionCheckoutSlot.js +44 -0
  32. package/dist/providers/stripe/WalletSubscriptionCheckoutSlot.d.ts +32 -0
  33. package/dist/providers/stripe/WalletSubscriptionCheckoutSlot.js +69 -0
  34. package/dist/providers/stripe/useStripeSubscriptionCheckoutSession.d.ts +30 -0
  35. package/dist/providers/stripe/useStripeSubscriptionCheckoutSession.js +164 -0
  36. package/dist/services/paywallOffer.service.d.ts +65 -0
  37. package/dist/services/paywallOffer.service.js +312 -0
  38. package/dist/services/planCatalog.service.d.ts +28 -0
  39. package/dist/services/planCatalog.service.js +60 -0
  40. package/dist/services/runtimeBillingPlanCatalog.service.d.ts +6 -0
  41. package/dist/services/runtimeBillingPlanCatalog.service.js +24 -0
  42. package/dist/services/stripe.service.d.ts +66 -6
  43. package/dist/services/stripe.service.js +409 -54
  44. package/package.json +3 -3
@@ -0,0 +1,312 @@
1
+ const isBillingDiscountList = (discounts) => {
2
+ return Array.isArray(discounts);
3
+ };
4
+ export const buildBillingDiscountCatalog = (discounts) => {
5
+ if (!isBillingDiscountList(discounts)) {
6
+ return discounts;
7
+ }
8
+ const first = discounts.find((discount) => discount.stage === 'first');
9
+ const second = discounts.find((discount) => discount.stage === 'second');
10
+ if (!first || !second) {
11
+ throw new Error('Billing discounts must include first and second stages');
12
+ }
13
+ return {
14
+ first,
15
+ second,
16
+ };
17
+ };
18
+ const PROMO_MONTHS = [
19
+ 'jan',
20
+ 'feb',
21
+ 'mar',
22
+ 'apr',
23
+ 'may',
24
+ 'jun',
25
+ 'jul',
26
+ 'aug',
27
+ 'sep',
28
+ 'oct',
29
+ 'nov',
30
+ 'dec',
31
+ ];
32
+ const BILLING_INTERVAL_DAYS = {
33
+ day: 1,
34
+ week: 7,
35
+ month: 30,
36
+ year: 365,
37
+ };
38
+ const clampRemainingSeconds = (value) => {
39
+ if (!Number.isFinite(value) || value <= 0) {
40
+ return 0;
41
+ }
42
+ return Math.max(0, Math.ceil(value));
43
+ };
44
+ const parseStoredSnapshot = (storedValue) => {
45
+ if (!storedValue) {
46
+ return null;
47
+ }
48
+ try {
49
+ const parsed = JSON.parse(storedValue);
50
+ return typeof parsed === 'object' && parsed ? parsed : null;
51
+ }
52
+ catch (_a) {
53
+ return null;
54
+ }
55
+ };
56
+ const parseStoredTimestamp = (value) => {
57
+ return typeof value === 'number' && Number.isFinite(value) ? value : null;
58
+ };
59
+ const getDiscount = (discounts, stage) => {
60
+ return discounts[stage];
61
+ };
62
+ const getDiscountDurationMs = (discounts, stage) => {
63
+ return getDiscount(discounts, stage).durationSeconds * 1000;
64
+ };
65
+ const getActiveStage = (stage) => {
66
+ return stage === 'second' || stage === 'upgraded' || stage === 'completed'
67
+ ? 'second'
68
+ : 'first';
69
+ };
70
+ const getStoredStage = (snapshot) => {
71
+ if (snapshot.stage) {
72
+ return snapshot.stage;
73
+ }
74
+ if (snapshot.offerStage === 'upgraded') {
75
+ return 'second';
76
+ }
77
+ if (snapshot.offerStage === 'completed') {
78
+ return 'completed';
79
+ }
80
+ return 'first';
81
+ };
82
+ const getExpiredStartedAtMs = (discounts, nowMs, stage) => {
83
+ return nowMs - getDiscountDurationMs(discounts, stage) - 1;
84
+ };
85
+ const getStoredDiscountStarts = (discounts, snapshot, nowMs) => {
86
+ var _a, _b;
87
+ const firstStartedAtMs = (_a = parseStoredTimestamp(snapshot.firstStartedAt)) !== null && _a !== void 0 ? _a : parseStoredTimestamp(snapshot.discountStartedAt);
88
+ const secondStartedAtMs = (_b = parseStoredTimestamp(snapshot.secondStartedAt)) !== null && _b !== void 0 ? _b : parseStoredTimestamp(snapshot.additionalDiscountStartedAt);
89
+ if (firstStartedAtMs !== null || secondStartedAtMs !== null) {
90
+ return {
91
+ firstStartedAtMs,
92
+ secondStartedAtMs,
93
+ };
94
+ }
95
+ const stage = getStoredStage(snapshot);
96
+ if (typeof snapshot.expiresAtMs === 'number') {
97
+ const activeStage = getActiveStage(stage);
98
+ const startedAtMs = snapshot.expiresAtMs - getDiscountDurationMs(discounts, activeStage);
99
+ return activeStage === 'second'
100
+ ? {
101
+ firstStartedAtMs: null,
102
+ secondStartedAtMs: startedAtMs,
103
+ }
104
+ : {
105
+ firstStartedAtMs: startedAtMs,
106
+ secondStartedAtMs: null,
107
+ };
108
+ }
109
+ if (snapshot.status === 'expired') {
110
+ const activeStage = getActiveStage(stage);
111
+ const expiredStartedAtMs = getExpiredStartedAtMs(discounts, nowMs, activeStage);
112
+ return activeStage === 'second'
113
+ ? {
114
+ firstStartedAtMs: null,
115
+ secondStartedAtMs: expiredStartedAtMs,
116
+ }
117
+ : {
118
+ firstStartedAtMs: expiredStartedAtMs,
119
+ secondStartedAtMs: null,
120
+ };
121
+ }
122
+ return null;
123
+ };
124
+ const createExpiredDiscountState = (input = {}) => {
125
+ var _a, _b, _c;
126
+ return {
127
+ status: 'expired',
128
+ stage: (_a = input.stage) !== null && _a !== void 0 ? _a : 'completed',
129
+ couponId: null,
130
+ discountPercent: null,
131
+ previousDiscountPercent: null,
132
+ expiresAtMs: null,
133
+ remainingSeconds: 0,
134
+ firstStartedAtMs: (_b = input.firstStartedAtMs) !== null && _b !== void 0 ? _b : null,
135
+ secondStartedAtMs: (_c = input.secondStartedAtMs) !== null && _c !== void 0 ? _c : null,
136
+ };
137
+ };
138
+ const createDiscountStateFromStarts = (input) => {
139
+ var _a, _b;
140
+ if (typeof input.secondStartedAtMs === 'number') {
141
+ const activeDiscount = getDiscount(input.discounts, 'second');
142
+ const expiresAtMs = input.secondStartedAtMs + getDiscountDurationMs(input.discounts, 'second');
143
+ const remainingSeconds = clampRemainingSeconds((expiresAtMs - input.nowMs) / 1000);
144
+ if (remainingSeconds <= 0) {
145
+ return createExpiredDiscountState({
146
+ stage: 'completed',
147
+ firstStartedAtMs: input.firstStartedAtMs,
148
+ secondStartedAtMs: input.secondStartedAtMs,
149
+ });
150
+ }
151
+ return {
152
+ status: 'active',
153
+ stage: 'second',
154
+ couponId: activeDiscount.couponId,
155
+ discountPercent: activeDiscount.discountPercent,
156
+ previousDiscountPercent: (_a = activeDiscount.previousDiscountPercent) !== null && _a !== void 0 ? _a : null,
157
+ expiresAtMs,
158
+ remainingSeconds,
159
+ firstStartedAtMs: input.firstStartedAtMs,
160
+ secondStartedAtMs: input.secondStartedAtMs,
161
+ };
162
+ }
163
+ if (typeof input.firstStartedAtMs !== 'number') {
164
+ return createDiscountStateFromStarts(Object.assign(Object.assign({}, input), { firstStartedAtMs: input.nowMs, secondStartedAtMs: null }));
165
+ }
166
+ const activeDiscount = getDiscount(input.discounts, 'first');
167
+ const expiresAtMs = input.firstStartedAtMs + getDiscountDurationMs(input.discounts, 'first');
168
+ const remainingSeconds = clampRemainingSeconds((expiresAtMs - input.nowMs) / 1000);
169
+ if (remainingSeconds <= 0) {
170
+ return createExpiredDiscountState({
171
+ stage: 'first',
172
+ firstStartedAtMs: input.firstStartedAtMs,
173
+ secondStartedAtMs: input.secondStartedAtMs,
174
+ });
175
+ }
176
+ return {
177
+ status: 'active',
178
+ stage: 'first',
179
+ couponId: activeDiscount.couponId,
180
+ discountPercent: activeDiscount.discountPercent,
181
+ previousDiscountPercent: (_b = activeDiscount.previousDiscountPercent) !== null && _b !== void 0 ? _b : null,
182
+ expiresAtMs,
183
+ remainingSeconds,
184
+ firstStartedAtMs: input.firstStartedAtMs,
185
+ secondStartedAtMs: input.secondStartedAtMs,
186
+ };
187
+ };
188
+ export const resolvePaywallDiscountState = (input) => {
189
+ const snapshot = parseStoredSnapshot(input.storedValue);
190
+ if (!snapshot) {
191
+ return createDiscountStateFromStarts({
192
+ discounts: input.discounts,
193
+ nowMs: input.nowMs,
194
+ firstStartedAtMs: input.nowMs,
195
+ secondStartedAtMs: null,
196
+ });
197
+ }
198
+ const storedStarts = getStoredDiscountStarts(input.discounts, snapshot, input.nowMs);
199
+ if (!storedStarts) {
200
+ return createDiscountStateFromStarts({
201
+ discounts: input.discounts,
202
+ nowMs: input.nowMs,
203
+ firstStartedAtMs: input.nowMs,
204
+ secondStartedAtMs: null,
205
+ });
206
+ }
207
+ return createDiscountStateFromStarts({
208
+ discounts: input.discounts,
209
+ nowMs: input.nowMs,
210
+ firstStartedAtMs: storedStarts.firstStartedAtMs,
211
+ secondStartedAtMs: storedStarts.secondStartedAtMs,
212
+ });
213
+ };
214
+ export const serializePaywallDiscountState = (state) => {
215
+ return JSON.stringify({
216
+ firstStartedAt: state.firstStartedAtMs,
217
+ secondStartedAt: state.secondStartedAtMs,
218
+ });
219
+ };
220
+ export const advancePaywallDiscountState = (input) => {
221
+ return createDiscountStateFromStarts({
222
+ discounts: input.discounts,
223
+ nowMs: input.nowMs,
224
+ firstStartedAtMs: input.state.firstStartedAtMs,
225
+ secondStartedAtMs: input.state.secondStartedAtMs,
226
+ });
227
+ };
228
+ export const activateSecondPaywallDiscount = (input) => {
229
+ var _a;
230
+ if (typeof input.state.secondStartedAtMs === 'number' ||
231
+ input.state.stage === 'completed') {
232
+ return input.state;
233
+ }
234
+ return createDiscountStateFromStarts({
235
+ discounts: input.discounts,
236
+ nowMs: input.nowMs,
237
+ firstStartedAtMs: (_a = input.state.firstStartedAtMs) !== null && _a !== void 0 ? _a : input.nowMs,
238
+ secondStartedAtMs: input.nowMs,
239
+ });
240
+ };
241
+ export const getDiscountedAmountCents = (amountCents, discountPercent) => {
242
+ const normalizedAmount = Number.isFinite(amountCents)
243
+ ? Math.max(0, Math.round(amountCents))
244
+ : 0;
245
+ const normalizedDiscountPercent = Math.min(100, Math.max(0, Math.round(discountPercent)));
246
+ return Math.max(0, Math.round((normalizedAmount * (100 - normalizedDiscountPercent)) / 100));
247
+ };
248
+ const formatMoneyFromCents = (amountCents, currency, locale) => {
249
+ return new Intl.NumberFormat(locale, {
250
+ style: 'currency',
251
+ currency,
252
+ minimumFractionDigits: 2,
253
+ maximumFractionDigits: 2,
254
+ }).format(amountCents / 100);
255
+ };
256
+ const getBillingDays = (plan) => {
257
+ const interval = plan.billingInterval;
258
+ if (!interval) {
259
+ return null;
260
+ }
261
+ const intervalCount = plan.billingIntervalCount && plan.billingIntervalCount > 0
262
+ ? plan.billingIntervalCount
263
+ : 1;
264
+ return BILLING_INTERVAL_DAYS[interval] * intervalCount;
265
+ };
266
+ export const buildDiscountedPaywallPlans = (input) => {
267
+ var _a, _b;
268
+ const currency = ((_a = input.currency) === null || _a === void 0 ? void 0 : _a.trim()) || 'USD';
269
+ const locale = ((_b = input.locale) === null || _b === void 0 ? void 0 : _b.trim()) || 'en-US';
270
+ return input.plans.map((plan) => {
271
+ var _a;
272
+ const billingDays = getBillingDays(plan);
273
+ const hasActiveDiscount = Boolean(input.couponId) && typeof input.discountPercent === 'number';
274
+ const discountedAmountCents = hasActiveDiscount
275
+ ? getDiscountedAmountCents(plan.amountCents, (_a = input.discountPercent) !== null && _a !== void 0 ? _a : 0)
276
+ : plan.amountCents;
277
+ const discountedPerDayAmount = billingDays && billingDays > 0
278
+ ? formatMoneyFromCents(Math.round(discountedAmountCents / billingDays), currency, locale)
279
+ : plan.perDayAmount;
280
+ return Object.assign(Object.assign({}, plan), { amountCents: discountedAmountCents, priceLabel: hasActiveDiscount
281
+ ? formatMoneyFromCents(discountedAmountCents, currency, locale)
282
+ : plan.priceLabel, oldPriceLabel: hasActiveDiscount ? plan.priceLabel : undefined, perDayAmount: hasActiveDiscount ? discountedPerDayAmount : plan.perDayAmount, baseAmountCents: plan.amountCents, basePriceLabel: plan.priceLabel, basePerDayAmount: plan.perDayAmount, oldPerDayAmount: hasActiveDiscount ? plan.perDayAmount : null, couponId: hasActiveDiscount ? input.couponId : null, hasActiveDiscount });
283
+ });
284
+ };
285
+ export const getPaywallPromoDisplayName = (referenceMs) => {
286
+ var _a;
287
+ const referenceDate = new Date(referenceMs);
288
+ if (Number.isNaN(referenceDate.getTime())) {
289
+ return 'discount_offer';
290
+ }
291
+ const monthLabel = (_a = PROMO_MONTHS[referenceDate.getMonth()]) !== null && _a !== void 0 ? _a : 'discount';
292
+ return `${monthLabel}_${referenceDate.getFullYear()}`;
293
+ };
294
+ const formatRecurringCadence = (plan) => {
295
+ const interval = plan.billingInterval;
296
+ if (!interval) {
297
+ return 'on your selected billing schedule';
298
+ }
299
+ const count = plan.billingIntervalCount && plan.billingIntervalCount > 0
300
+ ? plan.billingIntervalCount
301
+ : 1;
302
+ if (count === 1) {
303
+ return `every ${interval}`;
304
+ }
305
+ return `every ${count} ${interval}s`;
306
+ };
307
+ export const buildPaywallRenewalDisclaimer = (input) => {
308
+ var _a;
309
+ const introLabel = input.hasActiveDiscount ? 'discounted intro plan' : 'selected plan';
310
+ const productPrefix = ((_a = input.productName) === null || _a === void 0 ? void 0 : _a.trim()) ? `${input.productName.trim()} ` : '';
311
+ return `Without cancellation, before the selected ${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.`;
312
+ };
@@ -0,0 +1,28 @@
1
+ import type { BillingFallbackPlanConfig, BillingPlanCatalog, BillingPlanMappingCatalog } from '../config/billing.config.js';
2
+ export type RemoteProjectBillingPlan = {
3
+ id: string;
4
+ key: string;
5
+ projectPlanId: string | null;
6
+ providerPlanId: string;
7
+ displayName: string | null;
8
+ description: string | null;
9
+ interval: string | null;
10
+ intervalCount: number | null;
11
+ trialPeriodDays: number | null;
12
+ currency: string | null;
13
+ amountMinor: number | null;
14
+ amountDecimal: string | null;
15
+ metadata: Record<string, unknown>;
16
+ isActive: boolean;
17
+ };
18
+ export type ResolvedMappedProjectBillingPlan = RemoteProjectBillingPlan & {
19
+ funnelKey: string;
20
+ };
21
+ export type BillingPlanListItem = BillingFallbackPlanConfig & {
22
+ key: string;
23
+ };
24
+ export declare const isBillingPlanMappingCatalog: (value: unknown) => value is BillingPlanMappingCatalog;
25
+ export declare const buildBillingPlanMappingCatalog: (planCatalog: BillingPlanCatalog) => BillingPlanMappingCatalog;
26
+ export declare const buildBillingPlanList: (planCatalog: BillingPlanCatalog) => BillingPlanListItem[];
27
+ export declare const getFallbackBillingPlans: (planCatalog?: BillingPlanCatalog | readonly BillingFallbackPlanConfig[]) => BillingFallbackPlanConfig[];
28
+ export declare const resolveMappedProjectPlans: (mappingCatalog: BillingPlanMappingCatalog, projectPlans: readonly RemoteProjectBillingPlan[]) => ResolvedMappedProjectBillingPlan[];
@@ -0,0 +1,60 @@
1
+ const isRecord = (value) => {
2
+ return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
3
+ };
4
+ const isBillingPlanMappingConfig = (value) => {
5
+ if (!isRecord(value) || typeof value.projectPlanId !== 'string') {
6
+ return false;
7
+ }
8
+ return Object.keys(value).every((key) => key === 'projectPlanId');
9
+ };
10
+ export const isBillingPlanMappingCatalog = (value) => {
11
+ if (!isRecord(value)) {
12
+ return false;
13
+ }
14
+ const entries = Object.values(value);
15
+ return entries.length > 0 && entries.every((entry) => isBillingPlanMappingConfig(entry));
16
+ };
17
+ export const buildBillingPlanMappingCatalog = (planCatalog) => {
18
+ return Object.fromEntries(Object.entries(planCatalog).flatMap(([key, plan]) => {
19
+ const planKey = key.trim();
20
+ const projectPlanId = plan.projectPlanId.trim();
21
+ if (!planKey || !projectPlanId) {
22
+ return [];
23
+ }
24
+ return [
25
+ [
26
+ planKey,
27
+ {
28
+ projectPlanId,
29
+ },
30
+ ],
31
+ ];
32
+ }));
33
+ };
34
+ export const buildBillingPlanList = (planCatalog) => {
35
+ return Object.entries(planCatalog).map(([key, plan]) => (Object.assign({ key }, plan)));
36
+ };
37
+ export const getFallbackBillingPlans = (planCatalog) => {
38
+ if (!planCatalog) {
39
+ return [];
40
+ }
41
+ if (Array.isArray(planCatalog)) {
42
+ return [...planCatalog];
43
+ }
44
+ return Object.values(planCatalog);
45
+ };
46
+ export const resolveMappedProjectPlans = (mappingCatalog, projectPlans) => {
47
+ const projectPlanById = new Map(projectPlans.flatMap((plan) => {
48
+ const keys = [plan.projectPlanId, plan.id].filter((key) => typeof key === 'string' && key.length > 0);
49
+ return keys.map((key) => [key, plan]);
50
+ }));
51
+ return Object.entries(mappingCatalog).flatMap(([funnelKey, mapping]) => {
52
+ const projectPlan = projectPlanById.get(mapping.projectPlanId) || null;
53
+ if (!projectPlan) {
54
+ return [];
55
+ }
56
+ return [
57
+ Object.assign(Object.assign({}, projectPlan), { funnelKey }),
58
+ ];
59
+ });
60
+ };
@@ -0,0 +1,6 @@
1
+ import type { BillingPlanCatalog } from '../config/billing.config.js';
2
+ export type RuntimeModeBillingPlanCatalog = {
3
+ test?: BillingPlanCatalog;
4
+ live?: BillingPlanCatalog;
5
+ };
6
+ export declare const createRuntimeModeBillingPlanCatalog: (catalogsByMode: RuntimeModeBillingPlanCatalog) => BillingPlanCatalog;
@@ -0,0 +1,24 @@
1
+ import { getRuntimeMode } from '@funnelsgrove/runtime';
2
+ const EMPTY_BILLING_PLAN_CATALOG = {};
3
+ const resolveBillingPlanCatalog = (catalogsByMode) => {
4
+ const preferredMode = getRuntimeMode() === 'test' ? 'test' : 'live';
5
+ const fallbackMode = preferredMode === 'test' ? 'live' : 'test';
6
+ return catalogsByMode[preferredMode] || catalogsByMode[fallbackMode] || EMPTY_BILLING_PLAN_CATALOG;
7
+ };
8
+ export const createRuntimeModeBillingPlanCatalog = (catalogsByMode) => {
9
+ return new Proxy({}, {
10
+ get(_target, property) {
11
+ return Reflect.get(resolveBillingPlanCatalog(catalogsByMode), property);
12
+ },
13
+ getOwnPropertyDescriptor(_target, property) {
14
+ const descriptor = Reflect.getOwnPropertyDescriptor(resolveBillingPlanCatalog(catalogsByMode), property);
15
+ return descriptor ? Object.assign(Object.assign({}, descriptor), { configurable: true }) : undefined;
16
+ },
17
+ has(_target, property) {
18
+ return Reflect.has(resolveBillingPlanCatalog(catalogsByMode), property);
19
+ },
20
+ ownKeys() {
21
+ return Reflect.ownKeys(resolveBillingPlanCatalog(catalogsByMode));
22
+ },
23
+ });
24
+ };
@@ -1,6 +1,8 @@
1
1
  import { type Stripe } from '@stripe/stripe-js';
2
- import { type BillingFallbackPlanConfig, type BillingPlanCatalog } from '../config/billing.config';
2
+ import { type BillingFallbackPlanConfig, type BillingPlanCatalog, type BillingPlanInterval, type BillingPlanInputCatalog } from '../config/billing.config.js';
3
+ import { type ResolvedCountryPricing } from '@funnelsgrove/runtime';
3
4
  import type { RuntimeMode } from '@funnelsgrove/runtime';
5
+ import { type RemoteProjectBillingPlan } from './planCatalog.service.js';
4
6
  export type PaywallPlan = {
5
7
  id: string;
6
8
  title: string;
@@ -15,19 +17,46 @@ export type PaywallPlan = {
15
17
  comparison?: BillingFallbackPlanConfig['comparison'];
16
18
  checkoutOriginalAmountCents?: number;
17
19
  checkoutSummaryLabel?: string;
20
+ billingInterval?: BillingPlanInterval;
21
+ billingIntervalCount?: number;
18
22
  source: 'stripe' | 'config';
19
23
  };
20
- export declare function getFallbackPlans(planCatalog?: BillingPlanCatalog | readonly BillingFallbackPlanConfig[]): readonly PaywallPlan[];
21
- export declare function getDefaultPlanId(plans: readonly PaywallPlan[]): string | null;
22
- export declare function getStripeSyncedPlans(mode: RuntimeMode): Promise<readonly PaywallPlan[]>;
23
- export declare function getPaywallPlans(mode: RuntimeMode, planCatalog?: BillingPlanCatalog | readonly BillingFallbackPlanConfig[]): Promise<readonly PaywallPlan[]>;
24
+ export type StripeRuntimeConfigOverrides = {
25
+ apiBaseUrl?: string | null;
26
+ funnelId?: string | null;
27
+ funnelVersionId?: string | null;
28
+ funnelSdkPublishableKey?: string | null;
29
+ };
30
+ export type PaywallPlanResolutionOptions = {
31
+ locale?: string | null;
32
+ pricing?: ResolvedCountryPricing<string> | null;
33
+ };
34
+ export declare const buildPaywallPlanResolutionSignature: (options?: PaywallPlanResolutionOptions) => string;
35
+ export declare function buildConfigPaywallPlans(planCatalog?: BillingPlanCatalog | readonly BillingFallbackPlanConfig[], orderedPlanIds?: readonly string[]): readonly PaywallPlan[];
36
+ export declare function getFallbackPlans(planCatalog?: BillingPlanInputCatalog | readonly BillingFallbackPlanConfig[], options?: PaywallPlanResolutionOptions): readonly PaywallPlan[];
37
+ export declare function getOrderedPaywallPlans(plans: readonly PaywallPlan[], orderedPlanIds: readonly string[]): readonly PaywallPlan[];
38
+ export declare function getDefaultPlanId(plans: readonly PaywallPlan[], pricing?: ResolvedCountryPricing<string> | null): string | null;
39
+ export declare function getPaywallPlanSelectionValue(plan: Pick<PaywallPlan, 'id' | 'providerPlanId'>): string;
40
+ export declare function findPaywallPlan(plans: readonly PaywallPlan[], selectionValue: string | null | undefined): PaywallPlan | null;
41
+ export declare function getDefaultPlanSelectionValue(plans: readonly PaywallPlan[], pricing?: ResolvedCountryPricing<string> | null): string | null;
42
+ export declare function getStripeSyncedPlans(mode: RuntimeMode, planCatalog?: BillingPlanInputCatalog | readonly BillingFallbackPlanConfig[], options?: PaywallPlanResolutionOptions): Promise<readonly PaywallPlan[]>;
43
+ export declare function resolvePaywallPlansFromProjectPlans(projectPlans: readonly RemoteProjectBillingPlan[], planCatalog?: BillingPlanInputCatalog | readonly BillingFallbackPlanConfig[], options?: PaywallPlanResolutionOptions): readonly PaywallPlan[];
44
+ export declare function getPaywallPlans(mode: RuntimeMode, planCatalog?: BillingPlanInputCatalog | readonly BillingFallbackPlanConfig[], options?: PaywallPlanResolutionOptions): Promise<readonly PaywallPlan[]>;
24
45
  export declare function isStripeConfigured(mode: RuntimeMode): boolean;
25
46
  export declare function getStripePromise(mode: RuntimeMode, runtimePublishableKey?: string | null): Promise<Stripe | null> | null;
47
+ type CheckoutSessionPlanInput = Pick<PaywallPlan, 'amountCents' | 'billingInterval' | 'billingIntervalCount' | 'description' | 'id' | 'providerPlanId' | 'title'>;
48
+ type CheckoutPlanRecurringConfig = {
49
+ interval: BillingPlanInterval;
50
+ intervalCount: number;
51
+ };
52
+ export declare const resolveCheckoutPlanRecurringConfig: (plan: CheckoutSessionPlanInput) => CheckoutPlanRecurringConfig | null;
26
53
  type CreatePaymentIntentInput = {
27
54
  planId: string;
28
55
  amountCents: number;
29
- externalUserId?: string | null;
56
+ couponId?: string | null;
57
+ user_id?: string | null;
30
58
  environment?: RuntimeMode;
59
+ runtimeConfig?: StripeRuntimeConfigOverrides;
31
60
  };
32
61
  type CreatePaymentIntentResponse = {
33
62
  clientSecret: string;
@@ -35,4 +64,35 @@ type CreatePaymentIntentResponse = {
35
64
  environment?: 'test' | 'live';
36
65
  };
37
66
  export declare function createStripePaymentIntent(input: CreatePaymentIntentInput): Promise<CreatePaymentIntentResponse>;
67
+ type CreateSubscriptionCheckoutInput = {
68
+ plan: CheckoutSessionPlanInput;
69
+ couponId?: string | null;
70
+ customerEmail?: string | null;
71
+ user_id?: string | null;
72
+ environment?: RuntimeMode;
73
+ runtimeConfig?: StripeRuntimeConfigOverrides;
74
+ };
75
+ type CreateSubscriptionCheckoutResponse = {
76
+ clientSecret: string;
77
+ subscriptionId?: string;
78
+ stripePublishableKey?: string;
79
+ environment?: 'test' | 'live';
80
+ };
81
+ export declare function createStripeSubscriptionCheckout(input: CreateSubscriptionCheckoutInput): Promise<CreateSubscriptionCheckoutResponse>;
82
+ type CreateCheckoutSessionInput = {
83
+ plan: CheckoutSessionPlanInput;
84
+ successUrl: string;
85
+ cancelUrl: string;
86
+ couponId?: string | null;
87
+ customerEmail?: string | null;
88
+ user_id?: string | null;
89
+ environment?: RuntimeMode;
90
+ runtimeConfig?: StripeRuntimeConfigOverrides;
91
+ };
92
+ type CreateCheckoutSessionResponse = {
93
+ sessionId?: string;
94
+ url: string;
95
+ };
96
+ export declare function createStripeCheckoutSession(input: CreateCheckoutSessionInput): Promise<CreateCheckoutSessionResponse>;
97
+ export declare function redirectToStripeCheckout(input: CreateCheckoutSessionInput): Promise<string>;
38
98
  export {};