결제하기기능 next에서 빌게이츠 연동하기

5o_hyun·2025년 4월 22일
0
post-thumbnail

결제하기기능도 본인인증과 비슷한 플로우다.

결제 과정

결제버튼클릭으로 결제창열기 -> 결제하고 완료되면, 결과값과함께 return url로 이동 -> return url에서 부모창으로 결과값전달 -> 부모창에서 로직처리

1. 결제창열기

우선 Head에 script를 붙여준다.

결제를 완료하려면 다음과같은 함수를 호출해야하는것같다. 알아만두고 ㅇㅇ

결제버튼이 있는 페이지로 간다.
결제창을 만들어준다.

 // action이 테스트action이고 실결제는 문서를참고 
      <form name="payment" id="payment" method="POST" action="https://tpay.billgate.net">
        <input type="hidden" name="SERVICE_ID" />
        <input type="hidden" name="SERVICE_CODE" />
        <input type="hidden" name="SERVICE_TYPE" />
        <input type="hidden" name="ORDER_ID" />
        <input type="hidden" name="ORDER_DATE" />
        <input type="hidden" name="AMOUNT" />
        <input type="hidden" name="RETURN_URL" />
        <input type="hidden" name="ITEM_CODE" />
        <input type="hidden" name="ITEM_NAME" />
        <input type="hidden" name="USER_ID" />
        <input type="hidden" name="USER_NAME" />
        <input type="hidden" name="USER_EMAIL" />
        <input type="hidden" name="RESERVED1" />
        <input type="hidden" name="RESERVED2" />
        <input type="hidden" name="RESERVED3" />
        <input type="hidden" name="CANCEL_FLAG" />
        <input type="hidden" name="WEBAPI_FLAG" />
        <input type="hidden" name="LOGO" />
        {/* 추가 파라미터는 빌게이트 가이드 참조 */}
        <button type="button">결제하기</button>
      </form>

결제창을 열려면 form에 많은 값을 넣어줘야하는데, 이는 백엔드의 api에 요청하여 받아야한다.
나는 백엔드에 상품id , return url 을 보내어 form요청시 필요한 값을 받았다.
이후 form을 요청했다.

  // 결제하기 버튼클릭 
  const onClickPay = () => {
    getPGInfoMutation.mutate(rentalId);
  };
 // 결제하기 함수 
  const getPGInfoMutation = useMutation({
    mutationKey: ['getPGInfo'],
    mutationFn: (rental_id: number) =>
      client.Payments_PgInfo_ON_CENTER_CODE_POST(config.centerInfo.center_code, {
        rental_id,
        return_url: `${process.env.NEXT_PUBLIC_API_URL}/pay`,
      }),
    onSuccess: (response) => {
      const form = document.getElementById('payment') as HTMLFormElement;
      const data = response.data as PayInfo;

      if (form && data) {
        form.SERVICE_ID.value = data.SERVICE_ID;
        form.SERVICE_CODE.value = data.SERVICE_CODE;
        form.SERVICE_TYPE.value = data.SERVICE_TYPE;
        form.ORDER_ID.value = data.ORDER_ID;
        form.ORDER_DATE.value = data.ORDER_DATE;
        form.AMOUNT.value = data.AMOUNT;
        form.RETURN_URL.value = data.RETURN_URL;
        form.ITEM_CODE.value = data.ITEM_CODE;
        form.ITEM_NAME.value = data.ITEM_NAME;
        form.USER_ID.value = data.USER_ID;
        form.USER_NAME.value = data.USER_NAME;
        form.USER_EMAIL.value = data.USER_EMAIL;
        form.RESERVED1.value = data.RESERVED1;
        form.RESERVED2.value = data.RESERVED2;
        form.RESERVED3.value = data.RESERVED3;
        // form.CANCEL_FALG.value = data.CANCEL_FALG;
        form.CANCEL_FLAG.value = 'Y';
        form.WEBAPI_FLAG.value = data.WEBAPI_FLAG;
        form.LOGO.value = data.LOGO;

        GX_pay('payment', 'popup', 'https_tpay');
      }
    },
  });

결제창이 열린다.

2. 결제완료후 결과값받기

결제창이 뜨면 팝업창에서 결제를 완료한다.
결제완료를 하면 내가 설정해놨던 return url로 팝업창의 주소가 바뀐다. ${process.env.NEXT_PUBLIC_API_URL}/pay

/pay 에 가도록 설정했으니 해당경로에 페이지를 만들어주고,
여기서는 부모에게 결과값을 전달해주고 팝업을 닫는 역할만 한다.

// /pay
'use client';

import { useEffect } from 'react';

const PayClient = () => {
  const urlParams = new URLSearchParams(window.location.search);
  const status = urlParams.get('status');

  useEffect(() => {
    if (status === 'success') {
      window.opener?.postMessage({ type: 'PAY_SUCCESS' }, '*');
    } else {
      window.opener?.postMessage({ type: 'PAY_FAIL' }, '*');
    }

    window.close();
  }, [status]);

  return null;
};

export default PayClient;

3. 부모창에서 결과값으로 로직처리

  // 결제완료후 처리로직
  useEffect(() => {
    const handleMessage = async (event: MessageEvent) => {
      const { type } = event.data || {};

      if (type === 'PAY_SUCCESS') {
        Alert('결제가 정상 처리되었습니다.');
        GX_payClose();
        router.push('/my/rental');
      } else if (type === 'PAY_FAIL') {
        Alert('결제가 완료되지 않았습니다.');
        GX_payClose();
      }
    };

    window.addEventListener('message', handleMessage);
    return () => window.removeEventListener('message', handleMessage);
  }, []);

최종적으로 api는 폼에 필요한값을 받을때 1번만 요청하고, pg사에서 제공한 팝업안에서 결제완료가되면, 나는 팝업을 닫고 refetch만 해주었다.

profile
학생 점심 좀 차려

0개의 댓글