Documentation
React Native
One npm dependency, one effect, and the two platform-specific lines that make revenue attributable.
1. Add the bridge
npm install github:rishabhrk2345/react-native-roas#v0.1.6.1Pin the tag, never a branch. A moving ref means a build can report one sdk_version while running another, and "did it rebuild, or is the lockfile holding an old commit?" becomes unanswerable.
Autolinking (React Native 0.60+) registers the native module on both architectures; no Gradle line, no manual package registration. The bridge pins the native Android SDK itself. iOS additionally needs the pod line in ios/Podfile, because a podspec cannot name a git source for its own dependency:
pod 'RoasSensor', :git => 'https://github.com/rishabhrk2345/Roas-ios-SDK.git', :tag => '0.1.10'cd ios && pod install, and add NSUserTrackingUsageDescription to Info.plist.If npm install from the git URL fails, it is git access, not the package: the repository is public, so a plain git clone of that URL has to work first.
2. Initialise, from the root component's effect
import { useEffect } from 'react';
import { Linking, Platform } from 'react-native';
import { Roas } from 'react-native-roas';
const ROAS_BASE_URL = 'https://api.roassensor.com'; // from the panel -- ONE constant
// Read defensively: if react-native-config's native module is missing from a
// build, a plain `import Config` throws at load time and takes the whole app
// down before initialize() runs. Read this way, a config problem costs
// signing, never the app.
function readAppSecret(): string | undefined {
try {
return require('react-native-config').default?.ROAS_APP_SECRET || undefined;
} catch {
return undefined;
}
}
function App() {
useEffect(() => {
Roas.initialize({
publicKey: Platform.select({ android: 'ANDROID_PUBLIC_KEY', ios: 'IOS_PUBLIC_KEY' })!,
baseUrl: ROAS_BASE_URL,
appSecret: readAppSecret(),
requestTrackingAuthorization: false, // iOS only; ask later with Roas.requestTracking()
}).catch(e => console.warn('Roas init failed (tracking off, app unaffected)', e));
// Deep links: a tap on an already-installed app arrives as a URL, not a store referrer.
Linking.getInitialURL().then(url => url && Roas.handleDeepLink(url)).catch(() => {});
const sub = Linking.addEventListener('url', ({ url }) => Roas.handleDeepLink(url));
return () => sub.remove();
}, []);
// ...
}Root component, after the first render: tracking must never be able to hold or break the UI. An Android and an iOS app are two apps in the panel with two public keys, hence Platform.select.
3. The app secret
Use react-native-config, which reads a gitignored .env at build time:
npm install react-native-config
# android/app/build.gradle, after apply plugin: "com.facebook.react"
apply from: project(':react-native-config').projectDir.getPath() + "/dotenv.gradle"
# android/app/proguard-rules.pro -- required, or a minified release build ships unsigned
-keep class com.yourapp.BuildConfig { *; }
# .env (gitignored)
ROAS_APP_SECRET=…4. Bind purchases to the install
With react-native-iap 16.x, the field is set per platform in the same call. The two platforms take different values, and getting them crossed fails silently.
import { requestPurchase } from 'react-native-iap';
import { Roas } from 'react-native-roas';
const vid = await Roas.visitorId(); // Android: "rs" + 32 hex
const token = await Roas.appAccountToken(); // iOS: a UUID. null on Android.
await requestPurchase({
type: 'subs', // or 'in-app'
request: {
google: { skus: [productId], obfuscatedAccountId: vid ?? undefined, subscriptionOffers },
apple: { sku: productId, appAccountToken: token ?? undefined },
},
});| Platform | Value | Field | Comes back as |
|---|---|---|---|
| Android | Roas.visitorId() | request.google.obfuscatedAccountId | purchase.obfuscatedAccountIdAndroid |
| iOS | Roas.appAccountToken() | request.apple.appAccountToken | purchase.appAccountToken |
Log the returned value once on an internal build and compare it to what you passed. Older react-native-iap (≤ 12) took obfuscatedAccountIdAndroid flat on the call instead.
Optional: Roas.verifyPurchase({ purchaseToken, productId, isSubscription }) on Android or Roas.verifyPurchase({ transactionId }) on iOS reports the purchase the moment the store confirms it, rather than waiting for the store's notification. Available from bridge 0.1.7.
5. Identity and funnel events
import { Roas, RoasEvent, RoasProps } from 'react-native-roas';
await Roas.identify({ email: userEmail }); // hashed on device
Roas.track(RoasEvent.BEGIN_CHECKOUT, { [RoasProps.PRODUCT_ID]: productId });Events are never revenue. Anything an app can send, anyone can forge, so track() is the funnel surface only; money enters through the store notifications.
Check it worked
Roas.onDeliveryResult(({ path, ok, error }) => …)prints each beacon's outcome; a healthy launch shows/api/tracking/mobile/first-openok.- The app's Overview tab shows an install row with
sdk_versionmatching the bridge's native pin andsigned = true. - Then follow Testing a mobile integration for the store-mediated install and the test purchase.

