결제하기기능도 본인인증과 비슷한 플로우다.
결제버튼클릭으로 결제창열기 -> 결제하고 완료되면, 결과값과함께 return url로 이동 -> return url에서 부모창으로 결과값전달 -> 부모창에서 로직처리
우선 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');
}
},
});
결제창이 열린다.
결제창이 뜨면 팝업창에서 결제를 완료한다.
결제완료를 하면 내가 설정해놨던 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;
// 결제완료후 처리로직
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만 해주었다.