728x90
반응형
setState({
      ...state,
      [e.target.id]: { ...state[e.target.id], [e.target.name]: e.target.value },
    });

위와 같은 방식으로 사용하고자 할 때, typescript에서 에러가 발생할 경우가 있다.

그럴 땐아래 코드를 인터페이스에 추가해주자.

 

interface abc  {
    [prop: string]: any;
}
728x90
반응형
728x90
반응형

Grid container 과 같은 mui에서 자체적으로 사용하는 props를 styled로 사용하는 방법은 다음과 같다.

이 방법만 알아도 왠만한 css는 정리가 가능할 것이다.

const GridContainer = styled((props) => <Grid container {...props} />) <any>`
    width: ${(props) => props.width || `100%`};
    height: ${(props) => props.height};
`

 

- 예시 전체 코드

import { Grid } from '@mui/material'
import { GetServerSideProps, NextPage } from 'next'
import { styled } from '@mui/material/styles'

const GridContainer = styled((props) => <Grid container {...props} />) <any>`
    width: ${(props) => props.width || `100%`};
    height: ${(props) => props.height};
`

const GridItem = styled((props) => <Grid item {...props} />) <any>`
    width: ${(props) => props.width || `100%`};
    height: ${(props) => props.height};
`

const Login: NextPage = () => {
    return (
        <>
            <GridContainer height={"300px"} >
            </GridContainer>
            <GridContainer height={"300px"} >
                <GridItem xs={12}>
                    <GridContainer
                        direction={"row"}
                    >
                        <GridItem xs={4}>
                            a
                        </GridItem>
                        <GridItem xs={4}>
                            b
                        </GridItem>
                        <GridItem xs={4}>
                            c
                        </GridItem>
                    </GridContainer>
                </GridItem>
            </GridContainer>
        </>
    )
}

export default Login;


export const getServerSideProps: GetServerSideProps = async (context) => {

    return {
        props: {
        },
    };
}

 

참고로 모듈로 styled 파일을 따로 빼둘 때에는 tsx 확장자로 해야한다.

728x90
반응형
728x90
반응형

.babelrc 에 { "runtime" : "automatic" } 추가

 - 전체코드 

{
    "presets": [
        "next/babel",
        [
            "@babel/preset-env",
            {
                "targets": {
                    "chrome": "91",
                    "firefox": "89"
                }
            }
        ],
        [
            "@babel/preset-react",
            {
                "runtime": "automatic"
            }
        ]
    ],
    "plugins": [
        "@emotion"
    ]
}
728x90
반응형
728x90
반응형

레거시 코드를 빌드하는데 에러가 떠버렸다.

누가 짰는진 모르겠지만 이 코드는 답이 없었다...

 

일단 오늘 빌드를 무조건 해야하는 상황이라 확실한 방법은 나중에 찾아봐야겠다.

다음 주부터 처음부터 다시 만들어야겠다.

module.exports = {
    webpack: (config, { isServer }) => {
        if (!isServer) {
             config.resolve.fallback.fs = false
             config.resolve.fallback.dns = false
             config.resolve.fallback.net = false
        }

        return config;
    }
}
728x90
반응형
728x90
반응형
const beforeMinute = (date: any) => {
    const today = new Date();
    const timeValue = new Date(date);

    const betweenTime = Math.floor((today.getTime() - timeValue.getTime()) / 1000 / 60);
    if (betweenTime < 1) return '방금전';
    if (betweenTime < 60) {
        return `${betweenTime}분전`;
    }

    const betweenTimeHour = Math.floor(betweenTime / 60);
    if (betweenTimeHour < 24) {
        return `${betweenTimeHour}시간전`;
    }

    const betweenTimeDay = Math.floor(betweenTime / 60 / 24);
    if (betweenTimeDay < 365) {
        return `${betweenTimeDay}일전`;
    }

    return date;
}

 

