강의를 구매하기 위해 결제 API인 아임포트 API 를 이용하여 결제를 구현해보았다.
<!-- 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>
결제 화면을 구현하기 앞서 서버에서 결제를 검증하는 로직을 추가한다.
개발자들이 사용하기 쉽게 자바스크립트 형태로 제공하고 있기 때문에 결제 금액 및 결제 상태에 대한 변조가 가능하다.
따라서 요청한 금액과 결제가 올바르게 이루어졌는지에 대해 아임 포트 서버로 아임포트 거래 고유번호(imp_uid)나 주문 고유번호(merchant_uid)를 보내 확인하는 과정을 반드시 거쳐야 한다.
Rest Api를 통해 결제 검증 및 취소가 이루어진다. 이루어지기 앞서 Token을 발급받아야 한다. Token을 발급받기 위해 관리자 페이지의 시스템 설정에서 REST API키와 REST API secret을 복사해놓도록 한다.
allprojects {
repositories {
...
maven { url 'https://jitpack.io' }
}
}
dependencies {
implementation 'com.github.iamport:iamport-rest-client-java:0.2.21'
}
VerifyController
@Slf4j
@RestController
@RequiredArgsConstructor
@RequestMapping("/verifyIamport")
public class VerifyController {
/** Iamport 결제 검증 컨트롤러 **/
private final IamportClient iamportClient;
// 생성자를 통해 REST API 와 REST API secret 입력
public VerifyController(){
this.iamportClient = new IamportClient("...", "...");
}
/** 프론트에서 받은 PG사 결괏값을 통해 아임포트 토큰 발행 **/
@PostMapping("/{imp_uid}")
public IamportResponse<Payment> paymentByImpUid(@PathVariable String imp_uid) throws IamportResponseException, IOException{
log.info("paymentByImpUid 진입");
return iamportClient.paymentByImpUid(imp_uid);
}
}
paymentByImpUid
paymentByImpUid
paymentByImpUid
함수는 아임포트서버에서 imp_uid(거래 고유번호)를 검사하여, 데이터를 보내준다.
/** 결제 **/
// 결제 금액, 구매자의 이름, 이메일
const priceAmount = $('#totalPrice').val();
const buyerMemberEmail = $('#memberEmail').val();
const buyerMemberName = $('#memberName').val();
// const form = document.getElementById("payment");
console.log(priceAmount);
console.log(buyerMemberName);
console.log(buyerMemberEmail);
const IMP = window.IMP;
IMP.init('imp99053400');
function requestPay() {
// IMP.request_pay(param, callback) 결제창 호출
IMP.request_pay({ // param
pg: "kakaopay.TC0ONETIME",
pay_method: "card",
merchant_uid: 'cart_' + new Date().getTime(),
name: "Helpring 강의",
amount: priceAmount,
buyer_email: buyerMemberEmail,
buyer_name: buyerMemberName,
}, function (rsp) { // callback
/** 결제 검증 **/
$.ajax({
type: 'POST',
url: '/verifyIamport/'+rsp.imp_uid,
beforeSend: function(xhr){
xhr.setRequestHeader(header, token);
}
}).done(function(result){
// rsp.paid_amount와 result.response.amount(서버 검증) 비교 후 로직 실행
if(rsp.paid_amount === result.response.amount){
alert("결제가 완료되었습니다.");
$.ajax({
type:'POST',
url:'/lecture/payment',
beforeSend: function(xhr){
xhr.setRequestHeader(header, token);
}
}).done(function() {
window.location.reload();
}).fail(function(error){
alert(JSON.stringify(error));
})
} else{
alert("결제에 실패했습니다."+"에러코드 : "+rsp.error_code+"에러 메시지 : "+rsp.error_message);
}
})
});
};
const IMP = window.IMP;
IMP.init('__');
Iinit 함수의 인자에 가맹점 식별코드를 입력한다. 가맹점 식별코드는 아임포트 홈페이지 -> 대시보드 -> 로그인 -> 시스템 설정에서 찾을 수 있다.
IMP.request_pay({ // param
pg: "kakaopay.TC0ONETIME",
pay_method: "card",
merchant_uid: 'cart_' + new Date().getTime(),
name: "Helpring 강의",
amount: priceAmount,
buyer_email: buyerMemberEmail,
buyer_name: buyerMemberName
}
IMP.request_pay(param, callback)을 호출하는 코드를 작성
함수의 첫 번째 인자인 param에 결제 요청에 필요한 속성과 값을 담는다. 해당 함수를 호출하면 입력한 속성과 값에 따라 결제창을 보여주며, 따라서 파라미터들은 운영하는 서비스/환경에 맞게 설정해야 한다.
}, function (rsp) { // callback
/** 결제 검증 **/
$.ajax({
type: 'POST',
url: '/verifyIamport/'+rsp.imp_uid,
beforeSend: function(xhr){
xhr.setRequestHeader(header, token);
}
}).done(function(result){
// rsp.paid_amount와 result.response.amount(서버 검증) 비교 후 로직 실행
if(rsp.paid_amount === result.response.amount){
alert("결제가 완료되었습니다.");
$.ajax({
type:'POST',
url:'/lecture/payment',
beforeSend: function(xhr){
xhr.setRequestHeader(header, token);
}
}).done(function() {
window.location.reload();
}).fail(function(error){
alert(JSON.stringify(error));
})
} else{
alert("결제에 실패했습니다."+"에러코드 : "+rsp.error_code+"에러 메시지 : "+rsp.error_message);
}
})
사용자가 결제 완료 후 실행되는 함수
callback함수로 전달되는 rsp 인자에는 결제의 성공 여부, 결제 정보, 에러 정보 등을 가지고 있다.
결제 프로세스 완료 후 rsp인자를 통해 결과를 확인하고, 확인 이후에 수행하고자 하는 작업을 callback에서 작성한다.
paid_amount
와 rsp.paid.amount
를 비교하여 검증을 완료한다. 검증이 완료되면 해당 구매자에게 해당 강의를 수강할 수 있도록 권한을 부여한다.(lecture/payment에 post 방식으로 ajax 통신)
큐알코드 인증 시 '종료된 요청입니다.' 알림이 뜨면서 결제가 안 됨
내가 수강하고 있는 강의로 리다이렉트가 되지 않음
내 강의
로 리다이렉트하도록 설계했는데 왜 리다이렉트가 되지 않은 걸까? 그래서 우선 자바스크립트 내에서 결제가 성공되면 window.location.reload() 하는 걸로 설계를 변경했다.출처
https://docs.iamport.kr/implementation/payment#add-library
https://tyrannocoding.tistory.com/44
https://github.com/iamport/iamport-manual/blob/master/%EC%9D%B8%EC%A6%9D%EA%B2%B0%EC%A0%9C/sample/kakao.md
https://jee2memory.tistory.com/entry/%EC%95%84%EC%9E%84%ED%8F%AC%ED%8A%B8-%EC%B9%B4%EC%B9%B4%EC%98%A4%EC%A0%95%EA%B8%B0%EA%B2%B0%EC%A0%9C-API-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0
https://aandi.tistory.com/37
https://aandi.tistory.com/28
https://puzzle-puzzle.tistory.com/entry/import-%EC%95%84%EC%9E%84%ED%8F%AC%ED%8A%B8-%EA%B2%B0%EC%A0%9C-%EB%AA%A8%EB%93%88-%EB%A7%8C%EB%93%A4%EA%B8%B0
이런식으로 짜면 결제는 된 후에 검증이 되서 돈만 빠져나가고 DB 에 값은 입력 안되는거 아닌가요?