Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter-webflux'
testImplementation 'org.springframework.security:spring-security-test'
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'

// batch
implementation 'org.springframework.boot:spring-boot-starter-batch'
Expand Down Expand Up @@ -68,7 +69,6 @@ dependencies {
// swaggerDoc
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.3.0'


}

tasks.named('test') {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ public enum ErrorCode implements BaseCode {
// Recommend Activity
RECOMMEND_ACTIVITY_NOT_FOUND(HttpStatus.NOT_FOUND, "RECOMMEND_4041", "존재하지 않는 추천입니다."),

// Payment
TID_NOT_EXIST(HttpStatus.BAD_REQUEST, "PAYMENT_4001", "tid가 존재하지 않습니다."),
TID_SID_UNSUPPORTED(HttpStatus.BAD_REQUEST, "PAYMENT_4002", "지원되지 않는 tid, sid 입니다."),
SID_NOT_EXIST(HttpStatus.BAD_REQUEST, "PAYMENT_4003", "sid가 존재하지 않습니다."),
;

private final HttpStatus httpStatus;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,14 @@ public enum SuccessCode implements BaseCode {

// MatchRequest
MATCH_REQUEST_SUCCESS(HttpStatus.OK, "MATCH_REQUEST_2001", "매치 신청이 완료되었습니다."),

// Payment
PAYMENT_URL_CREATE_SUCCESS(HttpStatus.OK, "PAYMENT_2001", "카카오페이 URL을 생성하였습니다."),
PAYMENT_CANCEL_SUCCESS(HttpStatus.OK, "PAYMENT_2002", "결제 취소가 완료되었습니다."),
PAYMENT_SUBSCRIBE_SUCCESS(HttpStatus.OK, "PAYMENT_2003", "구독이 완료되었습니다."),
PAYMENT_SUBSCRIBE_CANCEL_SUCCESS(HttpStatus.OK, "PAYMENT_2004", "구독 취소가 완료되었습니다."),
PAYMENT_VIEW_SUBSCRIBE_STATUS_SUCCESS(HttpStatus.OK, "PAYMENT_2005", "구독 상태를 반환 완료하였습니다."),

;

private final HttpStatus httpStatus;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
package com.example.silverbridgeX_user.payment.controller;

import com.example.silverbridgeX_user.global.api_payload.ApiResponse;
import com.example.silverbridgeX_user.global.api_payload.SuccessCode;
import com.example.silverbridgeX_user.payment.converter.PaymentConverter;
import com.example.silverbridgeX_user.payment.domain.Payment;
import com.example.silverbridgeX_user.payment.dto.PaymentDto;
import com.example.silverbridgeX_user.payment.service.PaymentService;
import com.example.silverbridgeX_user.user.domain.User;
import com.example.silverbridgeX_user.user.jwt.CustomUserDetails;
import com.example.silverbridgeX_user.user.service.UserService;
import io.swagger.v3.oas.annotations.Operation;
import lombok.RequiredArgsConstructor;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;

@RestController
@RequestMapping("/payment")
@RequiredArgsConstructor
public class PaymentController {

private final PaymentService paymentService;
private final UserService userService;

@PostMapping("/ready")
@Operation(summary = "카카오페이 URL 생성 API", description = "카카오페이 URL을 생성하는 API 입니다.")
public ApiResponse<PaymentDto.KakaoReadyResponse> readyToKakaoPay(@AuthenticationPrincipal CustomUserDetails customUserDetails) {
PaymentDto.KakaoReadyResponse kakaoReadyResponse = paymentService.kakaoPayReady();

User user = userService.findByUserName(customUserDetails.getUsername());
Long userId = user.getId();

paymentService.saveTid(userId, kakaoReadyResponse.getTid());

return ApiResponse.onSuccess(SuccessCode.PAYMENT_URL_CREATE_SUCCESS, kakaoReadyResponse);
}

@GetMapping("/success")
public ModelAndView afterPayRequest(@RequestParam("pg_token") String pgToken) {
PaymentDto.KakaoApproveResponse kakaoApproveResponse = paymentService.approveResponse(pgToken);

ModelAndView modelAndView = new ModelAndView("success"); // "success"는 템플릿 파일 이름
modelAndView.addObject("paymentInfo", kakaoApproveResponse);

paymentService.saveSid(kakaoApproveResponse);

return modelAndView;
}

@GetMapping("/fail")
public String fail() {
return "fail";
}

@GetMapping("/cancel")
public ApiResponse<PaymentDto.KakaoCancelResponse> refund(@AuthenticationPrincipal CustomUserDetails customUserDetails) {

User user = userService.findByUserName(customUserDetails.getUsername());
Long userId = user.getId();

Payment kakaoPay = paymentService.getKakaoPayInfo(userId);

PaymentDto.KakaoCancelResponse kakaoCancelResponse = paymentService.cancelResponse(kakaoPay.getTid());

paymentService.cancelPay(userId);

return ApiResponse.onSuccess(SuccessCode.PAYMENT_CANCEL_SUCCESS, kakaoCancelResponse);
}

@PostMapping("/subscribe")
public ApiResponse<PaymentDto.KakaoApproveResponse> subscribePayRequest(@AuthenticationPrincipal CustomUserDetails customUserDetails) {
User user = userService.findByUserName(customUserDetails.getUsername());
Long userId = user.getId();

Payment kakaoPay = paymentService.getKakaoPayInfo(userId);

PaymentDto.KakaoApproveResponse kakaoApproveResponse = paymentService.approveSubscribeResponse(kakaoPay.getSid());

paymentService.savePayInfo(userId, kakaoApproveResponse);

return ApiResponse.onSuccess(SuccessCode.PAYMENT_SUBSCRIBE_SUCCESS, kakaoApproveResponse);
}

@PostMapping("/subscribe/cancel")
@Operation(summary = "카카오페이 구독 취소 API", description = "카카오페이 구독을 취소하는 API 입니다.")
public ApiResponse<PaymentDto.KakaoSubscribeCancelResponse> subscribeCancelRequest(@AuthenticationPrincipal CustomUserDetails customUserDetails) {
User user = userService.findByUserName(customUserDetails.getUsername());
Long userId = user.getId();

Payment kakaoPay = paymentService.getKakaoPayInfo(userId);

PaymentDto.KakaoSubscribeCancelResponse kakaoSubscribeCancelResponse = paymentService.subscribeCancelResponse(kakaoPay.getSid());

return ApiResponse.onSuccess(SuccessCode.PAYMENT_URL_CREATE_SUCCESS, kakaoSubscribeCancelResponse);
}

@GetMapping("/subscribe/status")
@Operation(summary = "카카오페이 구독 상태 확인 API", description = "카카오페이 구독 상태를 확인하는 API 입니다.")
public ApiResponse<PaymentDto.KakaoPayStatus> subscribeStatusRequest(@AuthenticationPrincipal CustomUserDetails customUserDetails) {
User user = userService.findByUserName(customUserDetails.getUsername());
Long userId = user.getId();

PaymentDto.KakaoSubscribeStatusResponse kakaoSubscribeStatusResponse = new PaymentDto.KakaoSubscribeStatusResponse();

boolean isLogExist = false;
if (paymentService.getKakaoPayLog(userId)) {
Payment kakaoPay = paymentService.getKakaoPayInfo(userId);

if (kakaoPay.getSid() == null || kakaoPay.getSid().isEmpty()) {}
else {
isLogExist = true;

kakaoSubscribeStatusResponse = paymentService.subscribeStatusResponse(kakaoPay.getSid());
}
}

return ApiResponse.onSuccess(SuccessCode.PAYMENT_VIEW_SUBSCRIBE_STATUS_SUCCESS, PaymentConverter.toKakaoPayStatus(isLogExist, kakaoSubscribeStatusResponse));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package com.example.silverbridgeX_user.payment.converter;

import com.example.silverbridgeX_user.payment.domain.Payment;
import com.example.silverbridgeX_user.payment.dto.PaymentDto;
import com.example.silverbridgeX_user.user.domain.User;

public class PaymentConverter {

public static Payment toKakaoPayTid(String tid, User user) {
return Payment.builder()
.user(user)
.tid(tid)
.build();

}

public static Payment toKakaoPay(String tid, String sid, User user) {
return Payment.builder()
.user(user)
.tid(tid)
.sid(sid)
.build();

}

public static PaymentDto.KakaoPayStatus toKakaoPayStatus(boolean isLogExist, PaymentDto.KakaoSubscribeStatusResponse kakaoSubscribeStatusResponse) {
String status = kakaoSubscribeStatusResponse.getStatus();

String last_approved_at;
if(kakaoSubscribeStatusResponse.getLast_approved_at() == null || kakaoSubscribeStatusResponse.getLast_approved_at().isEmpty()) {
last_approved_at = kakaoSubscribeStatusResponse.getCreated_at();
}
else last_approved_at = kakaoSubscribeStatusResponse.getLast_approved_at();

return PaymentDto.KakaoPayStatus.builder()
.isLogExist(isLogExist)
.status(status)
.last_approved_at(last_approved_at)
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.example.silverbridgeX_user.payment.domain;

import com.example.silverbridgeX_user.global.entity.BaseEntity;
import com.example.silverbridgeX_user.user.domain.User;
import jakarta.persistence.*;
import lombok.*;

@Entity
@Getter
@Builder
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
public class Payment extends BaseEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

private String tid;

private String sid;

@OneToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id")
private User user;

public void updateTid(String tid) { this.tid = tid; }

public void updateSid(String sid) { this.sid = sid; }

public void updatePayInfo(String tid, String sid) {
this.tid = tid;
this.sid = sid;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
package com.example.silverbridgeX_user.payment.dto;

import lombok.*;

public class PaymentDto {

@Getter
@Setter
@ToString
public static class Amount {
private int total;
private int tax_free;
private int tax;
private int point;
private int discount;
private int green_deposit;
}

@Data
public static class KakaoReadyResponse {
private String tid;
private String next_redirect_app_url;
private String next_redirect_mobile_url;
private String next_redirect_pc_url;
private String android_app_scheme;
private String ios_app_scheme;
private String created_at;
}

@Getter
@Setter
@ToString
public static class KakaoApproveResponse {
private String aid;
private String tid;
private String cid;
private String sid;
private String partner_order_id;
private String partner_user_id;
private String payment_method_type;
private Amount amount;
private String item_name;
private String item_code;
private int quantity;
private String created_at;
private String approved_at;
private String payload;
}

@Getter
@Setter
@ToString
public static class KakaoCancelResponse {
private String aid;
private String tid;
private String cid;
private String status;
private String partner_order_id;
private String partner_user_id;
private String payment_method_type;
private Amount amount;
private ApprovedCancelAmount approved_cancel_amount;
private CanceledAmount canceled_amount;
private CancelAvailableAmount cancel_available_amount;
private String item_name;
private String item_code;
private int quantity;
private String created_at;
private String approved_at;
private String canceled_at;
private String payload;
}

@Getter
@Setter
@ToString
public static class KakaoSubscribeCancelResponse {
private String cid;
private String sid;
private String status;
private String created_at;
private String last_approved_at;
private String inactivated_at;
}

@Getter
@Setter
@ToString
public static class KakaoSubscribeStatusResponse {
private boolean available;
private String cid;
private String sid;
private String status;
private String item_name;
private String payment_method_type;
private String created_at;
private String last_approved_at;
private String use_point_status;
}

@Getter
@Setter
@ToString
public static class ApprovedCancelAmount {
private int total;
private int tax_free;
private int vat;
private int point;
private int discount;
private int green_deposit;
}

@Getter
@Setter
@ToString
public static class CanceledAmount {
private int total;
private int tax_free;
private int vat;
private int point;
private int discount;
private int green_deposit;
}

@Getter
@Setter
@ToString
public static class CancelAvailableAmount {
private int total;
private int tax_free;
private int vat;
private int point;
private int discount;
private int green_deposit;
}

@Builder
@Getter
@Setter
@ToString
public static class KakaoPayStatus {
private boolean isLogExist;
private String status;
private String last_approved_at;
}
}
Loading