insert-affiliate-react-native-sdk 1.7.0 → 1.9.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.
- package/dist/DeepLinkIapProvider.d.ts +7 -1
- package/dist/DeepLinkIapProvider.js +130 -3
- package/dist/index.d.ts +1 -1
- package/dist/useDeepLinkIapProvider.d.ts +2 -1
- package/dist/useDeepLinkIapProvider.js +2 -1
- package/package.json +1 -1
- package/readme.md +100 -1
- package/src/DeepLinkIapProvider.tsx +152 -5
- package/src/index.ts +1 -1
- package/src/useDeepLinkIapProvider.tsx +2 -0
|
@@ -3,6 +3,11 @@ type T_DEEPLINK_IAP_PROVIDER = {
|
|
|
3
3
|
children: React.ReactNode;
|
|
4
4
|
};
|
|
5
5
|
export type InsertAffiliateIdentifierChangeCallback = (identifier: string | null) => void;
|
|
6
|
+
export type AffiliateDetails = {
|
|
7
|
+
affiliateName: string;
|
|
8
|
+
affiliateShortCode: string;
|
|
9
|
+
deeplinkurl: string;
|
|
10
|
+
} | null;
|
|
6
11
|
type CustomPurchase = {
|
|
7
12
|
[key: string]: any;
|
|
8
13
|
};
|
|
@@ -17,7 +22,8 @@ type T_DEEPLINK_IAP_CONTEXT = {
|
|
|
17
22
|
returnUserAccountTokenAndStoreExpectedTransaction: () => Promise<string | null>;
|
|
18
23
|
storeExpectedStoreTransaction: (purchaseToken: string) => Promise<void>;
|
|
19
24
|
trackEvent: (eventName: string) => Promise<void>;
|
|
20
|
-
setShortCode: (shortCode: string) => Promise<
|
|
25
|
+
setShortCode: (shortCode: string) => Promise<boolean>;
|
|
26
|
+
getAffiliateDetails: (affiliateCode: string) => Promise<AffiliateDetails>;
|
|
21
27
|
setInsertAffiliateIdentifier: (referringLink: string) => Promise<void | string>;
|
|
22
28
|
setInsertAffiliateIdentifierChangeCallback: (callback: InsertAffiliateIdentifierChangeCallback | null) => void;
|
|
23
29
|
handleInsertLinks: (url: string) => Promise<boolean>;
|
|
@@ -44,6 +44,8 @@ const clipboard_1 = __importDefault(require("@react-native-clipboard/clipboard")
|
|
|
44
44
|
const netinfo_1 = __importDefault(require("@react-native-community/netinfo"));
|
|
45
45
|
const react_native_device_info_1 = __importDefault(require("react-native-device-info"));
|
|
46
46
|
const react_native_play_install_referrer_1 = require("react-native-play-install-referrer");
|
|
47
|
+
// Development environment check for React Native
|
|
48
|
+
const isDevelopmentEnvironment = typeof __DEV__ !== 'undefined' && __DEV__;
|
|
47
49
|
const ASYNC_KEYS = {
|
|
48
50
|
REFERRER_LINK: '@app_referrer_link',
|
|
49
51
|
USER_PURCHASE: '@app_user_purchase',
|
|
@@ -65,7 +67,8 @@ exports.DeepLinkIapContext = (0, react_1.createContext)({
|
|
|
65
67
|
returnUserAccountTokenAndStoreExpectedTransaction: () => __awaiter(void 0, void 0, void 0, function* () { return ''; }),
|
|
66
68
|
storeExpectedStoreTransaction: (purchaseToken) => __awaiter(void 0, void 0, void 0, function* () { }),
|
|
67
69
|
trackEvent: (eventName) => __awaiter(void 0, void 0, void 0, function* () { }),
|
|
68
|
-
setShortCode: (shortCode) => __awaiter(void 0, void 0, void 0, function* () { }),
|
|
70
|
+
setShortCode: (shortCode) => __awaiter(void 0, void 0, void 0, function* () { return false; }),
|
|
71
|
+
getAffiliateDetails: (affiliateCode) => __awaiter(void 0, void 0, void 0, function* () { return null; }),
|
|
69
72
|
setInsertAffiliateIdentifier: (referringLink) => __awaiter(void 0, void 0, void 0, function* () { }),
|
|
70
73
|
setInsertAffiliateIdentifierChangeCallback: (callback) => { },
|
|
71
74
|
handleInsertLinks: (url) => __awaiter(void 0, void 0, void 0, function* () { return false; }),
|
|
@@ -1017,6 +1020,84 @@ const DeepLinkIapProvider = ({ children, }) => {
|
|
|
1017
1020
|
const isValidCharacters = /^[a-zA-Z0-9_]+$/.test(referringLink);
|
|
1018
1021
|
return isValidCharacters && referringLink.length >= 3 && referringLink.length <= 25;
|
|
1019
1022
|
};
|
|
1023
|
+
const checkAffiliateExists = (affiliateCode) => __awaiter(void 0, void 0, void 0, function* () {
|
|
1024
|
+
try {
|
|
1025
|
+
const activeCompanyCode = yield getActiveCompanyCode();
|
|
1026
|
+
if (!activeCompanyCode) {
|
|
1027
|
+
verboseLog('Cannot check affiliate: no company code available');
|
|
1028
|
+
return false;
|
|
1029
|
+
}
|
|
1030
|
+
const url = 'https://api.insertaffiliate.com/V1/checkAffiliateExists';
|
|
1031
|
+
const payload = {
|
|
1032
|
+
companyId: activeCompanyCode,
|
|
1033
|
+
affiliateCode: affiliateCode
|
|
1034
|
+
};
|
|
1035
|
+
verboseLog(`Checking if affiliate exists: ${affiliateCode}`);
|
|
1036
|
+
const response = yield axios_1.default.post(url, payload, {
|
|
1037
|
+
headers: {
|
|
1038
|
+
'Content-Type': 'application/json',
|
|
1039
|
+
},
|
|
1040
|
+
});
|
|
1041
|
+
verboseLog(`Affiliate check response: ${JSON.stringify(response.data)}`);
|
|
1042
|
+
if (response.status === 200 && response.data) {
|
|
1043
|
+
const exists = response.data.exists === true;
|
|
1044
|
+
if (exists) {
|
|
1045
|
+
verboseLog(`Affiliate ${affiliateCode} exists and is valid`);
|
|
1046
|
+
}
|
|
1047
|
+
else {
|
|
1048
|
+
verboseLog(`Affiliate ${affiliateCode} does not exist`);
|
|
1049
|
+
}
|
|
1050
|
+
return exists;
|
|
1051
|
+
}
|
|
1052
|
+
else {
|
|
1053
|
+
verboseLog(`Unexpected response checking affiliate: status ${response.status}`);
|
|
1054
|
+
return false;
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
1057
|
+
catch (error) {
|
|
1058
|
+
verboseLog(`Error checking affiliate exists: ${error}`);
|
|
1059
|
+
return false;
|
|
1060
|
+
}
|
|
1061
|
+
});
|
|
1062
|
+
const getAffiliateDetails = (affiliateCode) => __awaiter(void 0, void 0, void 0, function* () {
|
|
1063
|
+
try {
|
|
1064
|
+
const activeCompanyCode = yield getActiveCompanyCode();
|
|
1065
|
+
if (!activeCompanyCode) {
|
|
1066
|
+
verboseLog('Cannot get affiliate details: no company code available');
|
|
1067
|
+
return null;
|
|
1068
|
+
}
|
|
1069
|
+
const url = 'https://api.insertaffiliate.com/V1/checkAffiliateExists';
|
|
1070
|
+
const payload = {
|
|
1071
|
+
companyId: activeCompanyCode,
|
|
1072
|
+
affiliateCode: affiliateCode
|
|
1073
|
+
};
|
|
1074
|
+
verboseLog(`Getting affiliate details for: ${affiliateCode}`);
|
|
1075
|
+
const response = yield axios_1.default.post(url, payload, {
|
|
1076
|
+
headers: {
|
|
1077
|
+
'Content-Type': 'application/json',
|
|
1078
|
+
},
|
|
1079
|
+
});
|
|
1080
|
+
verboseLog(`Affiliate details response: ${JSON.stringify(response.data)}`);
|
|
1081
|
+
if (response.status === 200 && response.data && response.data.exists === true) {
|
|
1082
|
+
const affiliate = response.data.affiliate;
|
|
1083
|
+
if (affiliate) {
|
|
1084
|
+
verboseLog(`Retrieved affiliate details: ${JSON.stringify(affiliate)}`);
|
|
1085
|
+
return {
|
|
1086
|
+
affiliateName: affiliate.affiliateName || '',
|
|
1087
|
+
affiliateShortCode: affiliate.affiliateShortCode || '',
|
|
1088
|
+
deeplinkurl: affiliate.deeplinkurl || ''
|
|
1089
|
+
};
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
1092
|
+
verboseLog(`Affiliate ${affiliateCode} not found or invalid response`);
|
|
1093
|
+
return null;
|
|
1094
|
+
}
|
|
1095
|
+
catch (error) {
|
|
1096
|
+
verboseLog(`Error getting affiliate details: ${error}`);
|
|
1097
|
+
console.error('[Insert Affiliate] Error getting affiliate details:', error);
|
|
1098
|
+
return null;
|
|
1099
|
+
}
|
|
1100
|
+
});
|
|
1020
1101
|
function setShortCode(shortCode) {
|
|
1021
1102
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1022
1103
|
console.log('[Insert Affiliate] Setting short code.');
|
|
@@ -1024,8 +1105,18 @@ const DeepLinkIapProvider = ({ children, }) => {
|
|
|
1024
1105
|
// Validate it is a short code
|
|
1025
1106
|
const capitalisedShortCode = shortCode.toUpperCase();
|
|
1026
1107
|
isShortCode(capitalisedShortCode);
|
|
1027
|
-
//
|
|
1028
|
-
yield
|
|
1108
|
+
// Check if the affiliate exists before storing
|
|
1109
|
+
const exists = yield checkAffiliateExists(capitalisedShortCode);
|
|
1110
|
+
if (exists) {
|
|
1111
|
+
// If affiliate exists, set the Insert Affiliate Identifier
|
|
1112
|
+
yield storeInsertAffiliateIdentifier({ link: capitalisedShortCode });
|
|
1113
|
+
console.log(`[Insert Affiliate] Short code ${capitalisedShortCode} validated and stored successfully.`);
|
|
1114
|
+
return true;
|
|
1115
|
+
}
|
|
1116
|
+
else {
|
|
1117
|
+
console.warn(`[Insert Affiliate] Short code ${capitalisedShortCode} does not exist. Not storing.`);
|
|
1118
|
+
return false;
|
|
1119
|
+
}
|
|
1029
1120
|
});
|
|
1030
1121
|
}
|
|
1031
1122
|
function getOrCreateUserAccountToken() {
|
|
@@ -1058,6 +1149,15 @@ const DeepLinkIapProvider = ({ children, }) => {
|
|
|
1058
1149
|
}
|
|
1059
1150
|
}
|
|
1060
1151
|
catch (error) {
|
|
1152
|
+
// Handle E_IAP_NOT_AVAILABLE error gracefully
|
|
1153
|
+
if ((error === null || error === void 0 ? void 0 : error.code) === 'E_IAP_NOT_AVAILABLE' ||
|
|
1154
|
+
(error instanceof Error && error.message.includes('E_IAP_NOT_AVAILABLE'))) {
|
|
1155
|
+
if (isDevelopmentEnvironment) {
|
|
1156
|
+
console.warn('[Insert Affiliate] IAP not available in development environment. Cannot store expected transaction.');
|
|
1157
|
+
verboseLog('E_IAP_NOT_AVAILABLE error in returnUserAccountTokenAndStoreExpectedTransaction - gracefully handling in development');
|
|
1158
|
+
}
|
|
1159
|
+
return null; // Return null but don't crash
|
|
1160
|
+
}
|
|
1061
1161
|
console.error('[Insert Affiliate] Error in returnUserAccountTokenAndStoreExpectedTransaction:', error);
|
|
1062
1162
|
return null;
|
|
1063
1163
|
}
|
|
@@ -1258,7 +1358,21 @@ const DeepLinkIapProvider = ({ children, }) => {
|
|
|
1258
1358
|
});
|
|
1259
1359
|
}
|
|
1260
1360
|
const validatePurchaseWithIapticAPI = (jsonIapPurchase, iapticAppId, iapticAppName, iapticPublicKey) => __awaiter(void 0, void 0, void 0, function* () {
|
|
1361
|
+
var _a;
|
|
1261
1362
|
try {
|
|
1363
|
+
// Check for E_IAP_NOT_AVAILABLE error in development environment
|
|
1364
|
+
if (((_a = jsonIapPurchase === null || jsonIapPurchase === void 0 ? void 0 : jsonIapPurchase.error) === null || _a === void 0 ? void 0 : _a.code) === 'E_IAP_NOT_AVAILABLE' ||
|
|
1365
|
+
(jsonIapPurchase === null || jsonIapPurchase === void 0 ? void 0 : jsonIapPurchase.code) === 'E_IAP_NOT_AVAILABLE' ||
|
|
1366
|
+
(typeof jsonIapPurchase === 'string' && jsonIapPurchase.includes('E_IAP_NOT_AVAILABLE'))) {
|
|
1367
|
+
if (isDevelopmentEnvironment) {
|
|
1368
|
+
console.warn('[Insert Affiliate] IAP not available in development environment. This is expected behavior.');
|
|
1369
|
+
verboseLog('E_IAP_NOT_AVAILABLE error detected in development - gracefully handling');
|
|
1370
|
+
}
|
|
1371
|
+
else {
|
|
1372
|
+
console.error('[Insert Affiliate] IAP not available in production environment. Please check your IAP configuration.');
|
|
1373
|
+
}
|
|
1374
|
+
return false; // Return false but don't crash
|
|
1375
|
+
}
|
|
1262
1376
|
const baseRequestBody = {
|
|
1263
1377
|
id: iapticAppId,
|
|
1264
1378
|
type: 'application',
|
|
@@ -1308,6 +1422,18 @@ const DeepLinkIapProvider = ({ children, }) => {
|
|
|
1308
1422
|
}
|
|
1309
1423
|
}
|
|
1310
1424
|
catch (error) {
|
|
1425
|
+
// Handle E_IAP_NOT_AVAILABLE error gracefully
|
|
1426
|
+
if ((error === null || error === void 0 ? void 0 : error.code) === 'E_IAP_NOT_AVAILABLE' ||
|
|
1427
|
+
(error instanceof Error && error.message.includes('E_IAP_NOT_AVAILABLE'))) {
|
|
1428
|
+
if (isDevelopmentEnvironment) {
|
|
1429
|
+
console.warn('[Insert Affiliate] IAP not available in development environment. SDK will continue without purchase validation.');
|
|
1430
|
+
verboseLog('E_IAP_NOT_AVAILABLE error caught in validatePurchaseWithIapticAPI - gracefully handling in development');
|
|
1431
|
+
}
|
|
1432
|
+
else {
|
|
1433
|
+
console.error('[Insert Affiliate] IAP not available in production environment. Please check your IAP configuration.');
|
|
1434
|
+
}
|
|
1435
|
+
return false; // Return false but don't crash
|
|
1436
|
+
}
|
|
1311
1437
|
if (error instanceof Error) {
|
|
1312
1438
|
console.error(`validatePurchaseWithIapticAPI Error: ${error.message}`);
|
|
1313
1439
|
}
|
|
@@ -1492,6 +1618,7 @@ const DeepLinkIapProvider = ({ children, }) => {
|
|
|
1492
1618
|
userId,
|
|
1493
1619
|
OfferCode,
|
|
1494
1620
|
setShortCode,
|
|
1621
|
+
getAffiliateDetails,
|
|
1495
1622
|
returnInsertAffiliateIdentifier,
|
|
1496
1623
|
isAffiliateAttributionValid,
|
|
1497
1624
|
getAffiliateStoredDate,
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import DeepLinkIapProvider from "./DeepLinkIapProvider";
|
|
2
2
|
import useDeepLinkIapProvider from "./useDeepLinkIapProvider";
|
|
3
3
|
export { DeepLinkIapProvider, useDeepLinkIapProvider };
|
|
4
|
-
export type { InsertAffiliateIdentifierChangeCallback } from "./DeepLinkIapProvider";
|
|
4
|
+
export type { InsertAffiliateIdentifierChangeCallback, AffiliateDetails } from "./DeepLinkIapProvider";
|
|
@@ -10,7 +10,8 @@ declare const useDeepLinkIapProvider: () => {
|
|
|
10
10
|
isAffiliateAttributionValid: () => Promise<boolean>;
|
|
11
11
|
getAffiliateStoredDate: () => Promise<Date | null>;
|
|
12
12
|
trackEvent: (eventName: string) => Promise<void>;
|
|
13
|
-
setShortCode: (shortCode: string) => Promise<
|
|
13
|
+
setShortCode: (shortCode: string) => Promise<boolean>;
|
|
14
|
+
getAffiliateDetails: (affiliateCode: string) => Promise<import("./DeepLinkIapProvider").AffiliateDetails>;
|
|
14
15
|
setInsertAffiliateIdentifier: (referringLink: string) => Promise<void | string>;
|
|
15
16
|
setInsertAffiliateIdentifierChangeCallback: (callback: import("./DeepLinkIapProvider").InsertAffiliateIdentifierChangeCallback | null) => void;
|
|
16
17
|
handleInsertLinks: (url: string) => Promise<boolean>;
|
|
@@ -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, isAffiliateAttributionValid, getAffiliateStoredDate, 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, getAffiliateDetails, setInsertAffiliateIdentifier, setInsertAffiliateIdentifierChangeCallback, handleInsertLinks, initialize, isInitialized, OfferCode, } = (0, react_1.useContext)(DeepLinkIapProvider_1.DeepLinkIapContext);
|
|
7
7
|
return {
|
|
8
8
|
referrerLink,
|
|
9
9
|
userId,
|
|
@@ -15,6 +15,7 @@ const useDeepLinkIapProvider = () => {
|
|
|
15
15
|
getAffiliateStoredDate,
|
|
16
16
|
trackEvent,
|
|
17
17
|
setShortCode,
|
|
18
|
+
getAffiliateDetails,
|
|
18
19
|
setInsertAffiliateIdentifier,
|
|
19
20
|
setInsertAffiliateIdentifierChangeCallback,
|
|
20
21
|
handleInsertLinks,
|
package/package.json
CHANGED
package/readme.md
CHANGED
|
@@ -763,6 +763,7 @@ const DeepLinkHandler = () => {
|
|
|
763
763
|
// Listen for both deep link types
|
|
764
764
|
appsFlyer.onDeepLink(handleDeepLink);
|
|
765
765
|
appsFlyer.onAppOpenAttribution(handleDeepLink);
|
|
766
|
+
appsFlyer.onInstallConversionData(handleDeepLink);
|
|
766
767
|
|
|
767
768
|
initAppsFlyer();
|
|
768
769
|
}, [setInsertAffiliateIdentifier, isInitialized]);
|
|
@@ -828,6 +829,7 @@ const DeepLinkHandler = () => {
|
|
|
828
829
|
// Listen for both deep link types
|
|
829
830
|
appsFlyer.onDeepLink(handleDeepLink);
|
|
830
831
|
appsFlyer.onAppOpenAttribution(handleDeepLink);
|
|
832
|
+
appsFlyer.onInstallConversionData(handleDeepLink);
|
|
831
833
|
|
|
832
834
|
initAppsFlyer();
|
|
833
835
|
}, [setInsertAffiliateIdentifier, isInitialized]);
|
|
@@ -1214,6 +1216,12 @@ Short codes must meet the following criteria:
|
|
|
1214
1216
|
- Contain only **letters, numbers, and underscores** (alphanumeric characters and underscores).
|
|
1215
1217
|
- Replace {{ user_entered_short_code }} with the short code the user enters through your chosen input method, i.e. an input field / pop up element
|
|
1216
1218
|
|
|
1219
|
+
**Return Value**: `setShortCode` returns a `Promise<boolean>`:
|
|
1220
|
+
- Returns `true` if the short code exists and was successfully validated and stored
|
|
1221
|
+
- Returns `false` if the short code does not exist or validation failed
|
|
1222
|
+
|
|
1223
|
+
This allows you to provide immediate feedback to users about whether their entered code is valid.
|
|
1224
|
+
|
|
1217
1225
|
```javascript
|
|
1218
1226
|
import {
|
|
1219
1227
|
DeepLinkIapProvider,
|
|
@@ -1223,12 +1231,30 @@ Short codes must meet the following criteria:
|
|
|
1223
1231
|
setShortCode,
|
|
1224
1232
|
} = useDeepLinkIapProvider();
|
|
1225
1233
|
|
|
1234
|
+
// Basic usage (without validation feedback)
|
|
1226
1235
|
<Button
|
|
1227
1236
|
title={'Set Short Code'}
|
|
1228
1237
|
onPress={() => setShortCode('JOIN_123')}
|
|
1229
1238
|
/>
|
|
1239
|
+
|
|
1240
|
+
// Recommended usage with validation feedback
|
|
1241
|
+
<Button
|
|
1242
|
+
title={'Set Short Code'}
|
|
1243
|
+
onPress={async () => {
|
|
1244
|
+
const isValid = await setShortCode('JOIN_123');
|
|
1245
|
+
if (isValid) {
|
|
1246
|
+
// Show success message to user
|
|
1247
|
+
Alert.alert('Success', 'Affiliate code applied successfully!');
|
|
1248
|
+
} else {
|
|
1249
|
+
// Show error message to user
|
|
1250
|
+
Alert.alert('Error', 'Invalid affiliate code. Please check and try again.');
|
|
1251
|
+
}
|
|
1252
|
+
}}
|
|
1253
|
+
/>
|
|
1230
1254
|
```
|
|
1231
1255
|
|
|
1256
|
+
**Important**: The SDK will automatically validate the short code with the Insert Affiliate backend before storing it. Only valid short codes that exist in your company's affiliate list will be stored. Invalid codes will be rejected and not associated with the user.
|
|
1257
|
+
|
|
1232
1258
|
### Attribution Timeout
|
|
1233
1259
|
|
|
1234
1260
|
You can configure how long an affiliate link attribution remains active after being clicked. This allows you to control the attribution window for commissions.
|
|
@@ -1308,4 +1334,77 @@ const storedDate = await getAffiliateStoredDate();
|
|
|
1308
1334
|
3. **Expired Attribution**: If the attribution has expired, the method returns `null` instead of the affiliate identifier
|
|
1309
1335
|
4. **Bypass Option**: You can bypass the timeout check by passing `true` to `returnInsertAffiliateIdentifier(true)`
|
|
1310
1336
|
|
|
1311
|
-
This ensures that affiliates are only credited for purchases made within the specified attribution window, providing fair and accurate commission tracking.
|
|
1337
|
+
This ensures that affiliates are only credited for purchases made within the specified attribution window, providing fair and accurate commission tracking.
|
|
1338
|
+
|
|
1339
|
+
### Getting Affiliate Details
|
|
1340
|
+
|
|
1341
|
+
You can retrieve detailed information about an affiliate by their short code or deep link using the `getAffiliateDetails` method. This is useful for displaying affiliate information to users or showing personalized content based on the referrer.
|
|
1342
|
+
|
|
1343
|
+
#### Method Signature
|
|
1344
|
+
|
|
1345
|
+
```typescript
|
|
1346
|
+
getAffiliateDetails(affiliateCode: string): Promise<AffiliateDetails>
|
|
1347
|
+
|
|
1348
|
+
type AffiliateDetails = {
|
|
1349
|
+
affiliateName: string;
|
|
1350
|
+
affiliateShortCode: string;
|
|
1351
|
+
deeplinkurl: string;
|
|
1352
|
+
} | null;
|
|
1353
|
+
```
|
|
1354
|
+
|
|
1355
|
+
#### Usage Example
|
|
1356
|
+
|
|
1357
|
+
```javascript
|
|
1358
|
+
import { useDeepLinkIapProvider } from 'insert-affiliate-react-native-sdk';
|
|
1359
|
+
import type { AffiliateDetails } from 'insert-affiliate-react-native-sdk';
|
|
1360
|
+
|
|
1361
|
+
const MyComponent = () => {
|
|
1362
|
+
const { getAffiliateDetails } = useDeepLinkIapProvider();
|
|
1363
|
+
|
|
1364
|
+
const handleGetAffiliateInfo = async (code: string) => {
|
|
1365
|
+
const details = await getAffiliateDetails(code);
|
|
1366
|
+
|
|
1367
|
+
if (details) {
|
|
1368
|
+
console.log('Affiliate Name:', details.affiliateName);
|
|
1369
|
+
console.log('Short Code:', details.affiliateShortCode);
|
|
1370
|
+
console.log('Deep Link:', details.deeplinkurl);
|
|
1371
|
+
|
|
1372
|
+
// Example: Show affiliate name in UI
|
|
1373
|
+
Alert.alert(
|
|
1374
|
+
'Affiliate Found',
|
|
1375
|
+
`This code belongs to ${details.affiliateName}`
|
|
1376
|
+
);
|
|
1377
|
+
} else {
|
|
1378
|
+
console.log('Affiliate not found');
|
|
1379
|
+
Alert.alert('Error', 'Invalid affiliate code');
|
|
1380
|
+
}
|
|
1381
|
+
};
|
|
1382
|
+
|
|
1383
|
+
return (
|
|
1384
|
+
<View>
|
|
1385
|
+
<Button
|
|
1386
|
+
title="Get Affiliate Info"
|
|
1387
|
+
onPress={() => handleGetAffiliateInfo('JOIN_123')}
|
|
1388
|
+
/>
|
|
1389
|
+
</View>
|
|
1390
|
+
);
|
|
1391
|
+
};
|
|
1392
|
+
```
|
|
1393
|
+
|
|
1394
|
+
#### Return Value
|
|
1395
|
+
|
|
1396
|
+
- Returns an object with affiliate details if the code exists:
|
|
1397
|
+
- `affiliateName`: The name of the affiliate
|
|
1398
|
+
- `affiliateShortCode`: The affiliate's short code
|
|
1399
|
+
- `deeplinkurl`: The affiliate's deep link URL
|
|
1400
|
+
- Returns `null` if:
|
|
1401
|
+
- The affiliate code doesn't exist
|
|
1402
|
+
- The company code is not initialized
|
|
1403
|
+
- There's a network error or API issue
|
|
1404
|
+
|
|
1405
|
+
#### Important Notes
|
|
1406
|
+
|
|
1407
|
+
- This method does **not** store or set the affiliate identifier - it only retrieves information
|
|
1408
|
+
- Use `setShortCode()` to actually associate an affiliate with a user
|
|
1409
|
+
- The method automatically strips UUIDs from codes (e.g., "ABC123-uuid" becomes "ABC123")
|
|
1410
|
+
- Works with both short codes and deep link URLs
|
|
@@ -7,6 +7,9 @@ import NetInfo from '@react-native-community/netinfo';
|
|
|
7
7
|
import DeviceInfo from 'react-native-device-info';
|
|
8
8
|
import { PlayInstallReferrer, PlayInstallReferrerInfo } from 'react-native-play-install-referrer';
|
|
9
9
|
|
|
10
|
+
// Development environment check for React Native
|
|
11
|
+
const isDevelopmentEnvironment = typeof __DEV__ !== 'undefined' && __DEV__;
|
|
12
|
+
|
|
10
13
|
// TYPES USED IN THIS PROVIDER
|
|
11
14
|
type T_DEEPLINK_IAP_PROVIDER = {
|
|
12
15
|
children: React.ReactNode;
|
|
@@ -14,6 +17,12 @@ type T_DEEPLINK_IAP_PROVIDER = {
|
|
|
14
17
|
|
|
15
18
|
export type InsertAffiliateIdentifierChangeCallback = (identifier: string | null) => void;
|
|
16
19
|
|
|
20
|
+
export type AffiliateDetails = {
|
|
21
|
+
affiliateName: string;
|
|
22
|
+
affiliateShortCode: string;
|
|
23
|
+
deeplinkurl: string;
|
|
24
|
+
} | null;
|
|
25
|
+
|
|
17
26
|
type CustomPurchase = {
|
|
18
27
|
[key: string]: any; // Accept any fields to allow it to work wtih multiple IAP libraries
|
|
19
28
|
};
|
|
@@ -36,7 +45,8 @@ type T_DEEPLINK_IAP_CONTEXT = {
|
|
|
36
45
|
purchaseToken: string
|
|
37
46
|
) => Promise<void>;
|
|
38
47
|
trackEvent: (eventName: string) => Promise<void>;
|
|
39
|
-
setShortCode: (shortCode: string) => Promise<
|
|
48
|
+
setShortCode: (shortCode: string) => Promise<boolean>;
|
|
49
|
+
getAffiliateDetails: (affiliateCode: string) => Promise<AffiliateDetails>;
|
|
40
50
|
setInsertAffiliateIdentifier: (
|
|
41
51
|
referringLink: string
|
|
42
52
|
) => Promise<void | string>;
|
|
@@ -89,7 +99,8 @@ export const DeepLinkIapContext = createContext<T_DEEPLINK_IAP_CONTEXT>({
|
|
|
89
99
|
returnUserAccountTokenAndStoreExpectedTransaction: async () => '',
|
|
90
100
|
storeExpectedStoreTransaction: async (purchaseToken: string) => {},
|
|
91
101
|
trackEvent: async (eventName: string) => {},
|
|
92
|
-
setShortCode: async (shortCode: string) =>
|
|
102
|
+
setShortCode: async (shortCode: string) => false,
|
|
103
|
+
getAffiliateDetails: async (affiliateCode: string) => null,
|
|
93
104
|
setInsertAffiliateIdentifier: async (referringLink: string) => {},
|
|
94
105
|
setInsertAffiliateIdentifierChangeCallback: (callback: InsertAffiliateIdentifierChangeCallback | null) => {},
|
|
95
106
|
handleInsertLinks: async (url: string) => false,
|
|
@@ -1171,7 +1182,94 @@ const DeepLinkIapProvider: React.FC<T_DEEPLINK_IAP_PROVIDER> = ({
|
|
|
1171
1182
|
return isValidCharacters && referringLink.length >= 3 && referringLink.length <= 25;
|
|
1172
1183
|
};
|
|
1173
1184
|
|
|
1174
|
-
async
|
|
1185
|
+
const checkAffiliateExists = async (affiliateCode: string): Promise<boolean> => {
|
|
1186
|
+
try {
|
|
1187
|
+
const activeCompanyCode = await getActiveCompanyCode();
|
|
1188
|
+
if (!activeCompanyCode) {
|
|
1189
|
+
verboseLog('Cannot check affiliate: no company code available');
|
|
1190
|
+
return false;
|
|
1191
|
+
}
|
|
1192
|
+
|
|
1193
|
+
const url = 'https://api.insertaffiliate.com/V1/checkAffiliateExists';
|
|
1194
|
+
const payload = {
|
|
1195
|
+
companyId: activeCompanyCode,
|
|
1196
|
+
affiliateCode: affiliateCode
|
|
1197
|
+
};
|
|
1198
|
+
|
|
1199
|
+
verboseLog(`Checking if affiliate exists: ${affiliateCode}`);
|
|
1200
|
+
|
|
1201
|
+
const response = await axios.post(url, payload, {
|
|
1202
|
+
headers: {
|
|
1203
|
+
'Content-Type': 'application/json',
|
|
1204
|
+
},
|
|
1205
|
+
});
|
|
1206
|
+
|
|
1207
|
+
verboseLog(`Affiliate check response: ${JSON.stringify(response.data)}`);
|
|
1208
|
+
|
|
1209
|
+
if (response.status === 200 && response.data) {
|
|
1210
|
+
const exists = response.data.exists === true;
|
|
1211
|
+
if (exists) {
|
|
1212
|
+
verboseLog(`Affiliate ${affiliateCode} exists and is valid`);
|
|
1213
|
+
} else {
|
|
1214
|
+
verboseLog(`Affiliate ${affiliateCode} does not exist`);
|
|
1215
|
+
}
|
|
1216
|
+
return exists;
|
|
1217
|
+
} else {
|
|
1218
|
+
verboseLog(`Unexpected response checking affiliate: status ${response.status}`);
|
|
1219
|
+
return false;
|
|
1220
|
+
}
|
|
1221
|
+
} catch (error) {
|
|
1222
|
+
verboseLog(`Error checking affiliate exists: ${error}`);
|
|
1223
|
+
return false;
|
|
1224
|
+
}
|
|
1225
|
+
};
|
|
1226
|
+
|
|
1227
|
+
const getAffiliateDetails = async (affiliateCode: string): Promise<AffiliateDetails> => {
|
|
1228
|
+
try {
|
|
1229
|
+
const activeCompanyCode = await getActiveCompanyCode();
|
|
1230
|
+
if (!activeCompanyCode) {
|
|
1231
|
+
verboseLog('Cannot get affiliate details: no company code available');
|
|
1232
|
+
return null;
|
|
1233
|
+
}
|
|
1234
|
+
|
|
1235
|
+
const url = 'https://api.insertaffiliate.com/V1/checkAffiliateExists';
|
|
1236
|
+
const payload = {
|
|
1237
|
+
companyId: activeCompanyCode,
|
|
1238
|
+
affiliateCode: affiliateCode
|
|
1239
|
+
};
|
|
1240
|
+
|
|
1241
|
+
verboseLog(`Getting affiliate details for: ${affiliateCode}`);
|
|
1242
|
+
|
|
1243
|
+
const response = await axios.post(url, payload, {
|
|
1244
|
+
headers: {
|
|
1245
|
+
'Content-Type': 'application/json',
|
|
1246
|
+
},
|
|
1247
|
+
});
|
|
1248
|
+
|
|
1249
|
+
verboseLog(`Affiliate details response: ${JSON.stringify(response.data)}`);
|
|
1250
|
+
|
|
1251
|
+
if (response.status === 200 && response.data && response.data.exists === true) {
|
|
1252
|
+
const affiliate = response.data.affiliate;
|
|
1253
|
+
if (affiliate) {
|
|
1254
|
+
verboseLog(`Retrieved affiliate details: ${JSON.stringify(affiliate)}`);
|
|
1255
|
+
return {
|
|
1256
|
+
affiliateName: affiliate.affiliateName || '',
|
|
1257
|
+
affiliateShortCode: affiliate.affiliateShortCode || '',
|
|
1258
|
+
deeplinkurl: affiliate.deeplinkurl || ''
|
|
1259
|
+
};
|
|
1260
|
+
}
|
|
1261
|
+
}
|
|
1262
|
+
|
|
1263
|
+
verboseLog(`Affiliate ${affiliateCode} not found or invalid response`);
|
|
1264
|
+
return null;
|
|
1265
|
+
} catch (error) {
|
|
1266
|
+
verboseLog(`Error getting affiliate details: ${error}`);
|
|
1267
|
+
console.error('[Insert Affiliate] Error getting affiliate details:', error);
|
|
1268
|
+
return null;
|
|
1269
|
+
}
|
|
1270
|
+
};
|
|
1271
|
+
|
|
1272
|
+
async function setShortCode(shortCode: string): Promise<boolean> {
|
|
1175
1273
|
console.log('[Insert Affiliate] Setting short code.');
|
|
1176
1274
|
await generateThenSetUserID();
|
|
1177
1275
|
|
|
@@ -1179,8 +1277,18 @@ const DeepLinkIapProvider: React.FC<T_DEEPLINK_IAP_PROVIDER> = ({
|
|
|
1179
1277
|
const capitalisedShortCode = shortCode.toUpperCase();
|
|
1180
1278
|
isShortCode(capitalisedShortCode);
|
|
1181
1279
|
|
|
1182
|
-
//
|
|
1183
|
-
await
|
|
1280
|
+
// Check if the affiliate exists before storing
|
|
1281
|
+
const exists = await checkAffiliateExists(capitalisedShortCode);
|
|
1282
|
+
|
|
1283
|
+
if (exists) {
|
|
1284
|
+
// If affiliate exists, set the Insert Affiliate Identifier
|
|
1285
|
+
await storeInsertAffiliateIdentifier({ link: capitalisedShortCode });
|
|
1286
|
+
console.log(`[Insert Affiliate] Short code ${capitalisedShortCode} validated and stored successfully.`);
|
|
1287
|
+
return true;
|
|
1288
|
+
} else {
|
|
1289
|
+
console.warn(`[Insert Affiliate] Short code ${capitalisedShortCode} does not exist. Not storing.`);
|
|
1290
|
+
return false;
|
|
1291
|
+
}
|
|
1184
1292
|
}
|
|
1185
1293
|
|
|
1186
1294
|
async function getOrCreateUserAccountToken(): Promise<string> {
|
|
@@ -1213,6 +1321,17 @@ const DeepLinkIapProvider: React.FC<T_DEEPLINK_IAP_PROVIDER> = ({
|
|
|
1213
1321
|
return userAccountToken;
|
|
1214
1322
|
}
|
|
1215
1323
|
} catch (error) {
|
|
1324
|
+
// Handle E_IAP_NOT_AVAILABLE error gracefully
|
|
1325
|
+
if ((error as any)?.code === 'E_IAP_NOT_AVAILABLE' ||
|
|
1326
|
+
(error instanceof Error && error.message.includes('E_IAP_NOT_AVAILABLE'))) {
|
|
1327
|
+
|
|
1328
|
+
if (isDevelopmentEnvironment) {
|
|
1329
|
+
console.warn('[Insert Affiliate] IAP not available in development environment. Cannot store expected transaction.');
|
|
1330
|
+
verboseLog('E_IAP_NOT_AVAILABLE error in returnUserAccountTokenAndStoreExpectedTransaction - gracefully handling in development');
|
|
1331
|
+
}
|
|
1332
|
+
return null; // Return null but don't crash
|
|
1333
|
+
}
|
|
1334
|
+
|
|
1216
1335
|
console.error('[Insert Affiliate] Error in returnUserAccountTokenAndStoreExpectedTransaction:', error);
|
|
1217
1336
|
return null;
|
|
1218
1337
|
};
|
|
@@ -1455,6 +1574,20 @@ const DeepLinkIapProvider: React.FC<T_DEEPLINK_IAP_PROVIDER> = ({
|
|
|
1455
1574
|
iapticPublicKey: string
|
|
1456
1575
|
): Promise<boolean> => {
|
|
1457
1576
|
try {
|
|
1577
|
+
// Check for E_IAP_NOT_AVAILABLE error in development environment
|
|
1578
|
+
if ((jsonIapPurchase as any)?.error?.code === 'E_IAP_NOT_AVAILABLE' ||
|
|
1579
|
+
(jsonIapPurchase as any)?.code === 'E_IAP_NOT_AVAILABLE' ||
|
|
1580
|
+
(typeof jsonIapPurchase === 'string' && (jsonIapPurchase as string).includes('E_IAP_NOT_AVAILABLE'))) {
|
|
1581
|
+
|
|
1582
|
+
if (isDevelopmentEnvironment) {
|
|
1583
|
+
console.warn('[Insert Affiliate] IAP not available in development environment. This is expected behavior.');
|
|
1584
|
+
verboseLog('E_IAP_NOT_AVAILABLE error detected in development - gracefully handling');
|
|
1585
|
+
} else {
|
|
1586
|
+
console.error('[Insert Affiliate] IAP not available in production environment. Please check your IAP configuration.');
|
|
1587
|
+
}
|
|
1588
|
+
return false; // Return false but don't crash
|
|
1589
|
+
}
|
|
1590
|
+
|
|
1458
1591
|
const baseRequestBody: RequestBody = {
|
|
1459
1592
|
id: iapticAppId,
|
|
1460
1593
|
type: 'application',
|
|
@@ -1513,6 +1646,19 @@ const DeepLinkIapProvider: React.FC<T_DEEPLINK_IAP_PROVIDER> = ({
|
|
|
1513
1646
|
return false;
|
|
1514
1647
|
}
|
|
1515
1648
|
} catch (error) {
|
|
1649
|
+
// Handle E_IAP_NOT_AVAILABLE error gracefully
|
|
1650
|
+
if ((error as any)?.code === 'E_IAP_NOT_AVAILABLE' ||
|
|
1651
|
+
(error instanceof Error && error.message.includes('E_IAP_NOT_AVAILABLE'))) {
|
|
1652
|
+
|
|
1653
|
+
if (isDevelopmentEnvironment) {
|
|
1654
|
+
console.warn('[Insert Affiliate] IAP not available in development environment. SDK will continue without purchase validation.');
|
|
1655
|
+
verboseLog('E_IAP_NOT_AVAILABLE error caught in validatePurchaseWithIapticAPI - gracefully handling in development');
|
|
1656
|
+
} else {
|
|
1657
|
+
console.error('[Insert Affiliate] IAP not available in production environment. Please check your IAP configuration.');
|
|
1658
|
+
}
|
|
1659
|
+
return false; // Return false but don't crash
|
|
1660
|
+
}
|
|
1661
|
+
|
|
1516
1662
|
if (error instanceof Error) {
|
|
1517
1663
|
console.error(`validatePurchaseWithIapticAPI Error: ${error.message}`);
|
|
1518
1664
|
} else {
|
|
@@ -1738,6 +1884,7 @@ const DeepLinkIapProvider: React.FC<T_DEEPLINK_IAP_PROVIDER> = ({
|
|
|
1738
1884
|
userId,
|
|
1739
1885
|
OfferCode,
|
|
1740
1886
|
setShortCode,
|
|
1887
|
+
getAffiliateDetails,
|
|
1741
1888
|
returnInsertAffiliateIdentifier,
|
|
1742
1889
|
isAffiliateAttributionValid,
|
|
1743
1890
|
getAffiliateStoredDate,
|
package/src/index.ts
CHANGED
|
@@ -4,4 +4,4 @@ import useDeepLinkIapProvider from "./useDeepLinkIapProvider";
|
|
|
4
4
|
export { DeepLinkIapProvider, useDeepLinkIapProvider };
|
|
5
5
|
|
|
6
6
|
// Export types
|
|
7
|
-
export type { InsertAffiliateIdentifierChangeCallback } from "./DeepLinkIapProvider";
|
|
7
|
+
export type { InsertAffiliateIdentifierChangeCallback, AffiliateDetails } from "./DeepLinkIapProvider";
|
|
@@ -13,6 +13,7 @@ const useDeepLinkIapProvider = () => {
|
|
|
13
13
|
getAffiliateStoredDate,
|
|
14
14
|
trackEvent,
|
|
15
15
|
setShortCode,
|
|
16
|
+
getAffiliateDetails,
|
|
16
17
|
setInsertAffiliateIdentifier,
|
|
17
18
|
setInsertAffiliateIdentifierChangeCallback,
|
|
18
19
|
handleInsertLinks,
|
|
@@ -32,6 +33,7 @@ const useDeepLinkIapProvider = () => {
|
|
|
32
33
|
getAffiliateStoredDate,
|
|
33
34
|
trackEvent,
|
|
34
35
|
setShortCode,
|
|
36
|
+
getAffiliateDetails,
|
|
35
37
|
setInsertAffiliateIdentifier,
|
|
36
38
|
setInsertAffiliateIdentifierChangeCallback,
|
|
37
39
|
handleInsertLinks,
|