Build Marketplace App with Stripe Billing
Published: 2021-12-23 01:37:54

---
title: Build Marketplace App with Stripe Billing
published: true
description: 
tags: 
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2oa09uosc6h1m4lcew0f.png
 
---

## Introduction

As a result of being home bound amidst the pandemic, public interest in marketplace apps has skyrocketed as it became the primary place for people to buy products. The marketplace model explains why online marketplaces are so popular and beneficial. It can be built for any market and niche with no initial inventory required and can grow exponentially, with benefits to all - **sellers**, **buyers**, and of course the **marketplace owners.** 

To sell products online without a lot of marketing effort is appealing to sellers. Marketplace app is much easier and faster than creating your own e-commerce website/app and then struggling to get traffic.

It also makes it convenient for customers to buy products because of the wide selection and prices available, as well as the ability to compare products.

And owning a marketplace provides you with many effective ways to generate revenue. Advertising, charging a percentage for each deal, and a lot [more.](http://more.so/) So, let’s take a deep dive and know how to build one yourself!

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

## Let’s get started

- Setting up the React
    
Let’s begin by creating a boilerplate using the `creact-react-app`
    

```
npx create-react-app marketplace
```

- Install Material UI
    
We will be using Material UI to style the frontend that way we don’t end up writing the  CSS
    

```
cd marketplace
npm install @mui/material @emotion/react @emotion/styled
```

- Create a Product component
    
Create a directory inside `src` and name it `components` where we'll be place all the different components we are gonna build!
    
Create another directory inside of it and name it `Product`. Create a `Product.js` inside the `Product` directory. This component will display the list of products we have. At first, we will be looping through a static list of products

```jsx
import React from "react";
import {
  Box,
  Card,
  CardActions,
  CardContent,
  Button,
  Typography,
  Rating,
} from "@mui/material";

const Products = [
  {
    title: "Oculus Quest All-in-one VR Gaming Headset",
    price: "11.96",
    rating: 5,
  },
  {
    title: "Nintendo Switch with Neon Blue and Neon Red Joy‑Con",
    price: "15.96",
    rating: 3,
  },
  {
    title: "Mass Effect",
    price: "23",
    rating: 5,
  },
  {
    title: "The LeanStartup2: How constant innovative creators",
    price: "9.96",
    rating: 2,
  },
  {
    title: "Dual Shock Controller",
    price: "19.96",
    rating: 5,
  },
];

function Product() {
  return (
    <Box
      sx={{
        display: "flex",
        flexDirection: "row",
        justifyContent: "space-between",
        alignItems: "start",
      }}
    >
      {Products.map((product) => (
        <Card
          sx={{
            maxHeight: 450,
            minWidth: 100,
            width: "25%",
            margin: "1.5rem",
            display: "flex",
            flexDirection: "column",
            justifyContent: "space-between",
            alignItems: "start",
          }}
        >
          <CardContent>
            <Typography gutterBottom variant="h5" component="div">
              {product.title}
            </Typography>
            <Typography gutterBottom variant="h5" component="div">
              ${product.price}
            </Typography>
            <Rating name="read-only" value={product.rating} readOnly />
          </CardContent>
          <CardActions>
            <Button variant="contained" size="small">
              Buy now
            </Button>
          </CardActions>
        </Card>
      ))}
    </Box>
  );
}

export default Product;
```

- Create a Header component.
    
Create a directory inside components and name it header, and create a file inside it and name it `Header.js`. This file will hold the header where we will display the app’s name and later on user sign in/sign out button.

```jsx
import React from "react";

import Product from "./components/product/Product";
import Header from "./components/header/Header";

function App() {
  return (
    <>
      <Header />
      <Product />
    </>
  );
}

export default App;
```

It should look something like this:

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

- Time to get the API ready!
    
Getting your APIs for the Marketplace app comes at real ease. All you have to do is clone this production-ready [project](https://app.canonic.dev/projects/61bf5c65590bbb002e8a9dc3/graph) on Canonic, and you're done. It will provide you with the backend, APIs, and documentation you need for integration, without writing any code. Just make sure you add your own callback URI, client ID and secret for your 0auth provider
    
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bxaaeajk1fbv2fwm92av.png)
 
    
You will have to add CMS entries for the `Products` as cloning a project does not clone CMS entries. 
    
---
    
## Backend integration with GraphQL
    
Let's now integrate! Now that we have our APIs ready, let's move on by installing **GraphQL packages.**
    
- **Install GraphQL packages**
        
To pull our data from the backend, we will need two packages - Apollo Client and GraphQL 
        
```bash
npm i @apollo/client graphql
```
        
- Configure GraphQL to communicate with the backend
        
Configure the Apollo Client in the project directory, inside `index.js` configure your apollo client so it would communicate with the backend. 
        
> Note to replace the `uri` with the one you'll get from Canonic.
> 
        
```jsx
        import React from "react";
        import ReactDOM from "react-dom";
        import { ApolloClient, InMemoryCache, ApolloProvider } from "@apollo/client";
        
        import App from "./App";
        
        const client = new ApolloClient({
          uri: "https://marketplace-app.can.canonic.dev/graphql", //You can replace this with your URI
          cache: new InMemoryCache(),
        });
        
        ReactDOM.render(
          <React.StrictMode>
            <ApolloProvider client={client}>
              <App />
            </ApolloProvider>
          </React.StrictMode>,
          document.getElementById("root")
        );
```
                
- Query the data
        
For querying the data, we will create a directory inside src called `gql` and create a file inside it called `query.js`. In it will write all the data we need from the backend.
        
```
        import { gql } from "@apollo/client";
        
        export const LOGIN_WITH_GOOGLE = gql`
          query {
            getLoginUrlsForLogin {
              GOOGLE
            }
          }
        `;
        
        export const GET_PRODUCTS = gql`
          query {
            products {
              title
              _id
              price
              ratings
              priceApiId
              image {
                url
                name
              }
            }
          }
        `;
```
        
- Setting up login with Google 0auth
        
Let’s go back to the `Header` component, we will be importing the query we declared in previous step and holding it in `data` variable we get from `useQuery` hook by Apollo
        
```jsx
        import { useQuery } from "@apollo/client";
        import { LOGIN_WITH_GOOGLE } from "../../gql/query";
        
        	const { data, loading: loginDataLoading } = useQuery(LOGIN_WITH_GOOGLE)
```
        
We will add the following JSX to display the login button. `data?.getLoginUrlsForLogin?.GOOGLE` In here, the `GOOGLE` object contains login and callback links to let user login themeselves in. 
        
```jsx
        <Box sx={{ flexGrow: 0 }}>
          <Tooltip title="Account">
             {loginDataLoading && <CircularProgress color="secondary" />}
                <a href={data?.getLoginUrlsForLogin?.GOOGLE}>
                    <Button variant="contained" startIcon={<GoogleIcon />}>
                       <span sx={{ textDecoration: "none" }}>Login</span>
                    </Button>
                </a>
        </Box>
```
        
After adding these, the Header component should look something like this:
        
```jsx
        import React from "react";
        import { useQuery } from "@apollo/client";
        import {
          AppBar,
          Box,
          Toolbar,
          Typography,
          Container,
          Button,
          Tooltip,
          CircularProgress,
        } from "@mui/material";
        import GoogleIcon from "@mui/icons-material/Google";
        
        import { LOGIN_WITH_GOOGLE } from "../../gql/query";
        
        function Header() {
          const { data, loading: loginDataLoading } = useQuery(LOGIN_WITH_GOOGLE); 
          return (
            <AppBar position="static">
              <Container maxWidth="xl">
                <Toolbar disableGutters sx={{ justifyContent: "space-between" }}>
                  <Typography
                    variant="h6"
                    noWrap
                    component="div"
                    sx={{ mr: 2, display: { xs: "none", md: "flex" } }}
                  >
                    Marketplace
                  </Typography>
                  <Box sx={{ flexGrow: 0 }}>
                    <Tooltip title="Account">
                      {loginDataLoading && <CircularProgress color="secondary" />}
                      <a href={data?.getLoginUrlsForLogin?.GOOGLE}>
                        <Button variant="contained" startIcon={<GoogleIcon />}>
                          <span sx={{ textDecoration: "none" }}>Login</span>
                        </Button>
                      </a>
                    </Tooltip>
                  </Box>
                </Toolbar>
              </Container>
            </AppBar>
          );
        }
        
        export default Header;
```
        
        
- Getting logged in user’s status.
        
We are not yet done with the Header component, now that we have added a feature of letting the user sign themselves in on the app, we need to retrieve and store their information, so we could identify whether they are logged in or not, we can even use this persists their session, we also get some useful information such as first name, last name, email ID and all that good stuff. Let’s set up for it, first we have to create a mutation. Create a file called `mutation.js` inside the `gql` directory
        
```
        import { gql } from "@apollo/client";
        
        export const LOGIN_WITH_GOOGLE_MUTATION = gql`
          mutation Login($code: String!, $service: String!) {
            loginForLogin(code: $code, service: $service) {
              token
              user {
                email
                firstName
                lastName
                avatar {
                  url
                }
              }
            }
          }
        `;
```
        
Now that our mutation is ready, we can call that inside our `Header` component, let’s go back to it. The mutation would look like this:
        
```jsx
        import { useMutation } from "@apollo/client";
        
        const [loginMutation, { data: mutationData }] = useMutation(LOGIN_WITH_GOOGLE_MUTATION);
        const urlCode = new URLSearchParams(window.location.search).get("code"); //We receive a code after a successful sign in, here pulling that code from the URL
            if (urlCode) {
              loginMutation({ variables: { code: urlCode, service: "GOOGLE" } });
        		}
```
        
We receive a code from our provider be it, Google, Facebook or GitHub after the user successfully sign ins. We can get that code using this call to windows object `URLSearchParams(window.location.search).get("code")` and once we store that code we could just pass it in as a variable to the mutation. In service, you should write which ever service you are using, here this app just uses Google, so we added Google statically.
        
We will pass in this mutation inside a useEffect hook with empty array as a dependency. We’ll also need a state to hold this data we get back from mutation, so it could be used elsewhere, and while we are at it, we can store the token we receive from the mutation inside local storage, so we can persist the user login session.
        
```jsx
        const [accessToken, setAccessToken] = useState();
        const [isLoggedIn,setIsLoggedIn] = useState()
        
        useEffect(() => {
            const urlCode = new URLSearchParams(window.location.search).get("code");
            if (urlCode) {
              loginMutation({ variables: { code: urlCode, service: "GOOGLE" } }); 
          }, []);
        
        useEffect(() => {
            setAccessToken(mutationData?.loginForLogin?.token);
            setIsLoggedIn(mutationData?.loginForLogin?.user);
            if (accessToken) localStorage.setItem("_id", accessToken);
          }, [accessToken, mutationData, setIsLoggedIn]);
        
```
        
But, we will need this `isLoggedIn` state elsewhere as well, so a better option is to move this to App.js since it is a parent to all the component, we’ll let Header component receive it as props. So the App.js will look like this:
        
```jsx
        import React, { useState } from "react";
        
        import Header from "./components/header/Header";
        import Product from "./components/product/Product";
        
        function App() {
          const [isLoggedIn, setIsLoggedIn] = useState(); 
          
          return (
            <>
              {
                <div className="App">
                  <Header setIsLoggedIn={setIsLoggedIn} isLoggedIn={isLoggedIn} />
                  <Product />
                </div>
              }
            </>
          );
        }
        
        export default App;
```
        
We will also add a way, so user can sign out. For that we will be using Material UI’s components, after adding all that UI enhancements, the Header component will look like this
        
```jsx
        import React, { useEffect, useState, useCallback } from "react";
        import { useQuery, useMutation } from "@apollo/client";
        import {
          AppBar,
          Box,
          Toolbar,
          IconButton,
          Typography,
          Menu,
          Container,
          Avatar,
          Button,
          Tooltip,
          MenuItem,
          CircularProgress,
        } from "@mui/material";
        import GoogleIcon from "@mui/icons-material/Google";
        
        import { LOGIN_WITH_GOOGLE } from "../../gql/query";
        import { LOGIN_WITH_GOOGLE_MUTATION } from "../../gql/mutation";
        
        function Header({ setIsLoggedIn, isLoggedIn }) {
          const [accessToken, setAccessToken] = useState();
          const { data, loading: loginDataLoading } = useQuery(LOGIN_WITH_GOOGLE);
          const [loginMutation, { data: mutationData }] = useMutation(
            LOGIN_WITH_GOOGLE_MUTATION
          );
          useEffect(() => {
            const urlCode = new URLSearchParams(window.location.search).get("code"); 
            if (urlCode) {
              loginMutation({ variables: { code: urlCode, service: "GOOGLE" } }); 
            }
          }, []);
          useEffect(() => {
            setAccessToken(mutationData?.loginForLogin?.token);
            setIsLoggedIn(mutationData?.loginForLogin?.user);
            if (accessToken) localStorage.setItem("_id", accessToken); 
          }, [accessToken, mutationData, setIsLoggedIn]);
        
          const [anchorElNav, setAnchorElNav] = React.useState(null);
          const [anchorElUser, setAnchorElUser] = React.useState(null);
        
          const handleOpenUserMenu = useCallback((event) => {
            setAnchorElUser(event.currentTarget);
          });
        
          const handleCloseNavMenu = useCallback(() => {
            setAnchorElNav(null);
          });
        
          const handleCloseUserMenu = useCallback(() => {
            setAnchorElUser(null);
          });
        
          const onLogout = useCallback(() => {
            localStorage.removeItem("_id"); 
          });
        
          return (
            <AppBar position="static">
              <Container maxWidth="xl">
                <Toolbar disableGutters sx={{ justifyContent: "space-between" }}>
                  <Typography
                    variant="h6"
                    noWrap
                    component="div"
                    sx={{ mr: 2, display: { xs: "none", md: "flex" } }}
                  >
                    Marketplace
                  </Typography>
        
                  <Box sx={{ flexGrow: 0 }}>
                    <Tooltip title="Account">
                      {loginDataLoading ? (
                        <CircularProgress color="secondary" />
                      ) : !isLoggedIn && !localStorage.getItem("_id") ? ( 
                        <a href={data?.getLoginUrlsForLogin?.GOOGLE}>
                          <Button variant="contained" startIcon={<GoogleIcon />}>
                            <span sx={{ textDecoration: "none" }}>Login</span>
                          </Button>
                        </a>
                      ) : (
                        <IconButton onClick={handleOpenUserMenu} sx={{ p: 0 }}>
                          {isLoggedIn?.avatar?.url ? (
                            <Avatar alt="User" src={isLoggedIn.avatar.url} />
                          ) : (
                            <Avatar src="/broken-image.jpg" />
                          )}
                        </IconButton>
                      )}
                    </Tooltip>
                    <Menu
                      sx={{ mt: "45px" }}
                      id="menu-appbar"
                      anchorEl={anchorElUser}
                      anchorOrigin={{
                        vertical: "top",
                        horizontal: "right",
                      }}
                      keepMounted
                      transformOrigin={{
                        vertical: "top",
                        horizontal: "right",
                      }}
                      open={Boolean(anchorElUser)}
                      onClose={handleCloseUserMenu}
                    >
                      <MenuItem onClick={handleCloseNavMenu}>
                        <Typography textAlign="center">
                          <a
                            onClick={onLogout}
                            href={`https://www.google.com/accounts/Logout?continue=https://appengine.google.com/_ah/logout?continue=${window.origin}`}
                          >
                            Logout
                          </a>
                        </Typography>
                      </MenuItem>
                    </Menu>
                  </Box>
                </Toolbar>
              </Container>
            </AppBar>
          );
        }
        
        export default Header;
  ```
        
- Finish the Product component
        
We will now shift our focus to `Product` component. To maintain component simplicity so, one component doesn’t get too complicated, we will create a Home component that would act as a parent `Product` components. Let’s set that up by creating a directory inside component and calling it Home and creating a Home.js file inside it.
        
```jsx
        import React, { useMemo } from "react";
        import { useQuery } from "@apollo/client";
        import { Box, CircularProgress } from "@mui/material";
        
        import Product from "../product/Product.js";
        import { GET_PRODUCTS } from "../../gql/query";
        
        function Home() {
          const { data, loading: productsLoading } = useQuery(GET_PRODUCTS); 
          const products = useMemo(() => data?.products || [], [data?.products]); 
        
          return (
            <Box
              sx={{
                display: "flex",
                flexDirection: "row",
                flexWrap: "wrap",
                gap: "4rem",
                marginTop: "4rem",
              }}
            >
              {productsLoading && (
                <CircularProgress sx={{ position: "absolute", left: "50%" }} />
              )}
              {products.map((item, i) => {
                return (
                  <Product
                    key={i}
                    id={item.id}
                    title={item.title}
                    image={item.image.url}
                    price={item.price}
                    rating={item.ratings}
                    price_api={item.priceApiId}
                  />
                );
              })}
            </Box>
          );
        }
        
        export default Home;
