@outerjoyn/react-native
v1.5.0
Published
Drop-in loyalty SDK for React Native. Works in Expo Go.
Maintainers
Readme
@outerjoyn/react-native
Drop-in loyalty SDK for React Native. Let your customers pay with loyalty points at checkout.
Works in Expo Go — no native modules, no config plugins, no ejecting.
Installation
npm install @outerjoyn/react-native \
@react-native-community/slider expo-web-browser@react-native-community/slider (exchange amount slider) and
expo-web-browser (in-app OAuth for account linking) are peer dependencies.
All three are Expo Go compatible.
Optional peers, auto-detected if installed:
@react-native-async-storage/async-storage— offline balance cachereact-native-sse— live balance updatesreact-native-webview— legacy hosted-widget fallback
No pod install, no Gradle changes, no Expo config plugins.
Quick Start
1. Server-side: Create a session token
// Your backend
const outerjoyn = new OuterJoyn('oj_live_YOUR_API_KEY');
app.post('/api/loyalty-session', async (req, res) => {
const session = await outerjoyn.sessions.create({
partner_member_id: req.user.id,
scopes: ['link', 'balance:read', 'redeem'],
});
res.json({ token: session.token });
});2. Client-side: Wrap your app
import { OuterJoynProvider } from '@outerjoyn/react-native';
export default function App() {
const [token, setToken] = useState(null);
useEffect(() => {
fetch('https://yourapi.com/api/loyalty-session', { method: 'POST' })
.then(r => r.json())
.then(d => setToken(d.token));
}, []);
if (!token) return <ActivityIndicator />;
return (
<OuterJoynProvider sessionToken={token}>
<Navigation />
</OuterJoynProvider>
);
}3. Add components
import { LoyaltyBanner, useOuterJoyn } from '@outerjoyn/react-native';
function HomeScreen() {
return <LoyaltyBanner />;
}
function CheckoutScreen() {
const { openPay } = useOuterJoyn();
return (
<Button
title="Pay with Points"
onPress={() =>
openPay({
orderTotalCents: 4999,
// Send the breakdown you have. The server derives the redeemable
// basis from it and fails closed (422) without it on most
// storefronts. Omit a field you don't have — `absent` is NOT `0`.
merchandiseSubtotalCents: 4200,
taxCents: 399,
shippingCents: 400,
})
}
/>
);
}~20 lines of code. Theme, field visibility, and availability come from platform config automatically.
Order breakdown (important)
orderTotalCents alone is only enough when the storefront is in tender mode with
both "include tax" and "include shipping" on. Every other configuration derives the
redeemable basis from the breakdown and rejects the reserve without it:
| Storefront mode | Redeemable basis | Required fields |
|---|---|---|
| coupon | uncapped | — |
| discount | merchandise subtotal | merchandiseSubtotalCents |
| tender, tax off / ship off | merchandise subtotal | merchandiseSubtotalCents |
| tender, tax on | merchandise + tax | merchandiseSubtotalCents, taxCents |
| tender, ship on | merchandise + shipping | merchandiseSubtotalCents, shippingCents |
| tender, tax on + ship on | full order total | — |
A missing required field returns 422 merchandise_subtotal_required,
tax_amount_required, or shipping_amount_required.
Do not substitute 0 for a value you don't have. The gateway distinguishes
absent from zero: 0 asserts a genuinely zero subtotal and caps redemption at
nothing, while an omitted field produces a clear 422 instead of a silent
zero-point checkout.
Components
Provider + Hook
| Export | Description |
|--------|-------------|
| <OuterJoynProvider> | Wraps your app. Session lifecycle, balance state, demo fallback. |
| useOuterJoyn() | Hook: linkedPrograms, openPay(), openConnect(), t(), etc. |
Inline Components (pure RN, no WebView)
| Component | Description |
|-----------|-------------|
| <LoyaltyBanner> | Home/product page banner. Brand logos + balance. |
| <BalanceSummary> | Balance display: compact, card, or inline layout. |
| <CheckoutPointsApply> | Per-program sliders at checkout. Brand-colored tracks. |
| <LoyaltyProfileSection> | Profile card with linked programs + unlink. |
| <PostCheckoutSavePrompt> | Post-checkout phone OTP to save programs. |
Full-Screen Widgets (pure RN)
| Component | Description |
|-----------|-------------|
| <ConnectScreen> | Brand picker + OAuth consent + account linking. |
| <ExchangeScreen> | Exchange points between programs. |
| <RedeemScreen> | Browse and redeem rewards. |
| <AccountScreen> | Profile, connected brands, activity. |
Test Mode
Automatic based on API key prefix:
oj_test_...→ sandbox (test data)oj_live_...→ production (real transactions)
Demo Mode
Demo mode shows sample data with all mutations disabled. It activates only when:
- no
sessionTokenis provided (showcase / storybook), or - the API is unreachable when the provider first initializes (offline).
An authenticated session never silently falls back to demo data on a transient API error — screens surface a visible error state (with retry) instead, so demo data can't masquerade as a real balance, catalog, or account.
Theming
Configured in OuterJoyn platform (app.outerjoyn.com → SDK Settings). Override with appearance prop:
<OuterJoynProvider sessionToken={token} appearance={{ colors: { primary: '#FF6B35' } }} />Localization
Auto-detects device locale. 6 languages: EN, ES, FR, PT, DE, JA. Override any string.
Real-Time Updates
npm install react-native-sse — auto-detected, balances update live.
Requirements
- React Native 0.72+ / Expo SDK 49+
- Works in Expo Go
- iOS 14+ / Android API 24+
