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.
| Runtime | Access |
|---|---|
| Apple simulator | Allowed |
| Android emulator | Allowed only for recognized ranchu or goldfish emulator hardware |
| Physical device, including sandbox, TestFlight, and license testers | Blocked |
| macOS, Mac Catalyst, plain .NET desktop/server, or unknown runtime | Blocked |
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
| SDK | Integration |
|---|---|
| .NET / MAUI | Models and tools in Ansight.Core; StoreKit 2 through the Apple binding; binding-neutral Google Play callback helper |
| Android | Models and tools in ansight-core-android; optional ansight-purchases-googleplay-android callback adapter for Play Billing 9.1.0 |
| iOS / Swift | Core models and tools plus StoreKit 2 refresh in AnsightCore on iOS 15+ |
| React Native | Typed purchases facade over the native core |
| Flutter | Ansight.instance.purchases and typed observation/product inputs over the native core |
| Cordova / Capacitor | Typed 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 ID | Arguments |
|---|---|
purchases.get_state | Optional productId |
purchases.query_products | Optional productId |
purchases.query_transactions | Optional productId; returns retained observations, not full store history |
purchases.get_entitlements | Optional productId; preserves store, app, and backend evidence separately |
purchases.get_events | Optional after cursor; returns nextCursor and cursorGap |
purchases.validate | Required 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.