From 5119fb25acaa3940e05eafb81f9297e3c1389e46 Mon Sep 17 00:00:00 2001 From: hyeonjeongs Date: Fri, 9 Feb 2024 22:36:36 +0900 Subject: [PATCH 1/5] YEL-213 [feat] admob verify --- build.gradle | 4 + .../event/controller/EventController.java | 23 ++- .../event/dto/request/AdmobSsvRequest.java | 45 ++++ .../domain/event/service/EventService.java | 22 ++ .../yello/server/global/common/ErrorCode.java | 1 + .../server/global/common/SuccessCode.java | 1 + .../resources/static/docs/add-friend.html | 161 ++++++++------- src/main/resources/static/docs/apple.html | 158 +++++++------- .../resources/static/docs/check-keyword.html | 158 +++++++------- .../static/docs/check-user-by-id.html | 158 +++++++------- .../resources/static/docs/check-user-v2.html | 158 +++++++------- .../resources/static/docs/check-user.html | 158 +++++++------- .../static/docs/check-vote-available.html | 158 +++++++------- .../resources/static/docs/create-vote.html | 158 +++++++------- .../resources/static/docs/delete-friend.html | 158 +++++++------- .../resources/static/docs/delete-user-v2.html | 158 +++++++------- .../resources/static/docs/delete-user.html | 158 +++++++------- .../resources/static/docs/device-token.html | 158 +++++++------- src/main/resources/static/docs/edit-user.html | 158 +++++++------- .../static/docs/find-friend-votes-v2.html | 158 +++++++------- .../static/docs/find-friend-votes.html | 158 +++++++------- .../resources/static/docs/find-friends.html | 158 +++++++------- .../static/docs/find-group-friends.html | 158 +++++++------- .../static/docs/find-kakao-friends.html | 158 +++++++------- .../resources/static/docs/find-notice.html | 158 +++++++------- .../static/docs/find-onboarding-friends.html | 158 +++++++------- .../resources/static/docs/find-question.html | 158 +++++++------- src/main/resources/static/docs/find-vote.html | 158 +++++++------- .../resources/static/docs/find-votes.html | 158 +++++++------- .../static/docs/get-unread-vote.html | 158 +++++++------- src/main/resources/static/docs/google.html | 158 +++++++------- src/main/resources/static/docs/index.html | 188 ++++++++--------- src/main/resources/static/docs/login.html | 158 +++++++------- src/main/resources/static/docs/overview.html | 158 +++++++------- src/main/resources/static/docs/pay.html | 158 +++++++------- .../resources/static/docs/purchase-check.html | 158 +++++++------- .../resources/static/docs/purchase-info.html | 158 +++++++------- .../resources/static/docs/reissue-token.html | 192 ++++++++++-------- .../static/docs/reveal-full-name.html | 158 +++++++------- .../resources/static/docs/reveal-name.html | 158 +++++++------- .../static/docs/search-department.html | 158 +++++++------- .../resources/static/docs/search-friend.html | 158 +++++++------- .../static/docs/search-high-class.html | 158 +++++++------- .../static/docs/search-high-name.html | 158 +++++++------- .../resources/static/docs/search-school.html | 158 +++++++------- .../static/docs/shuffle-friends.html | 158 +++++++------- src/main/resources/static/docs/signup.html | 158 +++++++------- src/main/resources/static/docs/sub-check.html | 158 +++++++------- .../resources/static/docs/user-data-get.html | 158 +++++++------- .../resources/static/docs/user-data-post.html | 158 +++++++------- .../static/docs/validate-yelloid.html | 158 +++++++------- 51 files changed, 3687 insertions(+), 3586 deletions(-) create mode 100644 src/main/java/com/yello/server/domain/event/dto/request/AdmobSsvRequest.java diff --git a/build.gradle b/build.gradle index 67629786..c9a03190 100644 --- a/build.gradle +++ b/build.gradle @@ -105,6 +105,10 @@ dependencies { annotationProcessor "jakarta.persistence:jakarta.persistence-api" implementation "jakarta.annotation:jakarta.annotation-api" implementation "com.querydsl:querydsl-codegen:${queryDslVersion}" + + // tink + implementation 'com.google.crypto.tink:tink-android:1.4.0-rc1' + implementation 'com.google.crypto.tink:apps-rewardedads:1.10.0' } asciidoctor { diff --git a/src/main/java/com/yello/server/domain/event/controller/EventController.java b/src/main/java/com/yello/server/domain/event/controller/EventController.java index 4fe61519..1005edca 100644 --- a/src/main/java/com/yello/server/domain/event/controller/EventController.java +++ b/src/main/java/com/yello/server/domain/event/controller/EventController.java @@ -1,10 +1,12 @@ package com.yello.server.domain.event.controller; +import static com.yello.server.global.common.ErrorCode.ADMOB_URI_BAD_REQUEST_EXCEPTION; import static com.yello.server.global.common.ErrorCode.IDEMPOTENCY_KEY_BAD_REQUEST_EXCEPTION; import static com.yello.server.global.common.ErrorCode.IDEMPOTENCY_KEY_INVALID_FORM_BAD_REQUEST_EXCEPTION; import static com.yello.server.global.common.SuccessCode.EVENT_JOIN_SUCCESS; import static com.yello.server.global.common.SuccessCode.EVENT_NOTICE_SUCCESS; import static com.yello.server.global.common.SuccessCode.EVENT_REWARD_SUCCESS; +import static com.yello.server.global.common.SuccessCode.VERIFY_ADMOB_SSV_SUCCESS; import static com.yello.server.global.common.util.ConstantUtil.IdempotencyKeyHeader; import com.fasterxml.jackson.core.JsonProcessingException; @@ -17,6 +19,8 @@ import com.yello.server.global.common.annotation.AccessTokenUser; import com.yello.server.global.common.dto.BaseResponse; import jakarta.servlet.http.HttpServletRequest; +import java.net.URI; +import java.net.URISyntaxException; import java.util.List; import java.util.UUID; import lombok.RequiredArgsConstructor; @@ -36,7 +40,8 @@ public class EventController { private final EventService eventService; @GetMapping("/v1/event") - public BaseResponse> getEvents(@AccessTokenUser User user) throws JsonProcessingException { + public BaseResponse> getEvents(@AccessTokenUser User user) + throws JsonProcessingException { val data = eventService.getEvents(user.getId()); return BaseResponse.success(EVENT_NOTICE_SUCCESS, data); } @@ -78,4 +83,20 @@ public BaseResponse rewardEvent(@AccessTokenUser User user, val data = eventService.rewardEvent(user.getId(), uuidIdempotencyKey); return BaseResponse.success(EVENT_REWARD_SUCCESS, data); } + + @GetMapping("/v1/admob/verify") + public BaseResponse verifyAdmobReward(HttpServletRequest request) { + URI uri; + try { + uri = + new URI(request.getScheme(), null, request.getServerName(), request.getServerPort(), + request.getRequestURI(), request.getQueryString(), null); + } catch (URISyntaxException e) { + throw new EventBadRequestException(ADMOB_URI_BAD_REQUEST_EXCEPTION); + } + + eventService.verifyAdmobReward(uri, request); + + return BaseResponse.success(VERIFY_ADMOB_SSV_SUCCESS); + } } diff --git a/src/main/java/com/yello/server/domain/event/dto/request/AdmobSsvRequest.java b/src/main/java/com/yello/server/domain/event/dto/request/AdmobSsvRequest.java new file mode 100644 index 00000000..52720afb --- /dev/null +++ b/src/main/java/com/yello/server/domain/event/dto/request/AdmobSsvRequest.java @@ -0,0 +1,45 @@ +package com.yello.server.domain.event.dto.request; + +import java.util.Arrays; +import java.util.Map; +import java.util.Optional; +import java.util.function.Function; +import lombok.Builder; + +@Builder +public record AdmobSsvRequest( + String customData, + String signature, + Long keyId, + String transactionId, + String rewardItem, + Integer rewardAmount + +) { + public static AdmobSsvRequest of(Map parameters ) { + Function getParameter = (key) -> + Optional.ofNullable(parameters.get(key)) + .flatMap(arr -> Arrays.stream(arr).findFirst()) + .orElse(""); + + long keyId = Optional.ofNullable(parameters.get("key_id")) + .flatMap(arr -> Arrays.stream(arr).findFirst()) + .map(Long::parseLong) + .orElse(0L); + + int rewardAmount = Optional.ofNullable(parameters.get("rewardAmount")) + .flatMap(arr -> Arrays.stream(arr).findFirst()) + .map(Integer::parseInt) + .orElse(0); + + return AdmobSsvRequest.builder() + .customData(getParameter.apply("customData")) + .signature(getParameter.apply("signature")) + .keyId(keyId) + .transactionId(getParameter.apply("transaction_id")) + .rewardItem(getParameter.apply("reward_item")) + .rewardAmount(rewardAmount) + .build(); + } + +} diff --git a/src/main/java/com/yello/server/domain/event/service/EventService.java b/src/main/java/com/yello/server/domain/event/service/EventService.java index 3ea52c66..710be7d4 100644 --- a/src/main/java/com/yello/server/domain/event/service/EventService.java +++ b/src/main/java/com/yello/server/domain/event/service/EventService.java @@ -10,6 +10,8 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.crypto.tink.apps.rewardedads.RewardedAdsVerifier; +import com.yello.server.domain.event.dto.request.AdmobSsvRequest; import com.yello.server.domain.event.dto.request.EventJoinRequest; import com.yello.server.domain.event.dto.response.EventResponse; import com.yello.server.domain.event.dto.response.EventRewardResponse; @@ -27,18 +29,22 @@ import com.yello.server.domain.event.repository.EventRepository; import com.yello.server.domain.user.entity.User; import com.yello.server.domain.user.repository.UserRepository; +import jakarta.servlet.http.HttpServletRequest; import jakarta.validation.constraints.NotEmpty; import jakarta.validation.constraints.NotNull; +import java.net.URI; import java.time.Duration; import java.time.OffsetTime; import java.time.ZonedDateTime; import java.util.ArrayList; import java.util.List; +import java.util.Map; import java.util.Optional; import java.util.Random; import java.util.UUID; import lombok.Getter; import lombok.RequiredArgsConstructor; +import lombok.SneakyThrows; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -203,6 +209,22 @@ public EventRewardResponse rewardEvent(Long userId, UUID uuidIdempotencyKey) thr return EventRewardResponse.of(eventInstanceReward); } + @SneakyThrows + public void verifyAdmobReward(URI uri, HttpServletRequest request) { + // admob 검증하기 + RewardedAdsVerifier verifier = new RewardedAdsVerifier.Builder() + .fetchVerifyingPublicKeysWith( + RewardedAdsVerifier.KEYS_DOWNLOADER_INSTANCE_PROD) + .build(); + verifier.verify(uri.toString()); + + // request 정보 가져오기 + Map parameters = request.getParameterMap(); + AdmobSsvRequest admobRequest = AdmobSsvRequest.of(parameters); + + // 보상하기 + } + private @NotNull EventRewardMapping selectRandomly(@NotEmpty List eventRewardMappingList) { // 전체 확률 합계를 계산합니다. int totalProbability = eventRewardMappingList.stream() diff --git a/src/main/java/com/yello/server/global/common/ErrorCode.java b/src/main/java/com/yello/server/global/common/ErrorCode.java index ca5f4adf..4b7dba2d 100644 --- a/src/main/java/com/yello/server/global/common/ErrorCode.java +++ b/src/main/java/com/yello/server/global/common/ErrorCode.java @@ -45,6 +45,7 @@ public enum ErrorCode { EVENT_DATE_BAD_REQUEST_EXCEPTION(BAD_REQUEST, "해당 이벤트는 현재 유효한 날짜가 아닙니다."), EVENT_TIME_BAD_REQUEST_EXCEPTION(BAD_REQUEST, "해당 이벤트는 현재 유효한 시간이 아닙니다."), EVENT_COUNT_BAD_REQUEST_EXCEPTION(BAD_REQUEST, "해당 이벤트는 보상 횟수가 전부 소진되었습니다."), + ADMOB_URI_BAD_REQUEST_EXCEPTION(BAD_REQUEST, "URI의 값이 올바르지 않습니다."), /** * 401 UNAUTHORIZED diff --git a/src/main/java/com/yello/server/global/common/SuccessCode.java b/src/main/java/com/yello/server/global/common/SuccessCode.java index 587db04e..cbc4d011 100644 --- a/src/main/java/com/yello/server/global/common/SuccessCode.java +++ b/src/main/java/com/yello/server/global/common/SuccessCode.java @@ -62,6 +62,7 @@ public enum SuccessCode { EVENT_REWARD_CREATE_ADMIN_SUCCESS(OK, "어드민 권한으로 이벤트 보상 생성에 성공하였습니다."), EVENT_JOIN_SUCCESS(OK, "이벤트 참여에 성공하였습니다."), EVENT_REWARD_SUCCESS(OK, "이벤트 보상에 성공하였습니다."), + VERIFY_ADMOB_SSV_SUCCESS(OK, "Admob ssv 검증에 성공하였습니다."), /** * 201 CREATED diff --git a/src/main/resources/static/docs/add-friend.html b/src/main/resources/static/docs/add-friend.html index 64bb0f9c..70ee9041 100644 --- a/src/main/resources/static/docs/add-friend.html +++ b/src/main/resources/static/docs/add-friend.html @@ -4,23 +4,25 @@ - + 친구 추가하기 - - - - + + + \ No newline at end of file diff --git a/src/main/resources/static/docs/login.html b/src/main/resources/static/docs/login.html index 79046842..fd84b612 100644 --- a/src/main/resources/static/docs/login.html +++ b/src/main/resources/static/docs/login.html @@ -4,23 +4,25 @@ - + 소셜 로그인 + + + +
+
+

이벤트 참여

+
+
+

요청

+
+
+
POST /api/v1/admob/reward HTTP/1.1
+Content-Type: application/json;charset=UTF-8
+Authorization: Bearer your-access-token
+IdempotencyKey: 87552f7c-9b62-4b12-b567-1bd062b09288
+Content-Length: 134
+
+{
+  "rewardType" : "ADMOB_POINT",
+  "randomType" : "FIXED",
+  "uuid" : "87552f7c-9b62-4b12-b567-1bd062b09288",
+  "rewardNumber" : 10
+}
+
+
+
+
+

request body

+
+
    +
  • +

    "rewardType": String → "ADMOB_POINT" | "ADMOB_MULTIPLE_POINT"

    +
    +
      +
    • +

      ADMOB_POINT : 광고 보고 10 포인트

      +
    • +
    • +

      ADMOB_MULTIPLE_POINT : 광고 보고 포인트 2배 이벤트

      +
    • +
    +
    +
  • +
  • +

    "randomType" : String → "FIXED" | "ADMOB_RANDOM"

    +
    +
      +
    • +

      FIXED : 고정값 (현재 이것만 사용)

      +
    • +
    • +

      ADMOB_RANDOM : 랜덤값 (추후 랜덤으로 바뀔 것 고려)

      +
    • +
    +
    +
  • +
  • +

    "uuid" : String → UUID4 형식만 적용

    +
  • +
  • +

    "rewardNumber" : Integer → 포인트인 경우 10, 투표 포인트 2배 이벤트인 경우 현재 투표 후 받은 포인트 보내줘야함

    +
  • +
+
+
+
+

응답

+
+
+
HTTP/1.1 200 OK
+Vary: Origin
+Vary: Access-Control-Request-Method
+Vary: Access-Control-Request-Headers
+Content-Type: application/json
+
+{
+  "status" : 201,
+  "message" : "Admob 광고 보고 보상받기에 성공했습니다.",
+  "data" : {
+    "rewardTag" : "ADMOB_POINT",
+    "rewardValue" : 10,
+    "rewardTitle" : "10 포인트를 얻었어요!",
+    "rewardImage" : "https://storage.googleapis.com/yelloworld/image/ticket-reward.svg"
+  }
+}
+
+
+
+
+

NOTE

+
+
    +
  • +

    Header에 무작위한 UUID4 값을 넣어주세요

    +
    +
      +
    • +

      예시) IdempotencyKey: 0397b5f3-ecdc-47d6-b5d7-2b1afcf00e87

      +
    • +
    +
    +
  • +
  • +

    주의사항

    +
    +
      +
    • +

      같은 멱등성키를 2번 요청하면, 400번 에러.

      +
    • +
    +
    +
  • +
  • +

    ADMOB

    +
    +
      +
    • +

      ADMOB 서버에 SSV(ServerSideVerification) Options의 customData에 입력한 것과 동일한 멱등성 키를 넘겨주세요.

      +
    • +
    +
    +
  • +
+
+
+
+

CHANGELOG

+
+
    +
  • +

    2024.02.11 릴리즈

    +
  • +
+
+
+
+
+
+ + + \ No newline at end of file