const stringEllipsis = (str: string) => {
    let length = 30; // 표시할 글자수 기준
    if (str.length > length) {
        str = str.substring(0, length - 2) + '...';
    }
    return str;
}
728x90
반응형
728x90
반응형

로그인

1. 카카오 디벨로퍼 로그인

https://developers.kakao.com/

 

Kakao Developers

카카오 API를 활용하여 다양한 어플리케이션을 개발해보세요. 카카오 로그인, 메시지 보내기, 친구 API, 인공지능 API 등을 제공합니다.

developers.kakao.com

 

2. 애플리케이션 추가

 

3. 플랫폼 도메인 설정 후 등록하러가기 버튼 클릭

4. 로그인 활성화 및 Redirect URI 설정

5. 동의 항목 설정

 

6. _app.tsx 파일에  script 추가

_app.tsx

const MyApp: NextPage<AppProps> = ({ Component, pageProps }: AppProps) => {


  return (
    <React.StrictMode>
      <Head>
        <title></title>
        <meta name="viewport" content="initial-scale=1.0, width=device-width" />

        <script src="https://developers.kakao.com/sdk/js/kakao.js"
          defer
        >
        </script>


      </Head >
      <ThemeProvider theme={Theme}>
          <Component {...pageProps} />
      </ThemeProvider>


    </React.StrictMode>
  );
};

 

7. 쿠키 생성 라우터 ( Back-end )

community.js

var express = require('express');
var router = express.Router();

const jwt = require("jsonwebtoken");

router.post("/login", async function (req, res, next) {

    const body = req.body;

    const tokenValue = {
        email: body.email
    }

    const token = jwt.sign({ isLogined: tokenValue }, "secretkey", {
        expiresIn: 86400000, // 토큰 제한시간
    });

    res.cookie("A12Ba5os9", token, {
        maxAge: 86400000, // 쿠키 제한시간
        path: "/",
        httpOnly: true,
    });

    return res.send({ success: true })
})

module.exports = router;

 

8.  컴포넌트에 구현

import { GetServerSideProps, NextPage } from "next";
import { useRouter } from "next/router";

const Community: NextPage<any> = (props: any) => {
    const router = useRouter();

    const kakaoInit = () => {
        const kakao = (window as any).Kakao;
        if (!kakao.isInitialized()) {
            kakao.init(process.env.NEXT_PUBLIC_KAKAO_SHARE_KEY);
        }

        return kakao;
    }

    const kakaoLogin = async () => {
        // 카카오 초기화
        const kakao = kakaoInit();

        // 카카오 로그인 구현
        kakao.Auth.login({
            success: () => {
                kakao.API.request({
                    url: '/v2/user/me', // 사용자 정보 가져오기
                    success: (res: any) => {
                        // 로그인 성공할 경우 
                        if (!res.kakao_account.email || res.kakao_account.email === "") {
                            alert("해당 계정의 이메일이 존재하지 않습니다.")
                        }
                        else {
                        	// 쿠키 생성
                            await axios.post("/api/community/login", { email: res.kakao_account.email })
                                .then(({ data }) => {
                                    if (data.success) {
                                        dispatch({ name: "email", value: res.kakao_account.email })
                                    }
                                    else {
                                        return alert(" 로그인에 실패하였습니다.")
                                    }
                                })
                        }
                        
                    },
                    fail: (error: any) => {
                        console.log(error);
                    }
                })
            },
            fail: (error: any) => {
                console.log(error);
            }
        })
    }

    return (
        <>
            <button onClick={(e: any) => { kakaoLogin(); }}>
                카톡으로 로그인
            </button>
        </>
    )
}

export default Community;

export const getServerSideProps: GetServerSideProps = async (context) => {
    return {
        props: {

        }
    }
}

/api 로 시작하는 라우터는 next.config.js 로 백엔드 프록시 설정을 해서 그렇습니다.

9. 확인하기


로그아웃

카카오 로그아웃은 이 사이트 뿐만 아니라 카카오 소셜 계정 자체를 로그아웃 시켜야 하는데,

