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