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

# React Native Web Entitlements

> Real-time subscription unlock for web-purchased subscriptions

<Info>
  **Supported on:** iOS SDK `1.0.70+` · Android SDK `1.0.42+` · React Native SDK `1.0.7+` (New Architecture only)
</Info>

Web entitlements enable apps that sell subscriptions via web checkout (e.g., Stripe) to unlock premium features in the native app in real-time. After calling `identify()`, the SDK listens for entitlement changes automatically -- when a user purchases on the web, the app unlocks instantly without a restart.

## How It Works

1. User purchases a subscription on your website (via Stripe or another web payment provider).
2. Your backend writes the entitlement to AppDNA.
3. The SDK detects the change in real-time and updates the local cache.
4. Your app reacts via the subscription callback and unlocks premium features.

<Info>
  Web entitlements are separate from in-app purchase entitlements (StoreKit on iOS, Play Billing on Android). They are designed for apps that also sell subscriptions through a web checkout flow.
</Info>

## Check Current Entitlement

After `identify()`, the current web entitlement is available via an async getter on `AppDNA`. The native SDK returns the cached value immediately when available:

```typescript theme={null}
import { AppDNA } from '@appdna-ai/react-native-sdk';

declare function unlockPremium(): void;

const entitlement = await AppDNA.getWebEntitlement();
if (entitlement && entitlement.isActive) {
  // User has an active web subscription
  unlockPremium();
  console.log(`Plan: ${entitlement.planName ?? 'unknown'}, Status: ${entitlement.status}`);
  if (entitlement.currentPeriodEnd) {
    // A Unix timestamp in SECONDS — not an ISO string.
    console.log(`Renews: ${new Date(entitlement.currentPeriodEnd * 1000).toISOString()}`);
  }
}
```

## Listen for Changes

`AppDNA.onWebEntitlementChanged` is backed by a native event. Subscribe to react in real time when the entitlement status changes:

```typescript theme={null}
declare function unlockPremium(): void;
declare function lockPremium(): void;

const unsubscribe = AppDNA.onWebEntitlementChanged((entitlement) => {
  if (entitlement?.isActive === true) {
    unlockPremium();
  } else {
    lockPremium();
  }
});

// Remember to unsubscribe when no longer needed
// unsubscribe();
```

The callback fires immediately when:

* A new web subscription is purchased
* A subscription renews
* A subscription expires or is canceled
* A subscription enters a grace period

## WebEntitlement

| Property           | Type      | Description                                                                           |                                              |
| ------------------ | --------- | ------------------------------------------------------------------------------------- | -------------------------------------------- |
| `isActive`         | `boolean` | Whether the subscription is currently active                                          |                                              |
| `planName`         | \`string  | null\`                                                                                | Name of the subscription plan                |
| `priceId`          | \`string  | null\`                                                                                | Stripe price identifier                      |
| `status`           | `string`  | Current status (see below)                                                            |                                              |
| `currentPeriodEnd` | `number`  | End of the current billing period — Unix timestamp in **seconds** (not an ISO string) |                                              |
| `interval`         | \`string  | null\`                                                                                | Billing interval (e.g., `"month"`, `"year"`) |
| `trialEnd`         | `number`  | Trial expiration — Unix timestamp in **seconds**, if in a trial                       |                                              |

### Entitlement Statuses

| Status     | Description                                                      |
| ---------- | ---------------------------------------------------------------- |
| `active`   | Subscription is active and paid                                  |
| `trialing` | User is in a free trial period                                   |
| `past_due` | Payment failed, in grace period                                  |
| `canceled` | Subscription is canceled (access may continue until period ends) |

## Access

Web entitlements are accessed as top-level members on `AppDNA`:

```typescript theme={null}
// Current value (one-shot Promise)
const webEntitlement = await AppDNA.getWebEntitlement();

// Live subscription of changes (returns an unsubscribe function)
const unsubscribe = AppDNA.onWebEntitlementChanged((entitlement) => { /* ... */ });
```

## Auto-Tracked Events

| Event                       | Trigger                                 |
| --------------------------- | --------------------------------------- |
| `web_entitlement_activated` | Web subscription becomes active         |
| `web_entitlement_expired`   | Web subscription expires or is canceled |

## Full Example

```typescript theme={null}
import { AppDNA, WebEntitlement } from '@appdna-ai/react-native-sdk';

export class PremiumManager {
  private unsubscribe: (() => void) | null = null;

  setup(): void {
    // Listen for web entitlement changes
    this.unsubscribe = AppDNA.onWebEntitlementChanged((entitlement) => {
      this.updateAccessState(entitlement);
    });
  }

  dispose(): void {
    this.unsubscribe?.();
    this.unsubscribe = null;
  }

  async checkAccessOnLaunch(): Promise<void> {
    // Check in-app purchase entitlements
    const hasIAP = await AppDNA.billing.hasActiveSubscription();
    if (hasIAP) {
      this.unlockPremium();
      return;
    }

    // Check web entitlement
    const web = await AppDNA.getWebEntitlement();
    if (web && web.isActive) {
      this.unlockPremium();
      return;
    }

    // No active subscription from either source
    this.lockPremium();
  }

  private async updateAccessState(entitlement: WebEntitlement | null): Promise<void> {
    if (entitlement?.isActive === true) {
      this.unlockPremium();
    } else {
      // Re-check IAP entitlements before locking
      const hasIAP = await AppDNA.billing.hasActiveSubscription();
      if (!hasIAP) {
        this.lockPremium();
      }
    }
  }

  private unlockPremium(): void {
    // Enable premium features
  }

  private lockPremium(): void {
    // Disable premium features
  }
}
```

<Note>
  Web entitlements require Stripe (or compatible payment provider) integration configured in the Console under **Settings > Billing > Web Payments**. The real-time listener activates automatically after `identify()` is called.
</Note>
