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/