1학기가 끝나기까지 약 한달이 남았다.

비동기 작업 성능을 높여보고.. Swagger로 API 문서를 자동화하고.. Redis를 도입하는 등의 작업을 했지만..

사실상 이메일 발송 을 제외한 대부분의 기능들이 CRUD다!

따라서 새로운 기능이 도입되었으면 했고, 생각해 본 바로는 아래와 같다.

  1. Spring Batch를 이용해 대용량 데이터를 일괄처리하기

    1. 휴먼 계정이 있다고 할 때, 일괄로 이메일을 보내 알려주는건 어떨까?
  2. 결제 시스템 만들어보기

    1. 아보카도의 경우 예약 기능이 있는데, 소액의 예약금을 받는 식으로 넣어볼까?
  3. Slack 챗봇 연동

우선, 2번의 결제 시스템을 먼저 도입해보기로 했다!

💡 결제 기능 구현하기

<aside> 💡 결제 진행 흐름

  1. 클라이언트가 결제 요청시 프론트에서 결제창 호출
  2. Iamport 서비스에서 결제 진행
  3. 결제 완료시 응답을 백엔드 서버로 POST
  4. 서버는 해당 결제 내역을 저장하고, 해당 예약의 상태를 “결제 완료”로 바꿈

</aside>

결제 완료 응답을 받는 Controller

@PostMapping("/{id}")
public ResponseEntity<?> savePayment(
    @RequestBody final Map<String, Object> model,
		@PathVariable(value = "id") final Long appointmentId) throws JSONException, IOException {

		//응답 header 생성
    final HttpHeaders responseHeaders = makeHttpHeader();

    final String impUid = (String) model.get("imp_uid");
    final String merchantUid = (String) model.get("merchant_uid");
    final boolean success = (boolean) model.get("success");
    final String errorMsg = (String) model.get("error_msg");

    if (!success) {
		    log.error(errorMsg);
        return new ResponseEntity<>(errorMsg, responseHeaders, HttpStatus.OK);
		}

    try {
				//해당 예약이 이미 결제 상태인지는 아닌지 확인
		    validateAppointmentPayStatus(appointmentId);

        final var iamportClient = new IamportClient(API_KEY, API_SECRET);
        final Payment payment = extractPayment(impUid, iamportClient);

        return new ResponseEntity<>(paymentService.save(appointmentId, payment), responseHeaders, HttpStatus.OK);

		} catch (IamportResponseException | IOException e) {

		    log.error("{}", e);

        //예외 발생시 결제를 취소
				//뒤에서 구현할 예정

        return ResponseEntity.badRequest().build();
		}
}

private Payment extractPayment(final String imp_uid, final IamportClient iamportClient) throws IamportResponseException, IOException {
		return iamportClient.paymentByImpUid(imp_uid).getResponse();}

private void validateAppointmentPayStatus(final Long appointmentId) {
    final Appointment appointment = appointmentService.findById(appointmentId);
		
		if (appointment.getPayStatus() == PayStatus.COMPLETED) {
				throw new IllegalValueException("이미 결제된 예약입니다.", ErrorCode.ILLEGAL_STATE);
    }
		
		private HttpHeaders makeHttpHeader() {
        final HttpHeaders responseHeaders = new HttpHeaders();
        responseHeaders.add("Content-Type", "application/json; charset=UTF-8");
        responseHeaders.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
        return responseHeaders;
    }
}