Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

trinadhthatakula/SubscriptionTester

Open more actions menu

Repository files navigation

Thor Logo

Valhalla Billing V8 🛡️

A ruthless, production-ready implementation of Google Play Billing Library 8.3.0.

This project implements a clean, reactive architecture for handling Android subscriptions and one-time purchases without the spaghetti code usually found in billing tutorials.

🏛️ Architecture

We use a unidirectional data flow. The BillingHelper acts as the single source of truth, emitting state updates via StateFlow to the ViewModel, which the UI observes.

graph LR
    GP[Google Play Server] <-->|Connection| BH[BillingHelper]
    BH -->|StateFlow| VM[ViewModel]
    VM -->|UiState| UI[Compose UI]
    UI -->|User Intent| VM
    VM -->|Launch Flow| BH

Loading

🚀 Setup Checklist (The Ruthless Protocol)

CRITICAL WARNING: 99% of "Item Unavailable" errors occur because developers skip steps 3, 4, or 5. Do not skip them.

  • 1. Product IDs: Ensure IDs in BillingHelper.kt match your Play Console exactly.

  • Default: premium_sub (Subscription)

  • Default: premium_user (One-Time)

  • 2. Signed Build: You MUST sign your APK/AAB with your Release Keystore (or the specific debug key uploaded to the Console). The default Android debug key will fail.

  • 3. Internal Track: Upload your build to the Internal Testing track on the Play Console.

  • 4. Testers List: Add your Gmail address to the testers list in the Console.

  • 5. Accept Invite (CRITICAL): Copy the "Join on Web" link from the testers tab, open it on your phone, and click ACCEPT INVITE.

  • 6. License Testing: Go to Setup > License Testing in the Console and add your email. Set the response to RESPOND_NORMALLY.

💻 Integration Guide

1. Dependency Injection (Koin)

Inject the BillingHelper as a singleton. It manages its own lifecycle and connection retry logic.

val appModule = module {
    // Singleton Billing Client
    single { BillingHelper(androidContext()) }
    
    // ViewModel Injection
    viewModel { SubscriptionViewModel(get()) }
}

2. ViewModel Usage

The BillingHelper exposes hot flows (productDetailsList, activeSubsList). Merge them into a generic UiState in your ViewModel.

class SubscriptionViewModel(helper: BillingHelper) : ViewModel() {
    
    val uiState = combine(helper.productDetailsList, helper.activeSubsList) { products, purchases ->
        // Map to your UI state
    }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), SubscriptionUiState())

    fun buy(activity: Activity, product: ProductDetails) {
        helper.launchPurchaseFlow(activity, product)
    }
}

3. Handling Acknowledgment

Purchases are automatically acknowledged in BillingHelper.processPurchases().

Caution

If you remove the acknowledgment logic, Google will automatically refund the user after 3 days.

🔧 Troubleshooting

Error Cause Fix
Billing Unavailable Wrong build variant Use a Signed Release build or add your debug key to the Console.
Empty Product List User not a tester Accept the Invite via the web link in the Internal Testing tab.
Feature Not Supported Emulator issue Use a physical device or an Emulator image with "Google Play Store" icon.

🤝 Contributing

Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.

📝 License

MIT

Releases

Packages

Used by

Contributors

Languages

Morty Proxy This is a proxified and sanitized view of the page, visit original site.