In-App Purchases And Subscriptions in Jetpack Compose
In this article, I will guide you on a step-by-step process on how to implement in-app purchases and subscriptions in your Android project using Jetpack compose.
Step 1: Adding Dependencies
After creating an empty compose project, add this dependency in your project.
dependencies {
implementation 'com.github.Felix-Kariuki:InAppPurchasesComposeLibrary:0.1.3'
}
Ensure in your root build.gradle or settings.gradle that you have this code
allprojects {
repositories {
...
maven { url 'https://jitpack.io' }
}
}
If you are using kotlin dsl you can add it this way
repositories {
...
maven { url = uri("https://www.jitpack.io" ) }
}
Step 2 : create an android app on your playstore console
once you have created your app on playstore you can publish the first app bundle under closed or internal testing. Then, under products, click in app products and create one. For this case use the product_id as *test_product*
and add a name and description of your choosing. On the price, add an amount and save.
After saving select Activate to ensure your in-app purchase is activated.
...
NOTE. If you are working with in subscriptions you will create a subscription id by selecting subscriptions on the play console and follow the steps they are similar to in-app purchases
Step 3:
Navigate back to your Android studio project and initialize InAppPurchasesHelper class on your screen or activity. For this sample, we will initialize it on the MainActivity.kt like this. pass the activity and product_id of the in-app purchase as named in the play store console as parameters.
val billingPurchaseHelper = InAppPurchasesHelper(this,"test_product")
billingPurchaseHelper.setUpBillingPurchases()
if you would like to implement it on a composable screen you can do it this way
val billingPurchaseHelper = InAppPurchasesHelper(LocalContext.current, "test_product")
billingPurchaseHelper.setUpBillingPurchases()
If you are Working with subscriptions, initialize the subscriptions helper and the rest of the steps remain the same as in in-app purchases
val billingPurchaseHelper = SubscriptionsHelper(LocalContext.current, "test_product")
billingPurchaseHelper.setUpBillingPurchases()
Collect the purchase name and status after initializing the helper class like this
val purchaseDone by billingPurchaseHelper.purchaseDone.collectAsState(false)
val productName by billingPurchaseHelper.productName.collectAsState("")
val purchaseStatus by billingPurchaseHelper.purchaseStatus.collectAsState("")
Step 4 Initiliaze Purchases
On you button composable initialize purchases by calling
billingPurchaseHelper.initializePurchase()
full button code should look like this:
Button(
onClick = {
billingPurchaseHelper.initializePurchase()
},
shape = RoundedCornerShape(32.dp),
colors = ButtonDefaults.buttonColors(
backgroundColor = MaterialTheme.colors.surface,
contentColor = MaterialTheme.colors.onSurface
),
modifier = Modifier.wrapContentSize(),
enabled = purchaseDone
) {
Icon(
Icons.Filled.More,
contentDescription = "InApp Purchases",
modifier = Modifier.size(ButtonDefaults.IconSize)
)
Spacer(Modifier.size(ButtonDefaults.IconSpacing))
Text(
"In App Purchases",
fontSize = 16.sp,
modifier = Modifier.padding(8.dp)
)
}
Full main activity code MainActivity.kt
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
YummyTheme {
val billingPurchaseHelper = InAppPurchasesHelper(this,"test_product")
billingPurchaseHelper.setUpBillingPurchases()
val purchaseDone by billingPurchaseHelper.purchaseDone.collectAsState(false)
val productName by billingPurchaseHelper.productName.collectAsState("")
val purchaseStatus by billingPurchaseHelper.purchaseStatus.collectAsState("")
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colors.background
) {
Button(
onClick = {
billingPurchaseHelper.initializePurchase()
},
shape = RoundedCornerShape(32.dp),
colors = ButtonDefaults.buttonColors(
backgroundColor = MaterialTheme.colors.surface,
contentColor = MaterialTheme.colors.onSurface
),
modifier = Modifier.wrapContentSize(),
enabled = purchaseDone
) {
Icon(
Icons.Filled.More,
contentDescription = "InApp Purchases",
modifier = Modifier.size(ButtonDefaults.IconSize)
)
Spacer(Modifier.size(ButtonDefaults.IconSpacing))
Text(
"In App Purchases",
fontSize = 16.sp,
modifier = Modifier.padding(8.dp)
)
}
}
}
}
}
}
If you implement it on a composable screen it should look like this
@Composable
fun TestScreen() {
val billingPurchaseHelper = InAppPurchasesHelper(LocalContext.current as Activity, "test_product")
billingPurchaseHelper.setUpBillingPurchases()
Column(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.Center
) {
val purchaseDone by billingPurchaseHelper.purchaseDone.collectAsState(false)
val productName by billingPurchaseHelper.productName.collectAsState("")
val purchaseStatus by billingPurchaseHelper.purchaseStatus.collectAsState("")
Button(
onClick = {
billingPurchaseHelper.initializePurchase()
},
shape = RoundedCornerShape(32.dp),
colors = ButtonDefaults.buttonColors(
backgroundColor = MaterialTheme.colors.surface,
contentColor = MaterialTheme.colors.onSurface
),
modifier = Modifier.wrapContentSize(),
enabled = purchaseDone
) {
Icon(
Icons.Filled.More,
contentDescription = "In App Purchase",
modifier = Modifier.size(ButtonDefaults.IconSize)
)
Spacer(Modifier.size(ButtonDefaults.IconSpacing))
Text(
"In App Purchase",
fontSize = 16.sp,
modifier = Modifier.padding(8.dp)
)
}
}
}
You can now run your app and you should be able to get a result similar to this one.
If you don't see this you can try to upload the latest version of the application to play store to see if it solves your issue
To see this implementation on a full scale project, check out:
This app on [Github](https://github.com/Felix-Kariuki/Yummy)
This app on [Playstore](https://bit.ly/3W5b4wk)
The InAppComposeLibrary on [Github](https://github.com/Felix-Kariuki/InAppPurchasesComposeLibrary)
For any further questions or clarification reach out
Twitter - https://twitter.com/felixkariuki_
LinkedIn - https://www.linkedin.com/in/felix-kariuki/