2022. 1. 9. 20:49ㆍ스프링부트
아임포트에 회원가입을 하고 관리자 콘솔로 이동합니다
원하는 PG사를 선택하고 테스트모드를 ON해준 뒤 저장합니다
내 정보에서 가맹점식별코드, REST API키, RESET API secret을 확인 할 수 있습니다
결제가 필요한 order폴더의 order.jsp 아래에 아임포트 라이브러리를 추가합니다
<!-- iamport.payment.js -->
<script type="text/javascript" src="https://cdn.iamport.kr/js/iamport.payment-1.1.8.js"></script>
order.js에서 기존의 payment 함수를 수정합니다
orderNum은 주문페이지로 이동할때 자바단에서 생성하였었는데 결제시 프론트에서 생성하도록 수정하였습니다
자바단에서 orderNum생성로직을 삭제해줍니다
payMethod가 신용카드일땐 paymentCard()를 호출하게 수정합니다
// 주문번호 만들기
function createOrderNum(){
const date = new Date();
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, "0");
const day = String(date.getDate()).padStart(2, "0");
let orderNum = year + month + day;
for(let i=0;i<10;i++) {
orderNum += Math.floor(Math.random() * 8);
}
return orderNum;
}
// 카드 결제
function paymentCard(data) {
// 모바일로 결제시 이동페이지
const pathName = location.pathname;
const href = location.href;
const m_redirect = href.replaceAll(pathName, "");
IMP.init("imp99151903");
IMP.request_pay({ // param
pg: "html5_inicis",
pay_method: data.payMethod,
merchant_uid: data.orderNum,
name: data.name,
amount: data.amount,
buyer_email: "",
buyer_name: "",
buyer_tel: data.phone,
buyer_addr: data.deleveryAddress2 + " " + data.deleveryAddress3,
buyer_postcode: data.deleveryAddress1,
m_redirect_url: m_redirect,
},
function (rsp) { // callback
if (rsp.success) {
// 결제 성공 시 로직,
data.impUid = rsp.imp_uid;
data.merchant_uid = rsp.merchant_uid;
paymentComplete(data);
} else {
// 결제 실패 시 로직,
}
});
}
IMP.init에는 가맹점 식별코드를 적어줍니다
여기까지만 해도 결제는 되지만 html의 음식 가격을 조작했을 경우에도 그 가격 그대로 결제가 되버립니다
그래서 서버와 가격이 맞지 않을때 결제 성공시 응답받은 imp_uid로 결제를 취소해야합니다
주석 되있던 계산완료 함수를 주석해제 합니다
OrderInfo클래스에 아임포트 결제번호 주석을 해제합니다
private String impUid = ""; // 아임포트 결제번호 추가
OrderController에 결제 후 메서드를 추가합니다
// 카드 결제 성공 후
@PostMapping("/order/payment/complete")
public ResponseEntity<String> paymentComplete(HttpSession session, OrderInfo orderInfo, long totalPrice) throws IOException {
// 1. 아임포트 API 키와 SECRET키로 토큰을 생성
// 2. 토큰으로 결제 완료된 주문정보를 가져옴
// 3. 로그인하지 않았는데 사용포인트가 0 이상일경우 결제 취소
// 4. 로그인 사용자가 현재포인트보다 사용포인트가 많을 경우 결제 취소
// 5. DB에서 실제 계산되어야 할 가격가져오기
// 6. 결제 완료된 금액과 실제 계산되어야 할 금액이 다를경우 결제 취소
// 7. 결제에러시 결제 취소
return new ResponseEntity<>(HttpStatus.OK);
}
1. 아임포트 API 키와 SECRET키로 토큰을 생성
2. 토큰으로 결제 완료된 주문정보를 가져옴
3. 로그인하지 않았는데 사용포인트가 0 이상일경우 결제 취소
4. 로그인 사용자가 현재포인트보다 사용포인트가 많을 경우 결제 취소
5. DB에서 실제 계산되어야 할 가격가져오기
6. 결제 완료된 금액과 실제 계산되어야 할 금액이 다를경우 결제 취소
7. 결제에러시 결제 취소
service패키지에 결제를 담당할 PaymentService를 생성합니다
application.properties에 REST API키와 SECRET 키를 추가합니다
imp_key=REST API키
imp_secret=SECRET 키
PaymentService
public interface PaymentService {
String getToken() throws IOException;
int paymentInfo(String imp_uid, String access_token);
public void payMentCancle(String access_token, String imp_uid, String amount, String reason);
}
PaymentServiceImp
@Service
public class PaymentServiceImp implements PaymentService {
@Value("${imp_key}")
private String impKey;
@Value("${imp_secret}")
private String impSecret;
public String getToken() throws IOException {
HttpsURLConnection conn = null;
URL url = new URL("https://api.iamport.kr/users/getToken");
conn = (HttpsURLConnection) url.openConnection();
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-type", "application/json");
conn.setRequestProperty("Accept", "application/json");
conn.setDoOutput(true);
JsonObject json = new JsonObject();
json.addProperty("imp_key", impKey);
json.addProperty("imp_secret", impSecret);
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(conn.getOutputStream()));
bw.write(json.toString());
bw.flush();
bw.close();
BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream(), "utf-8"));
Gson gson = new Gson();
String response = gson.fromJson(br.readLine(), Map.class).get("response").toString();
System.out.println(response);
String token = gson.fromJson(response, Map.class).get("access_token").toString();
br.close();
conn.disconnect();
return token;
}
public int paymentInfo(String imp_uid, String access_token) {
return 0;
}
public void payMentCancle(String access_token, String imp_uid, String amount, String reason) {
}
}
컨트롤러에서 토큰이 생성되는지 확인해봅니다
@Autowired
private PaymentService paymentService;
String token = paymentService.getToken();
System.out.println("토큰 : " + token);
이제 토큰으로 결제 정보를 가져오는 코드를 추가합니다
public int paymentInfo(String imp_uid, String access_token) throws IOException {
HttpsURLConnection conn = null;
URL url = new URL("https://api.iamport.kr/payments/" + imp_uid);
conn = (HttpsURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setRequestProperty("Authorization", access_token);
conn.setDoOutput(true);
BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream(), "utf-8"));
Gson gson = new Gson();
Response response = gson.fromJson(br.readLine(), Response.class);
br.close();
conn.disconnect();
return response.getResponse().getAmount();
}
토큰으로 요청한 결제정보는 {"code":0,"message":null,"response":{"amount":6000, .... } 형태의 중첩 JSON형태입니다
이 JSON에서 필요한 값은 취소할 금액인 amount만 가져오면 됩니다
JSON을 파싱할 클래스 Response와 JsonInfo를 만드는데 PaymentServiceImp클래스 안에서만 사용하기 때문에
PaymentServiceImp 내부에 클래스를 생성합니다
각각의 조건에 맞게 코드를 추가합니다
// 카드 결제 성공 후
@PostMapping("/order/payment/complete")
public ResponseEntity<String> paymentComplete(HttpSession session, OrderInfo orderInfo, long totalPrice, @AuthenticationPrincipal LoginService user) throws IOException {
String token = paymentService.getToken();
System.out.println("토큰 : " + token);
// 결제 완료된 금액
int amount = paymentService.paymentInfo(orderInfo.getImpUid(), token);
try {
// 주문 시 사용한 포인트
int usedPoint = orderInfo.getUsedPoint();
if (user != null) {
int point = user.getUser().getPoint();
// 사용된 포인트가 유저의 포인트보다 많을 때
if (point < usedPoint) {
paymentService.payMentCancle(token, orderInfo.getImpUid(), amount, "유저 포인트 오류");
return new ResponseEntity<String>("유저 포인트 오류", HttpStatus.BAD_REQUEST);
}
} else {
// 로그인 하지않았는데 포인트사용 되었을 때
if (usedPoint != 0) {
paymentService.payMentCancle(token, orderInfo.getImpUid(), amount, "비회원 포인트사용 오류");
return new ResponseEntity<String>("비회원 포인트 사용 오류", HttpStatus.BAD_REQUEST);
}
}
CartList cartList = (CartList) session.getAttribute("cartList");
// 실제 계산 금액 가져오기
long orderPriceCheck = orderService.orderPriceCheck(cartList) - usedPoint;
// 계산 된 금액과 실제 금액이 다를 때
if (orderPriceCheck != amount) {
paymentService.payMentCancle(token, orderInfo.getImpUid(), amount, "결제 금액 오류");
return new ResponseEntity<String>("결제 금액 오류, 결제 취소", HttpStatus.BAD_REQUEST);
}
orderService.order(cartList, orderInfo, user);
session.removeAttribute("cartList");
return new ResponseEntity<>("주문이 완료되었습니다", HttpStatus.OK);
} catch (Exception e) {
paymentService.payMentCancle(token, orderInfo.getImpUid(), amount, "결제 에러");
return new ResponseEntity<String>("결제 에러", HttpStatus.BAD_REQUEST);
}
}
결제 취소 코드를 추가합니다
public void payMentCancle(String access_token, String imp_uid, int amount, String reason) throws IOException {
System.out.println("결제 취소");
System.out.println(access_token);
System.out.println(imp_uid);
HttpsURLConnection conn = null;
URL url = new URL("https://api.iamport.kr/payments/cancel");
conn = (HttpsURLConnection) url.openConnection();
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-type", "application/json");
conn.setRequestProperty("Accept", "application/json");
conn.setRequestProperty("Authorization", access_token);
conn.setDoOutput(true);
JsonObject json = new JsonObject();
json.addProperty("reason", reason);
json.addProperty("imp_uid", imp_uid);
json.addProperty("amount", amount);
json.addProperty("checksum", amount);
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(conn.getOutputStream()));
bw.write(json.toString());
bw.flush();
bw.close();
BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream(), "utf-8"));
br.close();
conn.disconnect();
}
정상 결제일때
금액을 조작했을때
관리자 페이지에서도 관리자가 주문거부를 할수있게 OderMapper의 order쿼리에 IMP_UID를 추가합니다
'스프링부트' 카테고리의 다른 글
STOMP로 채팅 만들기 (0) | 2022.02.25 |
---|---|
스프링부트+jsp로 배달사이트 만들기-41 관리자가 주문취소시 환불하기 추가 (1) | 2022.01.20 |
sts4 깃 임포트 후 db 연결 에러 (0) | 2022.01.07 |
스프링부트+jsp로 배달사이트 만들기-39 비밀번호 찾기, 비밀번호 변경 (0) | 2022.01.04 |
스프링부트+jsp로 배달사이트 만들기-38 이메일보내기(아이디 찾기,비번찾기) (0) | 2021.12.29 |