Skip to content
Documentation

Documentation

React Native

One npm dependency, one effect, and the two platform-specific lines that make revenue attributable.

1. Add the bridge

bash
npm install github:rishabhrk2345/react-native-roas#v0.1.6.1
A git tag, not an npm package. Copy the exact line from SDK integration in the panel.

Pin 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:

ruby
pod 'RoasSensor', :git => 'https://github.com/rishabhrk2345/Roas-ios-SDK.git', :tag => '0.1.10'
Then 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

tsx
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:

bash
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.

ts
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 },
  },
});
PlatformValueFieldComes back as
AndroidRoas.visitorId()request.google.obfuscatedAccountIdpurchase.obfuscatedAccountIdAndroid
iOSRoas.appAccountToken()request.apple.appAccountTokenpurchase.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

ts
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

  1. Roas.onDeliveryResult(({ path, ok, error }) => …) prints each beacon's outcome; a healthy launch shows /api/tracking/mobile/first-open ok.
  2. The app's Overview tab shows an install row with sdk_version matching the bridge's native pin and signed = true.
  3. Then follow Testing a mobile integration for the store-mediated install and the test purchase.