In-App Purchase Diagnostics

Inspect and validate StoreKit and Google Play purchase evidence safely on simulators and emulators across every Ansight SDK.

Purchase observations, bounded retention, validation, and six purchases.* remote tools are part of the base SDK. Swift and Kotlin own the native implementations used by React Native, Flutter, and Cordova/Capacitor. .NET exposes the same contract from Ansight.Core.

These tools inspect an application’s purchase integration. They do not initiate purchases, restore access, finish transactions, consume items, acknowledge payments, or alter StoreKit or Google Play test state. Exercise the app’s normal purchase UI, report the resulting evidence, and use the tools to validate it.

Simulator-Only Safeguard

Every reporting, validation, refresh, bridge, and remote-tool entry point checks the environment before doing work.

RuntimeAccess
Apple simulatorAllowed
Android emulatorAllowed only for recognized ranchu or goldfish emulator hardware
Physical device, including sandbox, TestFlight, and license testersBlocked
macOS, Mac Catalyst, plain .NET desktop/server, or unknown runtimeBlocked

Unavailable discovery reports purchases_environment_not_allowed. There is no public option, environment variable, or remote switch that bypasses the check. The suite is also read-only with respect to the store: it cannot initiate or complete a payment.

What Ships

SDKIntegration
.NET / MAUIModels and tools in Ansight.Core; StoreKit 2 through the Apple binding; binding-neutral Google Play callback helper
AndroidModels and tools in ansight-core-android; optional ansight-purchases-googleplay-android callback adapter for Play Billing 9.1.0
iOS / SwiftCore models and tools plus StoreKit 2 refresh in AnsightCore on iOS 15+
React NativeTyped purchases facade over the native core
FlutterAnsight.instance.purchases and typed observation/product inputs over the native core
Cordova / CapacitorTyped purchases export over the native core and Cordova plugin transport

The Google Play adapter does not create or own a BillingClient and does not bundle the billing library. Add Play Billing 9.1.0 to the app and forward its existing callbacks, or report observations through the base API directly.

Tool Contract

All six tools require Read policy and opt-in registration. Every response uses schema: ansight.purchases.v1 and identifies its coverage as observedOnly.

Tool IDArguments
purchases.get_stateOptional productId
purchases.query_productsOptional productId
purchases.query_transactionsOptional productId; returns retained observations, not full store history
purchases.get_entitlementsOptional productId; preserves store, app, and backend evidence separately
purchases.get_eventsOptional after cursor; returns nextCursor and cursorGap
purchases.validateRequired productId and expectedEntitled; optional transaction, verification, delivery-count, and freshness checks

Reads query a bounded cache; they never refresh or repair the application. An absent product means not observed, not invalid. The SDK retains at most 256 observations and 256 products. Clear diagnostics when the app account changes.

Registration and Reporting

.NET / MAUI

using Ansight.Purchases;

builder.WithPurchaseTools();
await PurchaseStoreKit.RefreshAsync(["premium.monthly"]);

PurchaseDiagnostics.Shared.Record(new PurchaseObservation(
    "premium.monthly",
    "app",
    DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(),
    Entitled: entitlementService.HasPremium,
    DeliveryCount: deliveryLedger.GrantCount));

On Android, forward primitive purchase fields to PurchaseGooglePlay.Record(...). Report the backend’s verification and account binding as a separate backend observation.

iOS / Swift

import AnsightCore

try AnsightRuntime.shared.registerPurchaseTools()
try await PurchaseStoreKit.refresh(productIds: ["premium.monthly"])

var observation = PurchaseObservation(
    productId: "premium.monthly",
    source: "app"
)
observation.entitled = entitlementService.hasPremium
try PurchaseDiagnostics.shared.record(observation)

StoreKit refresh reads product metadata and the latest transaction per product. It does not own transaction updates, finish transactions, or retrieve a complete transaction history.

Android / Kotlin

import ai.ansight.runtime.purchases.*
import ai.ansight.purchases.googleplay.GooglePlayPurchaseObserver

builder.withPurchaseTools()
val observer = GooglePlayPurchaseObserver()

// Forward the app's existing PurchasesUpdatedListener callbacks.
purchases.forEach { observer.record(it, productType = "subscription") }

PurchaseDiagnostics.shared.record(PurchaseObservation(
    productId = "premium.monthly",
    source = "app",
    entitled = entitlementService.hasPremium,
))
dependencies {
    debugImplementation("ai.ansight:ansight-purchases-googleplay-android:1.5.0")
    implementation("com.android.billingclient:billing-ktx:9.1.0")
}

React Native and Cordova / Capacitor

// React Native: import { purchases } from "@ansight/react-native";
import { purchases } from "@ansight/capacitor";

await purchases.register();
await purchases.refreshStoreKit(["premium.monthly"]); // Apple only
await purchases.record({
  productId: "premium.monthly",
  source: "app",
  entitled: true,
  deliveryCount: 1,
});

const report = await purchases.validate({
  productId: "premium.monthly",
  expectedEntitled: true,
  expectedDeliveryCount: 1,
});

Flutter

await Ansight.instance.purchases.register();
await Ansight.instance.purchases.record(const PurchaseObservation(
  productId: 'premium.monthly',
  source: 'app',
  entitled: true,
));

final report = await Ansight.instance.purchases.validate(
  productId: 'premium.monthly',
  expectedEntitled: true,
);

Calling from the CLI or a Workspace

Use the same tool IDs from the CLI:

ansight app call <session-id> purchases.get_state \
  --arguments-json '{"productId":"premium.monthly"}' --json
ansight app call <session-id> purchases.validate \
  --arguments-json '{"productId":"premium.monthly","expectedEntitled":true}' --json

Workspace tasks and triggers can call purchase tools through the generic tool surface; no task descriptor declaration is required:

const result = await app.callTool("purchases.validate", {
  productId: "premium.monthly",
  expectedEntitled: true,
  expectedDeliveryCount: 1,
});

Evidence and Validation Semantics

Observations use store, app, or backend as their source. Backend evidence is reported by the app; it is not an independent server audit. Verification is verified, unverified, or unknown, and finished, acknowledged, and consumed remain separate states.

purchases.validate compares the newest observation from each required source. Missing, future, or stale evidence is inconclusive. An explicit mismatch or verification rejection fails. A completed tool call can therefore succeed while the returned validation report is failed or inconclusive.

Use createTransactionReference / CreateTransactionReference to hash a provider identifier into an opaque, instance-local reference. Native adapters do not export raw transaction IDs, purchase tokens, receipts, signed payloads, or account tokens. Use UI tools after validation to verify that the purchased feature is actually visible and usable.