McTaba Labs logo
Software EngineeringThe WhiteboardProjectsEventsMasterclassesWebinarsLearn
Book Call

500+ developers already in the community

Get answers, find accountability partners, and stay ahead

Join Discord →
McTaba Labs logo

Educating 1 million students in tech by 2030.

Programs

  • HighSchool Graduates Launchpad
  • AI & Business Automation
  • Corporate Training
  • McTaba Story & Mission
  • Full-Stack Curriculum
  • Course Pricing & Plans
  • Frequently Asked Questions

Resources

  • Join Discord Community
  • Tech Events in Nairobi
  • Masterclasses
  • Free Resources
  • Learn
  • Campus
  • Africa Tech Talent Report
  • Partner With Us
  • Start a Project
  • Our Work

Contact Us

  • 124 Manyani East Road, Lavington, Nairobi
  • +254727795603
  • Book a Discovery Call

Office Hours:

Mon-Fri: 9am - 2pm EAT

Sat: 10am - 12pm EAT

© 2027 McTaba Labs - A product of Edtech Industries. All rights reserved.

Privacy PolicyTerms of ServiceRefund Policy
  1. Learn
  2. Paystack for African Developers
  3. Build Paystack Subscriptions in Android with Kotlin
Bonaventure OgetoBy Bonaventure Ogeto|Updated July 22, 2026

Build Paystack Subscriptions in Android with Kotlin

Subscribe a user by calling your backend to initialize a transaction with a Paystack plan code. Open the authorization_url in your WebView Activity. After checkout, Paystack creates the subscription. Your backend receives the subscription.create webhook. Check subscription status from Android by calling your backend status API, not Paystack directly.

In this article

  1. 1. Important: Google Play Billing Policy
  2. 2. Subscription ViewModel
  3. 3. Subscription Status Screen
  4. 4. Learn More

Important: Google Play Billing Policy

If you distribute your app on the Google Play Store and sell digital subscriptions through it, Google requires you to use Google Play Billing for in-app purchases. Paystack subscriptions work best for:

  • Apps distributed outside the Play Store (APK sideloading)
  • Business/B2B apps
  • Hybrid apps where the subscription is for a web service (which may qualify as an exemption)

For consumer apps on the Play Store with digital subscriptions, consult the Google Play policies before using Paystack for subscription billing. This guide is most applicable to non-Play Store distribution or B2B apps.

Subscription ViewModel

// viewmodel/SubscriptionViewModel.kt
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch

data class SubscriptionStatus(
    val active: Boolean = false,
    val planName: String? = null,
    val nextBillingDate: String? = null,
    val subscriptionCode: String? = null,
    val loading: Boolean = true,
    val error: String? = null
)

class SubscriptionViewModel : ViewModel() {
    private val _status = MutableStateFlow(SubscriptionStatus())
    val status: StateFlow<SubscriptionStatus> = _status

    fun checkStatus(userEmail: String) {
        viewModelScope.launch {
            _status.value = SubscriptionStatus(loading = true)
            try {
                val result = RetrofitClient.subscriptionApi.getStatus(userEmail)
                _status.value = SubscriptionStatus(
                    active = result.active,
                    planName = result.planName,
                    nextBillingDate = result.nextBillingDate,
                    subscriptionCode = result.subscriptionCode,
                    loading = false
                )
            } catch (e: Exception) {
                _status.value = SubscriptionStatus(loading = false, error = e.message)
            }
        }
    }

    fun cancelSubscription(subscriptionCode: String, emailToken: String) {
        viewModelScope.launch {
            try {
                RetrofitClient.subscriptionApi.cancel(subscriptionCode, emailToken)
                _status.value = _status.value.copy(active = false)
            } catch (e: Exception) {
                _status.value = _status.value.copy(error = "Cancel failed: ${e.message}")
            }
        }
    }
}

Get weekly developer tips

Join 25,000+ developers. Practical guides, job tips, and new content — straight to your inbox.

No spam. Unsubscribe anytime.

Subscription Status Screen

// SubscriptionActivity.kt
import android.os.Bundle
import android.view.View
import android.widget.Button
import android.widget.TextView
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.launch

class SubscriptionActivity : AppCompatActivity() {
    private val viewModel: SubscriptionViewModel by viewModels()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_subscription)

        val userEmail = intent.getStringExtra("email") ?: return finish()
        val statusText = findViewById<TextView>(R.id.statusText)
        val planText = findViewById<TextView>(R.id.planText)
        val nextBillingText = findViewById<TextView>(R.id.nextBillingText)
        val cancelButton = findViewById<Button>(R.id.cancelButton)
        val subscribeButton = findViewById<Button>(R.id.subscribeButton)

        lifecycleScope.launch {
            viewModel.status.collect { state ->
                if (state.loading) {
                    statusText.text = "Loading..."
                    return@collect
                }

                if (state.active) {
                    statusText.text = "Active"
                    planText.text = state.planName ?: "Unknown plan"
                    nextBillingText.text = "Next billing: ${state.nextBillingDate}"
                    cancelButton.visibility = View.VISIBLE
                    subscribeButton.visibility = View.GONE

                    cancelButton.setOnClickListener {
                        viewModel.cancelSubscription(
                            state.subscriptionCode ?: "",
                            getEmailToken()
                        )
                    }
                } else {
                    statusText.text = "No active subscription"
                    cancelButton.visibility = View.GONE
                    subscribeButton.visibility = View.VISIBLE

                    subscribeButton.setOnClickListener {
                        startPaymentActivity(userEmail)
                    }
                }
            }
        }

        viewModel.checkStatus(userEmail)
    }
}

