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: 2 additions & 0 deletions src/main/java/com/sesac/boheommong/BoheommongApplication.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.PropertySource;
import org.springframework.scheduling.annotation.EnableScheduling;

@EnableScheduling
@SpringBootApplication
public class BoheommongApplication {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public Map<String, SseEmitter> findAllEmitterByUserId(String userId) {

@Override
public Map<String, Object> findAllEventCacheByUserId(String userId) {
return emitters.entrySet().stream()
return eventCache.entrySet().stream()
.filter(entry -> entry.getKey().startsWith(userId))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.sesac.boheommong.domain.tosspayment.controller;

import com.sesac.boheommong.domain.notification.service.NotificationService;
import lombok.RequiredArgsConstructor;
import net.minidev.json.JSONObject;
import net.minidev.json.parser.JSONParser;
import net.minidev.json.parser.ParseException;
Expand All @@ -21,10 +23,13 @@
import java.util.Base64;

@Controller
@RequiredArgsConstructor
public class WidgetController {

private final Logger logger = LoggerFactory.getLogger(this.getClass());

private final NotificationService notificationService;

@RequestMapping(value = "/confirm")
public ResponseEntity<JSONObject> confirmPayment(@RequestBody String jsonBody) throws Exception {

Expand Down Expand Up @@ -75,6 +80,28 @@ public ResponseEntity<JSONObject> confirmPayment(@RequestBody String jsonBody) t
JSONObject jsonObject = (JSONObject) parser.parse(reader);
responseStream.close();

// (3) 결제가 성공했다면 SSE 알림 발송
if (isSuccess) {
logger.info("결제 성공: orderId={}, amount={}", orderId, amount);

// 예: orderId로 사용자 이메일이나 userId를 DB에서 찾아오거나,
// 일단 임시로 하드코딩한 이메일로 알림 전송:
String userEmail = "dummyUser@somewhere.com";

// 알림 메시지 예시
String content = String.format("결제가 성공적으로 완료되었습니다. 결제금액: %s원", amount);

// SSE 알림 발행
notificationService.send(
userEmail, // 기존의 SSE 구독이 userEmail로 되어 있다면 이 값 사용
null, // 알림 타입 (사용한다면 ENUM 넣기)
content,
null // 클릭 시 이동할 URL
);
} else {
logger.warn("결제 실패: code={}, response={}", code, jsonObject);
}

return ResponseEntity.status(code).body(jsonObject);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,6 @@ public interface AutoPaymentRepository extends JpaRepository<AutoPayment, Long>
* AutoPayment 엔티티 내부의 user 필드(연관관계) → user.userId
*/
List<AutoPayment> findByUser_UserId(Long userId);

List<AutoPayment> findByDayOfMonth(int dayOfMonth);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package com.sesac.boheommong.domain.tosspayment.scheduler;

import com.sesac.boheommong.domain.tosspayment.entity.AutoPayment;
import com.sesac.boheommong.domain.tosspayment.repository.AutoPaymentRepository;
import com.sesac.boheommong.domain.notification.service.NotificationService;
import com.sesac.boheommong.domain.notification.enums.NotificationType;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.time.LocalDate;
import java.time.LocalTime;
import java.util.List;

@Component
@RequiredArgsConstructor
@Slf4j
public class AutoPaymentScheduler {

private final AutoPaymentRepository autoPaymentRepository;
private final NotificationService notificationService;

/**
* 매일 오전 0시(혹은 원하는 시각)에 실행
* - “내일 자동결제 예정”인 AutoPayment 찾아서, 알림 발행
*/
@Scheduled(cron = "0 0 0 * * *")
public void notifyBeforeAutoPayment() {
log.info("자동결제 전날 알림 스케줄러 실행...");

// (1) 오늘 날짜 계산
LocalDate today = LocalDate.now();

// (2) "내일" 자동이체가 예정된 AutoPayment 찾기
// - dayOfMonth == tomorrow's day
// - time은 결제 시각이지만, 전날 알림이므로 여기서는 dayOfMonth만 체크
int tomorrowDay = today.plusDays(1).getDayOfMonth();

// dayOfMonth가 tomorrowDay인 AutoPayment 목록 (DB 쿼리 방식은 상황에 따라 다름)
List<AutoPayment> autoPayments = autoPaymentRepository.findByDayOfMonth(tomorrowDay);

// (3) 알림 발행
for (AutoPayment ap : autoPayments) {
Long userId = ap.getUser().getUserId();

// 메시지 예시
String content = "내일 (" + tomorrowDay + "일) 자동결제가 예정되어 있습니다. (상품ID: " + ap.getProduct().getProductId() + ")";

// Redis를 통해 발행(멀티 서버)
notificationService.publishNotification(
userId,
content,
"/mypage/autoPayment" // 알림 클릭 시 이동할 URL (예시)
);

log.info("자동결제 전날 알림 발행 - userId={}, autoPaymentId={}", userId, ap.getId());
}
}
}
2 changes: 1 addition & 1 deletion src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ spring:
client-id: ${KAKAO_OAUTH_CLIENT_ID}
client-secret: ${KAKAO_OAUTH_CLIENT_SECRET}
authorization-grant-type: authorization_code
redirect-uri: https://boheommong.site/login/oauth2/code/kakao
redirect-uri: http://localhost:8080/login/oauth2/code/kakao
client-authentication-method: client_secret_post
scope:
- profile_nickname
Expand Down