insert-affiliate-react-native-sdk 1.9.0 → 1.11.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/.claude/settings.local.json +8 -2
- package/dist/DeepLinkIapProvider.js +362 -158
- package/docs/deep-linking-appsflyer.md +385 -0
- package/docs/deep-linking-branch.md +351 -0
- package/docs/dynamic-offer-codes.md +369 -0
- package/package.json +1 -1
- package/readme.md +638 -1071
- package/src/DeepLinkIapProvider.tsx +369 -113
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
# AppsFlyer Deep Linking Integration
|
|
2
|
+
|
|
3
|
+
This guide shows how to integrate InsertAffiliateReactNative SDK with AppsFlyer for deep linking attribution.
|
|
4
|
+
|
|
5
|
+
## Prerequisites
|
|
6
|
+
|
|
7
|
+
- [AppsFlyer SDK for React Native](https://dev.appsflyer.com/hc/docs/react-native-plugin) installed and configured
|
|
8
|
+
- Create an AppsFlyer OneLink and provide it to affiliates via the [Insert Affiliate dashboard](https://app.insertaffiliate.com/affiliates)
|
|
9
|
+
|
|
10
|
+
## Platform Setup
|
|
11
|
+
|
|
12
|
+
Complete the deep linking setup for AppsFlyer by following their official documentation:
|
|
13
|
+
- [AppsFlyer Deferred Deep Link Integration Guide](https://dev.appsflyer.com/hc/docs/deeplinkintegrate)
|
|
14
|
+
|
|
15
|
+
This covers all platform-specific configurations including:
|
|
16
|
+
- iOS: Info.plist configuration, AppDelegate setup, and universal links
|
|
17
|
+
- Android: AndroidManifest.xml intent filters, MainActivity setup, and App Links
|
|
18
|
+
- Testing and troubleshooting for both platforms
|
|
19
|
+
|
|
20
|
+
## Integration Examples
|
|
21
|
+
|
|
22
|
+
Choose the example that matches your IAP verification platform:
|
|
23
|
+
|
|
24
|
+
### Example with RevenueCat
|
|
25
|
+
|
|
26
|
+
```javascript
|
|
27
|
+
import React, { useEffect } from 'react';
|
|
28
|
+
import { AppRegistry, Platform } from 'react-native';
|
|
29
|
+
import appsFlyer from 'react-native-appsflyer';
|
|
30
|
+
import Purchases from 'react-native-purchases';
|
|
31
|
+
import { useDeepLinkIapProvider, DeepLinkIapProvider } from 'insert-affiliate-react-native-sdk';
|
|
32
|
+
import App from './App';
|
|
33
|
+
import { name as appName } from './app.json';
|
|
34
|
+
|
|
35
|
+
const DeepLinkHandler = () => {
|
|
36
|
+
const { setInsertAffiliateIdentifier, isInitialized } = useDeepLinkIapProvider();
|
|
37
|
+
|
|
38
|
+
useEffect(() => {
|
|
39
|
+
if (!isInitialized) return;
|
|
40
|
+
|
|
41
|
+
// Initialize AppsFlyer
|
|
42
|
+
const initAppsFlyer = async () => {
|
|
43
|
+
try {
|
|
44
|
+
const initOptions = {
|
|
45
|
+
devKey: 'YOUR_APPSFLYER_DEV_KEY',
|
|
46
|
+
isDebug: true,
|
|
47
|
+
appId: Platform.OS === 'ios' ? 'YOUR_IOS_APP_ID' : 'YOUR_ANDROID_PACKAGE_NAME',
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
await appsFlyer.initSdk(initOptions);
|
|
51
|
+
} catch (error) {
|
|
52
|
+
console.error('AppsFlyer initialization error:', error);
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
// Handle deep link data
|
|
57
|
+
const handleDeepLink = async (deepLinkData) => {
|
|
58
|
+
if (deepLinkData && deepLinkData.data) {
|
|
59
|
+
const referringLink = deepLinkData.data.link || deepLinkData.data.deep_link_value;
|
|
60
|
+
|
|
61
|
+
if (referringLink) {
|
|
62
|
+
try {
|
|
63
|
+
const insertAffiliateIdentifier = await setInsertAffiliateIdentifier(referringLink);
|
|
64
|
+
|
|
65
|
+
if (insertAffiliateIdentifier) {
|
|
66
|
+
await Purchases.setAttributes({ "insert_affiliate": insertAffiliateIdentifier });
|
|
67
|
+
}
|
|
68
|
+
} catch (err) {
|
|
69
|
+
console.error('Error setting affiliate identifier:', err);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
// Listen for all deep link types
|
|
76
|
+
appsFlyer.onDeepLink(handleDeepLink);
|
|
77
|
+
appsFlyer.onAppOpenAttribution(handleDeepLink);
|
|
78
|
+
appsFlyer.onInstallConversionData(handleDeepLink);
|
|
79
|
+
|
|
80
|
+
initAppsFlyer();
|
|
81
|
+
}, [setInsertAffiliateIdentifier, isInitialized]);
|
|
82
|
+
|
|
83
|
+
return <App />;
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
const RootComponent = () => {
|
|
87
|
+
return (
|
|
88
|
+
<DeepLinkIapProvider>
|
|
89
|
+
<DeepLinkHandler />
|
|
90
|
+
</DeepLinkIapProvider>
|
|
91
|
+
);
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
AppRegistry.registerComponent(appName, () => RootComponent);
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
**Replace the following:**
|
|
98
|
+
- `YOUR_APPSFLYER_DEV_KEY` with your AppsFlyer Dev Key
|
|
99
|
+
- `YOUR_IOS_APP_ID` with your iOS App ID (numbers only, e.g., "123456789")
|
|
100
|
+
- `YOUR_ANDROID_PACKAGE_NAME` with your Android package name
|
|
101
|
+
|
|
102
|
+
### Example with Adapty
|
|
103
|
+
|
|
104
|
+
```javascript
|
|
105
|
+
import React, { useEffect, useRef } from 'react';
|
|
106
|
+
import { AppRegistry, Platform } from 'react-native';
|
|
107
|
+
import appsFlyer from 'react-native-appsflyer';
|
|
108
|
+
import { adapty } from 'react-native-adapty';
|
|
109
|
+
import { useDeepLinkIapProvider, DeepLinkIapProvider } from 'insert-affiliate-react-native-sdk';
|
|
110
|
+
import App from './App';
|
|
111
|
+
import { name as appName } from './app.json';
|
|
112
|
+
|
|
113
|
+
const DeepLinkHandler = () => {
|
|
114
|
+
const { setInsertAffiliateIdentifier, isInitialized } = useDeepLinkIapProvider();
|
|
115
|
+
const adaptyActivationPromiseRef = useRef(null);
|
|
116
|
+
|
|
117
|
+
// Initialize Adapty SDK
|
|
118
|
+
useEffect(() => {
|
|
119
|
+
const initAdapty = async () => {
|
|
120
|
+
try {
|
|
121
|
+
adaptyActivationPromiseRef.current = adapty.activate('YOUR_ADAPTY_PUBLIC_SDK_KEY', {
|
|
122
|
+
__ignoreActivationOnFastRefresh: __DEV__,
|
|
123
|
+
});
|
|
124
|
+
await adaptyActivationPromiseRef.current;
|
|
125
|
+
} catch (error) {
|
|
126
|
+
console.error('Failed to activate Adapty SDK:', error);
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
if (!adaptyActivationPromiseRef.current) {
|
|
131
|
+
initAdapty();
|
|
132
|
+
}
|
|
133
|
+
}, []);
|
|
134
|
+
|
|
135
|
+
useEffect(() => {
|
|
136
|
+
if (!isInitialized) return;
|
|
137
|
+
|
|
138
|
+
const initAppsFlyer = async () => {
|
|
139
|
+
try {
|
|
140
|
+
const initOptions = {
|
|
141
|
+
devKey: 'YOUR_APPSFLYER_DEV_KEY',
|
|
142
|
+
isDebug: true,
|
|
143
|
+
appId: Platform.OS === 'ios' ? 'YOUR_IOS_APP_ID' : 'YOUR_ANDROID_PACKAGE_NAME',
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
await appsFlyer.initSdk(initOptions);
|
|
147
|
+
} catch (error) {
|
|
148
|
+
console.error('AppsFlyer initialization error:', error);
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
const handleDeepLink = async (deepLinkData) => {
|
|
153
|
+
if (deepLinkData && deepLinkData.data) {
|
|
154
|
+
const referringLink = deepLinkData.data.link || deepLinkData.data.deep_link_value;
|
|
155
|
+
|
|
156
|
+
if (referringLink) {
|
|
157
|
+
try {
|
|
158
|
+
const insertAffiliateIdentifier = await setInsertAffiliateIdentifier(referringLink);
|
|
159
|
+
|
|
160
|
+
if (insertAffiliateIdentifier && adaptyActivationPromiseRef.current) {
|
|
161
|
+
// Wait for Adapty activation before updating profile
|
|
162
|
+
await adaptyActivationPromiseRef.current;
|
|
163
|
+
await adapty.updateProfile({
|
|
164
|
+
codableCustomAttributes: {
|
|
165
|
+
insert_affiliate: insertAffiliateIdentifier,
|
|
166
|
+
},
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
} catch (err) {
|
|
170
|
+
console.error('Error setting affiliate identifier:', err);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
appsFlyer.onDeepLink(handleDeepLink);
|
|
177
|
+
appsFlyer.onAppOpenAttribution(handleDeepLink);
|
|
178
|
+
appsFlyer.onInstallConversionData(handleDeepLink);
|
|
179
|
+
|
|
180
|
+
initAppsFlyer();
|
|
181
|
+
}, [setInsertAffiliateIdentifier, isInitialized]);
|
|
182
|
+
|
|
183
|
+
return <App />;
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
const RootComponent = () => {
|
|
187
|
+
return (
|
|
188
|
+
<DeepLinkIapProvider>
|
|
189
|
+
<DeepLinkHandler />
|
|
190
|
+
</DeepLinkIapProvider>
|
|
191
|
+
);
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
AppRegistry.registerComponent(appName, () => RootComponent);
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
### Example with Apphud
|
|
198
|
+
|
|
199
|
+
```javascript
|
|
200
|
+
import React, { useEffect } from 'react';
|
|
201
|
+
import { AppRegistry, Platform } from 'react-native';
|
|
202
|
+
import appsFlyer from 'react-native-appsflyer';
|
|
203
|
+
import Apphud from 'react-native-apphud';
|
|
204
|
+
import { useDeepLinkIapProvider, DeepLinkIapProvider } from 'insert-affiliate-react-native-sdk';
|
|
205
|
+
import App from './App';
|
|
206
|
+
import { name as appName } from './app.json';
|
|
207
|
+
|
|
208
|
+
const DeepLinkHandler = () => {
|
|
209
|
+
const { setInsertAffiliateIdentifier, isInitialized } = useDeepLinkIapProvider();
|
|
210
|
+
|
|
211
|
+
useEffect(() => {
|
|
212
|
+
if (!isInitialized) return;
|
|
213
|
+
|
|
214
|
+
const initAppsFlyer = async () => {
|
|
215
|
+
try {
|
|
216
|
+
const initOptions = {
|
|
217
|
+
devKey: 'YOUR_APPSFLYER_DEV_KEY',
|
|
218
|
+
isDebug: true,
|
|
219
|
+
appId: Platform.OS === 'ios' ? 'YOUR_IOS_APP_ID' : 'YOUR_ANDROID_PACKAGE_NAME',
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
await appsFlyer.initSdk(initOptions);
|
|
223
|
+
} catch (error) {
|
|
224
|
+
console.error('AppsFlyer initialization error:', error);
|
|
225
|
+
}
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
const handleDeepLink = async (deepLinkData) => {
|
|
229
|
+
if (deepLinkData && deepLinkData.data) {
|
|
230
|
+
const referringLink = deepLinkData.data.link || deepLinkData.data.deep_link_value;
|
|
231
|
+
|
|
232
|
+
if (referringLink) {
|
|
233
|
+
try {
|
|
234
|
+
const insertAffiliateIdentifier = await setInsertAffiliateIdentifier(referringLink);
|
|
235
|
+
|
|
236
|
+
if (insertAffiliateIdentifier) {
|
|
237
|
+
await Apphud.setUserProperty("insert_affiliate", insertAffiliateIdentifier, false);
|
|
238
|
+
}
|
|
239
|
+
} catch (err) {
|
|
240
|
+
console.error('Error setting affiliate identifier:', err);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
appsFlyer.onDeepLink(handleDeepLink);
|
|
247
|
+
appsFlyer.onAppOpenAttribution(handleDeepLink);
|
|
248
|
+
appsFlyer.onInstallConversionData(handleDeepLink);
|
|
249
|
+
|
|
250
|
+
initAppsFlyer();
|
|
251
|
+
}, [setInsertAffiliateIdentifier, isInitialized]);
|
|
252
|
+
|
|
253
|
+
return <App />;
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
const RootComponent = () => {
|
|
257
|
+
return (
|
|
258
|
+
<DeepLinkIapProvider>
|
|
259
|
+
<DeepLinkHandler />
|
|
260
|
+
</DeepLinkIapProvider>
|
|
261
|
+
);
|
|
262
|
+
};
|
|
263
|
+
|
|
264
|
+
AppRegistry.registerComponent(appName, () => RootComponent);
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
### Example with Iaptic / Store Direct Integration
|
|
268
|
+
|
|
269
|
+
```javascript
|
|
270
|
+
import React, { useEffect } from 'react';
|
|
271
|
+
import { AppRegistry, Platform } from 'react-native';
|
|
272
|
+
import appsFlyer from 'react-native-appsflyer';
|
|
273
|
+
import { useDeepLinkIapProvider, DeepLinkIapProvider } from 'insert-affiliate-react-native-sdk';
|
|
274
|
+
import App from './App';
|
|
275
|
+
import { name as appName } from './app.json';
|
|
276
|
+
|
|
277
|
+
const DeepLinkHandler = () => {
|
|
278
|
+
const { setInsertAffiliateIdentifier, isInitialized } = useDeepLinkIapProvider();
|
|
279
|
+
|
|
280
|
+
useEffect(() => {
|
|
281
|
+
if (!isInitialized) return;
|
|
282
|
+
|
|
283
|
+
const initAppsFlyer = async () => {
|
|
284
|
+
try {
|
|
285
|
+
const initOptions = {
|
|
286
|
+
devKey: 'YOUR_APPSFLYER_DEV_KEY',
|
|
287
|
+
isDebug: true,
|
|
288
|
+
appId: Platform.OS === 'ios' ? 'YOUR_IOS_APP_ID' : 'YOUR_ANDROID_PACKAGE_NAME',
|
|
289
|
+
};
|
|
290
|
+
|
|
291
|
+
await appsFlyer.initSdk(initOptions);
|
|
292
|
+
} catch (error) {
|
|
293
|
+
console.error('AppsFlyer initialization error:', error);
|
|
294
|
+
}
|
|
295
|
+
};
|
|
296
|
+
|
|
297
|
+
const handleDeepLink = async (deepLinkData) => {
|
|
298
|
+
if (deepLinkData && deepLinkData.data) {
|
|
299
|
+
const referringLink = deepLinkData.data.link || deepLinkData.data.deep_link_value;
|
|
300
|
+
|
|
301
|
+
if (referringLink) {
|
|
302
|
+
try {
|
|
303
|
+
await setInsertAffiliateIdentifier(referringLink);
|
|
304
|
+
// Affiliate identifier is stored automatically for Iaptic/direct store integration
|
|
305
|
+
} catch (err) {
|
|
306
|
+
console.error('Error setting affiliate identifier:', err);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
};
|
|
311
|
+
|
|
312
|
+
appsFlyer.onDeepLink(handleDeepLink);
|
|
313
|
+
appsFlyer.onAppOpenAttribution(handleDeepLink);
|
|
314
|
+
appsFlyer.onInstallConversionData(handleDeepLink);
|
|
315
|
+
|
|
316
|
+
initAppsFlyer();
|
|
317
|
+
}, [setInsertAffiliateIdentifier, isInitialized]);
|
|
318
|
+
|
|
319
|
+
return <App />;
|
|
320
|
+
};
|
|
321
|
+
|
|
322
|
+
const RootComponent = () => {
|
|
323
|
+
return (
|
|
324
|
+
<DeepLinkIapProvider>
|
|
325
|
+
<DeepLinkHandler />
|
|
326
|
+
</DeepLinkIapProvider>
|
|
327
|
+
);
|
|
328
|
+
};
|
|
329
|
+
|
|
330
|
+
AppRegistry.registerComponent(appName, () => RootComponent);
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
## Deep Link Listener Types
|
|
334
|
+
|
|
335
|
+
AppsFlyer provides three types of deep link callbacks:
|
|
336
|
+
|
|
337
|
+
| Callback | When It Fires | Use Case |
|
|
338
|
+
|----------|---------------|----------|
|
|
339
|
+
| `onDeepLink` | App opened via deep link (app already installed) | Direct attribution |
|
|
340
|
+
| `onAppOpenAttribution` | App opened via deep link with attribution data | Re-engagement campaigns |
|
|
341
|
+
| `onInstallConversionData` | First app launch after install | Deferred deep linking |
|
|
342
|
+
|
|
343
|
+
For comprehensive affiliate tracking, we recommend listening to all three as shown in the examples above.
|
|
344
|
+
|
|
345
|
+
## Testing
|
|
346
|
+
|
|
347
|
+
Test your AppsFlyer deep link integration:
|
|
348
|
+
|
|
349
|
+
```bash
|
|
350
|
+
# Test with your OneLink URL (iOS Simulator)
|
|
351
|
+
xcrun simctl openurl booted "https://your-app.onelink.me/abc123"
|
|
352
|
+
|
|
353
|
+
# Test with your OneLink URL (Android Emulator)
|
|
354
|
+
adb shell am start -W -a android.intent.action.VIEW -d "https://your-app.onelink.me/abc123"
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
## Troubleshooting
|
|
358
|
+
|
|
359
|
+
**Problem:** Attribution callback not firing
|
|
360
|
+
- **Solution:** Ensure AppsFlyer SDK is initialized with correct dev key and app ID
|
|
361
|
+
- Check AppsFlyer dashboard to verify OneLink is active
|
|
362
|
+
- Verify `isInitialized` is `true` before setting up listeners
|
|
363
|
+
|
|
364
|
+
**Problem:** Deep link parameters not captured
|
|
365
|
+
- **Solution:** Verify deep link contains correct parameters in AppsFlyer dashboard
|
|
366
|
+
- Check Info.plist has correct URL schemes and associated domains (iOS)
|
|
367
|
+
- Check AndroidManifest.xml has correct intent filters (Android)
|
|
368
|
+
|
|
369
|
+
**Problem:** Deferred deep linking not working
|
|
370
|
+
- **Solution:** Make sure `onInstallConversionData` listener is set up
|
|
371
|
+
- Test with a fresh app install (uninstall/reinstall)
|
|
372
|
+
- Verify AppsFlyer's "Deferred Deep Linking" is enabled in dashboard
|
|
373
|
+
|
|
374
|
+
**Problem:** `deep_link_value` is undefined
|
|
375
|
+
- **Solution:** Ensure you're accessing the correct property path in the callback data
|
|
376
|
+
- Log the full `deepLinkData` object to see available fields
|
|
377
|
+
|
|
378
|
+
## Next Steps
|
|
379
|
+
|
|
380
|
+
After completing AppsFlyer integration:
|
|
381
|
+
1. Test deep link attribution with a test affiliate link
|
|
382
|
+
2. Verify affiliate identifier is stored correctly
|
|
383
|
+
3. Make a test purchase to confirm tracking works end-to-end
|
|
384
|
+
|
|
385
|
+
[Back to Main README](../readme.md)
|