https://developers.kakao.com/docs/latest/ko/kakaologin/rest-api#logout-of-service-and-kakaoaccount

 

Kakao Developers

카카오 API를 활용하여 다양한 어플리케이션을 개발해보세요. 카카오 로그인, 메시지 보내기, 친구 API, 인공지능 API 등을 제공합니다.

developers.kakao.com

 

카카오에서 제공하는 api를 사용해야 한다.

 

1. Logout Redirect URI 설정

 

2. 쿠키 삭제 라우터 ( Back-end )

var express = require('express');
var router = express.Router();

const jwt = require("jsonwebtoken");

router.post("/login", async function (req, res, next) {

    const body = req.body;

    const tokenValue = {
        email: body.email
    }

    const token = jwt.sign({ isLogined: tokenValue }, "secretkey", {
        expiresIn: 86400000, // 토큰 제한시간
    });

    res.cookie("A12Ba5os9", token, {
        maxAge: 86400000, // 쿠키 제한시간
        path: "/",
        httpOnly: true,
    });

    return res.send({ success: true })
})

router.post("/logout", async function (req, res, next) {

    res.clearCookie("A12Ba5os9");

    return res.send({ success: true })
})


module.exports = router;

 

3. 컴포넌트 구현

import { GetServerSideProps, NextPage } from "next";
import { useRouter } from "next/router";

const Community: NextPage<any> = (props: any) => {
    const router = useRouter();

    const kakaoInit = () => {
        const kakao = (window as any).Kakao;
        if (!kakao.isInitialized()) {
            kakao.init(process.env.NEXT_PUBLIC_KAKAO_SHARE_KEY);
        }

        return kakao;
    }

    const kakaoLogin = async () => {
        // 카카오 초기화
        const kakao = kakaoInit();

        // 카카오 로그인 구현
        kakao.Auth.login({
            success: () => {
                kakao.API.request({
                    url: '/v2/user/me', // 사용자 정보 가져오기
                    success: (res: any) => {
                        // 로그인 성공할 경우 
                        if (!res.kakao_account.email || res.kakao_account.email === "") {
                            alert("해당 계정의 이메일이 존재하지 않습니다.")
                        }
                        else {
                        	// 쿠키 생성
                            await axios.post("/api/community/login", { email: res.kakao_account.email })
                                .then(({ data }) => {
                                    if (data.success) {
                                        dispatch({ name: "email", value: res.kakao_account.email })
                                    }
                                    else {
                                        return alert(" 로그인에 실패하였습니다.")
                                    }
                                })
                        }
                    },
                    fail: (error: any) => {
                        console.log(error);
                    }
                })
            },
            fail: (error: any) => {
                console.log(error);
            }
        })
    }
    
    const kakaoLogout = async () => {
        // 쿠키 제거
        await axios.post("/api/community/logout", state)
            .then(({ data }) => {
                if (data.success) {
                    dispatch({ type: "init" })
                    // 카카오 로그아웃 페이지
                    router.push(`https://kauth.kakao.com/oauth/logout?client_id=${process.env.NEXT_PUBLIC_KAKAO_SHARE_KEY}&logout_redirect_uri=http://localhost:3001`);
                }
                else {
                    return alert(" 로그아웃에 실패하였습니다.")
                }
            })
    }

    return (
        <>
            <button onClick={(e: any) => { kakaoLogin(); }}>
                카톡으로 로그인
            </button>
            <button onClick={(e: any) => { kakaoLogout(); }}>
                로그아웃
            </button>
        </>
    )
}

export default Community;

export const getServerSideProps: GetServerSideProps = async (context) => {
    return {
        props: {

        }
    }
}

로그인을 했을 경우 SSR로 props를 넘기면 쿠키 데이터를 읽어 로그인 된 상태를 구현할 수 있다. 

( 로그인 or 로그아웃 버튼 보이게끔 하는거 구현 가능 )

