테이프 요금 계산으로 시장 응용 프로그램 구축
소개하다.
전염병 사태로 집안에 갇히면서 시장 앱이 사람들이 제품을 구매하는 주요 장소가 되면서 대중들의 관심이 급격히 높아졌다.시장 모델은 왜 온라인 시장이 이렇게 인기가 많고 유익한지 설명한다.그것은 모든 시장과 리키 시장을 건설할 수 있으며, 초기 재고가 필요 없고, 배로 성장할 수 있으며, 모든 사람에게 이롭다. 판매자, 구매자, 물론 시장 소유자도 있다.
인터넷에서 제품을 판매하고 대량의 마케팅 업무를 하지 않는 것은 판매자에게 매우 매력적이다.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
uriwith 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가 datahook에서 얻은 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.)