insert-affiliate-react-native-sdk 1.6.5 → 1.7.0

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.
@@ -10,7 +10,9 @@ type T_DEEPLINK_IAP_CONTEXT = {
10
10
  referrerLink: string;
11
11
  userId: string;
12
12
  OfferCode: string | null;
13
- returnInsertAffiliateIdentifier: () => Promise<string | null>;
13
+ returnInsertAffiliateIdentifier: (ignoreTimeout?: boolean) => Promise<string | null>;
14
+ isAffiliateAttributionValid: () => Promise<boolean>;
15
+ getAffiliateStoredDate: () => Promise<Date | null>;
14
16
  validatePurchaseWithIapticAPI: (jsonIapPurchase: CustomPurchase, iapticAppId: string, iapticAppName: string, iapticPublicKey: string) => Promise<boolean>;
15
17
  returnUserAccountTokenAndStoreExpectedTransaction: () => Promise<string | null>;
16
18
  storeExpectedStoreTransaction: (purchaseToken: string) => Promise<void>;
@@ -19,7 +21,7 @@ type T_DEEPLINK_IAP_CONTEXT = {
19
21
  setInsertAffiliateIdentifier: (referringLink: string) => Promise<void | string>;
20
22
  setInsertAffiliateIdentifierChangeCallback: (callback: InsertAffiliateIdentifierChangeCallback | null) => void;
21
23
  handleInsertLinks: (url: string) => Promise<boolean>;
22
- initialize: (code: string | null, verboseLogging?: boolean, insertLinksEnabled?: boolean, insertLinksClipboardEnabled?: boolean) => Promise<void>;
24
+ initialize: (code: string | null, verboseLogging?: boolean, insertLinksEnabled?: boolean, insertLinksClipboardEnabled?: boolean, affiliateAttributionActiveTime?: number) => Promise<void>;
23
25
  isInitialized: boolean;
24
26
  };
25
27
  export declare const DeepLinkIapContext: React.Context<T_DEEPLINK_IAP_CONTEXT>;
@@ -51,13 +51,16 @@ const ASYNC_KEYS = {
51
51
  COMPANY_CODE: '@app_company_code',
52
52
  USER_ACCOUNT_TOKEN: '@app_user_account_token',
53
53
  IOS_OFFER_CODE: '@app_ios_offer_code',
54
+ AFFILIATE_STORED_DATE: '@app_affiliate_stored_date',
54
55
  };
55
56
  // STARTING CONTEXT IMPLEMENTATION
56
57
  exports.DeepLinkIapContext = (0, react_1.createContext)({
57
58
  referrerLink: '',
58
59
  userId: '',
59
60
  OfferCode: null,
60
- returnInsertAffiliateIdentifier: () => __awaiter(void 0, void 0, void 0, function* () { return ''; }),
61
+ returnInsertAffiliateIdentifier: (ignoreTimeout) => __awaiter(void 0, void 0, void 0, function* () { return ''; }),
62
+ isAffiliateAttributionValid: () => __awaiter(void 0, void 0, void 0, function* () { return false; }),
63
+ getAffiliateStoredDate: () => __awaiter(void 0, void 0, void 0, function* () { return null; }),
61
64
  validatePurchaseWithIapticAPI: (jsonIapPurchase, iapticAppId, iapticAppName, iapticPublicKey) => __awaiter(void 0, void 0, void 0, function* () { return false; }),
62
65
  returnUserAccountTokenAndStoreExpectedTransaction: () => __awaiter(void 0, void 0, void 0, function* () { return ''; }),
63
66
  storeExpectedStoreTransaction: (purchaseToken) => __awaiter(void 0, void 0, void 0, function* () { }),
@@ -66,7 +69,7 @@ exports.DeepLinkIapContext = (0, react_1.createContext)({
66
69
  setInsertAffiliateIdentifier: (referringLink) => __awaiter(void 0, void 0, void 0, function* () { }),
67
70
  setInsertAffiliateIdentifierChangeCallback: (callback) => { },
68
71
  handleInsertLinks: (url) => __awaiter(void 0, void 0, void 0, function* () { return false; }),
69
- initialize: (code, verboseLogging, insertLinksEnabled, insertLinksClipboardEnabled) => __awaiter(void 0, void 0, void 0, function* () { }),
72
+ initialize: (code, verboseLogging, insertLinksEnabled, insertLinksClipboardEnabled, affiliateAttributionActiveTime) => __awaiter(void 0, void 0, void 0, function* () { }),
70
73
  isInitialized: false,
71
74
  });
72
75
  const DeepLinkIapProvider = ({ children, }) => {
@@ -78,12 +81,16 @@ const DeepLinkIapProvider = ({ children, }) => {
78
81
  const [insertLinksEnabled, setInsertLinksEnabled] = (0, react_1.useState)(false);
79
82
  const [insertLinksClipboardEnabled, setInsertLinksClipboardEnabled] = (0, react_1.useState)(false);
80
83
  const [OfferCode, setOfferCode] = (0, react_1.useState)(null);
84
+ const [affiliateAttributionActiveTime, setAffiliateAttributionActiveTime] = (0, react_1.useState)(null);
81
85
  const insertAffiliateIdentifierChangeCallbackRef = (0, react_1.useRef)(null);
82
86
  // MARK: Initialize the SDK
83
- const initialize = (companyCode_1, ...args_1) => __awaiter(void 0, [companyCode_1, ...args_1], void 0, function* (companyCode, verboseLogging = false, insertLinksEnabled = false, insertLinksClipboardEnabled = false) {
87
+ const initialize = (companyCode_1, ...args_1) => __awaiter(void 0, [companyCode_1, ...args_1], void 0, function* (companyCode, verboseLogging = false, insertLinksEnabled = false, insertLinksClipboardEnabled = false, affiliateAttributionActiveTime) {
84
88
  setVerboseLogging(verboseLogging);
85
89
  setInsertLinksEnabled(insertLinksEnabled);
86
90
  setInsertLinksClipboardEnabled(insertLinksClipboardEnabled);
91
+ if (affiliateAttributionActiveTime !== undefined) {
92
+ setAffiliateAttributionActiveTime(affiliateAttributionActiveTime);
93
+ }
87
94
  if (verboseLogging) {
88
95
  console.log('[Insert Affiliate] [VERBOSE] Starting SDK initialization...');
89
96
  console.log('[Insert Affiliate] [VERBOSE] Company code provided:', companyCode ? 'Yes' : 'No');
@@ -1057,10 +1064,18 @@ const DeepLinkIapProvider = ({ children, }) => {
1057
1064
  ;
1058
1065
  });
1059
1066
  // MARK: Return Insert Affiliate Identifier
1060
- // Instead of just reading React state
1061
- const returnInsertAffiliateIdentifier = () => __awaiter(void 0, void 0, void 0, function* () {
1067
+ const returnInsertAffiliateIdentifier = (...args_1) => __awaiter(void 0, [...args_1], void 0, function* (ignoreTimeout = false) {
1062
1068
  try {
1063
- verboseLog('Getting insert affiliate identifier...');
1069
+ verboseLog(`Getting insert affiliate identifier (ignoreTimeout: ${ignoreTimeout})...`);
1070
+ // If timeout is enabled and we're not ignoring it, check validity first
1071
+ if (!ignoreTimeout && affiliateAttributionActiveTime) {
1072
+ const isValid = yield isAffiliateAttributionValid();
1073
+ if (!isValid) {
1074
+ verboseLog('Attribution has expired, returning null');
1075
+ return null;
1076
+ }
1077
+ }
1078
+ // Now get the actual identifier
1064
1079
  verboseLog(`React state - referrerLink: ${referrerLink || 'empty'}, userId: ${userId || 'empty'}`);
1065
1080
  // Try React state first
1066
1081
  if (referrerLink && userId) {
@@ -1092,6 +1107,48 @@ const DeepLinkIapProvider = ({ children, }) => {
1092
1107
  return null;
1093
1108
  }
1094
1109
  });
1110
+ // MARK: Attribution Timeout Functions
1111
+ // Check if the current affiliate attribution is still valid based on timeout
1112
+ const isAffiliateAttributionValid = () => __awaiter(void 0, void 0, void 0, function* () {
1113
+ try {
1114
+ // If no timeout is set, attribution is always valid
1115
+ if (!affiliateAttributionActiveTime) {
1116
+ verboseLog('No attribution timeout set, attribution is valid');
1117
+ return true;
1118
+ }
1119
+ const storedDate = yield getAffiliateStoredDate();
1120
+ if (!storedDate) {
1121
+ verboseLog('No stored date found, attribution is invalid');
1122
+ return false;
1123
+ }
1124
+ const now = new Date();
1125
+ const timeDifferenceSeconds = Math.floor((now.getTime() - storedDate.getTime()) / 1000);
1126
+ const isValid = timeDifferenceSeconds <= affiliateAttributionActiveTime;
1127
+ verboseLog(`Attribution timeout check: stored=${storedDate.toISOString()}, now=${now.toISOString()}, diff=${timeDifferenceSeconds}s, timeout=${affiliateAttributionActiveTime}s, valid=${isValid}`);
1128
+ return isValid;
1129
+ }
1130
+ catch (error) {
1131
+ verboseLog(`Error checking attribution validity: ${error}`);
1132
+ return false;
1133
+ }
1134
+ });
1135
+ // Get the date when the affiliate identifier was stored
1136
+ const getAffiliateStoredDate = () => __awaiter(void 0, void 0, void 0, function* () {
1137
+ try {
1138
+ const storedDateString = yield getValueFromAsync(ASYNC_KEYS.AFFILIATE_STORED_DATE);
1139
+ if (!storedDateString) {
1140
+ verboseLog('No affiliate stored date found');
1141
+ return null;
1142
+ }
1143
+ const storedDate = new Date(storedDateString);
1144
+ verboseLog(`Retrieved affiliate stored date: ${storedDate.toISOString()}`);
1145
+ return storedDate;
1146
+ }
1147
+ catch (error) {
1148
+ verboseLog(`Error getting affiliate stored date: ${error}`);
1149
+ return null;
1150
+ }
1151
+ });
1095
1152
  // MARK: Insert Affiliate Identifier
1096
1153
  function setInsertAffiliateIdentifier(referringLink) {
1097
1154
  return __awaiter(this, void 0, void 0, function* () {
@@ -1174,10 +1231,20 @@ const DeepLinkIapProvider = ({ children, }) => {
1174
1231
  function storeInsertAffiliateIdentifier(_a) {
1175
1232
  return __awaiter(this, arguments, void 0, function* ({ link }) {
1176
1233
  console.log(`[Insert Affiliate] Storing affiliate identifier: ${link}`);
1234
+ // Check if we're trying to store the same link (prevent duplicate storage)
1235
+ const existingLink = yield getValueFromAsync(ASYNC_KEYS.REFERRER_LINK);
1236
+ if (existingLink === link) {
1237
+ verboseLog(`Link ${link} is already stored, skipping duplicate storage`);
1238
+ return;
1239
+ }
1177
1240
  verboseLog(`Updating React state with referrer link: ${link}`);
1178
1241
  setReferrerLink(link);
1179
1242
  verboseLog(`Saving referrer link to AsyncStorage...`);
1180
1243
  yield saveValueInAsync(ASYNC_KEYS.REFERRER_LINK, link);
1244
+ // Store the current date/time when the affiliate identifier is stored
1245
+ const currentDate = new Date().toISOString();
1246
+ verboseLog(`Saving affiliate stored date: ${currentDate}`);
1247
+ yield saveValueInAsync(ASYNC_KEYS.AFFILIATE_STORED_DATE, currentDate);
1181
1248
  verboseLog(`Referrer link saved to AsyncStorage successfully`);
1182
1249
  // Automatically fetch and store offer code for any affiliate identifier
1183
1250
  verboseLog('Attempting to fetch offer code for stored affiliate identifier...');
@@ -1426,6 +1493,8 @@ const DeepLinkIapProvider = ({ children, }) => {
1426
1493
  OfferCode,
1427
1494
  setShortCode,
1428
1495
  returnInsertAffiliateIdentifier,
1496
+ isAffiliateAttributionValid,
1497
+ getAffiliateStoredDate,
1429
1498
  storeExpectedStoreTransaction,
1430
1499
  returnUserAccountTokenAndStoreExpectedTransaction,
1431
1500
  validatePurchaseWithIapticAPI,
@@ -6,13 +6,15 @@ declare const useDeepLinkIapProvider: () => {
6
6
  }, iapticAppId: string, iapticAppName: string, iapticPublicKey: string) => Promise<boolean>;
7
7
  storeExpectedStoreTransaction: (purchaseToken: string) => Promise<void>;
8
8
  returnUserAccountTokenAndStoreExpectedTransaction: () => Promise<string | null>;
9
- returnInsertAffiliateIdentifier: () => Promise<string | null>;
9
+ returnInsertAffiliateIdentifier: (ignoreTimeout?: boolean) => Promise<string | null>;
10
+ isAffiliateAttributionValid: () => Promise<boolean>;
11
+ getAffiliateStoredDate: () => Promise<Date | null>;
10
12
  trackEvent: (eventName: string) => Promise<void>;
11
13
  setShortCode: (shortCode: string) => Promise<void>;
12
14
  setInsertAffiliateIdentifier: (referringLink: string) => Promise<void | string>;
13
15
  setInsertAffiliateIdentifierChangeCallback: (callback: import("./DeepLinkIapProvider").InsertAffiliateIdentifierChangeCallback | null) => void;
14
16
  handleInsertLinks: (url: string) => Promise<boolean>;
15
- initialize: (code: string | null, verboseLogging?: boolean, insertLinksEnabled?: boolean, insertLinksClipboardEnabled?: boolean) => Promise<void>;
17
+ initialize: (code: string | null, verboseLogging?: boolean, insertLinksEnabled?: boolean, insertLinksClipboardEnabled?: boolean, affiliateAttributionActiveTime?: number) => Promise<void>;
16
18
  isInitialized: boolean;
17
19
  OfferCode: string | null;
18
20
  };
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const react_1 = require("react");
4
4
  const DeepLinkIapProvider_1 = require("./DeepLinkIapProvider");
5
5
  const useDeepLinkIapProvider = () => {
6
- const { referrerLink, userId, validatePurchaseWithIapticAPI, storeExpectedStoreTransaction, returnUserAccountTokenAndStoreExpectedTransaction, returnInsertAffiliateIdentifier, trackEvent, setShortCode, setInsertAffiliateIdentifier, setInsertAffiliateIdentifierChangeCallback, handleInsertLinks, initialize, isInitialized, OfferCode, } = (0, react_1.useContext)(DeepLinkIapProvider_1.DeepLinkIapContext);
6
+ const { referrerLink, userId, validatePurchaseWithIapticAPI, storeExpectedStoreTransaction, returnUserAccountTokenAndStoreExpectedTransaction, returnInsertAffiliateIdentifier, isAffiliateAttributionValid, getAffiliateStoredDate, trackEvent, setShortCode, setInsertAffiliateIdentifier, setInsertAffiliateIdentifierChangeCallback, handleInsertLinks, initialize, isInitialized, OfferCode, } = (0, react_1.useContext)(DeepLinkIapProvider_1.DeepLinkIapContext);
7
7
  return {
8
8
  referrerLink,
9
9
  userId,
@@ -11,6 +11,8 @@ const useDeepLinkIapProvider = () => {
11
11
  storeExpectedStoreTransaction,
12
12
  returnUserAccountTokenAndStoreExpectedTransaction,
13
13
  returnInsertAffiliateIdentifier,
14
+ isAffiliateAttributionValid,
15
+ getAffiliateStoredDate,
14
16
  trackEvent,
15
17
  setShortCode,
16
18
  setInsertAffiliateIdentifier,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "insert-affiliate-react-native-sdk",
3
- "version": "1.6.5",
3
+ "version": "1.7.0",
4
4
  "description": "A package for connecting with the Insert Affiliate Platform to add app based affiliate marketing.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
package/readme.md CHANGED
@@ -172,7 +172,8 @@ const Child = () => {
172
172
  "{{ your-company-code }}",
173
173
  false, // Enable for debugging
174
174
  true, // Enables Insert Links
175
- true // Enable Insert Links Clipboard access to avoid permission prompt
175
+ true, // Enable Insert Links Clipboard access to avoid permission prompt
176
+ 604800 // Optional: Attribution timeout in seconds (7 days)
176
177
  );
177
178
  }
178
179
  }, [initialize, isInitialized]);
@@ -1227,3 +1228,84 @@ Short codes must meet the following criteria:
1227
1228
  onPress={() => setShortCode('JOIN_123')}
1228
1229
  />
1229
1230
  ```
1231
+
1232
+ ### Attribution Timeout
1233
+
1234
+ You can configure how long an affiliate link attribution remains active after being clicked. This allows you to control the attribution window for commissions.
1235
+
1236
+ #### Basic Usage
1237
+
1238
+ When initializing the SDK, you can specify the attribution timeout in seconds:
1239
+
1240
+ ```javascript
1241
+ const Child = () => {
1242
+ const { initialize, isInitialized } = useDeepLinkIapProvider();
1243
+
1244
+ useEffect(() => {
1245
+ if (!isInitialized) {
1246
+ // Set attribution timeout to 7 days (7 * 24 * 60 * 60 = 604800 seconds)
1247
+ initialize(
1248
+ "{{ your-company-code }}",
1249
+ false, // verbose logging
1250
+ false, // insert links enabled
1251
+ false, // insert links clipboard enabled
1252
+ 604800 // attribution timeout in seconds
1253
+ );
1254
+ }
1255
+ }, [initialize, isInitialized]);
1256
+ }
1257
+ ```
1258
+
1259
+ **When to use `affiliateAttributionActiveTime`:**
1260
+ - Set to a number in seconds to define how long affiliate attributions remain active
1261
+ - Set to `null` or omit to disable attribution timeout (attribution never expires)
1262
+ - Common values: 86400 (1 day), 604800 (7 days), 2592000 (30 days)
1263
+
1264
+ #### Common Timeout Values
1265
+
1266
+ ```javascript
1267
+ // 1 day
1268
+ initialize("your-company-code", false, false, false, 86400);
1269
+
1270
+ // 7 days (default for many platforms)
1271
+ initialize("your-company-code", false, false, false, 604800);
1272
+
1273
+ // 30 days
1274
+ initialize("your-company-code", false, false, false, 2592000);
1275
+
1276
+ // No timeout (attribution never expires)
1277
+ initialize("your-company-code", false, false, false); // or pass null/undefined
1278
+ ```
1279
+
1280
+ #### Advanced Usage
1281
+
1282
+ The SDK provides methods to work with attribution timeouts:
1283
+
1284
+ ```javascript
1285
+ const {
1286
+ returnInsertAffiliateIdentifier,
1287
+ isAffiliateAttributionValid,
1288
+ getAffiliateStoredDate
1289
+ } = useDeepLinkIapProvider();
1290
+
1291
+ // Get affiliate identifier (respects timeout)
1292
+ const identifier = await returnInsertAffiliateIdentifier();
1293
+
1294
+ // Get affiliate identifier ignoring timeout
1295
+ const rawIdentifier = await returnInsertAffiliateIdentifier(true);
1296
+
1297
+ // Check if attribution is still valid
1298
+ const isValid = await isAffiliateAttributionValid();
1299
+
1300
+ // Get the date when affiliate was first stored
1301
+ const storedDate = await getAffiliateStoredDate();
1302
+ ```
1303
+
1304
+ #### How It Works
1305
+
1306
+ 1. **Attribution Storage**: When an affiliate link is clicked and processed, the SDK stores both the affiliate identifier and the current timestamp
1307
+ 2. **Timeout Check**: When `returnInsertAffiliateIdentifier()` is called, the SDK checks if the stored attribution is still within the timeout window
1308
+ 3. **Expired Attribution**: If the attribution has expired, the method returns `null` instead of the affiliate identifier
1309
+ 4. **Bypass Option**: You can bypass the timeout check by passing `true` to `returnInsertAffiliateIdentifier(true)`
1310
+
1311
+ This ensures that affiliates are only credited for purchases made within the specified attribution window, providing fair and accurate commission tracking.
@@ -22,7 +22,9 @@ type T_DEEPLINK_IAP_CONTEXT = {
22
22
  referrerLink: string;
23
23
  userId: string;
24
24
  OfferCode: string | null;
25
- returnInsertAffiliateIdentifier: () => Promise<string | null>;
25
+ returnInsertAffiliateIdentifier: (ignoreTimeout?: boolean) => Promise<string | null>;
26
+ isAffiliateAttributionValid: () => Promise<boolean>;
27
+ getAffiliateStoredDate: () => Promise<Date | null>;
26
28
  validatePurchaseWithIapticAPI: (
27
29
  jsonIapPurchase: CustomPurchase,
28
30
  iapticAppId: string,
@@ -40,7 +42,7 @@ type T_DEEPLINK_IAP_CONTEXT = {
40
42
  ) => Promise<void | string>;
41
43
  setInsertAffiliateIdentifierChangeCallback: (callback: InsertAffiliateIdentifierChangeCallback | null) => void;
42
44
  handleInsertLinks: (url: string) => Promise<boolean>;
43
- initialize: (code: string | null, verboseLogging?: boolean, insertLinksEnabled?: boolean, insertLinksClipboardEnabled?: boolean) => Promise<void>;
45
+ initialize: (code: string | null, verboseLogging?: boolean, insertLinksEnabled?: boolean, insertLinksClipboardEnabled?: boolean, affiliateAttributionActiveTime?: number) => Promise<void>;
44
46
  isInitialized: boolean;
45
47
  };
46
48
 
@@ -67,6 +69,7 @@ const ASYNC_KEYS = {
67
69
  COMPANY_CODE: '@app_company_code',
68
70
  USER_ACCOUNT_TOKEN: '@app_user_account_token',
69
71
  IOS_OFFER_CODE: '@app_ios_offer_code',
72
+ AFFILIATE_STORED_DATE: '@app_affiliate_stored_date',
70
73
  };
71
74
 
72
75
  // STARTING CONTEXT IMPLEMENTATION
@@ -74,7 +77,9 @@ export const DeepLinkIapContext = createContext<T_DEEPLINK_IAP_CONTEXT>({
74
77
  referrerLink: '',
75
78
  userId: '',
76
79
  OfferCode: null,
77
- returnInsertAffiliateIdentifier: async () => '',
80
+ returnInsertAffiliateIdentifier: async (ignoreTimeout?: boolean) => '',
81
+ isAffiliateAttributionValid: async () => false,
82
+ getAffiliateStoredDate: async () => null,
78
83
  validatePurchaseWithIapticAPI: async (
79
84
  jsonIapPurchase: CustomPurchase,
80
85
  iapticAppId: string,
@@ -88,7 +93,7 @@ export const DeepLinkIapContext = createContext<T_DEEPLINK_IAP_CONTEXT>({
88
93
  setInsertAffiliateIdentifier: async (referringLink: string) => {},
89
94
  setInsertAffiliateIdentifierChangeCallback: (callback: InsertAffiliateIdentifierChangeCallback | null) => {},
90
95
  handleInsertLinks: async (url: string) => false,
91
- initialize: async (code: string | null, verboseLogging?: boolean, insertLinksEnabled?: boolean, insertLinksClipboardEnabled?: boolean) => {},
96
+ initialize: async (code: string | null, verboseLogging?: boolean, insertLinksEnabled?: boolean, insertLinksClipboardEnabled?: boolean, affiliateAttributionActiveTime?: number) => {},
92
97
  isInitialized: false,
93
98
  });
94
99
 
@@ -103,13 +108,17 @@ const DeepLinkIapProvider: React.FC<T_DEEPLINK_IAP_PROVIDER> = ({
103
108
  const [insertLinksEnabled, setInsertLinksEnabled] = useState<boolean>(false);
104
109
  const [insertLinksClipboardEnabled, setInsertLinksClipboardEnabled] = useState<boolean>(false);
105
110
  const [OfferCode, setOfferCode] = useState<string | null>(null);
111
+ const [affiliateAttributionActiveTime, setAffiliateAttributionActiveTime] = useState<number | null>(null);
106
112
  const insertAffiliateIdentifierChangeCallbackRef = useRef<InsertAffiliateIdentifierChangeCallback | null>(null);
107
113
 
108
114
  // MARK: Initialize the SDK
109
- const initialize = async (companyCode: string | null, verboseLogging: boolean = false, insertLinksEnabled: boolean = false, insertLinksClipboardEnabled: boolean = false): Promise<void> => {
115
+ const initialize = async (companyCode: string | null, verboseLogging: boolean = false, insertLinksEnabled: boolean = false, insertLinksClipboardEnabled: boolean = false, affiliateAttributionActiveTime?: number): Promise<void> => {
110
116
  setVerboseLogging(verboseLogging);
111
117
  setInsertLinksEnabled(insertLinksEnabled);
112
118
  setInsertLinksClipboardEnabled(insertLinksClipboardEnabled);
119
+ if (affiliateAttributionActiveTime !== undefined) {
120
+ setAffiliateAttributionActiveTime(affiliateAttributionActiveTime);
121
+ }
113
122
 
114
123
  if (verboseLogging) {
115
124
  console.log('[Insert Affiliate] [VERBOSE] Starting SDK initialization...');
@@ -1210,10 +1219,20 @@ const DeepLinkIapProvider: React.FC<T_DEEPLINK_IAP_PROVIDER> = ({
1210
1219
  };
1211
1220
 
1212
1221
  // MARK: Return Insert Affiliate Identifier
1213
- // Instead of just reading React state
1214
- const returnInsertAffiliateIdentifier = async (): Promise<string | null> => {
1222
+ const returnInsertAffiliateIdentifier = async (ignoreTimeout: boolean = false): Promise<string | null> => {
1215
1223
  try {
1216
- verboseLog('Getting insert affiliate identifier...');
1224
+ verboseLog(`Getting insert affiliate identifier (ignoreTimeout: ${ignoreTimeout})...`);
1225
+
1226
+ // If timeout is enabled and we're not ignoring it, check validity first
1227
+ if (!ignoreTimeout && affiliateAttributionActiveTime) {
1228
+ const isValid = await isAffiliateAttributionValid();
1229
+ if (!isValid) {
1230
+ verboseLog('Attribution has expired, returning null');
1231
+ return null;
1232
+ }
1233
+ }
1234
+
1235
+ // Now get the actual identifier
1217
1236
  verboseLog(`React state - referrerLink: ${referrerLink || 'empty'}, userId: ${userId || 'empty'}`);
1218
1237
 
1219
1238
  // Try React state first
@@ -1252,6 +1271,54 @@ const DeepLinkIapProvider: React.FC<T_DEEPLINK_IAP_PROVIDER> = ({
1252
1271
  }
1253
1272
  };
1254
1273
 
1274
+ // MARK: Attribution Timeout Functions
1275
+
1276
+ // Check if the current affiliate attribution is still valid based on timeout
1277
+ const isAffiliateAttributionValid = async (): Promise<boolean> => {
1278
+ try {
1279
+ // If no timeout is set, attribution is always valid
1280
+ if (!affiliateAttributionActiveTime) {
1281
+ verboseLog('No attribution timeout set, attribution is valid');
1282
+ return true;
1283
+ }
1284
+
1285
+ const storedDate = await getAffiliateStoredDate();
1286
+ if (!storedDate) {
1287
+ verboseLog('No stored date found, attribution is invalid');
1288
+ return false;
1289
+ }
1290
+
1291
+ const now = new Date();
1292
+ const timeDifferenceSeconds = Math.floor((now.getTime() - storedDate.getTime()) / 1000);
1293
+ const isValid = timeDifferenceSeconds <= affiliateAttributionActiveTime;
1294
+
1295
+ verboseLog(`Attribution timeout check: stored=${storedDate.toISOString()}, now=${now.toISOString()}, diff=${timeDifferenceSeconds}s, timeout=${affiliateAttributionActiveTime}s, valid=${isValid}`);
1296
+
1297
+ return isValid;
1298
+ } catch (error) {
1299
+ verboseLog(`Error checking attribution validity: ${error}`);
1300
+ return false;
1301
+ }
1302
+ };
1303
+
1304
+ // Get the date when the affiliate identifier was stored
1305
+ const getAffiliateStoredDate = async (): Promise<Date | null> => {
1306
+ try {
1307
+ const storedDateString = await getValueFromAsync(ASYNC_KEYS.AFFILIATE_STORED_DATE);
1308
+ if (!storedDateString) {
1309
+ verboseLog('No affiliate stored date found');
1310
+ return null;
1311
+ }
1312
+
1313
+ const storedDate = new Date(storedDateString);
1314
+ verboseLog(`Retrieved affiliate stored date: ${storedDate.toISOString()}`);
1315
+ return storedDate;
1316
+ } catch (error) {
1317
+ verboseLog(`Error getting affiliate stored date: ${error}`);
1318
+ return null;
1319
+ }
1320
+ };
1321
+
1255
1322
  // MARK: Insert Affiliate Identifier
1256
1323
 
1257
1324
  async function setInsertAffiliateIdentifier(
@@ -1349,10 +1416,24 @@ const DeepLinkIapProvider: React.FC<T_DEEPLINK_IAP_PROVIDER> = ({
1349
1416
 
1350
1417
  async function storeInsertAffiliateIdentifier({ link }: { link: string }) {
1351
1418
  console.log(`[Insert Affiliate] Storing affiliate identifier: ${link}`);
1419
+
1420
+ // Check if we're trying to store the same link (prevent duplicate storage)
1421
+ const existingLink = await getValueFromAsync(ASYNC_KEYS.REFERRER_LINK);
1422
+ if (existingLink === link) {
1423
+ verboseLog(`Link ${link} is already stored, skipping duplicate storage`);
1424
+ return;
1425
+ }
1426
+
1352
1427
  verboseLog(`Updating React state with referrer link: ${link}`);
1353
1428
  setReferrerLink(link);
1354
1429
  verboseLog(`Saving referrer link to AsyncStorage...`);
1355
1430
  await saveValueInAsync(ASYNC_KEYS.REFERRER_LINK, link);
1431
+
1432
+ // Store the current date/time when the affiliate identifier is stored
1433
+ const currentDate = new Date().toISOString();
1434
+ verboseLog(`Saving affiliate stored date: ${currentDate}`);
1435
+ await saveValueInAsync(ASYNC_KEYS.AFFILIATE_STORED_DATE, currentDate);
1436
+
1356
1437
  verboseLog(`Referrer link saved to AsyncStorage successfully`);
1357
1438
 
1358
1439
  // Automatically fetch and store offer code for any affiliate identifier
@@ -1658,6 +1739,8 @@ const DeepLinkIapProvider: React.FC<T_DEEPLINK_IAP_PROVIDER> = ({
1658
1739
  OfferCode,
1659
1740
  setShortCode,
1660
1741
  returnInsertAffiliateIdentifier,
1742
+ isAffiliateAttributionValid,
1743
+ getAffiliateStoredDate,
1661
1744
  storeExpectedStoreTransaction,
1662
1745
  returnUserAccountTokenAndStoreExpectedTransaction,
1663
1746
  validatePurchaseWithIapticAPI,
@@ -9,6 +9,8 @@ const useDeepLinkIapProvider = () => {
9
9
  storeExpectedStoreTransaction,
10
10
  returnUserAccountTokenAndStoreExpectedTransaction,
11
11
  returnInsertAffiliateIdentifier,
12
+ isAffiliateAttributionValid,
13
+ getAffiliateStoredDate,
12
14
  trackEvent,
13
15
  setShortCode,
14
16
  setInsertAffiliateIdentifier,
@@ -26,6 +28,8 @@ const useDeepLinkIapProvider = () => {
26
28
  storeExpectedStoreTransaction,
27
29
  returnUserAccountTokenAndStoreExpectedTransaction,
28
30
  returnInsertAffiliateIdentifier,
31
+ isAffiliateAttributionValid,
32
+ getAffiliateStoredDate,
29
33
  trackEvent,
30
34
  setShortCode,
31
35
  setInsertAffiliateIdentifier,