4. 로그아웃 

  • 이 서비스만 로그아웃 : state 가 초기화 되었고, 쿠키가 삭제되었으므로 사이트는 로그아웃 됨. ( 하지만 로그인할 때 전에 했던 방식으로 로그인 됨 )
  • 카카오계정과 함께 로그아웃 : 소셜 계정이 로그아웃 되어 다른 계정으로 로그인 가능

 

728x90
반응형
728x90
반응형
    const [windowWidth, setWindowWidth] = useState(0);

    const resizeWindow = () => {
        setWindowWidth(window.innerWidth)
    }

    useEffect(() => {
        setWindowWidth(window.innerWidth)
        window.addEventListener("resize", resizeWindow)
        return () => {
            window.removeEventListener("resize", resizeWindow)
        }
    }, [windowWidth])

 

상태를 만들고 window 사이즈를 측정해서 쉽게 만들 수 있다.

728x90
반응형
728x90
반응형

kcp 를 쓰고 m_redirect_url을 지정해주면 모바일 웹앱 환경에서는 빌링키를 받을 때 콜백 함수가 실행이 되질 않는다..

하지만 이 방법을 쓰면 어떤 환경이든 다 가능하게끔 만들었다!

검색을 해보니 내가 redirect url 로 지정한 주소에 ?imp_uid="" 이런식으로 파라미터로 넘겨준다고 한다. 바로 방법이 떠올랐다.

  1. Next.js 에서 쓸 수있는 SSR 로 해당 파라미터를 읽는다.
  2. useEffect로 파라미터가 넘겨졌을때의 로직을 구성해준다.( 파라미터가 없을 경우엔 실행이 안되게끔)
  3. 파라미터로 넘겨졌을때의 라우터는 이전에 써왔던 것들과 동일하다.

1. 페이지들의 라우팅을 관리해주는 파일의 getServerSideProps 에서 파라미터를 읽는 부분을 추가해주자.

