diff --git a/.changeset/ui-onetrust-layer-cleanup.md b/.changeset/ui-onetrust-layer-cleanup.md
new file mode 100644
index 0000000..1db5494
--- /dev/null
+++ b/.changeset/ui-onetrust-layer-cleanup.md
@@ -0,0 +1,7 @@
+---
+"@contentpass/react-native-contentpass": patch
+"@contentpass/react-native-contentpass-ui": patch
+"@contentpass/react-native-contentpass-cmp-onetrust": patch
+---
+
+Clean up debug logs, fix consent layer theme and SDK version string. Add README documentation for all packages.
diff --git a/README.md b/README.md
index 0ebf92a..495ae42 100644
--- a/README.md
+++ b/README.md
@@ -1,150 +1,27 @@
-# @contentpass/react-native-contentpass
+# react-native-contentpass
-Contentpass React Native SDK enables easy integration of Contentpass functionality into your React Native applications.
+Monorepo for the Contentpass React Native SDK and related packages.
-## Installation
-Install the package using npm or Yarn:
+## Packages
-```sh
-npm install @contentpass/react-native-contentpass
-```
+| Package | Description |
+|---------|-------------|
+| [`@contentpass/react-native-contentpass`](./packages/react-native-contentpass) | Core SDK — authentication, subscription validation, and impression tracking. |
+| [`@contentpass/react-native-contentpass-ui`](./packages/react-native-contentpass-ui) | Pre-built UI components for the Contentpass consent layer. |
+| [`@contentpass/react-native-contentpass-cmp-onetrust`](./packages/react-native-contentpass-cmp-onetrust) | OneTrust CMP adapter for the Contentpass SDK. |
-or
+## Examples
-```sh
-yarn add @contentpass/react-native-contentpass
-```
-
-### Peer Dependencies
-The following peer dependencies must also be installed:
-- [react](https://github.com/facebook/react) (Required for React Native projects.)
-- [react-native](https://github.com/facebook/react-native) (Core React Native framework)
-- [react-native-app-auth](https://github.com/FormidableLabs/react-native-app-auth) (Used for OAuth 2.0 authentication)
-- [react-native-encrypted-storage](https://github.com/emeraldsanto/react-native-encrypted-storage) (Ensures secure storage of authentication tokens)
-
-Some dependencies require additional setup in the native code. Refer to their official guides:
-- [react-native-app-auth setup](https://commerce.nearform.com/open-source/react-native-app-auth/docs#setup)
-- [react-native-encrypted-storage setup](https://github.com/emeraldsanto/react-native-encrypted-storage?tab=readme-ov-file#installation)
-
-### Expo support
-If you are using Expo, you need to run the following command to enable modifications to the `ios` and `android` directories:
-
-```sh
-npx expo prebuild
-```
-
-## Usage
-
-### Initialization
-Wrap your app's root component with ContentpassSdkProvider. The provider requires a configuration object (contentpassConfig) with the following properties:
-- `propertyId` - Your unique property ID (ask Contentpass team for details)
-- `planId` - The ID of the plan you want to check the user's subscription status against (ask Contentpass team for details)
-- `issuer` - The OAuth 2.0 server URL (e.g. `https://my.contentpass.net`)
-- `redirectUrl` - the redirect URL of your app to which the OAuth2 server will redirect after the authentication
-- `samplingRate` - Optional: The rate at which the SDK will send impression events for unauthenticated users. Default is 0.05 (5%)
-- `logLevel` - Optional: The log level for the SDK. By default logger is disabled. Possible values are 'info', 'warn', 'error' and 'debug'
-
-
-```jsx
-import React from 'react';
-import { ContentpassSdkProvider } from '@contentpass/react-native-contentpass';
-
-const contentpassConfig = {
- propertyId: 'property-id',
- planId: 'plan-id',
- issuer: 'https://my.contentpass.net',
- redirectUrl: 'com.yourapp://oauthredirect',
- samplingRate: 0.1,
- logLevel: 'info'
-};
-
-const App = () => {
- return (
-
-
-
- );
-};
-
-export default App;
-```
-
-### SDK Methods
-The SDK exposes the following methods through the `useContentpassSdk` hook:
-
-### authenticate
-Initiates the OAuth 2.0 authentication process via a modal interface. It validates the user’s active Contentpass subscriptions
-upon successful authentication.
-
-### countImpression
-Tracks and increments the impression count for the current user. This method should be invoked whenever a user views a
-piece of content. It applies to all users, whether authenticated or unauthenticated.
-
-### registerObserver
-Registers a callback function to listen for changes in the user’s authentication and subscription status. The observer function
-receives a state object describing the current status (see the exported [ContentpassState](./packages/react-native-contentpass/src/types/ContentpassState.ts) type).
-
-### unregisterObserver
-Unregisters a previously registered observer. The observer will no longer receive updates.
-
-### logout
-Logs the user out by clearing all stored authentication tokens.
-
-### recoverFromError
-During background token refresh, an error state may occur due to poor or no internet connection. This is indicated by the
-`state` switching to `ERROR`. The state object includes a reference to the original exception that was thrown. As the SDK
-does not monitor the device's connection state, you must notify the SDK when the network connection has been reestablished
-or improved. The SDK will then refresh and revalidate the user's authentication tokens.
-
-```jsx
-import React, { useEffect } from 'react';
-import { useContentpassSdk } from '@contentpass/react-native-contentpass';
-import { Button, View } from 'react-native';
-
-const YourApp = () => {
- const {
- authenticate,
- countImpression,
- registerObserver,
- unregisterObserver,
- logout,
- recoverFromError
- } = useContentpassSdk();
-
- useEffect(() => {
- const observer = (state) => {
- console.log('Contentpass state changed:', state);
- };
-
- registerObserver(observer);
-
- return () => {
- unregisterObserver(observer);
- };
- }, []);
-
- return (
-
-
-
-
- );
-};
-```
-
-## Integration with Sourcepoint SDK
-
-See the [Sourcepoint SDK documentation](packages/react-native-contentpass/docs/SOURCEPOINT_SDK_INTEGRATION.md) for information on integrating the Contentpass SDK with the Sourcepoint SDK.
+| Example | Description |
+|---------|-------------|
+| [`examples/onetrust`](./examples/onetrust) | Integration using OneTrust as the CMP. |
+| [`examples/sourcepoint`](./examples/sourcepoint) | Integration using Sourcepoint as the CMP (bare React Native). |
+| [`examples/sourcepoint-expo`](./examples/sourcepoint-expo) | Integration using Sourcepoint as the CMP (Expo). |
## Contributing
-See the [contributing guide](packages/react-native-contentpass/docs/CONTRIBUTING.md) to learn how to contribute to the repository and the development workflow.
-
+See the [contributing guide](./packages/react-native-contentpass/docs/CONTRIBUTING.md).
## License
MIT
-
----
-
-Made with [create-react-native-library](https://github.com/callstack/react-native-builder-bob)
diff --git a/packages/react-native-contentpass-cmp-onetrust/README.md b/packages/react-native-contentpass-cmp-onetrust/README.md
new file mode 100644
index 0000000..301f798
--- /dev/null
+++ b/packages/react-native-contentpass-cmp-onetrust/README.md
@@ -0,0 +1,113 @@
+# @contentpass/react-native-contentpass-cmp-onetrust
+
+A [OneTrust](https://www.onetrust.com/) CMP adapter for `@contentpass/react-native-contentpass`. Bridges the OneTrust SDK (`react-native-onetrust-cmp`) to the Contentpass `CmpAdapter` interface, so the Contentpass consent layer can manage consent through OneTrust.
+
+## Installation
+
+```bash
+npm install @contentpass/react-native-contentpass-cmp-onetrust
+# or
+yarn add @contentpass/react-native-contentpass-cmp-onetrust
+```
+
+### Peer dependencies
+
+- `@contentpass/react-native-contentpass`
+- `react-native-onetrust-cmp` — the OneTrust React Native SDK must be installed and configured in your project
+
+## Usage
+
+First, initialize the OneTrust SDK, then create the adapter using `createOnetrustCmpAdapter`:
+
+```tsx
+import OTPublishersNativeSDK from 'react-native-onetrust-cmp';
+import { createOnetrustCmpAdapter } from '@contentpass/react-native-contentpass-cmp-onetrust';
+import type { CmpAdapter } from '@contentpass/react-native-contentpass';
+
+// 1. Start the OneTrust SDK
+await OTPublishersNativeSDK.startSDK(
+ 'cdn.cookielaw.org', // CDN location
+ 'YOUR_APP_ID', // OneTrust app ID
+ 'en', // language code
+ {}, // params
+ false // auto-show banner
+);
+
+// 2. Create the CMP adapter
+const cmpAdapter: CmpAdapter = await createOnetrustCmpAdapter(OTPublishersNativeSDK);
+```
+
+The returned `cmpAdapter` can then be passed to `ContentpassConsentGate` from `@contentpass/react-native-contentpass-ui`, or used directly via the `CmpAdapter` interface.
+
+### Full example
+
+```tsx
+import { useEffect, useState } from 'react';
+import { View, Text } from 'react-native';
+import OTPublishersNativeSDK from 'react-native-onetrust-cmp';
+import { ContentpassSdkProvider } from '@contentpass/react-native-contentpass';
+import { ContentpassConsentGate } from '@contentpass/react-native-contentpass-ui';
+import { createOnetrustCmpAdapter } from '@contentpass/react-native-contentpass-cmp-onetrust';
+import type { CmpAdapter } from '@contentpass/react-native-contentpass';
+
+export default function App() {
+ const [cmpAdapter, setCmpAdapter] = useState(null);
+
+ useEffect(() => {
+ OTPublishersNativeSDK.startSDK('cdn.cookielaw.org', 'YOUR_APP_ID', 'en', {}, false)
+ .then(() => createOnetrustCmpAdapter(OTPublishersNativeSDK))
+ .then((adapter) => setCmpAdapter(adapter))
+ .catch((error) => console.error('Failed to initialize CMP', error));
+ }, []);
+
+ if (!cmpAdapter) {
+ return Loading...;
+ }
+
+ return (
+
+
+
+ Your app content
+
+
+
+ );
+}
+```
+
+For a complete working example, see the [`examples/onetrust`](../../examples/onetrust) directory.
+
+## API
+
+### `createOnetrustCmpAdapter(sdk)`
+
+Factory function that creates a `CmpAdapter` from an initialized OneTrust SDK instance.
+
+| Parameter | Type | Description |
+|-----------|------|-------------|
+| `sdk` | `OTPublishersNativeSDK` | An initialized OneTrust SDK instance (after `startSDK` has resolved). |
+
+Returns `Promise`.
+
+The adapter fetches banner and preference center data from the OneTrust SDK during creation, and automatically extracts TCF purpose IDs and the vendor count.
+
+### `CmpAdapter` methods provided
+
+| Method | Description |
+|--------|-------------|
+| `acceptAll()` | Saves "accept all" consent via OneTrust. |
+| `denyAll()` | Saves "reject all" consent via OneTrust. |
+| `hasFullConsent()` | Checks whether all consent categories are granted. |
+| `onConsentStatusChange(callback)` | Registers a listener that fires whenever full-consent status changes. Returns an unsubscribe function. |
+| `showSecondLayer(view)` | Opens the OneTrust preference center (`'purpose'`) or vendor list (`'vendor'`) UI. The returned promise resolves when the user dismisses it. |
+| `getRequiredPurposes()` | Returns the list of TCF purpose identifiers extracted from OneTrust. |
+| `getNumberOfVendors()` | Returns the vendor count parsed from the OneTrust banner data. |
+| `waitForInit()` | Resolves immediately (OneTrust initialization is handled before adapter creation). |
+
+## License
+
+MIT
diff --git a/packages/react-native-contentpass-cmp-onetrust/src/OnetrustCmpAdapter.ts b/packages/react-native-contentpass-cmp-onetrust/src/OnetrustCmpAdapter.ts
index a1803e0..da6da01 100644
--- a/packages/react-native-contentpass-cmp-onetrust/src/OnetrustCmpAdapter.ts
+++ b/packages/react-native-contentpass-cmp-onetrust/src/OnetrustCmpAdapter.ts
@@ -9,11 +9,9 @@ export async function createOnetrustCmpAdapter(
): Promise {
try {
const bannerData = await sdk.getBannerData();
- console.log('Banner data', bannerData);
const preferenceCenterData: PreferenceCenterData =
await sdk.getPreferenceCenterData();
- console.log('Preference center data', preferenceCenterData);
return new OnetrustCmpAdapter(sdk, bannerData, preferenceCenterData);
} catch (error: any) {
diff --git a/packages/react-native-contentpass-ui/README.md b/packages/react-native-contentpass-ui/README.md
index c3c8637..f7f4689 100644
--- a/packages/react-native-contentpass-ui/README.md
+++ b/packages/react-native-contentpass-ui/README.md
@@ -1,6 +1,6 @@
# @contentpass/react-native-contentpass-ui
-React Native UI components for Contentpass layer rendering.
+Pre-built UI components for integrating the Contentpass consent layer into React Native apps. Renders the Contentpass first-layer consent dialog in a WebView and manages its visibility based on the user's authentication and consent state.
## Installation
@@ -10,18 +10,75 @@ npm install @contentpass/react-native-contentpass-ui
yarn add @contentpass/react-native-contentpass-ui
```
+### Peer dependencies
+
+Make sure the following peer dependencies are installed in your project:
+
+- `@contentpass/react-native-contentpass`
+- `react`
+- `react-native`
+- `react-native-webview`
+
+You also need a CMP adapter (e.g. `@contentpass/react-native-contentpass-cmp-onetrust`) that implements the `CmpAdapter` interface.
+
+## Components
+
+### `ContentpassConsentGate`
+
+A gate component that wraps your app content. It automatically shows the Contentpass consent layer when the user has neither authenticated with Contentpass nor given full CMP consent. Once consent is granted or the user authenticates, the layer is dismissed and the children are displayed.
+
+#### Props
+
+| Prop | Type | Required | Default | Description |
+|------|------|----------|---------|-------------|
+| `children` | `ReactNode` | Yes | — | The app content to render when consent is given or the user is authenticated. |
+| `cmpAdapter` | `CmpAdapter` | Yes | — | A CMP adapter instance (e.g. from `@contentpass/react-native-contentpass-cmp-onetrust`). |
+| `contentpassConfig` | `ContentpassConfig` | Yes | — | The Contentpass SDK configuration object. |
+| `hideAppWhenVisible` | `boolean` | No | `true` | When `true`, the app content is completely replaced by the consent layer. When `false`, the consent layer is rendered as an overlay on top of the app content. |
+| `onVisibilityChange` | `(visible: boolean) => void` | No | — | Callback invoked when the consent layer visibility changes. |
+
## Usage
+Wrap your app with `ContentpassSdkProvider` (from `@contentpass/react-native-contentpass`) and place `ContentpassConsentGate` around the content that should be gated:
+
```tsx
-import { YourComponent } from '@contentpass/react-native-contentpass-ui';
+import { ContentpassSdkProvider } from '@contentpass/react-native-contentpass';
+import { ContentpassConsentGate } from '@contentpass/react-native-contentpass-ui';
+import type { CmpAdapter } from '@contentpass/react-native-contentpass';
+
+function App() {
+ // Initialize your CMP and create an adapter first
+ const cmpAdapter: CmpAdapter = /* ... */;
+ const contentpassConfig = /* your ContentpassConfig */;
-// Use the component
+ return (
+
+
+ {/* Your app content */}
+
+
+ );
+}
```
+For a full working example using OneTrust as the CMP, see the [`examples/onetrust`](../../examples/onetrust) directory.
+
+## How it works
+
+1. `ContentpassConsentGate` waits for both the CMP adapter and the Contentpass SDK to be ready.
+2. It evaluates whether the user needs to see the consent layer: the layer is shown when the user is **neither** authenticated with Contentpass **nor** has given full consent via the CMP.
+3. The consent layer itself is a hosted web page rendered in a WebView (`ContentpassLayer`). It communicates back to the native app via `postMessage` to trigger actions like "accept all", "show CMP details", or "login/signup with Contentpass".
+4. When the user takes an action (e.g. accepts all cookies or logs in), the gate re-evaluates and hides the layer when appropriate.
+
## Requirements
- React Native >= 0.76.0
-- @contentpass/react-native-contentpass (peer dependency)
+- `@contentpass/react-native-contentpass` (peer dependency)
+- `react-native-webview` (peer dependency)
## License
diff --git a/packages/react-native-contentpass-ui/src/components/ContentpassConsentGate.tsx b/packages/react-native-contentpass-ui/src/components/ContentpassConsentGate.tsx
index d9c585a..90167c0 100644
--- a/packages/react-native-contentpass-ui/src/components/ContentpassConsentGate.tsx
+++ b/packages/react-native-contentpass-ui/src/components/ContentpassConsentGate.tsx
@@ -132,13 +132,6 @@ export default function ContentpassConsentGate({
cpAuthState.state === ContentpassStateType.AUTHENTICATED ||
hasFullConsent;
const visible = !isFine;
- console.log(
- '>>> Changing visibility to',
- visible,
- isFine,
- cpAuthState.state,
- hasFullConsent
- );
if (visible !== isVisible) {
onVisibilityChange?.(visible);
}
diff --git a/packages/react-native-contentpass-ui/src/components/ContentpassLayer.tsx b/packages/react-native-contentpass-ui/src/components/ContentpassLayer.tsx
index e10bec3..aea91b0 100644
--- a/packages/react-native-contentpass-ui/src/components/ContentpassLayer.tsx
+++ b/packages/react-native-contentpass-ui/src/components/ContentpassLayer.tsx
@@ -92,7 +92,7 @@ export default function ContentpassLayer({
}
if (!msg || msg.protocol !== MESSAGE_PROTOCOL) {
- console.log('WebView message with unknown protocol', msg.protocol, msg);
+ console.warn('WebView message with unknown protocol', msg.protocol, msg);
return;
}
diff --git a/packages/react-native-contentpass-ui/src/components/buildFirstLayerUrl.test.ts b/packages/react-native-contentpass-ui/src/components/buildFirstLayerUrl.test.ts
index 9b5f80a..dd91f7b 100644
--- a/packages/react-native-contentpass-ui/src/components/buildFirstLayerUrl.test.ts
+++ b/packages/react-native-contentpass-ui/src/components/buildFirstLayerUrl.test.ts
@@ -1,3 +1,4 @@
+import packageJson from '../../package.json';
import buildFirstLayerUrl from './buildFirstLayerUrl';
describe('buildFirstLayerUrl', () => {
@@ -26,7 +27,7 @@ describe('buildFirstLayerUrl', () => {
const url = buildFirstLayerUrl(defaultParams);
const parsed = new URL(url);
- expect(parsed.searchParams.get('theme')).toBe('steps');
+ expect(parsed.searchParams.get('theme')).toBe('classic-app');
});
it('should include the SDK version parameter', () => {
@@ -34,6 +35,9 @@ describe('buildFirstLayerUrl', () => {
const parsed = new URL(url);
expect(parsed.searchParams.get('v')).toBeTruthy();
+ expect(parsed.searchParams.get('v')).toBe(
+ `react-native-contentpass-ui@${packageJson.version}`
+ );
});
it('should set locale to en-US', () => {
diff --git a/packages/react-native-contentpass-ui/src/components/buildFirstLayerUrl.ts b/packages/react-native-contentpass-ui/src/components/buildFirstLayerUrl.ts
index 8755a33..c1ca416 100644
--- a/packages/react-native-contentpass-ui/src/components/buildFirstLayerUrl.ts
+++ b/packages/react-native-contentpass-ui/src/components/buildFirstLayerUrl.ts
@@ -1,9 +1,8 @@
-// const THEME = 'classic';
-// const THEME = 'classic-app';
-const THEME = 'steps';
+import packageJson from '../../package.json';
-// FIXME pass something from this SDK?
-const SDK_VERSION = '20260203105802-ef26e7d899';
+const THEME = 'classic-app';
+
+const SDK_VERSION = `react-native-contentpass-ui@${packageJson.version}`;
export default function buildFirstLayerUrl({
baseUrl,
diff --git a/packages/react-native-contentpass/README.md b/packages/react-native-contentpass/README.md
new file mode 100644
index 0000000..edf2a6d
--- /dev/null
+++ b/packages/react-native-contentpass/README.md
@@ -0,0 +1,180 @@
+# @contentpass/react-native-contentpass
+
+The core Contentpass SDK for React Native. Handles OAuth 2.0 authentication, subscription validation, and impression tracking for Contentpass-enabled apps.
+
+## Installation
+
+```bash
+npm install @contentpass/react-native-contentpass
+# or
+yarn add @contentpass/react-native-contentpass
+```
+
+### Peer dependencies
+
+The following peer dependencies must be installed:
+
+- [react](https://github.com/facebook/react)
+- [react-native](https://github.com/facebook/react-native)
+- [react-native-app-auth](https://github.com/FormidableLabs/react-native-app-auth) — OAuth 2.0 authentication
+- [react-native-encrypted-storage](https://github.com/emeraldsanto/react-native-encrypted-storage) — secure token storage
+
+Some peer dependencies require additional native setup. Refer to their official guides:
+
+- [react-native-app-auth setup](https://commerce.nearform.com/open-source/react-native-app-auth/docs#setup)
+- [react-native-encrypted-storage setup](https://github.com/emeraldsanto/react-native-encrypted-storage?tab=readme-ov-file#installation)
+
+### Expo support
+
+If you are using Expo, run the following command to enable native directory modifications:
+
+```bash
+npx expo prebuild
+```
+
+## Usage
+
+### Initialization
+
+Wrap your app's root component with `ContentpassSdkProvider`:
+
+```tsx
+import { ContentpassSdkProvider } from '@contentpass/react-native-contentpass';
+
+const contentpassConfig = {
+ propertyId: 'property-id',
+ planId: 'plan-id',
+ issuer: 'https://my.contentpass.net',
+ apiUrl: 'https://api.contentpass.net',
+ redirectUrl: 'com.yourapp://oauthredirect',
+ samplingRate: 0.1, // optional, default 0.05
+ logLevel: 'info', // optional, default disabled
+};
+
+export default function App() {
+ return (
+
+
+
+ );
+}
+```
+
+### Configuration
+
+| Property | Type | Required | Description |
+|----------|------|----------|-------------|
+| `propertyId` | `string` | Yes | Your unique property ID (provided by Contentpass). |
+| `planId` | `string` | Yes | The plan ID to check subscriptions against (provided by Contentpass). |
+| `issuer` | `string` | Yes | The OAuth 2.0 server URL (e.g. `https://my.contentpass.net`). |
+| `apiUrl` | `string` | Yes | The Contentpass API base URL. |
+| `redirectUrl` | `string` | Yes | The deep-link URL your app registers for OAuth redirects. |
+| `samplingRate` | `number` | No | Rate at which impression events are sent for unauthenticated users. Default `0.05` (5%). |
+| `logLevel` | `'debug' \| 'info' \| 'warn' \| 'error'` | No | SDK log level. Logging is disabled by default. |
+
+### SDK methods
+
+Access the SDK via the `useContentpassSdk` hook:
+
+```tsx
+import { useContentpassSdk } from '@contentpass/react-native-contentpass';
+
+function MyComponent() {
+ const {
+ authenticate,
+ countImpression,
+ registerObserver,
+ unregisterObserver,
+ logout,
+ recoverFromError,
+ event,
+ } = useContentpassSdk();
+
+ // ...
+}
+```
+
+#### `authenticate(route?)`
+
+Initiates the OAuth 2.0 authentication flow via a modal interface. Validates the user's active Contentpass subscription upon success.
+
+- `route` (optional): `'login'` or `'signup'` to pre-select the auth screen.
+
+#### `countImpression()`
+
+Tracks an impression for the current user. Call this whenever a user views a piece of content. Applies to both authenticated and unauthenticated users (subject to `samplingRate` for unauthenticated users).
+
+#### `registerObserver(observer)`
+
+Registers a callback that fires whenever the authentication/subscription state changes. The callback receives a `ContentpassState` object.
+
+#### `unregisterObserver(observer)`
+
+Removes a previously registered observer.
+
+#### `logout()`
+
+Logs the user out by clearing all stored authentication tokens.
+
+#### `recoverFromError()`
+
+When a background token refresh fails (e.g. due to network issues), the state transitions to `ERROR`. Call this method once connectivity is restored to retry token validation.
+
+#### `event(eventCategory, eventAction, eventLabel?, samplingRate?)`
+
+Sends a custom analytics event to Contentpass.
+
+### State types
+
+The observer callback receives one of the following states:
+
+| State | `hasValidSubscription` | Description |
+|-------|------------------------|-------------|
+| `INITIALISING` | — | SDK is starting up and restoring any stored session. |
+| `UNAUTHENTICATED` | `false` | No user is logged in. |
+| `AUTHENTICATED` | `boolean` | User is logged in. `hasValidSubscription` indicates whether they have an active plan. |
+| `ERROR` | — | A background token refresh failed. Contains an `error` property with the original exception. |
+
+```tsx
+import {
+ ContentpassStateType,
+ useContentpassSdk,
+} from '@contentpass/react-native-contentpass';
+import { useEffect } from 'react';
+
+function MyComponent() {
+ const { registerObserver, unregisterObserver } = useContentpassSdk();
+
+ useEffect(() => {
+ const observer = (state) => {
+ switch (state.state) {
+ case ContentpassStateType.AUTHENTICATED:
+ console.log('Subscription active:', state.hasValidSubscription);
+ break;
+ case ContentpassStateType.ERROR:
+ console.error('SDK error:', state.error);
+ break;
+ }
+ };
+
+ registerObserver(observer);
+ return () => unregisterObserver(observer);
+ }, [registerObserver, unregisterObserver]);
+}
+```
+
+### CMP adapter interface
+
+This package also exports the `CmpAdapter` type, which defines the interface that consent management platform adapters must implement to work with Contentpass. See [`@contentpass/react-native-contentpass-cmp-onetrust`](../react-native-contentpass-cmp-onetrust) for a ready-made OneTrust adapter.
+
+## Integration with Sourcepoint SDK
+
+See the [Sourcepoint SDK integration guide](./docs/SOURCEPOINT_SDK_INTEGRATION.md).
+
+## Contributing
+
+See the [contributing guide](./docs/CONTRIBUTING.md).
+
+## License
+
+MIT