Skip to content

Commit

Permalink
#1 [FEAT] 멤버십 권한 구분 및 차등 결제 구현
Browse files Browse the repository at this point in the history
  • Loading branch information
bbabbi committed Dec 21, 2024
1 parent 57c5402 commit 20bf321
Show file tree
Hide file tree
Showing 11 changed files with 114 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,6 @@ public List<AdminBookDto> getBookList(String search){
}




}
26 changes: 25 additions & 1 deletion src/main/java/org/vsa/server/book/service/BookService.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package org.vsa.server.book.service;

import org.vsa.server.Notification.entity.Notification;
import org.vsa.server.Notification.repository.NotificationRepository;
import org.vsa.server.book.dto.request.BookCreateRequest;
import org.vsa.server.book.dto.request.BookUpdateRequest;
import org.vsa.server.book.dto.response.*;
Expand Down Expand Up @@ -57,6 +59,8 @@ public class BookService {
@Autowired
private KeywordRepository keywordRepository;
@Autowired
private NotificationRepository notificationRepository;
@Autowired
private JavaMailSender emailSender;

@Value("${google.email}")
Expand All @@ -83,7 +87,7 @@ public BookCheckDto checkBook(Long bookId,Long memberId){
lastPage(lastPage).
category(book.getCategory()).
isMarked(bookmarkRepository.existsBookmarkByBook_BookIdAndMember_MemberId(book.getBookId(),memberId)).build();
return dto;
return dto;
}

public Page<BookDto> searchBook(String title, int pageNo, Long memberId){
Expand Down Expand Up @@ -188,13 +192,19 @@ public BookInfoResponse createBook(BookCreateRequest request) throws MessagingEx
);
bookRepository.save(book);



// 키워드로 관련 멤버 검색
List<Keyword> matchingKeywords = keywordRepository.findMatchingKeywords(book.getTitle());

if(matchingKeywords.isEmpty()){
return toResponse(book);
}

List<Member> matchedMembers = matchingKeywords.stream()
.map(Keyword::getMember)
.collect(Collectors.toList());

// 관련 멤버들의 socialId 가져오기
List<String> socialIds = matchingKeywords.stream()
.map(keyword -> keyword.getMember().getSocialId())
Expand All @@ -209,6 +219,20 @@ public BookInfoResponse createBook(BookCreateRequest request) throws MessagingEx
helper.setText("저번에 검색하신 책 " + book.getTitle() + "이 입고 되었습니다. 확인해주세요");
emailSender.send(mimeMessage);

for (Member member : matchedMembers) {
Notification notification = new Notification();
notification.setType("BOOK"); // 알림 타입
notification.setParamId(book.getBookId()); // 관련된 책 ID
notification.setExpiredDate(LocalDateTime.now().plusDays(7)); // 7일 후 만료
notification.setMessage("저번에 검색하신 책 '" + book.getTitle() + "'이 입고되었습니다. 확인해주세요!");
notification.setIsRead(false); // 읽지 않음 상태로 저장
notification.setMember(member); // 알림 받을 사용자

notificationRepository.save(notification);
}






Expand Down
2 changes: 1 addition & 1 deletion src/main/java/org/vsa/server/common/PrincipalDetails.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public Collection<? extends GrantedAuthority> getAuthorities() {
@Override
public String getAuthority() {
if(member!=null){
return member.getUserRole();
return member.getUserRole().toString();
}
return "없는 사용자입니다.";
}
Expand Down
8 changes: 4 additions & 4 deletions src/main/java/org/vsa/server/member/SecurityConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public class SecurityConfig {

private final JwtUtil jwtUtil;
private final MemberRepository memberRepository;
private final AuthenticationConfiguration authenticationConfiguration;;
private final AuthenticationConfiguration authenticationConfiguration;

public SecurityConfig(JwtUtil jwtUtil,AuthenticationConfiguration authenticationConfiguration, MemberRepository memberRepository) {
this.authenticationConfiguration = authenticationConfiguration;
Expand All @@ -31,14 +31,13 @@ public SecurityConfig(JwtUtil jwtUtil,AuthenticationConfiguration authentication
}

private static final String[] AUTH_WHITELIST = {
"/**",
"/v3/api-docs/**", // Swagger 3 documentation endpoint
"/swagger-ui/**", // Swagger UI endpoint
"/swagger-resources/**",
"/swagger-ui.html",
"/webjars/**", // Webjars used by Swagger UI
"/api/oauth", // 카카오 로그인 엔드포인트
"/api/logout"// 로그아웃 엔드포인트
"/api/logout", // 로그아웃 엔드포인트
};

@Bean
Expand All @@ -47,7 +46,8 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti
.csrf(csrf -> csrf.disable()) // CSRF 비활성화
.authorizeRequests(auth -> auth
.requestMatchers(CorsUtils::isPreFlightRequest).permitAll() // CORS 프리플라이트 요청 허용
.requestMatchers(AUTH_WHITELIST).permitAll() // 인증이 필요 없는 요청들
.requestMatchers("/api/inquire","/api/view").hasAnyRole("ADMIN", "STANDARD", "PREMIUM")
.requestMatchers(AUTH_WHITELIST).permitAll()// 인증이 필요 없는 요청들
.anyRequest().authenticated() // 그 외 요청은 인증 필요
)
.sessionManagement(session -> session
Expand Down
7 changes: 4 additions & 3 deletions src/main/java/org/vsa/server/member/entity/Member.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public class Member extends BaseEntity {
@OneToOne(mappedBy = "member", cascade = CascadeType.ALL)
private Subscribe subscribe;

private String userRole;
private UserRole userRole;

private String password;

Expand All @@ -46,8 +46,9 @@ public enum SocialType {




@Builder
public Member(Long subscribeId, String userRole, String password, String nickName, String socialId, SocialType socialType, String customerKey, String authKey, String billingKey) {
public Member(Long subscribeId, UserRole userRole, String password, String nickName, String socialId, SocialType socialType, String customerKey, String authKey, String billingKey) {
this.subscribeId = subscribeId;
this.userRole = userRole;
this.password = password;
Expand All @@ -58,4 +59,4 @@ public Member(Long subscribeId, String userRole, String password, String nickNam
this.authKey = authKey;
this.billingKey = billingKey;
}
}
}
5 changes: 5 additions & 0 deletions src/main/java/org/vsa/server/member/entity/UserRole.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package org.vsa.server.member.entity;

public enum UserRole {
NONE,STANDARD,PREMIUM,ADMIN
}
2 changes: 2 additions & 0 deletions src/main/java/org/vsa/server/member/service/KakaoService.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import org.vsa.server.member.dto.response.KakaoTokenResponseDto;
import org.vsa.server.member.entity.Member;
import org.vsa.server.member.entity.UserRole;
import org.vsa.server.member.repository.MemberRepository;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
Expand Down Expand Up @@ -160,6 +161,7 @@ public void registerMember(String email, String nickName){
if(duplicateMember==null){
initMember.setSocialId(email);
initMember.setNickName(nickName);
initMember.setUserRole(UserRole.NONE);

memberRepository.save(initMember);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.vsa.server.payment.config;

import org.vsa.server.common.FindLoginMember;
import org.vsa.server.payment.dto.PaymentRequest;
import org.vsa.server.payment.entity.Payment;
import org.vsa.server.payment.entity.PaymentStatus;
Expand Down Expand Up @@ -29,6 +30,9 @@ public class PaymentRetryJobConfig {

@Autowired
private PaymentRepository paymentRepository;
@Autowired
private FindLoginMember findLoginMember;


@Bean
public Job retryPaymentJob(JobRepository jobRepository, Step retryPaymentStep) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.vsa.server.payment.controller;

import org.vsa.server.common.FindLoginMember;
import org.vsa.server.payment.dto.PaymentRequest;
import org.vsa.server.payment.dto.WebhookRequest;
import org.vsa.server.payment.entity.PaymentType;
Expand Down Expand Up @@ -58,8 +59,12 @@ public String paymentPage(Model model) {
@PostMapping("/payments/complete")
@ResponseBody
public ResponseEntity<String> completePayment(@RequestBody PaymentRequest paymentRequest, @RequestParam(required = false) String imp_uid) {

String memberEmail = FindLoginMember.getCurrentUserId();


try {
paymentService.verifyAndSavePayment(paymentRequest, imp_uid);
paymentService.verifyAndSavePayment(paymentRequest, imp_uid, memberEmail);
return ResponseEntity.ok("Payment verified and saved successfully");
} catch (Exception e) {
return ResponseEntity.status(400).body("Payment verification failed: " + e.getMessage());
Expand Down
60 changes: 59 additions & 1 deletion src/main/java/org/vsa/server/payment/service/PaymentService.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package org.vsa.server.payment.service;

import org.vsa.server.member.entity.Member;
import org.vsa.server.member.entity.UserRole;
import org.vsa.server.member.repository.MemberRepository;
import org.vsa.server.payment.dto.PaymentRequest;
import org.vsa.server.payment.entity.Payment;
import org.vsa.server.payment.entity.PaymentStatus;
Expand Down Expand Up @@ -27,6 +30,8 @@ public class PaymentService {

@Autowired
private PaymentRepository paymentRepository;
@Autowired
private MemberRepository memberRepository;

private final ObjectMapper objectMapper = new ObjectMapper();

Expand Down Expand Up @@ -96,7 +101,57 @@ public JsonNode getPaymentData(String impUid) throws Exception {
}
}

// 3. 결제 금액 검증 및 결제 내역 저장
// 3. 결제 금액 검증 및 결제 내역 저장(일반 사용자 결제 저장 로직)
public void verifyAndSavePayment(PaymentRequest paymentRequest, String impUid, String userEmail) throws Exception {

// 1) test for UNPAID - 임의로 예외 발생
//throw new Exception("Intentionally failing payment verification for testing.");

// 2) 정상 동작에 대한 코드
JsonNode paymentData = getPaymentData(impUid);

//구매 사용자
Member paymentMember = memberRepository.findBySocialId(userEmail);

// 결제 금액 검증
int amountFromRequest = paymentRequest.amount(); // 수정된 부분
int amountFromIamport = paymentData.path("amount").asInt();

if(amountFromIamport == 7900){
paymentMember.setUserRole(UserRole.STANDARD);
}else if(amountFromIamport == 12900){
paymentMember.setUserRole(UserRole.PREMIUM);
}

memberRepository.save(paymentMember);



if (amountFromRequest != amountFromIamport) {
throw new Exception("Payment amount mismatch");
}

// 결제 내역 저장 또는 업데이트
Payment payment = paymentRepository.findByImpUid(impUid);
if (payment == null) {
payment = new Payment();
payment.setImpUid(impUid);
}

payment.setMerchantUid(paymentRequest.merchantUid());
payment.setAmount(amountFromIamport);
payment.setBuyerEmail(paymentData.path("buyer_email").asText());
payment.setBuyerName(paymentData.path("buyer_name").asText());
payment.setBuyerTel(paymentData.path("buyer_tel").asText());
payment.setStatus(paymentData.path("status").asText());
payment.setPaymentType(paymentRequest.paymentType());

paymentRepository.save(payment);
}



// 3. 결제 금액 검증 및 결제 내역 저장(Batch 사용 로직)
public void verifyAndSavePayment(PaymentRequest paymentRequest, String impUid) throws Exception {

// 1) test for UNPAID - 임의로 예외 발생
Expand Down Expand Up @@ -131,6 +186,9 @@ public void verifyAndSavePayment(PaymentRequest paymentRequest, String impUid) t
paymentRepository.save(payment);
}




public void markPaymentAsFailed(Payment payment) {
payment.setStatus(PaymentStatus.UNPAID.name());
paymentRepository.save(payment);
Expand Down
4 changes: 2 additions & 2 deletions src/main/resources/templates/payment.html
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ <h3>정기결제 등록에 실패했습니다.</h3>
var userEmail = document.getElementById('userEmail').value;
var userName = document.getElementById('userName').value;

var baseURL = "https://www.9dokme.p-e.kr/";
var baseURL = "https://localhost:8080/swagger-ui/";

var clientKey = "test_ck_d46qopOB89xLAkMQnk0YrZmM75y0";
var tossPayments = TossPayments(clientKey);
Expand Down Expand Up @@ -92,7 +92,7 @@ <h3>정기결제 등록에 실패했습니다.</h3>
pay_method: "card",
merchant_uid: "order_no_" + new Date().getTime(),
name: "9dokme 정기결제 등록",
amount: 9900,
amount: 7900,
buyer_email: userEmail,
buyer_name: userName,
buyer_tel: "010-1234-5678",
Expand Down

0 comments on commit 20bf321

Please # to comment.