export const getServerSideProps: GetServerSideProps = async (context) => {
  const imp_uid = context.query.imp_uid ? context.query.imp_uid : "";
  const merchant_uid = context.query.merchant_uid ? context.query.merchant_uid : "";
  
  ...

이런식으로 imp_ud와 merchant_uid를 받고 구독을 신청하는 페이지까지 전달해준다. ( 컴포넌트끼리의 props 전달은 블로그에 있는 다른 글을 참조 바랍니다. )

 

abc.com/view/category?imp_uid="1234" 이런식으로 넘어오면 props에 "1234"라는 데이터가 들어올 것이고 아니면 "" 빈 스트링으로 올 것이다. 이걸 활용해보자.

 

2. useEffect 로 파라미터를 감지해서 빈 스트링이 아닐 때 이전에 콜백함수에 담겨있던 로직을 실행해준다.

interface LoginData {
    loginData: any
}

const Subscribe: NextPage<LoginData> = (props: any) => {

    useEffect(() => {
        if (props.imp_uid !== "" && props.merchant_uid !== "") {
            var merchant_uid_date = moment().format('YYYY-MM-DD_HH:mm:ss');
            axios.post('/api/subscribe/payments', {
                muser_id: loginData.userId, // 로그인 아이디
                radioItem: radioItem, // 구독 개월 수 라디오 버튼
                merchant_uid: "subscribe_" + merchant_uid_date,
                customer_uid: loginData.userCompanyCode + "_" + loginData.userId,
                name: "기본정기결제",
                amount: 500
            }).then(({ data }) => {
                console.log("data : ", data)
                if (data.success) {
                    // 성공할 경우
                    alert('구독신청이 완료되었습니다. 다시 로그인해주세요.')
                    router.push('/');
                }
                else {
                    // 실패할 경우
                    alert('내용을 다시확인해주세요.')
                }
            })
        }
    }, [props])
    
    ...

 

이제 모바일로 접속했을 때 빌링키를 받고 redirect 되고, 파라미터를 받아 useEffect 로 결제 신청을 하는 것까지 완료되었다.

아임포트 웹훅으로 정기결제 예약까지 완성되었다.

아임포트의 관리자 콘솔에서 정기결제예약 스케줄을 확인할 수 있다. 

 


참고로 m_redirect_url에 먼저 내가 원하는 파라미터를 넣어도 좋다. 페이지가 리렌더링 되기 때문에 기존의 상태값들은 초깃값으로 넘어가기 때문에 원하는 값도 같이 전달하고 싶으면 아래와 같은 방법으로 해주면 좋다.

m_redirect_url: 'abc.com/view/subscribe?subscribe_item=' + subscribeItem

 

728x90
반응형
728x90
반응형

1. 버튼과 버튼 클릭 이벤트 함수를 만들어준다.

    const unSubscribeClickHandler = (e: any) => {
        if (window.confirm('구독을 취소하시겠습니까?')) {
            axios.post('/api/subscribe/unsubscribe', {
                muser_id: loginData.userId
            }).then(({ data }) => {
                if (data.success) {
                    alert('구독취소가 완료되었습니다. 다시 로그인해주세요.')
                    router.push('/')
                }
                else {
                    alert('구독취소 오류입니다. 관리자에게 문의 바랍니다.')
                }
            })
        }
        else {

        }
    }

 

2. 백엔드에 구독을 취소하는 라우터를 만들어준다. 

router.post("/unsubscribe", async function (req, res, next) {
    try {
        Subscribe.findOne({
            where: {
                subscribe_muser_id: req.body.muser_id,
                subscribe_type: "기본정기결제예약"
            },
            order: [['subscribe_createdAt', 'DESC']],
        }).then(async (result) => {

            if (result) {
                // 인증 토큰 발급 받기
                const getToken = await axios({
                    url: "https://api.iamport.kr/users/getToken",
                    method: "post", // POST method
                    headers: { "Content-Type": "application/json" }, // "Content-Type": "application/json"
                    data: {
                        imp_key: process.env.IMP_API_KEY, // REST API 키
                        imp_secret: process.env.IMP_API_SECRET_KEY // REST API Secret
                    }
                });
                const { access_token } = getToken.data.response; // 인증 토큰

                // 정기결제 예약 취소
                const paymentResult = await axios({
                    url: 'https://api.iamport.kr/subscribe/payments/unschedule',
                    method: "post",
                    headers: { "Authorization": access_token }, // 인증 토큰을 Authorization header에 추가
                    data: {
                        customer_uid: result.subscribe_customer_uid,
                        merchant_uid: result.subscribe_merchant_uid, // 새로 생성한 결제(재결제)용 주문 번호
                    }
                });

                MUserInfo.update({
                    muser_subscribe_type: ""
                }, {
                    where: {
                        muser_id: result.subscribe_muser_id
                    }
                })

                res.clearCookie('')
                res.send({ success: true })
            }
            else {
                res.send({ success: false })
            }
        })
    }
    catch (Err) {
        console.log("Err : ", Err)
        res.send({ success: false })
    }
})

module.exports = router;
728x90
반응형
728x90
반응형

아임포트 웹훅-

https://docs.iamport.kr/tech/webhook

 

[가이드] Webhook

아임포트 Webhook 이 문서는 아임포트 webhook을 사용하여 아임포트 서버에 저장된 결제 정보를 가맹점 서버에 동기화하고 네트워크 불안정성을 보완하는 방법을 설명합니다. Webhook이란? Webhook(웹훅

docs.iamport.kr


아임포트에서 제공하는 웹훅으로 결제가 완료된 후 다음 결제를 예약할 수 있다.

데이터베이스에 저장된 구독 남은 개월 수를 측정해서 카운팅한 후

남은 개월 수가 있을 경우 결제완료 후 다음 달로 결제 예약을 거는 시스템을 구현해보도록 하자.

 

1. 아임포트 관리자 콘솔에서 웹훅에 대한 설정을 해준다.

웹훅 발송 공통 URL을 내가 백엔드에서 요청받을 라우터로 지정해주면 결제가 완료될 때 원하는 행동을 할 수 있다.

 

2. 백엔드를 만들고 위에 이미지의 호출 테스트를 눌러본다.

router.post("/repayments", async function (req, res, next) {
    const body = req.body
    console.log("body : ", body)
    try {
        res.send({ success: true })
    }
    catch (Err) {
        console.log("err : ", Err)
        res.send({ success: false })
    }
});

 

- 호출테스트 눌렀을 때 정상적인 경우

 

3. 라우터에 로직을 만든다. 

결제가 잘되거나 예약결제가 시도되었을 땐 paid, 예약결제가 실패했을 땐 failed 결과값이 온다.

 

  1. 구독 테이블을 조회해서 남은 개월 수를 측정한다.
  2. 남은 개월 수가 0보다 클 경우 토큰을 발급받고 imp_uid 또는 merchant_uid로 결제 정보를 조회하고 잘 되었는지 확인한다.
  3.  예약결제가 정상적으로 이루어진 상태면 카운팅을 해주고 merchant_uid를 새로운 예약결제에 맞게 수정하며 다음 달에 예약을 걸고 쿠키를 수정한다.( 로그인 정보에 담아줘서 나중에 화면에 달리보이게 하기 위함)
  4. 만약 잔액 부족 등으로 결제가 안 된 상태면 로그인정보를 수정해서 구독 시 사용 가능한 서비스를 불가능하게 하고 다음날을 예약일로 요청한다. (구독에 관한 데이터는 남아있음).
  5. 남은 개월 수가 0일 경우 예약을 걸지 않고 구독해지를 해준다 로그인 정보를 바꾸고 해당 컬럼을 삭제한다.
router.post("/repayments", async function (req, res, next) {
    const body = req.body;
    const { imp_uid, merchant_uid } = req.body;
    try {
        // 1. 구독 테이블을 조회해서 남은 개월 수를 측정한다.
        Subscribe.findOne({
            where: {
                subscribe_merchant_uid: merchant_uid
            }
        }).then(async (result) => {
            if (result) {
                // 2. 남은 개월 수가 0보다 클 경우 토큰을 발급받고 imp_uid 또는 merchant_uid로 결제 정보를 조회하고 잘 되었는지 확인한다.
                if (result.subscribe_count > 0) {
                    // 액세스 토큰(access token) 발급 받기
                    const getToken = await axios({
                        url: "https://api.iamport.kr/users/getToken",
                        method: "post", // POST method
                        headers: { "Content-Type": "application/json" }, // "Content-Type": "application/json"
                        data: {
                            imp_key: process.env.IMP_API_KEY, // REST API 키
                            imp_secret: process.env.IMP_API_SECRET_KEY // REST API Secret
                        }
                    });
                    const { access_token } = getToken.data.response; // 인증 토큰

                    // imp_uid 또는 merchant_uid로 아임포트 서버에서 결제 정보 조회
                    const paymentResult = await axios({
                        url: 'https://api.iamport.kr/payments/' + imp_uid,
                        method: "get",
                        headers: { "Authorization": access_token }, // 인증 토큰을 Authorization header에 추가
                        data: {
                            imp_uid: imp_uid
                        }
                    });

                    console.log("repayments result : ", paymentResult)

                    var merchant_uid_date = moment().format('YYYY-MM-DD_HH:mm:ss');

                    if (paymentResult.data.response.status === 'paid') {
                        // 3. 예약결제가 정상적으로 이루어진 상태면 카운팅을 해주고 merchant_uid를 새로운 예약결제에 맞게 수정하며 다음 달에 예약을 걸고 로그인정보를 수정한다.( 로그인 정보에 담아줘서 나중에 화면에 달리보이게 하기 위함) 
                        // 카운팅, merchant_uid 수정

                        Subscribe.update(
                            {
                                subscribe_count: result.subscribe_count - 1,
                                subscribe_merchant_uid: "subscribe_" + merchant_uid_date,
                                subscribe_type: "기본정기결제예약"
                            }, {
                            where: {
                                subscribe_merchant_uid: merchant_uid
                            }
                        }).then(async (updateResult) => {
                            // 로그인 정보를 수정해줌 ( 잔액부족 등으로 결제 실패 후 다시 결제 했을 때 서비스 이용 가능하게 함)
                            MUserInfo.update({
                                muser_subscribe_type: result.subscribe_type
                            }, {
                                where: {
                                    muser_id: result.subscribe_muser_id
                                }
                            })

                            // 새로운 결제 예약
                            var today = new Date();
                            var schedule_at_time = new Date(today.getFullYear(), today.getMonth() + 1, today.getDate());
                            await axios({
                                url: "https://api.iamport.kr/subscribe/payments/schedule", // 예: https://api.iamport.kr/subscribe/payments/schedule
                                method: "post",
                                headers: { "Authorization": access_token }, // 인증 토큰 Authorization header에 추가
                                data: {
                                    customer_uid: result.subscribe_customer_uid, // 카드(빌링키)와 1:1로 대응하는 값
                                    schedules: [
                                        {
                                            merchant_uid: "subscribe_" + merchant_uid_date, // 주문 번호
                                            schedule_at: Math.floor(schedule_at_time.getTime() / 1000), // 결제 시도 시각 in Unix Time Stamp. 예: 다음 달 1일
                                            amount: result.subscribe_amount,
                                            name: "기본정기결제예약",
                                        }
                                    ]
                                }
                            });
                        })
                    }
                    else {
                        // 4. 만약 잔액 부족 등으로 결제가 안 된 상태면 로그인정보를 수정해서 구독 시 사용 가능한 서비스를 불가능하게 하고 다음날을 예약일로 요청한다. (구독에 관한 데이터는 남아있음).
                        Subscribe.update(
                            {
                                subscribe_merchant_uid: "subscribe_" + merchant_uid_date,
                                subscribe_type: "기본정기결제예약"
                            }, {
                            where: {
                                subscribe_merchant_uid: merchant_uid
                            }
                        }).then((updateResult) => {
                            // 로그인 정보를 수정해줌 ( 구독 서비스를 이용하지 못하게 함. )
                            MUserInfo.update({
                                muser_subscribe_type: ""
                            }, {
                                where: {
                                    muser_id: result.subscribe_muser_id
                                }
                            })

                            // 새로운 결제 예약
                            var today = new Date();
                            var schedule_at_time = new Date(today.getFullYear(), today.getMonth(), today.getDate() + 1);
                            axios({
                                url: "https://api.iamport.kr/subscribe/payments/schedule", // 예: https://api.iamport.kr/subscribe/payments/schedule
                                method: "post",
                                headers: { "Authorization": access_token }, // 인증 토큰 Authorization header에 추가
                                data: {
                                    customer_uid: result.subscribe_customer_uid, // 카드(빌링키)와 1:1로 대응하는 값
                                    schedules: [
                                        {
                                            merchant_uid: "subscribe_" + merchant_uid_date, // 주문 번호
                                            schedule_at: Math.floor(schedule_at_time.getTime() / 1000), // 결제 시도 시각 in Unix Time Stamp. 예: 다음 달 1일
                                            amount: result.subscribe_amount,
                                            name: "기본정기결제예약",
                                        }
                                    ]
                                }
                            });
                        })
                    }
                }
                // 5. 남은 개월 수가 0일 경우 예약을 걸지 않고 구독해지를 해준다 로그인 정보를 바꾸고 해당 컬럼을 삭제한다.
                else {
                    MUserInfo.update({
                        muser_subscribe_type: ""
                    }, {
                        where: {
                            muser_id: result.subscribe_muser_id
                        }
                    })
                }
            }
            else {
            }
        })

        res.send({ success: true })
    }
    catch (Err) {
        console.log("err : ", Err)
        res.send({ success: false })
    }
});
 
728x90
반응형

+ Recent posts