RevenueHogdocs
SDKs

React Native SDK

Add the pure JavaScript RevenueHog SDK to a React Native or Expo app, including Expo Go, to attribute purchases, identify users and serve paywall SKUs.

@revenuehog/react-native is pure TypeScript with no native code, so it works in Expo Go. It has zero runtime dependencies and detects optional modules at runtime.

Install

The package is not published to npm yet. Build it from a checkout of the SDK repository (dist/ is not committed) and install it as a local file dependency:

git clone https://github.com/Parra-Inc/revenue-hog-sdks
cd revenue-hog-sdks/react-native && npm install && npm run build
cd <your app> && npm install file:../revenue-hog-sdks/react-native

Optional modules, detected automatically and never required:

ModuleUsed for
@react-native-async-storage/async-storagePersists the anonymous ID, install ID, paywall cache and offline queue across relaunches. Recommended for production.
expo-application or react-native-device-infoDetects the bundle ID. react-native-device-info also provides the device model.
react-native-iapAutomatic purchase attribution.

Without AsyncStorage the SDK falls back to in-memory storage: the anonymous ID does not survive a relaunch. That is fine in development.

Quickstart

App.tsx
import RevenueHog from '@revenuehog/react-native';

RevenueHog.configure();
await RevenueHog.identify('user_42'); // after login
await RevenueHog.reset();             // on logout

How auth works

There is no API key and no attestation yet. Requests carry no Authorization header; RevenueHog resolves them by bundle ID and labels the data unverified. One exception: on iOS, a purchase attributed with a StoreKit 2 signed transaction (jws) is verified against Apple's signature, so that purchase link is trusted. See Authentication.

Attributing purchases

Automatic. When react-native-iap is installed and autoAttribution is not false, the SDK hooks purchaseUpdatedListener and attributes every purchase. It uses originalTransactionIdentifierIOS, falling back to transactionId, and passes the signed transaction from jwsRepresentationIos, jwsRepresentationIOS or verificationResultIOS when the value looks like a JWS (three dot-separated segments).

Manual. Call attributePurchase from your own listener:

import { purchaseUpdatedListener } from 'react-native-iap';

purchaseUpdatedListener((purchase) => {
  RevenueHog.attributePurchase({
    originalTransactionId:
      purchase.originalTransactionIdentifierIOS ?? purchase.transactionId!,
    productId: purchase.productId,
    jws: purchase.jwsRepresentationIos,
  });
});
// expo-in-app-purchases
setPurchaseListener(({ results }) => {
  for (const p of results ?? []) {
    RevenueHog.attributePurchase({
      originalTransactionId: p.originalOrderId ?? p.orderId,
      productId: p.productId,
    });
  }
});

On Android, pass the Play purchase token as originalTransactionId.

API reference

Every method returns a promise that resolves normally, even on failure.

CallWhat it does
RevenueHog.configure(config?)Sets up the SDK, flushes the queue and hooks react-native-iap when enabled. Never throws.
RevenueHog.identify(userId)Links the anonymous ID, and everything reported under it, to your user ID.
RevenueHog.setAttributes(attrs)Attaches a flat Record<string, string> to the user's profile.
RevenueHog.attributePurchase({ originalTransactionId, productId?, jws? })Links a purchase to the current user.
RevenueHog.reset()Forgets the user on logout and issues a fresh anonymous ID.
RevenueHog.flush()Retries the offline queue, for example when connectivity returns.
RevenueHog.paywall(entitlement, fallback, options?)Resolves the ordered SKU list. Never rejects. See Paywall.
RevenueHog.paywallShown(paywall, rendered)Reports that the paywall appeared, with the rendered product IDs.

The module also exports HogClient, memoryStorage and the types AttributePurchaseInput, LogLevel, Paywall, PaywallOptions, PaywallSku, PaywallSkuKind, RevenueHogConfig and StorageAdapter.

RevenueHogConfig

RevenueHog.configure({
  baseUrl: 'https://hog.internal.example',
  bundleId: 'com.your.app',
  autoAttribution: true,
  storage: myStorageAdapter,
  logLevel: 'warn',
  storeEnvironment: 'Sandbox',
});

Prop

Type

Paywall

interface Paywall {
  entitlement: string;
  skus: { productId: string; kind: 'subscription' | 'iap' | 'unknown' }[];
  productIds: string[];
  experimentId?: string;
  variantKey?: string;
  isFallback: boolean;
}
const paywall = await RevenueHog.paywall('pro', ['app_pro_annual', 'app_pro_monthly']);
const products = await getSubscriptions({ skus: paywall.productIds });
const ordered = paywall.productIds
  .map((id) => products.find((p) => p.productId === id))
  .filter(Boolean);
// after render:
RevenueHog.paywallShown(paywall, ordered.map((p) => p.productId));

options.forceVariant previews a variant for QA (see Experiments). The fetch deadline is 2.5 seconds.

Install ID stickiness

The install ID is a UUID persisted in your storage adapter. AsyncStorage survives logout but not reinstall, so experiment assignment is less sticky than on iOS, where the Keychain keeps the ID across reinstalls.

Behavior notes

  • Failed requests (network, 429, 5xx) retry up to 3 times with 500 ms then 1 s backoff, then queue (cap 100).
  • Other non-2xx responses are logged and dropped.
  • The SDK skips transactions already reported for the current user.

Troubleshooting

Last updated on

On this page