아임포트 결제 기능 구현하기(2)_결제 처리하기

이애옹·2023년 12월 7일
0
post-custom-banner

아임포트 결제 기능 구현 완료 후, 이제 결제결과를 검증하고 처리하는 단계만 남았다..

결제 기능 구현은 아임포트 결제 기능 구현하기(1)_결제 요청하기 해당 게시글 참고!

📝 결제 결과 처리하기

일단, 결제를 요청하고 난 뒤에 결제가 정상적으로 완료 되었을 경우

다음과 같은 결제결과를 받아볼 수 있다.

나같은 경우 PC를 통한 결제는 callback 함수로 처리하고,
모바일을 통한 결제는 m_redirect_url로 처리 했던 것 같은데..

왜 두개의 방법이 다른 방식으로 처리 되었는지는 다시 알아봐야 할 것 같다..😨
(확인 해 보니 새로운 페이지로 리디렉션 될 경우 그렇다네여)

일단 callback 함수를 이용해 결제 결과 처리를 구현하려면 다음과 같이 하면 된다.

IMP.request_pay({ 결제 요청 단계 },
  rsp => {
    $.ajax({
 		url: '/payments/complete',
	    method: 'POST',
		data: {
			imp_uid: rsp.imp_uid,
			merchant_uid: rsp.merchant_uid,
		    id: localStorage.getItem('id'),
		    companyName: localStorage.getItem('companyName'),
			phoneNum: localStorage.getItem('phoneNum'),
		},
		success: function(data) {
			// 가맹점 서버 결제 API 성공시 로직
        }
	})
  });

나는 위와 같은 방식으로 결제 결과를 처리했다.

imp_uid는 결제 고유번호, merchant_uid는 주문번호로 필수로 넣어주는 값이고..

나머지 값은 필요한대로 넣어주면 된다.

여기서 imp_uid 같은 경우 결제가 정상적으로 완료되면 자동으로 값이 넘어오는데,
merchant_uid 같은 경우 내가 앞단에서 따로 설정을 해줬다.

merchant_uid는 각 결제마다 다른 값을 받아야 하기 때문에,
reqQuery.merchantUid = '고유 name_' + new Date().getTime(); 이런 형식으로 설정해줬다.

그 외에 데이터는 신청자 id와 신청자명, 휴대폰 번호 정도만 넘겨줬다.

그리고 해당 응답이 success 할 경우, 로직은 본인이 원하는대로 설정 해 주면 된다.

나 같은 경우 결제가 완료되었다는 페이지로 넘어가게 해 줬다.

📝 결제 정보 검증하기

📌 결제 검증 코드 작성

응답이 정상적으로 떨어지게 하게 위해서는,
위 ajax문에서 선언해준 /payments/complete 내에 결제 검증 코드를 넣어줘야 한다.

router.post('/payments/complete', async function(req, res, next) {
  let results = null;
  const reqParams = req.params;
  const reqQuery = req.body;
 
 // 액세스 토큰(access token) 발급 받기
  const getToken = await axios({
    url: 'https://api.iamport.kr/users/getToken',
    method: 'post',
    headers: {"Content-Type": "application/json"},
    data: {
      imp_key: 'imp_key 정보',
      imp_secret: 'imp_secret 정보'
    }
  });

  // 인증 토큰
  const { access_token } = getToken.data.response;
 
  try{    
  
  	// imp_uid로 포트원 서버에서 결제 정보 조회
    const getPaymentData = await axios({
      url: 'https://api.iamport.kr/payments/' + reqQuery.imp_uid,
      method: 'get',
      headers: { "Authorization": access_token }
    });
    
    //조회한 결제 정보
    const paymentData = getPaymentData.data.response;

	//DB에서 결제되어야 하는 정보 조회 
    //결제 시도 시 결제정보를 DB에 insert 하고, 그 데이터를 꺼내오는 과정 필요
    const order = await Orders.findById(paymentData.merchant_uid);
    const amountToBePaid = order.amount; // 결제 되어야 하는 금액
    
    // 결제 검증하기
    const { amount, status } = paymentData;

	// 결제금액 일치. 결제 된 금액 === 결제 되어야 하는 금액
    if(amount == amountToBePaid){
    
    	//결제 성공 시 구현되는 부분 
    
       results = {};
       results.code = 'S00';
       results.message = '성공'
       res.send(results);

    }else{
    
      //위조된 결제 시도 시 구현되는 부분 
      
      results = {};
      results.code = 'forgery',
      results.message = '위조된 결제 시도'
      res.send(results);
      
    }
  }catch(error){
  
  	// 결제 실패 시 구현되는 부분
    
    results = {};
    results.code = "E99";
    results.message = "시스템 오류가 발생했습니다.";

	// 결제 실패 시 결제 취소
    await axios({
      url: 'https://api.iamport.kr/payments/cancel',
      method: 'post',
      headers: { "Authorization": access_token },
      data: {
        imp_uid: reqQuery.imp_uid,
        reason: '시스템 오류로 인한 취소'
      }
    });
    res.send(results);
  }

});

