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.
@@ -1,4 +1,4 @@
1
- import React from "react";
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: "@app_referrer_link",
45
- USER_PURCHASE: "@app_user_purchase",
46
- USER_ID: "@app_user_id",
47
- COMPANY_CODE: "@app_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("[Insert Affiliate] SDK is already initialized.");
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("[Insert Affiliate] SDK initialized without a company code.");
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 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
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("[Insert Affiliate] SDK has been reset.");
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 "error":
146
+ case 'error':
140
147
  console.error(`ENCOUNTER ERROR ~ ${message}`);
141
148
  break;
142
- case "warn":
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("[Insert Affiliate] Setting short code.");
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("[Insert Affiliate] Setting affiliate identifier.");
222
+ console.log('[Insert Affiliate] Setting affiliate identifier.');
181
223
  try {
182
- yield generateThenSetUserID();
183
- console.log("[Insert Affiliate] Completed generateThenSetUserID within setInsertAffiliateIdentifier.");
224
+ const customerID = yield generateThenSetUserID();
225
+ console.log('[Insert Affiliate] Completed generateThenSetUserID within setInsertAffiliateIdentifier.');
184
226
  if (!referringLink) {
185
- console.warn("[Insert Affiliate] Referring link is invalid.");
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() === "" && companyCode !== null) {
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("[Insert Affiliate] Company code is not set. Please initialize the SDK with a valid company code.");
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("[Insert Affiliate] Referring link is already a short code.");
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("[Insert Affiliate] Failed to encode affiliate link.");
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("[Insert Affiliate] urlString .", urlString);
260
+ console.log('[Insert Affiliate] urlString .', urlString);
216
261
  const response = yield axios_1.default.get(urlString, {
217
262
  headers: {
218
- "Content-Type": "application/json",
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("[Insert Affiliate] Short link received:", shortLink);
225
- yield storeInsertAffiliateIdentifier({ link: shortLink });
269
+ console.log('[Insert Affiliate] Short link received:', shortLink);
270
+ return `${shortLink}-${customerID}`;
226
271
  }
227
272
  else {
228
- console.warn("[Insert Affiliate] Unexpected response format.");
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("[Insert Affiliate] Error:", 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: "application",
296
+ type: 'application',
250
297
  };
251
298
  let transaction;
252
- if (react_native_1.Platform.OS === "ios") {
299
+ if (react_native_1.Platform.OS === 'ios') {
253
300
  transaction = {
254
301
  id: iapticAppId,
255
- type: "ios-appstore",
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: "android-playstore",
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: "POST",
326
+ method: 'POST',
280
327
  headers: {
281
328
  Authorization: `Basic ${btoa(`${iapticAppName}:${iapticPublicKey}`)}`,
282
- "Content-Type": "application/json",
329
+ 'Content-Type': 'application/json',
283
330
  },
284
331
  data: requestBody,
285
332
  });
286
333
  if (response.status === 200) {
287
- console.log("Validation successful:", response.data);
334
+ console.log('Validation successful:', response.data);
288
335
  return true;
289
336
  }
290
337
  else {
291
- console.error("Validation failed:", response.data);
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
- // MARK: Track Event
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("[Insert Affiliate] No affiliate identifier found. Please set one before tracking events.");
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("https://api.insertaffiliate.com/v1/trackEvent", payload, {
317
- headers: { "Content-Type": "application/json" },
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("[Insert Affiliate] Event tracked successfully");
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("[Insert Affiliate] Error tracking event:", 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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "insert-affiliate-react-native-sdk",
3
- "version": "1.3.5",
3
+ "version": "1.4.2",
4
4
  "description": "A package for connecting with the Insert Affiliate Platform to add app based affiliate marketing.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
package/readme.md CHANGED
@@ -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
- ### Step 1: Add the Deep Linking Platform Dependency
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
- In this example, the deep linking functionality is implemented using [Branch.io](https://dashboard.branch.io/).
311
+ ### Deep Linking with Branch.io
312
+ To set up deep linking with Branch.io, follow these steps:
219
313
 
220
- Any alternative deep linking platform can be used by passing the referring link to ```InsertAffiliateSwift.setInsertAffiliateIdentifier(referringLink: "{{ link }}")``` as in the below Branch.io example
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, returnInsertAffiliateIdentifier} = useDeepLinkIapProvider();
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
- await setInsertAffiliateIdentifier(referringLink);
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 "react";
2
- import { Platform } from "react-native";
3
- import axios from "axios";
4
- import AsyncStorage from "@react-native-async-storage/async-storage";
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: (referringLink: string) => Promise<void>;
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: "@app_referrer_link",
50
- USER_PURCHASE: "@app_user_purchase",
51
- USER_ID: "@app_user_id",
52
- COMPANY_CODE: "@app_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("[Insert Affiliate] SDK is already initialized.");
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(`[Insert Affiliate] SDK initialized with company code: ${companyCode}`);
101
+ console.log(
102
+ `[Insert Affiliate] SDK initialized with company code: ${companyCode}`
103
+ );
93
104
  } else {
94
- console.warn("[Insert Affiliate] SDK initialized without a company code.");
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
- "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
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("[Insert Affiliate] SDK has been reset.");
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?: "error" | "warn" | "log") => {
177
+ const errorLog = (message: string, type?: 'error' | 'warn' | 'log') => {
161
178
  switch (type) {
162
- case "error":
179
+ case 'error':
163
180
  console.error(`ENCOUNTER ERROR ~ ${message}`);
164
181
  break;
165
- case "warn":
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("[Insert Affiliate] Setting short code.");
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(referringLink: string): Promise<void> {
208
- console.log("[Insert Affiliate] Setting affiliate identifier.");
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("[Insert Affiliate] Completed generateThenSetUserID within setInsertAffiliateIdentifier.");
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("[Insert Affiliate] Referring link is invalid.");
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() === "" && companyCode !== null) {
221
- let companyCodeFromStorage = await getValueFromAsync(ASYNC_KEYS.COMPANY_CODE);
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
- "[Insert Affiliate] Company code is not set. Please initialize the SDK with a valid company code."
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("[Insert Affiliate] Referring link is already a short code.");
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("[Insert Affiliate] Failed to encode affiliate link.");
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("[Insert Affiliate] urlString .", urlString);
313
+ console.log('[Insert Affiliate] urlString .', urlString);
252
314
  const response = await axios.get(urlString, {
253
315
  headers: {
254
- "Content-Type": "application/json",
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("[Insert Affiliate] Short link received:", shortLink);
262
- await storeInsertAffiliateIdentifier({ link: shortLink });
323
+ console.log('[Insert Affiliate] Short link received:', shortLink);
324
+ return `${shortLink}-${customerID}`;
263
325
  } else {
264
- console.warn("[Insert Affiliate] Unexpected response format.");
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("[Insert Affiliate] Error:", 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: "application",
351
+ type: 'application',
288
352
  };
289
-
353
+
290
354
  let transaction;
291
355
 
292
- if (Platform.OS === "ios") {
356
+ if (Platform.OS === 'ios') {
293
357
  transaction = {
294
358
  id: iapticAppId,
295
- type: "ios-appstore",
359
+ type: 'ios-appstore',
296
360
  appStoreReceipt: jsonIapPurchase.transactionReceipt,
297
361
  };
298
362
  } else {
299
- const receiptJson = JSON.parse(atob(jsonIapPurchase.transactionReceipt || ""));
363
+ const receiptJson = JSON.parse(
364
+ atob(jsonIapPurchase.transactionReceipt || '')
365
+ );
300
366
  transaction = {
301
367
  id: receiptJson.orderId, // Extracted orderId
302
- type: "android-playstore",
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: "POST",
391
+ method: 'POST',
326
392
  headers: {
327
393
  Authorization: `Basic ${btoa(`${iapticAppName}:${iapticPublicKey}`)}`,
328
- "Content-Type": "application/json",
394
+ 'Content-Type': 'application/json',
329
395
  },
330
396
  data: requestBody,
331
397
  });
332
-
398
+
333
399
  if (response.status === 200) {
334
- console.log("Validation successful:", response.data);
400
+ console.log('Validation successful:', response.data);
335
401
  return true;
336
402
  } else {
337
- console.error("Validation failed:", response.data);
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(`validatePurchaseWithIapticAPI Unknown Error: ${JSON.stringify(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
- // MARK: Track Event
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
- "[Insert Affiliate] No affiliate identifier found. Please set one before tracking events."
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
- "https://api.insertaffiliate.com/v1/trackEvent",
479
+ 'https://api.insertaffiliate.com/v1/trackEvent',
369
480
  payload,
370
481
  {
371
- headers: { "Content-Type": "application/json" },
482
+ headers: { 'Content-Type': 'application/json' },
372
483
  }
373
484
  );
374
485
 
375
486
  if (response.status === 200) {
376
- console.log("[Insert Affiliate] Event tracked successfully");
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("[Insert Affiliate] Error tracking event:", 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,