TIL16 | React + Typescript + 아임포트 결제 요청

hyseoseo·2021년 9월 15일
6

TypeScript

목록 보기
1/2

기프티 프로젝트에서 결제 요청 기능을 구현 중이다.
처음에 REST API로 하는 줄 알고 API 문서를 신나게 읽어봤지만... 결제 후에 정보를 확인하는 부분만 API가 제공된다.
아임포트 github 에 가면 리액트로 client side에서 결제 요청하는 방법이 자세하게 안내되어 있다.
그러나 but 그대로 하면 타입스크립트에선 타입 에러가 난다.

1. index.html에 script 추가

  <!-- jQuery -->
  <script type="text/javascript" src="https://code.jquery.com/jquery-1.12.4.min.js" ></script>
  <!-- iamport.payment.js -->
  <script type="text/javascript" src="https://cdn.iamport.kr/js/iamport.payment-1.1.8.js"></script>

2. 가맹점 식별 코드 이용하여 IMP객체 초기화

  const IMP = window.IMP; // 생략 가능
  IMP.init("{Merchant ID}"); // Example: imp00000000

가맹점 식별코드는 https://admin.iamport.kr에 관리자 체험 모드로 들어가면 받을 수 있다. 아마 관리자 체험 모드인 경우 모두 동일하게 iamport일 것 같다.

여기까지 하면 window의 property에 IMP가 없다고 타입스크립트 에러가 뜬다.
일단 아래처럼 any 타입으로 선언만 해줘도 에러는 잠재울 수 있다.

declare global {
  interface Window {
    IMP: any
  }
}

그러나 타입스크립트 쓰면서 코드에 any가 보이면 아니 된다고 배웠습니다...
타입을 지정해 보자.
이 분 의 라이브러리를 참고했다. 다만 depreacated property 같은 것이 반영되지 않은 부분이 있어서 라이브러리를 사용하지는 않고, 따로 타입을 작성해 보았다.

3. 결제 데이터 정의하기

IMP.request_pay 함수 호출시 첫번째 인자로 결제 데이터가 전달된다. 필수 항목은 결제 금액, pay method, merchant_uid이다. 결제 데이터 property는 아임포트 공식문서 에서 확인하여 결제 데이터 타입을 정의할 수 있다.

export interface RequestPayAdditionalParams {
  digital?: boolean
  vbank_due?: string
  m_redirect_url?: string
  app_scheme?: string
  biz_num?: string
}

export interface Display {
  card_quota?: number[]
}

export interface RequestPayParams extends RequestPayAdditionalParams {
  pg?: string
  pay_method: string
  escrow?: boolean
  merchant_uid: string
  name?: string
  amount: number
  custom_data?: any
  tax_free?: number
  currency?: string
  language?: string
  buyer_name?: string
  buyer_tel: string
  buyer_email?: string
  buyer_addr?: string
  buyer_postcode?: string
  notice_url?: string | string[]
  display?: Display
}

4. 콜백 함수 정의하기

결제 후 실행될 로직을 콜백 함수에 정의한다. 콜백 함수는 IMP.request_pay 함수의 두번째 인자이다. 콜백 함수의 인자인 결제 결과 객체 역시 아임포트 공식 문서에 자세히 안내되어 있다.

export interface RequestPayAdditionalResponse {
  apply_num?: string
  vbank_num?: string
  vbank_name?: string
  vbank_holder?: string | null
  vbank_date?: number
}

export interface RequestPayResponse extends RequestPayAdditionalResponse {
  success: boolean
  error_code: string
  error_msg: string
  imp_uid: string | null
  merchant_uid: string
  pay_method?: string
  paid_amount?: number
  status?: string
  name?: string
  pg_provider?: string
  pg_tid?: string
  buyer_name?: string
  buyer_email?: string
  buyer_tel?: string
  buyer_addr?: string
  buyer_postcode?: string
  custom_data?: any
  paid_at?: number
  receipt_url?: string
}

custom_data는 any이다...ㅂㄷㅂㄷ 결제사가 임의로 custom하는 object라고 되어 있어서 타입을 모르겠다.

인자들의 타입이 모두 결정되었으니 IMP 객체의 함수 타입을 정하고 IMP 객체를 Window의 property로 넣어주면 된다.

export type RequestPayResponseCallback = (response: RequestPayResponse) => void

export interface Iamport {
  init: (accountID: string) => void
  request_pay: (
    params: RequestPayParams,
    callback?: RequestPayResponseCallback,
  ) => void
}

declare global {
  interface Window {
    IMP?: Iamport
  }
}

5. 결제 창 호출하기

위에서 정의한 결제 데이터 객체와 콜백 함수를 인자로 넣어 IMP 객체의 request_pay 함수를 호출하면 끝!

const handlePayment = () => {
    window.IMP?.init('iamport')
    const amount: number =
      priceSelections
        .filter((price) => price.value === order.price)
        .map((price) => price.amount)[0] || 0
    if (!amount) {
      alert('결제 금액을 확인해주세요')
      return
    }
    const data: RequestPayParams = {
      pg: 'html5_inicis',
      pay_method: 'card',
      merchant_uid: `mid_${new Date().getTime()}`,
      amount: amount,
      buyer_tel: '00-000-0000',
    }
    const callback = (response: RequestPayResponse) => {
      const { success, merchant_uid, error_msg, imp_uid, error_code } = response
      if (success) {
        console.log(response)
      } else {
        console.log(response)
      }
    }
    window.IMP?.request_pay(data, callback)
  }

간단한 코드이지만 작성하면서 타입스크립트의 장점을 다시 한 번 느껴볼 수 있었다. amount 설정시에 number가 아닌 값이 들어올 수 있다고 타입 에러가 나서, amount가 0일 경우 결제창을 호출하지 않고 리턴하도록 에러 처리를 했다. 미처 생각하지 못한, 에러 처리를 해야할 만한 상황을 미리 체크해볼 수 있는 것이 장점인 것 같다.

2개의 댓글

comment-user-thumbnail
2021년 9월 16일

오~~! 진행중이셨군요 !!

1개의 답글