포트원(아임포트) 테스트 결제 연동(2) - 서버에서 API사용하기

HS L·2023년 6월 26일
1

내일배움캠프

목록 보기
72/73

문제

포트원 연동 후 필요한 내용을 서버DB로 저장하기 위해 결제완료시 포트원으로부터 받는 status가 정상인 경우 서버로 저장해달라는 요청을 보냈었다.

프론트에서 사용자 조작을 통해 결제과정을 거치지 않고도 저장요청을 보낼 수 있어 구매하지 않고도 구매처리가 되는 경우를 겪었다.

이를 막기 위해 결제 완료시 서버에서 포트원에 구매내역이 있는지 확인하고 내역이 정상적인경우 DB저장과정을 진행했다.

이 글은 포트원 테스트결제 연동 이후의 과정을 적었고 테스트결제 연동은 이전 글을 참고
포트원(아임포트) 테스트 결제 연동(1) - 카카오페이
기존 카카오페이에서 KCP결제로 변경함..

DB저장 로직


순서대로 따라가본다면 클라이언트측에서 결제요청시 연동된 포트원에 요청, 결제 처리 후 포트원측에서 클라이언트로 결과 응답이 이루어진다.

구매여부에 따라 서버 DB저장을 구현하고자 한다면 서버 저장이 진행되는 부분은 4번이 될 것이다.

이때 결제 처리응답이 정상인 경우 구매관련 DB를 생성하는 코드를 작성한다면 프론트에서 쉽게 조작이 가능해진다.

결제 과정을 거치지 않고 DB저장 요청만 서버로 보내면 구매하지 않고도 구매한 사용자가 될 수 있다는 것이다.

결제요청에 대한 포트원의 응답에는 포트원에 저장되는 결제 아이디를 같이 담아준다.

포트원을 거쳐와야지만 생성되는 아이디를 사용하기 때문에 프론트에서의 조작을 방지할 수 있다.

해당 아이디를 사용해 서버에서 포트원으로 결제정보를 조회, 결제상태 판단 후 그에 따른 동작들을 작성하면 될 것 같다.


서버에서 포트원(아임포트) 결제조회하기

서버에서 포트원 결제조회를 하기 위해서는 공식문서를 참고했다.
포트원 개발자센터

  1. 포트원 API 토큰 발급받기
  2. 서버에서 결제조회 요청하기
  3. 응답에 따른 DB저장 코드작성하기

1. 포트원 API 토큰 발급받기

포트원 API를 사용하기 위해서는 토큰이 필요하다.

이전 가입했던 포트원계정으로 로그인해 필요한 정보들을 확인해야 한다.

홈 화면에서 계정관리로 들어가 API Keys버튼을 누른다.

해당 화면에서 필요한건 REST API Key와 REST API Secret 두가지이다.

다음으로 토큰 발급은 아래 공식문서를 따라간다.
포트원 API 토큰 발급

step01. 발급 요청하기에 API요청 코드가 있다.

최초 Python코드가 아닌 다른게 띄워져있기 때문에 해당 버튼을 눌러 코드를 가져오면 된다.

import requests
import json

def getTokenApi(path):
    API_HOST = "https://api.iamport.kr"
    url = API_HOST + path

    headers = {'Content-Type': 'application/json', 'charset': 'UTF-8', 'Accept': '*/*'}
    body = {
        "imp_key": "", # REST API Key
        "imp_secret": "" # REST API Secret
    }
    try:
        response = requests.post(url, headers=headers, data=json.dumps(body, ensure_ascii=False, indent="\t"))
        return response
    except Exception as ex:

res=getTokenApi("/users/getToken")  # API call
json_object=json.loads(res.text)    # json 객체로 변환
TokenVal = json_object['response']['access_token'] # 토큰값 파싱

print(TokenVal)

공식문서에서 요청코드를 알려줬으니 그대로 이식해주자.
아래 res=getTokenApi("/users/getToken") 부터 print까지는 토큰을 요청하는 곳에 적절히 이식하면 된다.

