Best Tutorial for AndroidDevelopers

Android App Development

Stay ahead with the latest tools, trends, and best practices in Android development

Razorpay Integration in Jetpack Compose

Razorpay Integration in Jetpack Compose  - Responsive Blogger Template
Razorpay Integration in Jetpack Compose

Razorpay Integration in Jetpack Compose: A Complete Guide

In the modern era of digital payments, integrating a seamless and secure payment gateway in your Android application has become essential. Among the most popular solutions available in India, Razorpay stands out because of its simplicity, developer-friendly APIs, and wide range of payment options such as UPI, wallets, cards, and net banking.

If you are building an app using Jetpack Compose, Google’s modern toolkit for building native Android UI, you can easily integrate Razorpay to handle in-app payments. In this article, we’ll walk through everything you need to know – from setup to implementation – in a way that feels natural and practical.

Why Choose Razorpay for Android Apps?

Before jumping into the code, let’s quickly look at why Razorpay is a go-to choice for developers:
  • Accept payments via UPI, cards (debit/credit), net-banking, and popular wallets—out of the box.
  • Easy SDK integration: Comes with a dedicated Android SDK.
  • Secure and reliable: PCI DSS compliant and provides end-to-end encryption.
  • Developer-friendly: Clear documentation, sandbox mode for testing, and fast onboarding.
  • Scalability: Suitable for startups as well as large-scale apps.

Prerequisites

Before integrating Razorpay, ensure the following:
  • Android Studio Narwhal or above.
  • Jetpack Compose already set up in your project.
  • To get started, you’ll need a Razorpay account. The good news is that Razorpay provides a free sandbox (test) account where you can experiment with payments before moving to live transactions.
  • Minimum Android SDK: 21+ (recommended).

How to use Razorpay in Android Jetpack Compose App?

Razorpay Payment Integration in Jetpack Compose using MVVM Architecture (2026 Guide)

Integrating payments is a critical part of many Android applications, especially in India where Razorpay is one of the most popular payment gateways. In this article, you will learn how to integrate Razorpay Checkout in a Jetpack Compose app using MVVM architecture.

This guide follows modern Android best practices:

  • Jetpack Compose UI
  • MVVM Architecture
  • Single Responsibility Principle
  • Clean separation of UI and business logic

Why Use MVVM for Payment Integration?

Payment logic should never be tightly coupled with UI code. MVVM helps us:

  • Keep UI clean and reactive
  • Handle payment states properly
  • Easily test and maintain code
  • Scale for subscriptions and multiple plans

Project Architecture Overview



com.example.paymentapp

│

├── ui

│   ├── PaymentScreen.kt

│

├── viewmodel

│   ├── PaymentViewModel.kt

│

├── payment

│   ├── RazorpayManager.kt

│

└── MainActivity.kt


Step 1: Add Razorpay Dependency



dependencies {

    implementation "com.razorpay:checkout:1.6.33"

}


Step 2: Android Manifest Setup



<uses-permission android:name="android.permission.INTERNET" />

<application>

    <activity

        android:name="com.razorpay.CheckoutActivity"

        android:theme="@style/Theme.AppCompat.Light.NoActionBar" />

</application>


Step 3: Payment State Model

We define a sealed class to represent payment states.



sealed class PaymentState {

    object Idle : PaymentState()

    object Loading : PaymentState()

    data class Success(val paymentId: String) : PaymentState()

    data class Error(val message: String) : PaymentState()

}


Step 4: Razorpay Manager (Business Logic Layer)

This class handles Razorpay checkout logic.



class RazorpayManager(

    private val activity: Activity

) {

    fun startPayment(

        amount: Int,

        onSuccess: (String) -> Unit,

        onError: (String) -> Unit

    ) {

        val checkout = Checkout()

        checkout.setKeyID("rzp_test_xxxxxxx")

        val options = JSONObject()

        options.put("name", "Coding Bihar")

        options.put("description", "Premium Course")

        options.put("currency", "INR")

        options.put("amount", amount)

        val prefill = JSONObject()

        prefill.put("email", "test@gmail.com")

        prefill.put("contact", "9999999999")

        options.put("prefill", prefill)

        try {

            checkout.open(activity, options)

        } catch (e: Exception) {

            onError(e.message ?: "Payment failed")

        }

    }

}


Step 5: Payment ViewModel

The ViewModel controls payment flow and exposes UI state.


class PaymentViewModel : ViewModel() {

    private val _paymentState = mutableStateOf<PaymentState>(PaymentState.Idle)

