BillingX is a library that makes in-app billing testing easier. It allows adding test products and purchases to a local data store without requiring a connection to the Google Play API. During development, the app uses this local data store instead of the real API. This allows testing purchases and managing inventory without publishing apps or using test accounts. The library provides a seamless experience for both testing and production by swapping the data source based on build type.
3. AIDL
• Copy Google’s IInAppBillingService.aidl
• Connect to Play Store via Service Connection
• Manually manage connection
• Communicate directly with IAB service
27. Test Accounts
1. Add test user accounts in Google Play Console
2. Deploy your app on Google Play
28. Test Accounts
1. Add test user accounts in Google Play Console
2. Deploy your app on Google Play
3. Create products in the Google Play Console
29. Test Accounts
1. Add test user accounts in Google Play Console
2. Deploy your app on Google Play
3. Create products in the Google Play Console
4. Install Release build on a device
30. Test Accounts
1. Add test user accounts in Google Play Console
2. Deploy your app on Google Play
3. Create products in the Google Play Console
4. Install Release build on a device
5. Make a purchase with a test account
31. Test Accounts - Problems
• Must know test user accounts in advance
• Purchasable items must be published
• Must run on a device
• Must be signed with release keys
• Can’t manually manage subscription state
32. Test Accounts - Problems
• Must know test user accounts in advance
• Purchasable items must be published
• Must run on a device
• Must be signed with release keys
• Can’t manually manage subscription state
• Subscription testing is slow
37. Goals
• Easy integration with minimal code changes
• No debug code in production
• Allow easy inventory/transaction management
• Seamless user experience
Easily test in app purchases and subscriptions
54. Goals
• Easy integration with minimal code changes
• No debug code in production
• Allow easy inventory/transaction management
• Seamless user experience
Easily test in app purchases and subscriptions
55. Goals
✓Easy integration with minimal code changes
• No debug code in production
• Allow easy inventory/transaction management
• Seamless user experience
Easily test in app purchases and subscriptions
56. Goals
✓Easy integration with minimal code changes
✓No debug code in production
• Allow easy inventory/transaction management
• Seamless user experience
Easily test in app purchases and subscriptions
58. BillingStore Google Play
Library Code
Your Code
BillingStore
IInAppBillingServicePlay Billing Library
MyApp
BillingX
Debug Release
59. BillingStore
interface BillingStore {
fun getSkuDetails(params: SkuDetailsParams): List<SkuDetails>
fun getPurchases(skuType: String): Purchase.PurchasesResult
fun addProduct(skuDetails: SkuDetails): BillingStore
fun removeProduct(sku: String): BillingStore
fun clearProducts(): BillingStore
fun addPurchase(purchase: Purchase): BillingStore
fun removePurchase(sku: String): BillingStore
fun clearPurchases(): BillingStore
}
60. BillingStore
interface BillingStore {
fun getSkuDetails(params: SkuDetailsParams): List<SkuDetails>
fun getPurchases(skuType: String): Purchase.PurchasesResult
fun addProduct(skuDetails: SkuDetails): BillingStore
fun removeProduct(sku: String): BillingStore
fun clearProducts(): BillingStore
fun addPurchase(purchase: Purchase): BillingStore
fun removePurchase(sku: String): BillingStore
fun clearPurchases(): BillingStore
}
61. BillingStore
interface BillingStore {
fun getSkuDetails(params: SkuDetailsParams): List<SkuDetails>
fun getPurchases(skuType: String): Purchase.PurchasesResult
fun addProduct(skuDetails: SkuDetails): BillingStore
fun removeProduct(sku: String): BillingStore
fun clearProducts(): BillingStore
fun addPurchase(purchase: Purchase): BillingStore
fun removePurchase(sku: String): BillingStore
fun clearPurchases(): BillingStore
}
62. BillingStore
interface BillingStore {
fun getSkuDetails(params: SkuDetailsParams): List<SkuDetails>
fun getPurchases(skuType: String): Purchase.PurchasesResult
fun addProduct(skuDetails: SkuDetails): BillingStore
fun removeProduct(sku: String): BillingStore
fun clearProducts(): BillingStore
fun addPurchase(purchase: Purchase): BillingStore
fun removePurchase(sku: String): BillingStore
fun clearPurchases(): BillingStore
}
63. BillingStore
class BillingClientFactory {
fun createBillingClient(activity: Activity,
updateListener: PurchasesUpdatedListener): BillingClient {
return DebugBillingClient(activity, updateListener)
}
}
64. BillingStore
class BillingClientFactory {
fun createBillingClient(activity: Activity,
updateListener: PurchasesUpdatedListener): BillingClient {
initializeData(activity)
return DebugBillingClient(activity, updateListener)
}
}
65. BillingStore
class BillingClientFactory {
fun createBillingClient(activity: Activity,
updateListener: PurchasesUpdatedListener): BillingClient {
initializeData(activity)
return DebugBillingClient(activity, updateListener)
}
private fun initializeData(context: Context) {
}
}
66. BillingStore
class BillingClientFactory {
fun createBillingClient(activity: Activity,
updateListener: PurchasesUpdatedListener): BillingClient {
initializeData(activity)
return DebugBillingClient(activity, updateListener)
}
private fun initializeData(context: Context) {
runOnce(context, "init_inventory") {
}
}
}
67. BillingStore
class BillingClientFactory {
fun createBillingClient(activity: Activity,
updateListener: PurchasesUpdatedListener): BillingClient {
initializeData(activity)
return DebugBillingClient(activity, updateListener)
}
private fun initializeData(context: Context) {
runOnce(context, "init_inventory") {
val store = BillingStore.defaultStore(context)
val inventory: List<SkuDetails> = loadInventory(context)
inventory.forEach { store.addProduct(it) }
}
}
}
68. BillingStore
class BillingClientFactory {
fun createBillingClient(activity: Activity,
updateListener: PurchasesUpdatedListener): BillingClient {
initializeData(activity)
return DebugBillingClient(activity, updateListener)
}
private fun initializeData(context: Context) {
runOnce(context, "init_inventory") {
val store = BillingStore.defaultStore(context)
val inventory: List<SkuDetails> = loadInventory(context)
inventory.forEach { store.addProduct(it) }
}
}
}
69. Google Play
Library Code
Your Code
BillingStore
IInAppBillingServicePlay Billing Library
MyApp
Debug Release
BillingStoreBillingStoreBillingStore
BillingX
77. Goals
✓Easy integration with minimal code changes
✓No debug code in production
• Allow easy inventory/transaction management
• Seamless user experience
Easily test in app purchases and subscriptions
78. Goals
✓Easy integration with minimal code changes
✓No debug code in production
✓Allow easy inventory/transaction management
• Seamless user experience
Easily test in app purchases and subscriptions
82. Goals
✓Easy integration with minimal code changes
✓No debug code in production
✓Allow easy inventory/transaction management
• Seamless user experience
Easily test in app purchases and subscriptions
83. Goals
✓Easy integration with minimal code changes
✓No debug code in production
✓Allow easy inventory/transaction management
✓Seamless user experience
Easily test in app purchases and subscriptions
84. Testing In App Billing
The easy way
@rharter
Ryan Harter
https://github.com/pixiteapps/billingx