Skip to content

Commit

Permalink
Merge pull request #22 from 9oormthon-univ/feature/deploy/#3
Browse files Browse the repository at this point in the history
Feat 예약 및 결제 구현
  • Loading branch information
BinarySstar authored Nov 23, 2024
2 parents 36f9ae1 + e807464 commit e38c39c
Show file tree
Hide file tree
Showing 11 changed files with 248 additions and 134 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package univ.goormthon.kongju.domain.payment;

import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;

import java.util.Objects;

public class SessionUtils {

public static void addAttribute(String name, Object value) {
Objects.requireNonNull(RequestContextHolder.getRequestAttributes()).setAttribute(name, value, RequestAttributes.SCOPE_SESSION);
}

public static String getStringAttributeValue(String name) {
return String.valueOf(getAttribute(name));
}

public static Object getAttribute(String name) {
return Objects.requireNonNull(RequestContextHolder.getRequestAttributes()).getAttribute(name, RequestAttributes.SCOPE_SESSION);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package univ.goormthon.kongju.domain.payment.controller;

import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import univ.goormthon.kongju.domain.payment.SessionUtils;
import univ.goormthon.kongju.domain.payment.dto.request.PayApproveRequest;
import univ.goormthon.kongju.domain.payment.dto.response.PayApproveResponse;
import univ.goormthon.kongju.domain.payment.dto.response.PayReadyResponse;
import univ.goormthon.kongju.domain.payment.service.KakaoPayService;
import univ.goormthon.kongju.domain.reservation.entity.Reservation;

@RestController
@RequiredArgsConstructor
@RequestMapping("/api/kongju/payment")
public class KakaoPayController {

private final KakaoPayService kakaoPayService;

@PostMapping("/ready")
public ResponseEntity<PayReadyResponse> ready(@RequestBody Reservation reservation, @RequestParam int totalFee) {
PayReadyResponse response = kakaoPayService.ready(reservation, totalFee);
SessionUtils.addAttribute("tid", response.tid());
return ResponseEntity.ok(response);
}

@GetMapping("/approval")
public ResponseEntity<PayApproveResponse> approval(@RequestParam("pg_token") String pgToken) {
String tid = SessionUtils.getStringAttributeValue("tid");
PayApproveResponse response = kakaoPayService.approve(pgToken, tid);
return ResponseEntity.ok(response);
}

@PostMapping("/cancel")
public ResponseEntity<?> cancel(@RequestParam String tid, @RequestParam int cancelAmount) {
return ResponseEntity.ok(null);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package univ.goormthon.kongju.domain.payment.dto.request;

import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Builder;

@Builder
public record PayApproveRequest(
@JsonProperty("cid") String cid,
@JsonProperty("tid") String tid,
@JsonProperty("partner_order_id") String partnerOrderId,
@JsonProperty("partner_user_id") String partnerUserId,
@JsonProperty("pg_token") String pgToken
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package univ.goormthon.kongju.domain.payment.dto.request;

import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Builder;

@Builder
public record PayRequest(
@JsonProperty("cid") String cid,
@JsonProperty("partner_order_id") String partnerOrderId,
@JsonProperty("partner_user_id") String partnerUserId,
@JsonProperty("item_name") String itemName,
@JsonProperty("quantity") int quantity,
@JsonProperty("total_amount") int totalAmount,
@JsonProperty("tax_free_amount") int taxFreeAmount,
@JsonProperty("approval_url") String approvalUrl,
@JsonProperty("cancel_url") String cancelUrl,
@JsonProperty("fail_url") String failUrl
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package univ.goormthon.kongju.domain.payment.dto.response;

import com.fasterxml.jackson.annotation.JsonProperty;

import java.time.LocalDateTime;

public record PayApproveResponse(
@JsonProperty("aid") String aid,
@JsonProperty("tid") String tid,
@JsonProperty("cid") String cid,
@JsonProperty("partner_order_id") String partnerOrderId,
@JsonProperty("partner_user_id") String partnerUserId,
@JsonProperty("payment_method_type") String paymentMethodType,

@JsonProperty("amount") Amount amount,
@JsonProperty("item_name") String itemName,
@JsonProperty("approved_at") LocalDateTime approvedAt

) {
public record Amount(
@JsonProperty("total") int total,
@JsonProperty("tax_free") int taxFree,
@JsonProperty("vat") int vat,
@JsonProperty("point") int point,
@JsonProperty("discount") int discount) {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package univ.goormthon.kongju.domain.payment.dto.response;

import com.fasterxml.jackson.annotation.JsonProperty;

public record PayReadyResponse(
@JsonProperty("tid") String tid,
@JsonProperty("next_redirect_pc_url") String nextRedirectPcUrl
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package univ.goormthon.kongju.domain.payment.service;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import univ.goormthon.kongju.domain.payment.dto.request.PayApproveRequest;
import univ.goormthon.kongju.domain.payment.dto.request.PayRequest;
import univ.goormthon.kongju.domain.payment.dto.response.PayApproveResponse;
import univ.goormthon.kongju.domain.payment.dto.response.PayReadyResponse;
import univ.goormthon.kongju.domain.reservation.entity.Reservation;

@Service
public class KakaoPayService {

@Value("${kakaopay.secret-key}")
private String secretKey;

@Value("${kakaopay.cid}")
private String cid;

public PayReadyResponse ready(Reservation reservation, int totalFee) {
PayRequest request = PayRequest.builder()
.partnerOrderId(String.valueOf(reservation.getId()))
.partnerUserId(String.valueOf(reservation.getMemberId()))
.itemName("공유 주차장 결제 진행")
.quantity(1)
.totalAmount(totalFee)
.taxFreeAmount(0)
.approvalUrl("http://localhost:8080/api/kongju/payment/approval")
.cancelUrl("http://localhost:8080/api/kongju/payment/cancel")
.failUrl("http://localhost:8080/api/kongju/payment/fail")
.build();

HttpEntity<PayRequest> entity = new HttpEntity<>(request, this.getHeaders());

RestTemplate restTemplate = new RestTemplate();
String url = "https://kapi.kakao.com/v1/payment/ready";
ResponseEntity<PayReadyResponse> response = restTemplate.postForEntity(url, request, PayReadyResponse.class);

return response.getBody();
}

public PayApproveResponse approve(String pgToken, String tid) {
PayApproveRequest request = PayApproveRequest.builder()
.cid(cid)
.tid(tid)
.partnerOrderId("1")
.partnerUserId("1")
.pgToken(pgToken)
.build();

HttpEntity<PayApproveRequest> entity = new HttpEntity<>(request, this.getHeaders());

RestTemplate restTemplate = new RestTemplate();
String url = "https://kapi.kakao.com/v1/payment/approve";

return restTemplate.postForObject(url, request, PayApproveResponse.class);
}

private HttpHeaders getHeaders() {
HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", "SECRET_KEY " + secretKey);
headers.set("Content-type", "application/json");

return headers;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ public class ReservationController {
@ApiResponse(responseCode = "404", description = "회원 세션이 존재하지 않음", content = @Content(schema = @Schema(implementation = ErrorCode.class)))
})
@GetMapping("/list")
public ResponseEntity<List<ReservationResponse>> getReservations(HttpSession session){
return ResponseEntity.ok(reservationService.getReservations(session));
public ResponseEntity<List<ReservationResponse>> getReservations(@RequestParam String memberId){
return ResponseEntity.ok(reservationService.getReservations(memberId));
}

@Operation(summary = "예약 조회", description = "현재 로그인한 사용자의 예약 하나를 조회합니다.")
Expand All @@ -46,47 +46,21 @@ public ResponseEntity<List<ReservationResponse>> getReservations(HttpSession ses
@ApiResponse(responseCode = "404", description = "회원 세션이 존재하지 않음", content = @Content(schema = @Schema(implementation = ErrorCode.class)))
})
@GetMapping
public ResponseEntity<ReservationResponse> reserve(HttpSession session, @RequestParam Long parkingId) {
return ResponseEntity.ok(reservationService.getReservation(session, parkingId));
public ResponseEntity<ReservationResponse> getReservation(@RequestParam String memberId, @RequestParam Long parkingId) {
return ResponseEntity.ok(reservationService.getReservation(memberId, parkingId));
}

@Operation(summary = "예약 대기", description = "현재 로그인한 사용자의 예약을 진행하기 전에 대기합니다.")
@Operation(summary = "예약 진행", description = "현재 로그인한 사용자의 예약을 진행합니다.")
@Parameters(value = {
@Parameter(name = "request", description = "예약 요청 JSON 형식의 데이터")
})
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "예약 성공", content = @Content(schema = @Schema(implementation = ReservationResponse.class))),
@ApiResponse(responseCode = "200", description = "예약 진행", content = @Content(schema = @Schema(implementation = ReservationResponse.class))),
@ApiResponse(responseCode = "404", description = "회원 세션이 존재하지 않음", content = @Content(schema = @Schema(implementation = ErrorCode.class)))
})
@PostMapping
public ResponseEntity<ReservationResponse> reserve(HttpSession session, @RequestBody ReservationRequest request) {
return ResponseEntity.ok(reservationService.reserve(session, request));
}

@Operation(summary = "예약 취소", description = "현재 로그인한 사용자의 예약을 취소합니다.")
@Parameters(value = {
@Parameter(name = "parkingId", description = "주차장 ID")
})
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "예약 취소 성공", content = @Content(schema = @Schema(implementation = ReservationResponse.class))),
@ApiResponse(responseCode = "404", description = "회원 세션이 존재하지 않음", content = @Content(schema = @Schema(implementation = ErrorCode.class)))
})
@PostMapping("/cancel")
public ResponseEntity<ReservationResponse> cancelReservation(HttpSession session, @RequestParam Long parkingId) {
return ResponseEntity.ok(reservationService.cancel(session, parkingId));
}

@Operation(summary = "예약 수락", description = "현재 로그인한 사용자의 예약을 수락합니다.")
@Parameters(value = {
@Parameter(name = "parkingId", description = "주차장 ID")
})
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "예약 수락 성공", content = @Content(schema = @Schema(implementation = ReservationResponse.class))),
@ApiResponse(responseCode = "404", description = "회원 세션이 존재하지 않음", content = @Content(schema = @Schema(implementation = ErrorCode.class)))
})
@PostMapping("/accept")
public ResponseEntity<ReservationResponse> acceptReservation(HttpSession session, @RequestParam Long parkingId) {
return ResponseEntity.ok(reservationService.accept(session, parkingId));
public ResponseEntity<ReservationResponse> reserve(@RequestParam String memberId, @RequestBody ReservationRequest request) {
return ResponseEntity.ok(reservationService.reserve(memberId, request));
}

}
Loading

0 comments on commit e38c39c

Please sign in to comment.