@frak-labs/core-sdk 0.1.1 → 0.2.0-beta.7898df5b
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 +58 -0
- package/cdn/bundle.js +14 -0
- package/dist/actions.cjs +1 -1
- package/dist/actions.d.cts +3 -3
- package/dist/actions.d.ts +3 -3
- package/dist/actions.js +1 -1
- package/dist/bundle.cjs +1 -1
- package/dist/bundle.d.cts +4 -6
- package/dist/bundle.d.ts +4 -6
- package/dist/bundle.js +1 -1
- package/dist/computeLegacyProductId-CCAZvLa5.d.cts +537 -0
- package/dist/computeLegacyProductId-b5cUWdAm.d.ts +537 -0
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +3 -4
- package/dist/index.d.ts +3 -4
- package/dist/index.js +1 -1
- package/dist/{openSso-D--Airj6.d.cts → openSso-B0g7-807.d.cts} +173 -136
- package/dist/{openSso-DsKJ4y0j.d.ts → openSso-CMzwvaCa.d.ts} +173 -136
- package/dist/setupClient-BICl5fdX.js +13 -0
- package/dist/setupClient-nl8Dhh4V.cjs +13 -0
- package/dist/siweAuthenticate-BWmI2_TN.cjs +1 -0
- package/dist/{index-d8xS4ryI.d.ts → siweAuthenticate-CVigMOxz.d.cts} +113 -92
- package/dist/{index-C6FxkWPC.d.cts → siweAuthenticate-CnCZ7mok.d.ts} +113 -92
- package/dist/siweAuthenticate-zczqxm0a.js +1 -0
- package/dist/trackEvent-CeLFVzZn.js +1 -0
- package/dist/trackEvent-Ew5r5zfI.cjs +1 -0
- package/package.json +11 -22
- package/src/actions/displayEmbeddedWallet.ts +1 -0
- package/src/actions/displayModal.test.ts +12 -11
- package/src/actions/displayModal.ts +7 -18
- package/src/actions/ensureIdentity.ts +68 -0
- package/src/actions/{getProductInformation.test.ts → getMerchantInformation.test.ts} +33 -50
- package/src/actions/getMerchantInformation.ts +16 -0
- package/src/actions/index.ts +3 -2
- package/src/actions/openSso.ts +4 -2
- package/src/actions/referral/processReferral.test.ts +117 -242
- package/src/actions/referral/processReferral.ts +134 -204
- package/src/actions/referral/referralInteraction.test.ts +4 -12
- package/src/actions/referral/referralInteraction.ts +3 -13
- package/src/actions/sendInteraction.ts +46 -22
- package/src/actions/trackPurchaseStatus.test.ts +354 -141
- package/src/actions/trackPurchaseStatus.ts +48 -11
- package/src/actions/watchWalletStatus.ts +2 -3
- package/src/actions/wrapper/modalBuilder.test.ts +0 -14
- package/src/actions/wrapper/modalBuilder.ts +3 -12
- package/src/bundle.ts +0 -1
- package/src/clients/createIFrameFrakClient.ts +10 -5
- package/src/clients/transports/iframeLifecycleManager.test.ts +163 -4
- package/src/clients/transports/iframeLifecycleManager.ts +172 -33
- package/src/constants/interactionTypes.ts +12 -41
- package/src/index.ts +27 -16
- package/src/types/config.ts +6 -0
- package/src/types/context.ts +48 -6
- package/src/types/index.ts +15 -11
- package/src/types/lifecycle/client.ts +24 -1
- package/src/types/lifecycle/iframe.ts +6 -0
- package/src/types/rpc/displayModal.ts +2 -4
- package/src/types/rpc/embedded/index.ts +2 -2
- package/src/types/rpc/interaction.ts +31 -39
- package/src/types/rpc/merchantInformation.ts +77 -0
- package/src/types/rpc/modal/index.ts +0 -4
- package/src/types/rpc/modal/login.ts +5 -1
- package/src/types/rpc/walletStatus.ts +1 -7
- package/src/types/rpc.ts +22 -30
- package/src/types/tracking.ts +31 -0
- package/src/utils/FrakContext.test.ts +270 -186
- package/src/utils/FrakContext.ts +78 -56
- package/src/utils/backendUrl.test.ts +83 -0
- package/src/utils/backendUrl.ts +62 -0
- package/src/utils/clientId.test.ts +41 -0
- package/src/utils/clientId.ts +43 -0
- package/src/utils/compression/compress.test.ts +1 -1
- package/src/utils/compression/compress.ts +2 -2
- package/src/utils/compression/decompress.test.ts +8 -4
- package/src/utils/compression/decompress.ts +2 -2
- package/src/utils/{computeProductId.ts → computeLegacyProductId.ts} +2 -2
- package/src/utils/constants.ts +5 -0
- package/src/utils/deepLinkWithFallback.test.ts +243 -0
- package/src/utils/deepLinkWithFallback.ts +103 -0
- package/src/utils/formatAmount.ts +6 -0
- package/src/utils/iframeHelper.test.ts +18 -5
- package/src/utils/iframeHelper.ts +10 -3
- package/src/utils/index.ts +16 -1
- package/src/utils/merchantId.test.ts +653 -0
- package/src/utils/merchantId.ts +143 -0
- package/src/utils/sso.ts +18 -11
- package/src/utils/trackEvent.test.ts +23 -5
- package/src/utils/trackEvent.ts +13 -0
- package/cdn/bundle.iife.js +0 -14
- package/dist/actions-B5j-i1p0.cjs +0 -1
- package/dist/actions-q090Z0oR.js +0 -1
- package/dist/index-7OZ39x1U.d.ts +0 -195
- package/dist/index-CRsQWnTs.d.cts +0 -351
- package/dist/index-Ck1hudEi.d.ts +0 -351
- package/dist/index-zDq-VlKx.d.cts +0 -195
- package/dist/interaction-DMJ3ZfaF.d.cts +0 -45
- package/dist/interaction-KX1h9a7V.d.ts +0 -45
- package/dist/interactions-DnfM3oe0.js +0 -1
- package/dist/interactions-EIXhNLf6.cjs +0 -1
- package/dist/interactions.cjs +0 -1
- package/dist/interactions.d.cts +0 -2
- package/dist/interactions.d.ts +0 -2
- package/dist/interactions.js +0 -1
- package/dist/productTypes-BUkXJKZ7.cjs +0 -1
- package/dist/productTypes-CGb1MmBF.js +0 -1
- package/dist/src-1LQ4eLq5.js +0 -13
- package/dist/src-hW71KjPN.cjs +0 -13
- package/dist/trackEvent-CHnYa85W.js +0 -1
- package/dist/trackEvent-GuQm_1Nm.cjs +0 -1
- package/src/actions/getProductInformation.ts +0 -14
- package/src/actions/openSso.test.ts +0 -407
- package/src/actions/sendInteraction.test.ts +0 -219
- package/src/constants/interactionTypes.test.ts +0 -128
- package/src/constants/productTypes.test.ts +0 -130
- package/src/constants/productTypes.ts +0 -33
- package/src/interactions/index.ts +0 -5
- package/src/interactions/pressEncoder.test.ts +0 -215
- package/src/interactions/pressEncoder.ts +0 -53
- package/src/interactions/purchaseEncoder.test.ts +0 -291
- package/src/interactions/purchaseEncoder.ts +0 -99
- package/src/interactions/referralEncoder.test.ts +0 -170
- package/src/interactions/referralEncoder.ts +0 -47
- package/src/interactions/retailEncoder.test.ts +0 -107
- package/src/interactions/retailEncoder.ts +0 -37
- package/src/interactions/webshopEncoder.test.ts +0 -56
- package/src/interactions/webshopEncoder.ts +0 -30
- package/src/types/rpc/modal/openSession.ts +0 -25
- package/src/types/rpc/productInformation.ts +0 -59
- package/src/utils/computeProductId.test.ts +0 -80
- package/src/utils/sso.test.ts +0 -361
|
@@ -1,256 +1,186 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { type Address, type Hex, isAddressEqual } from "viem";
|
|
3
|
-
import { ReferralInteractionEncoder } from "../../interactions";
|
|
1
|
+
import { isAddressEqual } from "viem";
|
|
4
2
|
import type {
|
|
5
|
-
DisplayEmbeddedWalletParamsType,
|
|
6
3
|
FrakClient,
|
|
7
4
|
FrakContext,
|
|
5
|
+
FrakContextV2,
|
|
8
6
|
WalletStatusReturnType,
|
|
9
7
|
} from "../../types";
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
8
|
+
import { isV1Context, isV2Context } from "../../types";
|
|
9
|
+
import { FrakContextManager, getClientId, trackEvent } from "../../utils";
|
|
10
|
+
import { sendInteraction } from "../sendInteraction";
|
|
12
11
|
|
|
13
12
|
/**
|
|
14
|
-
*
|
|
13
|
+
* Options for the referral auto-interaction process.
|
|
14
|
+
*/
|
|
15
|
+
export type ProcessReferralOptions = {
|
|
16
|
+
/**
|
|
17
|
+
* If true, always replace the URL with the current user's referral context
|
|
18
|
+
* so the next visitor gets referred by this user.
|
|
19
|
+
* @defaultValue false
|
|
20
|
+
*/
|
|
21
|
+
alwaysAppendUrl?: boolean;
|
|
22
|
+
/**
|
|
23
|
+
* Merchant ID for building the current user's referral context.
|
|
24
|
+
* Required when `alwaysAppendUrl` is true and the incoming context is V1.
|
|
25
|
+
* For V2 contexts, the merchantId is already embedded in the context.
|
|
26
|
+
*/
|
|
27
|
+
merchantId?: string;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* The different states of the referral process.
|
|
15
32
|
* @inline
|
|
16
33
|
*/
|
|
17
34
|
type ReferralState =
|
|
18
35
|
| "idle"
|
|
19
36
|
| "processing"
|
|
20
37
|
| "success"
|
|
21
|
-
| "no-wallet"
|
|
22
|
-
| "no-session"
|
|
23
|
-
| "error"
|
|
24
38
|
| "no-referrer"
|
|
25
39
|
| "self-referral";
|
|
26
40
|
|
|
27
41
|
/**
|
|
28
|
-
*
|
|
29
|
-
|
|
30
|
-
export type ProcessReferralOptions = {
|
|
31
|
-
/**
|
|
32
|
-
* If we want to always append the url with the frak context or not
|
|
33
|
-
* @defaultValue false
|
|
34
|
-
*/
|
|
35
|
-
alwaysAppendUrl?: boolean;
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* This function handle all the heavy lifting of the referral interaction process
|
|
40
|
-
* 1. Check if the user has been referred or not (if not, early exit)
|
|
41
|
-
* 2. Then check if the user is logged in or not
|
|
42
|
-
* 2.1 If not logged in, try a soft login, if it fail, display a modal for the user to login
|
|
43
|
-
* 3. Check if that's not a self-referral (if yes, early exit)
|
|
44
|
-
* 4. Check if the user has an interaction session or not
|
|
45
|
-
* 4.1 If not, display a modal for the user to open a session
|
|
46
|
-
* 5. Push the referred interaction
|
|
47
|
-
* 6. Update the current url with the right data
|
|
48
|
-
* 7. Return the resulting referral state
|
|
42
|
+
* Track an arrival event if the context version is recognized.
|
|
43
|
+
* Sends both tracking analytics and the arrival interaction RPC.
|
|
49
44
|
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
* @param client - The current Frak Client
|
|
53
|
-
* @param args
|
|
54
|
-
* @param args.walletStatus - The current user wallet status
|
|
55
|
-
* @param args.frakContext - The current frak context
|
|
56
|
-
* @param args.modalConfig - The modal configuration to display if the user is not logged in
|
|
57
|
-
* @param args.productId - The product id to interact with (if not specified will be recomputed from the current domain)
|
|
58
|
-
* @param args.options - Some options for the referral interaction
|
|
59
|
-
* @returns A promise with the resulting referral state
|
|
60
|
-
*
|
|
61
|
-
* @see {@link displayModal} for more details about the displayed modal
|
|
62
|
-
* @see {@link sendInteraction} for more details on the interaction submission part
|
|
63
|
-
* @see {@link ReferralInteractionEncoder} for more details about the referred interaction
|
|
64
|
-
* @see {@link @frak-labs/core-sdk!ModalStepTypes} for more details on each modal steps types
|
|
45
|
+
* @returns true if the context was valid and tracked, false otherwise
|
|
65
46
|
*/
|
|
66
|
-
|
|
47
|
+
function trackArrivalIfValid(
|
|
67
48
|
client: FrakClient,
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
options?: ProcessReferralOptions;
|
|
80
|
-
}
|
|
81
|
-
) {
|
|
82
|
-
// Early exit if we don't have any referral informations
|
|
83
|
-
if (!frakContext?.r) {
|
|
84
|
-
return "no-referrer";
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
// If we got a context, log an event
|
|
88
|
-
trackEvent(client, "user_referred_started", {
|
|
89
|
-
properties: {
|
|
90
|
-
referrer: frakContext?.r,
|
|
91
|
-
walletStatus: walletStatus?.key,
|
|
92
|
-
},
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
// Helper to fetch a fresh wallet status
|
|
96
|
-
let walletRequest = false;
|
|
97
|
-
async function getFreshWalletStatus() {
|
|
98
|
-
if (walletRequest) {
|
|
99
|
-
return;
|
|
100
|
-
}
|
|
101
|
-
walletRequest = true;
|
|
102
|
-
return ensureWalletConnected(client, {
|
|
103
|
-
modalConfig: {
|
|
104
|
-
...modalConfig,
|
|
105
|
-
loggedIn: {
|
|
106
|
-
action: {
|
|
107
|
-
key: "referred",
|
|
108
|
-
},
|
|
109
|
-
},
|
|
49
|
+
frakContext: FrakContext,
|
|
50
|
+
walletStatus?: WalletStatusReturnType
|
|
51
|
+
): boolean {
|
|
52
|
+
const landingUrl =
|
|
53
|
+
typeof window !== "undefined" ? window.location.href : undefined;
|
|
54
|
+
|
|
55
|
+
if (isV2Context(frakContext)) {
|
|
56
|
+
trackEvent(client, "user_referred_started", {
|
|
57
|
+
properties: {
|
|
58
|
+
referrerClientId: frakContext.c,
|
|
59
|
+
walletStatus: walletStatus?.key,
|
|
110
60
|
},
|
|
111
|
-
walletStatus,
|
|
112
61
|
});
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
62
|
+
sendInteraction(client, {
|
|
63
|
+
type: "arrival",
|
|
64
|
+
referrerClientId: frakContext.c,
|
|
65
|
+
referrerMerchantId: frakContext.m,
|
|
66
|
+
referralTimestamp: frakContext.t,
|
|
67
|
+
landingUrl,
|
|
119
68
|
});
|
|
120
|
-
|
|
69
|
+
return true;
|
|
121
70
|
}
|
|
122
71
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
const { status, currentWallet } = await processReferralLogic({
|
|
126
|
-
initialWalletStatus: walletStatus,
|
|
127
|
-
getFreshWalletStatus,
|
|
128
|
-
pushReferralInteraction,
|
|
129
|
-
// We can enforce this type cause of the condition at the start
|
|
130
|
-
frakContext: frakContext as Pick<FrakContext, "r">,
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
// Update the current url with the right data
|
|
134
|
-
FrakContextManager.replaceUrl({
|
|
135
|
-
url: window.location?.href,
|
|
136
|
-
context: options?.alwaysAppendUrl ? { r: currentWallet } : null,
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
// Track the event
|
|
140
|
-
trackEvent(client, "user_referred_completed", {
|
|
72
|
+
if (isV1Context(frakContext)) {
|
|
73
|
+
trackEvent(client, "user_referred_started", {
|
|
141
74
|
properties: {
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
wallet: currentWallet,
|
|
75
|
+
referrer: frakContext.r,
|
|
76
|
+
walletStatus: walletStatus?.key,
|
|
145
77
|
},
|
|
146
78
|
});
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
// Track the error event
|
|
153
|
-
trackEvent(client, "user_referred_error", {
|
|
154
|
-
properties: {
|
|
155
|
-
referrer: frakContext?.r,
|
|
156
|
-
error:
|
|
157
|
-
error instanceof FrakRpcError
|
|
158
|
-
? `[${error.code}] ${error.name} - ${error.message}`
|
|
159
|
-
: error instanceof Error
|
|
160
|
-
? error.message
|
|
161
|
-
: "undefined",
|
|
162
|
-
},
|
|
79
|
+
sendInteraction(client, {
|
|
80
|
+
type: "arrival",
|
|
81
|
+
referrerWallet: frakContext.r,
|
|
82
|
+
landingUrl,
|
|
163
83
|
});
|
|
164
|
-
|
|
165
|
-
// Update the current url with the right data
|
|
166
|
-
FrakContextManager.replaceUrl({
|
|
167
|
-
url: window.location?.href,
|
|
168
|
-
context: options?.alwaysAppendUrl
|
|
169
|
-
? { r: walletStatus?.wallet }
|
|
170
|
-
: null,
|
|
171
|
-
});
|
|
172
|
-
|
|
173
|
-
// And map the error a state
|
|
174
|
-
return mapErrorToState(error);
|
|
84
|
+
return true;
|
|
175
85
|
}
|
|
86
|
+
|
|
87
|
+
return false;
|
|
176
88
|
}
|
|
177
89
|
|
|
178
90
|
/**
|
|
179
|
-
*
|
|
180
|
-
*
|
|
181
|
-
* @param walletStatus
|
|
182
|
-
* @param frakContext
|
|
91
|
+
* Build a V2 context representing the current user for URL replacement.
|
|
92
|
+
* @returns A V2 context, or null if clientId or merchantId is unavailable
|
|
183
93
|
*/
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
}) {
|
|
195
|
-
// Get the current wallet, without auto displaying the modal
|
|
196
|
-
let currentWallet = initialWalletStatus?.wallet;
|
|
94
|
+
function buildCurrentUserContext(merchantId: string): FrakContextV2 | null {
|
|
95
|
+
const clientId = getClientId();
|
|
96
|
+
if (!clientId) return null;
|
|
97
|
+
return {
|
|
98
|
+
v: 2,
|
|
99
|
+
c: clientId,
|
|
100
|
+
m: merchantId,
|
|
101
|
+
t: Math.floor(Date.now() / 1000),
|
|
102
|
+
};
|
|
103
|
+
}
|
|
197
104
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
105
|
+
/**
|
|
106
|
+
* Client-side self-referral preflight check.
|
|
107
|
+
* Prevents unnecessary backend round-trips for obvious self-referrals.
|
|
108
|
+
*/
|
|
109
|
+
function isSelfReferral(
|
|
110
|
+
frakContext: FrakContext,
|
|
111
|
+
walletStatus?: WalletStatusReturnType
|
|
112
|
+
): boolean {
|
|
113
|
+
if (isV2Context(frakContext)) {
|
|
114
|
+
return getClientId() === frakContext.c;
|
|
202
115
|
}
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
return { status: "self-referral", currentWallet } as const;
|
|
116
|
+
if (isV1Context(frakContext) && walletStatus?.wallet) {
|
|
117
|
+
return isAddressEqual(frakContext.r, walletStatus.wallet);
|
|
206
118
|
}
|
|
207
|
-
|
|
208
|
-
// If the current wallet doesn't have an interaction session, display the modal
|
|
209
|
-
if (!initialWalletStatus?.interactionSession) {
|
|
210
|
-
currentWallet = await getFreshWalletStatus();
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
// Push the referred interaction
|
|
214
|
-
await pushReferralInteraction(frakContext.r);
|
|
215
|
-
return { status: "success", currentWallet } as const;
|
|
119
|
+
return false;
|
|
216
120
|
}
|
|
217
121
|
|
|
218
122
|
/**
|
|
219
|
-
*
|
|
123
|
+
* Handle the full referral interaction flow:
|
|
124
|
+
*
|
|
125
|
+
* 1. Check if the user has been referred (if not, early exit)
|
|
126
|
+
* 2. Preflight self-referral check (if yes, early exit)
|
|
127
|
+
* 3. Track the arrival event
|
|
128
|
+
* 4. Replace the current URL with the user's own referral context
|
|
129
|
+
* 5. Return the resulting referral state
|
|
130
|
+
*
|
|
131
|
+
* @param client - The current Frak Client
|
|
132
|
+
* @param args
|
|
133
|
+
* @param args.walletStatus - The current user wallet status
|
|
134
|
+
* @param args.frakContext - The referral context parsed from the URL
|
|
135
|
+
* @param args.options - Options for URL replacement and merchant context
|
|
136
|
+
* @returns The referral state
|
|
137
|
+
*
|
|
138
|
+
* @see {@link @frak-labs/core-sdk!ModalStepTypes} for modal step types
|
|
220
139
|
*/
|
|
221
|
-
|
|
140
|
+
export function processReferral(
|
|
222
141
|
client: FrakClient,
|
|
223
142
|
{
|
|
224
|
-
modalConfig,
|
|
225
143
|
walletStatus,
|
|
144
|
+
frakContext,
|
|
145
|
+
options,
|
|
226
146
|
}: {
|
|
227
|
-
modalConfig?: DisplayEmbeddedWalletParamsType;
|
|
228
147
|
walletStatus?: WalletStatusReturnType;
|
|
148
|
+
frakContext?: FrakContext | null;
|
|
149
|
+
options?: ProcessReferralOptions;
|
|
229
150
|
}
|
|
230
|
-
) {
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
const result = await displayEmbeddedWallet(client, modalConfig ?? {});
|
|
234
|
-
return result?.wallet ?? undefined;
|
|
151
|
+
): ReferralState {
|
|
152
|
+
if (!frakContext) {
|
|
153
|
+
return "no-referrer";
|
|
235
154
|
}
|
|
236
155
|
|
|
237
|
-
|
|
238
|
-
|
|
156
|
+
if (isSelfReferral(frakContext, walletStatus)) {
|
|
157
|
+
return "self-referral";
|
|
158
|
+
}
|
|
239
159
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
* @param error
|
|
243
|
-
*/
|
|
244
|
-
function mapErrorToState(error: unknown): ReferralState {
|
|
245
|
-
if (error instanceof FrakRpcError) {
|
|
246
|
-
switch (error.code) {
|
|
247
|
-
case RpcErrorCodes.walletNotConnected:
|
|
248
|
-
return "no-wallet";
|
|
249
|
-
case RpcErrorCodes.serverErrorForInteractionDelegation:
|
|
250
|
-
return "no-session";
|
|
251
|
-
default:
|
|
252
|
-
return "error";
|
|
253
|
-
}
|
|
160
|
+
if (!trackArrivalIfValid(client, frakContext, walletStatus)) {
|
|
161
|
+
return "no-referrer";
|
|
254
162
|
}
|
|
255
|
-
|
|
163
|
+
|
|
164
|
+
// V2 context embeds merchantId; V1 falls back to options
|
|
165
|
+
const contextMerchantId = isV2Context(frakContext)
|
|
166
|
+
? frakContext.m
|
|
167
|
+
: options?.merchantId;
|
|
168
|
+
|
|
169
|
+
const replaceContext =
|
|
170
|
+
options?.alwaysAppendUrl && contextMerchantId
|
|
171
|
+
? buildCurrentUserContext(contextMerchantId)
|
|
172
|
+
: null;
|
|
173
|
+
|
|
174
|
+
FrakContextManager.replaceUrl({
|
|
175
|
+
url: window.location?.href,
|
|
176
|
+
context: replaceContext,
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
trackEvent(client, "user_referred_completed", {
|
|
180
|
+
properties: {
|
|
181
|
+
status: "success",
|
|
182
|
+
},
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
return "success";
|
|
256
186
|
}
|
|
@@ -21,9 +21,6 @@ describe("referralInteraction", () => {
|
|
|
21
21
|
request: vi.fn(),
|
|
22
22
|
} as any;
|
|
23
23
|
|
|
24
|
-
const mockProductId =
|
|
25
|
-
"0x0000000000000000000000000000000000000000000000000000000000000002" as Hex;
|
|
26
|
-
|
|
27
24
|
beforeEach(() => {
|
|
28
25
|
vi.clearAllMocks();
|
|
29
26
|
Object.defineProperty(global, "window", {
|
|
@@ -55,8 +52,8 @@ describe("referralInteraction", () => {
|
|
|
55
52
|
|
|
56
53
|
vi.mocked(FrakContextManager.parse).mockReturnValue({} as any);
|
|
57
54
|
vi.mocked(watchWalletStatus).mockResolvedValue({
|
|
55
|
+
key: "connected",
|
|
58
56
|
wallet: "0x123" as Hex,
|
|
59
|
-
interactionSession: true,
|
|
60
57
|
} as any);
|
|
61
58
|
vi.mocked(processReferral).mockResolvedValue("success");
|
|
62
59
|
|
|
@@ -72,7 +69,6 @@ describe("referralInteraction", () => {
|
|
|
72
69
|
|
|
73
70
|
const mockContext = { r: "0xreferrer" as Hex };
|
|
74
71
|
const mockWalletStatus = { wallet: "0x123" as Hex };
|
|
75
|
-
const mockModalConfig = { type: "login" };
|
|
76
72
|
const mockOptions = { alwaysAppendUrl: true };
|
|
77
73
|
|
|
78
74
|
vi.mocked(FrakContextManager.parse).mockReturnValue(mockContext as any);
|
|
@@ -80,16 +76,12 @@ describe("referralInteraction", () => {
|
|
|
80
76
|
vi.mocked(processReferral).mockResolvedValue("success");
|
|
81
77
|
|
|
82
78
|
await referralInteraction(mockClient, {
|
|
83
|
-
productId: mockProductId,
|
|
84
|
-
modalConfig: mockModalConfig as any,
|
|
85
79
|
options: mockOptions,
|
|
86
80
|
});
|
|
87
81
|
|
|
88
82
|
expect(processReferral).toHaveBeenCalledWith(mockClient, {
|
|
89
83
|
walletStatus: mockWalletStatus,
|
|
90
84
|
frakContext: mockContext,
|
|
91
|
-
modalConfig: mockModalConfig,
|
|
92
|
-
productId: mockProductId,
|
|
93
85
|
options: mockOptions,
|
|
94
86
|
});
|
|
95
87
|
});
|
|
@@ -115,7 +107,9 @@ describe("referralInteraction", () => {
|
|
|
115
107
|
|
|
116
108
|
vi.mocked(FrakContextManager.parse).mockReturnValue({} as any);
|
|
117
109
|
vi.mocked(watchWalletStatus).mockResolvedValue(null as any);
|
|
118
|
-
vi.mocked(processReferral).
|
|
110
|
+
vi.mocked(processReferral).mockImplementation(() => {
|
|
111
|
+
throw new Error("Test error");
|
|
112
|
+
});
|
|
119
113
|
|
|
120
114
|
const consoleSpy = vi
|
|
121
115
|
.spyOn(console, "warn")
|
|
@@ -144,8 +138,6 @@ describe("referralInteraction", () => {
|
|
|
144
138
|
expect(processReferral).toHaveBeenCalledWith(
|
|
145
139
|
mockClient,
|
|
146
140
|
expect.objectContaining({
|
|
147
|
-
modalConfig: undefined,
|
|
148
|
-
productId: undefined,
|
|
149
141
|
options: undefined,
|
|
150
142
|
})
|
|
151
143
|
);
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type { DisplayEmbeddedWalletParamsType, FrakClient } from "../../types";
|
|
1
|
+
import type { FrakClient } from "../../types";
|
|
3
2
|
import { FrakContextManager } from "../../utils";
|
|
4
3
|
import { watchWalletStatus } from "../index";
|
|
5
4
|
import {
|
|
@@ -8,11 +7,9 @@ import {
|
|
|
8
7
|
} from "./processReferral";
|
|
9
8
|
|
|
10
9
|
/**
|
|
11
|
-
* Function used to
|
|
10
|
+
* Function used to handle referral interactions
|
|
12
11
|
* @param client - The current Frak Client
|
|
13
12
|
* @param args
|
|
14
|
-
* @param args.productId - The product id to interact with (if not specified will be recomputed from the current domain)
|
|
15
|
-
* @param args.modalConfig - The modal configuration to display if the user is not logged in
|
|
16
13
|
* @param args.options - Some options for the referral interaction
|
|
17
14
|
*
|
|
18
15
|
* @returns A promise with the resulting referral state, or undefined in case of an error
|
|
@@ -20,17 +17,12 @@ import {
|
|
|
20
17
|
* @description This function will automatically handle the referral interaction process
|
|
21
18
|
*
|
|
22
19
|
* @see {@link processReferral} for more details on the automatic referral handling process
|
|
23
|
-
* @see {@link @frak-labs/core-sdk!ModalStepTypes} for more details on each modal steps types
|
|
24
20
|
*/
|
|
25
21
|
export async function referralInteraction(
|
|
26
22
|
client: FrakClient,
|
|
27
23
|
{
|
|
28
|
-
productId,
|
|
29
|
-
modalConfig,
|
|
30
24
|
options,
|
|
31
25
|
}: {
|
|
32
|
-
productId?: Hex;
|
|
33
|
-
modalConfig?: DisplayEmbeddedWalletParamsType;
|
|
34
26
|
options?: ProcessReferralOptions;
|
|
35
27
|
} = {}
|
|
36
28
|
) {
|
|
@@ -43,11 +35,9 @@ export async function referralInteraction(
|
|
|
43
35
|
const currentWalletStatus = await watchWalletStatus(client);
|
|
44
36
|
|
|
45
37
|
try {
|
|
46
|
-
return
|
|
38
|
+
return processReferral(client, {
|
|
47
39
|
walletStatus: currentWalletStatus,
|
|
48
40
|
frakContext,
|
|
49
|
-
modalConfig,
|
|
50
|
-
productId,
|
|
51
41
|
options,
|
|
52
42
|
});
|
|
53
43
|
} catch (error) {
|
|
@@ -1,32 +1,56 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
SendInteractionReturnType,
|
|
5
|
-
} from "../types";
|
|
6
|
-
import { computeProductId } from "../utils/computeProductId";
|
|
1
|
+
import type { FrakClient } from "../types";
|
|
2
|
+
import type { SendInteractionParamsType } from "../types/rpc/interaction";
|
|
3
|
+
import { getClientId } from "../utils/clientId";
|
|
7
4
|
|
|
8
5
|
/**
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
6
|
+
* Send an interaction to the backend via the listener RPC.
|
|
7
|
+
* Fire-and-forget: errors are caught and logged, not thrown.
|
|
8
|
+
*
|
|
9
|
+
* @param client - The Frak client instance
|
|
10
|
+
* @param params - The interaction parameters
|
|
11
|
+
*
|
|
12
|
+
* @description Sends a user interaction event through the wallet iframe RPC. Supports three interaction types: arrival tracking, sharing events, and custom interactions.
|
|
12
13
|
*
|
|
13
14
|
* @example
|
|
14
|
-
*
|
|
15
|
-
*
|
|
15
|
+
* Track a user arrival with referral attribution:
|
|
16
|
+
* ```ts
|
|
17
|
+
* await sendInteraction(client, {
|
|
18
|
+
* type: "arrival",
|
|
19
|
+
* referrerWallet: "0x1234...abcd",
|
|
20
|
+
* landingUrl: window.location.href,
|
|
21
|
+
* utmSource: "twitter",
|
|
22
|
+
* utmMedium: "social",
|
|
23
|
+
* utmCampaign: "launch-2026",
|
|
16
24
|
* });
|
|
17
|
-
*
|
|
18
|
-
*
|
|
25
|
+
* ```
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* Track a sharing event:
|
|
29
|
+
* ```ts
|
|
30
|
+
* await sendInteraction(client, { type: "sharing" });
|
|
31
|
+
* ```
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* Send a custom interaction:
|
|
35
|
+
* ```ts
|
|
36
|
+
* await sendInteraction(client, {
|
|
37
|
+
* type: "custom",
|
|
38
|
+
* customType: "newsletter_signup",
|
|
39
|
+
* data: { email: "user@example.com" },
|
|
19
40
|
* });
|
|
20
|
-
*
|
|
41
|
+
* ```
|
|
21
42
|
*/
|
|
22
43
|
export async function sendInteraction(
|
|
23
44
|
client: FrakClient,
|
|
24
|
-
|
|
25
|
-
): Promise<
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
}
|
|
45
|
+
params: SendInteractionParamsType
|
|
46
|
+
): Promise<void> {
|
|
47
|
+
try {
|
|
48
|
+
await client.request({
|
|
49
|
+
method: "frak_sendInteraction",
|
|
50
|
+
params: [params, { clientId: getClientId() }],
|
|
51
|
+
});
|
|
52
|
+
} catch {
|
|
53
|
+
// Silent failure - fire-and-forget
|
|
54
|
+
console.warn("[Frak SDK] Failed to send interaction:", params.type);
|
|
55
|
+
}
|
|
32
56
|
}
|