ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 아마존 E-commerce 클론 -18) payment Button 만들기
    NODE.JS 2021. 5. 28. 13:17

     

    먼저 샌드박스 계정을 만들고 

    sandbox로 앱을 create해준다. 

     

     

     

    이름입력

     

    우리가 페이팔을 이용하기위해서는 client Id가 필요하다. 

    .env파일에 안전하게 보관한다. 

    JWT_SECRET = somethingsecret
    
    PAYPAL_CLIENT_ID=AQ5wLOPTCxFTcWOJV43zenkyYO4C8E-e6U0E0znOHJuiysC7n4NB8QjkKJVgVC8ztpOfXupCKnISYBbc

     

     

    server.js에 클라이언트키에 접근하는 API를 만들어준다. 

    app.get('/api/config/paypal', (req, res)=>{
      res.send(process.env.PAYPAL_CLIENT_ID || 'sb')
    })
    

     

    만약 라이브로 하고싶다면 live로 앱을 생성해서 클라이언트 아이디 넣어주면 된다. 

     

    이제 orderscreen.js로 가보자. 

    다음고 같이 추가하고 useEffect는 이처럼 변경해준다. 

        
        const orderId = props.match.params.id;
        const [sdkReady, setsdkReady] = useState(false)
    
        useEffect(() => {
            const addPayPalScript = async () =>{
                const {data} = await axios.get('/api/config/paypal');
                const script = document.createElement('script');
                script.type = "text/javascript";
                script.src = `http://www.paypal.com/sdk/js?client-id=${data}`
                script.async = true;
                script.onload = ()=>{
                    setsdkReady(true);
                };
                document.body.appendChild(script);
            }
            if(!order._id){
                dispatch(detailsOrder(orderId));
            }else{
                if(!order.isPaid){
                    if(!window.paypal){
                        addPayPalScript();
                    }
                }else{
                    setsdkReady(true);
                }
            }
        }, [dispatch, order, sdkReady, orderId])
    

     

     

    이제 frontend에 페이팔 버튼을 설치한다. 

    npm install react-paypal-button-v2

    이제 오더스크린에 버튼을 추가한다. 

    import {PaypalButton} from 'react-paypal-button-v2';
        const successPaymentHandler = () =>{
    
        }
    
    
                                <li>
                                    <div className="row">
                                        <div>
                                            <strong>Order Total</strong>
                                        </div>
                                        <div>
                                            <strong>${order.totalPrice.toFixed(2)}</strong>
                                        </div>
                                    </div>
                                </li>
                                {
                                    !order.isPaid &&(
                                        <li>
                                            {!sdkReady? (<LoadingBox></LoadingBox>):
                                            (
                                                <PaypalButton amount={order.totalPrice}  onSuccess = {successPaymentHandler}></PaypalButton>
                                            )}
                                        </li>
                                    )
                                }
                            
                            </ul>

     

    여기서 orderDetailsReducer를 다음과 같이 order={}를제거해준다. 

    이는 우리가 디테일을 가져오기 전에 빈 객체로 만들어주면

    또다시 orderscreen의 addpaypalscript의 useEffect가 작동하는데 이는 우리가 원한것이 아니기 때문이다. 

    export const orderDetailsReducer = (state = {loading:true}, action) =>{
        switch(action.type){
            case ORDER_DETAILS_REQUEST:
                return {loading:true}
            case ORDER_DETAILS_SUCCESS:
                return {loading:false, order:action.payload}
            case ORDER_DETAILS_FAIL:
                return {loading:false, error:action.payload}
            default:
                return state;
        }
    }

     

     

    만약 라이브 계정이라면 결제가 진행될 것이다. 

    이제 주문이 진행되면 페이먼트가 완료된 창이 뜨도록 설정해두자. 

     

     

     

    order모델에 상세 정보를 추가하자. 

        paymentMethod:{
            type:String,
            required:true
        },
        paymentResult:{
            id:String,
            Status:String,
            update_time:String,
            email_address:String
        },
        item

    라우터를 추가해준다. 

    orderRouter.put('/:id/pay', isAuth, expressAsyncHandler(async (req, res)=>{
        const order = await Order.findById(req.params.id);
        if(order){
            order.isPaid = true,
            order.paidAt = Date.now();
            order.paymentResult = {id: req.body.id, status:req.body.status, update_time:req.body.update_time, email:req.body.email_address}
            const updatedOrder = await order.save();
            res.send({message:'Order Paid', order: updatedOrder})
        }else{
            res.status(404).send({message:'Order Not Found'})
        }
    }))

     

     

    orderconstants추가한다. 

    export const ORDER_PAY_REQUEST='ORDER_PAY_REQUEST'
    export const ORDER_PAY_SUCCESS='ORDER_PAY_SUCCESS'
    export const ORDER_PAY_FAIL='ORDER_PAY_FAIL'
    export const ORDER_PAY_RESET= 'ORDER_PAY_RESET'

     

    orderActions.js

    export const payOrder = (order, paymentResult)=>(dispatch, getState)=>{
        dispatch({
            type:ORDER_PAY_REQUEST, 
            payload:{order, paymentResult}
        })
        const {userSignin:{userInfo}} = getState();
        try{
            const {data} = axios.put(`/api/orders/${order._id}/pay`, paymentResult,{
                headers:{Authorization:`Bearer ${userInfo.token}`}
            })
            dispatch({
                type:ORDER_PAY_SUCCESS ,
                payload:data
            })
        }catch(error){
            const message = error.response && error.response.data.message
            ? error.response.data.message
            : error.message;
            dispatch({
                type:ORDER_PAY_FAIL,
                payload:message
            })
        }
    }

    orderReducers.js

     

    export const orderPayReducer = (state={}, action)=>{
        switch(action.type){
            case ORDER_PAY_REQUEST:
                return {loading:true}
            case ORDER_PAY_SUCCESS:
                return {loading:false, success:true};
            case ORDER_PAY_FAIL:
                return {loading:false, error:action.payload}
            default:
                return state;
        }
    }

     

    store.js

    const reducer = combineReducers({
        productList: productListReducer,
        productDetails : productDetailsReducer,
        cart:cartReducer,
        userSignin: userSigninReducer,
        userRegister: userRegisterReducer,
        orderCreate:orderCreateReducer,
        orderDetails:orderDetailsReducer,
        orderPay: orderPayReducer
        
    
    })

     

     

     

    orderScreen.js

    만약에 주문이 존재하지 않거나, 이미 지불했거나, 주문이 존재하지만 주문아이디가 동일하지 않으면 주문상세페이지로 이동한다. 

     

    import { detailsOrder, payOrder } from '../actions/orderActions';
    import axios from 'axios';
    import {PayPalButton} from 'react-paypal-button-v2';
    
        const orderPay = useSelector(state=>state.orderPay)
        const {loading:loadingPay, error:errorPay, success:successPay} = orderPay;
    
    
    
        useEffect(() => {
            const addPayPalScript = async () => {
              const { data } = await axios.get('/api/config/paypal');
              const script = document.createElement('script');
              script.type = 'text/javascript';
              script.src = `https://www.paypal.com/sdk/js?client-id=${data}`;
              script.async = true;
              script.onload = () => {
                setSdkReady(true);
              };
              document.body.appendChild(script);
            };
            if (
              !order ||
              successPay ||
              (order && order._id !== orderId)
            ) {
              dispatch(detailsOrder(orderId));
            } else {
              if (!order.isPaid) {
                if (!window.paypal) {
                  addPayPalScript();
                } else {
                  setSdkReady(true);
                }
              }
            }
          }, [dispatch, orderId, sdkReady, successPay, order]);
        
        const successPaymentHandler = (paymentResult) =>{
            dispatch(payOrder(order, paymentResult))
        }
    
    
    
    
                                {
                                    !order.isPaid && (
                                        <li>
                                            {!sdkReady? (<LoadingBox></LoadingBox>):
                                            (
                                                <>
                                                {
                                                    errorPay && <MessageBox variant='danger'>{errorPay}</MessageBox>
                                                }
                                                {
                                                    loadingPay && <LoadingBox></LoadingBox>
                                                }
                                                <PayPalButton amount={order.totalPrice}  onSuccess = {successPaymentHandler}></PayPalButton>
                                                </>
                                            )}
                                        </li>
                                    )
                                }

     

     

    스타일을 주자. 

    .alert-success{
      color:#20a020;
      background-color: #ffe0e0e0;
    }

     

    추가로결제가 완료되면 페이지가 자꾸 새로고침 되는 것을 고치자. 

    reducers.에 추가한다. 

            case ORDER_PAY_RESET:
                return {};
            default:
                return state;
        }

     

    orderscreen에 추가

            if (
              !order ||
              successPay ||
              (order && order._id !== orderId)
            ) {
              dispatch({
                  type:ORDER_PAY_RESET
              })  
              dispatch(detailsOrder(orderId));
            } else {
              if (!order.isPaid) {
                if (!window.paypal) {
                  addPayPalScript();
                } else {
                  setSdkReady(true);
                }
              }

     

     

    --------------

    여기 페이팔 테스트가 실행이안된다. ㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠ

     

     

     

Designed by Tistory.