해당 코드가 결제정보를 받아 처리하는 부분이다.

코드가 생각보다 길어서 어려워보이는데,
그냥 포트원에서 제공하는 코드를 복사해서 쓰면 되는거라서 생각보다 어렵지않다.

🔍 액세스 토큰 발급받기 & 토큰 값 저장

차례대로 확인 해 보면,

  • getToken : 액세스 토큰 발급받기
    • imp_key : 내 식별코드 · API Keys 에 있는 REST API Key
    • imp_secret : 내 식별코드 · API Keys 에 있는 REST API Secret

위 부분은 액세스 토큰키를 발급받는 부분이다.
다른건 수정 할 게 없고, imp_keyimp_secret 부분에만 사용자 개인에게 할당되는 Key값과 Secret 값을 넣어주면 된다.

이렇게 하면 액세스 토큰 발급까지는 완료!!!

  • const { access_token } : 액세스 토큰 값 저장

여기서 액세스 토큰 값이 저장된다.
access_token은 결제정보를 조회할때 headers에 넣어줘야 하는 값이다.

🔍 결제 정보 조회하기

  • getPaymentData : 결제 정보 조회하기

각 결제 정보를 조회하는 부분이다.

결제결과 처리하는 부분에서 넘겨줬던 imp_uid 값을 여기서 사용해야 하는데,
본인 코드의 req.body에서 해당 값을 받아 온 후에 https://api.iamport.kr/payments/라는 url 뒤에 붙여서 넘겨주면 된다.

이제 이렇게 하면 paymentData 값을 받아 올 수 있다.

paymentData 안에는 결제 금액 정보가 들어있는데,
해당 결제 금액 정보로 결제 정보가 일치하는지 확인 할 수 있다.

🔍 결제 금액 일치 여부 확인하기

  • if(amount == amountToBePaid){ : 결제 예정 금액과 실결제 금액이 일치하는지 확인
    • amount : 실 결제 금액
    • amountToBePaid : 결제 되어야 하는 금액

여기서, amountToBePaid의 경우 DB에 저장된 값을 가져와야 한다고 적혀있는데
이 부분을 구현하기 위해서는 사용자가 결제를 시도 했을때 결제 되어야 하는 값을 DB에 넣어준 후,
그 금액값을 가져오는 부분을 넣어줘야한다.

이건 본인이 쿼리를 작성해서 넣어주면 되는데
나 같은 경우에도 결제정보를 저장하는 테이블을 미리 생성 해 둔 다음
merchant_uid라는 고유값으로 WHERE 조건문을 걸어서 값을 받아와줬다~

그런데, 해당 구문이 왜 필요한지 의문이 들어서 찾아봤는데..
사용자가 스크립트를 조작하여 결제 금액을 낮추는 등의 해킹 방법이 있어서 그걸 막기 위해 사용된다고 한다!!

나도 이전에 테스트를 할때, amount값을 임의로 낮춰서 테스트 했었는데
그때마다 오류가 떨어졌었다 ㅎ.ㅎ 신기

하여튼 해당 값이 일치하지 않을 경우에는 위조된 결제 시도 라는 값을 반환해서 처리해줬다.

🔍 결제 금액 반환하기 (결제 실패 시)

이제 마지막으로 결제 실패가 떨어졌을 경우

// 결제 실패 시 결제 취소
    await axios({
      url: 'https://api.iamport.kr/payments/cancel',
      method: 'post',
      headers: { "Authorization": access_token },
      data: {
        imp_uid: reqQuery.imp_uid,
        reason: '시스템 오류로 인한 취소'
      }
    });
    res.send(results);
  }

다음과 같은 부분을 추가 해 줬는데,
결제가 실패하면 사용자에게 그 금액을 반환 해 줘야 하기 때문에 다음과 같은 코드를 넣어주면 된다.

🔍 결제 완료 로직 추가하기

참고로

IMP.request_pay({ 결제 요청 단계 },
  rsp => {
    $.ajax({
 		url: '/payments/complete',
	    method: 'POST',
		data: {
			imp_uid: rsp.imp_uid,
			merchant_uid: rsp.merchant_uid,
		    id: localStorage.getItem('id'),
		    companyName: localStorage.getItem('companyName'),
			phoneNum: localStorage.getItem('phoneNum'),
		},
		success: function(data) {
			// 가맹점 서버 결제 API 성공시 로직
        }
	})
  });

해당 부분에 결제 성공 시 로직과 인증 실패 및 위조된 결제 시도 시 로직을 추가 해 줘야한다~~~

나같은 경우 각각의 경우에 코드값을 넘겨주기 때문에
코드가 S00인 경우에는 성공 페이지로 넘기고 그 외 페이지에는 alert을 띄우는 등의 처리를 추가했다.

👀 참고자료

profile
안녕하세요
post-custom-banner

0개의 댓글