React에서 모의결제 구현 - Portone API

semin Ryu·2024년 6월 3일
0

PortOne API

  • 모의결제를 구현하기 위해 사용한 API

포트원 사이트

포트원 결제 연동 Docs

포트원 API 사용 전 세팅

모의결제를 진행하기 위한 채널 추가

  • 연동관리 > 연동정보 > 채널추가

    • 연동모드, 결제대행사, 결제모듈 선택 후 다음선택

  • 채널이름과 속성, PG상점 아이디(MID) 선택 후 저장.

결제창 연결에 필요한 식별코드 & 키 확인

  • 연동 관리 > 연동 정보
    • 결제창 연결에 필요한 가맹점 식별코드와,  REST API KEYREST API Secret 키를 확인가능

전체적인 흐름

  1. 프론트엔드백엔드 주문 정보 생성 요청
  2. 백엔드프론트엔드 주문 정보(주문 번호, 가격 등) 생성 후 반환
  3. 프론트엔드포트원 생성된 주문 정보로 결제 요청
  4. 아임포트PG 해당 PG사에 결제 요청
  5. 사용자PG 결제 정보 확인 및 카드사 선택
  6. PG카드사 해당 카드사에 결제 요청
  7. 사용자카드사 카드 정보 입력하고 결제
  8. 카드사PG 결제 결과 반환
  9. PG포트원 결제 결과 반환
  10. 포트원프론트엔드 결제 결과 반환
  11. 프론트엔드백엔드 결제 정보와 주문 정보를 이용하여 검증 요청
  12. 백엔드프론트엔드 결제 정보와 주문 정보가 일치하는지 확인하여 검증하고 결과 반환

1. PortOne 라이브러리 추가

  • index.html 파일에 추가

  • 포트원 V1버전 <script src="https://cdn.iamport.kr/v1/iamport.js"></script>

<!-- iamport.payment.js -->
  <script type="text/javascript" src="https://cdn.iamport.kr/js/iamport.payment-1.1.8.js"></script>

2. IMP 객체 초기화

  • IMP(아임포트 모듈) 객체를 window에서 추출

  • 해당 초기화 작업 1회 이상 처리되지 않도록 유의

const { IMP } = window; // IMP(아임포트 모듈) 객체를 IMP window에서 추출

IMP.init('고객사 식별코드') // 가맹점 식별 코드로 초기화, 예): 'imp00000000a'

사전 검증

  • 프론트에서 결제 요청 이전에 구매할 상품의 가격, 개수 등의 정보로 백엔드에 요청을 보내 주문 정보 생성, 주문번호와 같은 정보를 반환 받아 사용
  • 결제정보 사전 검증은 클라이언트 변조를 원천적으로 차단하기 위한 필수 절차.
  • 결제창을 띄우는 프론트엔드를 보여주기 전에 어떤 주문번호로 얼마만큼의 결제가 이루어져야 하는지를 아래의 API를 사용하여 사전에 등록.
// 사용자에게 결제 화면을 보여주기 전에 서버 코드에서
await axios({
  url: "https://api.iamport.kr/payments/prepare",
  method: "post",
  headers: { "Content-Type": "application/json" },
  data: {
    merchant_uid: "...", // 고객사 주문번호
    amount: 420000, // 결제 예정금액
  },
});
  • 백엔드 서버로부터 반환받은 주문번호와 결제 예정금액으로 사전검증 진행

3. 결제 요청하기

  • merchant_uid와 같은 고유한 번호는 백엔드 서버로부터 받은 값을 사용

  • IMP.request_pay 는 결제를 요청하는 함수로, 첫 번째 인자로는 결제 데이터를, 두 번째 인자로는 콜백함수를 전달한다.

IMP.request_pay(
  {
    // 결제 요청 파라미터
	pg: {PG사 코드}.{상점 ID}, // PG사, 예시) `html5_inicis.INIpayTest`
    pay_method: "card", // 결제수단
    merchant_uid: `주문번호`, // 주문번호
    amount: 10000, // 결제금액
    name: `주문명`, // 주문명
    buyer_name: `구매자 이름`, // 구매자 이름
    buyer_tel: `구매자 전화번호`, // 구매자 전화번호
    buyer_email: `구매자 이메일`, // 구매자 이메일
    buyer_addr: `구매자 주소`, // 구매자 주소
    buyer_postcode: `구매자 우편번호`, // 구매자 우편번호
    m_redirect_url: "{리디렉션 될 URL}" // 만약 모바일 환경에서 결제할 경우 기입.
  },
  function (response) {
    // 결제 종료 시 호출되는 콜백 함수
    // response.imp_uid 값으로 결제 단건조회 API를 호출하여 결제 결과를 확인하고,
    // 결제 결과를 처리하는 로직을 작성합니다.
  },
);

결제 화면

4. 결제결과 처리

  • 결제가 종료된 후 응답받은 포트원 결제고유번호(imp_uid), 고객사 주문번호(merchant_uid)를 사용하여 백엔드로 결제검증 요청.

  • 백엔드는 결제 검증을 수행 후 결제 결과값을 반환, 이를 사용하여 프론트에서 결제 완료 처리를 진행.

iframe 방식

  • PC환경에서는 iframe 방식 결제

    • callback 함수 실행
IMP.request_pay(
  {/* 결제 요청 객체 */},
  rsp => {
    if (rsp.success) {   
      // axios로 HTTP 요청
      axios({
        url: "{서버의 결제 정보를 받는 endpoint}",
        method: "post",
        headers: { "Content-Type": "application/json" },
        data: {
          imp_uid: rsp.imp_uid, //결제 고유번호
          merchant_uid: rsp.merchant_uid // 고객사 주문번호
        }
      }).then((data) => {
        // 서버 결제 API 성공시 로직
      })
    } else {
      console.log(`결제 실패: ${rsp.error_msg}`);
    }
  });

Redirect 방식

  • 모바일 환경에서는 Redirect 방식 결제

    • 브라우저가 결제창으로 redirect

    • 결제창에서의 작업이 끝나면 m_redirect_url의 URL로 redirect

      • 모바일 환경의 경우 callback 함수 실행 X
IMP.request_pay(
{
  /* 결제 요청 객체 */
  m_redirect_url: "{리디렉션 될 URL}"
}, 
/* callback */); // 리디렉션 방식의 경우 callback은 실행되지 않습니다.
  • 결제완료 이후 해당 URL 주소로 결제 결과를 쿼리 스트링(Query String) 형태로 전송

    • 쿼리 스트링으로 전달되는 내용

      설명비고
      imp_uid포트원 결제 ID공통
      merchant_uid고객사 주문 고유 번호공통
      error_code오류 코드실패 시 포함
      error_msg오류 문구실패 시 포함

예시

merchant_uid가 payment-39ecfa97, m_redirect_urlhttps://example.com/payment-redirect인 경우, 결제 성공 시에
https://example.com/payment-redirect?merchant_uid=payment-39ecfa97 으로 redirect

  • m_redirect_url로 설정한, redirect될 URL에서 결제 결과에 대한 쿼리 스트링을 사용하여 검증 로직과 결제완료 로직 수행.

이렇게 리액트에서 모의결제를 구현할 수 있었습니다.

profile
류세민님의 개발블로그 입니다

2개의 댓글

comment-user-thumbnail
2024년 6월 5일

정말 도움이 됐어요.

1개의 답글