insert-affiliate-react-native-sdk 1.1.3 → 1.1.6

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.
@@ -1,14 +1,4 @@
1
1
  import React, { createContext, useEffect, useState } from "react";
2
- import {
3
- Purchase,
4
- Subscription,
5
- useIAP,
6
- requestSubscription,
7
- endConnection,
8
- withIAPContext,
9
- } from "react-native-iap";
10
- import { isPlay } from "react-native-iap/src/internal";
11
- import branch from "react-native-branch";
12
2
  import { Platform } from "react-native";
13
3
  import axios from "axios";
14
4
  import AsyncStorage from "@react-native-async-storage/async-storage";
@@ -16,22 +6,28 @@ import AsyncStorage from "@react-native-async-storage/async-storage";
16
6
  // TYPES USED IN THIS PROVIDER
17
7
  type T_DEEPLINK_IAP_PROVIDER = {
18
8
  children: React.ReactNode;
19
- iapSkus: string[];
20
9
  iapticAppId: string;
21
10
  iapticAppName: string;
22
11
  iapticPublicKey: string;
23
12
  };
24
13
 
14
+ type CustomPurchase = {
15
+ [key: string]: any; // Accept any fields to allow it to work wtih multiple IAP libraries
16
+ };
17
+
25
18
  type T_DEEPLINK_IAP_CONTEXT = {
26
- iapLoading: boolean;
27
- alreadyPurchased: boolean;
28
- subscriptions: Subscription[];
29
- userPurchase: Purchase | null;
30
19
  referrerLink: string;
31
20
  userId: string;
32
- isIapticValidated: boolean | undefined;
33
- handleBuySubscription: (productId: string, offerToken?: string) => void;
21
+ returnInsertAffiliateIdentifier: () => Promise<string | null>;
22
+ handlePurchaseValidation: (jsonIapPurchase: CustomPurchase) => Promise<boolean>;
34
23
  trackEvent: (eventName: string) => Promise<void>;
24
+ setShortCode: (shortCode: string) => Promise<void>;
25
+ setInsertAffiliateIdentifier: (
26
+ referringLink: string,
27
+ completion: (shortLink: string | null) => void
28
+ ) => Promise<void>;
29
+ initialize: (code: string | null) => Promise<void>;
30
+ isInitialized: boolean;
35
31
  };
36
32
 
