React에서 Portone(포트원) 결제 연동하기(카카오페이)
- -
# 배경
하루네컷 서비스에서 결제기능을 맡게 되었다.
결제 요청은 프론트에서 진행하고, 그것을 검증하는 단계를 백엔드에서 맡아주기로 했다. 우리가 진행한 방식은 아래와 같다
# 결제 연동 코드 작성 전 준비 단계
결제 연동 코드를 작성하기 위해, 알고 있어야 할 정보가 있다.
1. 고객사 식별 코드(IMP)
2. PG 상점 아이디(MID)
테스트 용으로 채널 등록을 할 때, '일반 결제'를 선택했더니 제공되었다
# 포트원 라이브러리 추가
- 공식 문서에는 리액트 메뉴얼이 없어서, 리액트로 변경했다. 코드는 다른께서 블로그에 잘 정리해주셔서 활용했다
- useEffect 안에 스트립트를 넣었다.
useEffect(() => {
// 포트원 라이브러리 추가
let script = document.querySelector(
`script[src="https://cdn.iamport.kr/v1/iamport.js"]`
);
// 만약 스크립트가 존재하지 않으면
if (!script) {
// 새로운 스크립트 요소를 생성
script = document.createElement("script");
script.src = "https://cdn.iamport.kr/v1/iamport.js";
script.async = true;
document.body.appendChild(script); // 스크립트를 문서의 body에 추가
}
// 컴포넌트가 언마운트될 때 실행되는 함수 반환
return () => {
// 스크립트 요소가 존재하는지 확인 후 제거
if (script && script.parentNode === document.body) {
document.body.removeChild(script);
}
};
}, []);
HTML 문서에 <script> 요소가 중복해서 삽입되는 것을 원천적으로 막기위해, HTML 문서에서 해당 <script> 요소가 이미 존재하는지 확인하는 예외처리가 들어갔다
https://www.daleseo.com/react-hooks-use-script/
React에서 <script> 태그로 자바스크립트 불러오기
Engineering Blog by Dale Seo
www.daleseo.com
# onclickPay() 함수로 결제 요청
onclickPay()함수를 만들어서, 결제 요청하는 기능을 담당하도록 했다.
const onclickPay = (pgValue, payMethod) => {
window.IMP.init("imp11122174");
const impUid = "imp11122174";
let data;
if (selectedPencil === 1) {
data = {
pg: pgValue,
pay_method: payMethod,
merchant_uid: generateMerchantUid(),
name: "20연필(HARU4CUT)",
amount: 3000,
m_redirect_url: "",
};
} else if (selectedPencil === 2) {
data = {
pg: pgValue,
pay_method: payMethod,
merchant_uid: generateMerchantUid(),
name: "50연필(HARU4CUT)",
amount: 5000,
m_redirect_url: "",
};
} else if (selectedPencil === 3) {
data = {
pg: pgValue,
pay_method: payMethod,
merchant_uid: generateMerchantUid(),
name: "100연필(HARU4CUT)",
amount: 10000,
m_redirect_url: "",
};
}
console.log("data:", data);
window.IMP.request_pay(data, async (rsp) => {
if (rsp.success) {
const success = await verifyPayment(data.merchant_uid, impUid);
if (success) {
console.log("결제 성공");
} else {
console.log("결제 검증 실패");
}
} else {
console.log("결제 실패");
}
});
};
data에서 pg에 대해 설명하자면, pg에는 pg사 구분코드.{ PG 상점 아이디 }가 들어간다.- pg사 구분코드. PG 상점 아이디 예시 )
kakaopay.TC0ONETIME
- pg상점 아이디는 앞서, 사이트로부터 얻어왔었다
- 사용자가 선택한 옵션을 redux로 가져와서 selectedPencil 변수에 저장한 후, selectedPencil의 값에 따라 조건부로 data설정하였다.
- merchantUId는 겹치지 않도록 랜덤값으로 달라는 요청에 따라, generatemerchantUId함수를 통해, 랜덤값으로 넘겨주었다.
# verifyPayment() 함수로 결제 시 검증하기
const verifyPayment = async (merchantUid, impUid) => {
try {
const response = await axios.post(
`/verify/${merchantUid}/${userId}/${impUid}`
);
return response.data.success; // 성공 여부를 반환
} catch (error) {
console.error("결제 검증 요청 에러:", error);
return false;
}
};
# 버튼 클릭 시 결제창 띄우기
<button onClick={() => onclickPay("kakaopay.TC0ONETIME", "kakaopay")}>
# 전체 코드
import React, { useEffect } from "react";
import { useSelector } from "react-redux";
import axios from "axios";
const PaymentPage = () => {
const userId = useSelector((state) => state.userId);
const selectedPencil = useSelector((state) => state.selectedBox);
const generateMerchantUid = () => {
return "HARU4CUT-" + Math.random().toString(36).substr(2, 9);
};
useEffect(() => {
// 포트원 라이브러리 추가
let script = document.querySelector(
`script[src="https://cdn.iamport.kr/v1/iamport.js"]`
);
if (!script) {
script = document.createElement("script");
script.src = "https://cdn.iamport.kr/v1/iamport.js";
script.async = true;
document.body.appendChild(script);
}
return () => {
// 스크립트 요소가 존재하는지 확인 후 제거
if (script && script.parentNode === document.body) {
document.body.removeChild(script);
}
};
}, []);
const onclickPay = (pgValue, payMethod) => {
window.IMP.init("imp11122174");
const impUid = "imp11122174";
let data;
if (selectedPencil === 1) {
data = {
pg: pgValue,
pay_method: payMethod,
merchant_uid: generateMerchantUid(),
name: "20연필(HARU4CUT)",
amount: 3000,
m_redirect_url: "",
};
} else if (selectedPencil === 2) {
data = {
pg: pgValue,
pay_method: payMethod,
merchant_uid: generateMerchantUid(),
name: "50연필(HARU4CUT)",
amount: 5000,
m_redirect_url: "",
};
} else if (selectedPencil === 3) {
data = {
pg: pgValue,
pay_method: payMethod,
merchant_uid: generateMerchantUid(),
name: "100연필(HARU4CUT)",
amount: 10000,
m_redirect_url: "",
};
}
console.log("data:", data);
window.IMP.request_pay(data, async (rsp) => {
if (rsp.success) {
const success = await verifyPayment(data.merchant_uid, impUid);
if (success) {
console.log("결제 성공");
} else {
console.log("결제 검증 실패");
}
} else {
console.log("결제 실패");
}
});
};
const verifyPayment = async (merchantUid, impUid) => {
try {
const response = await axios.post(
`/verify/${merchantUid}/${userId}/${impUid}`
);
return response.data.success;
} catch (error) {
console.error("결제 검증 요청 에러:", error);
return false;
}
};
return (
<>
<button onClick={() => onclickPay("kakaopay.TC0ONETIME", "kakaopay")}>
카카오페이
</button>
</>
);
};
export default PaymentPage;
# 참고
https://pink1016.tistory.com/264
[React] PortOne(포트원) 이용해서 결제 연동하기
# 배경 회사에서 개발자끼리 스터디를 하기로 했다. 경매 서비스를 만들기로 했다. 거기서 결제하는 부분을 맡게 되었다. # 참조 https://portone.gitbook.io/docs/ready 결제 연동 준비하기 - PortOne Docs 포트
pink1016.tistory.com
지금은 서버 준비가 안되어서, 실 테스트는 내일 해보고 다시 작성하기
'FE > 기능' 카테고리의 다른 글
React 카카오 소셜 로그인 구현(REST API) (0) | 2024.05.30 |
---|
소중한 공감 감사합니다