# Dart core SDK

`appvision_iap_client` 0.5.0 is the public-client core for Dart and Flutter. It uses `package:http` across mobile, desktop, and web and requires Dart 3.8 or later. It accepts signed store evidence from your application's storefront integration; it does not launch a purchase UI.

## Add the package from source

Public registry publication is disabled in this preview. Use the approved source checkout:

```yaml
dependencies:
  appvision_iap_client:
    path: ../cherrysub/sdk/dart
```

Adjust the path, then run `dart pub get` or `flutter pub get`. Flutter apps using the official purchase plugin can use the higher-level [Flutter storefront adapter](/docs/flutter).

## Configure authentication

```dart
import 'package:appvision_iap_client/appvision_iap_client.dart';

final tokens = InMemoryCustomerTokenProvider(
  fetchToken: (appUserId) async {
    final response = await sessionApi.createIapToken(appUserId);
    return CustomerToken(
      token: response.token,
      expiresAt: DateTime.parse(response.expiresAt),
    );
  },
);
final iap = IapClient(
  baseUrl: Uri.parse('https://api-staging.cherrysub.com'),
  publishableKey: 'iap_pk_REPLACE_WITH_YOUR_KEY',
  customerTokenProvider: tokens.provider,
);
```

`sessionApi` is your own authenticated broker client. Adapt its field decoding to the HTTP `expires_at` token envelope. Only a publishable key belongs in this configuration; the broker keeps the app secret on your backend.

## Read presentation and submit evidence

```dart
final productSet = await iap.currentProductSet(user.id);

await iap.submitGooglePurchase(
  appUserId: user.id,
  purchaseToken: purchaseToken,
  productType: GoogleProductType.subscription,
);
final subscriber = await iap.subscriber(user.id);
final hasPro = subscriber.accessLevels['pro']?.active == true;
```

This example submits a subscription. Use the one-time-product type for a Google in-app product. Apple submission needs the actual transaction JWS from the store bridge. The core is submit-only: your application must complete the storefront purchase only after success, or use the Flutter adapter to coordinate that ordering.

A null current product set is an explicit configuration state. A subscriber 404 before the first qualifying receipt is not a paid subscription. Your UI must handle both without inventing a fallback purchase or access grant.

## Manage lifetime and resources

The token provider caches per user, refreshes 30 seconds before expiry by default, and coalesces simultaneous refreshes. Invalidating a user prevents an earlier refresh from repopulating that user's cache. The SDK performs one forced refresh after a 401 and does not universally retry other statuses.

```dart
tokens.invalidate(user.id);
iap.close();
```

Close the client when its owning application service is disposed. An internally created HTTP client is closed by `IapClient`; an injected client remains caller-owned. Clear application-owned access state on logout and use server-side token revocation when your policy requires it.

## Test the core and the storefront separately

Run `dart test` in `sdk/dart` for the core's transport, token lifecycle, product-set, and lossless-ID behavior. Preserve event IDs as strings on Dart Web. Then test your store bridge on the actual target platform; passing a mock HTTP test is not evidence that StoreKit/Play purchase completion is wired correctly.

See [receipts and restores](/docs/receipts-and-restores) for retry/ownership rules and [testing](/docs/testing) for end-to-end acceptance cases.
