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

# Android Web Entitlements

> Real-time subscription unlock for web-purchased subscriptions

<Info>
  **Supported on:** Android SDK `1.0.33+`
</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 callback and unlocks premium features.

<Info>
  Web entitlements are separate from in-app purchase entitlements (Play Billing). 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 synchronously from cache:

```kotlin theme={null}
import java.util.Date

val entitlement = AppDNA.webEntitlement
if (entitlement != null && entitlement.isActive) {
    // User has an active web subscription
    unlockPremium()
    Log.d("WebEnt", "Plan: ${entitlement.planName ?: "unknown"}, Status: ${entitlement.status}")
    val periodEnd = entitlement.currentPeriodEnd // Long? — Unix seconds
    if (periodEnd != null) {
        Log.d("WebEnt", "Renews: ${Date(periodEnd * 1000L)}")
    }
}
```

## Listen for Changes

Register a callback to react in real-time when the entitlement status changes:

```kotlin theme={null}
AppDNA.onWebEntitlementChanged { entitlement ->
    if (entitlement?.isActive == true) {
        unlockPremium()
    } else {
        lockPremium()
    }
}
```

This 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?` | Name of the subscription plan                               |
| `priceId`          | `String?` | Stripe price identifier                                     |
| `status`           | `String`  | Current status (see below)                                  |
| `currentPeriodEnd` | `Long?`   | End of the current billing period (Unix timestamp, seconds) |
| `interval`         | `String?` | Billing interval (e.g., `"month"`, `"year"`)                |
| `trialEnd`         | `Long?`   | Trial expiration (Unix timestamp, 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 a top-level property on `AppDNA`:

```kotlin theme={null}
val webEntitlement = AppDNA.webEntitlement
```

## Auto-Tracked Events

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

## Full Example

```kotlin theme={null}
import ai.appdna.sdk.AppDNA
import ai.appdna.sdk.webentitlements.WebEntitlement
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch

class PremiumManager(
    private val scope: CoroutineScope = CoroutineScope(Dispatchers.Main),
) {

    fun setup() {
        // Listen for web entitlement changes
        AppDNA.onWebEntitlementChanged { entitlement ->
            updateAccessState(entitlement)
        }
    }

    fun checkAccessOnLaunch() {
        scope.launch {
            // Check in-app purchase entitlements
            val hasIAP = AppDNA.billing.hasActiveSubscription()
            if (hasIAP) {
                unlockPremium()
                return@launch
            }

            // Check web entitlement
            val web = AppDNA.webEntitlement
            if (web != null && web.isActive) {
                unlockPremium()
                return@launch
            }

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

    private fun updateAccessState(entitlement: WebEntitlement?) {
        if (entitlement?.isActive == true) {
            unlockPremium()
        } else {
            // Re-check IAP entitlements before locking
            scope.launch {
                val hasIAP = AppDNA.billing.hasActiveSubscription()
                if (!hasIAP) {
                    lockPremium()
                }
            }
        }
    }

    private fun unlockPremium() {
        // Enable premium features
    }

    private fun 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>
