insert-affiliate-react-native-sdk 1.6.0 → 1.6.1
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 +1 -1
- package/dist/DeepLinkIapProvider.js +43 -42
- package/dist/useDeepLinkIapProvider.d.ts +1 -1
- package/dist/useDeepLinkIapProvider.js +2 -2
- package/package.json +1 -1
- package/readme.md +88 -260
- package/src/DeepLinkIapProvider.tsx +56 -47
- package/src/useDeepLinkIapProvider.tsx +2 -2
|
@@ -8,7 +8,6 @@ type CustomPurchase = {
|
|
|
8
8
|
type T_DEEPLINK_IAP_CONTEXT = {
|
|
9
9
|
referrerLink: string;
|
|
10
10
|
userId: string;
|
|
11
|
-
iOSOfferCode: string | null;
|
|
12
11
|
returnInsertAffiliateIdentifier: () => Promise<string | null>;
|
|
13
12
|
validatePurchaseWithIapticAPI: (jsonIapPurchase: CustomPurchase, iapticAppId: string, iapticAppName: string, iapticPublicKey: string) => Promise<boolean>;
|
|
14
13
|
returnUserAccountTokenAndStoreExpectedTransaction: () => Promise<string | null>;
|
|
@@ -17,6 +16,7 @@ type T_DEEPLINK_IAP_CONTEXT = {
|
|
|
17
16
|
setShortCode: (shortCode: string) => Promise<void>;
|
|
18
17
|
setInsertAffiliateIdentifier: (referringLink: string) => Promise<void | string>;
|
|
19
18
|
initialize: (code: string | null, verboseLogging?: boolean) => Promise<void>;
|
|
19
|
+
fetchAndConditionallyOpenUrl: (affiliateIdentifier: string, offerCodeUrlId: string) => Promise<boolean>;
|
|
20
20
|
isInitialized: boolean;
|
|
21
21
|
};
|
|
22
22
|
export declare const DeepLinkIapContext: React.Context<T_DEEPLINK_IAP_CONTEXT>;
|
|
@@ -46,13 +46,11 @@ const ASYNC_KEYS = {
|
|
|
46
46
|
USER_ID: '@app_user_id',
|
|
47
47
|
COMPANY_CODE: '@app_company_code',
|
|
48
48
|
USER_ACCOUNT_TOKEN: '@app_user_account_token',
|
|
49
|
-
IOS_OFFER_CODE: '@app_ios_offer_code',
|
|
50
49
|
};
|
|
51
50
|
// STARTING CONTEXT IMPLEMENTATION
|
|
52
51
|
exports.DeepLinkIapContext = (0, react_1.createContext)({
|
|
53
52
|
referrerLink: '',
|
|
54
53
|
userId: '',
|
|
55
|
-
iOSOfferCode: null,
|
|
56
54
|
returnInsertAffiliateIdentifier: () => __awaiter(void 0, void 0, void 0, function* () { return ''; }),
|
|
57
55
|
validatePurchaseWithIapticAPI: (jsonIapPurchase, iapticAppId, iapticAppName, iapticPublicKey) => __awaiter(void 0, void 0, void 0, function* () { return false; }),
|
|
58
56
|
returnUserAccountTokenAndStoreExpectedTransaction: () => __awaiter(void 0, void 0, void 0, function* () { return ''; }),
|
|
@@ -61,6 +59,7 @@ exports.DeepLinkIapContext = (0, react_1.createContext)({
|
|
|
61
59
|
setShortCode: (shortCode) => __awaiter(void 0, void 0, void 0, function* () { }),
|
|
62
60
|
setInsertAffiliateIdentifier: (referringLink) => __awaiter(void 0, void 0, void 0, function* () { }),
|
|
63
61
|
initialize: (code, verboseLogging) => __awaiter(void 0, void 0, void 0, function* () { }),
|
|
62
|
+
fetchAndConditionallyOpenUrl: (affiliateIdentifier, offerCodeUrlId) => __awaiter(void 0, void 0, void 0, function* () { return false; }),
|
|
64
63
|
isInitialized: false,
|
|
65
64
|
});
|
|
66
65
|
const DeepLinkIapProvider = ({ children, }) => {
|
|
@@ -69,7 +68,6 @@ const DeepLinkIapProvider = ({ children, }) => {
|
|
|
69
68
|
const [companyCode, setCompanyCode] = (0, react_1.useState)(null);
|
|
70
69
|
const [isInitialized, setIsInitialized] = (0, react_1.useState)(false);
|
|
71
70
|
const [verboseLogging, setVerboseLogging] = (0, react_1.useState)(false);
|
|
72
|
-
const [iOSOfferCode, setIOSOfferCode] = (0, react_1.useState)(null);
|
|
73
71
|
// MARK: Initialize the SDK
|
|
74
72
|
const initialize = (companyCode_1, ...args_1) => __awaiter(void 0, [companyCode_1, ...args_1], void 0, function* (companyCode, verboseLogging = false) {
|
|
75
73
|
setVerboseLogging(verboseLogging);
|
|
@@ -109,11 +107,9 @@ const DeepLinkIapProvider = ({ children, }) => {
|
|
|
109
107
|
const uId = yield getValueFromAsync(ASYNC_KEYS.USER_ID);
|
|
110
108
|
const refLink = yield getValueFromAsync(ASYNC_KEYS.REFERRER_LINK);
|
|
111
109
|
const companyCodeFromStorage = yield getValueFromAsync(ASYNC_KEYS.COMPANY_CODE);
|
|
112
|
-
const storedIOSOfferCode = yield getValueFromAsync(ASYNC_KEYS.IOS_OFFER_CODE);
|
|
113
110
|
verboseLog(`User ID found: ${uId ? 'Yes' : 'No'}`);
|
|
114
111
|
verboseLog(`Referrer link found: ${refLink ? 'Yes' : 'No'}`);
|
|
115
112
|
verboseLog(`Company code found: ${companyCodeFromStorage ? 'Yes' : 'No'}`);
|
|
116
|
-
verboseLog(`iOS Offer Code found: ${storedIOSOfferCode ? 'Yes' : 'No'}`);
|
|
117
113
|
if (uId && refLink) {
|
|
118
114
|
setUserId(uId);
|
|
119
115
|
setReferrerLink(refLink);
|
|
@@ -123,10 +119,6 @@ const DeepLinkIapProvider = ({ children, }) => {
|
|
|
123
119
|
setCompanyCode(companyCodeFromStorage);
|
|
124
120
|
verboseLog('Company code restored from storage');
|
|
125
121
|
}
|
|
126
|
-
if (storedIOSOfferCode) {
|
|
127
|
-
setIOSOfferCode(storedIOSOfferCode);
|
|
128
|
-
verboseLog('iOS Offer Code restored from storage');
|
|
129
|
-
}
|
|
130
122
|
}
|
|
131
123
|
catch (error) {
|
|
132
124
|
errorLog(`ERROR ~ fetchAsyncEssentials: ${error}`);
|
|
@@ -217,9 +209,9 @@ const DeepLinkIapProvider = ({ children, }) => {
|
|
|
217
209
|
};
|
|
218
210
|
// MARK: Short Codes
|
|
219
211
|
const isShortCode = (referringLink) => {
|
|
220
|
-
// Short codes are
|
|
221
|
-
const isValidCharacters = /^[a-zA-Z0-
|
|
222
|
-
return isValidCharacters && referringLink.length
|
|
212
|
+
// Short codes are less than 10 characters
|
|
213
|
+
const isValidCharacters = /^[a-zA-Z0-9]+$/.test(referringLink);
|
|
214
|
+
return isValidCharacters && referringLink.length < 25 && referringLink.length > 3;
|
|
223
215
|
};
|
|
224
216
|
function setShortCode(shortCode) {
|
|
225
217
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -384,9 +376,6 @@ const DeepLinkIapProvider = ({ children, }) => {
|
|
|
384
376
|
verboseLog(`Saving referrer link to AsyncStorage...`);
|
|
385
377
|
yield saveValueInAsync(ASYNC_KEYS.REFERRER_LINK, link);
|
|
386
378
|
verboseLog(`Referrer link saved to AsyncStorage successfully`);
|
|
387
|
-
// Automatically fetch and store offer code for any affiliate identifier
|
|
388
|
-
verboseLog('Attempting to fetch offer code for stored affiliate identifier...');
|
|
389
|
-
yield retrieveAndStoreOfferCode(link);
|
|
390
379
|
});
|
|
391
380
|
}
|
|
392
381
|
const validatePurchaseWithIapticAPI = (jsonIapPurchase, iapticAppId, iapticAppName, iapticPublicKey) => __awaiter(void 0, void 0, void 0, function* () {
|
|
@@ -541,15 +530,34 @@ const DeepLinkIapProvider = ({ children, }) => {
|
|
|
541
530
|
return Promise.reject(error);
|
|
542
531
|
}
|
|
543
532
|
});
|
|
544
|
-
const
|
|
533
|
+
const fetchAndConditionallyOpenUrl = (affiliateIdentifier, offerCodeUrlId) => __awaiter(void 0, void 0, void 0, function* () {
|
|
545
534
|
try {
|
|
546
|
-
|
|
547
|
-
if (
|
|
548
|
-
|
|
549
|
-
|
|
535
|
+
verboseLog(`Attempting to fetch and conditionally open URL for affiliate: ${affiliateIdentifier}, offerCodeUrlId: ${offerCodeUrlId}`);
|
|
536
|
+
if (react_native_1.Platform.OS !== 'ios') {
|
|
537
|
+
console.warn("[Insert Affiliate] Offer codes are only supported on iOS");
|
|
538
|
+
verboseLog("Offer codes are only supported on iOS");
|
|
539
|
+
return false;
|
|
550
540
|
}
|
|
541
|
+
const offerCode = yield fetchOfferCode(affiliateIdentifier);
|
|
542
|
+
if (offerCode && offerCode.length > 0) {
|
|
543
|
+
yield openRedeemURL(offerCode, offerCodeUrlId);
|
|
544
|
+
return true;
|
|
545
|
+
}
|
|
546
|
+
else {
|
|
547
|
+
verboseLog("No valid offer code found");
|
|
548
|
+
return false;
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
catch (error) {
|
|
552
|
+
console.error('[Insert Affiliate] Error fetching and opening offer code URL:', error);
|
|
553
|
+
verboseLog(`Error fetching and opening offer code URL: ${error}`);
|
|
554
|
+
return false;
|
|
555
|
+
}
|
|
556
|
+
});
|
|
557
|
+
const fetchOfferCode = (affiliateLink) => __awaiter(void 0, void 0, void 0, function* () {
|
|
558
|
+
try {
|
|
551
559
|
const encodedAffiliateLink = encodeURIComponent(affiliateLink);
|
|
552
|
-
const url = `https://api.insertaffiliate.com/v1/affiliateReturnOfferCode/${
|
|
560
|
+
const url = `https://api.insertaffiliate.com/v1/affiliateReturnOfferCode/${encodedAffiliateLink}`;
|
|
553
561
|
verboseLog(`Fetching offer code from: ${url}`);
|
|
554
562
|
const response = yield axios_1.default.get(url);
|
|
555
563
|
if (response.status === 200) {
|
|
@@ -579,41 +587,33 @@ const DeepLinkIapProvider = ({ children, }) => {
|
|
|
579
587
|
return null;
|
|
580
588
|
}
|
|
581
589
|
});
|
|
582
|
-
const
|
|
590
|
+
const openRedeemURL = (offerCode, offerCodeUrlId) => __awaiter(void 0, void 0, void 0, function* () {
|
|
583
591
|
try {
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
yield
|
|
589
|
-
|
|
590
|
-
verboseLog(
|
|
591
|
-
console.log('[Insert Affiliate] Offer code retrieved and stored successfully');
|
|
592
|
+
const redeemUrl = `https://apps.apple.com/redeem?ctx=offercodes&id=${offerCodeUrlId}&code=${offerCode}`;
|
|
593
|
+
verboseLog(`Opening redeem URL: ${redeemUrl}`);
|
|
594
|
+
const canOpen = yield react_native_1.Linking.canOpenURL(redeemUrl);
|
|
595
|
+
if (canOpen) {
|
|
596
|
+
yield react_native_1.Linking.openURL(redeemUrl);
|
|
597
|
+
console.log('[Insert Affiliate] Successfully opened redeem URL');
|
|
598
|
+
verboseLog('Successfully opened redeem URL');
|
|
592
599
|
}
|
|
593
600
|
else {
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
yield saveValueInAsync(ASYNC_KEYS.IOS_OFFER_CODE, '');
|
|
597
|
-
setIOSOfferCode(null);
|
|
601
|
+
console.error(`[Insert Affiliate] Could not launch redeem URL: ${redeemUrl}`);
|
|
602
|
+
verboseLog(`Could not launch redeem URL: ${redeemUrl}`);
|
|
598
603
|
}
|
|
599
604
|
}
|
|
600
605
|
catch (error) {
|
|
601
|
-
console.error('[Insert Affiliate] Error
|
|
602
|
-
verboseLog(`Error
|
|
606
|
+
console.error('[Insert Affiliate] Error opening redeem URL:', error);
|
|
607
|
+
verboseLog(`Error opening redeem URL: ${error}`);
|
|
603
608
|
}
|
|
604
609
|
});
|
|
605
|
-
const removeSpecialCharacters = (offerCode) => {
|
|
606
|
-
// Remove special characters, keep only alphanumeric and underscores
|
|
607
|
-
return offerCode.replace(/[^a-zA-Z0-9_]/g, '');
|
|
608
|
-
};
|
|
609
610
|
const cleanOfferCode = (offerCode) => {
|
|
610
611
|
// Remove special characters, keep only alphanumeric
|
|
611
|
-
return
|
|
612
|
+
return offerCode.replace(/[^a-zA-Z0-9]/g, '');
|
|
612
613
|
};
|
|
613
614
|
return (react_1.default.createElement(exports.DeepLinkIapContext.Provider, { value: {
|
|
614
615
|
referrerLink,
|
|
615
616
|
userId,
|
|
616
|
-
iOSOfferCode,
|
|
617
617
|
setShortCode,
|
|
618
618
|
returnInsertAffiliateIdentifier,
|
|
619
619
|
storeExpectedStoreTransaction,
|
|
@@ -623,6 +623,7 @@ const DeepLinkIapProvider = ({ children, }) => {
|
|
|
623
623
|
setInsertAffiliateIdentifier,
|
|
624
624
|
initialize,
|
|
625
625
|
isInitialized,
|
|
626
|
+
fetchAndConditionallyOpenUrl,
|
|
626
627
|
} }, children));
|
|
627
628
|
};
|
|
628
629
|
exports.default = DeepLinkIapProvider;
|
|
@@ -12,6 +12,6 @@ declare const useDeepLinkIapProvider: () => {
|
|
|
12
12
|
setInsertAffiliateIdentifier: (referringLink: string) => Promise<void | string>;
|
|
13
13
|
initialize: (code: string | null, verboseLogging?: boolean) => Promise<void>;
|
|
14
14
|
isInitialized: boolean;
|
|
15
|
-
|
|
15
|
+
fetchAndConditionallyOpenUrl: (affiliateIdentifier: string, offerCodeUrlId: string) => Promise<boolean>;
|
|
16
16
|
};
|
|
17
17
|
export default useDeepLinkIapProvider;
|
|
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
const react_1 = require("react");
|
|
4
4
|
const DeepLinkIapProvider_1 = require("./DeepLinkIapProvider");
|
|
5
5
|
const useDeepLinkIapProvider = () => {
|
|
6
|
-
const { referrerLink, userId, validatePurchaseWithIapticAPI, storeExpectedStoreTransaction, returnUserAccountTokenAndStoreExpectedTransaction, returnInsertAffiliateIdentifier, trackEvent, setShortCode, setInsertAffiliateIdentifier, initialize, isInitialized,
|
|
6
|
+
const { referrerLink, userId, validatePurchaseWithIapticAPI, storeExpectedStoreTransaction, returnUserAccountTokenAndStoreExpectedTransaction, returnInsertAffiliateIdentifier, trackEvent, setShortCode, setInsertAffiliateIdentifier, initialize, isInitialized, fetchAndConditionallyOpenUrl } = (0, react_1.useContext)(DeepLinkIapProvider_1.DeepLinkIapContext);
|
|
7
7
|
return {
|
|
8
8
|
referrerLink,
|
|
9
9
|
userId,
|
|
@@ -16,7 +16,7 @@ const useDeepLinkIapProvider = () => {
|
|
|
16
16
|
setInsertAffiliateIdentifier,
|
|
17
17
|
initialize,
|
|
18
18
|
isInitialized,
|
|
19
|
-
|
|
19
|
+
fetchAndConditionallyOpenUrl
|
|
20
20
|
};
|
|
21
21
|
};
|
|
22
22
|
exports.default = useDeepLinkIapProvider;
|
package/package.json
CHANGED
package/readme.md
CHANGED
|
@@ -252,7 +252,7 @@ const Child = () => {
|
|
|
252
252
|
<Button
|
|
253
253
|
disabled={iapLoading}
|
|
254
254
|
title={`Click to Buy Subscription`}
|
|
255
|
-
onPress={() => handleBuySubscription("
|
|
255
|
+
onPress={() => handleBuySubscription("oneMonthSubscriptionTwo")}
|
|
256
256
|
/>
|
|
257
257
|
{iapLoading && <ActivityIndicator size={"small"} color={"black"} />}
|
|
258
258
|
</View>
|
|
@@ -520,301 +520,131 @@ const {
|
|
|
520
520
|
/>
|
|
521
521
|
```
|
|
522
522
|
|
|
523
|
-
### 2.
|
|
523
|
+
### 2. Offer Codes
|
|
524
524
|
|
|
525
|
-
|
|
525
|
+
Offer Codes allow you to automatically present a discount to users who access an affiliate's link or enter a short code. This provides affiliates with a compelling incentive to promote your app, as discounts are automatically applied during the redemption flow [(learn more)](https://docs.insertaffiliate.com/offer-codes).
|
|
526
526
|
|
|
527
|
-
|
|
527
|
+
**Note: Offer Codes are currently only supported on iOS.**
|
|
528
528
|
|
|
529
|
-
|
|
529
|
+
You'll need your Offer Code URL ID, which can be created and retrieved from App Store Connect. Instructions to retrieve your Offer Code URL ID are available [here](https://docs.insertaffiliate.com/offer-codes#create-the-codes-within-app-store-connect).
|
|
530
530
|
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
#### Basic Usage
|
|
535
|
-
|
|
536
|
-
##### 1. Automatic Offer Code Fetching
|
|
537
|
-
If an affiliate short code is stored, the SDK automatically fetches and saves the associated offer code modifier.
|
|
538
|
-
|
|
539
|
-
##### 2. Access the Offer Code Modifier
|
|
540
|
-
The offer code modifier is available through the context:
|
|
531
|
+
To fetch an Offer Code and conditionally redirect the user to redeem it, pass the affiliate identifier (deep link or short code) to:
|
|
541
532
|
|
|
542
533
|
```javascript
|
|
543
|
-
const {
|
|
544
|
-
```
|
|
545
|
-
|
|
546
|
-
##### Setup Requirements
|
|
547
|
-
|
|
548
|
-
#### App Store Connect Configuration
|
|
549
|
-
1. Create both a base and a promotional product:
|
|
550
|
-
- Base product: `oneMonthSubscription`
|
|
551
|
-
- Promo product: `oneMonthSubscription_oneWeekFree`
|
|
552
|
-
2. Ensure **both** products are approved and available for sale.
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
**Product Naming Pattern:**
|
|
556
|
-
- Follow the pattern: `{baseProductId}{iOSOfferCode}`
|
|
557
|
-
- Example: `oneMonthSubscription` + `_oneWeekFree` = `oneMonthSubscription_oneWeekFree`
|
|
558
|
-
|
|
559
|
-
---
|
|
560
|
-
|
|
561
|
-
#### RevenueCat Dashboard Configuration
|
|
534
|
+
const { fetchAndConditionallyOpenUrl } = useDeepLinkIapProvider();
|
|
562
535
|
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
- Base offering: `premium_monthly`
|
|
566
|
-
- Modified offering: `premium_monthly_oneWeekFree`
|
|
567
|
-
|
|
568
|
-
2. Add both product IDs under different offerings in RevenueCat.
|
|
569
|
-
|
|
570
|
-
3. Ensure modified products follow this naming pattern: {baseProductId}_{cleanOfferCode}. e.g. premium_monthly_oneWeekFree
|
|
536
|
+
await fetchAndConditionallyOpenUrl("your_affiliate_identifier", "your_offer_code_url_id");
|
|
537
|
+
```
|
|
571
538
|
|
|
539
|
+
#### Branch.io Example
|
|
572
540
|
|
|
573
|
-
### Integration Example
|
|
574
541
|
```javascript
|
|
575
|
-
import React, { useEffect
|
|
576
|
-
import
|
|
542
|
+
import React, { useEffect } from 'react';
|
|
543
|
+
import branch from 'react-native-branch';
|
|
577
544
|
import { useDeepLinkIapProvider } from 'insert-affiliate-react-native-sdk';
|
|
578
|
-
import Purchases from 'react-native-purchases';
|
|
579
|
-
|
|
580
|
-
const PurchaseHandler = () => {
|
|
581
|
-
const { iOSOfferCode } = useDeepLinkIapProvider();
|
|
582
|
-
const [subscriptions, setSubscriptions] = useState([]);
|
|
583
|
-
|
|
584
|
-
const fetchSubscriptions = async () => {
|
|
585
|
-
const offerings = await Purchases.getOfferings();
|
|
586
|
-
let packagesToUse = [];
|
|
587
|
-
|
|
588
|
-
if (iOSOfferCode) {
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
// Construct modified product IDs from base products
|
|
592
|
-
const baseProducts = offerings.current.availablePackages;
|
|
593
|
-
|
|
594
|
-
for (const basePackage of baseProducts) {
|
|
595
|
-
const baseProductId = basePackage.product.identifier;
|
|
596
|
-
const modifiedProductId = `${baseProductId}_${iOSOfferCode}`;
|
|
597
545
|
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
546
|
+
const DeepLinkHandler = () => {
|
|
547
|
+
const { fetchAndConditionallyOpenUrl } = useDeepLinkIapProvider();
|
|
548
|
+
|
|
549
|
+
useEffect(() => {
|
|
550
|
+
const branchSubscription = branch.subscribe(async ({ error, params }) => {
|
|
551
|
+
if (error) {
|
|
552
|
+
console.error('Error from Branch:', error);
|
|
553
|
+
return;
|
|
554
|
+
}
|
|
601
555
|
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
556
|
+
if (params['+clicked_branch_link']) {
|
|
557
|
+
const referringLink = params['~referring_link'];
|
|
558
|
+
if (referringLink) {
|
|
559
|
+
try {
|
|
560
|
+
await fetchAndConditionallyOpenUrl(
|
|
561
|
+
referringLink,
|
|
562
|
+
"{{ your_offer_code_url_id }}"
|
|
563
|
+
);
|
|
606
564
|
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
break;
|
|
565
|
+
// Other code required for Insert Affiliate in the other listed steps...
|
|
566
|
+
} catch (err) {
|
|
567
|
+
console.error('Error with offer code:', err);
|
|
611
568
|
}
|
|
612
569
|
}
|
|
613
|
-
|
|
614
|
-
// Fallback to base product if no modified version
|
|
615
|
-
if (!foundModified) {
|
|
616
|
-
packagesToUse.push(basePackage);
|
|
617
|
-
}
|
|
618
570
|
}
|
|
619
|
-
}
|
|
620
|
-
packagesToUse = offerings.current.availablePackages;
|
|
621
|
-
}
|
|
622
|
-
|
|
623
|
-
setSubscriptions(packagesToUse);
|
|
624
|
-
};
|
|
625
|
-
|
|
626
|
-
const handlePurchase = async (subscriptionPackage) => {
|
|
627
|
-
try {
|
|
628
|
-
await Purchases.purchasePackage(subscriptionPackage);
|
|
629
|
-
} catch (error) {
|
|
630
|
-
console.error('Purchase failed:', error);
|
|
631
|
-
}
|
|
632
|
-
};
|
|
571
|
+
});
|
|
633
572
|
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
}, [iOSOfferCode]);
|
|
573
|
+
return () => branchSubscription();
|
|
574
|
+
}, [fetchAndConditionallyOpenUrl]);
|
|
637
575
|
|
|
638
|
-
return
|
|
639
|
-
<View>
|
|
640
|
-
{subscriptions.map((pkg) => (
|
|
641
|
-
<Button
|
|
642
|
-
key={pkg.identifier}
|
|
643
|
-
title={`Buy: ${pkg.product.identifier}`}
|
|
644
|
-
onPress={() => handlePurchase(pkg)}
|
|
645
|
-
/>
|
|
646
|
-
))}
|
|
647
|
-
{iOSOfferCode && (
|
|
648
|
-
<Text>Special offer applied: {iOSOfferCode}</Text>
|
|
649
|
-
)}
|
|
650
|
-
</View>
|
|
651
|
-
);
|
|
576
|
+
return <App />;
|
|
652
577
|
};
|
|
653
578
|
```
|
|
654
|
-
---
|
|
655
579
|
|
|
656
|
-
####
|
|
657
|
-
|
|
658
|
-
For apps using `react-native-iap` directly:
|
|
580
|
+
#### Short Code Example
|
|
659
581
|
|
|
660
582
|
```javascript
|
|
661
|
-
import React, { useState
|
|
662
|
-
import { View,
|
|
583
|
+
import React, { useState } from 'react';
|
|
584
|
+
import { View, TextInput, Button, StyleSheet } from 'react-native';
|
|
663
585
|
import { useDeepLinkIapProvider } from 'insert-affiliate-react-native-sdk';
|
|
664
|
-
import {
|
|
665
|
-
initConnection,
|
|
666
|
-
getSubscriptions,
|
|
667
|
-
requestSubscription,
|
|
668
|
-
useIAP
|
|
669
|
-
} from 'react-native-iap';
|
|
670
|
-
|
|
671
|
-
const NativeIAPPurchaseView = () => {
|
|
672
|
-
const { iOSOfferCode, returnUserAccountTokenAndStoreExpectedTransaction } = useDeepLinkIapProvider();
|
|
673
|
-
const [availableProducts, setAvailableProducts] = useState([]);
|
|
674
|
-
const [loading, setLoading] = useState(false);
|
|
675
|
-
const { currentPurchase, connected } = useIAP();
|
|
676
|
-
|
|
677
|
-
const baseProductIdentifier = "oneMonthSubscription";
|
|
678
|
-
|
|
679
|
-
// Dynamic product identifier that includes offer code
|
|
680
|
-
const dynamicProductIdentifier = iOSOfferCode
|
|
681
|
-
? `${baseProductIdentifier}${iOSOfferCode}` // e.g., "oneMonthSubscription_oneWeekFree"
|
|
682
|
-
: baseProductIdentifier;
|
|
683
586
|
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
if (iOSOfferCode) {
|
|
693
|
-
productIds.push(baseProductIdentifier);
|
|
694
|
-
}
|
|
695
|
-
|
|
696
|
-
const products = await getSubscriptions({ skus: productIds });
|
|
697
|
-
|
|
698
|
-
// Prioritize the dynamic product if it exists
|
|
699
|
-
let sortedProducts = products;
|
|
700
|
-
if (iOSOfferCode && products.length > 1) {
|
|
701
|
-
sortedProducts = products.sort((a, b) =>
|
|
702
|
-
a.productId === dynamicProductIdentifier ? -1 : 1
|
|
703
|
-
);
|
|
704
|
-
}
|
|
705
|
-
|
|
706
|
-
setAvailableProducts(sortedProducts);
|
|
707
|
-
console.log(`Loaded products for: ${productIds.join(', ')}`);
|
|
708
|
-
|
|
709
|
-
} catch (error) {
|
|
587
|
+
const ShortCodeInputWidget = () => {
|
|
588
|
+
const [shortCode, setShortCode] = useState('');
|
|
589
|
+
const { setShortCode: setInsertAffiliateShortCode, fetchAndConditionallyOpenUrl } = useDeepLinkIapProvider();
|
|
590
|
+
|
|
591
|
+
const handleShortCodeSubmission = async () => {
|
|
592
|
+
const trimmedCode = shortCode.trim();
|
|
593
|
+
|
|
594
|
+
if (trimmedCode.length > 0) {
|
|
710
595
|
try {
|
|
711
|
-
//
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
596
|
+
// Set the short code for affiliate tracking
|
|
597
|
+
await setInsertAffiliateShortCode(trimmedCode);
|
|
598
|
+
|
|
599
|
+
// Fetch and conditionally open offer code URL
|
|
600
|
+
await fetchAndConditionallyOpenUrl(
|
|
601
|
+
trimmedCode,
|
|
602
|
+
"{{ your_offer_code_url_id }}"
|
|
603
|
+
);
|
|
604
|
+
} catch (error) {
|
|
605
|
+
console.error('Error handling short code:', error);
|
|
716
606
|
}
|
|
717
|
-
} finally {
|
|
718
|
-
setLoading(false);
|
|
719
607
|
}
|
|
720
608
|
};
|
|
721
609
|
|
|
722
|
-
const handlePurchase = async (productId) => {
|
|
723
|
-
// Implement the purchase handling logic as outlined in the remaining SDK integration steps.
|
|
724
|
-
};
|
|
725
|
-
|
|
726
|
-
useEffect(() => {
|
|
727
|
-
if (connected) {
|
|
728
|
-
fetchProducts();
|
|
729
|
-
}
|
|
730
|
-
}, [connected, iOSOfferCode]);;
|
|
731
|
-
|
|
732
|
-
const primaryProduct = availableProducts[0];
|
|
733
|
-
|
|
734
610
|
return (
|
|
735
|
-
<View style={
|
|
736
|
-
<
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
{loading ? (
|
|
749
|
-
<Text>Loading products...</Text>
|
|
750
|
-
) : primaryProduct ? (
|
|
751
|
-
<View>
|
|
752
|
-
<Text style={{ fontSize: 16, marginBottom: 5 }}>
|
|
753
|
-
{primaryProduct.title}
|
|
754
|
-
</Text>
|
|
755
|
-
<Text style={{ fontSize: 14, color: '#666', marginBottom: 5 }}>
|
|
756
|
-
Price: {primaryProduct.localizedPrice}
|
|
757
|
-
</Text>
|
|
758
|
-
<Text style={{ fontSize: 12, color: '#999', marginBottom: 15 }}>
|
|
759
|
-
Product ID: {primaryProduct.productId}
|
|
760
|
-
</Text>
|
|
761
|
-
|
|
762
|
-
<Button
|
|
763
|
-
title={loading ? "Processing..." : "Subscribe Now"}
|
|
764
|
-
onPress={() => handlePurchase(primaryProduct.productId)}
|
|
765
|
-
disabled={loading}
|
|
766
|
-
/>
|
|
767
|
-
|
|
768
|
-
{primaryProduct.productId === dynamicProductIdentifier && iOSOfferCode && (
|
|
769
|
-
<Text style={{ fontSize: 12, color: '#4caf50', marginTop: 10 }}>
|
|
770
|
-
✓ Promotional pricing applied
|
|
771
|
-
</Text>
|
|
772
|
-
)}
|
|
773
|
-
</View>
|
|
774
|
-
) : (
|
|
775
|
-
<View>
|
|
776
|
-
<Text style={{ color: '#f44336', marginBottom: 10 }}>
|
|
777
|
-
Product not found: {dynamicProductIdentifier}
|
|
778
|
-
</Text>
|
|
779
|
-
<Button
|
|
780
|
-
title="Retry"
|
|
781
|
-
onPress={fetchProducts}
|
|
782
|
-
/>
|
|
783
|
-
</View>
|
|
784
|
-
)}
|
|
785
|
-
|
|
786
|
-
{availableProducts.length > 1 && (
|
|
787
|
-
<View style={{ marginTop: 20 }}>
|
|
788
|
-
<Text style={{ fontSize: 14, fontWeight: 'bold', marginBottom: 10 }}>
|
|
789
|
-
Other Options:
|
|
790
|
-
</Text>
|
|
791
|
-
{availableProducts.slice(1).map((product) => (
|
|
792
|
-
<Button
|
|
793
|
-
key={product.productId}
|
|
794
|
-
title={`${product.title} - ${product.localizedPrice}`}
|
|
795
|
-
onPress={() => handlePurchase(product.productId)}
|
|
796
|
-
/>
|
|
797
|
-
))}
|
|
798
|
-
</View>
|
|
799
|
-
)}
|
|
611
|
+
<View style={styles.container}>
|
|
612
|
+
<TextInput
|
|
613
|
+
style={styles.input}
|
|
614
|
+
value={shortCode}
|
|
615
|
+
onChangeText={setShortCode}
|
|
616
|
+
placeholder="Enter your code"
|
|
617
|
+
placeholderTextColor="#ABC123"
|
|
618
|
+
/>
|
|
619
|
+
<Button
|
|
620
|
+
title="Apply Code"
|
|
621
|
+
onPress={handleShortCodeSubmission}
|
|
622
|
+
/>
|
|
800
623
|
</View>
|
|
801
624
|
);
|
|
802
625
|
};
|
|
803
|
-
```
|
|
804
|
-
|
|
805
|
-
##### Key Features of Native IAP Integration:
|
|
806
|
-
|
|
807
|
-
1. **Dynamic Product Loading**: Automatically constructs product IDs using the offer code modifier
|
|
808
|
-
2. **Fallback Strategy**: If the promotional product isn't found, falls back to the base product
|
|
809
|
-
3. **Visual Feedback**: Shows users when promotional pricing is applied
|
|
810
|
-
4. **Error Handling**: Graceful handling when products aren't available
|
|
811
626
|
|
|
627
|
+
const styles = StyleSheet.create({
|
|
628
|
+
container: {
|
|
629
|
+
padding: 20,
|
|
630
|
+
},
|
|
631
|
+
input: {
|
|
632
|
+
borderWidth: 1,
|
|
633
|
+
borderColor: '#ddd',
|
|
634
|
+
padding: 10,
|
|
635
|
+
marginBottom: 10,
|
|
636
|
+
borderRadius: 5,
|
|
637
|
+
},
|
|
638
|
+
});
|
|
639
|
+
|
|
640
|
+
export default ShortCodeInputWidget;
|
|
641
|
+
```
|
|
812
642
|
|
|
813
643
|
### 3. Short Codes (Beta)
|
|
814
644
|
|
|
815
645
|
#### What are Short Codes?
|
|
816
646
|
|
|
817
|
-
Short codes are unique identifiers that affiliates can use to promote products or subscriptions. These codes are ideal for influencers or partners, making them easier to share than long URLs.
|
|
647
|
+
Short codes are unique, 10-character alphanumeric identifiers that affiliates can use to promote products or subscriptions. These codes are ideal for influencers or partners, making them easier to share than long URLs.
|
|
818
648
|
|
|
819
649
|
**Example Use Case**: An influencer promotes a subscription with the short code "JOIN12345" within their TikTok video's description. When users enter this code within your app during sign-up or before purchase, the app tracks the subscription back to the influencer for commission payouts.
|
|
820
650
|
|
|
@@ -825,12 +655,10 @@ For more information, visit the [Insert Affiliate Short Codes Documentation](htt
|
|
|
825
655
|
Use the `setShortCode` method to associate a short code with an affiliate. This is ideal for scenarios where users enter the code via an input field, pop-up, or similar UI element.
|
|
826
656
|
|
|
827
657
|
Short codes must meet the following criteria:
|
|
828
|
-
-
|
|
829
|
-
- Contain only **letters
|
|
658
|
+
- Exactly **10 characters long**.
|
|
659
|
+
- Contain only **letters and numbers** (alphanumeric characters).
|
|
830
660
|
- Replace {{ user_entered_short_code }} with the short code the user enters through your chosen input method, i.e. an input field / pop up element
|
|
831
661
|
|
|
832
|
-
When a short code is set, the SDK automatically attempts to fetch and store any associated offer codes for iOS users.
|
|
833
|
-
|
|
834
662
|
```javascript
|
|
835
663
|
import {
|
|
836
664
|
DeepLinkIapProvider,
|
|
@@ -842,6 +670,6 @@ When a short code is set, the SDK automatically attempts to fetch and store any
|
|
|
842
670
|
|
|
843
671
|
<Button
|
|
844
672
|
title={'Set Short Code'}
|
|
845
|
-
onPress={() => setShortCode('
|
|
673
|
+
onPress={() => setShortCode('JOIN123456')}
|
|
846
674
|
/>
|
|
847
675
|
```
|
|
@@ -15,7 +15,6 @@ type CustomPurchase = {
|
|
|
15
15
|
type T_DEEPLINK_IAP_CONTEXT = {
|
|
16
16
|
referrerLink: string;
|
|
17
17
|
userId: string;
|
|
18
|
-
iOSOfferCode: string | null;
|
|
19
18
|
returnInsertAffiliateIdentifier: () => Promise<string | null>;
|
|
20
19
|
validatePurchaseWithIapticAPI: (
|
|
21
20
|
jsonIapPurchase: CustomPurchase,
|
|
@@ -33,6 +32,10 @@ type T_DEEPLINK_IAP_CONTEXT = {
|
|
|
33
32
|
referringLink: string
|
|
34
33
|
) => Promise<void | string>;
|
|
35
34
|
initialize: (code: string | null, verboseLogging?: boolean) => Promise<void>;
|
|
35
|
+
fetchAndConditionallyOpenUrl: (
|
|
36
|
+
affiliateIdentifier: string,
|
|
37
|
+
offerCodeUrlId: string
|
|
38
|
+
) => Promise<boolean>;
|
|
36
39
|
isInitialized: boolean;
|
|
37
40
|
};
|
|
38
41
|
|
|
@@ -58,14 +61,12 @@ const ASYNC_KEYS = {
|
|
|
58
61
|
USER_ID: '@app_user_id',
|
|
59
62
|
COMPANY_CODE: '@app_company_code',
|
|
60
63
|
USER_ACCOUNT_TOKEN: '@app_user_account_token',
|
|
61
|
-
IOS_OFFER_CODE: '@app_ios_offer_code',
|
|
62
64
|
};
|
|
63
65
|
|
|
64
66
|
// STARTING CONTEXT IMPLEMENTATION
|
|
65
67
|
export const DeepLinkIapContext = createContext<T_DEEPLINK_IAP_CONTEXT>({
|
|
66
68
|
referrerLink: '',
|
|
67
69
|
userId: '',
|
|
68
|
-
iOSOfferCode: null,
|
|
69
70
|
returnInsertAffiliateIdentifier: async () => '',
|
|
70
71
|
validatePurchaseWithIapticAPI: async (
|
|
71
72
|
jsonIapPurchase: CustomPurchase,
|
|
@@ -79,6 +80,10 @@ export const DeepLinkIapContext = createContext<T_DEEPLINK_IAP_CONTEXT>({
|
|
|
79
80
|
setShortCode: async (shortCode: string) => {},
|
|
80
81
|
setInsertAffiliateIdentifier: async (referringLink: string) => {},
|
|
81
82
|
initialize: async (code: string | null, verboseLogging?: boolean) => {},
|
|
83
|
+
fetchAndConditionallyOpenUrl: async (
|
|
84
|
+
affiliateIdentifier: string,
|
|
85
|
+
offerCodeUrlId: string
|
|
86
|
+
) => false,
|
|
82
87
|
isInitialized: false,
|
|
83
88
|
});
|
|
84
89
|
|
|
@@ -90,7 +95,6 @@ const DeepLinkIapProvider: React.FC<T_DEEPLINK_IAP_PROVIDER> = ({
|
|
|
90
95
|
const [companyCode, setCompanyCode] = useState<string | null>(null);
|
|
91
96
|
const [isInitialized, setIsInitialized] = useState<boolean>(false);
|
|
92
97
|
const [verboseLogging, setVerboseLogging] = useState<boolean>(false);
|
|
93
|
-
const [iOSOfferCode, setIOSOfferCode] = useState<string | null>(null);
|
|
94
98
|
|
|
95
99
|
// MARK: Initialize the SDK
|
|
96
100
|
const initialize = async (companyCode: string | null, verboseLogging: boolean = false): Promise<void> => {
|
|
@@ -138,12 +142,10 @@ const DeepLinkIapProvider: React.FC<T_DEEPLINK_IAP_PROVIDER> = ({
|
|
|
138
142
|
const uId = await getValueFromAsync(ASYNC_KEYS.USER_ID);
|
|
139
143
|
const refLink = await getValueFromAsync(ASYNC_KEYS.REFERRER_LINK);
|
|
140
144
|
const companyCodeFromStorage = await getValueFromAsync(ASYNC_KEYS.COMPANY_CODE);
|
|
141
|
-
const storedIOSOfferCode = await getValueFromAsync(ASYNC_KEYS.IOS_OFFER_CODE);
|
|
142
145
|
|
|
143
146
|
verboseLog(`User ID found: ${uId ? 'Yes' : 'No'}`);
|
|
144
147
|
verboseLog(`Referrer link found: ${refLink ? 'Yes' : 'No'}`);
|
|
145
148
|
verboseLog(`Company code found: ${companyCodeFromStorage ? 'Yes' : 'No'}`);
|
|
146
|
-
verboseLog(`iOS Offer Code found: ${storedIOSOfferCode ? 'Yes' : 'No'}`);
|
|
147
149
|
|
|
148
150
|
if (uId && refLink) {
|
|
149
151
|
setUserId(uId);
|
|
@@ -155,11 +157,6 @@ const DeepLinkIapProvider: React.FC<T_DEEPLINK_IAP_PROVIDER> = ({
|
|
|
155
157
|
setCompanyCode(companyCodeFromStorage);
|
|
156
158
|
verboseLog('Company code restored from storage');
|
|
157
159
|
}
|
|
158
|
-
|
|
159
|
-
if (storedIOSOfferCode) {
|
|
160
|
-
setIOSOfferCode(storedIOSOfferCode);
|
|
161
|
-
verboseLog('iOS Offer Code restored from storage');
|
|
162
|
-
}
|
|
163
160
|
} catch (error) {
|
|
164
161
|
errorLog(`ERROR ~ fetchAsyncEssentials: ${error}`);
|
|
165
162
|
verboseLog(`Error loading from AsyncStorage: ${error}`);
|
|
@@ -262,9 +259,9 @@ const DeepLinkIapProvider: React.FC<T_DEEPLINK_IAP_PROVIDER> = ({
|
|
|
262
259
|
|
|
263
260
|
// MARK: Short Codes
|
|
264
261
|
const isShortCode = (referringLink: string): boolean => {
|
|
265
|
-
// Short codes are
|
|
266
|
-
const isValidCharacters = /^[a-zA-Z0-
|
|
267
|
-
return isValidCharacters && referringLink.length
|
|
262
|
+
// Short codes are less than 10 characters
|
|
263
|
+
const isValidCharacters = /^[a-zA-Z0-9]+$/.test(referringLink);
|
|
264
|
+
return isValidCharacters && referringLink.length < 25 && referringLink.length > 3;
|
|
268
265
|
};
|
|
269
266
|
|
|
270
267
|
async function setShortCode(shortCode: string): Promise<void> {
|
|
@@ -452,10 +449,6 @@ const DeepLinkIapProvider: React.FC<T_DEEPLINK_IAP_PROVIDER> = ({
|
|
|
452
449
|
verboseLog(`Saving referrer link to AsyncStorage...`);
|
|
453
450
|
await saveValueInAsync(ASYNC_KEYS.REFERRER_LINK, link);
|
|
454
451
|
verboseLog(`Referrer link saved to AsyncStorage successfully`);
|
|
455
|
-
|
|
456
|
-
// Automatically fetch and store offer code for any affiliate identifier
|
|
457
|
-
verboseLog('Attempting to fetch offer code for stored affiliate identifier...');
|
|
458
|
-
await retrieveAndStoreOfferCode(link);
|
|
459
452
|
}
|
|
460
453
|
|
|
461
454
|
const validatePurchaseWithIapticAPI = async (
|
|
@@ -653,16 +646,41 @@ const DeepLinkIapProvider: React.FC<T_DEEPLINK_IAP_PROVIDER> = ({
|
|
|
653
646
|
}
|
|
654
647
|
};
|
|
655
648
|
|
|
656
|
-
const
|
|
649
|
+
const fetchAndConditionallyOpenUrl = async (
|
|
650
|
+
affiliateIdentifier: string,
|
|
651
|
+
offerCodeUrlId: string
|
|
652
|
+
): Promise<boolean> => {
|
|
657
653
|
try {
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
654
|
+
verboseLog(`Attempting to fetch and conditionally open URL for affiliate: ${affiliateIdentifier}, offerCodeUrlId: ${offerCodeUrlId}`);
|
|
655
|
+
|
|
656
|
+
if (Platform.OS !== 'ios') {
|
|
657
|
+
console.warn("[Insert Affiliate] Offer codes are only supported on iOS");
|
|
658
|
+
verboseLog("Offer codes are only supported on iOS");
|
|
659
|
+
return false;
|
|
662
660
|
}
|
|
663
661
|
|
|
662
|
+
const offerCode = await fetchOfferCode(affiliateIdentifier);
|
|
663
|
+
|
|
664
|
+
if (offerCode && offerCode.length > 0) {
|
|
665
|
+
await openRedeemURL(offerCode, offerCodeUrlId);
|
|
666
|
+
return true;
|
|
667
|
+
} else {
|
|
668
|
+
verboseLog("No valid offer code found");
|
|
669
|
+
return false;
|
|
670
|
+
}
|
|
671
|
+
} catch (error) {
|
|
672
|
+
console.error('[Insert Affiliate] Error fetching and opening offer code URL:', error);
|
|
673
|
+
verboseLog(`Error fetching and opening offer code URL: ${error}`);
|
|
674
|
+
return false;
|
|
675
|
+
}
|
|
676
|
+
};
|
|
677
|
+
|
|
678
|
+
const fetchOfferCode = async (affiliateLink: string): Promise<string | null> => {
|
|
679
|
+
try {
|
|
680
|
+
|
|
681
|
+
|
|
664
682
|
const encodedAffiliateLink = encodeURIComponent(affiliateLink);
|
|
665
|
-
const url = `https://api.insertaffiliate.com/v1/affiliateReturnOfferCode/${
|
|
683
|
+
const url = `https://api.insertaffiliate.com/v1/affiliateReturnOfferCode/${encodedAffiliateLink}`;
|
|
666
684
|
|
|
667
685
|
verboseLog(`Fetching offer code from: ${url}`);
|
|
668
686
|
|
|
@@ -698,38 +716,29 @@ const DeepLinkIapProvider: React.FC<T_DEEPLINK_IAP_PROVIDER> = ({
|
|
|
698
716
|
}
|
|
699
717
|
};
|
|
700
718
|
|
|
701
|
-
const
|
|
719
|
+
const openRedeemURL = async (offerCode: string, offerCodeUrlId: string): Promise<void> => {
|
|
702
720
|
try {
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
const offerCode = await fetchOfferCode(affiliateLink);
|
|
721
|
+
const redeemUrl = `https://apps.apple.com/redeem?ctx=offercodes&id=${offerCodeUrlId}&code=${offerCode}`;
|
|
722
|
+
verboseLog(`Opening redeem URL: ${redeemUrl}`);
|
|
706
723
|
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
await
|
|
710
|
-
|
|
711
|
-
verboseLog(
|
|
712
|
-
console.log('[Insert Affiliate] Offer code retrieved and stored successfully');
|
|
724
|
+
const canOpen = await Linking.canOpenURL(redeemUrl);
|
|
725
|
+
if (canOpen) {
|
|
726
|
+
await Linking.openURL(redeemUrl);
|
|
727
|
+
console.log('[Insert Affiliate] Successfully opened redeem URL');
|
|
728
|
+
verboseLog('Successfully opened redeem URL');
|
|
713
729
|
} else {
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
await saveValueInAsync(ASYNC_KEYS.IOS_OFFER_CODE, '');
|
|
717
|
-
setIOSOfferCode(null);
|
|
730
|
+
console.error(`[Insert Affiliate] Could not launch redeem URL: ${redeemUrl}`);
|
|
731
|
+
verboseLog(`Could not launch redeem URL: ${redeemUrl}`);
|
|
718
732
|
}
|
|
719
733
|
} catch (error) {
|
|
720
|
-
console.error('[Insert Affiliate] Error
|
|
721
|
-
verboseLog(`Error
|
|
734
|
+
console.error('[Insert Affiliate] Error opening redeem URL:', error);
|
|
735
|
+
verboseLog(`Error opening redeem URL: ${error}`);
|
|
722
736
|
}
|
|
723
737
|
};
|
|
724
738
|
|
|
725
|
-
const removeSpecialCharacters = (offerCode: string): string => {
|
|
726
|
-
// Remove special characters, keep only alphanumeric and underscores
|
|
727
|
-
return offerCode.replace(/[^a-zA-Z0-9_]/g, '');
|
|
728
|
-
};
|
|
729
|
-
|
|
730
739
|
const cleanOfferCode = (offerCode: string): string => {
|
|
731
740
|
// Remove special characters, keep only alphanumeric
|
|
732
|
-
return
|
|
741
|
+
return offerCode.replace(/[^a-zA-Z0-9]/g, '');
|
|
733
742
|
};
|
|
734
743
|
|
|
735
744
|
return (
|
|
@@ -737,7 +746,6 @@ const DeepLinkIapProvider: React.FC<T_DEEPLINK_IAP_PROVIDER> = ({
|
|
|
737
746
|
value={{
|
|
738
747
|
referrerLink,
|
|
739
748
|
userId,
|
|
740
|
-
iOSOfferCode,
|
|
741
749
|
setShortCode,
|
|
742
750
|
returnInsertAffiliateIdentifier,
|
|
743
751
|
storeExpectedStoreTransaction,
|
|
@@ -747,6 +755,7 @@ const DeepLinkIapProvider: React.FC<T_DEEPLINK_IAP_PROVIDER> = ({
|
|
|
747
755
|
setInsertAffiliateIdentifier,
|
|
748
756
|
initialize,
|
|
749
757
|
isInitialized,
|
|
758
|
+
fetchAndConditionallyOpenUrl,
|
|
750
759
|
}}
|
|
751
760
|
>
|
|
752
761
|
{children}
|
|
@@ -14,7 +14,7 @@ const useDeepLinkIapProvider = () => {
|
|
|
14
14
|
setInsertAffiliateIdentifier,
|
|
15
15
|
initialize,
|
|
16
16
|
isInitialized,
|
|
17
|
-
|
|
17
|
+
fetchAndConditionallyOpenUrl
|
|
18
18
|
} = useContext(DeepLinkIapContext);
|
|
19
19
|
|
|
20
20
|
return {
|
|
@@ -29,7 +29,7 @@ const useDeepLinkIapProvider = () => {
|
|
|
29
29
|
setInsertAffiliateIdentifier,
|
|
30
30
|
initialize,
|
|
31
31
|
isInitialized,
|
|
32
|
-
|
|
32
|
+
fetchAndConditionallyOpenUrl
|
|
33
33
|
};
|
|
34
34
|
};
|
|
35
35
|
|