@frak-labs/core-sdk 0.2.1-beta.b38eef2e → 0.2.1-beta.d04602ec

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 (74) hide show
  1. package/README.md +1 -2
  2. package/cdn/bundle.js +55 -3
  3. package/dist/actions.cjs +1 -1
  4. package/dist/actions.d.cts +3 -3
  5. package/dist/actions.d.ts +3 -3
  6. package/dist/actions.js +1 -1
  7. package/dist/bundle.cjs +1 -1
  8. package/dist/bundle.d.cts +4 -4
  9. package/dist/bundle.d.ts +4 -4
  10. package/dist/bundle.js +1 -1
  11. package/dist/{computeLegacyProductId-CCAZvLa5.d.cts → computeLegacyProductId-fKvxbC4k.d.ts} +91 -37
  12. package/dist/{computeLegacyProductId-b5cUWdAm.d.ts → computeLegacyProductId-rYIvY4c3.d.cts} +91 -37
  13. package/dist/index.cjs +1 -1
  14. package/dist/index.d.cts +3 -3
  15. package/dist/index.d.ts +3 -3
  16. package/dist/index.js +1 -1
  17. package/dist/{openSso-B0g7-807.d.cts → openSso-CMZM06uR.d.ts} +258 -46
  18. package/dist/{openSso-CMzwvaCa.d.ts → openSso-CebB8mFv.d.cts} +258 -46
  19. package/dist/setupClient-B_XMB52l.cjs +13 -0
  20. package/dist/setupClient-jYx-fbxB.js +13 -0
  21. package/dist/siweAuthenticate-CWcVvP-G.cjs +1 -0
  22. package/dist/siweAuthenticate-DQfdb5UQ.js +1 -0
  23. package/dist/{siweAuthenticate-CnCZ7mok.d.ts → siweAuthenticate-Dc_Yg9Bg.d.cts} +102 -8
  24. package/dist/{siweAuthenticate-CVigMOxz.d.cts → siweAuthenticate-Ddhl-o4N.d.ts} +102 -8
  25. package/dist/trackEvent-Ce1XlsIE.js +1 -0
  26. package/dist/trackEvent-CvbJTTqA.cjs +1 -0
  27. package/package.json +8 -8
  28. package/src/actions/displayEmbeddedWallet.ts +6 -2
  29. package/src/actions/displayModal.ts +6 -2
  30. package/src/actions/displaySharingPage.ts +49 -0
  31. package/src/actions/ensureIdentity.ts +2 -2
  32. package/src/actions/getMerchantInformation.test.ts +13 -1
  33. package/src/actions/getMerchantInformation.ts +20 -5
  34. package/src/actions/getUserReferralStatus.ts +42 -0
  35. package/src/actions/index.ts +7 -1
  36. package/src/actions/referral/setupReferral.test.ts +79 -0
  37. package/src/actions/referral/setupReferral.ts +32 -0
  38. package/src/actions/trackPurchaseStatus.test.ts +32 -20
  39. package/src/actions/trackPurchaseStatus.ts +3 -5
  40. package/src/actions/wrapper/modalBuilder.test.ts +4 -2
  41. package/src/actions/wrapper/modalBuilder.ts +6 -8
  42. package/src/clients/createIFrameFrakClient.ts +148 -25
  43. package/src/clients/transports/iframeLifecycleManager.test.ts +0 -80
  44. package/src/clients/transports/iframeLifecycleManager.ts +0 -44
  45. package/src/index.ts +17 -4
  46. package/src/types/config.ts +10 -3
  47. package/src/types/index.ts +13 -1
  48. package/src/types/lifecycle/client.ts +22 -27
  49. package/src/types/lifecycle/iframe.ts +0 -8
  50. package/src/types/resolvedConfig.ts +122 -0
  51. package/src/types/rpc/displaySharingPage.ts +77 -0
  52. package/src/types/rpc/interaction.ts +4 -0
  53. package/src/types/rpc/userReferralStatus.ts +20 -0
  54. package/src/types/rpc.ts +42 -5
  55. package/src/utils/backendUrl.test.ts +2 -2
  56. package/src/utils/backendUrl.ts +1 -1
  57. package/src/utils/cache/index.ts +7 -0
  58. package/src/utils/cache/lruMap.test.ts +55 -0
  59. package/src/utils/cache/lruMap.ts +38 -0
  60. package/src/utils/cache/withCache.test.ts +162 -0
  61. package/src/utils/cache/withCache.ts +105 -0
  62. package/src/utils/inAppBrowser.ts +60 -0
  63. package/src/utils/index.ts +6 -4
  64. package/src/utils/sdkConfigStore.test.ts +405 -0
  65. package/src/utils/sdkConfigStore.ts +263 -0
  66. package/src/utils/sso.ts +3 -7
  67. package/dist/setupClient-CqTHGvVa.cjs +0 -13
  68. package/dist/setupClient-DTyvAPgh.js +0 -13
  69. package/dist/siweAuthenticate-BWmI2_TN.cjs +0 -1
  70. package/dist/siweAuthenticate-zczqxm0a.js +0 -1
  71. package/dist/trackEvent-CeLFVzZn.js +0 -1
  72. package/dist/trackEvent-Ew5r5zfI.cjs +0 -1
  73. package/src/utils/merchantId.test.ts +0 -653
  74. package/src/utils/merchantId.ts +0 -143
