> ## Documentation Index
> Fetch the complete documentation index at: https://docs.gatlio.io/llms.txt
> Use this file to discover all available pages before exploring further.

# React Native

> Native billing enforcement for React Native apps — no WebView, no native module linking.

## Installation

```sh theme={null}
npm install gatlio-react-native
```

No native module linking required. The package uses only React Native core components and the built-in `fetch` API.

## Quick start

Wrap the authenticated portion of your app in `<GatlioGate>`:

```tsx theme={null}
import { GatlioGate } from 'gatlio-react-native'

export function App() {
  return (
    <GatlioGate
      apiBase="https://api.gatlio.io"
      tenantSlug="acme"
      customerId={currentUser.stripeCustomerId}
      publishableKey="pk_live_abc123"
    >
      <YourApp />
    </GatlioGate>
  )
}
```

* **Active** — `<YourApp />` renders normally
* **Warning** — a dismissable banner appears above content
* **Lockout** — a full-screen overlay replaces all content until card is updated

## `<GatlioGate>` props

```typescript theme={null}
interface GatlioGateProps {
  // Required
  apiBase: string
  tenantSlug: string
  customerId: string        // Stripe customer ID (cus_…)
  publishableKey: string    // Gatlio publishable key

  // Optional
  pollInterval?: number     // ms, default 600_000 (10 min), minimum 60_000

  // Lifecycle callbacks — fire on transitions, not every poll tick
  onLockout?: (customerId: string) => void
  onWarning?: (customerId: string) => void
  onActive?: (customerId: string) => void
  onRecovered?: (customerId: string) => void
  onError?: (error: Error) => void

  // UI overrides
  lockoutScreen?: React.ReactNode
  warningBanner?: React.ReactNode

  children: React.ReactNode
}
```

### Render behaviour

| Status    | Children | Overlay              |
| --------- | -------- | -------------------- |
| `loading` | Yes      | None                 |
| `active`  | Yes      | None                 |
| `warning` | Yes      | Warning banner (top) |
| `lockout` | No       | Full-screen lockout  |
| `error`   | Yes      | None (fail-open)     |

## `useGatlio` hook

Use the hook directly for custom UI or state management:

```tsx theme={null}
import { useGatlio } from 'gatlio-react-native'

function MyScreen() {
  const { status, cardUpdateUrl, triggerCardUpdate, dismissWarning } = useGatlio({
    apiBase: 'https://api.gatlio.io',
    tenantSlug: 'acme',
    customerId: currentUser.stripeCustomerId,
    publishableKey: 'pk_live_abc123',
  })

  if (status === 'lockout') {
    return (
      <View>
        <Text>Payment method needs updating.</Text>
        <Pressable onPress={triggerCardUpdate}>
          <Text>Update now</Text>
        </Pressable>
      </View>
    )
  }

  return <YourContent />
}
```

<Note>
  Always use `triggerCardUpdate()` rather than calling `Linking.openURL(cardUpdateUrl)` directly. Bypassing it means `onRecovered` will never fire — the next poll returning `active` fires `onActive` instead.
</Note>

### Config stability

Every config change triggers a full reset. Pass a stable object or memoize:

```tsx theme={null}
const config = useMemo(() => ({
  apiBase: 'https://api.gatlio.io',
  tenantSlug: 'acme',
  customerId: user.stripeCustomerId,
  publishableKey: 'pk_live_abc123',
}), [user.stripeCustomerId])

const state = useGatlio(config)
```

## Callbacks

| Callback      | Fires when                                                 |
| ------------- | ---------------------------------------------------------- |
| `onLockout`   | Status transitions **to** `lockout` (including first load) |
| `onWarning`   | Status transitions **to** `warning` from a non-null state  |
| `onActive`    | Status transitions **to** `active` via polling             |
| `onRecovered` | Subscriber completes the card update flow                  |
| `onError`     | Network failure or 401/404                                 |

## Testing

### Force a state

```tsx theme={null}
<GatlioGate {...config} forcedStatus="lockout">
  <App />
</GatlioGate>
```

No network calls are made when `forcedStatus` is set. Remove before shipping.

### Interactive sandbox

`<GatlioSandbox>` lets you switch states without a real Gatlio account:

```tsx theme={null}
import { GatlioSandbox } from 'gatlio-react-native'

export function DevScreen() {
  return (
    <GatlioSandbox
      onLockout={(id) => console.log('locked out:', id)}
      onWarning={(id) => console.log('warning:', id)}
      onActive={(id) => console.log('active:', id)}
    >
      <YourApp />
    </GatlioSandbox>
  )
}
```

A `DEV` badge appears in the bottom-right corner. Tap it to open a control sheet with state pills and a callback log.

Remove `<GatlioSandbox>` before shipping to production.

## TypeScript

All types are exported from the package root:

```typescript theme={null}
import type {
  GatlioConfig,
  GatlioState,
  GatlioStatus,
  GatlioGateProps,
} from 'gatlio-react-native'
```