    val paymentState: State<PaymentState> = _paymentState

    fun startPayment(

        razorpayManager: RazorpayManager,

        amount: Int

    ) {

        _paymentState.value = PaymentState.Loading

        razorpayManager.startPayment(

            amount = amount,

            onSuccess = {

                _paymentState.value = PaymentState.Success(it)

            },

            onError = {

                _paymentState.value = PaymentState.Error(it)

            }

        )

    }

}


Step 6: Jetpack Compose UI

Compose UI reacts automatically to payment state changes.



@Composable

fun PaymentScreen(

    viewModel: PaymentViewModel,

    onPayClick: () -> Unit

) {

    val state = viewModel.paymentState.value

    Column(

        modifier = Modifier.fillMaxSize(),

        verticalArrangement = Arrangement.Center,

        horizontalAlignment = Alignment.CenterHorizontally

    ) {

        when (state) {

            is PaymentState.Loading -> {

                CircularProgressIndicator()

            }

            is PaymentState.Success -> {

                Text("Payment Successful")

                Text("Payment ID: ${state.paymentId}")

            }

            is PaymentState.Error -> {

                Text("Payment Failed")

                Text(state.message)

            }

            else -> {

                Button(onClick = onPayClick) {

                    Text("Pay ₹499")

                }

            }

        }

    }

}


Step 7: MainActivity Integration



class MainActivity : ComponentActivity(), PaymentResultListener {

    private lateinit var razorpayManager: RazorpayManager

    private val viewModel: PaymentViewModel by viewModels()

    override fun onCreate(savedInstanceState: Bundle?) {

        super.onCreate(savedInstanceState)

        Checkout.preload(applicationContext)

        razorpayManager = RazorpayManager(this)

        setContent {

            PaymentScreen(

                viewModel = viewModel,

                onPayClick = {

                    viewModel.startPayment(

                        razorpayManager,

                        amount = 49900

                    )

                }

            )

        }

    }

    override fun onPaymentSuccess(paymentId: String?) {

        paymentId?.let {

            viewModel.paymentState.value =

                PaymentState.Success(it)

        }

    }

    override fun onPaymentError(code: Int, response: String?) {

        viewModel.paymentState.value =

            PaymentState.Error(response ?: "Unknown error")

    }

}


Security Best Practices

  • Never store Razorpay Secret Key in app
  • Create order ID from backend
  • Verify payment signature on server
  • Use HTTPS only

Conclusion

Using MVVM with Jetpack Compose makes Razorpay integration clean, scalable, and production-ready. This architecture is suitable for:

  • Paid courses
  • Subscriptions
  • In-app purchases
  • UPI-based payments

If you want advanced topics like server-side verification, subscriptions, or UPI-only checkout, you can extend this architecture easily.

Happy Coding πŸš€

Best Practices for Razorpay Integration

  • It’s a good practice to let your backend server handle the process of creating and validating payment orders. Don’t expose sensitive keys in the app.
  • Handle network failures gracefully and give retry options.
  • Show clear UI messages so users know if their payment is processing.
  • This ensures better security and prevents misuse of your API keys. While testing your integration, keep detailed logs enabled so you can track any errors easily. However, once your app goes live, make sure to turn off unnecessary logging to keep your production environment secure and clean.
  • Test across devices to avoid unexpected crashes.

Common Issues & Fixes

SDK Version Conflicts → Ensure Gradle dependencies are up to date.
Payment Activity Not Found → Add the CheckoutActivity in manifest correctly.
Amount Format Errors → Always pass amount in paise (e.g., 500 INR = 50000).
Callback Not Triggered → Make sure PaymentResultListener is implemented in the calling activity.

Conclusion

Integrating Razorpay in Jetpack Compose is quite straightforward once you understand the flow. From designing the UI with Compose to invoking Razorpay Checkout, the process is smooth and developer-friendly. By following best practices like testing in sandbox mode, securing your keys, and handling callbacks properly, you can ensure a safe and seamless payment experience for your users.

With Razorpay’s flexibility and Jetpack Compose’s modern UI approach, you can build reliable apps that not only look good but also provide a trustworthy checkout experience.
Sandeep Kumar - Android Developer

About the Author

Sandeep Kumar is an Android developer and educator who writes beginner-friendly Jetpack Compose tutorials on CodingBihar.com. His focus is on clean UI, Material Design 3, and real-world Android apps.

SkillDedication

— Python High Level Programming Language- Expert-Written Tutorials, Projects, and Tools—

Coding Bihar

Welcome To Coding BiharπŸ‘¨‍🏫