@@ -233,86 +233,6 @@ describe("createIFrameLifecycleManager", () => {
233
233
  });
234
234
  });
235
235
 
236
- describe("handshake event", () => {
237
- test("should post handshake-response with token to iframe origin", async () => {
238
- const { createIFrameLifecycleManager } = await import(
239
- "./iframeLifecycleManager"
240
- );
241
-
242
- const mockPostMessage = vi.fn();
243
- const mockIframe = {
244
- src: "https://wallet.frak.id/listener",
245
- contentWindow: {
246
- postMessage: mockPostMessage,
247
- },
248
- } as any;
249
-
250
- const manager = createIFrameLifecycleManager({
251
- iframe: mockIframe,
252
- targetOrigin: WALLET_ORIGIN,
253
- });
254
-
255
- const event = {
256
- iframeLifecycle: "handshake" as const,
257
- data: { token: "handshake-token-123" },
258
- };
259
-
260
- await manager.handleEvent(event);
261
-
262
- expect(mockPostMessage).toHaveBeenCalledWith(
263
- {
264
- clientLifecycle: "handshake-response",
265
- data: {
266
- token: "handshake-token-123",
267
- currentUrl: "https://test.com",
268
- clientId: "mock-client-id",
269
- },
270
- },
271
- "https://wallet.frak.id"
272
- );
273
- });
274
-
275
- test("should include current URL in handshake response", async () => {
276
- const { createIFrameLifecycleManager } = await import(
277
- "./iframeLifecycleManager"
278
- );
279
-
280
- Object.defineProperty(window, "location", {
281
- value: { href: "https://example.com/page?param=value" },
282
- writable: true,
283
- });
284
-
285
- const mockPostMessage = vi.fn();
286
- const mockIframe = {
287
- src: "https://wallet.frak.id/listener",
288
- contentWindow: {
289
- postMessage: mockPostMessage,
290
- },
291
- } as any;
292
-
293
- const manager = createIFrameLifecycleManager({
294
- iframe: mockIframe,
295
- targetOrigin: WALLET_ORIGIN,
296
- });
297
-
298
- const event = {
299
- iframeLifecycle: "handshake" as const,
300
- data: { token: "token" },
301
- };
302
-
303
- await manager.handleEvent(event);
304
-
305
- expect(mockPostMessage).toHaveBeenCalledWith(
306
- expect.objectContaining({
307
- data: expect.objectContaining({
308
- currentUrl: "https://example.com/page?param=value",
309
- }),
310
- }),
311
- "https://wallet.frak.id"
312
- );
313
- });
314
- });
315
-
316
236
  describe("redirect event", () => {
317
237
  test("should redirect with appended current URL for HTTP URLs", async () => {
318
238
  const { createIFrameLifecycleManager } = await import(
@@ -1,6 +1,5 @@
1
1
  import { Deferred } from "@frak-labs/frame-connector";
2
2
  import type { FrakLifecycleEvent } from "../../types";
3
- import { getClientId } from "../../utils/clientId";
4
3
  import { BACKUP_KEY } from "../../utils/constants";
5
4
  import {
6
5
  isFrakDeepLink,
@@ -47,42 +46,6 @@ function handleBackup(backup: string | undefined): void {
47
46
  }
48
47
  }
49
48
 
50
- /**
51
- * Handle handshake with iframe — sends client metadata so the listener can resolve the correct merchant
52
- * @param iframe - The iframe element to post the handshake response to
53
- * @param token - The handshake token received from the iframe
54
- * @param targetOrigin - The target origin for postMessage security
55
- * @param configDomain - Optional override domain for merchant resolution in tunneled/proxied environments
56
- */
57
- function handleHandshake(
58
- iframe: HTMLIFrameElement,
59
- token: string,
60
- targetOrigin: string,
61
- configDomain?: string
62
- ): void {
63
- const url = new URL(window.location.href);
64
- const pendingMergeToken = url.searchParams.get("fmt") ?? undefined;
65
-
66
- iframe.contentWindow?.postMessage(
67
- {
68
- clientLifecycle: "handshake-response",
69
- data: {
70
- token,
71
- currentUrl: window.location.href,
72
- pendingMergeToken,
73
- configDomain,
74
- clientId: getClientId(),
75
- },
76
- },
77
- targetOrigin
78
- );
79
-
80
- if (pendingMergeToken) {
81
- url.searchParams.delete("fmt");
82
- window.history.replaceState({}, "", url.toString());
83
- }
84
- }
85
-
86
49
  /**
87
50
  * Compute final redirect URL with parameter substitution
88
51
  */
@@ -167,17 +130,14 @@ function handleRedirect(
167
130
  * @param args
168
131
  * @param args.iframe - The iframe element used for wallet communication
169
132
  * @param args.targetOrigin - The wallet URL origin for postMessage security
170
- * @param args.configDomain - Optional domain override forwarded during handshake for tunneled/proxied environments
171
133
  * @ignore
172
134
  */
173
135
  export function createIFrameLifecycleManager({
174
136
  iframe,
175
137
  targetOrigin,
176
- configDomain,
177
138
  }: {
178
139
  iframe: HTMLIFrameElement;
179
140
  targetOrigin: string;
180
- configDomain?: string;
181
141
  }): IframeLifecycleManager {
182
142
  // Create the isConnected listener
183
143
  const isConnectedDeferred = new Deferred<boolean>();
@@ -206,10 +166,6 @@ export function createIFrameLifecycleManager({
206
166
  case "hide":
207
167
  changeIframeVisibility({ iframe, isVisible: event === "show" });
208
168
  break;
209
- // Handshake handling
210
- case "handshake":
211
- handleHandshake(iframe, data.token, targetOrigin, configDomain);
212
- break;
213
169
  // Redirect handling
214
170
  case "redirect":
215
171
  handleRedirect(
package/src/index.ts CHANGED
@@ -19,6 +19,9 @@ export type {
19
19
  DisplayEmbeddedWalletParamsType,
20
20
  DisplayEmbeddedWalletResultType,
21
21
  DisplayModalParamsType,
22
+ // RPC Sharing page
23
+ DisplaySharingPageParamsType,
24
+ DisplaySharingPageResultType,
22
25
  EmbeddedViewActionReferred,
23
26
  EmbeddedViewActionSharing,
24
27
  EstimatedReward,
@@ -47,6 +50,7 @@ export type {
47
50
  LoggedInEmbeddedView,
48
51
  LoggedOutEmbeddedView,
49
52
  LoginModalStepType,
53
+ MerchantConfigResponse,
50
54
  ModalRpcMetadata,
51
55
  ModalRpcStepsInput,
52
56
  ModalRpcStepsResultType,
@@ -58,12 +62,16 @@ export type {
58
62
  OpenSsoReturnType,
59
63
  PrepareSsoParamsType,
60
64
  PrepareSsoReturnType,
65
+ ResolvedPlacement,
66
+ ResolvedSdkConfig,
61
67
  RewardTier,
68
+ SdkResolvedConfig,
62
69
  // RPC Interaction
63
70
  SendInteractionParamsType,
64
71
  SendTransactionModalStepType,
65
72
  SendTransactionReturnType,
66
73
  SendTransactionTxType,
74
+ SharingPageProduct,
67
75
  SiweAuthenticateModalStepType,
68
76
  SiweAuthenticateReturnType,
69
77
  SiweAuthenticationParams,
@@ -72,8 +80,9 @@ export type {
72
80
  // Tracking
73
81
  TrackArrivalParams,
74
82
  TrackArrivalResult,
75
- UtmParams,
76
83
  // Rpc
84
+ UserReferralStatusType,
85
+ UtmParams,
77
86
  WalletStatusReturnType,
78
87
  } from "./types";
79
88
  export { isV1Context, isV2Context } from "./types";
@@ -84,7 +93,7 @@ export {
84
93
  base64urlEncode,
85
94
  baseIframeProps,
86
95
  type CompressedSsoData,
87
- clearMerchantIdCache,
96
+ clearAllCache,
88
97
  compressJsonToB64,
89
98
  createIframe,
90
99
  DEEP_LINK_SCHEME,
@@ -93,20 +102,24 @@ export {
93
102
  FrakContextManager,
94
103
  type FrakEvent,
95
104
  type FullSsoParams,
96
- fetchMerchantId,
97
105
  findIframeInOpener,
98
106
  formatAmount,
99
107
  generateSsoUrl,
100
108
  getBackendUrl,
109
+ getCache,
101
110
  getClientId,
102
111
  getCurrencyAmountKey,
103
112
  getSupportedCurrency,
104
113
  getSupportedLocale,
105
114
  isChromiumAndroid,
106
115
  isFrakDeepLink,
107
- resolveMerchantId,
116
+ isInAppBrowser,
117
+ isIOS,
118
+ redirectToExternalBrowser,
119
+ sdkConfigStore,
108
120
  toAndroidIntentUrl,
109
121
  trackEvent,
110
122
  triggerDeepLinkWithFallback,
123
+ withCache,
111
124
  } from "./utils";
112
125
  export { computeLegacyProductId } from "./utils/computeLegacyProductId";
@@ -27,7 +27,7 @@ export type FrakWalletSdkConfig = {
27
27
  /**
28
28
  * Your application name (will be displayed in a few modals and in SSO)
29
29
  */
30
- name: string;
30
+ name?: string;
31
31
  /**
32
32
  * Your merchant ID from the Frak dashboard (UUID format)
33
33
  * Used for referral tracking and analytics
@@ -71,6 +71,13 @@ export type FrakWalletSdkConfig = {
71
71
  * @defaultValue window.location.host
72
72
  */
73
73
  domain?: string;
74
+ /**
75
+ * Wait for backend config before rendering components.
76
+ * When true (default), components show a spinner until backend config is resolved.
77
+ * When false, components render immediately with SDK static config / HTML attributes.
78
+ * @defaultValue true
79
+ */
80
+ waitForBackendConfig?: boolean;
74
81
  };
75
82
 
76
83
  /**
@@ -111,7 +118,7 @@ export type I18nConfig =
111
118
  | LocalizedI18nConfig;
112
119
 
113
120
  /**
114
- * A localized i18n config
121
+ * A localized i18n config (inline objects only — URL-based i18n removed)
115
122
  * @category Config
116
123
  */
117
- export type LocalizedI18nConfig = `${string}.css` | { [key: string]: string };
124
+ export type LocalizedI18nConfig = { [key: string]: string };
@@ -17,11 +17,16 @@ export type {
17
17
  // Utils
18
18
  export type { FrakContext, FrakContextV1, FrakContextV2 } from "./context";
19
19
  export { isV1Context, isV2Context } from "./context";
20
-
21
20
  export type {
22
21
  ClientLifecycleEvent,
23
22
  IFrameLifecycleEvent,
24
23
  } from "./lifecycle";
24
+ export type {
25
+ MerchantConfigResponse,
26
+ ResolvedPlacement,
27
+ ResolvedSdkConfig,
28
+ SdkResolvedConfig,
29
+ } from "./resolvedConfig";
25
30
  export type { IFrameRpcSchema } from "./rpc";
26
31
  // Modal related
27
32
  export type {
@@ -31,6 +36,12 @@ export type {
31
36
  ModalRpcStepsResultType,
32
37
  ModalStepTypes,
33
38
  } from "./rpc/displayModal";
39
+ // Sharing page related
40
+ export type {
41
+ DisplaySharingPageParamsType,
42
+ DisplaySharingPageResultType,
43
+ SharingPageProduct,
44
+ } from "./rpc/displaySharingPage";
34
45
  export type {
35
46
  DisplayEmbeddedWalletParamsType,
36
47
  DisplayEmbeddedWalletResultType,
@@ -65,6 +76,7 @@ export type {
65
76
  PrepareSsoReturnType,
66
77
  SsoMetadata,
67
78
  } from "./rpc/sso";
79
+ export type { UserReferralStatusType } from "./rpc/userReferralStatus";
68
80
  export type { WalletStatusReturnType } from "./rpc/walletStatus";
69
81
  // Tracking
70
82
  export type {
@@ -1,4 +1,5 @@
1
1
  import type { I18nConfig } from "../config";
2
+ import type { ResolvedSdkConfig } from "../resolvedConfig";
2
3
 
3
4
  /**
4
5
  * Event related to the iframe lifecycle
@@ -9,9 +10,9 @@ export type ClientLifecycleEvent =
9
10
  | CustomI18nEvent
10
11
  | RestoreBackupEvent
11
12
  | HearbeatEvent
12
- | HandshakeResponse
13
13
  | SsoRedirectCompleteEvent
14
- | DeepLinkFailedEvent;
14
+ | DeepLinkFailedEvent
15
+ | ResolvedConfigEvent;
15
16
 
16
17
  type CustomCssEvent = {
17
18
  clientLifecycle: "modal-css";
@@ -33,31 +34,6 @@ type HearbeatEvent = {
33
34
  data?: never;
34
35
  };
35
36
 
36
- type HandshakeResponse = {
37
- clientLifecycle: "handshake-response";
38
- data: {
39
- token: string;
40
- currentUrl: string;
41
- /**
42
- * Pending merge token extracted from URL (?fmt= parameter)
43
- * When present, listener should execute identity merge in background
44
- * URL is cleaned after handshake response is sent
45
- */
46
- pendingMergeToken?: string;
47
- /**
48
- * Client ID for identity tracking (belt & suspenders fallback)
49
- * Primary delivery is via iframe URL query param; handshake is backup for SSR
50
- */
51
- clientId?: string;
52
- /**
53
- * Explicit domain from SDK config (FrakWalletSdkConfig.domain)
54
- * When present, listener should prefer this over URL-derived domain
55
- * for merchant resolution (handles proxied/tunneled environments)
56
- */
57
- configDomain?: string;
58
- };
59
- };
60
-
61
37
  type SsoRedirectCompleteEvent = {
62
38
  clientLifecycle: "sso-redirect-complete";
63
39
  data: { compressed: string };
@@ -67,3 +43,22 @@ type DeepLinkFailedEvent = {
67
43
  clientLifecycle: "deep-link-failed";
68
44
  data: { originalUrl: string };
69
45
  };
46
+
47
+ type ResolvedConfigEvent = {
48
+ clientLifecycle: "resolved-config";
49
+ data: {
50
+ merchantId: string;
51
+ /** The domain the backend resolved this config for */
52
+ domain: string;
53
+ /** All domains registered for this merchant (for domain proof) */
54
+ allowedDomains: string[];
55
+ /** Full URL of the parent page (for interaction tracking) */
56
+ sourceUrl: string;
57
+ /**
58
+ * Pending merge token extracted from URL (?fmt= parameter).
59
+ * When present, listener should execute identity merge in background.
60
+ */
61
+ pendingMergeToken?: string;
62
+ sdkConfig?: ResolvedSdkConfig;
63
+ };
64
+ };
@@ -8,7 +8,6 @@ export type IFrameLifecycleEvent =
8
8
  data?: never;
9
9
  }
10
10
  | DoBackupEvent
11
- | HandshakeRequestEvent
12
11
  | RedirectRequestEvent;
13
12
 
14
13
  type DoBackupEvent = {
@@ -16,13 +15,6 @@ type DoBackupEvent = {
16
15
  data: { backup?: string };
17
16
  };
18
17
 
19
- type HandshakeRequestEvent = {
20
- iframeLifecycle: "handshake";
21
- data: {
22
- token: string;
23
- };
24
- };
25
-
26
18
  type RedirectRequestEvent = {
27
19
  iframeLifecycle: "redirect";
28
20
  data: {
@@ -0,0 +1,122 @@
1
+ import type { Currency, Language } from "./config";
2
+
3
+ /**
4
+ * Response from the merchant resolve endpoint
5
+ * @category Config
6
+ */
7
+ export type MerchantConfigResponse = {
8
+ merchantId: string;
9
+ name: string;
10
+ domain: string;
11
+ allowedDomains: string[];
12
+ sdkConfig?: ResolvedSdkConfig;
13
+ };
14
+
15
+ /**
16
+ * Resolved placement config from backend
17
+ * Translations already flattened: default + lang-specific merged into one record
18
+ * @category Config
19
+ */
20
+ export type ResolvedPlacement = {
21
+ /** Per-component configuration within this placement */
22
+ components?: {
23
+ buttonShare?: {
24
+ text?: string;
25
+ noRewardText?: string;
26
+ clickAction?: "embedded-wallet" | "share-modal" | "sharing-page";
27
+ useReward?: boolean;
28
+ css?: string;
29
+ };
30
+ buttonWallet?: {
31
+ position?: "right" | "left";
32
+ css?: string;
33
+ };
34
+ openInApp?: {
35
+ text?: string;
36
+ css?: string;
37
+ };
38
+ postPurchase?: {
39
+ refereeText?: string;
40
+ refereeNoRewardText?: string;
41
+ referrerText?: string;
42
+ referrerNoRewardText?: string;
43
+ ctaText?: string;
44
+ ctaNoRewardText?: string;
45
+ css?: string;
46
+ };
47
+ banner?: {
48
+ referralTitle?: string;
49
+ referralDescription?: string;
50
+ referralCta?: string;
51
+ inappTitle?: string;
52
+ inappDescription?: string;
53
+ inappCta?: string;
54
+ css?: string;
55
+ };
56
+ };
57
+ targetInteraction?: string;
58
+ /** Already flattened: default + lang-specific merged into one record */
59
+ translations?: Record<string, string>;
60
+ /** Global placement CSS (applied to modals/listener) */
61
+ css?: string;
62
+ };
63
+
64
+ /**
65
+ * Resolved SDK config from backend `/resolve` endpoint
66
+ * Language resolution and translation merging already applied
67
+ * @category Config
68
+ */
69
+ export type ResolvedSdkConfig = {
70
+ name?: string;
71
+ logoUrl?: string;
72
+ homepageLink?: string;
73
+ currency?: Currency;
74
+ lang?: Language;
75
+ /** When true, all SDK components should be hidden */
76
+ hidden?: boolean;
77
+ css?: string;
78
+ translations?: Record<string, string>;
79
+ placements?: Record<string, ResolvedPlacement>;
80
+ };
81
+
82
+ /**
83
+ * Internal SDK config store state
84
+ * Merged config: backend > SDK static > defaults
85
+ * Components subscribe to this reactively
86
+ * @category Config
87
+ */
88
+ export type SdkResolvedConfig = {
89
+ /** Whether the backend config has been resolved */
90
+ isResolved: boolean;
91
+
92
+ /** Merchant ID from resolution */
93
+ merchantId: string;
94
+
95
+ /** Domain returned by the resolve endpoint */
96
+ domain?: string;
97
+
98
+ /** Domains allowed for this merchant (used by iframe trust check) */
99
+ allowedDomains?: string[];
100
+
101
+ /** Whether the resolve returned a backend sdkConfig object */
102
+ hasRawSdkConfig?: boolean;
103
+
104
+ /** Merged metadata fields */
105
+ name?: string;
106
+ logoUrl?: string;
107
+ homepageLink?: string;
108
+ lang?: Language;
109
+ currency?: Currency;
110
+
111
+ /** When true, all SDK components should be hidden */
112
+ hidden?: boolean;
113
+
114
+ /** Global CSS from backend config (passed to iframe) */
115
+ css?: string;
116
+
117
+ /** Global translations (for reference / component fallback) */
118
+ translations?: Record<string, string>;
119
+
120
+ /** Named placements (keyed by placement ID) */
121
+ placements?: Record<string, ResolvedPlacement>;
122
+ };
@@ -0,0 +1,77 @@
1
+ import type { InteractionTypeKey } from "../../constants/interactionTypes";
2
+ import type { I18nConfig } from "../config";
3
+
4
+ /**
5
+ * Product information to display on the sharing page
6
+ * @group Sharing Page
7
+ */
8
+ export type SharingPageProduct = {
9
+ /**
10
+ * The product title / name
11
+ */
12
+ title: string;
13
+ /**
14
+ * Optional product image URL
15
+ */
16
+ imageUrl?: string;
17
+ };
18
+
19
+ /**
20
+ * Parameters to display the sharing page
21
+ * @group Sharing Page
22
+ * @group RPC Schema
23
+ */
24
+ export type DisplaySharingPageParamsType = {
25
+ /**
26
+ * Products to showcase on the sharing page
27
+ * If provided, they will be displayed in a product card section
28
+ */
29
+ products?: SharingPageProduct[];
30
+ /**
31
+ * Optional link override for sharing
32
+ * If not provided, the sharing link will be generated from the current page URL + merchant context
33
+ */
34
+ link?: string;
35
+ /**
36
+ * Optional metadata overrides for the sharing page
37
+ */
38
+ metadata?: {
39
+ /**
40
+ * Logo override for the sharing page header
41
+ */
42
+ logo?: string;
43
+ /**
44
+ * Link to the homepage of the calling website
45
+ */
46
+ homepageLink?: string;
47
+ /**
48
+ * The target interaction behind this sharing page
49
+ */
50
+ targetInteraction?: InteractionTypeKey;
51
+ /**
52
+ * i18n overrides for the sharing page
53
+ */
54
+ i18n?: I18nConfig;
55
+ };
56
+ };
57
+
58
+ /**
59
+ * Result of the sharing page display
60
+ * @group Sharing Page
61
+ * @group RPC Schema
62
+ */
63
+ export type DisplaySharingPageResultType = {
64
+ /**
65
+ * The action the user took
66
+ * - "shared": User used the native share dialog
67
+ * - "copied": User copied the link to clipboard
68
+ * - "dismissed": User dismissed the sharing page without acting
69
+ */
70
+ action: "shared" | "copied" | "dismissed";
71
+ /**
72
+ * The install URL for the Frak app
73
+ * Can be used as a fallback to redirect the user to the install page
74
+ * from the merchant's top-level page (e.g. via `window.location.href`)
75
+ */
76
+ installUrl?: string;
77
+ };
@@ -26,6 +26,10 @@ export type SendInteractionParamsType =
26
26
  }
27
27
  | {
28
28
  type: "sharing";
29
+ /** Epoch seconds timestamp matching the V2 context `t` field embedded in the referral link URL, used for backend correlation */
30
+ sharingTimestamp?: number;
31
+ /** Merchant order ID linking this sharing event to a purchase (stays server-side, never in URL) */
32
+ purchaseId?: string;
29
33
  }
30
34
  | {
31
35
  type: "custom";
@@ -0,0 +1,20 @@
1
+ /**
2
+ * User referral status returned by `frak_getUserReferralStatus`.
3
+ *
4
+ * Generic referral context for the current user on a merchant.
5
+ * Used by components like `<frak-post-purchase>` and `<frak-referred-banner>`
6
+ * to adapt their display based on the user's referral relationship.
7
+ *
8
+ * Returns `null` when the user's identity cannot be resolved
9
+ * (e.g. no clientId and no wallet session).
10
+ *
11
+ * @group RPC Schema
12
+ */
13
+ export type UserReferralStatusType = {
14
+ /**
15
+ * Whether the user was referred to this merchant by someone else.
16
+ *
17
+ * `true` means a referral link exists where this user is the referee.
18
+ */
19
+ isReferred: boolean;
20
+ };