위에서 확인한 REST API Key / Secret정보들을 해당 위치에 넣어준다.

이때 노출되면 안되기 때문에 필자는 Django SecretKey와 같이 보관 해줬다.

except 이후 코드가 없어 필자는 except문으로 빠지는 경우 위치를 알릴 수 있도록 프린트문을 하나 넣어뒀다.

except Exception as ex:
        # 포트원 공식문서 사용법 가져온거라 익셉트로 빠지는 경우 위치확인용으로 프린트 남겼습니다.
        print(
            "포트원 토큰 요청 에러\n/emoticons/views.py/getTokenApi(path) - 17번째 줄, 또는 사용된 위치에서 에러 발생"
        )

여기까지 진행했다면 해당 요청을 실행했을때 print문으로 찍혀 나오는건 포트원 API Access Token이 찍힐 것이다.

요청할 API header에 넣어줘야하는 값이니 요청이 필요한 곳에 적절히 사용하면 된다.

2. 서버에서 결제조회 요청하기

마찬가지로 공식문서 참고
포트원 결제내역 단건조회 API


단순하다..
해당 url에 GET방식으로 요청을 넣으면 상태에 따라 응답을 받게 된다.
각 항목들을 눌러보면 자세한 내용들을 확인할 수 있다.

imp_uid는 프론트에서 결제과정을 거치고 나서 받게되는 데이터에서 확인할 수 있다.
요청할 API url에 사용해야하기 때문에 결제과정 진행 후 해당 값을 담아 서버로 POST요청을 보내준다.

이제 서버쪽에서는 받아온 imp_uid를 사용해서 API요청하는 코드를 작성해준다.

3. (이어서)요청과 응답에 따른 DB저장 코드작성하기

아래는 내가 작성한 코드 예시이다.

def post(self, request):
        emoticon = get_object_or_404(
            Emoticon, id=int(request.data["emoticon_id"]), db_status=1
        )

        # 포트원 access token 받아오기
        res = getTokenApi("/users/getToken")  # API call
        json_object = json.loads(res.text)  # json 객체로 변환
        TokenVal = json_object["response"]["access_token"]  # 토큰값 파싱

        # 요청에 필요한 값
        imp_uid = request.data["imp_uid"] # 프론트에서 받아온 imp_uid
        headers = {"Authorization": TokenVal}

        # 포트원 결제 조회 요청하기(공식문서에 명시된 url과 요청방식 사용)
        response = requests.get( 
            f"https://api.iamport.kr/payments/{imp_uid}", headers=headers
        )

        # 공식문서에 명세된 내용들에 따라 클라이언트에 응답할 내용들 작성
        if response.status_code == 200:
            payment_response = response.json()
            if payment_response["response"]["status"] == "paid":
                UserEmoticonList.objects.create(
                    sold_emoticon=emoticon, buyer=request.user
                )
                return Response({"message": "결제 완료!"}, status=status.HTTP_200_OK)
            else:
                return Response(
                    {"message": "결제 취소 또는 결제실패"},
                    status=status.HTTP_402_PAYMENT_REQUIRED,
                )
        elif response.status_code == 401:
            return Response(
                {"message": "포트원 request 토큰 오류"},
                status=status.HTTP_402_PAYMENT_REQUIRED,
            )
        elif response.status_code == 404:
            return Response(
                {"message": "유효하지 않은 imp_uid"}, status=status.HTTP_402_PAYMENT_REQUIRED
            )
        else:
            return Response(
                {"message": "확인 되지 않는 오류, 포트원 문의 필요"},
                status=status.HTTP_402_PAYMENT_REQUIRED,
            )

나름 공식문서 명세에 맞춰 작성한다고 했는데 부족한 부분도 있을 것이다..
이 부분은 필요에 맞춰 커스텀하면 될 것 같다.

profile
식이

0개의 댓글