```
        
Now that we are receiving the data dynamically, we can finally toss the static array of data from Product.js, let’s get to it.
        
```jsx
        import React from "react";
        import {
          Card,
          CardContent,
          CardMedia,
          Typography,
          Rating,
        } from "@mui/material";
        
        function Product({ title, price, rating, image, price_api }) {
          return (
            <Card
              sx={{
                maxHeight: 450,
                minWidth: 100,
                width: "25%",
                margin: "1.5rem",
                display: "flex",
                flexDirection: "column",
                justifyContent: "space-between",
                alignItems: "start",
              }}
            >
              <CardMedia
                component="img"
                alt="title"
                height="auto"
                image={image}
                sx={{ objectFit: "contain", maxHeight: "200px" }}
              />
              <CardContent>
                <Typography gutterBottom variant="h5" component="div">
                  {title}
                </Typography>
                <Typography gutterBottom variant="h5" component="div">
                  ${price}
                </Typography>
                <Rating name="read-only" value={rating} readOnly />
              </CardContent>
            </Card>
          );
        }
        
        export default Product;
```
                
We are almost the completion here, just need to `Stripe’s client only checkout` 
        
For that, first you need to have an account on Stripe, after that you should have Stripe’s API key. You can get your keys [here](https://dashboard.stripe.com/account/apikeys). Then we will install stripe 
        
```
        npm install --save @stripe/react-stripe-js @stripe/stripe-js
