@funnelsgrove/runtime 0.1.44 → 0.1.46
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 +44 -39
- package/dist/index.d.ts +1 -0
- package/dist/index.js +6 -0
- package/dist/runtime/offer-set-runtime.d.ts +2 -0
- package/dist/runtime/offer-set-runtime.js +16 -5
- package/dist/runtime/posthog-flags.d.ts +1 -0
- package/dist/runtime/posthog-flags.js +12 -1
- package/dist/services/api.service.js +42 -137
- package/dist/services/funnel-sdk.service.d.ts +207 -0
- package/dist/services/funnel-sdk.service.js +319 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,60 +2,65 @@
|
|
|
2
2
|
|
|
3
3
|
Shared funnel runtime contracts and helpers.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Read this file before editing shared funnel mechanics. Deeper runtime behavior is
|
|
6
|
+
documented in [`docs/product-specs/funnel-template-runtime.md`](../../docs/product-specs/funnel-template-runtime.md).
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
- The build normalizes generated relative ESM imports to explicit `.js` files for published package consumers.
|
|
8
|
+
## Owns funnel mechanics
|
|
9
|
+
|
|
10
|
+
`@funnelsgrove/runtime` owns the reusable mechanics that every generated or published funnel depends on: manifests, routing, env resolution, funnel state, preview/runtime detection, and generic runtime UI. Funnels bring their own copy, visual design, billing catalog, and step JSX.
|
|
11
11
|
|
|
12
12
|
## Use This Package For
|
|
13
13
|
|
|
14
14
|
- manifest types and validation
|
|
15
15
|
- route resolution and entry-point handling
|
|
16
|
+
- runtime mode and public env/config resolution
|
|
16
17
|
- experiment assignment helpers
|
|
17
18
|
- preview-bridge parsing and preview/runtime detection
|
|
18
19
|
- published theme contract and CSS variable helpers
|
|
19
|
-
- funnel context,
|
|
20
|
-
-
|
|
20
|
+
- funnel context, flow controller, and generic runtime UI primitives
|
|
21
|
+
- funnel-scoped state/storage helpers
|
|
21
22
|
- browser-safe API client helpers used by funnels
|
|
22
|
-
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
-
|
|
28
|
-
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
-
|
|
33
|
-
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
-
|
|
38
|
-
-
|
|
39
|
-
-
|
|
40
|
-
-
|
|
41
|
-
-
|
|
42
|
-
- subscription handoff link rendering for iOS, Android, and desktop/web fallbacks from runtime env
|
|
43
|
-
- subscription handoff browser-only values are resolved after mount so published success routes hydrate without text mismatches
|
|
44
|
-
- public subscription summaries with provider plan and period metadata for funnel-local management screens
|
|
45
|
-
- a bottom-right `RuntimeDevInfoBox` that funnels can mount in test mode to inspect public runtime config with one canonical env/config name per row, copy the current funnel user id/email, switch to live mode, and clear funnel-scoped paywall state
|
|
23
|
+
- subscription handoff and subscription management screens whose copy stays funnel-local
|
|
24
|
+
- test-mode developer info surfaces for funnel preview tooling
|
|
25
|
+
|
|
26
|
+
## Do Not Use This Package For
|
|
27
|
+
|
|
28
|
+
- funnel-specific step JSX
|
|
29
|
+
- brand/theme assets
|
|
30
|
+
- billing catalog data
|
|
31
|
+
- analytics transport
|
|
32
|
+
- Stripe checkout UI
|
|
33
|
+
- funnel-specific developer widget copy
|
|
34
|
+
- direct provider SDK calls
|
|
35
|
+
|
|
36
|
+
## Public Surfaces
|
|
37
|
+
|
|
38
|
+
- `config/*`: manifest, theme, font, env, and builder-preview contracts.
|
|
39
|
+
- `runtime/*`: flow, routing, attribution, feature flags, preview bridge, URL attributes, and manifest validation.
|
|
40
|
+
- `services/*`: API client, runtime mode, public env, project env, logger, and funnel state helpers.
|
|
41
|
+
- `components/*`: generic runtime context, subscription handoff/management, editor panel, dev info, and base controls.
|
|
42
|
+
- `content/*` and `steps/types`: shared step content and taxonomy contracts.
|
|
46
43
|
|
|
47
44
|
## Storage Conventions
|
|
48
45
|
|
|
49
46
|
- runtime user ids stay funnel-scoped under `funnel:<funnelId>:user-id`
|
|
50
47
|
- runtime-generated user ids use the `u_` prefix
|
|
51
48
|
- paywall state is stored through runtime helpers under `fg_state:<funnelId>`
|
|
52
|
-
- developer reset controls should clear paywall state through `clearPaywallStateValue`, including
|
|
49
|
+
- developer reset controls should clear paywall state through `clearPaywallStateValue`, including funnel-provided compatibility keys when a live funnel still needs them
|
|
53
50
|
|
|
54
|
-
##
|
|
51
|
+
## Engineering Rules
|
|
55
52
|
|
|
56
|
-
-
|
|
57
|
-
-
|
|
58
|
-
-
|
|
59
|
-
-
|
|
60
|
-
|
|
61
|
-
|
|
53
|
+
- Do not import `@funnelsgrove/payments` or `@funnelsgrove/analytics` here for checkout or transport orchestration.
|
|
54
|
+
- Runtime UI must be generic and copy-injectable.
|
|
55
|
+
- Browser-only values must resolve after mount when SSR hydration can be affected.
|
|
56
|
+
- Keep env mapping centralized in `config/env.config.ts`.
|
|
57
|
+
|
|
58
|
+
## Build, Test, Publish
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
npm run test:run --workspace @funnelsgrove/runtime
|
|
62
|
+
npm run build --workspace @funnelsgrove/runtime
|
|
63
|
+
npm publish --workspace @funnelsgrove/runtime --access public
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
The build normalizes generated relative ESM imports to explicit `.js` files for published package consumers.
|
package/dist/index.d.ts
CHANGED
|
@@ -21,6 +21,7 @@ export * from './runtime/subscription-handoff.js';
|
|
|
21
21
|
export * from './runtime/url-user-attributes.js';
|
|
22
22
|
export * from './runtime/use-url-user-attributes-sync.js';
|
|
23
23
|
export * from './services/api.service.js';
|
|
24
|
+
export * from './services/funnel-sdk.service.js';
|
|
24
25
|
export * from './services/funnel-state.service.js';
|
|
25
26
|
export * from './services/logger.js';
|
|
26
27
|
export * from './services/preview-frame.service.js';
|
package/dist/index.js
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
|
+
// Shared config contracts.
|
|
1
2
|
export * from './config/builder-preview.protocol.js';
|
|
2
3
|
export * from './config/env.config.js';
|
|
3
4
|
export * from './config/funnel.manifest.types.js';
|
|
4
5
|
export * from './config/funnel.experiments.types.js';
|
|
5
6
|
export * from './config/funnel-theme.js';
|
|
6
7
|
export * from './config/font-config.js';
|
|
8
|
+
// Structured content contracts.
|
|
7
9
|
export * from './content/step-content.js';
|
|
10
|
+
// Runtime mechanics.
|
|
8
11
|
export * from './runtime/experiment-assignment.js';
|
|
9
12
|
export * from './runtime/browser-helpers.js';
|
|
10
13
|
export * from './runtime/funnel-flow.js';
|
|
@@ -20,7 +23,9 @@ export * from './runtime/route-resolver.js';
|
|
|
20
23
|
export * from './runtime/subscription-handoff.js';
|
|
21
24
|
export * from './runtime/url-user-attributes.js';
|
|
22
25
|
export * from './runtime/use-url-user-attributes-sync.js';
|
|
26
|
+
// Browser-safe services.
|
|
23
27
|
export * from './services/api.service.js';
|
|
28
|
+
export * from './services/funnel-sdk.service.js';
|
|
24
29
|
export * from './services/funnel-state.service.js';
|
|
25
30
|
export * from './services/logger.js';
|
|
26
31
|
export * from './services/preview-frame.service.js';
|
|
@@ -28,6 +33,7 @@ export * from './services/project-env.js';
|
|
|
28
33
|
export * from './services/public-env.js';
|
|
29
34
|
export * from './services/runtime-api.config.js';
|
|
30
35
|
export * from './services/runtime-mode.service.js';
|
|
36
|
+
// SDK and generic runtime UI.
|
|
31
37
|
export * from './sdk/userAnswers.js';
|
|
32
38
|
export * from './components/FunnelContext.js';
|
|
33
39
|
export * from './components/FunnelEditorPanel.js';
|
|
@@ -2,6 +2,7 @@ import type { RuntimeMode } from '../services/runtime-mode.service.js';
|
|
|
2
2
|
export type GeneratedOfferSetItemKind = 'plan' | 'upsell' | 'downsell';
|
|
3
3
|
export type GeneratedOfferSetPlanModeConfig = {
|
|
4
4
|
readonly providerPlanId?: string | null;
|
|
5
|
+
readonly paymentMode?: string | null;
|
|
5
6
|
readonly priceLabel?: string | null;
|
|
6
7
|
readonly perDayAmount?: string | null;
|
|
7
8
|
readonly perDayLabel?: string | null;
|
|
@@ -63,6 +64,7 @@ export type OfferSetRuntimeDiscount = {
|
|
|
63
64
|
export type OfferSetRuntimePlanOverrides = {
|
|
64
65
|
projectPlanId?: string;
|
|
65
66
|
providerPlanId?: string;
|
|
67
|
+
paymentMode?: 'one_time';
|
|
66
68
|
analyticsPurchaseValue?: number;
|
|
67
69
|
title?: string;
|
|
68
70
|
description?: string;
|
|
@@ -34,6 +34,15 @@ const readPositiveInteger = (value) => {
|
|
|
34
34
|
const numeric = readFiniteNumber(value);
|
|
35
35
|
return numeric !== null && numeric > 0 ? Math.round(numeric) : null;
|
|
36
36
|
};
|
|
37
|
+
const readOneTimePaymentMode = (value) => {
|
|
38
|
+
if (typeof value !== 'string') {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
const normalized = value.trim().toLowerCase().replace(/[\s-]+/g, '_');
|
|
42
|
+
return normalized === 'one_time' || normalized === 'payment' || normalized === 'payment_intent'
|
|
43
|
+
? 'one_time'
|
|
44
|
+
: null;
|
|
45
|
+
};
|
|
37
46
|
const getOfferSetItemKind = (item) => {
|
|
38
47
|
var _a;
|
|
39
48
|
const metadata = isRecord(item.metadata) ? item.metadata : {};
|
|
@@ -117,7 +126,7 @@ const readGeneratedPlanNumberValue = (item, mode, itemKeys, metadataKeys = itemK
|
|
|
117
126
|
return ((_b = (_a = readFiniteNumberField(modeConfig, metadataKeys)) !== null && _a !== void 0 ? _a : itemValue) !== null && _b !== void 0 ? _b : readFiniteNumberField(metadata, metadataKeys));
|
|
118
127
|
};
|
|
119
128
|
const buildGeneratedPlanOverrides = (item, mode, isDefault) => {
|
|
120
|
-
var _a, _b, _c, _d, _e, _f;
|
|
129
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
|
|
121
130
|
const metadata = readOfferItemMetadata(item);
|
|
122
131
|
const providerPlanId = readProviderPlanId(item, mode);
|
|
123
132
|
const projectPlanId = (_a = readString(item.projectBillingPlanId)) !== null && _a !== void 0 ? _a : readStringField(metadata, ['projectBillingPlanId', 'projectPlanId', 'project_billing_plan_id']);
|
|
@@ -130,10 +139,12 @@ const buildGeneratedPlanOverrides = (item, mode, isDefault) => {
|
|
|
130
139
|
const billingInterval = readGeneratedPlanStringValue(item, mode, ['billingInterval']);
|
|
131
140
|
const billingIntervalCount = readGeneratedPlanNumberValue(item, mode, ['billingIntervalCount']);
|
|
132
141
|
const amountCents = readGeneratedPlanNumberValue(item, mode, ['amountCents']);
|
|
133
|
-
const
|
|
134
|
-
const
|
|
135
|
-
const
|
|
136
|
-
|
|
142
|
+
const modeConfig = readGeneratedModeConfig(item, mode);
|
|
143
|
+
const paymentMode = (_k = (_j = (_h = (_g = (_f = (_e = (_d = readOneTimePaymentMode(modeConfig.paymentMode)) !== null && _d !== void 0 ? _d : readOneTimePaymentMode(modeConfig.payment_mode)) !== null && _e !== void 0 ? _e : readOneTimePaymentMode(modeConfig.checkoutMode)) !== null && _f !== void 0 ? _f : readOneTimePaymentMode(modeConfig.checkout_mode)) !== null && _g !== void 0 ? _g : readOneTimePaymentMode(metadata.paymentMode)) !== null && _h !== void 0 ? _h : readOneTimePaymentMode(metadata.payment_mode)) !== null && _j !== void 0 ? _j : readOneTimePaymentMode(metadata.checkoutMode)) !== null && _k !== void 0 ? _k : readOneTimePaymentMode(metadata.checkout_mode);
|
|
144
|
+
const analyticsPurchaseValue = (_l = readFiniteNumber(item.analyticsPurchaseValue)) !== null && _l !== void 0 ? _l : readFiniteNumberField(metadata, ['analyticsPurchaseValue']);
|
|
145
|
+
const featuredTag = (_m = readString(item.featuredTag)) !== null && _m !== void 0 ? _m : readStringField(metadata, ['featuredTag']);
|
|
146
|
+
const oldPriceLabel = (_o = readString(item.oldPriceLabel)) !== null && _o !== void 0 ? _o : readStringField(metadata, ['oldPriceLabel']);
|
|
147
|
+
return Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, (projectPlanId ? { projectPlanId } : {})), (providerPlanId ? { providerPlanId } : {})), (paymentMode ? { paymentMode } : {})), (analyticsPurchaseValue !== null ? { analyticsPurchaseValue } : {})), (title ? { title } : {})), (description ? { description } : {})), (priceLabel ? { priceLabel } : {})), (perDayAmount ? { perDayAmount } : {})), (perDayLabel ? { perDayLabel } : {})), (amountCents !== null ? { amountCents: Math.max(0, Math.round(amountCents)) } : {})), (checkoutSummaryLabel ? { checkoutSummaryLabel } : {})), (billingInterval ? { billingInterval } : {})), (billingIntervalCount !== null ? { billingIntervalCount: Math.max(1, Math.round(billingIntervalCount)) } : {})), (featuredTag ? { featuredTag } : {})), (oldPriceLabel ? { oldPriceLabel } : {})), (isDefault ? { isDefault: true } : {}));
|
|
137
148
|
};
|
|
138
149
|
export const applyGeneratedOfferSetToPlanCatalog = (baseCatalog, mode, offerSet) => {
|
|
139
150
|
var _a, _b, _c;
|
|
@@ -3,6 +3,7 @@ import { runtimePublicConfig } from '../services/public-env.js';
|
|
|
3
3
|
let initialized = false;
|
|
4
4
|
let readyPromise = null;
|
|
5
5
|
export const POSTHOG_FEATURE_FLAG_READY_TIMEOUT_MS = 2500;
|
|
6
|
+
export const POSTHOG_SCOPED_FLAG_RELOAD_RETRY_MS = 300;
|
|
6
7
|
const STORED_PERSON_PROPERTIES_KEY = '$stored_person_properties';
|
|
7
8
|
const buildFlagScopeProperties = (config) => {
|
|
8
9
|
var _a, _b;
|
|
@@ -84,7 +85,17 @@ export const bootstrapPostHog = (config) => {
|
|
|
84
85
|
bootstrap: { distinctID: config.distinctId },
|
|
85
86
|
loaded: () => {
|
|
86
87
|
let unsubscribe = null;
|
|
87
|
-
|
|
88
|
+
let scopedReloadRetried = false;
|
|
89
|
+
unsubscribe = posthog.onFeatureFlags((flags) => {
|
|
90
|
+
if (Object.keys(scopeProperties).length > 0 &&
|
|
91
|
+
flags.length === 0 &&
|
|
92
|
+
!scopedReloadRetried) {
|
|
93
|
+
scopedReloadRetried = true;
|
|
94
|
+
setTimeout(() => {
|
|
95
|
+
posthog.reloadFeatureFlags();
|
|
96
|
+
}, POSTHOG_SCOPED_FLAG_RELOAD_RETRY_MS);
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
88
99
|
unsubscribe === null || unsubscribe === void 0 ? void 0 : unsubscribe();
|
|
89
100
|
markReady();
|
|
90
101
|
});
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { buildSdkHeaders, FUNNEL_ID, getFunnelSdkPublishableKey, } from './runtime-api.config.js';
|
|
2
|
+
import { funnelSdkService } from './funnel-sdk.service.js';
|
|
2
3
|
import { mergeFunnelUserAttribution, } from '../runtime/funnel-attribution.js';
|
|
3
4
|
import { applyUrlUserAttributesToUser, hasUrlUserProfileAttributes, resolveUrlUserAttributes, } from '../runtime/url-user-attributes.js';
|
|
4
5
|
const DEFAULT_USER_ID_STORAGE_KEY = 'funnel:user-id';
|
|
@@ -144,28 +145,6 @@ const toAppUser = (input) => {
|
|
|
144
145
|
document: Object.keys(apiDocument).length > 0 ? apiDocument : ((_b = input.fallbackDocument) !== null && _b !== void 0 ? _b : {}),
|
|
145
146
|
};
|
|
146
147
|
};
|
|
147
|
-
const parseErrorMessage = async (response, fallbackMessage) => {
|
|
148
|
-
try {
|
|
149
|
-
const payload = (await response.json());
|
|
150
|
-
const apiError = asString(payload.error);
|
|
151
|
-
if (apiError) {
|
|
152
|
-
return apiError;
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
catch (_a) {
|
|
156
|
-
// ignore JSON parsing errors
|
|
157
|
-
}
|
|
158
|
-
try {
|
|
159
|
-
const errorText = await response.text();
|
|
160
|
-
if (errorText.trim()) {
|
|
161
|
-
return errorText;
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
catch (_b) {
|
|
165
|
-
// ignore text parsing errors
|
|
166
|
-
}
|
|
167
|
-
return fallbackMessage;
|
|
168
|
-
};
|
|
169
148
|
class ApiService {
|
|
170
149
|
readFileAsDataUrl(file) {
|
|
171
150
|
return new Promise((resolve, reject) => {
|
|
@@ -209,18 +188,10 @@ class ApiService {
|
|
|
209
188
|
if (!publishableKey || !FUNNEL_ID) {
|
|
210
189
|
return null;
|
|
211
190
|
}
|
|
212
|
-
const url = new URL(buildMainApiUrl('/sdk/public/users/me'));
|
|
213
|
-
url.searchParams.set('funnelId', FUNNEL_ID);
|
|
214
|
-
url.searchParams.set('user_id', userId);
|
|
215
191
|
try {
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
headers: buildSdkHeaders({}),
|
|
192
|
+
return await funnelSdkService.getCurrentUser({
|
|
193
|
+
userId,
|
|
219
194
|
});
|
|
220
|
-
if (!response.ok) {
|
|
221
|
-
return null;
|
|
222
|
-
}
|
|
223
|
-
return (await response.json());
|
|
224
195
|
}
|
|
225
196
|
catch (_a) {
|
|
226
197
|
return null;
|
|
@@ -260,30 +231,16 @@ class ApiService {
|
|
|
260
231
|
fallbackDocument: withMergedAttributionDocument({}, input === null || input === void 0 ? void 0 : input.attribution),
|
|
261
232
|
});
|
|
262
233
|
}
|
|
263
|
-
const
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
user_id: userId,
|
|
273
|
-
email: inputEmail,
|
|
274
|
-
fullName: inputName,
|
|
275
|
-
metadata: {
|
|
276
|
-
source: 'funnel-session',
|
|
277
|
-
environment: resolveRuntimeAnalyticsEnvironment(),
|
|
278
|
-
},
|
|
279
|
-
attribution: input === null || input === void 0 ? void 0 : input.attribution,
|
|
280
|
-
}),
|
|
234
|
+
const payload = await funnelSdkService.bootstrapUser({
|
|
235
|
+
userId,
|
|
236
|
+
email: inputEmail,
|
|
237
|
+
fullName: inputName,
|
|
238
|
+
metadata: {
|
|
239
|
+
source: 'funnel-session',
|
|
240
|
+
environment: resolveRuntimeAnalyticsEnvironment(),
|
|
241
|
+
},
|
|
242
|
+
attribution: input === null || input === void 0 ? void 0 : input.attribution,
|
|
281
243
|
});
|
|
282
|
-
if (!response.ok) {
|
|
283
|
-
const errorMessage = await parseErrorMessage(response, 'Failed to bootstrap user session');
|
|
284
|
-
throw new Error(errorMessage);
|
|
285
|
-
}
|
|
286
|
-
const payload = (await response.json());
|
|
287
244
|
const persistedUserId = persistUserId(asString((_a = payload.user) === null || _a === void 0 ? void 0 : _a.user_id) ||
|
|
288
245
|
asString(payload.user_id) ||
|
|
289
246
|
userId, FUNNEL_ID) || userId;
|
|
@@ -307,33 +264,19 @@ class ApiService {
|
|
|
307
264
|
fallbackDocument: withMergedAttributionDocument(user.document, options === null || options === void 0 ? void 0 : options.attribution),
|
|
308
265
|
});
|
|
309
266
|
}
|
|
310
|
-
const
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
progress: {
|
|
323
|
-
attributes: user.attributes || {},
|
|
324
|
-
},
|
|
325
|
-
metadata: {
|
|
326
|
-
source: 'funnel',
|
|
327
|
-
environment: resolveRuntimeAnalyticsEnvironment(),
|
|
328
|
-
},
|
|
329
|
-
attribution: options === null || options === void 0 ? void 0 : options.attribution,
|
|
330
|
-
}),
|
|
267
|
+
const payload = await funnelSdkService.updateFunnelUser({
|
|
268
|
+
userId: persistedUserId,
|
|
269
|
+
email: user.email || undefined,
|
|
270
|
+
fullName: user.name || undefined,
|
|
271
|
+
progress: {
|
|
272
|
+
attributes: user.attributes || {},
|
|
273
|
+
},
|
|
274
|
+
metadata: {
|
|
275
|
+
source: 'funnel',
|
|
276
|
+
environment: resolveRuntimeAnalyticsEnvironment(),
|
|
277
|
+
},
|
|
278
|
+
attribution: options === null || options === void 0 ? void 0 : options.attribution,
|
|
331
279
|
});
|
|
332
|
-
if (!response.ok) {
|
|
333
|
-
const errorMessage = await parseErrorMessage(response, 'Failed to update user');
|
|
334
|
-
throw new Error(errorMessage);
|
|
335
|
-
}
|
|
336
|
-
const payload = (await response.json());
|
|
337
280
|
return toAppUser({
|
|
338
281
|
apiUser: payload.user,
|
|
339
282
|
userId: persistedUserId,
|
|
@@ -407,26 +350,13 @@ class ApiService {
|
|
|
407
350
|
uploadedAt: new Date().toISOString(),
|
|
408
351
|
};
|
|
409
352
|
}
|
|
410
|
-
const
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
publishableKey,
|
|
417
|
-
funnelId: FUNNEL_ID || undefined,
|
|
418
|
-
user_id: userId,
|
|
419
|
-
fileName: input.file.name || 'capture.jpg',
|
|
420
|
-
mimeType: input.file.type || undefined,
|
|
421
|
-
imageDataUrl,
|
|
422
|
-
source: input.source || 'upload',
|
|
423
|
-
}),
|
|
353
|
+
const payload = await funnelSdkService.uploadPhoto({
|
|
354
|
+
userId,
|
|
355
|
+
fileName: input.file.name || 'capture.jpg',
|
|
356
|
+
mimeType: input.file.type || undefined,
|
|
357
|
+
imageDataUrl,
|
|
358
|
+
source: input.source || 'upload',
|
|
424
359
|
});
|
|
425
|
-
if (!response.ok) {
|
|
426
|
-
const errorMessage = await parseErrorMessage(response, 'Failed to upload photo');
|
|
427
|
-
throw new Error(errorMessage);
|
|
428
|
-
}
|
|
429
|
-
const payload = (await response.json());
|
|
430
360
|
const publicUrl = asString(payload.publicUrl);
|
|
431
361
|
const key = asString(payload.key);
|
|
432
362
|
const contentType = asString(payload.contentType) || input.file.type || 'image/jpeg';
|
|
@@ -446,48 +376,23 @@ class ApiService {
|
|
|
446
376
|
async getManageSubscriptions() {
|
|
447
377
|
const userId = this.getSubscriptionManagementUserId();
|
|
448
378
|
const stripeCustomerId = this.getSubscriptionManagementStripeCustomerId();
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
if (stripeCustomerId) {
|
|
454
|
-
query.set('stripe_customer_id', stripeCustomerId);
|
|
455
|
-
}
|
|
456
|
-
if (FUNNEL_ID) {
|
|
457
|
-
query.set('funnelId', FUNNEL_ID);
|
|
458
|
-
}
|
|
459
|
-
const response = await fetch(buildMainApiUrl(`/sdk/public/subscriptions?${query.toString()}`), {
|
|
460
|
-
method: 'GET',
|
|
461
|
-
cache: 'no-store',
|
|
462
|
-
headers: buildSdkHeaders(),
|
|
379
|
+
return funnelSdkService.listSubscriptions({
|
|
380
|
+
userId,
|
|
381
|
+
stripeCustomerId,
|
|
382
|
+
funnelId: FUNNEL_ID || undefined,
|
|
463
383
|
});
|
|
464
|
-
if (!response.ok) {
|
|
465
|
-
const errorMessage = await parseErrorMessage(response, 'Failed to load subscriptions');
|
|
466
|
-
throw new Error(errorMessage);
|
|
467
|
-
}
|
|
468
|
-
return (await response.json());
|
|
469
384
|
}
|
|
470
385
|
async updateSubscription(input) {
|
|
471
|
-
var _a;
|
|
472
386
|
const userId = this.getSubscriptionManagementUserId();
|
|
473
387
|
const stripeCustomerId = this.getSubscriptionManagementStripeCustomerId();
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
stripe_customer_id: stripeCustomerId || undefined,
|
|
482
|
-
funnelId: FUNNEL_ID || undefined,
|
|
483
|
-
coupon_id: ((_a = input.couponId) === null || _a === void 0 ? void 0 : _a.trim()) || undefined,
|
|
484
|
-
}),
|
|
388
|
+
return funnelSdkService.updateSubscription({
|
|
389
|
+
subscriptionId: input.subscriptionId,
|
|
390
|
+
action: input.action,
|
|
391
|
+
couponId: input.couponId,
|
|
392
|
+
userId,
|
|
393
|
+
stripeCustomerId,
|
|
394
|
+
funnelId: FUNNEL_ID || undefined,
|
|
485
395
|
});
|
|
486
|
-
if (!response.ok) {
|
|
487
|
-
const errorMessage = await parseErrorMessage(response, 'Failed to update subscription');
|
|
488
|
-
throw new Error(errorMessage);
|
|
489
|
-
}
|
|
490
|
-
return (await response.json());
|
|
491
396
|
}
|
|
492
397
|
}
|
|
493
398
|
export const apiService = new ApiService();
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
export type FunnelSdkRuntimeMode = 'test' | 'live';
|
|
2
|
+
export type FunnelSdkRuntimeConfigOverrides = {
|
|
3
|
+
apiBaseUrl?: string | null;
|
|
4
|
+
funnelId?: string | null;
|
|
5
|
+
funnelVersionId?: string | null;
|
|
6
|
+
funnelSdkPublishableKey?: string | null;
|
|
7
|
+
};
|
|
8
|
+
export type FunnelSdkPaymentPlan = Record<string, unknown>;
|
|
9
|
+
export type FunnelSdkJsonObject = Record<string, unknown>;
|
|
10
|
+
export type FunnelSdkListPaymentPlansResponse = {
|
|
11
|
+
funnelId?: string;
|
|
12
|
+
environment?: FunnelSdkRuntimeMode;
|
|
13
|
+
plans: FunnelSdkPaymentPlan[];
|
|
14
|
+
};
|
|
15
|
+
export type FunnelSdkBootstrapUserInput = {
|
|
16
|
+
userId?: string | null;
|
|
17
|
+
email?: string | null;
|
|
18
|
+
fullName?: string | null;
|
|
19
|
+
metadata?: FunnelSdkJsonObject | null;
|
|
20
|
+
attribution?: FunnelSdkJsonObject | null;
|
|
21
|
+
runtimeConfig?: FunnelSdkRuntimeConfigOverrides;
|
|
22
|
+
};
|
|
23
|
+
export type FunnelSdkGetCurrentUserInput = {
|
|
24
|
+
userId: string;
|
|
25
|
+
runtimeConfig?: FunnelSdkRuntimeConfigOverrides;
|
|
26
|
+
};
|
|
27
|
+
export type FunnelSdkGetUserSubscriptionStatusInput = {
|
|
28
|
+
userId?: string | null;
|
|
29
|
+
email?: string | null;
|
|
30
|
+
runtimeConfig?: FunnelSdkRuntimeConfigOverrides;
|
|
31
|
+
};
|
|
32
|
+
export type FunnelSdkClaimUserSubscriptionInput = {
|
|
33
|
+
userId: string;
|
|
34
|
+
runtimeConfig?: FunnelSdkRuntimeConfigOverrides;
|
|
35
|
+
};
|
|
36
|
+
export type FunnelSdkUpdateFunnelUserInput = {
|
|
37
|
+
userId: string;
|
|
38
|
+
email?: string | null;
|
|
39
|
+
fullName?: string | null;
|
|
40
|
+
document?: FunnelSdkJsonObject | null;
|
|
41
|
+
progress?: FunnelSdkJsonObject | null;
|
|
42
|
+
metadata?: FunnelSdkJsonObject | null;
|
|
43
|
+
attribution?: FunnelSdkJsonObject | null;
|
|
44
|
+
subscription?: FunnelSdkJsonObject | null;
|
|
45
|
+
subscriptionStatus?: string | null;
|
|
46
|
+
runtimeConfig?: FunnelSdkRuntimeConfigOverrides;
|
|
47
|
+
};
|
|
48
|
+
export type FunnelSdkUploadPhotoInput = {
|
|
49
|
+
fileName: string;
|
|
50
|
+
imageDataUrl: string;
|
|
51
|
+
mimeType?: string | null;
|
|
52
|
+
source?: 'camera' | 'upload' | string | null;
|
|
53
|
+
userId?: string | null;
|
|
54
|
+
runtimeConfig?: FunnelSdkRuntimeConfigOverrides;
|
|
55
|
+
};
|
|
56
|
+
export type FunnelSdkCreateHostedCheckoutSessionInput = {
|
|
57
|
+
planId: string;
|
|
58
|
+
amountCents: number;
|
|
59
|
+
successUrl: string;
|
|
60
|
+
cancelUrl: string;
|
|
61
|
+
analyticsMetadata?: Record<string, unknown> | null;
|
|
62
|
+
billingInterval?: string | null;
|
|
63
|
+
billingIntervalCount?: number | null;
|
|
64
|
+
couponId?: string | null;
|
|
65
|
+
customerEmail?: string | null;
|
|
66
|
+
description?: string | null;
|
|
67
|
+
environment?: FunnelSdkRuntimeMode;
|
|
68
|
+
providerPlanId?: string | null;
|
|
69
|
+
runtimeConfig?: FunnelSdkRuntimeConfigOverrides;
|
|
70
|
+
title?: string | null;
|
|
71
|
+
userId?: string | null;
|
|
72
|
+
};
|
|
73
|
+
export type FunnelSdkCreateHostedCheckoutSessionResponse = {
|
|
74
|
+
sessionId?: string;
|
|
75
|
+
url: string;
|
|
76
|
+
environment?: FunnelSdkRuntimeMode;
|
|
77
|
+
};
|
|
78
|
+
export type FunnelSdkCreateSubscriptionCheckoutInput = {
|
|
79
|
+
planId: string;
|
|
80
|
+
amountCents: number;
|
|
81
|
+
returnUrl: string;
|
|
82
|
+
analyticsMetadata?: Record<string, unknown> | null;
|
|
83
|
+
billingInterval?: string | null;
|
|
84
|
+
billingIntervalCount?: number | null;
|
|
85
|
+
couponId?: string | null;
|
|
86
|
+
customerEmail?: string | null;
|
|
87
|
+
description?: string | null;
|
|
88
|
+
environment?: FunnelSdkRuntimeMode;
|
|
89
|
+
providerPlanId?: string | null;
|
|
90
|
+
runtimeConfig?: FunnelSdkRuntimeConfigOverrides;
|
|
91
|
+
title?: string | null;
|
|
92
|
+
userId?: string | null;
|
|
93
|
+
};
|
|
94
|
+
export type FunnelSdkCreateSubscriptionCheckoutResponse = {
|
|
95
|
+
clientSecret: string;
|
|
96
|
+
checkoutSessionId?: string;
|
|
97
|
+
subscriptionId?: string;
|
|
98
|
+
stripePublishableKey?: string;
|
|
99
|
+
environment?: FunnelSdkRuntimeMode;
|
|
100
|
+
};
|
|
101
|
+
export type FunnelSdkUpdateSubscriptionCheckoutPlanInput = {
|
|
102
|
+
checkoutSessionId: string;
|
|
103
|
+
planId: string;
|
|
104
|
+
amountCents: number;
|
|
105
|
+
analyticsMetadata?: Record<string, unknown> | null;
|
|
106
|
+
billingInterval?: string | null;
|
|
107
|
+
billingIntervalCount?: number | null;
|
|
108
|
+
couponId?: string | null;
|
|
109
|
+
customerEmail?: string | null;
|
|
110
|
+
description?: string | null;
|
|
111
|
+
environment?: FunnelSdkRuntimeMode;
|
|
112
|
+
providerPlanId?: string | null;
|
|
113
|
+
runtimeConfig?: FunnelSdkRuntimeConfigOverrides;
|
|
114
|
+
title?: string | null;
|
|
115
|
+
userId?: string | null;
|
|
116
|
+
};
|
|
117
|
+
export type FunnelSdkUpdateSubscriptionCheckoutPlanResponse = {
|
|
118
|
+
checkoutSessionId: string;
|
|
119
|
+
environment?: FunnelSdkRuntimeMode;
|
|
120
|
+
};
|
|
121
|
+
export type FunnelSdkVerifySubscriptionCheckoutInput = {
|
|
122
|
+
checkoutSessionId: string;
|
|
123
|
+
environment?: FunnelSdkRuntimeMode;
|
|
124
|
+
runtimeConfig?: FunnelSdkRuntimeConfigOverrides;
|
|
125
|
+
};
|
|
126
|
+
export type FunnelSdkVerifySubscriptionCheckoutResponse = {
|
|
127
|
+
checkoutSessionId: string;
|
|
128
|
+
paid: boolean;
|
|
129
|
+
paymentStatus?: string | null;
|
|
130
|
+
status?: string | null;
|
|
131
|
+
environment?: FunnelSdkRuntimeMode;
|
|
132
|
+
analyticsMetadata?: Record<string, unknown>;
|
|
133
|
+
};
|
|
134
|
+
export type FunnelSdkCreateOneTimePaymentIntentInput = {
|
|
135
|
+
planId: string;
|
|
136
|
+
amountCents: number;
|
|
137
|
+
analyticsMetadata?: Record<string, unknown> | null;
|
|
138
|
+
couponId?: string | null;
|
|
139
|
+
environment?: FunnelSdkRuntimeMode;
|
|
140
|
+
runtimeConfig?: FunnelSdkRuntimeConfigOverrides;
|
|
141
|
+
userId?: string | null;
|
|
142
|
+
};
|
|
143
|
+
export type FunnelSdkCreateOneTimePaymentIntentResponse = {
|
|
144
|
+
clientSecret: string;
|
|
145
|
+
stripePublishableKey?: string;
|
|
146
|
+
environment?: FunnelSdkRuntimeMode;
|
|
147
|
+
};
|
|
148
|
+
export type FunnelSdkChargeOneClickPaymentInput = {
|
|
149
|
+
planId: string;
|
|
150
|
+
amountCents: number;
|
|
151
|
+
analyticsMetadata?: Record<string, unknown> | null;
|
|
152
|
+
checkoutSessionId?: string | null;
|
|
153
|
+
currency?: string | null;
|
|
154
|
+
environment?: FunnelSdkRuntimeMode;
|
|
155
|
+
idempotencyKey?: string | null;
|
|
156
|
+
runtimeConfig?: FunnelSdkRuntimeConfigOverrides;
|
|
157
|
+
stripeCustomerId?: string | null;
|
|
158
|
+
userId?: string | null;
|
|
159
|
+
};
|
|
160
|
+
export type FunnelSdkChargeOneClickPaymentResponse = {
|
|
161
|
+
paymentIntentId: string;
|
|
162
|
+
status: string;
|
|
163
|
+
amountCents?: number;
|
|
164
|
+
clientSecret?: string;
|
|
165
|
+
currency?: string;
|
|
166
|
+
environment?: FunnelSdkRuntimeMode;
|
|
167
|
+
stripePublishableKey?: string;
|
|
168
|
+
};
|
|
169
|
+
export type FunnelSdkUpdateSubscriptionInput = {
|
|
170
|
+
subscriptionId: string;
|
|
171
|
+
action: 'cancel' | 'renew' | 'apply-discount';
|
|
172
|
+
couponId?: string | null;
|
|
173
|
+
funnelId?: string | null;
|
|
174
|
+
runtimeConfig?: FunnelSdkRuntimeConfigOverrides;
|
|
175
|
+
stripeCustomerId?: string | null;
|
|
176
|
+
userId?: string | null;
|
|
177
|
+
};
|
|
178
|
+
export type FunnelSdkListSubscriptionsInput = {
|
|
179
|
+
funnelId?: string | null;
|
|
180
|
+
runtimeConfig?: FunnelSdkRuntimeConfigOverrides;
|
|
181
|
+
stripeCustomerId?: string | null;
|
|
182
|
+
userId?: string | null;
|
|
183
|
+
};
|
|
184
|
+
declare class FunnelSdkService {
|
|
185
|
+
private getJson;
|
|
186
|
+
private postJson;
|
|
187
|
+
bootstrapUser<ResponsePayload = FunnelSdkJsonObject>(input?: FunnelSdkBootstrapUserInput): Promise<ResponsePayload>;
|
|
188
|
+
getCurrentUser<ResponsePayload = FunnelSdkJsonObject>(input: FunnelSdkGetCurrentUserInput): Promise<ResponsePayload>;
|
|
189
|
+
getUserSubscriptionStatus<ResponsePayload = FunnelSdkJsonObject>(input?: FunnelSdkGetUserSubscriptionStatusInput): Promise<ResponsePayload>;
|
|
190
|
+
claimUserSubscription<ResponsePayload = FunnelSdkJsonObject>(input: FunnelSdkClaimUserSubscriptionInput): Promise<ResponsePayload>;
|
|
191
|
+
updateFunnelUser<ResponsePayload = FunnelSdkJsonObject>(input: FunnelSdkUpdateFunnelUserInput): Promise<ResponsePayload>;
|
|
192
|
+
uploadPhoto<ResponsePayload = FunnelSdkJsonObject>(input: FunnelSdkUploadPhotoInput): Promise<ResponsePayload>;
|
|
193
|
+
listPaymentPlans(input?: {
|
|
194
|
+
environment?: FunnelSdkRuntimeMode;
|
|
195
|
+
runtimeConfig?: FunnelSdkRuntimeConfigOverrides;
|
|
196
|
+
}): Promise<FunnelSdkListPaymentPlansResponse>;
|
|
197
|
+
createHostedCheckoutSession(input: FunnelSdkCreateHostedCheckoutSessionInput): Promise<FunnelSdkCreateHostedCheckoutSessionResponse>;
|
|
198
|
+
createSubscriptionCheckout(input: FunnelSdkCreateSubscriptionCheckoutInput): Promise<FunnelSdkCreateSubscriptionCheckoutResponse>;
|
|
199
|
+
updateSubscriptionCheckoutPlan(input: FunnelSdkUpdateSubscriptionCheckoutPlanInput): Promise<FunnelSdkUpdateSubscriptionCheckoutPlanResponse>;
|
|
200
|
+
verifySubscriptionCheckout(input: FunnelSdkVerifySubscriptionCheckoutInput): Promise<FunnelSdkVerifySubscriptionCheckoutResponse>;
|
|
201
|
+
createOneTimePaymentIntent(input: FunnelSdkCreateOneTimePaymentIntentInput): Promise<FunnelSdkCreateOneTimePaymentIntentResponse>;
|
|
202
|
+
chargeOneClickPayment(input: FunnelSdkChargeOneClickPaymentInput): Promise<FunnelSdkChargeOneClickPaymentResponse>;
|
|
203
|
+
updateSubscription<ResponsePayload>(input: FunnelSdkUpdateSubscriptionInput): Promise<ResponsePayload>;
|
|
204
|
+
listSubscriptions<ResponsePayload = FunnelSdkJsonObject>(input?: FunnelSdkListSubscriptionsInput): Promise<ResponsePayload>;
|
|
205
|
+
}
|
|
206
|
+
export declare const funnelSdkService: FunnelSdkService;
|
|
207
|
+
export {};
|
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
import { buildMainApiUrl, FUNNEL_ID, FUNNEL_SDK_PUBLISHABLE_KEY, FUNNEL_VERSION_ID, } from './runtime-api.config.js';
|
|
2
|
+
import { resolvePreviewPaymentPublishableKey } from './public-env.js';
|
|
3
|
+
const trimTrailingSlash = (value) => value.replace(/\/+$/, '');
|
|
4
|
+
const toNormalizedPath = (path) => (path.startsWith('/') ? path : `/${path}`);
|
|
5
|
+
const asTrimmedStringOrNull = (value) => {
|
|
6
|
+
if (typeof value !== 'string') {
|
|
7
|
+
return null;
|
|
8
|
+
}
|
|
9
|
+
const trimmed = value.trim();
|
|
10
|
+
return trimmed || null;
|
|
11
|
+
};
|
|
12
|
+
const removeEmptyFields = (input) => {
|
|
13
|
+
return Object.fromEntries(Object.entries(input).filter(([, value]) => value !== undefined && value !== null && value !== ''));
|
|
14
|
+
};
|
|
15
|
+
const resolveRuntimeConfig = (runtimeConfig) => {
|
|
16
|
+
var _a, _b, _c;
|
|
17
|
+
return ({
|
|
18
|
+
apiBaseUrl: asTrimmedStringOrNull(runtimeConfig === null || runtimeConfig === void 0 ? void 0 : runtimeConfig.apiBaseUrl),
|
|
19
|
+
funnelId: (_a = asTrimmedStringOrNull(runtimeConfig === null || runtimeConfig === void 0 ? void 0 : runtimeConfig.funnelId)) !== null && _a !== void 0 ? _a : asTrimmedStringOrNull(FUNNEL_ID),
|
|
20
|
+
funnelVersionId: (_b = asTrimmedStringOrNull(runtimeConfig === null || runtimeConfig === void 0 ? void 0 : runtimeConfig.funnelVersionId)) !== null && _b !== void 0 ? _b : asTrimmedStringOrNull(FUNNEL_VERSION_ID),
|
|
21
|
+
funnelSdkPublishableKey: resolvePreviewPaymentPublishableKey((_c = asTrimmedStringOrNull(runtimeConfig === null || runtimeConfig === void 0 ? void 0 : runtimeConfig.funnelSdkPublishableKey)) !== null && _c !== void 0 ? _c : asTrimmedStringOrNull(FUNNEL_SDK_PUBLISHABLE_KEY)) || null,
|
|
22
|
+
});
|
|
23
|
+
};
|
|
24
|
+
const buildApiUrl = (path, runtimeConfig) => {
|
|
25
|
+
const { apiBaseUrl } = resolveRuntimeConfig(runtimeConfig);
|
|
26
|
+
if (!apiBaseUrl) {
|
|
27
|
+
return buildMainApiUrl(path);
|
|
28
|
+
}
|
|
29
|
+
return `${trimTrailingSlash(apiBaseUrl)}${toNormalizedPath(path)}`;
|
|
30
|
+
};
|
|
31
|
+
const buildHeaders = (runtimeConfig, headers) => {
|
|
32
|
+
const { funnelSdkPublishableKey } = resolveRuntimeConfig(runtimeConfig);
|
|
33
|
+
if (!funnelSdkPublishableKey) {
|
|
34
|
+
return Object.assign({}, (headers !== null && headers !== void 0 ? headers : {}));
|
|
35
|
+
}
|
|
36
|
+
return Object.assign({ 'x-sdk-publishable-key': funnelSdkPublishableKey }, (headers !== null && headers !== void 0 ? headers : {}));
|
|
37
|
+
};
|
|
38
|
+
const withRuntimeFields = (runtimeConfig, body, options) => {
|
|
39
|
+
const { funnelId, funnelVersionId, funnelSdkPublishableKey } = resolveRuntimeConfig(runtimeConfig);
|
|
40
|
+
return removeEmptyFields(Object.assign({ publishableKey: (options === null || options === void 0 ? void 0 : options.includePublishableKey) === false
|
|
41
|
+
? undefined
|
|
42
|
+
: funnelSdkPublishableKey || undefined, funnelId: funnelId || undefined, funnelVersionId: funnelVersionId || undefined }, (body !== null && body !== void 0 ? body : {})));
|
|
43
|
+
};
|
|
44
|
+
const parseErrorMessage = async (response, fallbackMessage) => {
|
|
45
|
+
try {
|
|
46
|
+
const payload = (await response.json());
|
|
47
|
+
const apiError = asTrimmedStringOrNull(payload.error);
|
|
48
|
+
if (apiError) {
|
|
49
|
+
return apiError;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
catch (_a) {
|
|
53
|
+
// ignore JSON parsing errors
|
|
54
|
+
}
|
|
55
|
+
try {
|
|
56
|
+
const text = await response.text();
|
|
57
|
+
if (text.trim()) {
|
|
58
|
+
return text;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
catch (_b) {
|
|
62
|
+
// ignore text parsing errors
|
|
63
|
+
}
|
|
64
|
+
return fallbackMessage;
|
|
65
|
+
};
|
|
66
|
+
class FunnelSdkService {
|
|
67
|
+
async getJson(path, input) {
|
|
68
|
+
var _a;
|
|
69
|
+
const url = new URL(buildApiUrl(path, input.runtimeConfig));
|
|
70
|
+
for (const [key, value] of Object.entries((_a = input.searchParams) !== null && _a !== void 0 ? _a : {})) {
|
|
71
|
+
const normalizedValue = asTrimmedStringOrNull(value);
|
|
72
|
+
if (normalizedValue) {
|
|
73
|
+
url.searchParams.set(key, normalizedValue);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
const response = await fetch(url.toString(), {
|
|
77
|
+
method: 'GET',
|
|
78
|
+
headers: buildHeaders(input.runtimeConfig),
|
|
79
|
+
});
|
|
80
|
+
if (!response.ok) {
|
|
81
|
+
throw new Error(await parseErrorMessage(response, input.errorMessage));
|
|
82
|
+
}
|
|
83
|
+
return (await response.json());
|
|
84
|
+
}
|
|
85
|
+
async postJson(path, input) {
|
|
86
|
+
var _a;
|
|
87
|
+
const response = await fetch(buildApiUrl(path, input.runtimeConfig), {
|
|
88
|
+
method: 'POST',
|
|
89
|
+
headers: buildHeaders(input.runtimeConfig, {
|
|
90
|
+
'Content-Type': 'application/json',
|
|
91
|
+
}),
|
|
92
|
+
body: JSON.stringify(input.includeRuntimeFields === false
|
|
93
|
+
? removeEmptyFields((_a = input.body) !== null && _a !== void 0 ? _a : {})
|
|
94
|
+
: withRuntimeFields(input.runtimeConfig, input.body, {
|
|
95
|
+
includePublishableKey: input.includePublishableKey,
|
|
96
|
+
})),
|
|
97
|
+
});
|
|
98
|
+
if (!response.ok) {
|
|
99
|
+
throw new Error(await parseErrorMessage(response, input.errorMessage));
|
|
100
|
+
}
|
|
101
|
+
return (await response.json());
|
|
102
|
+
}
|
|
103
|
+
async bootstrapUser(input = {}) {
|
|
104
|
+
return this.postJson('/sdk/public/users/bootstrap', {
|
|
105
|
+
runtimeConfig: input.runtimeConfig,
|
|
106
|
+
body: {
|
|
107
|
+
user_id: input.userId,
|
|
108
|
+
email: input.email,
|
|
109
|
+
fullName: input.fullName,
|
|
110
|
+
metadata: input.metadata,
|
|
111
|
+
attribution: input.attribution,
|
|
112
|
+
},
|
|
113
|
+
errorMessage: 'Failed to bootstrap user session',
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
async getCurrentUser(input) {
|
|
117
|
+
const { funnelId } = resolveRuntimeConfig(input.runtimeConfig);
|
|
118
|
+
return this.getJson('/sdk/public/users/me', {
|
|
119
|
+
runtimeConfig: input.runtimeConfig,
|
|
120
|
+
searchParams: {
|
|
121
|
+
funnelId,
|
|
122
|
+
user_id: input.userId,
|
|
123
|
+
},
|
|
124
|
+
errorMessage: 'Failed to load user',
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
async getUserSubscriptionStatus(input = {}) {
|
|
128
|
+
const { funnelId } = resolveRuntimeConfig(input.runtimeConfig);
|
|
129
|
+
return this.getJson('/sdk/public/users/subscription', {
|
|
130
|
+
runtimeConfig: input.runtimeConfig,
|
|
131
|
+
searchParams: {
|
|
132
|
+
funnelId,
|
|
133
|
+
user_id: input.userId,
|
|
134
|
+
email: input.email,
|
|
135
|
+
},
|
|
136
|
+
errorMessage: 'Failed to load user subscription status',
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
async claimUserSubscription(input) {
|
|
140
|
+
return this.postJson(`/sdk/public/users/${encodeURIComponent(input.userId)}/claim_subscription`, {
|
|
141
|
+
runtimeConfig: input.runtimeConfig,
|
|
142
|
+
errorMessage: 'Failed to claim user subscription',
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
async updateFunnelUser(input) {
|
|
146
|
+
return this.postJson('/public/funnel-users/upsert', {
|
|
147
|
+
runtimeConfig: input.runtimeConfig,
|
|
148
|
+
body: {
|
|
149
|
+
user_id: input.userId,
|
|
150
|
+
email: input.email,
|
|
151
|
+
fullName: input.fullName,
|
|
152
|
+
document: input.document,
|
|
153
|
+
progress: input.progress,
|
|
154
|
+
metadata: input.metadata,
|
|
155
|
+
attribution: input.attribution,
|
|
156
|
+
subscription: input.subscription,
|
|
157
|
+
subscriptionStatus: input.subscriptionStatus,
|
|
158
|
+
},
|
|
159
|
+
errorMessage: 'Failed to update user',
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
async uploadPhoto(input) {
|
|
163
|
+
return this.postJson('/sdk/public/uploads/photos', {
|
|
164
|
+
runtimeConfig: input.runtimeConfig,
|
|
165
|
+
body: {
|
|
166
|
+
user_id: input.userId,
|
|
167
|
+
fileName: input.fileName,
|
|
168
|
+
mimeType: input.mimeType,
|
|
169
|
+
imageDataUrl: input.imageDataUrl,
|
|
170
|
+
source: input.source,
|
|
171
|
+
},
|
|
172
|
+
errorMessage: 'Failed to upload photo',
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
async listPaymentPlans(input = {}) {
|
|
176
|
+
const { funnelId } = resolveRuntimeConfig(input.runtimeConfig);
|
|
177
|
+
return this.getJson('/sdk/public/payments/plans', {
|
|
178
|
+
runtimeConfig: input.runtimeConfig,
|
|
179
|
+
searchParams: {
|
|
180
|
+
environment: input.environment,
|
|
181
|
+
funnelId,
|
|
182
|
+
},
|
|
183
|
+
errorMessage: 'Unable to load payment plans',
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
async createHostedCheckoutSession(input) {
|
|
187
|
+
return this.postJson('/sdk/public/payments/checkout-session', {
|
|
188
|
+
runtimeConfig: input.runtimeConfig,
|
|
189
|
+
body: {
|
|
190
|
+
planId: input.planId,
|
|
191
|
+
providerPlanId: input.providerPlanId,
|
|
192
|
+
title: input.title,
|
|
193
|
+
description: input.description,
|
|
194
|
+
amountCents: input.amountCents,
|
|
195
|
+
billingInterval: input.billingInterval,
|
|
196
|
+
billingIntervalCount: input.billingIntervalCount,
|
|
197
|
+
couponId: input.couponId,
|
|
198
|
+
analyticsMetadata: input.analyticsMetadata,
|
|
199
|
+
successUrl: input.successUrl,
|
|
200
|
+
cancelUrl: input.cancelUrl,
|
|
201
|
+
customerEmail: input.customerEmail,
|
|
202
|
+
user_id: input.userId,
|
|
203
|
+
environment: input.environment,
|
|
204
|
+
},
|
|
205
|
+
errorMessage: 'Unable to create checkout session',
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
async createSubscriptionCheckout(input) {
|
|
209
|
+
return this.postJson('/sdk/public/payments/subscriptions', {
|
|
210
|
+
runtimeConfig: input.runtimeConfig,
|
|
211
|
+
body: {
|
|
212
|
+
planId: input.planId,
|
|
213
|
+
providerPlanId: input.providerPlanId,
|
|
214
|
+
title: input.title,
|
|
215
|
+
description: input.description,
|
|
216
|
+
amountCents: input.amountCents,
|
|
217
|
+
billingInterval: input.billingInterval,
|
|
218
|
+
billingIntervalCount: input.billingIntervalCount,
|
|
219
|
+
couponId: input.couponId,
|
|
220
|
+
analyticsMetadata: input.analyticsMetadata,
|
|
221
|
+
customerEmail: input.customerEmail,
|
|
222
|
+
returnUrl: input.returnUrl,
|
|
223
|
+
user_id: input.userId,
|
|
224
|
+
environment: input.environment,
|
|
225
|
+
},
|
|
226
|
+
errorMessage: 'Unable to create subscription checkout',
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
async updateSubscriptionCheckoutPlan(input) {
|
|
230
|
+
return this.postJson('/sdk/public/payments/subscriptions/update-plan', {
|
|
231
|
+
runtimeConfig: input.runtimeConfig,
|
|
232
|
+
body: {
|
|
233
|
+
checkoutSessionId: input.checkoutSessionId,
|
|
234
|
+
planId: input.planId,
|
|
235
|
+
providerPlanId: input.providerPlanId,
|
|
236
|
+
title: input.title,
|
|
237
|
+
description: input.description,
|
|
238
|
+
amountCents: input.amountCents,
|
|
239
|
+
billingInterval: input.billingInterval,
|
|
240
|
+
billingIntervalCount: input.billingIntervalCount,
|
|
241
|
+
couponId: input.couponId,
|
|
242
|
+
analyticsMetadata: input.analyticsMetadata,
|
|
243
|
+
customerEmail: input.customerEmail,
|
|
244
|
+
user_id: input.userId,
|
|
245
|
+
environment: input.environment,
|
|
246
|
+
},
|
|
247
|
+
errorMessage: 'Unable to update subscription checkout',
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
async verifySubscriptionCheckout(input) {
|
|
251
|
+
return this.postJson('/sdk/public/payments/subscriptions/checkout-session-status', {
|
|
252
|
+
runtimeConfig: input.runtimeConfig,
|
|
253
|
+
body: {
|
|
254
|
+
checkoutSessionId: input.checkoutSessionId,
|
|
255
|
+
environment: input.environment,
|
|
256
|
+
},
|
|
257
|
+
errorMessage: 'Unable to verify subscription checkout',
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
async createOneTimePaymentIntent(input) {
|
|
261
|
+
return this.postJson('/sdk/public/payments/intents', {
|
|
262
|
+
runtimeConfig: input.runtimeConfig,
|
|
263
|
+
body: {
|
|
264
|
+
planId: input.planId,
|
|
265
|
+
amountCents: input.amountCents,
|
|
266
|
+
couponId: input.couponId,
|
|
267
|
+
analyticsMetadata: input.analyticsMetadata,
|
|
268
|
+
user_id: input.userId,
|
|
269
|
+
environment: input.environment,
|
|
270
|
+
},
|
|
271
|
+
errorMessage: 'Unable to create payment intent',
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
async chargeOneClickPayment(input) {
|
|
275
|
+
return this.postJson('/sdk/public/payments/one-click', {
|
|
276
|
+
runtimeConfig: input.runtimeConfig,
|
|
277
|
+
body: {
|
|
278
|
+
planId: input.planId,
|
|
279
|
+
amountCents: input.amountCents,
|
|
280
|
+
currency: input.currency,
|
|
281
|
+
analyticsMetadata: input.analyticsMetadata,
|
|
282
|
+
user_id: input.userId,
|
|
283
|
+
stripe_customer_id: input.stripeCustomerId,
|
|
284
|
+
checkoutSessionId: input.checkoutSessionId,
|
|
285
|
+
idempotencyKey: input.idempotencyKey,
|
|
286
|
+
environment: input.environment,
|
|
287
|
+
},
|
|
288
|
+
errorMessage: 'Unable to charge one-click payment',
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
async updateSubscription(input) {
|
|
292
|
+
return this.postJson(`/sdk/public/subscriptions/${encodeURIComponent(input.subscriptionId)}/${input.action}`, {
|
|
293
|
+
runtimeConfig: input.runtimeConfig,
|
|
294
|
+
body: {
|
|
295
|
+
user_id: input.userId,
|
|
296
|
+
stripe_customer_id: input.stripeCustomerId,
|
|
297
|
+
funnelId: input.funnelId,
|
|
298
|
+
coupon_id: input.couponId,
|
|
299
|
+
},
|
|
300
|
+
includeRuntimeFields: false,
|
|
301
|
+
includePublishableKey: false,
|
|
302
|
+
errorMessage: 'Failed to update subscription',
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
async listSubscriptions(input = {}) {
|
|
306
|
+
var _a;
|
|
307
|
+
const { funnelId } = resolveRuntimeConfig(input.runtimeConfig);
|
|
308
|
+
return this.getJson('/sdk/public/subscriptions', {
|
|
309
|
+
runtimeConfig: input.runtimeConfig,
|
|
310
|
+
searchParams: {
|
|
311
|
+
user_id: input.userId,
|
|
312
|
+
stripe_customer_id: input.stripeCustomerId,
|
|
313
|
+
funnelId: (_a = input.funnelId) !== null && _a !== void 0 ? _a : funnelId,
|
|
314
|
+
},
|
|
315
|
+
errorMessage: 'Failed to load subscriptions',
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
export const funnelSdkService = new FunnelSdkService();
|