Magic and Stripe: Pt로 유료 회원 사이트를 만듭니다.2-반응 클라이언트
                                            
                                                
                                                
                                                
                                                
                                                
                                                 52392 단어  passwordlessmagicauthstripe
                    
손님: 네.
유료 회원 사이트 클라이언트를 구축하는 데 필요한 주요 절차를 살펴보고 깊이 있게 살펴보겠습니다.
표준 인증 설정
로그인한 사용자 추적
우리는 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-renderings이 useEffect 내부의 /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');
  });
};
결제 양식 작성
PaymentForm에 client/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
좋습니다.
PaymentForm과 Payment의 구성 요소가 완성됨에 따라 우리는 최종적으로 /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에서 보듯이 promise은 Payment 구성 요소에 전달되는 도구입니다. 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;
다음
현재, 당신은 등록, 지불, 로그인과 취소 페이지가 어떻게 구축되었는지, 그리고 그들이 어떻게 일을 하는지 이해했고, 지금은 노드 서버가 비용을 지불하는 회원 사이트에 어떻게 동력을 제공하는지 이해할 때입니다.계속하려면 을 클릭합니다.
                
                    
        
    
    
    
    
    
                
                
                
                
                    
                        
                            
                            
                            Reference
                            
                            이 문제에 관하여(Magic and Stripe: Pt로 유료 회원 사이트를 만듭니다.2-반응 클라이언트), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다
                                
                                https://dev.to/magiclabs/build-a-paid-membership-site-with-magic-and-stripe-pt-2-react-client-3j3m
                            
                            
                            
                                텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
                            
                            
                                
                                
                                
                                
                                
                                우수한 개발자 콘텐츠 발견에 전념
                                (Collection and Share based on the CC Protocol.)
                            
                            
                        
                    
                
                
                
            
Reference
이 문제에 관하여(Magic and Stripe: Pt로 유료 회원 사이트를 만듭니다.2-반응 클라이언트), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/magiclabs/build-a-paid-membership-site-with-magic-and-stripe-pt-2-react-client-3j3m텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
                                
                                
                                
                                
                                
                                우수한 개발자 콘텐츠 발견에 전념
                                (Collection and Share based on the CC Protocol.)