테이프 요금 계산으로 시장 응용 프로그램 구축
소개하다.
전염병 사태로 집안에 갇히면서 시장 앱이 사람들이 제품을 구매하는 주요 장소가 되면서 대중들의 관심이 급격히 높아졌다.시장 모델은 왜 온라인 시장이 이렇게 인기가 많고 유익한지 설명한다.그것은 모든 시장과 리키 시장을 건설할 수 있으며, 초기 재고가 필요 없고, 배로 성장할 수 있으며, 모든 사람에게 이롭다. 판매자, 구매자, 물론 시장 소유자도 있다.
인터넷에서 제품을 판매하고 대량의 마케팅 업무를 하지 않는 것은 판매자에게 매우 매력적이다.Marketplace 응용 프로그램은 자신의 전자상거래 사이트/응용 프로그램을 만든 다음에 데이터를 더 간단하고 빨리 얻으려고 노력한다.
광범위한 선택과 가격, 그리고 제품을 비교하는 능력이 있기 때문에 고객이 제품을 구매하는 것도 편리하다.
시장을 가지고 있으면 당신에게 많은 효과적인 수익 창출 방식을 제공할 수 있다.광고, 매 거래는 일정 비율의 비용을 받고 more.도 많기 때문에 우리는 깊이 있게 이해하고 어떻게 스스로 하나를 만드는지 알아봅시다!
시작해보도록 하겠습니다.
creact-react-app
를 사용하여 템플릿을 작성합니다.npx create-react-app marketplace
cd marketplace
npm install @mui/material @emotion/react @emotion/styled
src
에 디렉터리를 만들고 components
라고 명명하면 우리가 구축하고자 하는 모든 다른 구성 요소를 놓을 것입니다!다른 디렉토리를 만들고 이름을
Product
로 지정합니다.Product.js
디렉터리에 Product
를 만듭니다.이 구성 요소는 우리가 가지고 있는 제품 목록을 보여 줍니다.우선, 우리는 정적 제품 목록을 순환적으로 훑어볼 것이다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;
Header.js
라고 이름을 붙입니다.이 파일은 제목을 포함하고 프로그램의 이름을 표시한 다음 사용자 로그인/로그아웃 단추를 표시합니다.import React from "react";
import Product from "./components/product/Product";
import Header from "./components/header/Header";
function App() {
return (
<>
<Header />
<Product />
</>
);
}
export default App;
이렇게 해야 합니다.클론 항목은 CMS 항목을 복제하지 않으므로
Products
에 CMS 항목을 추가해야 합니다.GraphQL과의 백엔드 통합
이제 통합합시다!이제 API가 준비되었으므로 GraphQL 패키지를 계속 설치해 보겠습니다.
npm i @apollo/client graphql
index.js
에 Apollo 클라이언트를 설정하여 백엔드와 통신하도록 합니다.Note to replace the
uri
with the one you'll get from Canonic.
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")
);
gql
라는 디렉터리를 만들고, 그 중에서 query.js
라는 파일을 만들 것입니다.그것은 백엔드에서 우리가 필요로 하는 모든 데이터를 쓸 것이다. 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
}
}
}
`;
Header
구성 요소로 돌아가서 이전 단계에서 설명한 검색어를 가져오고 Apollo가 data
hook에서 얻은 useQuery
변수에 저장합니다 import { useQuery } from "@apollo/client";
import { LOGIN_WITH_GOOGLE } from "../../gql/query";
const { data, loading: loginDataLoading } = useQuery(LOGIN_WITH_GOOGLE)
다음 JSX를 추가하여 로그인 버튼을 표시합니다.data?.getLoginUrlsForLogin?.GOOGLE
여기서 GOOGLE
대상은 로그인과 리셋 링크를 포함하고 사용자는 자신에 로그인할 수 있다. <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>
이러한 요소를 추가하면 Header 구성 요소가 다음과 같이 표시됩니다. 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;
mutation.js
디렉터리에 gql
라는 파일을 만듭니다. 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
}
}
}
}
`;
현재 우리의 돌연변이가 준비되어 있습니다. Header
구성 요소에서 그것을 호출해서 그것을 되돌릴 수 있습니다.돌변은 다음과 같습니다. 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" } });
}
사용자가 성공적으로 로그인하면 it, 구글, 페이스북, GitHub 공급자로부터 코드를 받을 수 있습니다.우리는 윈도우즈 대상URLSearchParams(window.location.search).get("code")
에 대한 호출을 사용하여 코드를 얻을 수 있으며, 코드를 저장하면 변이에 변수로 전달할 수 있다.서비스에서 당신이 사용하고 있는 서비스를 적어야 합니다. 이 앱은 구글만 사용하기 때문에 Google은 정태적으로 구글을 추가했습니다.우리는 이 변이를 의존항으로 빈 그룹을 가진useEffect 갈고리에 전달할 것입니다.우리는 변이에서 얻은 데이터를 다른 곳에서 사용할 수 있도록 상태를 저장해야 한다. 그리고 변이에서 받은 영패를 로컬 저장소에 저장해서 사용자 로그인 세션을 지속시킬 수 있다.
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]);
그러나 다른 곳에서도 이 상태isLoggedIn
가 필요하기 때문에 응용 프로그램으로 이동하는 것이 더 좋다.이것은 모든 구성 요소의 부모 구성 요소이기 때문에, 헤더 구성 요소를 도구로 받아들일 것입니다.그래서 이 앱은js는 다음과 같습니다. 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;
사용자가 로그아웃할 수 있도록 방법도 추가할 것입니다.이를 위해 Material UI의 구성 요소를 사용하고 모든 UI 개선 사항을 추가하면 제목 구성 요소는 다음과 같습니다 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;
Product
구성 요소로 중점을 옮길 것이다.구성 요소의 단순성을 유지하고 구성 요소가 복잡하지 않도록 하기 위해서, 부모 구성 요소 Product
로 사용할 주 구성 요소를 만들 것입니다.구성 요소 내에 디렉터리를 만들고 그것을 Home이라고 부르며 Home을 만들어서 설정합니다.안에 js 파일이 있습니다. 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;
현재 우리는 동적으로 데이터를 수신하고 있으며, 우리는 마침내 제품에서 정적 데이터 그룹을 던질 수 있다.js, 우리 시작합시다. 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;
우리는 거의 이곳을 완성했다. 단지 Stripe’s client only checkout
이를 위해서는 먼저 Stripe에 계정이 있어야 하고, Stripe의 API 키가 있어야 한다.너는 열쇠를 받을 수 있다here.그리고 stripe를 설치할 거예요. npm install --save @stripe/react-stripe-js @stripe/stripe-js
이제 이 모든 것이 끝났습니다. Home
구성 요소로 돌아가겠습니다."Buy now"버튼의 handle click이므로 비동기 함수를 만듭니다. 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);
}
};
cancelUrl과 successUrl을 사용자 URL으로 바꿀 수 있습니다. process.env.REACT_APP_STRIPE_API
사용자 API 키를 추가하는 것이 아니라.여기서 price api를 가격 키의 값으로 사용합니다.stripe에서 모든 제품은 유일하다product price ID
. 저는 모든 제품의 가격 ID를 Canonic CMS에 저장했기 때문에 당신은 그것을 값으로 사용할 수 있습니다.프로젝트의 CMS에서 price api 필드에 자신의 제품의 price ID를 추가할 수 있습니다.
우리가 할 수 있는 마지막 검증은 계속하기 전에 사용자가 로그인했는지 확인하는 것이다. 왜냐하면 우리는 응용 프로그램 구성 요소에
isLoggedIn
있기 때문에, 우리는 그것을 메인 구성 요소에 쉽게 전달할 수 있기 때문이다. 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;
handleClick 기능을 불러오는 상태와 함께 전달해야 합니다. 불러오는 상태를 사용하면 사용자가 클릭한 후에 '즉시 구매' 단추를 사용하지 않기 때문에 Stripe를 여러 번 호출하지 않습니다. 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;
제품 구성 요소는handleClick과loading의 도구를 받고 있습니다. 드디어 Buy now 단추를 만들 수 있습니다. 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;
이것만 있으면 우리는 우리의 앞부분을 끝낼 수 있다.상금! - 상금!이메일 및 알림 보내기
우리는 제품을 구매할 때 느슨한 알림을 보내거나 이메일 알림을 보내는 등 몇 가지 작업을 수행하기 위해 웹훅을 추가할 수 있다.어떻게 하는지 보여줘.
Canonic에서 클론 항목을 열고 API로 이동하여 Notify table을 선택합니다.createNotify endpoint를 누르면 슬랙의 메시지 웹훅을 볼 수 있습니다. 이를 누르면 자신의 트리거 URL과 메시지체로 트리거 URL과 메시지체를 바꿀 수 있습니다.이 점에 연결하면 Canonic의 전자메일 웹훅을 발견할 수 있습니다. 거기에서도 전자메일 테마와 본문을 내용으로 바꿀 수 있습니다.
export const NOTIFY = gql`
mutation Notify($title: String!) {
createNotify(input: { title: $title }) {
title
}
}
`;
최종 파일은 다음과 같습니다. 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
}
}
`;
우리는 반드시 집에서 이런 돌변을 촉발해야 한다.Home 구성 요소로 돌아가서handleClick 함수 successUrl
를 수정해서 제품이 성공적으로 서명되었을 때 URL에 제품의 제목을 포함할 수 있도록 합니다. 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");
};
또한 URL에 제품 제목이 포함되어 있는지 확인하기 위해useEffect를 빈 의존 항목 그룹에 추가합니다 useEffect(() => {
const hasSuccessUrl = new URLSearchParams(window.location.search).get(
"session_id"
);
if (hasSuccessUrl) {
//Do something
}
}, []);
이제 모든 것이 준비되었으니 우리는 돌변을 일으킬 수 있다 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 } });
}
}, []);
마지막 파일은 이렇습니다.
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;
일단 네가 여기에 도착하면, 너는 시장 프로젝트를 성공적으로 완성할 수 있을 것이다.이 예제 항목을 복제할 수 있습니다.우리는 이 안내서가 당신이 자신의 시장 응용 프로그램을 만드는 데 도움을 줄 수 있기를 바랍니다.만약 당신이 원한다면, 우리 불협화음의 지역사회에서 우리와 나누세요.만약 당신이 아직 회원이 아니라면, 우리에 가입하여, 우리가 공동으로 노력할 수 있도록 하세요.
연결Discord.만약 네가 우리의 다른 가이드를 보고 싶다면, 그들은 모두 그렇다.지원 요청이 있으면 [email protected]에 문의하십시오.Canonic에 대한 자세한 내용은 웹 사이트를 참조하십시오.
Reference
이 문제에 관하여(테이프 요금 계산으로 시장 응용 프로그램 구축), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/canonic/build-marketplace-app-with-stripe-billing-3j8텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)