Magic and Stripe: Pt로 유료 회원 사이트를 만듭니다.2-반응 클라이언트

두 번째 부분에서는 유료 회원 사이트와 마술, 줄무늬 시리즈를 구축한다.계속하기 전에 우리의 건의를 따라야 한다.

손님: 네.


유료 회원 사이트 클라이언트를 구축하는 데 필요한 주요 절차를 살펴보고 깊이 있게 살펴보겠습니다.
  • 사용자 등록, 결제, 로그인 및 로그오프 프로세스를 설정합니다.
  • 은 지불 양식과 이 양식을 포함하는 지불 페이지를 구축한다.
  • 은 결제 경로를 작성하여 결제 페이지에 액세스할 수 있도록 합니다.
  • 표준 인증 설정


    로그인한 사용자 추적


    우리는 React의 useContext 갈고리를 사용하여 로그인 사용자의 상태를 추적할 것입니다.App.js에서는 전체 어플리케이션을 <UserContext.Provider>으로 포장합니다.이렇게 하면 모든 하위 구성 요소가 우리가 만든 갈고리 (즉 const [user, setUser] = useState();) 에 접근하여 사용자가 로그인했는지 확인하는 데 도움을 줄 수 있습니다.
    /* File: client/src/App.js */
    
    import React, { useState, useEffect } from "react";
    import { Switch, Route, BrowserRouter as Router } from "react-router-dom";
    import { UserContext } from "./lib/UserContext";
    
    // Import UI components
    import Home from "./components/home";
    import  PremiumContent  from  "./components/premium-content";
    import Login from "./components/login";
    import  SignUp  from  "./components/signup";
    import Profile from "./components/profile";
    import Layout from "./components/layout";
    
    // Import Magic-related things
    import { magic } from "./lib/magic";
    
    function App() {
    
      // Create a hook to help us determine whether or not the  user is logged in
      const [user, setUser] = useState();
    
      // If isLoggedIn is true, set the UserContext with user data
      // Otherwise, set it to {user: null}
      useEffect(() => {
        setUser({ loading: true });
        magic.user.isLoggedIn().then((isLoggedIn) => {
          return isLoggedIn
            ? magic.user.getMetadata().then((userData) => setUser(userData))
            : setUser({ user: null });
        });
      }, []);
    
      return (
        <Router>
          <Switch>
            <UserContext.Provider value={[user, setUser]}>
                  <Layout>
                    <Route path="/" exact component={Home} />
                    <Route path="/premium-content" component={PremiumContent} />
                    <Route path="/signup" component={SignUp} />
                    <Route path="/login" component={Login} />
                    <Route path="/profile" component={Profile} />
                  </Layout>
            </UserContext.Provider>
          </Switch>
        </Router>
      );
    }
    
    export default App;
    
    참고: 사용자가 로그아웃하지 않는 한 Magic으로 로그인하면 7일 동안 인증을 유지합니다.

    유료 사용자 추적


    우리는 또한 사용자가 useContext 갈고리를 사용하여 종신 방문 비용을 지불했는지 추적할 것이다.마찬가지로 App.js 내부에서 우리는 두 개의 새로운 상하문으로 전체 응용 프로그램을 포장했다. 그것이 바로 <LifetimeContext>이고 그 다음은 <LifetimeAccessRequestStatusContext>이다.
    /* File: client/src/App.js */
    import React, { useState, useEffect } from "react";
    import { Switch, Route, BrowserRouter as Router } from "react-router-dom";
    import { UserContext } from "./lib/UserContext";
    import { LifetimeContext } from "./lib/LifetimeContext";
    import { LifetimeAccessRequestStatusContext } from "./lib/LifetimeAccessRequestStatusContext";
    
    // Import UI components
    import Home from "./components/home";
    import  PremiumContent  from  "./components/premium-content";
    import Login from "./components/login";
    import  SignUp  from  "./components/signup";
    import Profile from "./components/profile";
    import Layout from "./components/layout";
    
    // Import Magic-related things
    import { magic } from "./lib/magic";
    
    function App() {
    
      // Create a hook to check whether or not user has lifetime access
      const [lifetimeAccess, setLifetimeAccess] = useState(false);
      // Create a hook to prevent infinite loop in useEffect inside of /components/premium-content
      const [
        lifetimeAccessRequestStatus,
        setLifetimeAccessRequestStatus,
      ] = useState("");
      // Create a hook to help us determine whether or not the  user is logged in
      const [user, setUser] = useState();
    
      // If isLoggedIn is true, set the UserContext with user data
      // Otherwise, set it to {user: null}
      useEffect(() => {
        setUser({ loading: true });
        magic.user.isLoggedIn().then((isLoggedIn) => {
          return isLoggedIn
            ? magic.user.getMetadata().then((userData) => setUser(userData))
            : setUser({ user: null });
        });
      }, []);
    
      return (
        <Router>
          <Switch>
            <UserContext.Provider value={[user, setUser]}>
              <LifetimeContext.Provider value={[lifetimeAccess, setLifetimeAccess]}>
                <LifetimeAccessRequestStatusContext.Provider
                  value={[
                    lifetimeAccessRequestStatus,
                    setLifetimeAccessRequestStatus,
                  ]}
                >
                  <Layout>
                    <Route path="/" exact component={Home} />
                    <Route path="/premium-content" component={PremiumContent} />
                    <Route path="/signup" component={SignUp} />
                    <Route path="/login" component={Login} />
                    <Route path="/profile" component={Profile} />
                  </Layout>
                </LifetimeAccessRequestStatusContext.Provider>
              </LifetimeContext.Provider>
            </UserContext.Provider>
          </Switch>
        </Router>
      );
    }
    
    export default App;
    
    보시다시피 우리는 두 개의 새로운 갈고리를 추가했다.첫 번째 갈고리는 사용자가 평생 접근 권한을 가지고 있는지 확인하는 데 도움을 줄 것입니다.
    const [lifetimeAccess, setLifetimeAccess] = useState(false);
    
    두 번째 갈고리는 an infinite loop of component re-renderingsuseEffect 내부의 /components/premium-content으로 인해 발생하는 것을 방지하는 데 도움을 줄 것이다.
      const [
        lifetimeAccessRequestStatus,
        setLifetimeAccessRequestStatus,
      ] = useState("");
    

    Magic Link Auth를 사용하여 로그인

    client/src/components/login.js에서 magic.auth.loginWithMagicLink()은 사용자에게 전자메일을 보내는 신기한 링크를 터치한다.그것은 두 개의 매개 변수를 가진 대상을 받아들인다. 그것이 바로 email과 선택할 수 있는 redirectURI이다.
    Magic에서는 새 탭을 열고 응용 프로그램으로 사용자를 가져오도록 전자 메일 링크를 구성할 수 있습니다.리디렉션을 사용하지 않기 때문에 사용자는 원본 옵션에만 로그인할 수 있습니다.
    사용자가 이메일 링크를 클릭하면 인증을 위해 didToken /login의 서버 엔드포인트로 보냅니다.만약 영패가 유효하다면, 우리는 UserContext을 설정하여 사용자의 상태를 업데이트한 다음, 이를 설정 파일 페이지로 다시 지정합니다.
    /* File: client/src/components/login.js */
    
      async function handleLoginWithEmail(email) {
        try {
          setDisabled(true); // Disable login button to prevent multiple emails from being triggered
    
          // Trigger Magic link to be sent to user
          let didToken = await magic.auth.loginWithMagicLink({
            email,
          });
    
          // Validate didToken with server
          const res = await fetch(`${process.env.REACT_APP_SERVER_URL}/login`, {
            method: "POST",
            headers: {
              "Content-Type": "application/json",
              Authorization: "Bearer " + didToken,
            },
          });
    
          if (res.status === 200) {
            // Get info for the logged in user
            let userMetadata = await magic.user.getMetadata();
            // Set the UserContext to the now logged in user
            await setUser(userMetadata);
            history.push("/profile");
          }
        } catch (error) {
          setDisabled(false); // Re-enable login button - user may have requested to edit their email
          console.log(error);
        }
      }
    

    Magic Link Auth 등록


    우리는 Login 구성 요소(SignUp에 위치)에서 client/src/components/signup.js 구성 요소와 거의 같은 코드를 적용할 것이다.유일한 차이점은 사용자 체험이다.
    사용자가 우리의 페이지에 처음 로그인했을 때, 그들은 우리의 무료 내용을 방문할 수 있다.

    우리는 또한 고급 콘텐츠 페이지에서 그들에게 우리의 놀라운 점을 보여줄 수 있다.

    일단 그들이 우리가 얼마나 훌륭한지 깨닫고 500달러를 지불하고 평생 통행증을 구매하기로 결정하면, 그들은 Count Me In을 클릭할 수 있다.

    사용자가 로그인하지 않았기 때문에, 우리 프로그램은 그들에게 먼저 새 계정을 등록하도록 요구할 것이다.Magic 인증을 통과하면 결제 페이지로 리디렉션됩니다. 그곳에서 거래를 봉인하여 평생 놀라운 경험을 할 수 있습니다!

    Magic을 사용하여 로그아웃


    사용자가 로그아웃할 수 있도록 logout 구성 요소에 Header 함수를 추가합니다.logout()은 Magic을 사용하여 사용자 세션을 종료하고 UserContext에서 사용자 정보를 지우고 사용자의 평생 방문과 평생 방문 요청 상태를 초기화하며 사용자를 로그인 페이지로 다시 지정합니다.
    /* File: client/src/components/header.js */
    
    const logout = () => {
      magic.user.logout().then(() => {
        setUser({ user: null }); // Clear user's info
        setLifetimeAccess(false); // Reset user's lifetime access state
        setLifetimeAccessRequestStatus(""); // Reset status of lifetime access request
        history.push('/login');
      });
    };
    

    결제 양식 작성

    PaymentFormclient/src/components/payment-form.js의 구성요소를 구축합니다.

    나라를 세우다

    PaymentForm 구성 요소를 만들려면 먼저 지불 추적, 오류 표시, 사용자 인터페이스 관리를 위한 상태를 초기화해야 합니다.
    /* File: client/src/components/payment-form.js */
    
      const [succeeded, setSucceeded] = useState(false);
      const [error, setError] = useState(null);
      const [processing, setProcessing] = useState("");
      const [disabled, setDisabled] = useState(true);
      const [clientSecret, setClientSecret] = useState("");
    
    우리는 아직 두 개의 주가 필요하다.하나는 우리가 만든 고객을 추적하는 것이다.
    /* File: client/src/components/payment-form.js */
    
      const [customerID, setCustomerID] = useState("");
    
    다른 하나는 사용자가 결제에 성공했을 때 평생 접근 상태를 true로 설정하는 데 사용됩니다.
    /* File: client/src/components/payment-form.js */
    
      const [, setLifetimeAccess] = useContext(LifetimeContext);
    

    Stripe에 대한 참조 저장


    우리는 Stripe를 사용하여 고객의 지불을 처리하기 때문에 Stripe 라이브러리에 접근해야 합니다.Stripe의 useStripe()useElements() 갈고리를 호출하여 이를 실현했습니다.
    /* File: client/src/components/payment-form.js */
    
      const stripe = useStripe();
      const elements = useElements();
    

    월급을 받다

    PaymentForm을 로드하면 /create-payment-intent 파일의 server.js 엔드포인트에 요청합니다.이 경로를 호출하면 스트라이프 고객과 스트라이프 PaymentIntent 이 생성됩니다.PaymentIntent은 고객의 지불 주기를 추적하는 데 도움을 줄 것입니다.
    클라이언트가 반환한 data에는 clientSecret이 반환한 PaymentIntent이 포함됩니다.우리는 이것으로 지불을 완성할 것이다. 그래서 우리는 setClientSecret()으로 그것을 보존했다.data에는 PaymentIntent이 속한 고객의 ID도 포함됩니다.고객 정보를 업데이트할 때 이 ID가 필요하므로 setCustomerID()으로 저장합니다.
    /* File: client/src/components/payment-form.js */
    
      useEffect(() => {
        // Create PaymentIntent as soon as the page loads
        fetch(`${process.env.REACT_APP_SERVER_URL}/create-payment-intent`, {
          method: "POST",
          headers: {
            "Content-Type": "application/json",
          },
          body: JSON.stringify({ email }),
        })
          .then((res) => {
            return res.json();
          })
          .then((data) => {
            setClientSecret(data.clientSecret);
            setCustomerID(data.customer);
          });
      }, [email]);
    

    Stripe 고객 업데이트


    스트라이프 결제 거래가 성공하면 고객 ID를 서버의 /update-customer 엔드포인트로 전송하여 스트라이프 고객의 정보를 메타데이터로 업데이트하여 사용자가 평생 액세스 권한을 가지고 있는지 확인하도록 도와줍니다.
    이 요청이 완료되면 고객은 Premium 콘텐츠 페이지로 이동하여 콘텐츠에 집중할 수 있습니다.
    /* File: client/src/components/payment-form.js */
    
      const handleSubmit = async (ev) => {
        ev.preventDefault();
        setProcessing(true);
        const payload = await stripe.confirmCardPayment(clientSecret, {
          payment_method: {
            card: elements.getElement(CardElement),
          },
        });
    
        if (payload.error) {
          setError(`Payment failed ${payload.error.message}`);
          setProcessing(false);
        } else {
          setError(null);
          setProcessing(false);
          setSucceeded(true);
          setLifetimeAccess(true);
          // Update Stripe customer info to include metadata
          // which will help us determine whether or not they
          // are a Lifetime Access member.
          fetch(`${process.env.REACT_APP_SERVER_URL}/update-customer`, {
            method: "POST",
            headers: {
              "Content-Type": "application/json",
            },
            body: JSON.stringify({ customerID }),
          })
            .then((res) => {
              return res.json();
            })
            .then((data) => {
              console.log("Updated Stripe customer object: ", data);
              history.push("/premium-content");
            });
        }
      };
    

    CardElement 추가

    PaymentForm 구성 요소를 완료하는 마지막 작업은 Stripe에서 제공하는 CardElement 구성 요소를 추가하는 것입니다.CardElement은 카드 데이터를 수집하는 데 필요한 입력 필드를 가진 iframe를 삽입했다.이렇게 하면 카드 번호, 만료 날짜, CVC 및 우편 번호를 수집하는 단일 입력이 작성됩니다.
    /* File: client/src/components/payment-form.js */
    
            <CardElement
              id="card-element"
              options={cardStyle}
              onChange={handleChange}
            />
    

    결제 페이지 구성


    현재 우리의 PaymentForm 모듈은 이미 준비가 되어 있으며, 그것을 수용할 Payment 모듈을 구축할 때가 되었다!구성 요소는 client/src/components/payment.js에 있습니다.Payment 구성 요소에 대해 주의해야 할 두 가지 중요한 사항은 다음과 같습니다.
  • 사용자가 로그인했는지 확인하려면 user에 설정된 UserContext 상태를 사용하십시오.
  • 구성요소는 Elements, PaymentForm, promise을 도구로 사용하여 스트라이프 결제 양식을 정확하게 보여 줍니다.
  • /* File: client/src/components/payment.js */
    
    import { useContext, useEffect } from "react";
    import { useHistory } from "react-router";
    import { UserContext } from "../lib/UserContext";
    import Loading from "./loading";
    
    export default function Payment({ Elements, PaymentForm, promise }) {
      const [user] = useContext(UserContext);
      const history = useHistory();
    
      // If not loading and no user found, redirect to /login
      useEffect(() => {
        user && !user.loading && !user.issuer && history.push("/login");
      }, [user, history]);
    
      return (
        <>
          <h3 className="h3-header">
            Purchase Lifetime Access Pass to Awesomeness 🤩
          </h3>
          <p>
            Hi again {user?.loading ? <Loading /> : user?.email}! You successfully
            signed up with your email. Please enter your card information below to
            purchase your Lifetime Access Pass securely via Stripe:
          </p>
          {user?.loading ? (
            <Loading />
          ) : (
            <Elements stripe={promise}>
              <PaymentForm email={user.email} />
            </Elements>
          )}
          <style>{`
            p {
              margin-bottom: 15px;
            }
            .h3-header {
              font-size: 22px;
              margin: 25px 0;
            }
          `}</style>
        </>
      );
    }
    

    응용 프로그램에 지불 노선을 추가합니다.js


    좋습니다. PaymentFormPayment의 구성 요소가 완성됨에 따라 우리는 최종적으로 /payment을 업데이트하여 사용자 경로를 client/src/App.js페이지로 이동할 수 있습니다!
    우선, 우리는 줄무늬를 수입한다.App.js 파일에 js 및 Stripe Elements UI 라이브러리를 추가합니다.
    /* File: client/src/App.js */
    
    import { loadStripe } from  "@stripe/stripe-js";
    import { Elements } from  "@stripe/react-stripe-js";
    
    그런 다음 Stripe.js의 렌더링 외에 App.js을 로드하여 각 렌더링에 스트라이프 객체가 다시 작성되지 않도록 합니다.
    /* File: client/src/App.js */
    
    const  promise = loadStripe(process.env.REACT_APP_STRIPE_PK_KEY);
    
    주의: Build the Payment Page에서 보듯이 promisePayment 구성 요소에 전달되는 도구입니다. Elements은 하위 요소인 PaymentForm에 접근할 수 있는 권한을 제공합니다.
    다음은 /payment이라는 새로운 루트를 추가합니다. 이것은 우리가 이전에 만든 Payment 구성 요소와 테이프 결제 폼을 정확하게 보여주는 데 필요한 도구를 되돌려줍니다.
    /* File: client/src/App.js */
    
    function  App() {
    
    ...
                    <Route
                      path="/payment"
                      render={(props) => {
                        return (
                          <Payment
                            Elements={Elements}
                            PaymentForm={PaymentForm}
                            promise={promise}
                          />
                        );
                      }}
                    />
     ...               
    }
    export  default  App;
    

    다음


    현재, 당신은 등록, 지불, 로그인과 취소 페이지가 어떻게 구축되었는지, 그리고 그들이 어떻게 일을 하는지 이해했고, 지금은 노드 서버가 비용을 지불하는 회원 사이트에 어떻게 동력을 제공하는지 이해할 때입니다.계속하려면 을 클릭합니다.

    좋은 웹페이지 즐겨찾기