insert-affiliate-react-native-sdk 1.3.5 → 1.4.2
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 +4 -2
- package/dist/DeepLinkIapProvider.js +148 -54
- package/dist/useDeepLinkIapProvider.d.ts +3 -1
- package/dist/useDeepLinkIapProvider.js +3 -1
- package/package.json +1 -1
- package/readme.md +107 -10
- package/src/DeepLinkIapProvider.tsx +199 -77
- package/src/useDeepLinkIapProvider.tsx +4 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React from
|
|
1
|
+
import React from 'react';
|
|
2
2
|
type T_DEEPLINK_IAP_PROVIDER = {
|
|
3
3
|
children: React.ReactNode;
|
|
4
4
|
};
|
|
@@ -10,9 +10,11 @@ type T_DEEPLINK_IAP_CONTEXT = {
|
|
|
10
10
|
userId: string;
|
|
11
11
|
returnInsertAffiliateIdentifier: () => Promise<string | null>;
|
|
12
12
|
validatePurchaseWithIapticAPI: (jsonIapPurchase: CustomPurchase, iapticAppId: string, iapticAppName: string, iapticPublicKey: string) => Promise<boolean>;
|
|
13
|
+
returnUserAccountTokenAndStoreExpectedTransaction: () => Promise<string | null>;
|
|
14
|
+
storeExpectedStoreTransaction: (purchaseToken: string) => Promise<void>;
|
|
13
15
|
trackEvent: (eventName: string) => Promise<void>;
|
|
14
16
|
setShortCode: (shortCode: string) => Promise<void>;
|
|
15
|
-
setInsertAffiliateIdentifier: (referringLink: string) => Promise<void>;
|
|
17
|
+
setInsertAffiliateIdentifier: (referringLink: string) => Promise<void | string>;
|
|
16
18
|
initialize: (code: string | null) => Promise<void>;
|
|
17
19
|
isInitialized: boolean;
|
|
18
20
|
};
|
|
@@ -41,42 +41,45 @@ const react_native_1 = require("react-native");
|
|
|
41
41
|
const axios_1 = __importDefault(require("axios"));
|
|
42
42
|
const async_storage_1 = __importDefault(require("@react-native-async-storage/async-storage"));
|
|
43
43
|
const ASYNC_KEYS = {
|
|
44
|
-
REFERRER_LINK:
|
|
45
|
-
USER_PURCHASE:
|
|
46
|
-
USER_ID:
|
|
47
|
-
COMPANY_CODE:
|
|
44
|
+
REFERRER_LINK: '@app_referrer_link',
|
|
45
|
+
USER_PURCHASE: '@app_user_purchase',
|
|
46
|
+
USER_ID: '@app_user_id',
|
|
47
|
+
COMPANY_CODE: '@app_company_code',
|
|
48
|
+
USER_ACCOUNT_TOKEN: '@app_user_account_token',
|
|
48
49
|
};
|
|
49
50
|
// STARTING CONTEXT IMPLEMENTATION
|
|
50
51
|
exports.DeepLinkIapContext = (0, react_1.createContext)({
|
|
51
|
-
referrerLink:
|
|
52
|
-
userId:
|
|
53
|
-
returnInsertAffiliateIdentifier: () => __awaiter(void 0, void 0, void 0, function* () { return
|
|
52
|
+
referrerLink: '',
|
|
53
|
+
userId: '',
|
|
54
|
+
returnInsertAffiliateIdentifier: () => __awaiter(void 0, void 0, void 0, function* () { return ''; }),
|
|
54
55
|
validatePurchaseWithIapticAPI: (jsonIapPurchase, iapticAppId, iapticAppName, iapticPublicKey) => __awaiter(void 0, void 0, void 0, function* () { return false; }),
|
|
56
|
+
returnUserAccountTokenAndStoreExpectedTransaction: () => __awaiter(void 0, void 0, void 0, function* () { return ''; }),
|
|
57
|
+
storeExpectedStoreTransaction: (purchaseToken) => __awaiter(void 0, void 0, void 0, function* () { }),
|
|
55
58
|
trackEvent: (eventName) => __awaiter(void 0, void 0, void 0, function* () { }),
|
|
56
59
|
setShortCode: (shortCode) => __awaiter(void 0, void 0, void 0, function* () { }),
|
|
57
60
|
setInsertAffiliateIdentifier: (referringLink) => __awaiter(void 0, void 0, void 0, function* () { }),
|
|
58
61
|
initialize: (code) => __awaiter(void 0, void 0, void 0, function* () { }),
|
|
59
|
-
isInitialized: false
|
|
62
|
+
isInitialized: false,
|
|
60
63
|
});
|
|
61
64
|
const DeepLinkIapProvider = ({ children, }) => {
|
|
62
|
-
const [referrerLink, setReferrerLink] = (0, react_1.useState)(
|
|
63
|
-
const [userId, setUserId] = (0, react_1.useState)(
|
|
65
|
+
const [referrerLink, setReferrerLink] = (0, react_1.useState)('');
|
|
66
|
+
const [userId, setUserId] = (0, react_1.useState)('');
|
|
64
67
|
const [companyCode, setCompanyCode] = (0, react_1.useState)(null);
|
|
65
68
|
const [isInitialized, setIsInitialized] = (0, react_1.useState)(false);
|
|
66
69
|
// MARK: Initialize the SDK
|
|
67
70
|
const initialize = (companyCode) => __awaiter(void 0, void 0, void 0, function* () {
|
|
68
71
|
if (isInitialized) {
|
|
69
|
-
console.error(
|
|
72
|
+
console.error('[Insert Affiliate] SDK is already initialized.');
|
|
70
73
|
return;
|
|
71
74
|
}
|
|
72
|
-
if (companyCode && companyCode.trim() !==
|
|
75
|
+
if (companyCode && companyCode.trim() !== '') {
|
|
73
76
|
setCompanyCode(companyCode);
|
|
74
77
|
yield saveValueInAsync(ASYNC_KEYS.COMPANY_CODE, companyCode);
|
|
75
78
|
setIsInitialized(true);
|
|
76
79
|
console.log(`[Insert Affiliate] SDK initialized with company code: ${companyCode}`);
|
|
77
80
|
}
|
|
78
81
|
else {
|
|
79
|
-
console.warn(
|
|
82
|
+
console.warn('[Insert Affiliate] SDK initialized without a company code.');
|
|
80
83
|
setIsInitialized(true);
|
|
81
84
|
}
|
|
82
85
|
});
|
|
@@ -103,14 +106,18 @@ const DeepLinkIapProvider = ({ children, }) => {
|
|
|
103
106
|
let userId = yield getValueFromAsync(ASYNC_KEYS.USER_ID);
|
|
104
107
|
if (!userId) {
|
|
105
108
|
userId = generateUserID();
|
|
109
|
+
setUserId(userId);
|
|
106
110
|
yield saveValueInAsync(ASYNC_KEYS.USER_ID, userId);
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
107
113
|
setUserId(userId);
|
|
108
114
|
}
|
|
115
|
+
return userId;
|
|
109
116
|
});
|
|
110
117
|
}
|
|
111
118
|
const generateUserID = () => {
|
|
112
|
-
const characters =
|
|
113
|
-
let uniqueId =
|
|
119
|
+
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
|
120
|
+
let uniqueId = '';
|
|
114
121
|
for (let i = 0; i < 6; i++) {
|
|
115
122
|
const randomIndex = Math.floor(Math.random() * characters.length);
|
|
116
123
|
uniqueId += characters[randomIndex];
|
|
@@ -120,7 +127,7 @@ const DeepLinkIapProvider = ({ children, }) => {
|
|
|
120
127
|
const reset = () => {
|
|
121
128
|
setCompanyCode(null);
|
|
122
129
|
setIsInitialized(false);
|
|
123
|
-
console.log(
|
|
130
|
+
console.log('[Insert Affiliate] SDK has been reset.');
|
|
124
131
|
};
|
|
125
132
|
// Helper funciton Storage / Retrieval
|
|
126
133
|
const saveValueInAsync = (key, value) => __awaiter(void 0, void 0, void 0, function* () {
|
|
@@ -136,10 +143,10 @@ const DeepLinkIapProvider = ({ children, }) => {
|
|
|
136
143
|
// Helper function to log errors
|
|
137
144
|
const errorLog = (message, type) => {
|
|
138
145
|
switch (type) {
|
|
139
|
-
case
|
|
146
|
+
case 'error':
|
|
140
147
|
console.error(`ENCOUNTER ERROR ~ ${message}`);
|
|
141
148
|
break;
|
|
142
|
-
case
|
|
149
|
+
case 'warn':
|
|
143
150
|
console.warn(`ENCOUNTER WARNING ~ ${message}`);
|
|
144
151
|
break;
|
|
145
152
|
default:
|
|
@@ -155,8 +162,8 @@ const DeepLinkIapProvider = ({ children, }) => {
|
|
|
155
162
|
};
|
|
156
163
|
function setShortCode(shortCode) {
|
|
157
164
|
return __awaiter(this, void 0, void 0, function* () {
|
|
158
|
-
console.log(
|
|
159
|
-
generateThenSetUserID();
|
|
165
|
+
console.log('[Insert Affiliate] Setting short code.');
|
|
166
|
+
yield generateThenSetUserID();
|
|
160
167
|
// Validate it is a short code
|
|
161
168
|
const capitalisedShortCode = shortCode.toUpperCase();
|
|
162
169
|
isShortCode(capitalisedShortCode);
|
|
@@ -164,6 +171,41 @@ const DeepLinkIapProvider = ({ children, }) => {
|
|
|
164
171
|
yield storeInsertAffiliateIdentifier({ link: capitalisedShortCode });
|
|
165
172
|
});
|
|
166
173
|
}
|
|
174
|
+
function getOrCreateUserAccountToken() {
|
|
175
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
176
|
+
let userAccountToken = yield getValueFromAsync(ASYNC_KEYS.USER_ACCOUNT_TOKEN);
|
|
177
|
+
if (!userAccountToken) {
|
|
178
|
+
userAccountToken = UUID();
|
|
179
|
+
yield saveValueInAsync(ASYNC_KEYS.USER_ACCOUNT_TOKEN, userAccountToken);
|
|
180
|
+
}
|
|
181
|
+
return userAccountToken;
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
;
|
|
185
|
+
const returnUserAccountTokenAndStoreExpectedTransaction = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
186
|
+
try {
|
|
187
|
+
const shortCode = yield returnInsertAffiliateIdentifier();
|
|
188
|
+
if (!shortCode) {
|
|
189
|
+
console.log('[Insert Affiliate] No affiliate stored - not saving expected transaction.');
|
|
190
|
+
return null;
|
|
191
|
+
}
|
|
192
|
+
const userAccountToken = yield getOrCreateUserAccountToken();
|
|
193
|
+
console.log('[Insert Affiliate] User account token:', userAccountToken);
|
|
194
|
+
if (!userAccountToken) {
|
|
195
|
+
console.error('[Insert Affiliate] Failed to generate user account token.');
|
|
196
|
+
return null;
|
|
197
|
+
}
|
|
198
|
+
else {
|
|
199
|
+
yield storeExpectedStoreTransaction(userAccountToken);
|
|
200
|
+
return userAccountToken;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
catch (error) {
|
|
204
|
+
console.error('[Insert Affiliate] Error in returnUserAccountTokenAndStoreExpectedTransaction:', error);
|
|
205
|
+
return null;
|
|
206
|
+
}
|
|
207
|
+
;
|
|
208
|
+
});
|
|
167
209
|
// MARK: Return Insert Affiliate Identifier
|
|
168
210
|
const returnInsertAffiliateIdentifier = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
169
211
|
try {
|
|
@@ -177,60 +219,65 @@ const DeepLinkIapProvider = ({ children, }) => {
|
|
|
177
219
|
// MARK: Insert Affiliate Identifier
|
|
178
220
|
function setInsertAffiliateIdentifier(referringLink) {
|
|
179
221
|
return __awaiter(this, void 0, void 0, function* () {
|
|
180
|
-
console.log(
|
|
222
|
+
console.log('[Insert Affiliate] Setting affiliate identifier.');
|
|
181
223
|
try {
|
|
182
|
-
yield generateThenSetUserID();
|
|
183
|
-
console.log(
|
|
224
|
+
const customerID = yield generateThenSetUserID();
|
|
225
|
+
console.log('[Insert Affiliate] Completed generateThenSetUserID within setInsertAffiliateIdentifier.');
|
|
184
226
|
if (!referringLink) {
|
|
185
|
-
console.warn(
|
|
227
|
+
console.warn('[Insert Affiliate] Referring link is invalid.');
|
|
228
|
+
let heldReferrerLinkBeforeAsyncStateUpdate = referrerLink;
|
|
186
229
|
yield storeInsertAffiliateIdentifier({ link: referringLink });
|
|
187
|
-
return
|
|
230
|
+
return `${heldReferrerLinkBeforeAsyncStateUpdate}-${customerID}`;
|
|
188
231
|
}
|
|
189
|
-
if (!companyCode || companyCode.trim() ===
|
|
232
|
+
if (!companyCode || (companyCode.trim() === '' && companyCode !== null)) {
|
|
190
233
|
let companyCodeFromStorage = yield getValueFromAsync(ASYNC_KEYS.COMPANY_CODE);
|
|
191
234
|
if (companyCodeFromStorage !== null) {
|
|
192
235
|
setCompanyCode(companyCodeFromStorage);
|
|
193
236
|
}
|
|
194
237
|
else {
|
|
195
|
-
console.error(
|
|
238
|
+
console.error('[Insert Affiliate] Company code is not set. Please initialize the SDK with a valid company code.');
|
|
196
239
|
return;
|
|
197
240
|
}
|
|
198
241
|
}
|
|
199
242
|
// Check if referring link is already a short code, if so save it and stop here.
|
|
200
243
|
if (isShortCode(referringLink)) {
|
|
201
|
-
console.log(
|
|
244
|
+
console.log('[Insert Affiliate] Referring link is already a short code.');
|
|
245
|
+
let heldReferrerLinkBeforeAsyncStateUpdate = referrerLink;
|
|
202
246
|
yield storeInsertAffiliateIdentifier({ link: referringLink });
|
|
203
|
-
return
|
|
247
|
+
return `${heldReferrerLinkBeforeAsyncStateUpdate}-${customerID}`;
|
|
204
248
|
}
|
|
205
249
|
// If the code is not already a short code, encode it raedy to send to our endpoint to return the short code. Save it before making the call in case something goes wrong
|
|
206
250
|
// Encode the referring link
|
|
207
251
|
const encodedAffiliateLink = encodeURIComponent(referringLink);
|
|
208
252
|
if (!encodedAffiliateLink) {
|
|
209
|
-
console.error(
|
|
253
|
+
console.error('[Insert Affiliate] Failed to encode affiliate link.');
|
|
254
|
+
let heldReferrerLinkBeforeAsyncStateUpdate = referrerLink;
|
|
210
255
|
yield storeInsertAffiliateIdentifier({ link: referringLink });
|
|
211
|
-
return
|
|
256
|
+
return `${heldReferrerLinkBeforeAsyncStateUpdate}-${customerID}`;
|
|
212
257
|
}
|
|
213
258
|
// Create the request URL
|
|
214
259
|
const urlString = `https://api.insertaffiliate.com/V1/convert-deep-link-to-short-link?companyId=${companyCode}&deepLinkUrl=${encodedAffiliateLink}`;
|
|
215
|
-
console.log(
|
|
260
|
+
console.log('[Insert Affiliate] urlString .', urlString);
|
|
216
261
|
const response = yield axios_1.default.get(urlString, {
|
|
217
262
|
headers: {
|
|
218
|
-
|
|
263
|
+
'Content-Type': 'application/json',
|
|
219
264
|
},
|
|
220
265
|
});
|
|
221
266
|
// Call to the backend for the short code and save the resolse in valid
|
|
222
267
|
if (response.status === 200 && response.data.shortLink) {
|
|
223
268
|
const shortLink = response.data.shortLink;
|
|
224
|
-
console.log(
|
|
225
|
-
|
|
269
|
+
console.log('[Insert Affiliate] Short link received:', shortLink);
|
|
270
|
+
return `${shortLink}-${customerID}`;
|
|
226
271
|
}
|
|
227
272
|
else {
|
|
228
|
-
console.warn(
|
|
273
|
+
console.warn('[Insert Affiliate] Unexpected response format.');
|
|
274
|
+
let heldReferrerLinkBeforeAsyncStateUpdate = referrerLink;
|
|
229
275
|
yield storeInsertAffiliateIdentifier({ link: referringLink });
|
|
276
|
+
return `${heldReferrerLinkBeforeAsyncStateUpdate}-${customerID}`;
|
|
230
277
|
}
|
|
231
278
|
}
|
|
232
279
|
catch (error) {
|
|
233
|
-
console.error(
|
|
280
|
+
console.error('[Insert Affiliate] Error:', error);
|
|
234
281
|
}
|
|
235
282
|
});
|
|
236
283
|
}
|
|
@@ -238,29 +285,29 @@ const DeepLinkIapProvider = ({ children, }) => {
|
|
|
238
285
|
function storeInsertAffiliateIdentifier(_a) {
|
|
239
286
|
return __awaiter(this, arguments, void 0, function* ({ link }) {
|
|
240
287
|
console.log(`[Insert Affiliate] Storing affiliate identifier: ${link}`);
|
|
241
|
-
yield saveValueInAsync(ASYNC_KEYS.REFERRER_LINK, link);
|
|
242
288
|
setReferrerLink(link);
|
|
289
|
+
yield saveValueInAsync(ASYNC_KEYS.REFERRER_LINK, link);
|
|
243
290
|
});
|
|
244
291
|
}
|
|
245
292
|
const validatePurchaseWithIapticAPI = (jsonIapPurchase, iapticAppId, iapticAppName, iapticPublicKey) => __awaiter(void 0, void 0, void 0, function* () {
|
|
246
293
|
try {
|
|
247
294
|
const baseRequestBody = {
|
|
248
295
|
id: iapticAppId,
|
|
249
|
-
type:
|
|
296
|
+
type: 'application',
|
|
250
297
|
};
|
|
251
298
|
let transaction;
|
|
252
|
-
if (react_native_1.Platform.OS ===
|
|
299
|
+
if (react_native_1.Platform.OS === 'ios') {
|
|
253
300
|
transaction = {
|
|
254
301
|
id: iapticAppId,
|
|
255
|
-
type:
|
|
302
|
+
type: 'ios-appstore',
|
|
256
303
|
appStoreReceipt: jsonIapPurchase.transactionReceipt,
|
|
257
304
|
};
|
|
258
305
|
}
|
|
259
306
|
else {
|
|
260
|
-
const receiptJson = JSON.parse(atob(jsonIapPurchase.transactionReceipt ||
|
|
307
|
+
const receiptJson = JSON.parse(atob(jsonIapPurchase.transactionReceipt || ''));
|
|
261
308
|
transaction = {
|
|
262
309
|
id: receiptJson.orderId, // Extracted orderId
|
|
263
|
-
type:
|
|
310
|
+
type: 'android-playstore',
|
|
264
311
|
purchaseToken: receiptJson.purchaseToken, // Extracted purchase token
|
|
265
312
|
receipt: jsonIapPurchase.transactionReceipt, // Full receipt (Base64)
|
|
266
313
|
signature: receiptJson.signature, // Receipt signature
|
|
@@ -276,19 +323,19 @@ const DeepLinkIapProvider = ({ children, }) => {
|
|
|
276
323
|
// Send validation request to server
|
|
277
324
|
const response = yield (0, axios_1.default)({
|
|
278
325
|
url: `https://validator.iaptic.com/v1/validate`,
|
|
279
|
-
method:
|
|
326
|
+
method: 'POST',
|
|
280
327
|
headers: {
|
|
281
328
|
Authorization: `Basic ${btoa(`${iapticAppName}:${iapticPublicKey}`)}`,
|
|
282
|
-
|
|
329
|
+
'Content-Type': 'application/json',
|
|
283
330
|
},
|
|
284
331
|
data: requestBody,
|
|
285
332
|
});
|
|
286
333
|
if (response.status === 200) {
|
|
287
|
-
console.log(
|
|
334
|
+
console.log('Validation successful:', response.data);
|
|
288
335
|
return true;
|
|
289
336
|
}
|
|
290
337
|
else {
|
|
291
|
-
console.error(
|
|
338
|
+
console.error('Validation failed:', response.data);
|
|
292
339
|
return false;
|
|
293
340
|
}
|
|
294
341
|
}
|
|
@@ -302,29 +349,67 @@ const DeepLinkIapProvider = ({ children, }) => {
|
|
|
302
349
|
return false;
|
|
303
350
|
}
|
|
304
351
|
});
|
|
305
|
-
|
|
352
|
+
const storeExpectedStoreTransaction = (purchaseToken) => __awaiter(void 0, void 0, void 0, function* () {
|
|
353
|
+
if (!companyCode || (companyCode.trim() === '' && companyCode !== null)) {
|
|
354
|
+
console.error("[Insert Affiliate] Company code is not set. Please initialize the SDK with a valid company code.");
|
|
355
|
+
return;
|
|
356
|
+
}
|
|
357
|
+
const shortCode = yield returnInsertAffiliateIdentifier();
|
|
358
|
+
if (!shortCode) {
|
|
359
|
+
console.error("[Insert Affiliate] No affiliate identifier found. Please set one before tracking events.");
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
362
|
+
// Build JSON payload
|
|
363
|
+
const payload = {
|
|
364
|
+
UUID: purchaseToken,
|
|
365
|
+
companyCode,
|
|
366
|
+
shortCode,
|
|
367
|
+
storedDate: new Date().toISOString(), // ISO8601 format
|
|
368
|
+
};
|
|
369
|
+
console.log("[Insert Affiliate] Storing expected transaction: ", payload);
|
|
370
|
+
try {
|
|
371
|
+
const response = yield fetch("https://api.insertaffiliate.com/v1/api/app-store-webhook/create-expected-transaction", {
|
|
372
|
+
method: "POST",
|
|
373
|
+
headers: {
|
|
374
|
+
"Content-Type": "application/json",
|
|
375
|
+
},
|
|
376
|
+
body: JSON.stringify(payload),
|
|
377
|
+
});
|
|
378
|
+
if (response.ok) {
|
|
379
|
+
console.info("[Insert Affiliate] Expected transaction stored successfully.");
|
|
380
|
+
}
|
|
381
|
+
else {
|
|
382
|
+
const errorText = yield response.text();
|
|
383
|
+
console.error(`[Insert Affiliate] Failed to store expected transaction with status code: ${response.status}. Response: ${errorText}`);
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
catch (error) {
|
|
387
|
+
console.error(`[Insert Affiliate] Error storing expected transaction: ${error}`);
|
|
388
|
+
}
|
|
389
|
+
});
|
|
390
|
+
// MARK: Track Event
|
|
306
391
|
const trackEvent = (eventName) => __awaiter(void 0, void 0, void 0, function* () {
|
|
307
392
|
try {
|
|
308
393
|
if (!referrerLink || !userId) {
|
|
309
|
-
console.warn(
|
|
394
|
+
console.warn('[Insert Affiliate] No affiliate identifier found. Please set one before tracking events.');
|
|
310
395
|
return Promise.resolve();
|
|
311
396
|
}
|
|
312
397
|
const payload = {
|
|
313
398
|
eventName,
|
|
314
399
|
deepLinkParam: `${referrerLink}/${userId}`,
|
|
315
400
|
};
|
|
316
|
-
const response = yield axios_1.default.post(
|
|
317
|
-
headers: {
|
|
401
|
+
const response = yield axios_1.default.post('https://api.insertaffiliate.com/v1/trackEvent', payload, {
|
|
402
|
+
headers: { 'Content-Type': 'application/json' },
|
|
318
403
|
});
|
|
319
404
|
if (response.status === 200) {
|
|
320
|
-
console.log(
|
|
405
|
+
console.log('[Insert Affiliate] Event tracked successfully');
|
|
321
406
|
}
|
|
322
407
|
else {
|
|
323
408
|
console.error(`[Insert Affiliate] Failed to track event with status code: ${response.status}`);
|
|
324
409
|
}
|
|
325
410
|
}
|
|
326
411
|
catch (error) {
|
|
327
|
-
console.error(
|
|
412
|
+
console.error('[Insert Affiliate] Error tracking event:', error);
|
|
328
413
|
return Promise.reject(error);
|
|
329
414
|
}
|
|
330
415
|
});
|
|
@@ -333,11 +418,20 @@ const DeepLinkIapProvider = ({ children, }) => {
|
|
|
333
418
|
userId,
|
|
334
419
|
setShortCode,
|
|
335
420
|
returnInsertAffiliateIdentifier,
|
|
421
|
+
storeExpectedStoreTransaction,
|
|
422
|
+
returnUserAccountTokenAndStoreExpectedTransaction,
|
|
336
423
|
validatePurchaseWithIapticAPI,
|
|
337
424
|
trackEvent,
|
|
338
425
|
setInsertAffiliateIdentifier,
|
|
339
426
|
initialize,
|
|
340
|
-
isInitialized
|
|
427
|
+
isInitialized,
|
|
341
428
|
} }, children));
|
|
342
429
|
};
|
|
343
430
|
exports.default = DeepLinkIapProvider;
|
|
431
|
+
function UUID() {
|
|
432
|
+
// Generate a random UUID (version 4)
|
|
433
|
+
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
|
|
434
|
+
const r = (Math.random() * 16) | 0, v = c === 'x' ? r : (r & 0x3) | 0x8;
|
|
435
|
+
return v.toString(16);
|
|
436
|
+
});
|
|
437
|
+
}
|
|
@@ -4,10 +4,12 @@ declare const useDeepLinkIapProvider: () => {
|
|
|
4
4
|
validatePurchaseWithIapticAPI: (jsonIapPurchase: {
|
|
5
5
|
[key: string]: any;
|
|
6
6
|
}, iapticAppId: string, iapticAppName: string, iapticPublicKey: string) => Promise<boolean>;
|
|
7
|
+
storeExpectedStoreTransaction: (purchaseToken: string) => Promise<void>;
|
|
8
|
+
returnUserAccountTokenAndStoreExpectedTransaction: () => Promise<string | null>;
|
|
7
9
|
returnInsertAffiliateIdentifier: () => Promise<string | null>;
|
|
8
10
|
trackEvent: (eventName: string) => Promise<void>;
|
|
9
11
|
setShortCode: (shortCode: string) => Promise<void>;
|
|
10
|
-
setInsertAffiliateIdentifier: (referringLink: string) => Promise<void>;
|
|
12
|
+
setInsertAffiliateIdentifier: (referringLink: string) => Promise<void | string>;
|
|
11
13
|
initialize: (code: string | null) => Promise<void>;
|
|
12
14
|
isInitialized: boolean;
|
|
13
15
|
};
|
|
@@ -3,11 +3,13 @@ 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, returnInsertAffiliateIdentifier, trackEvent, setShortCode, setInsertAffiliateIdentifier, initialize, isInitialized } = (0, react_1.useContext)(DeepLinkIapProvider_1.DeepLinkIapContext);
|
|
6
|
+
const { referrerLink, userId, validatePurchaseWithIapticAPI, storeExpectedStoreTransaction, returnUserAccountTokenAndStoreExpectedTransaction, returnInsertAffiliateIdentifier, trackEvent, setShortCode, setInsertAffiliateIdentifier, initialize, isInitialized } = (0, react_1.useContext)(DeepLinkIapProvider_1.DeepLinkIapContext);
|
|
7
7
|
return {
|
|
8
8
|
referrerLink,
|
|
9
9
|
userId,
|
|
10
10
|
validatePurchaseWithIapticAPI,
|
|
11
|
+
storeExpectedStoreTransaction,
|
|
12
|
+
returnUserAccountTokenAndStoreExpectedTransaction,
|
|
11
13
|
returnInsertAffiliateIdentifier,
|
|
12
14
|
trackEvent,
|
|
13
15
|
setShortCode,
|
package/package.json
CHANGED
package/readme.md
CHANGED
|
@@ -71,6 +71,8 @@ const App = () => {
|
|
|
71
71
|
Insert Affiliate requires a Receipt Verification platform to validate in-app purchases. You must choose **one** of our supported partners:
|
|
72
72
|
- [RevenueCat](https://www.revenuecat.com/)
|
|
73
73
|
- [Iaptic](https://www.iaptic.com/account)
|
|
74
|
+
- [App Store Direct Integration](#app-store-direct-integration)
|
|
75
|
+
- [Google Play Store Direct Integration](#google-play-store-direct-integration)
|
|
74
76
|
|
|
75
77
|
### Option 1: RevenueCat Integration
|
|
76
78
|
#### Step 1. Code Setup
|
|
@@ -156,7 +158,7 @@ const Child = () => {
|
|
|
156
158
|
// ***...***
|
|
157
159
|
// Fetch & Load your subscription/purchases and handling the IAP purchase here as per the Iaptic Documentation...
|
|
158
160
|
// ***...***
|
|
159
|
-
|
|
161
|
+
|
|
160
162
|
// Initialize the Insert Affiliate SDK at the earliest possible moment
|
|
161
163
|
useEffect(() => {
|
|
162
164
|
if (!isInitialized) {
|
|
@@ -164,7 +166,6 @@ const Child = () => {
|
|
|
164
166
|
}
|
|
165
167
|
}, [initialize, isInitialized]);
|
|
166
168
|
|
|
167
|
-
|
|
168
169
|
// Validate the purchase with Iaptic through Insert Affiliate's SDK for Affiliate Tracking
|
|
169
170
|
useEffect(() => {
|
|
170
171
|
if (currentPurchase) {
|
|
@@ -211,22 +212,118 @@ export default App;
|
|
|
211
212
|
- Replace `{{ your_iaptic_public_key }}` with your **Iaptic Public Key**. You can find this [here](https://www.iaptic.com/settings).
|
|
212
213
|
- Replace `{{ your_company_code }}` with the unique company code associated with your Insert Affiliate account. You can find this code in your dashboard under [Settings](http://app.insertaffiliate.com/settings).
|
|
213
214
|
|
|
215
|
+
### Option 3: App Store Direct Integration
|
|
216
|
+
|
|
217
|
+
Our direct App Store integration is currently in beta and currently supports subscriptions only. **Consumables and one-off purchases are not yet supported** due to App Store server-to-server notification limitations.
|
|
218
|
+
|
|
219
|
+
We plan to release support for consumables and one-off purchases soon. In the meantime, you can use a receipt verification platform from the other integration options.
|
|
220
|
+
|
|
221
|
+
#### Apple App Store Notification Setup
|
|
222
|
+
To proceed, visit [our docs](https://docs.insertaffiliate.com/direct-store-purchase-integration#1-apple-app-store-server-notifications) and complete the required setup steps to set up App Store Server to Server Notifications.
|
|
223
|
+
|
|
224
|
+
#### Implementing Purchases
|
|
225
|
+
|
|
226
|
+
##### 1. Import Required Modules
|
|
227
|
+
|
|
228
|
+
Ensure you import the necessary dependencies, including `Platform` and `useDeepLinkIapProvider` from the SDK.
|
|
229
|
+
|
|
230
|
+
```javascript
|
|
231
|
+
import { Platform } from 'react-native';
|
|
232
|
+
import { DeepLinkIapProvider, useDeepLinkIapProvider } from 'insert-affiliate-react-native-sdk';
|
|
233
|
+
import { requestSubscription } from 'react-native-iap';
|
|
234
|
+
|
|
235
|
+
const { returnUserAccountTokenAndStoreExpectedTransaction } = useDeepLinkIapProvider();
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
##### 2. Handle the Purchase
|
|
240
|
+
When a user initiates a subscription, retrieve the appAccountToken and pass it to the requestSubscription call:
|
|
241
|
+
|
|
242
|
+
```javascript
|
|
243
|
+
const handleBuySubscription = async (product: SubscriptionAndroid | Subscription) => {
|
|
244
|
+
try {
|
|
245
|
+
let appAccountToken = null;
|
|
246
|
+
|
|
247
|
+
// Step 1: Retrieve the appAccountToken for iOS
|
|
248
|
+
if (Platform.OS === 'ios') {
|
|
249
|
+
appAccountToken = await returnUserAccountTokenAndStoreExpectedTransaction();
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// Step 2: Request the subscription and pass the token for tracking
|
|
253
|
+
await requestSubscription({
|
|
254
|
+
sku: product?.productId,
|
|
255
|
+
...(appAccountToken ? { applicationUsername: appAccountToken } : {}),
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
} catch (error) {
|
|
259
|
+
console.error("Error processing subscription:", error);
|
|
260
|
+
}
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
### Option 4: Google Play Store Direct Integration
|
|
267
|
+
Our direct Google Play Store integration is currently in beta.
|
|
268
|
+
|
|
269
|
+
#### Real Time Developer Notifications (RTDN) Setup
|
|
270
|
+
|
|
271
|
+
Visit [our docs](https://docs.insertaffiliate.com/direct-google-play-store-purchase-integration) and complete the required set up steps for Google Play's Real Time Developer Notifications.
|
|
272
|
+
|
|
273
|
+
#### Implementing Purchases
|
|
274
|
+
|
|
275
|
+
##### 1. Import Required Modules
|
|
276
|
+
|
|
277
|
+
Ensure you import the necessary dependencies, including `Platform` and `useDeepLinkIapProvider` from the SDK.
|
|
278
|
+
|
|
279
|
+
```javascript
|
|
280
|
+
import React, {useEffect, useState} from 'react';
|
|
281
|
+
import { Platform } from 'react-native';
|
|
282
|
+
import { DeepLinkIapProvider, useDeepLinkIapProvider } from 'insert-affiliate-react-native-sdk';
|
|
283
|
+
import { currentPurchase, requestSubscription } from 'react-native-iap';
|
|
284
|
+
|
|
285
|
+
const { storeExpectedStoreTransaction } = useDeepLinkIapProvider();
|
|
286
|
+
|
|
287
|
+
useEffect(() => {
|
|
288
|
+
if (currentPurchase) {
|
|
289
|
+
if (Platform.OS === 'android' && currentPurchase.purchaseToken) {
|
|
290
|
+
// Step 1: Store the expected transaction for Google Play purchases
|
|
291
|
+
storeExpectedStoreTransaction(
|
|
292
|
+
currentPurchase.purchaseToken
|
|
293
|
+
);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
}, [currentPurchase, storeExpectedStoreTransaction]);
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
|
|
214
301
|
## Deep Link Setup [Required]
|
|
215
302
|
|
|
216
|
-
|
|
303
|
+
Insert Affiliate requires a Deep Linking platform to create links for your affiliates. Our platform works with **any** deep linking provider, and you only need to follow these steps:
|
|
304
|
+
1. **Create a deep link** in your chosen third-party platform and pass it to our dashboard when an affiliate signs up.
|
|
305
|
+
2. **Handle deep link clicks** in your app by passing the clicked link:
|
|
306
|
+
```javascript
|
|
307
|
+
await setInsertAffiliateIdentifier(referringLink)
|
|
308
|
+
```
|
|
309
|
+
3. **Integrate with a Receipt Verification platform** by using the result from `setInsertAffiliateIdentifier` to log in or set your application’s username. Examples below include [**Iaptic**](https://github.com/Insert-Affiliate/InsertAffiliateReactNativeSDK?tab=readme-ov-file#example-with-iaptic) and [**RevenueCat**](https://github.com/Insert-Affiliate/InsertAffiliateReactNativeSDK?tab=readme-ov-file#example-with-revenuecat)
|
|
217
310
|
|
|
218
|
-
|
|
311
|
+
### Deep Linking with Branch.io
|
|
312
|
+
To set up deep linking with Branch.io, follow these steps:
|
|
219
313
|
|
|
220
|
-
|
|
314
|
+
1. Create a deep link in Branch and pass it to our dashboard when an affiliate signs up.
|
|
315
|
+
- Example: [Create Affiliate](https://docs.insertaffiliate.com/create-affiliate).
|
|
316
|
+
2. Modify Your Deep Link Handling in `App.tsx`
|
|
317
|
+
- After setting up your Branch integration, add the following code to initialise our SDK in your app:
|
|
221
318
|
|
|
222
|
-
After setting up your Branch integration, add the following code to your ```App.tsx```
|
|
223
319
|
|
|
224
320
|
#### Example with RevenueCat
|
|
225
321
|
```javascript
|
|
322
|
+
import { DeepLinkIapProvider, useDeepLinkIapProvider } from 'insert-affiliate-react-native-sdk';
|
|
226
323
|
import { useDeepLinkIapProvider } from 'insert-affiliate-react-native-sdk';
|
|
227
324
|
|
|
228
325
|
//...
|
|
229
|
-
const {setInsertAffiliateIdentifier
|
|
326
|
+
const {setInsertAffiliateIdentifier} = useDeepLinkIapProvider();
|
|
230
327
|
|
|
231
328
|
useEffect(() => {
|
|
232
329
|
if (!isInitialized) return;
|
|
@@ -244,9 +341,8 @@ import { useDeepLinkIapProvider } from 'insert-affiliate-react-native-sdk';
|
|
|
244
341
|
const referringLink = params['~referring_link'];
|
|
245
342
|
if (referringLink) {
|
|
246
343
|
try {
|
|
247
|
-
|
|
344
|
+
let insertAffiliateIdentifier = await setInsertAffiliateIdentifier(referringLink);
|
|
248
345
|
|
|
249
|
-
let insertAffiliateIdentifier = await returnInsertAffiliateIdentifier();
|
|
250
346
|
if (insertAffiliateIdentifier) {
|
|
251
347
|
const { customerInfo, created } = await Purchases.logIn(insertAffiliateIdentifier);
|
|
252
348
|
}
|
|
@@ -266,10 +362,11 @@ import { useDeepLinkIapProvider } from 'insert-affiliate-react-native-sdk';
|
|
|
266
362
|
//...
|
|
267
363
|
```
|
|
268
364
|
|
|
269
|
-
#### Example with Iaptic
|
|
365
|
+
#### Example with Iaptic / App Store Direct Integration / Google Play Direct Integration
|
|
270
366
|
```javascript
|
|
271
367
|
import branch from 'react-native-branch';
|
|
272
368
|
import { DeepLinkIapProvider, useDeepLinkIapProvider } from 'insert-affiliate-react-native-sdk';
|
|
369
|
+
const {setInsertAffiliateIdentifier} = useDeepLinkIapProvider();
|
|
273
370
|
|
|
274
371
|
branch.subscribe(async ({ error, params }) => {
|
|
275
372
|
if (error) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import React, { createContext, useEffect, useState } from
|
|
2
|
-
import { Platform } from
|
|
3
|
-
import axios from
|
|
4
|
-
import AsyncStorage from
|
|
1
|
+
import React, { createContext, useEffect, useState } from 'react';
|
|
2
|
+
import { Platform } from 'react-native';
|
|
3
|
+
import axios from 'axios';
|
|
4
|
+
import AsyncStorage from '@react-native-async-storage/async-storage';
|
|
5
5
|
|
|
6
6
|
// TYPES USED IN THIS PROVIDER
|
|
7
7
|
type T_DEEPLINK_IAP_PROVIDER = {
|
|
@@ -22,9 +22,15 @@ type T_DEEPLINK_IAP_CONTEXT = {
|
|
|
22
22
|
iapticAppName: string,
|
|
23
23
|
iapticPublicKey: string
|
|
24
24
|
) => Promise<boolean>;
|
|
25
|
+
returnUserAccountTokenAndStoreExpectedTransaction: () => Promise<string | null>;
|
|
26
|
+
storeExpectedStoreTransaction: (
|
|
27
|
+
purchaseToken: string
|
|
28
|
+
) => Promise<void>;
|
|
25
29
|
trackEvent: (eventName: string) => Promise<void>;
|
|
26
30
|
setShortCode: (shortCode: string) => Promise<void>;
|
|
27
|
-
setInsertAffiliateIdentifier: (
|
|
31
|
+
setInsertAffiliateIdentifier: (
|
|
32
|
+
referringLink: string
|
|
33
|
+
) => Promise<void | string>;
|
|
28
34
|
initialize: (code: string | null) => Promise<void>;
|
|
29
35
|
isInitialized: boolean;
|
|
30
36
|
};
|
|
@@ -46,52 +52,59 @@ type RequestBody = {
|
|
|
46
52
|
};
|
|
47
53
|
|
|
48
54
|
const ASYNC_KEYS = {
|
|
49
|
-
REFERRER_LINK:
|
|
50
|
-
USER_PURCHASE:
|
|
51
|
-
USER_ID:
|
|
52
|
-
COMPANY_CODE:
|
|
55
|
+
REFERRER_LINK: '@app_referrer_link',
|
|
56
|
+
USER_PURCHASE: '@app_user_purchase',
|
|
57
|
+
USER_ID: '@app_user_id',
|
|
58
|
+
COMPANY_CODE: '@app_company_code',
|
|
59
|
+
USER_ACCOUNT_TOKEN: '@app_user_account_token',
|
|
53
60
|
};
|
|
54
61
|
|
|
55
62
|
// STARTING CONTEXT IMPLEMENTATION
|
|
56
63
|
export const DeepLinkIapContext = createContext<T_DEEPLINK_IAP_CONTEXT>({
|
|
57
|
-
referrerLink:
|
|
58
|
-
userId:
|
|
59
|
-
returnInsertAffiliateIdentifier: async () =>
|
|
64
|
+
referrerLink: '',
|
|
65
|
+
userId: '',
|
|
66
|
+
returnInsertAffiliateIdentifier: async () => '',
|
|
60
67
|
validatePurchaseWithIapticAPI: async (
|
|
61
68
|
jsonIapPurchase: CustomPurchase,
|
|
62
69
|
iapticAppId: string,
|
|
63
70
|
iapticAppName: string,
|
|
64
71
|
iapticPublicKey: string
|
|
65
72
|
) => false,
|
|
73
|
+
returnUserAccountTokenAndStoreExpectedTransaction: async () => '',
|
|
74
|
+
storeExpectedStoreTransaction: async (purchaseToken: string) => {},
|
|
66
75
|
trackEvent: async (eventName: string) => {},
|
|
67
76
|
setShortCode: async (shortCode: string) => {},
|
|
68
77
|
setInsertAffiliateIdentifier: async (referringLink: string) => {},
|
|
69
78
|
initialize: async (code: string | null) => {},
|
|
70
|
-
isInitialized: false
|
|
79
|
+
isInitialized: false,
|
|
71
80
|
});
|
|
72
81
|
|
|
73
82
|
const DeepLinkIapProvider: React.FC<T_DEEPLINK_IAP_PROVIDER> = ({
|
|
74
83
|
children,
|
|
75
84
|
}) => {
|
|
76
|
-
const [referrerLink, setReferrerLink] = useState<string>(
|
|
77
|
-
const [userId, setUserId] = useState<string>(
|
|
85
|
+
const [referrerLink, setReferrerLink] = useState<string>('');
|
|
86
|
+
const [userId, setUserId] = useState<string>('');
|
|
78
87
|
const [companyCode, setCompanyCode] = useState<string | null>(null);
|
|
79
88
|
const [isInitialized, setIsInitialized] = useState<boolean>(false);
|
|
80
89
|
|
|
81
90
|
// MARK: Initialize the SDK
|
|
82
91
|
const initialize = async (companyCode: string | null): Promise<void> => {
|
|
83
92
|
if (isInitialized) {
|
|
84
|
-
console.error(
|
|
93
|
+
console.error('[Insert Affiliate] SDK is already initialized.');
|
|
85
94
|
return;
|
|
86
95
|
}
|
|
87
|
-
|
|
88
|
-
if (companyCode && companyCode.trim() !==
|
|
96
|
+
|
|
97
|
+
if (companyCode && companyCode.trim() !== '') {
|
|
89
98
|
setCompanyCode(companyCode);
|
|
90
99
|
await saveValueInAsync(ASYNC_KEYS.COMPANY_CODE, companyCode);
|
|
91
100
|
setIsInitialized(true);
|
|
92
|
-
console.log(
|
|
101
|
+
console.log(
|
|
102
|
+
`[Insert Affiliate] SDK initialized with company code: ${companyCode}`
|
|
103
|
+
);
|
|
93
104
|
} else {
|
|
94
|
-
console.warn(
|
|
105
|
+
console.warn(
|
|
106
|
+
'[Insert Affiliate] SDK initialized without a company code.'
|
|
107
|
+
);
|
|
95
108
|
setIsInitialized(true);
|
|
96
109
|
}
|
|
97
110
|
};
|
|
@@ -120,15 +133,19 @@ const DeepLinkIapProvider: React.FC<T_DEEPLINK_IAP_PROVIDER> = ({
|
|
|
120
133
|
let userId = await getValueFromAsync(ASYNC_KEYS.USER_ID);
|
|
121
134
|
if (!userId) {
|
|
122
135
|
userId = generateUserID();
|
|
136
|
+
setUserId(userId);
|
|
123
137
|
await saveValueInAsync(ASYNC_KEYS.USER_ID, userId);
|
|
138
|
+
} else {
|
|
124
139
|
setUserId(userId);
|
|
125
140
|
}
|
|
141
|
+
|
|
142
|
+
return userId;
|
|
126
143
|
}
|
|
127
144
|
|
|
128
145
|
const generateUserID = () => {
|
|
129
146
|
const characters =
|
|
130
|
-
|
|
131
|
-
let uniqueId =
|
|
147
|
+
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
|
148
|
+
let uniqueId = '';
|
|
132
149
|
for (let i = 0; i < 6; i++) {
|
|
133
150
|
const randomIndex = Math.floor(Math.random() * characters.length);
|
|
134
151
|
uniqueId += characters[randomIndex];
|
|
@@ -139,7 +156,7 @@ const DeepLinkIapProvider: React.FC<T_DEEPLINK_IAP_PROVIDER> = ({
|
|
|
139
156
|
const reset = (): void => {
|
|
140
157
|
setCompanyCode(null);
|
|
141
158
|
setIsInitialized(false);
|
|
142
|
-
console.log(
|
|
159
|
+
console.log('[Insert Affiliate] SDK has been reset.');
|
|
143
160
|
};
|
|
144
161
|
|
|
145
162
|
// Helper funciton Storage / Retrieval
|
|
@@ -157,12 +174,12 @@ const DeepLinkIapProvider: React.FC<T_DEEPLINK_IAP_PROVIDER> = ({
|
|
|
157
174
|
};
|
|
158
175
|
|
|
159
176
|
// Helper function to log errors
|
|
160
|
-
const errorLog = (message: string, type?:
|
|
177
|
+
const errorLog = (message: string, type?: 'error' | 'warn' | 'log') => {
|
|
161
178
|
switch (type) {
|
|
162
|
-
case
|
|
179
|
+
case 'error':
|
|
163
180
|
console.error(`ENCOUNTER ERROR ~ ${message}`);
|
|
164
181
|
break;
|
|
165
|
-
case
|
|
182
|
+
case 'warn':
|
|
166
183
|
console.warn(`ENCOUNTER WARNING ~ ${message}`);
|
|
167
184
|
break;
|
|
168
185
|
default:
|
|
@@ -171,7 +188,6 @@ const DeepLinkIapProvider: React.FC<T_DEEPLINK_IAP_PROVIDER> = ({
|
|
|
171
188
|
}
|
|
172
189
|
};
|
|
173
190
|
|
|
174
|
-
|
|
175
191
|
// MARK: Short Codes
|
|
176
192
|
const isShortCode = (referringLink: string): boolean => {
|
|
177
193
|
// Short codes are less than 10 characters
|
|
@@ -180,8 +196,8 @@ const DeepLinkIapProvider: React.FC<T_DEEPLINK_IAP_PROVIDER> = ({
|
|
|
180
196
|
};
|
|
181
197
|
|
|
182
198
|
async function setShortCode(shortCode: string): Promise<void> {
|
|
183
|
-
console.log(
|
|
184
|
-
generateThenSetUserID();
|
|
199
|
+
console.log('[Insert Affiliate] Setting short code.');
|
|
200
|
+
await generateThenSetUserID();
|
|
185
201
|
|
|
186
202
|
// Validate it is a short code
|
|
187
203
|
const capitalisedShortCode = shortCode.toUpperCase();
|
|
@@ -191,6 +207,41 @@ const DeepLinkIapProvider: React.FC<T_DEEPLINK_IAP_PROVIDER> = ({
|
|
|
191
207
|
await storeInsertAffiliateIdentifier({ link: capitalisedShortCode });
|
|
192
208
|
}
|
|
193
209
|
|
|
210
|
+
async function getOrCreateUserAccountToken(): Promise<string> {
|
|
211
|
+
let userAccountToken = await getValueFromAsync(ASYNC_KEYS.USER_ACCOUNT_TOKEN);
|
|
212
|
+
|
|
213
|
+
if (!userAccountToken) {
|
|
214
|
+
userAccountToken = UUID();
|
|
215
|
+
await saveValueInAsync(ASYNC_KEYS.USER_ACCOUNT_TOKEN, userAccountToken);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
return userAccountToken;
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
const returnUserAccountTokenAndStoreExpectedTransaction = async (): Promise<string | null> => {
|
|
222
|
+
try {
|
|
223
|
+
const shortCode = await returnInsertAffiliateIdentifier();
|
|
224
|
+
if (!shortCode) {
|
|
225
|
+
console.log('[Insert Affiliate] No affiliate stored - not saving expected transaction.');
|
|
226
|
+
return null;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
const userAccountToken = await getOrCreateUserAccountToken();
|
|
230
|
+
console.log('[Insert Affiliate] User account token:', userAccountToken);
|
|
231
|
+
|
|
232
|
+
if (!userAccountToken) {
|
|
233
|
+
console.error('[Insert Affiliate] Failed to generate user account token.');
|
|
234
|
+
return null;
|
|
235
|
+
} else {
|
|
236
|
+
await storeExpectedStoreTransaction(userAccountToken);
|
|
237
|
+
return userAccountToken;
|
|
238
|
+
}
|
|
239
|
+
} catch (error) {
|
|
240
|
+
console.error('[Insert Affiliate] Error in returnUserAccountTokenAndStoreExpectedTransaction:', error);
|
|
241
|
+
return null;
|
|
242
|
+
};
|
|
243
|
+
};
|
|
244
|
+
|
|
194
245
|
// MARK: Return Insert Affiliate Identifier
|
|
195
246
|
const returnInsertAffiliateIdentifier = async (): Promise<string | null> => {
|
|
196
247
|
try {
|
|
@@ -199,80 +250,93 @@ const DeepLinkIapProvider: React.FC<T_DEEPLINK_IAP_PROVIDER> = ({
|
|
|
199
250
|
errorLog(`ERROR ~ returnInsertAffiliateIdentifier: ${error}`);
|
|
200
251
|
return null;
|
|
201
252
|
}
|
|
202
|
-
}
|
|
203
|
-
|
|
253
|
+
};
|
|
204
254
|
|
|
205
255
|
// MARK: Insert Affiliate Identifier
|
|
206
256
|
|
|
207
|
-
async function setInsertAffiliateIdentifier(
|
|
208
|
-
|
|
257
|
+
async function setInsertAffiliateIdentifier(
|
|
258
|
+
referringLink: string
|
|
259
|
+
): Promise<void | string> {
|
|
260
|
+
console.log('[Insert Affiliate] Setting affiliate identifier.');
|
|
209
261
|
|
|
210
262
|
try {
|
|
211
|
-
await generateThenSetUserID();
|
|
212
|
-
console.log(
|
|
263
|
+
const customerID = await generateThenSetUserID();
|
|
264
|
+
console.log(
|
|
265
|
+
'[Insert Affiliate] Completed generateThenSetUserID within setInsertAffiliateIdentifier.'
|
|
266
|
+
);
|
|
213
267
|
|
|
214
268
|
if (!referringLink) {
|
|
215
|
-
console.warn(
|
|
269
|
+
console.warn('[Insert Affiliate] Referring link is invalid.');
|
|
270
|
+
let heldReferrerLinkBeforeAsyncStateUpdate = referrerLink;
|
|
216
271
|
await storeInsertAffiliateIdentifier({ link: referringLink });
|
|
217
|
-
return
|
|
272
|
+
return `${heldReferrerLinkBeforeAsyncStateUpdate}-${customerID}`;
|
|
218
273
|
}
|
|
219
|
-
|
|
220
|
-
if (!companyCode || companyCode.trim() ===
|
|
221
|
-
let companyCodeFromStorage = await getValueFromAsync(
|
|
274
|
+
|
|
275
|
+
if (!companyCode || (companyCode.trim() === '' && companyCode !== null)) {
|
|
276
|
+
let companyCodeFromStorage = await getValueFromAsync(
|
|
277
|
+
ASYNC_KEYS.COMPANY_CODE
|
|
278
|
+
);
|
|
222
279
|
|
|
223
280
|
if (companyCodeFromStorage !== null) {
|
|
224
281
|
setCompanyCode(companyCodeFromStorage);
|
|
225
282
|
} else {
|
|
226
283
|
console.error(
|
|
227
|
-
|
|
284
|
+
'[Insert Affiliate] Company code is not set. Please initialize the SDK with a valid company code.'
|
|
228
285
|
);
|
|
229
286
|
return;
|
|
230
287
|
}
|
|
231
288
|
}
|
|
232
|
-
|
|
289
|
+
|
|
233
290
|
// Check if referring link is already a short code, if so save it and stop here.
|
|
234
291
|
if (isShortCode(referringLink)) {
|
|
235
|
-
console.log(
|
|
292
|
+
console.log(
|
|
293
|
+
'[Insert Affiliate] Referring link is already a short code.'
|
|
294
|
+
);
|
|
295
|
+
let heldReferrerLinkBeforeAsyncStateUpdate = referrerLink;
|
|
236
296
|
await storeInsertAffiliateIdentifier({ link: referringLink });
|
|
237
|
-
return
|
|
297
|
+
return `${heldReferrerLinkBeforeAsyncStateUpdate}-${customerID}`;
|
|
238
298
|
}
|
|
239
|
-
|
|
299
|
+
|
|
240
300
|
// If the code is not already a short code, encode it raedy to send to our endpoint to return the short code. Save it before making the call in case something goes wrong
|
|
241
301
|
// Encode the referring link
|
|
242
302
|
const encodedAffiliateLink = encodeURIComponent(referringLink);
|
|
243
303
|
if (!encodedAffiliateLink) {
|
|
244
|
-
console.error(
|
|
304
|
+
console.error('[Insert Affiliate] Failed to encode affiliate link.');
|
|
305
|
+
|
|
306
|
+
let heldReferrerLinkBeforeAsyncStateUpdate = referrerLink;
|
|
245
307
|
await storeInsertAffiliateIdentifier({ link: referringLink });
|
|
246
|
-
return
|
|
308
|
+
return `${heldReferrerLinkBeforeAsyncStateUpdate}-${customerID}`;
|
|
247
309
|
}
|
|
248
|
-
|
|
310
|
+
|
|
249
311
|
// Create the request URL
|
|
250
312
|
const urlString = `https://api.insertaffiliate.com/V1/convert-deep-link-to-short-link?companyId=${companyCode}&deepLinkUrl=${encodedAffiliateLink}`;
|
|
251
|
-
console.log(
|
|
313
|
+
console.log('[Insert Affiliate] urlString .', urlString);
|
|
252
314
|
const response = await axios.get(urlString, {
|
|
253
315
|
headers: {
|
|
254
|
-
|
|
316
|
+
'Content-Type': 'application/json',
|
|
255
317
|
},
|
|
256
318
|
});
|
|
257
|
-
|
|
319
|
+
|
|
258
320
|
// Call to the backend for the short code and save the resolse in valid
|
|
259
321
|
if (response.status === 200 && response.data.shortLink) {
|
|
260
322
|
const shortLink = response.data.shortLink;
|
|
261
|
-
console.log(
|
|
262
|
-
|
|
323
|
+
console.log('[Insert Affiliate] Short link received:', shortLink);
|
|
324
|
+
return `${shortLink}-${customerID}`;
|
|
263
325
|
} else {
|
|
264
|
-
console.warn(
|
|
326
|
+
console.warn('[Insert Affiliate] Unexpected response format.');
|
|
327
|
+
let heldReferrerLinkBeforeAsyncStateUpdate = referrerLink;
|
|
265
328
|
await storeInsertAffiliateIdentifier({ link: referringLink });
|
|
329
|
+
return `${heldReferrerLinkBeforeAsyncStateUpdate}-${customerID}`;
|
|
266
330
|
}
|
|
267
331
|
} catch (error) {
|
|
268
|
-
console.error(
|
|
332
|
+
console.error('[Insert Affiliate] Error:', error);
|
|
269
333
|
}
|
|
270
334
|
};
|
|
271
335
|
|
|
272
336
|
async function storeInsertAffiliateIdentifier({ link }: { link: string }) {
|
|
273
337
|
console.log(`[Insert Affiliate] Storing affiliate identifier: ${link}`);
|
|
274
|
-
await saveValueInAsync(ASYNC_KEYS.REFERRER_LINK, link);
|
|
275
338
|
setReferrerLink(link);
|
|
339
|
+
await saveValueInAsync(ASYNC_KEYS.REFERRER_LINK, link);
|
|
276
340
|
}
|
|
277
341
|
|
|
278
342
|
const validatePurchaseWithIapticAPI = async (
|
|
@@ -284,22 +348,24 @@ const DeepLinkIapProvider: React.FC<T_DEEPLINK_IAP_PROVIDER> = ({
|
|
|
284
348
|
try {
|
|
285
349
|
const baseRequestBody: RequestBody = {
|
|
286
350
|
id: iapticAppId,
|
|
287
|
-
type:
|
|
351
|
+
type: 'application',
|
|
288
352
|
};
|
|
289
|
-
|
|
353
|
+
|
|
290
354
|
let transaction;
|
|
291
355
|
|
|
292
|
-
if (Platform.OS ===
|
|
356
|
+
if (Platform.OS === 'ios') {
|
|
293
357
|
transaction = {
|
|
294
358
|
id: iapticAppId,
|
|
295
|
-
type:
|
|
359
|
+
type: 'ios-appstore',
|
|
296
360
|
appStoreReceipt: jsonIapPurchase.transactionReceipt,
|
|
297
361
|
};
|
|
298
362
|
} else {
|
|
299
|
-
const receiptJson = JSON.parse(
|
|
363
|
+
const receiptJson = JSON.parse(
|
|
364
|
+
atob(jsonIapPurchase.transactionReceipt || '')
|
|
365
|
+
);
|
|
300
366
|
transaction = {
|
|
301
367
|
id: receiptJson.orderId, // Extracted orderId
|
|
302
|
-
type:
|
|
368
|
+
type: 'android-playstore',
|
|
303
369
|
purchaseToken: receiptJson.purchaseToken, // Extracted purchase token
|
|
304
370
|
receipt: jsonIapPurchase.transactionReceipt, // Full receipt (Base64)
|
|
305
371
|
signature: receiptJson.signature, // Receipt signature
|
|
@@ -318,43 +384,88 @@ const DeepLinkIapProvider: React.FC<T_DEEPLINK_IAP_PROVIDER> = ({
|
|
|
318
384
|
applicationUsername: `${insertAffiliateIdentifier}`,
|
|
319
385
|
};
|
|
320
386
|
}
|
|
321
|
-
|
|
387
|
+
|
|
322
388
|
// Send validation request to server
|
|
323
389
|
const response = await axios({
|
|
324
390
|
url: `https://validator.iaptic.com/v1/validate`,
|
|
325
|
-
method:
|
|
391
|
+
method: 'POST',
|
|
326
392
|
headers: {
|
|
327
393
|
Authorization: `Basic ${btoa(`${iapticAppName}:${iapticPublicKey}`)}`,
|
|
328
|
-
|
|
394
|
+
'Content-Type': 'application/json',
|
|
329
395
|
},
|
|
330
396
|
data: requestBody,
|
|
331
397
|
});
|
|
332
|
-
|
|
398
|
+
|
|
333
399
|
if (response.status === 200) {
|
|
334
|
-
console.log(
|
|
400
|
+
console.log('Validation successful:', response.data);
|
|
335
401
|
return true;
|
|
336
402
|
} else {
|
|
337
|
-
console.error(
|
|
403
|
+
console.error('Validation failed:', response.data);
|
|
338
404
|
return false;
|
|
339
405
|
}
|
|
340
406
|
} catch (error) {
|
|
341
407
|
if (error instanceof Error) {
|
|
342
408
|
console.error(`validatePurchaseWithIapticAPI Error: ${error.message}`);
|
|
343
409
|
} else {
|
|
344
|
-
console.error(
|
|
410
|
+
console.error(
|
|
411
|
+
`validatePurchaseWithIapticAPI Unknown Error: ${JSON.stringify(
|
|
412
|
+
error
|
|
413
|
+
)}`
|
|
414
|
+
);
|
|
345
415
|
}
|
|
346
416
|
|
|
347
417
|
return false;
|
|
348
418
|
}
|
|
349
419
|
};
|
|
350
|
-
|
|
351
420
|
|
|
352
|
-
|
|
421
|
+
const storeExpectedStoreTransaction = async (purchaseToken: string): Promise<void> => {
|
|
422
|
+
if (!companyCode || (companyCode.trim() === '' && companyCode !== null)) {
|
|
423
|
+
console.error("[Insert Affiliate] Company code is not set. Please initialize the SDK with a valid company code.");
|
|
424
|
+
return;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
const shortCode = await returnInsertAffiliateIdentifier();
|
|
428
|
+
if (!shortCode) {
|
|
429
|
+
console.error("[Insert Affiliate] No affiliate identifier found. Please set one before tracking events.");
|
|
430
|
+
return;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
// Build JSON payload
|
|
434
|
+
const payload = {
|
|
435
|
+
UUID: purchaseToken,
|
|
436
|
+
companyCode,
|
|
437
|
+
shortCode,
|
|
438
|
+
storedDate: new Date().toISOString(), // ISO8601 format
|
|
439
|
+
};
|
|
440
|
+
|
|
441
|
+
console.log("[Insert Affiliate] Storing expected transaction: ", payload);
|
|
442
|
+
|
|
443
|
+
try {
|
|
444
|
+
const response = await fetch("https://api.insertaffiliate.com/v1/api/app-store-webhook/create-expected-transaction", {
|
|
445
|
+
method: "POST",
|
|
446
|
+
headers: {
|
|
447
|
+
"Content-Type": "application/json",
|
|
448
|
+
},
|
|
449
|
+
body: JSON.stringify(payload),
|
|
450
|
+
});
|
|
451
|
+
|
|
452
|
+
if (response.ok) {
|
|
453
|
+
console.info("[Insert Affiliate] Expected transaction stored successfully.");
|
|
454
|
+
} else {
|
|
455
|
+
const errorText = await response.text();
|
|
456
|
+
console.error(`[Insert Affiliate] Failed to store expected transaction with status code: ${response.status}. Response: ${errorText}`);
|
|
457
|
+
}
|
|
458
|
+
} catch (error) {
|
|
459
|
+
console.error(`[Insert Affiliate] Error storing expected transaction: ${error}`);
|
|
460
|
+
}
|
|
461
|
+
};
|
|
462
|
+
|
|
463
|
+
// MARK: Track Event
|
|
353
464
|
const trackEvent = async (eventName: string): Promise<void> => {
|
|
354
465
|
try {
|
|
355
466
|
if (!referrerLink || !userId) {
|
|
356
467
|
console.warn(
|
|
357
|
-
|
|
468
|
+
'[Insert Affiliate] No affiliate identifier found. Please set one before tracking events.'
|
|
358
469
|
);
|
|
359
470
|
return Promise.resolve();
|
|
360
471
|
}
|
|
@@ -365,22 +476,22 @@ const DeepLinkIapProvider: React.FC<T_DEEPLINK_IAP_PROVIDER> = ({
|
|
|
365
476
|
};
|
|
366
477
|
|
|
367
478
|
const response = await axios.post(
|
|
368
|
-
|
|
479
|
+
'https://api.insertaffiliate.com/v1/trackEvent',
|
|
369
480
|
payload,
|
|
370
481
|
{
|
|
371
|
-
headers: {
|
|
482
|
+
headers: { 'Content-Type': 'application/json' },
|
|
372
483
|
}
|
|
373
484
|
);
|
|
374
485
|
|
|
375
486
|
if (response.status === 200) {
|
|
376
|
-
console.log(
|
|
487
|
+
console.log('[Insert Affiliate] Event tracked successfully');
|
|
377
488
|
} else {
|
|
378
489
|
console.error(
|
|
379
490
|
`[Insert Affiliate] Failed to track event with status code: ${response.status}`
|
|
380
491
|
);
|
|
381
492
|
}
|
|
382
493
|
} catch (error) {
|
|
383
|
-
console.error(
|
|
494
|
+
console.error('[Insert Affiliate] Error tracking event:', error);
|
|
384
495
|
return Promise.reject(error);
|
|
385
496
|
}
|
|
386
497
|
};
|
|
@@ -392,11 +503,13 @@ const DeepLinkIapProvider: React.FC<T_DEEPLINK_IAP_PROVIDER> = ({
|
|
|
392
503
|
userId,
|
|
393
504
|
setShortCode,
|
|
394
505
|
returnInsertAffiliateIdentifier,
|
|
506
|
+
storeExpectedStoreTransaction,
|
|
507
|
+
returnUserAccountTokenAndStoreExpectedTransaction,
|
|
395
508
|
validatePurchaseWithIapticAPI,
|
|
396
509
|
trackEvent,
|
|
397
510
|
setInsertAffiliateIdentifier,
|
|
398
511
|
initialize,
|
|
399
|
-
isInitialized
|
|
512
|
+
isInitialized,
|
|
400
513
|
}}
|
|
401
514
|
>
|
|
402
515
|
{children}
|
|
@@ -405,3 +518,12 @@ const DeepLinkIapProvider: React.FC<T_DEEPLINK_IAP_PROVIDER> = ({
|
|
|
405
518
|
};
|
|
406
519
|
|
|
407
520
|
export default DeepLinkIapProvider;
|
|
521
|
+
function UUID(): string {
|
|
522
|
+
// Generate a random UUID (version 4)
|
|
523
|
+
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
|
|
524
|
+
const r = (Math.random() * 16) | 0,
|
|
525
|
+
v = c === 'x' ? r : (r & 0x3) | 0x8;
|
|
526
|
+
return v.toString(16);
|
|
527
|
+
});
|
|
528
|
+
}
|
|
529
|
+
|
|
@@ -6,6 +6,8 @@ const useDeepLinkIapProvider = () => {
|
|
|
6
6
|
referrerLink,
|
|
7
7
|
userId,
|
|
8
8
|
validatePurchaseWithIapticAPI,
|
|
9
|
+
storeExpectedStoreTransaction,
|
|
10
|
+
returnUserAccountTokenAndStoreExpectedTransaction,
|
|
9
11
|
returnInsertAffiliateIdentifier,
|
|
10
12
|
trackEvent,
|
|
11
13
|
setShortCode,
|
|
@@ -18,6 +20,8 @@ const useDeepLinkIapProvider = () => {
|
|
|
18
20
|
referrerLink,
|
|
19
21
|
userId,
|
|
20
22
|
validatePurchaseWithIapticAPI,
|
|
23
|
+
storeExpectedStoreTransaction,
|
|
24
|
+
returnUserAccountTokenAndStoreExpectedTransaction,
|
|
21
25
|
returnInsertAffiliateIdentifier,
|
|
22
26
|
trackEvent,
|
|
23
27
|
setShortCode,
|