Purchase a Package and track the outcome
Package-based store/Direct purchases, Kotlin Activity, attempt keys and restart-safe Android purchase outcomes.
Fetch the selected offering and retain the original Package. The SDK derives product/offering/package identifiers and product type; don't reconstruct those from display text.
Flutter
packageToPurchase is required. idempotencyKey, paywallId, and paywallRevision default to null; attribution ID/revision must be a matching paywall pair. The SDK generates a key when omitted. For host restart-safe correlation, save a valid key before purchase and reuse it only for that logical attempt: 1–128 letters/digits or _ . : -.
Android supplies its Activity through the bridge. New purchase params have no API key, market/RSA, discount amount, isCrypto or product-type override. Convenience purchase(params) is nullable; prefer Result for financial decisions.
Kotlin: store and Direct
A new store purchase requires the Activity; the Activity-free Builder serves Direct or existing paid work. Do not retain Activity-backed params past its lifecycle. Optional paywallAttribution(id, revision) is valid Direct presentation context, not a price setter.
Operation success versus payment completion
| State | Host action |
|---|---|
NEEDTOPAY with URL | Continue in a trusted browser; no access grant |
PROCESSING or uncertain retryable failure | Wait/reconcile the same attempt; no second payment |
COMPLETED, RESTORED, ALREADY_PROCESSED | Read CustomerInfo for access |
DELIVERY_REQUIRED | Durably grant the consumable once |
CONSUME_REQUIRED | Complete consumption without re-crediting |
REJECTED | No access/inventory grant |
Not every enum state arrives as Success; some pending states are recoverable Failures. Flutter exposes mobile.storePurchaseStatus, Kotlin storePurchaseStatus. isSuccess describes the SDK operation only.
Direct payments
The host opens the returned URL. On mobile await InappifySdk.isPaymentUrlAllowed(url) and require success/data true; Kotlin's extension client.isPaymentUrlAllowed(url) returns Boolean. Follow the SDK destination policy; never substitute arbitrary query/user URLs.
New Android Direct results retain the original attempt through compatible backend support. On iOS this release's key only correlates local result/failure; it is not sent in the Direct request. Replaying it does not promise server deduplication. Reconcile uncertainty before another payment.
Web has a separate checkout contract and verifies returns using checkout ID; follow the Web example. A return link, browser closure or successful checkout creation is not proof of payment.
Android: track across process restart
Subscribe before purchase and cancel when the UI owner ends. Kotlin client.addEventListener { event -> event.purchaseAttempt ... } returns a closable registration. getPurchaseAttempts() also reads current-scope history without HTTP/store calls.
Attempt statuses are pending/succeeded/failed. Successful null lookup means unknown, not failed. Android history is encrypted, bounded and app/customer/market-scoped. Events can repeat or be missed while closed; query the saved attempt after configure. Recorded success is not current CustomerInfo or durable inventory authority.
Network failure, account change, empty sync results or unrelated events must not erase the host attempt or start a new payment. Attempt-history APIs are unsupported on iOS and Flutter Web.