[SpringBoot]결제, 아임포트 문제 해결 과정 (2)

Sungmin kim·2022년 11월 16일
0
post-thumbnail

문제 정의

  • Axios post방식을 통해 넘어온 데이터를 2개의 테이블에 저장해야하는 상황. (결제 내역, 예매 내역)
  • json 형식으로 넘어오는 json 데이터 객체를 서버에서 데이터를 어떻게 처리할지 고안 필요.

조건

  • jsonArray가 아닌 jsonObject로 요청.
  • 데이터를 담는 객체인 DTO(Vo)를 새로 만들지 않고 기본 테이블을 활용.

위 조건으로 진행하는 이유는 기존 자주 사용하던 방식을 탈피한 새로운 방식으로 데이터 처리를 해보고자 제약을 두었습니다.


문제 해결 절차

  1. front에서 아임포트를 통해 결제를 성공 후 결제내역과 예매내역을 json 형태로 만들어 데이터 저장을 요청.

  2. 여러가지 시도를 통해 Jackson라이브러리JsonNode 객체를 매개변수로하여 원하는 json 내부의 json객체를 찾아 뽑아낼 수 있음을 알게됨.
    이를 활용하여 원하는 객체를 뽑고 ObjectMapper(Jackson라이브러리) 객체를 생성자를 통해 호출하여 JsonNode 객체를 java객체로 변환.

    @PostMapping("/complete")
    public ResponseEntity<String> paymentComplete(@RequestBody JsonNode json_node) throws IOException {
    
        // 방법1.jackson 사용
        ObjectMapper mapper =new ObjectMapper();
    
        Payment payment = mapper.treeToValue(json_node.get("payment"),Payment.class);
        Booking booking = mapper.treeToValue(json_node.get("booking"),Booking.class);

  3. 아임포트에 토큰을 요청하여 응답 받은 후 해당 토큰으로 완료된 결제 정보 중 금액만 받아와 취소시 매개변수로 사용.

        //1. 아임포트 토큰 생성
        String token = paymentService.getToken();
        //System.out.println("토큰 : " + token);
    
        //2. 토큰으로 결제 완료된 주문 정보 호출하여 결제 완료된 금액
        int amount = paymentService.paymentInfo(payment.getImp_uid(), token);
        //System.out.println(amount);
    
        try {
            int my_point = myPageService.selectPoint("1");
            int used_point = payment.getPay_use_point();
    
            //3-1. 사용된 포인트가 유저가 기존에 보유한 포인트보다 많을 경우
            if (my_point < used_point) {
                    paymentService.paymentCancle(token, payment.getImp_uid(), amount, "사용 가능 포인트 부족");
                    return new ResponseEntity<String>("[결제 취소] 사용가능한 포인트가 부족합니다.", HttpStatus.BAD_REQUEST);
            }
    
            //4. 특별한 문제가 없을 경우 payment 데이터 저장
            paymentService.insertPaymentData(payment);
    
            //5. booking 데이터 저장
            bookingService.insertBookingData(booking);
    
            return new ResponseEntity<>("주문이 완료되었습니다", HttpStatus.OK);
            } catch (Exception e){
                paymentService.paymentCancle(token, payment.getImp_uid(),amount,"결제 예러");
                return new ResponseEntity<>("결제 에러", HttpStatus.BAD_REQUEST);
        }
     }

비즈니스 로직 메서드(Service)

paymentInfo

결제번호와 토큰을 통해 아임포트에 저장된 결제 내역을 가지고 오는 역할을 수행하는 메서드. 기존에 Json-simple 라이브러리를 보통 사용하였으나 이번은 조금 더 간결하게 json을 파싱할 수 있는 Gson이라는 구글 오픈소스 라이브러리 알게되어 사용. Gson을 사용해 json을 java 객체로 변환한 뒤 getter를 사용하여 원하는 값 (가격) 을 반환.

//JSON내부의 response를 파싱할 객체
@ToString
@Getter
private class Response{
	public PaymentInfo getResponse() {
    	return response;
    }

	private PaymentInfo response;
}

//JSON에서 금액을 가져오기위한 객체
@ToString
@Getter
private class PaymentInfo{
	public int getAmount() {
    	return amount;
    }

	private int amount;
}


public int paymentInfo(String imp_uid, String access_token) throws IOException {
        HttpsURLConnection conn = null;

        //아임포트 결제정보 API 호출(결제번호)
        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();

        //json -> Object
        Response response = gson.fromJson(br.readLine(), Response.class);

        br.close();//버퍼 닫기
        conn.disconnect();//HttpURLConnection 연결 끊기.


        return response.getResponse().getAmount();
    }

paymentCancle

결제 취소를 실행하는 메서드. OutputStream을 통해 데이터를 전달하여 아임포트 api에 취소 요청.

 public void paymentCancle(String access_token, String imp_uid, int amount ,String reason) throws IOException {

        //HttpURLConnection 인스턴스 객체 생성
        HttpsURLConnection conn = null;

        //URL 검증 및 객체 생성
        URL url = new URL("https://api.iamport.kr/payments/cancel");

        //URL 연결(웹페이지 URL 연결)
        conn = (HttpsURLConnection) url.openConnection();

        //요청방식 선택
        conn.setRequestMethod("POST");

        // 타입설정(application/json) 형식으로 전송 (Request Body 전달시 application/json로 서버에 전달.)
        conn.setRequestProperty("Content-type","application/json");

        // 서버 Response Data를 JSON 형식의 타입으로 요청.
        conn.setRequestProperty("Accept","application/json");

        // 권한을 access_token 으로 부여
        conn.setRequestProperty("Authorization",access_token);

        // OutputStream으로 POST 데이터를 넘겨주겠다는 옵션.
        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());

        //flush는 쉽게 말해서 stream에 남아 있는 데이터를 강제로 내보내는 역할
        bw.flush();
        bw.close();

        BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream(),"UTF-8"));

        br.close();
        conn.disconnect();

    }

getToken

@value 어노테이션을 통해 yml파일에 표시된 아임포트 api key와 imp_secret 값을 받은 클래스 변수를 json 객체에 넣어 아임포트에 요청. 요청을 통해 응답 받은 json 객체 내부의 access token을 Gson으로 파싱하여 반환.

@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.put("imp_key", impKey);
    json.put("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;
}

위 메서드들의 코드는 아래 링크들을 참고하여 작성했습니다.

profile
Keep Trying & Enjoing

0개의 댓글