insert-affiliate-react-native-sdk 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +28 -0
- package/readme.md +125 -0
- package/src/DeepLinkIapProvider.tsx +367 -0
- package/src/index.ts +4 -0
- package/src/useDeepLinkIapProvider.tsx +28 -0
- package/tsconfig.json +15 -0
package/package.json
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "insert-affiliate-react-native-sdk",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "A package that will give context having implementation of react-native-branch and react-native-iap and iaptic validate api.",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"build": "tsc"
|
|
9
|
+
},
|
|
10
|
+
"keywords": [
|
|
11
|
+
"react-native",
|
|
12
|
+
"react-native-branch",
|
|
13
|
+
"react-native-iap",
|
|
14
|
+
"iaptic"
|
|
15
|
+
],
|
|
16
|
+
"author": "Haseeb Ahmed",
|
|
17
|
+
"license": "MIT",
|
|
18
|
+
"peerDependencies": {
|
|
19
|
+
"@react-native-async-storage/async-storage": "^2.0.0",
|
|
20
|
+
"@types/react": "^18.2.6",
|
|
21
|
+
"axios": "^1.7.7",
|
|
22
|
+
"react": "^18.3.1",
|
|
23
|
+
"react-native": "^0.75.3",
|
|
24
|
+
"react-native-branch": "^6.2.2",
|
|
25
|
+
"react-native-iap": "^12.15.4",
|
|
26
|
+
"typescript": "^5.0.4"
|
|
27
|
+
}
|
|
28
|
+
}
|
package/readme.md
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# InsertAffiliateReactNative SDK
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
The **InsertAffiliateReactNative SDK** is designed for React Native applications, providing seamless integration with the [Insert Affiliate platform](https://insertaffiliate.com). This SDK enables functionalities such as managing affiliate links, handling in-app purchases (IAP), and utilising deep links. For more details and to access the Insert Affiliate dashboard, visit [app.insertaffiliate.com](https://app.insertaffiliate.com).
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- **Unique Device Identification**: Generates and stores a short unique device ID to identify users effectively.
|
|
10
|
+
- **Affiliate Identifier Management**: Set and retrieve the affiliate identifier based on user-specific links.
|
|
11
|
+
- **In-App Purchase (IAP) Initialisation**: Easily reinitialise in-app purchases with validation options using the affiliate identifier.
|
|
12
|
+
- **Offer Code Handling**: Fetch offer codes from the Insert Affiliate API and open redeem URLs directly in the App Store.
|
|
13
|
+
|
|
14
|
+
## Peer Dependencies
|
|
15
|
+
|
|
16
|
+
Before using this package, ensure you have the following dependencies installed:
|
|
17
|
+
|
|
18
|
+
- [react-native-iap](https://www.npmjs.com/package/react-native-iap)
|
|
19
|
+
- [react-native-branch](https://www.npmjs.com/package/react-native-branch)
|
|
20
|
+
- [axios](https://www.npmjs.com/package/axios)
|
|
21
|
+
|
|
22
|
+
## Installation
|
|
23
|
+
|
|
24
|
+
To integrate the InsertAffiliateReactNative SDK into your project, run:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
npm install insert-affiliate-react-native-sdk
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Usage
|
|
31
|
+
### Importing the SDK
|
|
32
|
+
Import the provider from the package:
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
```javascript
|
|
36
|
+
import { DeepLinkIapProvider } from 'insert-affiliate-react-native-sdk';
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## 1. Integrating the Provider in Your Application (`App.tsx`)
|
|
40
|
+
### Step 1: Wrap Your Application with the Iaptic Provider and Pass Context Properties
|
|
41
|
+
|
|
42
|
+
- Replace `{{ your_iaptic_app_id }}` with your **Iaptic App ID**. You can find this [here](https://www.iaptic.com/account).
|
|
43
|
+
- Replace `{{ your_iaptic_app_name }}` with your **Iaptic App Name**. You can find this [here](https://www.iaptic.com/account).
|
|
44
|
+
- Replace `{{ your_iaptic_secret_key }}` with your **Iaptic Secret Key**. You can find this [here](https://www.iaptic.com/settings).
|
|
45
|
+
|
|
46
|
+
Here's the code with placeholders for you to swap out:
|
|
47
|
+
|
|
48
|
+
```javascript
|
|
49
|
+
const App = () => {
|
|
50
|
+
return (
|
|
51
|
+
<DeepLinkIapProvider
|
|
52
|
+
iapSkus={IAP_SKUS}
|
|
53
|
+
iapticAppId="{{ your_iaptic_app_id }}"
|
|
54
|
+
iapticAppName="{{ your_iaptic_app_name }}"
|
|
55
|
+
iapticAppSecret="{{ your_iaptic_app_id }}">
|
|
56
|
+
<Child />
|
|
57
|
+
</DeepLinkIapProvider>
|
|
58
|
+
);
|
|
59
|
+
};
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## 2. When the User Makes a Purchase, Call Our SDK's "handleBuySubscription"
|
|
63
|
+
Here’s a complete example of how to use the SDK:
|
|
64
|
+
|
|
65
|
+
In the code below, please remember to update your IAP_SKUS with the comma separated list of your in app purchase SKU's.
|
|
66
|
+
|
|
67
|
+
```javascript
|
|
68
|
+
import React from 'react';
|
|
69
|
+
import { ActivityIndicator, Button, StyleSheet, Text, View } from 'react-native';
|
|
70
|
+
import { DeepLinkIapProvider, useDeepLinkIapProvider } from 'insert-affiliate-react-native-sdk';
|
|
71
|
+
|
|
72
|
+
const Child = () => {
|
|
73
|
+
const {
|
|
74
|
+
referrerLink,
|
|
75
|
+
subscriptions,
|
|
76
|
+
iapLoading,
|
|
77
|
+
handleBuySubscription,
|
|
78
|
+
userId,
|
|
79
|
+
userPurchase,
|
|
80
|
+
isIapticValidated,
|
|
81
|
+
} = useDeepLinkIapProvider();
|
|
82
|
+
|
|
83
|
+
export const IAP_SKUS = Platform.select({
|
|
84
|
+
android: [''], // Here, put a comma separated list of the In App Purchase SKU's
|
|
85
|
+
ios: [''], // Here, put a comma separated list of the In App Purchase SKU's
|
|
86
|
+
}) as string[];
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
return (
|
|
90
|
+
<View>
|
|
91
|
+
{subscriptions.length ? (
|
|
92
|
+
<Button
|
|
93
|
+
disabled={iapLoading}
|
|
94
|
+
title={
|
|
95
|
+
userPurchase
|
|
96
|
+
? `Successfully Purchased`
|
|
97
|
+
: `Click to Buy (${subscriptions[0].localizedPrice} / ${subscriptions[0].subscriptionPeriodUnitIOS})`
|
|
98
|
+
}
|
|
99
|
+
onPress={() => {
|
|
100
|
+
if (iapLoading) return;
|
|
101
|
+
if (!userPurchase) handleBuySubscription(subscriptions[0].productId); //
|
|
102
|
+
}}
|
|
103
|
+
/>
|
|
104
|
+
) : null}
|
|
105
|
+
{iapLoading && <ActivityIndicator size={'small'} color={'black'} />}
|
|
106
|
+
</View>
|
|
107
|
+
);
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
const App = () => {
|
|
111
|
+
return (
|
|
112
|
+
// Wrapped application code from the previous step...
|
|
113
|
+
<DeepLinkIapProvider
|
|
114
|
+
iapSkus={IAP_SKUS}
|
|
115
|
+
iapticAppId="IAPTIC_APP_BUNDLE_IDENTIFIER"
|
|
116
|
+
iapticAppName="IAPTIC_APP_NAME"
|
|
117
|
+
iapticAppSecret="IAPTIC_APP_SECRET_KEY">
|
|
118
|
+
<Child />
|
|
119
|
+
</DeepLinkIapProvider>
|
|
120
|
+
);
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
export default App;
|
|
124
|
+
```
|
|
125
|
+
|
|
@@ -0,0 +1,367 @@
|
|
|
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
|
+
import axios from "axios";
|
|
13
|
+
import AsyncStorage from "@react-native-async-storage/async-storage";
|
|
14
|
+
|
|
15
|
+
// TYPES USED IN THIS PROVIDER
|
|
16
|
+
type T_DEEPLINK_IAP_PROVIDER = {
|
|
17
|
+
children: React.ReactNode;
|
|
18
|
+
iapSkus: string[];
|
|
19
|
+
iapticAppId: string;
|
|
20
|
+
iapticAppName: string;
|
|
21
|
+
iapticAppSecret: string;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
type T_DEEPLINK_IAP_CONTEXT = {
|
|
25
|
+
iapLoading: boolean;
|
|
26
|
+
alreadyPurchased: boolean;
|
|
27
|
+
subscriptions: Subscription[];
|
|
28
|
+
userPurchase: Purchase | null;
|
|
29
|
+
referrerLink: string;
|
|
30
|
+
userId: string;
|
|
31
|
+
isIapticValidated: boolean | undefined;
|
|
32
|
+
handleBuySubscription: (productId: string, offerToken?: string) => void;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const ASYNC_KEYS = {
|
|
36
|
+
REFERRER_LINK: "@app_referrer_link",
|
|
37
|
+
USER_PURCHASE: "@app_user_purchase",
|
|
38
|
+
USER_ID: "@app_user_id",
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
// STARTING CONTEXT IMPLEMENTATION
|
|
42
|
+
export const DeepLinkIapContext = createContext<T_DEEPLINK_IAP_CONTEXT>({
|
|
43
|
+
iapLoading: false,
|
|
44
|
+
alreadyPurchased: false,
|
|
45
|
+
isIapticValidated: undefined,
|
|
46
|
+
subscriptions: [],
|
|
47
|
+
userPurchase: null,
|
|
48
|
+
referrerLink: "",
|
|
49
|
+
userId: "",
|
|
50
|
+
handleBuySubscription: (productId: string, offerToken?: string) => {},
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
const DeepLinkIapProvider: React.FC<T_DEEPLINK_IAP_PROVIDER> = ({
|
|
54
|
+
children,
|
|
55
|
+
iapSkus,
|
|
56
|
+
iapticAppId,
|
|
57
|
+
iapticAppName,
|
|
58
|
+
iapticAppSecret,
|
|
59
|
+
}) => {
|
|
60
|
+
// LOCAL STATES
|
|
61
|
+
const [iapLoading, setIapLoading] = useState<boolean>(false);
|
|
62
|
+
const [alreadyPurchased, setAlreadyPurchased] = useState<boolean>(false);
|
|
63
|
+
const [isIapticValidated, setIapticValidated] = useState<boolean | undefined>(
|
|
64
|
+
undefined
|
|
65
|
+
);
|
|
66
|
+
const [userPurchase, setUserPurchase] = useState<Purchase | null>(null);
|
|
67
|
+
const [referrerLink, setReferrerLink] = useState<string>("");
|
|
68
|
+
const [userId, setUserId] = useState<string>("");
|
|
69
|
+
|
|
70
|
+
const {
|
|
71
|
+
connected,
|
|
72
|
+
purchaseHistory,
|
|
73
|
+
getPurchaseHistory,
|
|
74
|
+
getSubscriptions,
|
|
75
|
+
subscriptions,
|
|
76
|
+
finishTransaction,
|
|
77
|
+
currentPurchase,
|
|
78
|
+
currentPurchaseError,
|
|
79
|
+
} = useIAP();
|
|
80
|
+
|
|
81
|
+
// ASYNC FUNCTIONS
|
|
82
|
+
const saveValueInAsync = async (key: string, value: string) => {
|
|
83
|
+
await AsyncStorage.setItem(key, value);
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
const getValueFromAsync = async (key: string) => {
|
|
87
|
+
const response = await AsyncStorage.getItem(key);
|
|
88
|
+
return response;
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
const clearAsyncStorage = async () => {
|
|
92
|
+
await AsyncStorage.clear();
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
// EFFECT TO FETCH USER ID AND REF LINK
|
|
96
|
+
// IF ALREADY EXISTS IN ASYNC STORAGE
|
|
97
|
+
useEffect(() => {
|
|
98
|
+
const fetchAsyncEssentials = async () => {
|
|
99
|
+
try {
|
|
100
|
+
const uId = await getValueFromAsync(ASYNC_KEYS.USER_ID);
|
|
101
|
+
const refLink = await getValueFromAsync(ASYNC_KEYS.REFERRER_LINK);
|
|
102
|
+
|
|
103
|
+
if (uId && refLink) {
|
|
104
|
+
setUserId(uId);
|
|
105
|
+
setReferrerLink(refLink);
|
|
106
|
+
}
|
|
107
|
+
} catch (error) {
|
|
108
|
+
errorLog(`ERROR ~ fetchAsyncEssentials: ${error}`);
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
fetchAsyncEssentials();
|
|
113
|
+
}, []);
|
|
114
|
+
|
|
115
|
+
// FUNCTION TO SHOW LOG, ERROR and WARN
|
|
116
|
+
const errorLog = (message: string, type?: "error" | "warn" | "log") => {
|
|
117
|
+
switch (type) {
|
|
118
|
+
case "error":
|
|
119
|
+
console.error(`ENCOUNTER ERROR ~ ${message}`);
|
|
120
|
+
break;
|
|
121
|
+
case "warn":
|
|
122
|
+
console.warn(`ENCOUNTER WARNING ~ ${message}`);
|
|
123
|
+
break;
|
|
124
|
+
default:
|
|
125
|
+
console.log(`LOGGING ~ ${message}`);
|
|
126
|
+
break;
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
// GENERATING UNIQUE USER ID
|
|
131
|
+
const generateUserID = () => {
|
|
132
|
+
const characters =
|
|
133
|
+
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
|
134
|
+
let uniqueId = "";
|
|
135
|
+
for (let i = 0; i < 6; i++) {
|
|
136
|
+
const randomIndex = Math.floor(Math.random() * characters.length);
|
|
137
|
+
uniqueId += characters[randomIndex];
|
|
138
|
+
}
|
|
139
|
+
return uniqueId;
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
// BRANCH IMPLEMENTATION
|
|
143
|
+
useEffect(() => {
|
|
144
|
+
const branchSubscription = branch.subscribe(async ({ error, params }) => {
|
|
145
|
+
if (error) {
|
|
146
|
+
errorLog(`branchSubscription: ${JSON.stringify(error)}`, "error");
|
|
147
|
+
return;
|
|
148
|
+
} else if (!params) {
|
|
149
|
+
errorLog(`branchSubscription: params does not exits`, "warn");
|
|
150
|
+
return;
|
|
151
|
+
} else if (!params["+clicked_branch_link"]) {
|
|
152
|
+
errorLog(`branchSubscription: Not a branch link`, "warn");
|
|
153
|
+
return;
|
|
154
|
+
} else {
|
|
155
|
+
if (params["~referring_link"]) {
|
|
156
|
+
setReferrerLink(params["~referring_link"]);
|
|
157
|
+
const userId = generateUserID();
|
|
158
|
+
setUserId(userId);
|
|
159
|
+
await saveValueInAsync(ASYNC_KEYS.USER_ID, userId);
|
|
160
|
+
await saveValueInAsync(
|
|
161
|
+
ASYNC_KEYS.REFERRER_LINK,
|
|
162
|
+
params["~referring_link"]
|
|
163
|
+
);
|
|
164
|
+
} else
|
|
165
|
+
errorLog(
|
|
166
|
+
`branchSubscription: Params does't have referring_link`,
|
|
167
|
+
"warn"
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
return () => {
|
|
172
|
+
if (branchSubscription) {
|
|
173
|
+
branchSubscription();
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
}, []);
|
|
177
|
+
|
|
178
|
+
// IN APP PURCHASE IMPLEMENTATION STARTS
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* This function is responsisble to
|
|
182
|
+
* fetch the subscriptions
|
|
183
|
+
*/
|
|
184
|
+
const handleGetSubscriptions = async () => {
|
|
185
|
+
try {
|
|
186
|
+
await getSubscriptions({ skus: iapSkus });
|
|
187
|
+
} catch (error) {
|
|
188
|
+
errorLog(`handleGetSubscriptions: ${error}`, "error");
|
|
189
|
+
}
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* This function is responsible to
|
|
194
|
+
* fetch the purchase history
|
|
195
|
+
*/
|
|
196
|
+
const handleGetPurchaseHistory = async () => {
|
|
197
|
+
try {
|
|
198
|
+
await getPurchaseHistory();
|
|
199
|
+
if (purchaseHistory.length > 0) {
|
|
200
|
+
setAlreadyPurchased(true);
|
|
201
|
+
setUserPurchase(currentPurchase ? currentPurchase : null);
|
|
202
|
+
}
|
|
203
|
+
} catch (error) {
|
|
204
|
+
errorLog(`handleGetPurchaseHistory: ${error}`, "error");
|
|
205
|
+
}
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
// Effect to fetch IAP subscriptions + purchase history
|
|
209
|
+
useEffect(() => {
|
|
210
|
+
const fetchIapEssentials = async () => {
|
|
211
|
+
try {
|
|
212
|
+
await handleGetSubscriptions();
|
|
213
|
+
await handleGetPurchaseHistory();
|
|
214
|
+
} catch (error) {
|
|
215
|
+
errorLog(`fetchIapEssentials: ${error}`);
|
|
216
|
+
}
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
if (connected) fetchIapEssentials();
|
|
220
|
+
}, [connected]);
|
|
221
|
+
|
|
222
|
+
const handlePurchaseValidation = async (jsonIapPurchase: Purchase) => {
|
|
223
|
+
try {
|
|
224
|
+
if (!userId || !referrerLink) {
|
|
225
|
+
errorLog(
|
|
226
|
+
`WANR ~ handlePurchaseValidation: No Referrer Link or User ID for validation`
|
|
227
|
+
);
|
|
228
|
+
|
|
229
|
+
await axios({
|
|
230
|
+
url: `https://validator.iaptic.com/v1/validate`,
|
|
231
|
+
method: "POST",
|
|
232
|
+
headers: {
|
|
233
|
+
Authorization: `Basic ${btoa(iapticAppName + ":" + iapticAppSecret)}`,
|
|
234
|
+
},
|
|
235
|
+
data: {
|
|
236
|
+
id: iapticAppId,
|
|
237
|
+
type: "application",
|
|
238
|
+
transaction: {
|
|
239
|
+
id: iapticAppId,
|
|
240
|
+
type: "ios-appstore",
|
|
241
|
+
appStoreReceipt: jsonIapPurchase.transactionReceipt,
|
|
242
|
+
},
|
|
243
|
+
},
|
|
244
|
+
});
|
|
245
|
+
setIapticValidated(true);
|
|
246
|
+
} else {
|
|
247
|
+
await axios({
|
|
248
|
+
url: `https://validator.iaptic.com/v1/validate`,
|
|
249
|
+
method: "POST",
|
|
250
|
+
headers: {
|
|
251
|
+
Authorization: `Basic ${btoa(iapticAppName + ":" + iapticAppSecret)}`,
|
|
252
|
+
},
|
|
253
|
+
data: {
|
|
254
|
+
id: iapticAppId,
|
|
255
|
+
type: "application",
|
|
256
|
+
transaction: {
|
|
257
|
+
id: iapticAppId,
|
|
258
|
+
type: "ios-appstore",
|
|
259
|
+
appStoreReceipt: jsonIapPurchase.transactionReceipt,
|
|
260
|
+
},
|
|
261
|
+
additionalData: {
|
|
262
|
+
applicationUsername: `${referrerLink}/${userId}`,
|
|
263
|
+
},
|
|
264
|
+
},
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
} catch (error) {
|
|
268
|
+
errorLog(`handlePurchaseValidation: ${error}`, "error");
|
|
269
|
+
setIapticValidated(false);
|
|
270
|
+
}
|
|
271
|
+
};
|
|
272
|
+
|
|
273
|
+
useEffect(() => {
|
|
274
|
+
const checkCurrentPurchase = async () => {
|
|
275
|
+
try {
|
|
276
|
+
if (currentPurchase?.productId) {
|
|
277
|
+
setUserPurchase(currentPurchase);
|
|
278
|
+
|
|
279
|
+
await handlePurchaseValidation(currentPurchase);
|
|
280
|
+
|
|
281
|
+
await finishTransaction({
|
|
282
|
+
purchase: currentPurchase,
|
|
283
|
+
isConsumable: true,
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
await saveValueInAsync(
|
|
287
|
+
ASYNC_KEYS.USER_PURCHASE,
|
|
288
|
+
JSON.stringify(currentPurchase)
|
|
289
|
+
);
|
|
290
|
+
setIapLoading(false);
|
|
291
|
+
}
|
|
292
|
+
} catch (error) {
|
|
293
|
+
setIapLoading(false);
|
|
294
|
+
errorLog(`checkCurrentPurchase: ${error}`, "error");
|
|
295
|
+
}
|
|
296
|
+
};
|
|
297
|
+
|
|
298
|
+
checkCurrentPurchase();
|
|
299
|
+
}, [currentPurchase, finishTransaction]);
|
|
300
|
+
|
|
301
|
+
useEffect(() => {
|
|
302
|
+
const checkCurrentPurchaseError = async () => {
|
|
303
|
+
if (currentPurchaseError) {
|
|
304
|
+
setIapLoading(false);
|
|
305
|
+
errorLog(
|
|
306
|
+
`checkCurrentPurchaseError: ${currentPurchaseError.message}`,
|
|
307
|
+
"error"
|
|
308
|
+
);
|
|
309
|
+
}
|
|
310
|
+
};
|
|
311
|
+
checkCurrentPurchaseError();
|
|
312
|
+
}, [currentPurchaseError]);
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* Function is responsible to
|
|
316
|
+
* buy a subscription
|
|
317
|
+
* @param {string} productId
|
|
318
|
+
* @param {string} [offerToken]
|
|
319
|
+
*/
|
|
320
|
+
const handleBuySubscription = async (
|
|
321
|
+
productId: string,
|
|
322
|
+
offerToken?: string
|
|
323
|
+
) => {
|
|
324
|
+
if (isPlay && !offerToken) {
|
|
325
|
+
console.warn(
|
|
326
|
+
`There are no subscription Offers for selected product (Only requiered for Google Play purchases): ${productId}`
|
|
327
|
+
);
|
|
328
|
+
}
|
|
329
|
+
try {
|
|
330
|
+
setIapLoading(true);
|
|
331
|
+
await requestSubscription({
|
|
332
|
+
sku: productId,
|
|
333
|
+
...(offerToken && {
|
|
334
|
+
subscriptionOffers: [{ sku: productId, offerToken }],
|
|
335
|
+
}),
|
|
336
|
+
});
|
|
337
|
+
} catch (error) {
|
|
338
|
+
setIapLoading(false);
|
|
339
|
+
errorLog(`handleBuySubscription: ${error}`, "error");
|
|
340
|
+
}
|
|
341
|
+
};
|
|
342
|
+
|
|
343
|
+
useEffect(() => {
|
|
344
|
+
return () => {
|
|
345
|
+
endConnection();
|
|
346
|
+
};
|
|
347
|
+
}, []);
|
|
348
|
+
|
|
349
|
+
return (
|
|
350
|
+
<DeepLinkIapContext.Provider
|
|
351
|
+
value={{
|
|
352
|
+
iapLoading,
|
|
353
|
+
alreadyPurchased,
|
|
354
|
+
isIapticValidated,
|
|
355
|
+
subscriptions,
|
|
356
|
+
userPurchase,
|
|
357
|
+
referrerLink,
|
|
358
|
+
userId,
|
|
359
|
+
handleBuySubscription,
|
|
360
|
+
}}
|
|
361
|
+
>
|
|
362
|
+
{children}
|
|
363
|
+
</DeepLinkIapContext.Provider>
|
|
364
|
+
);
|
|
365
|
+
};
|
|
366
|
+
|
|
367
|
+
export default withIAPContext(DeepLinkIapProvider);
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { useContext } from "react";
|
|
2
|
+
import { DeepLinkIapContext } from "./DeepLinkIapProvider";
|
|
3
|
+
|
|
4
|
+
const useDeepLinkIapProvider = () => {
|
|
5
|
+
const {
|
|
6
|
+
alreadyPurchased,
|
|
7
|
+
handleBuySubscription,
|
|
8
|
+
iapLoading,
|
|
9
|
+
referrerLink,
|
|
10
|
+
isIapticValidated,
|
|
11
|
+
subscriptions,
|
|
12
|
+
userId,
|
|
13
|
+
userPurchase,
|
|
14
|
+
} = useContext(DeepLinkIapContext);
|
|
15
|
+
|
|
16
|
+
return {
|
|
17
|
+
alreadyPurchased,
|
|
18
|
+
handleBuySubscription,
|
|
19
|
+
iapLoading,
|
|
20
|
+
referrerLink,
|
|
21
|
+
subscriptions,
|
|
22
|
+
userId,
|
|
23
|
+
isIapticValidated,
|
|
24
|
+
userPurchase,
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export default useDeepLinkIapProvider;
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES6",
|
|
4
|
+
"module": "commonjs",
|
|
5
|
+
"outDir": "dist",
|
|
6
|
+
"declaration": true,
|
|
7
|
+
"declarationDir": "dist",
|
|
8
|
+
"sourceMap": true,
|
|
9
|
+
"strict": true,
|
|
10
|
+
"esModuleInterop": true,
|
|
11
|
+
"jsx": "react-native",
|
|
12
|
+
"lib": ["esnext", "dom"]
|
|
13
|
+
},
|
|
14
|
+
"include": ["src/**/*"]
|
|
15
|
+
}
|