```
        
Now that all these is done, let’s hop back into `Home` component. We will create an asynchronous function let’s name it handle click since it will be handle click from a ‘Buy now’ button.
        
```jsx
        import { loadStripe } from "@stripe/stripe-js";
        
        const stripePromise = loadStripe(process.env.REACT_APP_STRIPE_API);
        const handleClick = async () => {
              setLoading(true);
              const stripe = await stripePromise;
              const { error } = await stripe.redirectToCheckout({
                lineItems: [
                  {
                    price: price_api, 
                    quantity: 1,
                  },
                ],
                mode: "payment",
                cancelUrl: "https://canonic-marketplace.netlify.app/", 
                successUrl: "https://canonic-marketplace.netlify.app/", 
              if (error) {
                setLoading(false);
                console.log("The error ", error);
              }
          };
```
        
Instead of `process.env.REACT_APP_STRIPE_API` you can add your own API key, you can replace cancelUrl and successUrl with your own URL as well. In here we are using price_api as a value to price key. In stripe every product has a unique `product price ID` I have stored all my product price ID on Canonic CMS, you thus using it as a value. 
        
In your own project’s CMS you can add your own Product’s price ID in the price_api field.
        
Last bit of validation we can is to check if the user is signed in or not before proceeding, since we have `isLoggedIn` inside the App component, we can easily get it through pass it to Home components 
        
```jsx
        import React, { useState } from "react";
        
        import Header from "./components/header/Header";
        import Home from "./components/home/Home";
        
        function App() {
          const [isLoggedIn, setIsLoggedIn] = useState(); 
          
          return (
            <>
              {
                <div className="App">
                  <Header setIsLoggedIn={setIsLoggedIn} isLoggedIn={isLoggedIn} />
                  **<Home isLoggedIn={isLoggedIn} />**
                </div>
              }
            </>
          );
        }
        
        export default App;
```
                
We will have to pass in handleClick function along with loading state, we are using loading state to disable to ‘Buy now’ button after user click on it, so there won’t be multiple call to Stripe.
        
```jsx
        import React, { useState, useEffect, useMemo } from "react";
        import {  useQuery } from "@apollo/client";
        import { loadStripe } from "@stripe/stripe-js";
        import { Box, CircularProgress } from "@mui/material";
        
        import Product from "../product/Product.js";
        import { GET_PRODUCTS } from "../../gql/query";
        
        function Home({ **isLoggedIn** }) {
          const { data, loading: productsLoading } = useQuery(GET_PRODUCTS); 
          const products = useMemo(() => data?.products || [], [data?.products]);
          const stripePromise = loadStripe(process.env.REACT_APP_STRIPE_API); 
          const [loading, setLoading] = useState();
        
          const handleClick = async (price_api, title) => {
        
            if (isLoggedIn) {
              setLoading(true);
              const stripe = await stripePromise;
              const { error } = await stripe.redirectToCheckout({
                lineItems: [
                  {
                    price: price_api, 
                    quantity: 1,
                  },
                ],
                mode: "payment",
                cancelUrl: window.origin,
                successUrl: window.origin + `?session_id=${title}`,
              });
              if (error) {
                setLoading(false);
              }
            } else alert("Please log in to continue");
          };
        
          return (
            <Box
              sx={{
                display: "flex",
                flexDirection: "row",
                flexWrap: "wrap",
                gap: "4rem",
                marginTop: "4rem",
              }}
            >
              {productsLoading && (
                <CircularProgress sx={{ position: "absolute", left: "50%" }} />
              )}
              {products.map((item, i) => {
                return (
                  <Product
                    key={i}
                    id={item.id}
                    title={item.title}
                    image={item.image.url}
                    price={item.price}
                    rating={item.ratings}
                    price_api={item.priceApiId}
                    **handleClick={handleClick}
                    loading={loading}**
                  />
                );
              })}
            </Box>
          );
        }
        
        export default Home;
```
        
Product component is now receiving props of handleClick and loading, we can finally create a Buy now button 
        
```jsx
        import React from "react";
        import {
          Card,
          CardActions,
          CardContent,
          CardMedia,
          Button,
          Typography,
          Rating,
        } from "@mui/material";
        
        function Product({
          title,
          price,
          rating,
          image,
          price_api,
          handleClick,
          loading,
        }) {
          return (
            <Card
              sx={{
                maxHeight: 450,
                minWidth: 100,
                width: "25%",
                margin: "1.5rem",
                display: "flex",
                flexDirection: "column",
                justifyContent: "space-between",
                alignItems: "start",
              }}
            >
              <CardMedia
                component="img"
                alt={title}
                height="auto"
                image={image}
                sx={{ objectFit: "contain", maxHeight: "200px" }}
              />
              <CardContent>
                <Typography gutterBottom variant="h5" component="div">
                  {title}
                </Typography>
                <Typography gutterBottom variant="h5" component="div">
                  ${price}
                </Typography>
                <Rating name="read-only" value={rating} readOnly />
              </CardContent>
              <CardActions>
                <Button
                  variant="contained"
                  size="small"
                  onClick={() => handleClick(price_api, title)} 
                  disabled={loading}
                >
                  Buy now
                </Button>
              </CardActions>
            </Card>
          );
        }
        
        export default Product;
```
                
And, with this we can conclude our frontend.
        
---
        
## Bonus! - Sending email and Slack notification
        
We can add webhooks to perform actions such as send a Slack notification whenever a product is purchased or send an email notification. Let’s see how that done.
        
- **Configuring the webhooks**
            
Open your cloned project on Canonic, navigate to API and choose Notify table. Click on **createNotify** endpoint there you can see Message webhook of Slack, you can click on it and replace trigger URL and message body with your own trigger URL and message body. Chained to that you will find email webhook of Canonic, there as well, you can replace email subject and email body with your content.
            
![Screenshot 2021-12-21 at 8.04.17 PM.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/24d2c646-1882-4dc3-bf11-626da7f57166/Screenshot_2021-12-21_at_8.04.17_PM.png)
            
- Configuring frontend
            
In order to trigger these webhooks, we need to make a mutation. Let’s begin by declaring a mutation in our mutation.js file
            
```graphql
            export const NOTIFY = gql`
              mutation Notify($title: String!) {
                createNotify(input: { title: $title }) {
                  title
                }
              }
            `;
```
            
Final file would look like this:
            
```graphql
            import { gql } from "@apollo/client";
            
            export const LOGIN_WITH_GOOGLE_MUTATION = gql`
              mutation Login($code: String!, $service: String!) {
                #This mutation is used to get logged in user's details
                loginForLogin(code: $code, service: $service) {
                  #We feed in code which we get after user successfully signs in, services are the 0auth services we are using such as Google,Github and Facebook.
                  token
                  user {
                    email
                    firstName
                    lastName
                    avatar {
                      url
                    }
                  }
                }
              }
            `;
            
            export const NOTIFY = gql`
              mutation Notify($title: String!) {
                createNotify(input: { title: $title }) {
                  title
                }
              }
            `;
```
            
We have to trigger this mutation from Home component. Let’s go back to the Home component, there we will modify `successUrl` in handleClick function to include the title of the product in URL whenever a product is checked out successfully
            
```jsx
            const handleClick = async (price_api, title) => {
            
                if (isLoggedIn) {
                  setLoading(true);
                  const stripe = await stripePromise;
                  const { error } = await stripe.redirectToCheckout({
                    lineItems: [
                      {
                      price: price_api, 
                        quantity: 1,
                      },
                    ],
                    mode: "payment",
                    cancelUrl: window.origin,
                    successUrl: window.origin + `?session_id=${title}`,
                  });
                  if (error) {
                    setLoading(false);
                  }
                } else alert("Please log in to continue");
              };
```
            
And, add useEffect to with empty dependency array, to check if URL has the product’s title
            
```jsx
            useEffect(() => {
                const hasSuccessUrl = new URLSearchParams(window.location.search).get(
                  "session_id"
                ); 
                if (hasSuccessUrl) {
                 //Do something
                }
              }, []);
```
            
Now that everything is set up we can trigger the mutation
            
```jsx
            import { useMutation } from "@apollo/client";
            import { NOTIFY } from "../../gql/mutation";
            
            const [notify] = useMutation(NOTIFY);
            
            useEffect(() => {
                const hasSuccessUrl = new URLSearchParams(window.location.search).get(
                  "session_id"
                ); 
                if (hasSuccessUrl) {
                  notify({ variables: { title: hasSuccessUrl } });
                }
              }, []);
```
            
So the final file would look like this
            
```jsx

import React, { useState, useEffect, useMemo } from "react";
import { useMutation, useQuery } from "@apollo/client";
import { NOTIFY } from "../../gql/mutation";
import { loadStripe } from "@stripe/stripe-js";
import { Box, CircularProgress } from "@mui/material";
            
import Product from "../product/Product.js";
import { GET_PRODUCTS } from "../../gql/query";
            
function Home({ isLoggedIn }) {
  const { data, loading: productsLoading } = 
  useQuery(GET_PRODUCTS); 
  const products = useMemo(() => data?.products || [], [data?.products]); 
  const stripePromise = loadStripe(process.env.REACT_APP_STRIPE_API); 
  const [loading, setLoading] = useState(); 
  const [notify] = useMutation(NOTIFY);
            
  const handleClick = async (price_api, title) => {
                if (isLoggedIn) {
                  setLoading(true);
                  const stripe = await stripePromise;
                  const { error } = await stripe.redirectToCheckout({
                    lineItems: [
                      {
                        price: price_api, 
                        quantity: 1,
                      },
                    ],
                    mode: "payment",
                    cancelUrl: window.origin,
                    successUrl: window.origin + `?session_id=${title}`, 
                  });
            
                  if (error) {
                    setLoading(false);
                  }
                } else alert("Please log in to continue");
              };
            
              useEffect(() => {
                const hasSuccessUrl = new URLSearchParams(window.location.search).get(
                  "session_id"
                ); 
                if (hasSuccessUrl) {
                  notify({ variables: { title: hasSuccessUrl } });
                }
              }, []);
              return (
                <Box
                  sx={{
                    display: "flex",
                    flexDirection: "row",
                    flexWrap: "wrap",
                    gap: "4rem",
                    marginTop: "4rem",
                  }}
                >
                  {productsLoading && (
                    <CircularProgress sx={{ position: "absolute", left: "50%" }} />
                  )}
                  {products.map((item, i) => {
                    return (
                      <Product
                        key={i}
                        id={item.id}
                        title={item.title}
                        image={item.image.url}
                        price={item.price}
                        rating={item.ratings}
                        price_api={item.priceApiId}
                        handleClick={handleClick}
                        loading={loading}
                      />
                    );
                  })}
                </Box>
              );
            }
            
  export default Home;
            
```            
    
Once you've reached here, you've successfully completed the Marketplace project.
    
You can clone this sample project here. We hope this guide helped you to create your own Marketplace app; if you do, then share it with us in our Discord community.If you are not already a member, join us, and let's build together.
    
Connect on [Discord](https://discord.gg/9dyytsM).If you'd like to see our other guides, they're all **[here](https://app.canonic.dev/projects/61af33b3b9347d002d5beb7b/api-2?utm_source=dev.to&utm_medium=organic_article&utm_campaign=30daysofcanonic_uc_4&utm_term=step5_here&utm_content=day_22)**.For any support requests, write to us at [support@canonic.dev](mailto:support@canonic.dev). Check out our [website](https://www.canonic.dev/30-days-of-canonic/?utm_source=dev.to&utm_medium=organic_article&utm_campaign=30daysofcanonic_uc_4&utm_term=conclusion_website&utm_content=day_22) to know more about Canonic.