react-shop-web/front & back
Paypal로 결제 성공 후 할 일들
tjdvyzl
2023. 2. 22. 01:53
- 카트를 비우기
- 결제 정보 저장하기
- Payment Collection (Detailed)
- User Collection (Simple)
더보기
import React, {useEffect, useState} from 'react'
import {useDispatch} from 'react-redux';
import {getCartItems, removeCartItem, onSuccessBuy} from '../../../_actions/user_actions';
import UserCardBlock from './Sections/UserCardBlock';
import {Empty, Result} from 'antd'
import Paypal from '../../utils/Paypal';
function CartPage(props) {
const dispatch = useDispatch();
const [Total, setTotal] = useState(0);
const [ShowTotal, setShowTotal] = useState(false)
const [showSuccess, setshowSuccess] = useState(false)
useEffect(() => {
let cartItems = []
// 리덕스 user state에서 cart안에 상품이 들어있는지 확인
if(props.user.userData && props.user.userData.cart) {
// 카트 안에 상품이 하나 이상 들어있다면
if(props.user.userData.cart.length > 0){
props.user.userData.cart.forEach(item => {
cartItems.push(item.id)
})
// 밑에 액션에 매개변수로 방금 props로부터 받은 user의 userData에서 cart들을 담은 변수와,
// userData의 cart 정보도 주자. (이때 두번째 매개변수를 넣어주는 이유는 quantity는 user의 cart 부분에만 존재하기 때문)
dispatch(getCartItems(cartItems, props.user.userData.cart))
.then(response => {
calculateTotal(response.payload)
})
}
}
return () => {
}
}, [props.user.userData])
const calculateTotal = (cartDetail) => {
let total = 0;
cartDetail.map(item => {
total += parseInt(item.price) * item.quantity
})
setTotal(total);
setShowTotal(true);
}
let removeFromCart = (productId) => {
dispatch(removeCartItem(productId))
.then(response => {
if(response.payload.productInfo.length <= 0){
setShowTotal(false);
}
else {
calculateTotal(response.payload.productInfo)
}
})
}
// 매개 변수 값으로 받은 data는 Paypal.js에서 onSuccess 함수의 payment값이다.
const transactionSuccess = (data) => {
dispatch(onSuccessBuy({
paymentData: data,
cartDetail: props.user.cartDetail
}))
.then(response => {
if(response.payload.success) {
setShowTotal(false)
setshowSuccess(true)
}
})
}
return (
<div style={{ width: '85%', margin: '3rem auto'}}>
<h1>My Cart</h1>
<div>
<UserCardBlock products={props.user.cartDetail && props.user.cartDetail} removeItem={removeFromCart}/>
</div>
{ShowTotal ?
<div style={{marginTop: '3rem'}}>
<h2>Total Amount: ${Total}</h2>
<Paypal
total={Total}
onSuccess={transactionSuccess}
/>
</div>
:
showSuccess ?
<Result
status="success"
title="Successfully Purchased Items"
/>
:
<>
<br/>
<Empty description={false}/>
</>
}
</div>
)
}
export default CartPage
CartPage.js
export function onSuccessBuy(data){
const request = axios.post(`/api/users/successBuy`, data)
.then(response => {response => response.data});
return {
type: ON_SUCCESS_BUY,
payload: request
}
}
user_actions.js
case ON_SUCCESS_BUY:
return {...state, cartDetail: action.payload.cartDetail,
userData: {
...state.userData, cart: action.payload.cart
}
}
user_reducer.js
더보기
router.post("/successBuy", auth, (req,res) => {
// 1. User Collection 안에 history 필드 안에 간단한 결제 정보 넣어주기
// ㄴ dateOfPurchase, name, id, price, quantity, paymentId
let history = [];
let transactionData = {};
console.log(req.body);
req.body.cartDetail.forEach(item => {
history.push({
dateOfPurchase: Date.now(),
name: item.title,
id: item._id,
price: item.price,
quantity: item.quantity,
// paymentID는 Paypal.js에서 onSuccess 함수의 매개변수 값에 들어있다.
paymentId: req.body.paymentData.paymentID
})
})
// 2. Payment collection 안에 자세한 결제 정보 넣어주기
transactionData.user = {
// 해당 데이터들은 auth 미들웨어를 통해 얻은 정보를 넣어주는거임
id: req.user._id,
name: req.user.name,
email: req.user.email
}
transactionData.data = req.body.paymentData
transactionData.product = history
// history 정보 저장
User.findOneAndUpdate(
{_id: req.user._id},
// 결제가 완료되면 카트에 있던 것들을 지워줘야함 그래서 set 문법으로 카트를 지워주자
{$push: {history: history}, $set: {cart: []}},
// userCollection에 새롭게 업데이트된 doc를 받을 수 있게 밑의 코드도 넣어주자
{new: true},
(err, user) => {
if(err) return res.json({ success: false, err})
// payment에다가 transaction 정보 저장
const payment = new Payment(transactionData)
payment.save((err, doc) => {
if(err) return res.json({ success: false, err})
// 3. Product Collection 안에 있는 sold 필드 정보 업데이트 시켜주기
// 상품 당 몇 개의 quantity를 샀는지
let products = [];
doc.product.forEach(item => {
products.push({ id: item.id, quantity: item.quantity})
})
async.eachSeries(products, (item, callback) => {
Product.update(
{_id: item.id},
{
$inc: {
"sold" : item.quantity
}
},
// 원래라면 업데이트된 doc을 frontEnd에다가 보내줘야하기 때문에 {new: true}를 넣어줬었다.
// 이번에는 굳이 front에 보내주지 않아도 되므로 {new: false}를 넣어주자.
{new: false},
callback
)
}, (err) => {
if(err) return res.status(400).json({ success: false, err})
// 결제가 성공했다면 카트를 다 비워줬기 때문에 cartDetail은 비어있을 것이다.
res.status(200).json({ success: true, cart: user.cart, cartDetail: []})
})
})
}
)
})
server/routes/users.js
위 과정은 다음과 같다.
- Paypal 컴포넌트에서 결제가 성공했다면 부모 컴포넌트(CartPage.js)로 부터 받은 onSuccess함수를 통해 매개변수로 결제에 성공하면서 얻은 payment값과 함께 넣어주면서 부모 컴포넌트의 transactionSuccess 함수를 호출한다.
- CartPage.js에서 호출된 transactionSuccess 함수는 onSuccessBuy액션을 dispatch을 이용해서 reducer에 전달한다.
- onSuccessBuy 액션에서는 '/api/users/successBut' api에 post로 데이터를 요청한다. 액션에서 서버에 요청할 때 준 data값에는 payment값과 cartDetail이 들어있다.
- 서버의 '/successBuy' 라우트에서 해줄 일은 두 가지이다.
- User Collection 에 있는 history 필드 안에 간단한 결제 정보 넣어주기(dateOfPurchase, name, id, price, quantity, paymentId)
- Payment collection 안에 자세한 결제 정보 넣어주기
- 전 게시글에서도 말했었듯이 Payment Model에는 user, data, product 필드가 존재한다. 위 코드에서 transactionData 객체를 만들어서 넣어주는 값들이 세 가지에 해당한다. history배열과 transactionData객체를 Payment DB에 새로 저장해준다.
위 코드에서 aynsc.eachSeries라는 문법을 사용했는데 이는 for문으로 일일이 조건 따지면서 DB를 업데이트 하지 않고 eachSeries 함수를 이용하여 보다 더 간결하게 코드를 짜기위해 사용했다.
root 디렉토리에서 다운받아주자.
npm install async --save
https://www.npmjs.com/package/async
ant design에서 Result를 갖고와서 결제가 성공했다면 띄워줄 수 있는 문구를 넣어줬다.
https://ant.design/components/result