Supported on: Android SDK
1.0.33+Configuration
The Android SDK uses Google Play Billing exclusively — there is nobillingProvider configuration option. You enable billing simply by calling AppDNA.configure(...) from your Application.onCreate() and ensuring the Play Billing dependency is on your classpath (the SDK pulls it in transitively).
RevenueCat and Adapty bridges are iOS-only. On Android, all billing flows route through the native
NativeBillingManager against Google Play. If you have a multi-platform app and use one of those providers on iOS, your Android implementation will exchange the same product IDs but go directly through Play.Module Access
Access the billing module through theAppDNA.billing property:
Get Products
Retrieve product details for one or more product IDs (suspend function — call from a coroutine, or use theFuture overload from Java):
AppDNA.billing.getProductsFuture(productIds): CompletableFuture<List<ProductInfo>>.
ProductInfo
| Property | Type | Description |
|---|---|---|
id | String | Product identifier |
name | String | Display name |
description | String | Localized product description |
formattedPrice | String | Localized price string (e.g., “$9.99”) |
priceMicros | Long | Price in micros (e.g., 9990000) |
currencyCode | String | ISO 4217 currency code (e.g., “USD”) |
offerToken | String? | Offer token for the subscription offer |
Purchase a Subscription
Initiate a purchase flow for a specific product.purchase is a suspend function that returns a TransactionInfo on success and requires the current Activity to host the Play Billing flow:
AppDNABillingDelegate (see below).
Promotional offers
Pass aPurchaseOptions to select a specific subscription offer token:
Server-side receipt verification is performed automatically by the SDK. You do not need to send purchase tokens to your own server for validation.
Restore Purchases
Restore previously purchased subscriptions and one-time products (e.g., after a device change or reinstall). Suspend function — returnsList<String> of restored product IDs:
Restoring purchases queries Google Play for all active subscriptions and updates entitlements accordingly. This is useful after a reinstall or device change.
Entitlements
Retrieve the current user’s entitlements (suspend function):Entitlement
| Property | Type | Description |
|---|---|---|
productId | String | Product identifier |
store | String | Store identifier (e.g., "play_store") |
status | String | Status (e.g., "active", "expired") |
expiresAt | String? | ISO 8601 expiration timestamp |
isTrial | Boolean | Whether this is a free trial |
offerType | String? | Offer type if applicable |
Check Active Subscription
Quickly check if the user has any active subscription:Listen for Entitlement Changes
Register a listener to be notified when entitlements change (e.g., subscription renewal, expiration, or new purchase):AppDNABillingDelegate
All 4 methods on this delegate fire from the active billing bridge (Google PlayNativeBillingManager). Register via AppDNA.billingDelegate = yourHandler.
onPurchaseCompletedandonPurchaseFailedfire for every purchase regardless of entry point — paywall-driven OR directAppDNA.billing.purchase(...)calls.onRestoreCompletedfires after every successful restore.onEntitlementsChangedfires whenever entitlements change (also broadcast via the localEntitlementCache).
AppDNAPaywallDelegate: when a purchase comes from a paywall, both AppDNAPaywallDelegate.onPaywallPurchaseCompleted AND AppDNABillingDelegate.onPurchaseCompleted fire. Each represents a different layer (paywall lifecycle vs platform billing). Pick one as your source of truth for your analytics — they will not double-count if you only listen on one.
For more granular control, implement the AppDNABillingDelegate interface:
Example Implementation
Data Types
TransactionInfo
| Property | Type | Description |
|---|---|---|
transactionId | String | Google Play transaction identifier |
productId | String | Product identifier |
purchaseDate | String | ISO 8601 purchase timestamp |
environment | String | "production" or "sandbox" |
PurchaseResult
ThePurchaseResult sealed class represents all possible outcomes of a purchase:
| Variant | Description |
|---|---|
PurchaseResult.Purchased(entitlement) | Purchase succeeded |
PurchaseResult.Cancelled | User cancelled the purchase flow |
PurchaseResult.Pending | Purchase is pending (e.g., awaiting payment) |
PurchaseResult.Unknown | Unknown result |
PurchaseResult.Failed(error) | Purchase failed with an error |
Auto-Tracked Events
The billing module automatically tracks the following events:| Event | Triggered When |
|---|---|
billing_purchase_requested | A purchase call has been queued and is about to launch the Play Billing flow |
purchase_started | A purchase flow is initiated |
purchase_completed | A purchase completes successfully |
purchase_failed | A purchase fails with an error |
purchase_canceled | The user cancels the purchase flow |
purchase_pending | A purchase enters pending state |
purchase_restored | An individual purchase is restored |
subscription_renewed | An auto-renewing subscription successfully renews |
subscription_canceled | A subscription is cancelled (access may continue until period end) |
subscription_renewal_failed | A renewal attempt fails (payment issue) |
These events include the product identifier and relevant metadata. They are tracked regardless of whether you implement
AppDNABillingDelegate.The SDK automatically acknowledges purchases within 3 days as required by Google Play. If a purchase is not acknowledged within this window, Google Play will automatically refund it. The
NativeBillingManager also handles ITEM_ALREADY_OWNED errors by automatically triggering a restore flow.Full Example
Next Steps
- Configure Onboarding flows to guide new users
- Present Paywalls with integrated purchase handling
- Learn about Offline Support for billing resilience