Learn More

This guide is part of the Paystack integration guides by language and framework series.

Sign up for the McTaba newsletter

Key Takeaways

  • ✓Subscription checkout in Android is the same as one-time payment — just pass a plan code on the backend.
  • ✓After checkout, Paystack manages renewals automatically. The Android app does not trigger them.
  • ✓Check subscription status by calling your backend, which queries the Paystack API.
  • ✓Display plan details, next billing date, and subscription code from your backend response.
  • ✓Use a ViewModel and StateFlow to manage subscription state across screens.
  • ✓Use Google Play Billing API if distributing on the Play Store — it is required for in-app subscriptions sold through the store.

Frequently Asked Questions

Can I use Paystack subscriptions instead of Google Play Billing?
For apps distributed on the Play Store selling digital content, Google requires Play Billing. Paystack subscriptions work for apps distributed outside the Play Store, B2B apps, and services sold for physical goods or external services. Check current Play Store policies before deciding.
How does my Android app know a subscription renewal succeeded?
Your backend receives a charge.success webhook from Paystack with a plan property. Your backend updates the subscription record. The Android app checks the backend status on next launch or receives an FCM push notification.
Where do I store the subscription code in Android?
Store it in your backend database, not on the device. The Android app fetches subscription details from your backend. If needed for offline display, cache it in SharedPreferences or Room, but always treat the backend as the source of truth.
How do I implement feature gating based on subscription in Android?
Observe the subscription ViewModel in your main Activity. If subscription.active is true, unlock premium features. If false, show the subscription screen. Use a repository pattern so all screens share the same subscription status. Refresh on app resume to stay current.

Learn Paystack Integration

KES 2,999

The definitive guide to building payment systems with Paystack — from your first transaction to production-grade payment infrastructure across Africa. 9 modules, 47 lessons. Free preview available.

Start Paystack Course

Not ready? Create Free Account

Keep reading

Paystack Integration Guides by Language and Framework

A complete directory of Paystack integration guides organized by programming language and framework. Each guide covers accepting payments, verifying transactions, handling webhooks, and building subscriptions in your specific stack.

Read more

Accept Payments with Paystack in Android with Kotlin

Accept Paystack payments in a native Android app built with Kotlin. This guide covers initializing transactions from your backend, displaying the Paystack checkout in a WebView Activity, detecting the callback redirect, and verifying payment.

Read more

Handle Paystack Webhooks in Android with Kotlin

Paystack webhooks arrive at your server, not your Android app. This guide covers building the backend handler and delivering payment events to an Android Kotlin app via FCM push notifications or polling.

Read more

Handling subscription.create, subscription.disable and invoice Events

A complete walkthrough of every Paystack subscription and invoice webhook event. Covers subscription.create, subscription.not_renew, subscription.disable, invoice.create, invoice.payment_failed, and invoice.update with production-ready Node.js handlers.

Read more

Paystack Subscription Not Charging

Your Paystack subscription exists but recurring charges are not happening. The subscription might be disabled, the authorization code might not be reusable, the card might have expired, the plan amount might have changed, or the subscription might be in arrears. This guide walks through every cause with a systematic debug checklist.

Read more

Paystack Card Not Supported for Recurring

When Paystack says a card is not supported for recurring charges, the card type or issuing bank blocks tokenization. This guide covers why it happens, which card types are affected, how to handle it gracefully, and alternative billing strategies.

Read more

Explore related topics

M-Pesa & African Stack

M-Pesa Daraja API tutorials, USSD development, and WhatsApp Business API guides. The African Stack that global bootcamps do not teach.

Learn to Code in Kenya

Everything a Kenyan needs to know before writing their first line of code. Career paths, real costs, hardware requirements, and honest answers about what it takes.

Learning to Code

Roadmaps, language guides, and practical advice for learning to code and launching a programming career.

Breaking Into Tech

Practical, no-nonsense guides for anyone looking to start or change their career in tech. From choosing your first language to acing interviews.

The African Stack: Skills for This Market

The skills that make Kenyan developers hard to replace: M-Pesa, USSD, WhatsApp, fintech, and building for the African market.

Bonaventure Ogeto

Bonaventure Ogeto

Founder, McTaba Labs

Bonaventure is the founder of McTaba Labs, a coding bootcamp in Nairobi, Kenya. He has spent years building products for the African market and mentoring the next generation of developers across the continent.