How I Built an Android Ecommerce App with Medusa
Tags: programming, opensource, android, tutorial
Published: 2023-03-02 14:20:06


In addition to ecommerce websites, many ecommerce startups now require mobile applications to enhance the shopping experience for users with features such as push [notifications](https://medusajs.com/blog/medusa-slack/), personalization, less friction, and more.


[Medusa](https://github.com/medusajs/medusa) is a set of ecommerce building blocks that gives developers full control over building an ecommerce application. Its modules make up a [headless](https://medusajs.com/blog/headless-architecture/) backend that any frontend, storefront, app, or admin dashboard, can connect to through [REST APIs](https://medusajs.com/blog/ecommerce-apis/). “Headless” in this case means that the backend and frontend are separate.


This article explains how to build an Android ecommerce app with Medusa. The code source of the Android client is available on [GitHub](https://github.com/arjunaskykok/medusa-android-app). 


Here is a preview of what your application should look like at the end of this tutorial.



![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fyf41x43iswn0a5661ev.gif)


## Prerequisites


To follow this guide, it's essential to have the following:

- The latest stable version of [Node.js](https://nodejs.org/)
- [Yarn](https://classic.yarnpkg.com/), but you can use [npm](https://www.npmjs.com/) or [pnpm](https://pnpm.io/) as alternatives to yarn if you prefer.
- [Android Studio](https://developer.android.com/studio/)

## Set Up Medusa Server


Install the Medusa CLI app with the following command:


```bash
yarn global add @medusajs/medusa-cli
```


Create a new Medusa project:


```bash
medusa new my-medusa-server --seed
```


By default, it will use SQLite database. You can configure it to use PostgreSQL database. Refer to [the documentation](https://docs.medusajs.com/usage/configurations/#postgresql-configurations) for the details.


Start the server with the commands below:


```bash
cd my-medusa-server
medusa develop
```


Your server runs at port 9000. To verify that the server is working properly, you can open the URL `http://localhost:9000/store/products` with your browser. You should get the JSON data of products available on your server.


## Set Up the Android Ecommerce Project


Begin by launching Android Studio and create a new project. In the _New Project_ window, choose _Empty Compose Activity._



![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hkxsyeht3v675ry55uwc.png)


In the following screen, choose _API 33: Android Tiramisu_ in the _Minimum SDK_ field.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l2ehtt44pxo3qjp954on.png)


You can name your application and your package as you wish. In this tutorial, the application's name is _Medusa Android Application_ and the package name is `com.medusajs.android.medusaandroidapplication`.


## Install Dependencies


Once the project is ready, edit the application `build.gradle` and add these libraries in the `dependencies` block:


```kotlin
dependencies {
		...
    
		// Add the new dependencies here
    implementation 'com.squareup.retrofit2:retrofit:2.9.0'
    implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
    implementation 'com.github.skydoves:landscapist-glide:2.1.0'
    implementation 'androidx.navigation:navigation-compose:2.5.3'
}
```


Sync the Gradle build file by clicking the `Sync Now` button. The Retrofit library allows you to connect to API with objects, while the Glide library facilitates the loading of remote images. The navigation connects screens in your Android application.


Create an XML file named `network_security_config.xml` inside `app/res/xml` and add the following content:


```xml
<?xml version="1.0" encoding="utf-8"?>
<network-security-config xmlns:android="http://schemas.android.com/apk/res/android">
    <domain-config cleartextTrafficPermitted="true">
        <domain includeSubdomains="true">localhost</domain>
        <domain includeSubdomains="true">10.0.2.2</domain>
    </domain-config>
</network-security-config>
```


Add this network rule to connect to your `localhost` in the Android emulator without https. The Android emulator recognizes `localhost` as 10.0.2.2.


Edit `AndroidManifest.xml` in `app/manifests`. Add these lines inside the `manifest` node:


```xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <!-- Add them here -->
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
    <uses-permission android:name="android.permission.INTERNET" />
		
		<application> ... </application>
</manifest>
```


Add this line as an attribute in the `application` node:


```xml
android:networkSecurityConfig="@xml/network_security_config"
```


## Connect the Android Project to the Medusa Server


You have to model the object that represents the API in the Medusa Server. Create the package `model` in `app/java/com/medusajs/android/medusaandroidapplication`. Inside the package, create the `Response` class, and replace the content with the following code:


```kotlin
package com.medusajs.android.medusaandroidapplication.model

data class ProductsResult(val products: List<Product>)

data class ProductResult(val product: Product)

data class CartResult(val cart: Cart)

data class CartRequest(val items: List<Item>)

data class Product(
  val id: String?,
  val title: String?,
  val thumbnail: String?,
  val variants: List<Variant>
)

data class Variant(
  val id: String?,
  val prices: List<Price>
)

class Price {
  var currency_code: String = ""
  var amount: Int = 0

  fun getPrice() : String {
    return "$currency_code $amount"
  }
}

data class Item(
  val variant_id: String,
  val title: String?,
  val thumbnail: String?,
  val quantity: Int
)

data class Cart(
  val id: String?,
  val items: List<Item>
)

data class LineItem(
  val variant_id: String,
  val quantity: Int
)
```


Each data class represents the JSON data structure you get from or send to the Medusa server. You can take a look at the complete API reference of the Medusa server in [this store API reference](https://docs.medusajs.com/api/store/).


Check if the result of an API call has this structure:


```json
{
  products: {
    {
      id: ...,
      title: ...
    },
    {
      id: ...,
      title: ...
    }
  }
}
```


If that is the case, then the correspondence data class are these classes:


```kotlin
data class ProductsResult(val products: List<Product>)

data class Product(
  val id: String?,
  val title: String?
)
```


You need to access the API endpoints. Create the `MedusaService` interface in `app/java/com/medusajs/android/medusaandroidapplication/``model` and replace the content with the following code:


```kotlin
package com.medusajs.android.medusaandroidapplication.model

import retrofit2.Call
import retrofit2.http.*

interface MedusaService {

  @GET("/store/products")
  fun retrieveProducts(): Call<ProductsResult>

  @GET("/store/products/{id}")
  fun getProduct(@Path("id") id: String) : Call<ProductResult>

  @Headers("Content-Type: application/json")
  @POST("/store/carts")
  fun createCart(@Body cart: CartRequest): Call<CartResult>

  @Headers("Content-Type: application/json")
  @POST("/store/carts/{id}/line-items")
  fun addProductToCart(@Path("id") id: String, @Body lineItem: LineItem): Call<CartResult>

  @GET("/store/carts/{id}")
  fun getCart(@Path("id") id: String) : Call<CartResult>
}
```


As you can see, there are two annotations. One is for the GET request and the other is for the POST request. The argument for the annotation is the API endpoint in the Medusa server. The argument `/store/products` mean `http://``localhost:9000``/store/products`. The return result represents the result type you will get.


The `@Body` represents the JSON data you send to the API endpoint. The `@Path` represents the parameter of the API endpoint.


This is only an interface. You need to implement it. Create the `ProductsRetriever` class inside the `app/java/com/medusajs/android/medusaandroidapplication/model` package and replace the content with the following code:


```kotlin
package com.medusajs.android.medusaandroidapplication.model

import retrofit2.Callback
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory

class ProductsRetriever {
  private val service: MedusaService

  companion object {
    const val BASE_URL = "http://10.0.2.2:9000"
  }

  init {
    val retrofit = Retrofit.Builder()
      .baseUrl(BASE_URL)
      .addConverterFactory(GsonConverterFactory.create())
      .build()

    service = retrofit.create(MedusaService::class.java)
  }

  fun getProducts(callback: Callback<ProductsResult>) {
    val call = service.retrieveProducts()
    call.enqueue(callback)
  }

  fun getProduct(productId: String, callback: Callback<ProductResult>) {
    val call = service.getProduct(productId)
    call.enqueue(callback)
  }

  fun createCart(cartId: String, variantId: String, callback: Callback<CartResult>) {
    if (cartId.isNotEmpty()) {
      val lineItemRequest = LineItem(variantId, 1)
      val call = service.addProductToCart(cartId, lineItemRequest)
      call.enqueue(callback)
    } else {
      val items = listOf(
        Item(variant_id=variantId, quantity=1, thumbnail=null, title=null)
      )
      val cartRequest = CartRequest(items)
      val call = service.createCart(cartRequest)
      call.enqueue(callback)
    }
  }

  fun getCart(cartId: String, callback: Callback<CartResult>) {
    if (cartId.isNotEmpty()) {
      val call = service.getCart(cartId)
      call.enqueue(callback)
    }
  }
}
```


In this class, you initialize the `retrofit` variable with the base URL `http://10.0.2.2:9000` and the Gson converter to handle JSON. Remember that the emulator knows the `localhost` as `10.0.2.2`. Then you create a service with the `create` method from the `retrofit` variable with `MedusaService`.


You use this `service` variable within the implemented methods. You execute the method defined in the interface, resulting in the call object. Then you call the `enqueue` method from this object with the callback argument. After the API endpoint is called, this callback will be executed.


## Products List Screen


Now that you can connect to API endpoints, you can create a UI interface for the products list. Create a new file called `ProductsList.kt` in `app/java/com/medusajs/android/``medusaandroidapplication``ui` and replace the content with the following code:


```kotlin
package com.medusajs.android.medusaandroidapplication.ui

import androidx.compose.foundation.*
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material.Button
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.dp
import com.medusajs.android.medusaandroidapplication.model.Product
import com.skydoves.landscapist.ImageOptions
import com.skydoves.landscapist.glide.GlideImage

@Composable
fun ProductsList(products: List<Product>,
                 onItemSelected: (String) -> Unit,
                 onCartButtonClick: () -> Unit) {
  Column(modifier = Modifier
    .verticalScroll(rememberScrollState())
    .fillMaxSize()
    .padding(16.dp),
    horizontalAlignment = Alignment.CenterHorizontally
  ) {
    Button(onClick = {
      onCartButtonClick()
    }) {
      Text("My Cart")
    }
    products.forEach { product ->
      Column(
        modifier = Modifier
          .padding(8.dp)
          .border(BorderStroke(2.dp, Color.Gray))
          .clickable {
            onItemSelected(product.id!!)
          }
        ,
        horizontalAlignment = Alignment.CenterHorizontally
      ) {
        GlideImage(
          imageModel = { product.thumbnail!! },
          imageOptions = ImageOptions(
            contentScale = ContentScale.Crop,
            requestSize = IntSize(400,600),
            alignment = Alignment.Center
          )
        )
        Text(product.title!!, fontWeight = FontWeight.Bold)
        Text(product.variants[0].prices[0].getPrice())
      }
    }

  }
}
```


In this `ProductsList` function, you create a button to go to the cart screen and a list of products. Each card is composed of a remote image, a title of the product, and the price.


Each product in Medusa has more than one variant. Each variant can have many prices (depending on which region). For this mobile ecommerce tutorial, each product uses the first variant and the first price from the variant. 


The purpose of this decision is to make the tutorial simple. But in production, you can make a different architecture, such as displaying products with their variants.


## Product Info Screen


Now it’s time to create a detail page. On that page, a user can add the product to their shopping cart.


Create the `ProductItem.kt`file inside `app/java/com/medusajs/androidmedusaandroidapplication/ui` and replace the content with the following code:


```kotlin
package com.medusajs.android.<your application name>.ui

import android.util.Log
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material.Button
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.medusajs.android.medusaandroidapplication.model.CartResult
import com.medusajs.android.medusaandroidapplication.model.Product
import com.medusajs.android.medusaandroidapplication.model.ProductsRetriever
import com.skydoves.landscapist.ImageOptions
import com.skydoves.landscapist.glide.GlideImage
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response


@Composable
fun ProductItem(product: Product,
                cartId: String,
                onCartChange: (String) -> Unit) {
  Column(
    modifier = Modifier
      .padding(8.dp)
      .fillMaxSize(),
    horizontalAlignment = Alignment.CenterHorizontally
  ) {
    GlideImage(
      imageModel = { product.thumbnail!! },
      imageOptions = ImageOptions(
        alignment = Alignment.Center,
        requestSize = IntSize(800,1200)
      )
    )
    Text(product.title!!, fontSize = 30.sp)
    Text(product.variants[0].prices[0].getPrice())
    Button(onClick = {
      val productsRetriever = ProductsRetriever()
      val callback = object : Callback<CartResult> {
        override fun onFailure(call: Call<CartResult>, t: Throwable) {
          Log.e("MainActivity", t.message!!)
        }

        override fun onResponse(call: Call<CartResult>, response: Response<CartResult>) {
          response.isSuccessful.let {
            response.body()?.let { c ->
              onCartChange(c.cart.id!!)
            }
          }
        }
      }
      productsRetriever.createCart(cartId, product.variants[0].id!!, callback)
    }) {
      Text("Add 1 Item to Cart")
    }
  }
}
```


In the button's callback, you create an object of `Callback<CartResult>` with two methods: `onFailure` and `onResponse`. If the API call is successful, the `onReponse` method will be triggered. You call `onCartChange` with the cart id you receive from the server. The method saves the cart id to a variable so you can reuse the cart id later. If the API call is unsuccessful, the `onFailure` method will be called. In this case, you just log the error.


You call the `createCart` method with the variant id of the product. To simplify the tutorial, you just send the first variant of the product. Then you can only add one piece of the product. 


If you want to add two product units, you must press the button twice. You also send the cart ID. Initially, the cart ID will be 'null’, in which case the server will create a cart for you. However, if it is not null, you will reuse the existing cart, allowing you to add the product.


To create a cart, you call the `createCart` method with the `CartRequest` object. To add an item to the cart, call the `addProductToCart` method with the `LineItem` object.


You can verify the JSON data the Medusa server requires in [the API documentation](https://docs.medusajs.com/api/store/).


For example, to add a line item, the expected payload is:


```json
{
    "variant_id": "string",
    "quantity": 0,
    "metadata": {}
}
```


Notice the JSON fields (`variant_id` and `quantity`) are the same as the fields in the `LineItem` data class.


## Create a Cart


You need to create the `ViewModel`. Create `CartViewModel` in `app/java/com/medusajs/android/``m``edusaandroidapplication/model`:


```kotlin
package com.medusajs.android.medusaandroidapplication.model

import androidx.lifecycle.ViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow


data class CartModel (
  val title: String,
  val quantity: Int,
  val thumbnail: String,
)

class CartViewModel : ViewModel() {

  private val _cartState = MutableStateFlow(emptyList<CartModel>())
  val cartState: StateFlow<List<CartModel>> = _cartState.asStateFlow()

  fun setCart(cartModels: List<CartModel>) {
    _cartState.value = cartModels
  }
}
```


The `ViewModel` has `_cartState` that is a `MutableStateFlow` holding `CartModel`.


Users might want to see what products they have added to their shopping cart. You need to create a screen for the cart. Create `CartCompose` in `app/java/com/medusajs/android/<your application name>/ui` and replace its content with the following code:


```kotlin
package com.raywenderlich.android.medusaandroidapplication.ui

import android.util.Log
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import com.medusajs.android.medusaandroidapplication.model.CartModel
import com.medusajs.android.medusaandroidapplication.model.CartResult
import com.medusajs.android.medusaandroidapplication.model.CartViewModel
import com.medusajs.android.medusaandroidapplication.model.ProductsRetriever
import com.skydoves.landscapist.ImageOptions
import com.skydoves.landscapist.glide.GlideImage
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response


@Composable
fun CartCompose(cartId: String,
                cartViewModel: CartViewModel = viewModel()
) {
  val cartStates by cartViewModel.cartState.collectAsState()

  val callback = object : Callback<CartResult> {
    override fun onFailure(call: Call<CartResult>, t: Throwable) {
      Log.e("MainActivity", t.message!!)
    }

    override fun onResponse(call: Call<CartResult>, response: Response<CartResult>) {
      response.isSuccessful.let {
        response.body()?.let { c ->
          val cartModels : MutableList<CartModel> = mutableListOf()
          c.cart.items.forEach { item ->
            val title = item.title!!
            val thumbnail = item.thumbnail!!
            val quantity = item.quantity
            val cartModel = CartModel(title, quantity, thumbnail)
            cartModels.add(cartModel)
          }
          cartViewModel.setCart(cartModels.toList())
        }
      }
    }
  }
  val productsRetriever = ProductsRetriever()
  productsRetriever.getCart(cartId, callback)
  Column(modifier = Modifier
    .verticalScroll(rememberScrollState())
    .fillMaxSize()
    .padding(16.dp),
    horizontalAlignment = Alignment.CenterHorizontally
  ) {
    Text("My Cart")
    cartStates.forEach { cartState ->
      Row(modifier = Modifier.fillMaxWidth().padding(16.dp),
      horizontalArrangement = Arrangement.SpaceBetween) {
        GlideImage(
          imageModel = { cartState.thumbnail },
          imageOptions = ImageOptions(
            alignment = Alignment.Center,
            requestSize = IntSize(200,300)
          )
        )
        Text(cartState.title)
        Text("${cartState.quantity} pcs")
      }
    }
  }
}
```


You use `cartViewModel` and a `ViewModel` to hold the products you added to the cart.


In the `onResponse` method, after a successful API call, you set the `ViewModel` with the cart JSON object from the server.


You call the `getCart` method with the cart id argument to get the information about the cart.


## Set up the Navigation


All that’s left is to create the navigation that connects the screens. Create `MainAppCompose` inside `app/java/com/medusajs/ui` and replace the content with the following code:


```kotlin
package com.medusajs.android.medusaandroidapplication.ui

import android.util.Log
import androidx.compose.runtime.*
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.navigation.NavType
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import androidx.navigation.navArgument
import com.medusajs.android.medusaandroidapplication.model.*
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response

@Composable
fun MainApp(products: List<Product>) {
  var cartId by rememberSaveable { mutableStateOf("") }

  val navController = rememberNavController()
  var product : Product? = null

  NavHost(navController = navController, startDestination = "list") {
    composable("list") {
      ProductsList(products,
        onItemSelected = { product_id ->
          navController.navigate("item/$product_id")
        },
        onCartButtonClick = {
          navController.navigate("cart")
        }
      )
    }
    composable(
      "item/{product_id}",
            arguments =  listOf(navArgument("product_id") { type = NavType.StringType})) { it ->
      val productId = it.arguments?.getString("product_id")!!
      val callback = object : Callback<ProductResult> {
        override fun onFailure(call: Call<ProductResult>, t: Throwable) {
          Log.e("MainActivity", t.message!!)
        }

        override fun onResponse(call: Call<ProductResult>, response: Response<ProductResult>) {
          response.isSuccessful.let {
            response.body()?.let { p ->
              product = p.product
            }
          }
        }
      }
      val productsRetriever = ProductsRetriever()
      productsRetriever.getProduct(productId, callback)
      product?.let { ProductItem(it, cartId, onCartChange = { cartId = it }) }
    }

    composable("cart") {
      CartCompose(cartId)
    }
  }
}
```


You created `NavHost`, which is composed of three compose functions or screens. You navigate from one screen to another screen with the `navigate` method of the `navController` object.


Finally, edit `MainActivity` that is inside the `app/java/com/medusajs/android/medusaandroidapplication` package to call the `MainApp` that connects the screens. Replace the content with the following code:


```kotlin
package com.medusajs.android.medusaandroidapplication

import android.os.Bundle
import android.util.Log
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.material.*
import com.medusajs.android.medusaandroidapplication.model.Product
import com.medusajs.android.medusaandroidapplication.model.ProductsResult
import com.medusajs.android.medusaandroidapplication.model.ProductsRetriever
import com.medusajs.android.medusaandroidapplication.ui.MainApp
import com.medusajs.android.medusaandroidapplication.ui.theme.MedusaAndroidApplicationTheme
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response


class MainActivity : ComponentActivity() {

  private val productsRetriever = ProductsRetriever()

  private var products : List<Product> = emptyList()

  private val callback = object : Callback<ProductsResult> {
    override fun onFailure(call: Call<ProductsResult>, t: Throwable) {
      Log.e("MainActivity", t.message!!)
    }

    override fun onResponse(call: Call<ProductsResult>, response: Response<ProductsResult>) {
      response.isSuccessful.let {
        products = response.body()?.products ?: emptyList()
        setContentWithProducts()
      }
    }
  }

  fun setContentWithProducts() {
    setContent {
      MedusaAndroidApplicationTheme {
        Scaffold(
          topBar = { TopAppBar(title = { Text(text = "Medusa App") }) }
        ) {
          MainApp(products = products)
        }
      }
    }
  }

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    productsRetriever.getProducts(callback)
  }
}
```


## Test the Application


Now that you have written the code, it's time to build the application. Make sure the Medusa server is running and run the following command:


```bash
cd my-medusa-server
medusa develop
```


Build and run the application in the Android emulator. But first, you need to [create a device in Device Manager](https://developer.android.com/studio/run/emulator). You will get the screen of the products list.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tpyr4ae5x1739jyzfmde.png)


Click one of the products, then you'll see the product detail page.



![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1cf8lowar9p47i6v3vjd.png)


Add some products to your shopping cart in the products list screen; click the button to view the cart.



![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lww3vw9gm6e125dyb69g.png)


## Conclusion


This tutorial provides an overview of how to build an Android ecommerce application with Medusa. This application provides basic functionalities, but you can implement more functionalities to enhance it:

- [Add a “region switcher"](https://docs.medusajs.com/advanced/backend/regions/overview/) for customers’ region selection.
- [Use product variants](https://docs.medusajs.com/references/js-client/classes/internal.productvariant/) to provide product options like colors on the details view.
- [Implement authentication flow](https://docs.medusajs.com/references/js-client/classes/authresource/) allowing customers to sign up with their email or log in via social media.
- [Implement the full checkout experience](https://docs.medusajs.com/advanced/storefront/how-to-implement-checkout-flow/) when customers confirm the payment.
- Integrate payment methods such as [Stripe](https://docs.medusajs.com/add-plugins/stripe).

Visit [the Medusa store API reference](https://docs.medusajs.com/api/store/) to discover all the possibilities to build your ecommerce application. 


> Should you have any issues or questions related to Medusa, then feel free to reach out to the Medusa team via [Discord](https://discord.gg/F87eGuwkTp).