Skip to content

Commit

Permalink
Feat: 카카오메시지 전송 프로토타입 생성 완료 (#161)
Browse files Browse the repository at this point in the history
feat: 카카오 메시지 (포인트 충전 접수, 포인트 충전 완료, 포인트 출금 접수, 포인트 출금 완료) 추가
  • Loading branch information
GitJIHO authored Nov 5, 2024
1 parent d1fce24 commit 2a9fb26
Show file tree
Hide file tree
Showing 7 changed files with 294 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import com.example.sinitto.auth.entity.KakaoToken;
import com.example.sinitto.auth.repository.KakaoTokenRepository;
import com.example.sinitto.common.exception.InvalidJwtException;
import com.example.sinitto.common.exception.NotFoundException;
import jakarta.transaction.Transactional;
import org.springframework.stereotype.Service;

Expand Down Expand Up @@ -36,7 +35,11 @@ public void saveKakaoToken(String email, KakaoTokenResponse kakaoTokenResponse)
@Transactional
public String getValidAccessTokenInServer(String email) {
KakaoToken kakaoToken = kakaoTokenRepository.findByMemberEmail(email)
.orElseThrow(() -> new NotFoundException("email에 해당하는 카카오 토큰이 없습니다."));
.orElse(null);

if (kakaoToken == null) {
return null;
}

if (kakaoToken.isAccessTokenExpired()) {
if (kakaoToken.isRefreshTokenExpired()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
public record KakaoProperties(
String clientId,
String redirectUri,
String devRedirectUri
String devRedirectUri,
String frontUri
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,254 @@
package com.example.sinitto.common.service;

import com.example.sinitto.auth.service.KakaoTokenService;
import com.example.sinitto.common.properties.KakaoProperties;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.RequestEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import java.net.URI;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;

@Service
public class KakaoMessageService {

private static final String KAKAO_SEND_ME_BASE_URL = "https://kapi.kakao.com/v2/api/talk/memo/default/send";
private static final String SINITTO_IMAGE_URL = "https://ifh.cc/g/GhjQyC.jpg";

private final KakaoTokenService kakaoTokenService;
private final RestTemplate restTemplate;
private final KakaoProperties kakaoProperties;

public KakaoMessageService(KakaoTokenService kakaoTokenService, RestTemplate restTemplate, KakaoProperties kakaoProperties) {
this.kakaoTokenService = kakaoTokenService;
this.restTemplate = restTemplate;
this.kakaoProperties = kakaoProperties;
}

public void sendPointChargeRequestReceivedMessage(String email, int point, String name) {
String accessToken = kakaoTokenService.getValidAccessTokenInServer(email);

if (accessToken == null) {
return;
}

HttpHeaders headers = new HttpHeaders();
headers.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE);
headers.setBearerAuth(accessToken);

String templateObject = String.format(
"{" +
"\"object_type\": \"feed\"," +
"\"content\": {" +
"\"title\": \"%s님의 포인트 충전 요청 접수\"," +
"\"description\": \"충전까지 최대 2~3영업일이 소요됩니다.\"," +
"\"image_url\": \"%s\"," +
"\"image_width\": 640," +
"\"image_height\": 640," +
"\"link\": {" +
"\"web_url\": \"%s\"," +
"\"mobile_web_url\": \"%s\"" +
"}" +
"}," +
"\"item_content\": {" +
"\"items\": [" +
"{\"item\": \"충전 요청\", \"item_op\": \"%d points\"}" +
"]" +
"}," +
"\"buttons\": [" +
"{" +
"\"title\": \"서비스 이용하기\"," +
"\"link\": {" +
"\"web_url\": \"%s\"," +
"\"mobile_web_url\": \"%s\"" +
"}" +
"}" +
"]" +
"}",
name, SINITTO_IMAGE_URL, kakaoProperties.frontUri(), kakaoProperties.frontUri(), point,
kakaoProperties.frontUri(), kakaoProperties.frontUri()
);


String encodedTemplateObject = URLEncoder.encode(templateObject, StandardCharsets.UTF_8);

RequestEntity<String> request = new RequestEntity<>(
"template_object=" + encodedTemplateObject,
headers, HttpMethod.POST, URI.create(KAKAO_SEND_ME_BASE_URL));

restTemplate.exchange(request, String.class);
}

public void sendPointChargeCompleteMessage(String email, int point, String name) {
String accessToken = kakaoTokenService.getValidAccessTokenInServer(email);

if (accessToken == null) {
return;
}

HttpHeaders headers = new HttpHeaders();
headers.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE);
headers.setBearerAuth(accessToken);

String templateObject = String.format(
"{" +
"\"object_type\": \"feed\"," +
"\"content\": {" +
"\"title\": \"%s님의 포인트 충전이 완료\"," +
"\"description\": \"서비스를 이용해주셔서 감사합니다.\"," +
"\"image_url\": \"%s\"," +
"\"image_width\": 640," +
"\"image_height\": 640," +
"\"link\": {" +
"\"web_url\": \"%s\"," +
"\"mobile_web_url\": \"%s\"" +
"}" +
"}," +
"\"item_content\": {" +
"\"items\": [" +
"{\"item\": \"충전된 포인트\", \"item_op\": \"%d points\"}" +
"]" +
"}," +
"\"buttons\": [" +
"{" +
"\"title\": \"서비스 이용하기\"," +
"\"link\": {" +
"\"web_url\": \"%s\"," +
"\"mobile_web_url\": \"%s\"" +
"}" +
"}" +
"]" +
"}",
name, SINITTO_IMAGE_URL, kakaoProperties.frontUri(), kakaoProperties.frontUri(), point,
kakaoProperties.frontUri(), kakaoProperties.frontUri()
);

String encodedTemplateObject = URLEncoder.encode(templateObject, StandardCharsets.UTF_8);

RequestEntity<String> request = new RequestEntity<>(
"template_object=" + encodedTemplateObject,
headers, HttpMethod.POST, URI.create(KAKAO_SEND_ME_BASE_URL));

restTemplate.exchange(request, String.class);
}

public void sendPointWithdrawRequestReceivedMessage(String email, int point, String name, String bankName, String accountNumber) {
String accessToken = kakaoTokenService.getValidAccessTokenInServer(email);

if (accessToken == null) {
return;
}

HttpHeaders headers = new HttpHeaders();
headers.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE);
headers.setBearerAuth(accessToken);

String templateObject = String.format(
"{" +
"\"object_type\": \"feed\"," +
"\"content\": {" +
"\"title\": \"%s님의 포인트 인출 요청 접수\"," +
"\"description\": \"인출에는 최대 2~3영업일이 소요됩니다.\"," +
"\"image_url\": \"%s\"," +
"\"image_width\": 640," +
"\"image_height\": 640," +
"\"link\": {" +
"\"web_url\": \"%s\"," +
"\"mobile_web_url\": \"%s\"" +
"}" +
"}," +
"\"item_content\": {" +
"\"items\": [" +
"{\"item\": \"인출 포인트\", \"item_op\": \"%d points\"}," +
"{\"item\": \"인출 금액\", \"item_op\": \"%.0f 원\"}," +
"{\"item\": \"은행\", \"item_op\": \"%s\"}," +
"{\"item\": \"계좌번호\", \"item_op\": \"%s\"}," +
"{\"item\": \"성명\", \"item_op\": \"%s\"}" +
"]" +
"}," +
"\"buttons\": [" +
"{" +
"\"title\": \"서비스 이용하기\"," +
"\"link\": {" +
"\"web_url\": \"%s\"," +
"\"mobile_web_url\": \"%s\"" +
"}" +
"}" +
"]" +
"}",
name, SINITTO_IMAGE_URL, kakaoProperties.frontUri(), kakaoProperties.frontUri(), point, point * 0.95,
bankName, accountNumber, name, kakaoProperties.frontUri(), kakaoProperties.frontUri()
);


String encodedTemplateObject = URLEncoder.encode(templateObject, StandardCharsets.UTF_8);

RequestEntity<String> request = new RequestEntity<>(
"template_object=" + encodedTemplateObject,
headers, HttpMethod.POST, URI.create(KAKAO_SEND_ME_BASE_URL));

restTemplate.exchange(request, String.class);
}

public void sendPointWithdrawCompleteMessage(String email, int point, String name, String bankName, String accountNumber) {
String accessToken = kakaoTokenService.getValidAccessTokenInServer(email);

if (accessToken == null) {
return;
}

HttpHeaders headers = new HttpHeaders();
headers.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE);
headers.setBearerAuth(accessToken);

String templateObject = String.format(
"{" +
"\"object_type\": \"feed\"," +
"\"content\": {" +
"\"title\": \"%s님의 포인트 인출 완료\"," +
"\"description\": \"서비스를 이용해주셔서 감사합니다.\"," +
"\"image_url\": \"%s\"," +
"\"image_width\": 640," +
"\"image_height\": 640," +
"\"link\": {" +
"\"web_url\": \"%s\"," +
"\"mobile_web_url\": \"%s\"" +
"}" +
"}," +
"\"item_content\": {" +
"\"items\": [" +
"{\"item\": \"인출 포인트\", \"item_op\": \"%d points\"}," +
"{\"item\": \"인출 금액\", \"item_op\": \"%.0f 원\"}," +
"{\"item\": \"은행\", \"item_op\": \"%s\"}," +
"{\"item\": \"계좌번호\", \"item_op\": \"%s\"}," +
"{\"item\": \"성명\", \"item_op\": \"%s\"}" +
"]" +
"}," +
"\"buttons\": [" +
"{" +
"\"title\": \"서비스 이용하기\"," +
"\"link\": {" +
"\"web_url\": \"%s\"," +
"\"mobile_web_url\": \"%s\"" +
"}" +
"}" +
"]" +
"}",
name, SINITTO_IMAGE_URL, kakaoProperties.frontUri(), kakaoProperties.frontUri(), point, point * 0.95,
bankName, accountNumber, name, kakaoProperties.frontUri(), kakaoProperties.frontUri()
);

String encodedTemplateObject = URLEncoder.encode(templateObject, StandardCharsets.UTF_8);

RequestEntity<String> request = new RequestEntity<>(
"template_object=" + encodedTemplateObject,
headers, HttpMethod.POST, URI.create(KAKAO_SEND_ME_BASE_URL));

restTemplate.exchange(request, String.class);
}

}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.example.sinitto.point.service;

import com.example.sinitto.common.exception.NotFoundException;
import com.example.sinitto.common.service.KakaoMessageService;
import com.example.sinitto.member.entity.Member;
import com.example.sinitto.member.repository.MemberRepository;
import com.example.sinitto.point.dto.PointLogWithBankInfo;
Expand All @@ -25,12 +26,14 @@ public class PointAdminService {
private final PointRepository pointRepository;
private final SinittoBankInfoRepository sinittoBankInfoRepository;
private final MemberRepository memberRepository;
private final KakaoMessageService kakaoMessageService;

public PointAdminService(PointLogRepository pointLogRepository, PointRepository pointRepository, SinittoBankInfoRepository sinittoBankInfoRepository, MemberRepository memberRepository) {
public PointAdminService(PointLogRepository pointLogRepository, PointRepository pointRepository, SinittoBankInfoRepository sinittoBankInfoRepository, MemberRepository memberRepository, KakaoMessageService kakaoMessageService) {
this.pointLogRepository = pointLogRepository;
this.pointRepository = pointRepository;
this.sinittoBankInfoRepository = sinittoBankInfoRepository;
this.memberRepository = memberRepository;
this.kakaoMessageService = kakaoMessageService;
}

@Transactional(readOnly = true)
Expand Down Expand Up @@ -76,6 +79,8 @@ public void earnPointAndChangeToChargeComplete(Long pointLogId) {

pointLog.changeStatusToChargeComplete();
point.earn(pointLog.getPrice());

kakaoMessageService.sendPointChargeCompleteMessage(pointLog.getMember().getEmail(), pointLog.getPrice(), pointLog.getMember().getName());
}

@Transactional
Expand Down Expand Up @@ -103,6 +108,9 @@ public void changeWithdrawLogToComplete(Long pointLogId) {
.orElseThrow(() -> new NotFoundException("포인트 로그를 찾을 수 없습니다."));

pointLog.changeStatusToWithdrawComplete();

SinittoBankInfo sinittoBankInfo = sinittoBankInfoRepository.findByMemberId(pointLog.getMember().getId()).orElseThrow(() -> new NotFoundException("시니또의 은행 계좌 정보가 없습니다."));
kakaoMessageService.sendPointWithdrawCompleteMessage(pointLog.getMember().getEmail(), pointLog.getPrice(), pointLog.getMember().getName(), sinittoBankInfo.getBankName(), sinittoBankInfo.getAccountNumber());
}

@Transactional
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.example.sinitto.common.exception.BadRequestException;
import com.example.sinitto.common.exception.ForbiddenException;
import com.example.sinitto.common.exception.NotFoundException;
import com.example.sinitto.common.service.KakaoMessageService;
import com.example.sinitto.member.entity.Member;
import com.example.sinitto.member.repository.MemberRepository;
import com.example.sinitto.point.dto.PointChargeResponse;
Expand All @@ -12,6 +13,7 @@
import com.example.sinitto.point.entity.PointLog;
import com.example.sinitto.point.repository.PointLogRepository;
import com.example.sinitto.point.repository.PointRepository;
import com.example.sinitto.sinitto.entity.SinittoBankInfo;
import com.example.sinitto.sinitto.repository.SinittoBankInfoRepository;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
Expand All @@ -25,12 +27,14 @@ public class PointService {
private final PointRepository pointRepository;
private final PointLogRepository pointLogRepository;
private final SinittoBankInfoRepository sinittoBankInfoRepository;
private final KakaoMessageService kakaoMessageService;

public PointService(MemberRepository memberRepository, PointRepository pointRepository, PointLogRepository pointLogRepository, SinittoBankInfoRepository sinittoBankInfoRepository) {
public PointService(MemberRepository memberRepository, PointRepository pointRepository, PointLogRepository pointLogRepository, SinittoBankInfoRepository sinittoBankInfoRepository, KakaoMessageService kakaoMessageService) {
this.memberRepository = memberRepository;
this.pointRepository = pointRepository;
this.pointLogRepository = pointLogRepository;
this.sinittoBankInfoRepository = sinittoBankInfoRepository;
this.kakaoMessageService = kakaoMessageService;
}

@Transactional(readOnly = true)
Expand Down Expand Up @@ -68,6 +72,8 @@ public PointChargeResponse savePointChargeRequest(Long memberId, int price) {

pointLogRepository.save(new PointLog(PointLog.Content.CHARGE_REQUEST.getMessage(), member, price, PointLog.Status.CHARGE_REQUEST));

kakaoMessageService.sendPointChargeRequestReceivedMessage(member.getEmail(), price, member.getName());

return new PointChargeResponse(member.getDepositMessage());
}

Expand Down Expand Up @@ -95,6 +101,9 @@ public void savePointWithdrawRequest(Long memberId, int price) {
point.deduct(price);

pointLogRepository.save(new PointLog(PointLog.Content.WITHDRAW_REQUEST.getMessage(), member, price, PointLog.Status.WITHDRAW_REQUEST));

SinittoBankInfo sinittoBankInfo = sinittoBankInfoRepository.findByMemberId(memberId).orElseThrow(() -> new NotFoundException("시니또의 은행 계좌 정보가 없습니다."));
kakaoMessageService.sendPointWithdrawRequestReceivedMessage(member.getEmail(), price, member.getName(), sinittoBankInfo.getBankName(), sinittoBankInfo.getAccountNumber());
}

@Transactional
Expand Down
Loading

0 comments on commit 2a9fb26

Please sign in to comment.