> ## 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.

# Flutter Web Entitlements

> Real-time subscription unlock for web-purchased subscriptions

<Info>
  **Supported on:** iOS SDK `1.0.61+` · Android SDK `1.0.33+` · Flutter SDK `1.0.3+`
</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 stream 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:

```dart theme={null}
final entitlement = await AppDNA.webEntitlement;
if (entitlement != null && entitlement.isActive) {
  // User has an active web subscription
  unlockPremium();
  print("Plan: ${entitlement.planName ?? "unknown"}, Status: ${entitlement.status}");
  if (entitlement.currentPeriodEnd != null) {
    print("Renews: ${entitlement.currentPeriodEnd}");
  }
}
```

## Listen for Changes

`AppDNA.onWebEntitlementChanged` is a broadcast `Stream<WebEntitlement?>` backed by a Flutter `EventChannel`. Subscribe to react in real-time when the entitlement status changes:

```dart theme={null}
final subscription = AppDNA.onWebEntitlementChanged.listen((entitlement) {
  if (entitlement?.isActive == true) {
    unlockPremium();
  } else {
    lockPremium();
  }
});

// Remember to cancel the subscription when no longer needed
// subscription.cancel();
```

The stream emits 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`         | `bool`      | Whether the subscription is currently active |
| `planName`         | `String?`   | Name of the subscription plan                |
| `priceId`          | `String?`   | Stripe price identifier                      |
| `status`           | `String`    | Current status (see below)                   |
| `currentPeriodEnd` | `DateTime?` | End of the current billing period            |
| `interval`         | `String?`   | Billing interval (e.g., `"month"`, `"year"`) |
| `trialEnd`         | `DateTime?` | Trial expiration date, 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`:

```dart theme={null}
// Current value (one-shot Future)
final webEntitlement = await AppDNA.webEntitlement;

// Live stream of changes (broadcast Stream)
AppDNA.onWebEntitlementChanged.listen((entitlement) { /* ... */ });
```

## Auto-Tracked Events

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

## Full Example

```dart theme={null}
import 'dart:async';
import 'package:appdna_sdk/appdna_sdk.dart';

class PremiumManager {
  StreamSubscription<WebEntitlement?>? _webSub;

  void setup() {
    // Listen for web entitlement changes
    _webSub = AppDNA.onWebEntitlementChanged.listen((entitlement) {
      _updateAccessState(entitlement);
    });
  }

  void dispose() {
    _webSub?.cancel();
  }

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

    // Check web entitlement
    final web = await AppDNA.webEntitlement;
    if (web != null && web.isActive) {
      unlockPremium();
      return;
    }

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

  Future<void> _updateAccessState(WebEntitlement? entitlement) async {
    if (entitlement?.isActive == true) {
      unlockPremium();
    } else {
      // Re-check IAP entitlements before locking
      final hasIAP = await AppDNA.billing.hasActiveSubscription();
      if (!hasIAP) {
        lockPremium();
      }
    }
  }

  void unlockPremium() {
    // Enable premium features
  }

  void lockPremium() {
    // 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>