37
33
  type RequestBody = {
@@ -58,44 +54,54 @@ const ASYNC_KEYS = {
58
54
 
59
55
  // STARTING CONTEXT IMPLEMENTATION
60
56
  export const DeepLinkIapContext = createContext<T_DEEPLINK_IAP_CONTEXT>({
61
- iapLoading: false,
62
- alreadyPurchased: false,
63
- isIapticValidated: undefined,
64
- subscriptions: [],
65
- userPurchase: null,
66
57
  referrerLink: "",
67
58
  userId: "",
68
- handleBuySubscription: (productId: string, offerToken?: string) => {},
59
+ returnInsertAffiliateIdentifier: async () => "",
60
+ handlePurchaseValidation: async (jsonIapPurchase: CustomPurchase) => false,
69
61
  trackEvent: async (eventName: string) => {},
62
+ setShortCode: async (shortCode: string) => {},
63
+ setInsertAffiliateIdentifier: async (
64
+ referringLink: string,
65
+ completion: (shortLink: string | null) => void
66
+ ) => {},
67
+ initialize: async (code: string | null) => {},
68
+ isInitialized: false
70
69
  });
71
70
 
72
71
  const DeepLinkIapProvider: React.FC<T_DEEPLINK_IAP_PROVIDER> = ({
73
72
  children,
74
- iapSkus,
75
73
  iapticAppId,
76
74
  iapticAppName,
77
75
  iapticPublicKey,
78
76
  }) => {
79
- // LOCAL STATES
80
- const [iapLoading, setIapLoading] = useState<boolean>(false);
81
- const [alreadyPurchased, setAlreadyPurchased] = useState<boolean>(false);
82
- const [isIapticValidated, setIapticValidated] = useState<boolean | undefined>(
83
- undefined
84
- );
85
- const [userPurchase, setUserPurchase] = useState<Purchase | null>(null);
86
77
  const [referrerLink, setReferrerLink] = useState<string>("");
87
78
  const [userId, setUserId] = useState<string>("");
79
+ const [companyCode, setCompanyCode] = useState<string | null>(null);
80
+ const [isInitialized, setIsInitialized] = useState<boolean>(false);
81
+
82
+ const initialize = async (code: string | null): Promise<void> => {
83
+ if (isInitialized) {
84
+ console.error("[Insert Affiliate] SDK is already initialized.");
85
+ return;
86
+ }
87
+
88
+ if (code && code.trim() !== "") {
89
+ setCompanyCode(code);
90
+ setIsInitialized(true);
91
+ console.log(`[Insert Affiliate] SDK initialized with company code: ${code}`);
92
+ } else {
93
+ console.warn("[Insert Affiliate] SDK initialized without a company code.");
94
+ setIsInitialized(true);
95
+ }
96
+ };
97
+
98
+ const reset = (): void => {
99
+ setCompanyCode(null);
100
+ setIsInitialized(false);
101
+ console.log("[Insert Affiliate] SDK has been reset.");
102
+ };
88
103
 
89
- const {
90
- connected,
91
- purchaseHistory,
92
- getPurchaseHistory,
93
- getSubscriptions,
94
- subscriptions,
95
- finishTransaction,
96
- currentPurchase,
97
- currentPurchaseError,
98
- } = useIAP();
104
+
99
105
 
100
106
  // ASYNC FUNCTIONS
101
107
  const saveValueInAsync = async (key: string, value: string) => {
@@ -146,6 +152,15 @@ const DeepLinkIapProvider: React.FC<T_DEEPLINK_IAP_PROVIDER> = ({
146
152
  }
147
153
  };
148
154
 
155
+ const returnInsertAffiliateIdentifier = async (): Promise<string | null> => {
156
+ try {
157
+ return `${referrerLink}-${userId}`;
158
+ } catch (error) {
159
+ errorLog(`ERROR ~ returnInsertAffiliateIdentifier: ${error}`);
160
+ return null;
161
+ }
162
+ }
163
+
149
164
  // GENERATING UNIQUE USER ID
150
165
  const generateUserID = () => {
151
166
  const characters =
@@ -157,88 +172,129 @@ const DeepLinkIapProvider: React.FC<T_DEEPLINK_IAP_PROVIDER> = ({
157
172
  }
158
173
  return uniqueId;
159
174
  };
175
+
176
+ // Helper function to determine if a link is a short code
177
+ const isShortCode = (referringLink: string): boolean => {
178
+ // Example check: short codes are less than 10 characters
179
+ return referringLink.length < 10;
180
+ };
160
181
 
161
- // BRANCH IMPLEMENTATION
162
- useEffect(() => {
163
- const branchSubscription = branch.subscribe(async ({ error, params }) => {
164
- if (error) {
165
- errorLog(`branchSubscription: ${JSON.stringify(error)}`, "error");
182
+ const setInsertAffiliateIdentifier = async (
183
+ referringLink: string,
184
+ completion: (shortLink: string | null) => void
185
+ ) => {
186
+ try {
187
+ setUserID();
188
+
189
+ if (!referringLink) {
190
+ console.warn("[Insert Affiliate] Referring link is invalid.");
191
+ storeInsertAffiliateIdentifier({ link: referringLink });
192
+ completion(null);
193
+ return;
194
+ }
195
+
196
+ if (!isInitialized || !companyCode) {
197
+ console.error("[Insert Affiliate] SDK is not initialized. Please initialize the SDK with a valid company code.");
198
+ completion(null);
199
+ return;
200
+ }
201
+
202
+ if (!companyCode || companyCode.trim() === "") {
203
+ console.error(
204
+ "[Insert Affiliate] Company code is not set. Please initialize the SDK with a valid company code."
205
+ );
206
+ completion(null);
166
207
  return;
167
- } else if (!params) {
168
- errorLog(`branchSubscription: params does not exits`, "warn");
208
+ }
209
+
210
+ // Check if referring link is already a short code, if so save it and stop here.
211
+ if (isShortCode(referringLink)) {
212
+ console.log("[Insert Affiliate] Referring link is already a short code.");
213
+ storeInsertAffiliateIdentifier({ link: referringLink });
214
+ completion(referringLink);
169
215
  return;
170
- } else if (!params["+clicked_branch_link"]) {
171
- errorLog(`branchSubscription: Not a branch link`, "warn");
216
+ }
217
+
218
+ // 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
219
+ // Encode the referring link
220
+ const encodedAffiliateLink = encodeURIComponent(referringLink);
221
+ if (!encodedAffiliateLink) {
222
+ console.error("[Insert Affiliate] Failed to encode affiliate link.");
223
+ storeInsertAffiliateIdentifier({ link: referringLink });
224
+ completion(null);
172
225
  return;
173
- } else {
174
- if (params["~referring_link"]) {
175
- setReferrerLink(params["~referring_link"]);
176
- const userId = generateUserID();
177
- setUserId(userId);
178
- await saveValueInAsync(ASYNC_KEYS.USER_ID, userId);
179
- await saveValueInAsync(
180
- ASYNC_KEYS.REFERRER_LINK,
181
- params["~referring_link"]
182
- );
183
- } else
184
- errorLog(
185
- `branchSubscription: Params does't have referring_link`,
186
- "warn"
187
- );
188
226
  }
189
- });
190
- return () => {
191
- if (branchSubscription) {
192
- branchSubscription();
227
+
228
+ // Create the request URL
229
+ const urlString = `http://api.insertaffiliate.com/V1/convert-deep-link-to-short-link?companyId=${companyCode}&deepLinkUrl=${encodedAffiliateLink}`;
230
+ const response = await axios.get(urlString, {
231
+ headers: {
232
+ "Content-Type": "application/json",
233
+ },
234
+ });
235
+
236
+ // Call to the backend for the short code and save the resolse in valid
237
+ if (response.status === 200 && response.data.shortLink) {
238
+ const shortLink = response.data.shortLink;
239
+ console.log("[Insert Affiliate] Short link received:", shortLink);
240
+ storeInsertAffiliateIdentifier({ link: shortLink });
241
+ completion(shortLink);
242
+ } else {
243
+ console.warn("[Insert Affiliate] Unexpected response format.");
244
+ storeInsertAffiliateIdentifier({ link: referringLink });
245
+ completion(null);
193
246
  }
194
- };
195
- }, []);
196
-
197
- // IN APP PURCHASE IMPLEMENTATION STARTS
198
-
199
- /**
200
- * This function is responsisble to
201
- * fetch the subscriptions
202
- */
203
- const handleGetSubscriptions = async () => {
204
- try {
205
- await getSubscriptions({ skus: iapSkus });
206
247
  } catch (error) {
207
- errorLog(`handleGetSubscriptions: ${error}`, "error");
248
+ console.error("[Insert Affiliate] Error:", error);
249
+ completion(null);
208
250
  }
209
251
  };
210
252
 
211
- /**
212
- * This function is responsible to
213
- * fetch the purchase history
214
- */
215
- const handleGetPurchaseHistory = async () => {
216
- try {
217
- await getPurchaseHistory();
218
- if (purchaseHistory.length > 0) {
219
- setAlreadyPurchased(true);
220
- setUserPurchase(currentPurchase ? currentPurchase : null);
221
- }
222
- } catch (error) {
223
- errorLog(`handleGetPurchaseHistory: ${error}`, "error");
253
+ async function setUserID() {
254
+ let userId = await getValueFromAsync(ASYNC_KEYS.USER_ID);
255
+ if (!userId) {
256
+ userId = generateUserID();
257
+ await saveValueInAsync(ASYNC_KEYS.USER_ID, userId);
258
+ setUserId(userId);
224
259
  }
225
- };
260
+ }
226
261
 
227
- // Effect to fetch IAP subscriptions + purchase history
228
- useEffect(() => {
229
- const fetchIapEssentials = async () => {
230
- try {
231
- await handleGetSubscriptions();
232
- await handleGetPurchaseHistory();
233
- } catch (error) {
234
- errorLog(`fetchIapEssentials: ${error}`);
235
- }
236
- };
262
+ async function setShortCode(shortCode: string): Promise<void> {
263
+ setUserID();
237
264
 
238
- if (connected) fetchIapEssentials();
239
- }, [connected]);
265
+ // Capitalise the shortcode
266
+ const capitalisedShortCode = shortCode.toUpperCase();
240
267
 
241
- const handlePurchaseValidation = async (jsonIapPurchase: Purchase) => {
268
+ // Ensure the short code is exactly 10 characters long
269
+ if (capitalisedShortCode.length !== 10) {
270
+ console.error("[Insert Affiliate] Error: Short code must be exactly 10 characters long.");
271
+ return;
272
+ }
273
+
274
+ // Check if the short code contains only letters and numbers
275
+ const isValidShortCode = /^[a-zA-Z0-9]+$/.test(capitalisedShortCode);
276
+ if (!isValidShortCode) {
277
+ console.error("[Insert Affiliate] Error: Short code must contain only letters and numbers.");
278
+ return;
279
+ }
280
+
281
+ // If all checks pass, set the Insert Affiliate Identifier
282
+ await storeInsertAffiliateIdentifier({ link: capitalisedShortCode });
283
+
284
+ if (referrerLink) {
285
+ console.log(`[Insert Affiliate] Successfully set affiliate identifier: ${referrerLink}`);
286
+ } else {
287
+ console.error("[Insert Affiliate] Failed to set affiliate identifier.");
288
+ }
289
+ }
290
+
291
+ async function storeInsertAffiliateIdentifier({ link }: { link: string }) {
292
+ console.log(`[Insert Affiliate] Storing affiliate identifier: ${link}`);
293
+ await saveValueInAsync(ASYNC_KEYS.REFERRER_LINK, link);
294
+ setReferrerLink(link);
295
+ }
296
+
297
+ const handlePurchaseValidation = async (jsonIapPurchase: CustomPurchase): Promise<boolean> => {
242
298
  try {
243
299
  const baseRequestBody: RequestBody = {
244
300
  id: iapticAppId,
@@ -246,7 +302,7 @@ const DeepLinkIapProvider: React.FC<T_DEEPLINK_IAP_PROVIDER> = ({
246
302
  };
247
303
 
248
304
  let transaction;
249
-
305
+
250
306
  if (Platform.OS === "ios") {
251
307
  transaction = {
252
308
  id: iapticAppId,
@@ -263,7 +319,7 @@ const DeepLinkIapProvider: React.FC<T_DEEPLINK_IAP_PROVIDER> = ({
263
319
  signature: receiptJson.signature, // Receipt signature
264
320
  };
265
321
  }
266
-
322
+
267
323
  const requestBody = {
268
324
  ...baseRequestBody,
269
325
  transaction,
@@ -271,7 +327,7 @@ const DeepLinkIapProvider: React.FC<T_DEEPLINK_IAP_PROVIDER> = ({
271
327
 
272
328
  if (userId && referrerLink) {
273
329
  requestBody.additionalData = {
274
- applicationUsername: `${referrerLink}/${userId}`,
330
+ applicationUsername: `${referrerLink}-${userId}`,
275
331
  };
276
332
  }
277
333
 
@@ -288,10 +344,10 @@ const DeepLinkIapProvider: React.FC<T_DEEPLINK_IAP_PROVIDER> = ({
288
344
 
289
345
  if (response.status === 200) {
290
346
  console.log("Validation successful:", response.data);
291
- setIapticValidated(true);
347
+ return true;
292
348
  } else {
293
349
  console.error("Validation failed:", response.data);
294
- setIapticValidated(false);
350
+ return false;
295
351
  }
296
352
  } catch (error) {
297
353
  if (error instanceof Error) {
@@ -300,77 +356,7 @@ const DeepLinkIapProvider: React.FC<T_DEEPLINK_IAP_PROVIDER> = ({
300
356
  console.error(`handlePurchaseValidation Unknown Error: ${JSON.stringify(error)}`);
301
357
  }
302
358
 
303
- setIapticValidated(false);
304
- }
305
- };
306
-
307
- useEffect(() => {
308
- const checkCurrentPurchase = async () => {
309
- try {
310
- if (currentPurchase?.productId) {
311
- setUserPurchase(currentPurchase);
312
-
313
- await handlePurchaseValidation(currentPurchase);
314
-
315
- await finishTransaction({
316
- purchase: currentPurchase,
317
- isConsumable: true,
318
- });
319
-
320
- await saveValueInAsync(
321
- ASYNC_KEYS.USER_PURCHASE,
322
- JSON.stringify(currentPurchase)
323
- );
324
- setIapLoading(false);
325
- }
326
- } catch (error) {
327
- setIapLoading(false);
328
- errorLog(`checkCurrentPurchase: ${error}`, "error");
329
- }
330
- };
331
-
332
- checkCurrentPurchase();
333
- }, [currentPurchase, finishTransaction]);
334
-
335
- useEffect(() => {
336
- const checkCurrentPurchaseError = async () => {
337
- if (currentPurchaseError) {
338
- setIapLoading(false);
339
- errorLog(
340
- `checkCurrentPurchaseError: ${currentPurchaseError.message}`,
341
- "error"
342
- );
343
- }
344
- };
345
- checkCurrentPurchaseError();
346
- }, [currentPurchaseError]);
347
-
348
- /**
349
- * Function is responsible to
350
- * buy a subscription
351
- * @param {string} productId
352
- * @param {string} [offerToken]
353
- */
354
- const handleBuySubscription = async (
355
- productId: string,
356
- offerToken?: string
357
- ) => {
358
- if (isPlay && !offerToken) {
359
- console.warn(
360
- `There are no subscription Offers for selected product (Only requiered for Google Play purchases): ${productId}`
361
- );
362
- }
363
- try {
364
- setIapLoading(true);
365
- await requestSubscription({
366
- sku: productId,
367
- ...(offerToken && {
368
- subscriptionOffers: [{ sku: productId, offerToken }],
369
- }),
370
- });
371
- } catch (error) {
372
- setIapLoading(false);
373
- errorLog(`handleBuySubscription: ${error}`, "error");
359
+ return false;
374
360
  }
375
361
  };
376
362
 
@@ -385,7 +371,7 @@ const DeepLinkIapProvider: React.FC<T_DEEPLINK_IAP_PROVIDER> = ({
385
371
 
386
372
  const payload = {
387
373
  eventName,
388
- deepLinkParam: `${referrerLink}/${userId}`, // Similar to Swift SDK
374
+ deepLinkParam: `${referrerLink}/${userId}`,
389
375
  };
390
376
 
391
377
  const response = await axios.post(
@@ -409,24 +395,18 @@ const DeepLinkIapProvider: React.FC<T_DEEPLINK_IAP_PROVIDER> = ({
409
395
  }
410
396
  };
411
397
 
412
- useEffect(() => {
413
- return () => {
414
- endConnection();
415
- };
416
- }, []);
417
-
418
398
  return (
419
399
  <DeepLinkIapContext.Provider
420
400
  value={{
421
- iapLoading,
422
- alreadyPurchased,
423
- isIapticValidated,
424
- subscriptions,
425
- userPurchase,
426
401
  referrerLink,
427
402
  userId,
428
- handleBuySubscription,
403
+ setShortCode,
404
+ returnInsertAffiliateIdentifier,
405
+ handlePurchaseValidation,
429
406
  trackEvent,
407
+ setInsertAffiliateIdentifier,
408
+ initialize,
409
+ isInitialized
430
410
  }}
431
411
  >
432
412
  {children}
@@ -434,4 +414,4 @@ const DeepLinkIapProvider: React.FC<T_DEEPLINK_IAP_PROVIDER> = ({
434
414
  );
435
415
  };
436
416
 
437
- export default withIAPContext(DeepLinkIapProvider);
417
+ export default DeepLinkIapProvider;
@@ -3,27 +3,27 @@ import { DeepLinkIapContext } from "./DeepLinkIapProvider";
3
3
 
4
4
  const useDeepLinkIapProvider = () => {
5
5
  const {
6
- alreadyPurchased,
7
- handleBuySubscription,
8
- iapLoading,
9
6
  referrerLink,
10
- isIapticValidated,
11
- subscriptions,
12
7
  userId,
13
- userPurchase,
8
+ handlePurchaseValidation,
9
+ returnInsertAffiliateIdentifier,
14
10
  trackEvent,
11
+ setShortCode,
12
+ setInsertAffiliateIdentifier,
13
+ initialize,
14
+ isInitialized
15
15
  } = useContext(DeepLinkIapContext);
16
16
 
17
17
  return {
18
- alreadyPurchased,
19
- handleBuySubscription,
20
- iapLoading,
21
18
  referrerLink,
22
- subscriptions,
23
19
  userId,
24
- isIapticValidated,
25
- userPurchase,
26
- trackEvent
20
+ handlePurchaseValidation,
21
+ returnInsertAffiliateIdentifier,
22
+ trackEvent,
23
+ setShortCode,
24
+ setInsertAffiliateIdentifier,
25
+ initialize,
26
+ isInitialized
27
27
  };
28
28
  };
29
29