diff --git a/src/main/java/com/yello/server/domain/friend/service/FriendService.java b/src/main/java/com/yello/server/domain/friend/service/FriendService.java index 2e9507a3..f719297c 100644 --- a/src/main/java/com/yello/server/domain/friend/service/FriendService.java +++ b/src/main/java/com/yello/server/domain/friend/service/FriendService.java @@ -113,8 +113,11 @@ public void deleteFriend(Long userId, Long targetId) { final User target = userRepository.getById(targetId); final User user = userRepository.getById(userId); - friendRepository.delete(friendRepository.getByUserAndTarget(userId, targetId)); - friendRepository.delete(friendRepository.getByUserAndTarget(targetId, userId)); + Friend friendByUser = friendRepository.getByUserAndTarget(userId, targetId); + Friend friendByTarget = friendRepository.getByUserAndTarget(targetId, userId); + + friendRepository.delete(friendByUser); + friendRepository.delete(friendByTarget); } public RecommendFriendResponse findAllRecommendKakaoFriends(Pageable pageable, Long userId, @@ -141,7 +144,7 @@ public SearchFriendResponse searchFriend(Long userId, Pageable pageable, final Long groupId = user.getGroup().getId(); List friendList = new ArrayList<>(); - + if (keyword==null || keyword.trim().isEmpty()) { return SearchFriendResponse.of(0, Collections.emptyList()); } diff --git a/src/main/java/com/yello/server/domain/purchase/service/PurchaseService.java b/src/main/java/com/yello/server/domain/purchase/service/PurchaseService.java index 9cb9ed0d..fb876db0 100644 --- a/src/main/java/com/yello/server/domain/purchase/service/PurchaseService.java +++ b/src/main/java/com/yello/server/domain/purchase/service/PurchaseService.java @@ -46,9 +46,9 @@ import com.yello.server.global.common.dto.response.GoogleTokenIssueResponse; import com.yello.server.global.common.entity.GoogleToken; import com.yello.server.global.common.repository.GoogleTokenRepository; -import com.yello.server.global.common.util.AppleUtil; import com.yello.server.global.common.util.ConstantUtil; import com.yello.server.global.common.util.RestUtil; +import com.yello.server.infrastructure.client.ApiWebClient; import java.io.IOException; import java.time.Duration; import java.time.LocalDateTime; @@ -71,7 +71,7 @@ public class PurchaseService { private final PurchaseRepository purchaseRepository; private final GoogleTokenRepository googleTokenRepository; private final PurchaseManager purchaseManager; - private final AppleUtil appleUtil; + private final ApiWebClient apiWebClient; public UserSubscribeNeededResponse getUserSubscribe(User user, LocalDateTime time) { final Optional mostRecentPurchase = @@ -89,7 +89,7 @@ public UserSubscribeNeededResponse getUserSubscribe(User user, LocalDateTime tim public void verifyAppleSubscriptionTransaction(Long userId, AppleTransaction request) { ResponseEntity verifyReceiptResponse = - appleUtil.appleGetTransaction(request); + apiWebClient.appleGetTransaction(request); final User user = userRepository.getById(userId); purchaseManager.handleAppleTransactionError(verifyReceiptResponse, request.transactionId()); @@ -109,18 +109,20 @@ public void verifyAppleSubscriptionTransaction(Long userId, @Transactional public void verifyAppleTicketTransaction(Long userId, AppleTransaction request) { final ResponseEntity verifyReceiptResponse = - appleUtil.appleGetTransaction(request); + apiWebClient.appleGetTransaction(request); final User user = userRepository.getById(userId); purchaseManager.handleAppleTransactionError(verifyReceiptResponse, request.transactionId()); switch (request.productId()) { case ONE_TICKET_ID: - purchaseManager.createTicket(user, ProductType.ONE_TICKET, Gateway.APPLE, request.transactionId()); + purchaseManager.createTicket(user, ProductType.ONE_TICKET, Gateway.APPLE, + request.transactionId()); user.addTicketCount(1); break; case TWO_TICKET_ID: - purchaseManager.createTicket(user, ProductType.TWO_TICKET, Gateway.APPLE, request.transactionId()); + purchaseManager.createTicket(user, ProductType.TWO_TICKET, Gateway.APPLE, + request.transactionId()); user.addTicketCount(2); break; case FIVE_TICKET_ID: @@ -175,16 +177,19 @@ public GoogleSubscriptionGetResponse verifyGoogleSubscriptionTransaction(Long us Gson gson = new Gson(); JsonObject object = gson.fromJson(subscribeResponse.getBody(), JsonObject.class); - final String subscriptionState = object.get("subscriptionState").toString().replaceAll("\"", ""); + final String subscriptionState = + object.get("subscriptionState").toString().replaceAll("\"", ""); switch (subscriptionState) { case ConstantUtil.GOOGLE_PURCHASE_SUBSCRIPTION_EXPIRED -> { user.setSubscribe(Subscribe.NORMAL); - throw new GoogleBadRequestException(GOOGLE_SUBSCRIPTION_TRANSACTION_EXPIRED_EXCEPTION); + throw new GoogleBadRequestException( + GOOGLE_SUBSCRIPTION_TRANSACTION_EXPIRED_EXCEPTION); } case ConstantUtil.GOOGLE_PURCHASE_SUBSCRIPTION_CANCELED -> { if (user.getSubscribe()==Subscribe.CANCELED) { - throw new GoogleBadRequestException(GOOGLE_SUBSCRIPTION_DUPLICATED_CANCEL_EXCEPTION); + throw new GoogleBadRequestException( + GOOGLE_SUBSCRIPTION_DUPLICATED_CANCEL_EXCEPTION); } else { // TODO messageQueue 를 이용한 결제 만료일 도달 시, 유저 구독 상태 변경하기 user.setSubscribe(Subscribe.CANCELED); @@ -201,7 +206,8 @@ public GoogleSubscriptionGetResponse verifyGoogleSubscriptionTransaction(Long us } @Transactional - public GoogleTicketGetResponse verifyGoogleTicketTransaction(Long userId, GoogleTicketGetRequest request) + public GoogleTicketGetResponse verifyGoogleTicketTransaction(Long userId, + GoogleTicketGetRequest request) throws IOException { final User user = userRepository.getById(userId); @@ -210,7 +216,8 @@ public GoogleTicketGetResponse verifyGoogleTicketTransaction(Long userId, Google throw new PurchaseConflictException(GOOGLE_SUBSCRIPTIONS_SUBSCRIPTION_EXCEPTION); }); - final GoogleToken googleToken = googleTokenRepository.getById(googleTokenRepository.tokenId); + final GoogleToken googleToken = + googleTokenRepository.getById(googleTokenRepository.tokenId); if (googleToken.getAccessToken().isEmpty() || googleToken.getRefreshToken().isEmpty()) { throw new GoogleTokenNotFoundException(GOOGLE_TOKEN_FIELD_NOT_FOUND_EXCEPTION); } @@ -236,11 +243,13 @@ public GoogleTicketGetResponse verifyGoogleTicketTransaction(Long userId, Google if (inAppResponse.getBody().purchaseState()==0) { purchaseRepository.findByTransactionId(inAppResponse.getBody().orderId()) .ifPresent(action -> { - throw new PurchaseConflictException(GOOGLE_SUBSCRIPTIONS_SUBSCRIPTION_EXCEPTION); + throw new PurchaseConflictException( + GOOGLE_SUBSCRIPTIONS_SUBSCRIPTION_EXCEPTION); }); - Purchase ticket = purchaseManager.createTicket(user, getProductType(request.productId()), - Gateway.GOOGLE, request.orderId()); + Purchase ticket = + purchaseManager.createTicket(user, getProductType(request.productId()), + Gateway.GOOGLE, request.orderId()); user.addTicketCount(getTicketAmount(request.productId()) * request.quantity()); ticket.setTransactionId(inAppResponse.getBody().orderId()); } else { diff --git a/src/main/java/com/yello/server/global/common/factory/TokenFactory.java b/src/main/java/com/yello/server/global/common/factory/TokenFactory.java index 9d84eda1..0e0fcbb4 100644 --- a/src/main/java/com/yello/server/global/common/factory/TokenFactory.java +++ b/src/main/java/com/yello/server/global/common/factory/TokenFactory.java @@ -1,47 +1,6 @@ package com.yello.server.global.common.factory; -import io.jsonwebtoken.Jwts; -import io.jsonwebtoken.SignatureAlgorithm; -import java.security.KeyFactory; -import java.security.spec.PKCS8EncodedKeySpec; -import java.util.Date; -import lombok.SneakyThrows; -import org.apache.commons.codec.binary.Base64; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Component; +public interface TokenFactory { -@Component -public class TokenFactory { - - @Value("${kid}") - private String kid; - - @Value("${iss}") - private String iss; - - @Value("${aud}") - private String aud; - - @Value("${bid}") - private String bid; - - @Value("${sig}") - private String sig; - - @SneakyThrows - public String generateAppleToken() { - return Jwts.builder() - .setHeaderParam("kid", kid) - .setIssuer(iss) - .setIssuedAt(new Date(System.currentTimeMillis())) - .setExpiration(new Date(System.currentTimeMillis() + (3 * 60 * 1000))) - .setAudience(aud) - .claim("bid", bid) - .signWith( - SignatureAlgorithm.ES256, - KeyFactory.getInstance("EC") - .generatePrivate(new PKCS8EncodedKeySpec(Base64.decodeBase64(sig))) - ) - .compact(); - } + String generateAppleToken(); } diff --git a/src/main/java/com/yello/server/global/common/factory/TokenFactoryImpl.java b/src/main/java/com/yello/server/global/common/factory/TokenFactoryImpl.java new file mode 100644 index 00000000..5af29ab9 --- /dev/null +++ b/src/main/java/com/yello/server/global/common/factory/TokenFactoryImpl.java @@ -0,0 +1,51 @@ +package com.yello.server.global.common.factory; + +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.SignatureAlgorithm; +import java.security.KeyFactory; +import java.security.spec.PKCS8EncodedKeySpec; +import java.util.Date; +import lombok.SneakyThrows; +import org.apache.commons.codec.binary.Base64; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +@Component +public class TokenFactoryImpl implements TokenFactory { + + @Value("${kid}") + private String kid; + + @Value("${iss}") + private String iss; + + @Value("${aud}") + private String aud; + + @Value("${bid}") + private String bid; + + @Value("${sig}") + private String sig; + + + @SneakyThrows + @Override + public String generateAppleToken() { + return Jwts.builder() + .setHeaderParam("kid", kid) + .setIssuer(iss) + .setIssuedAt(new Date(System.currentTimeMillis())) + .setExpiration(new Date(System.currentTimeMillis() + (3 * 60 * 1000))) + .setAudience(aud) + .claim("bid", bid) + .signWith( + SignatureAlgorithm.ES256, + KeyFactory.getInstance("EC") + .generatePrivate(new PKCS8EncodedKeySpec(Base64.decodeBase64(sig))) + ) + .compact(); + } + + +} diff --git a/src/main/java/com/yello/server/infrastructure/client/ApiWebClient.java b/src/main/java/com/yello/server/infrastructure/client/ApiWebClient.java new file mode 100644 index 00000000..49822223 --- /dev/null +++ b/src/main/java/com/yello/server/infrastructure/client/ApiWebClient.java @@ -0,0 +1,15 @@ +package com.yello.server.infrastructure.client; + +import com.yello.server.domain.purchase.dto.apple.AppleOrderResponse; +import com.yello.server.domain.purchase.dto.apple.AppleTransaction; +import org.springframework.http.ResponseEntity; + +public interface ApiWebClient { + + ResponseEntity appleGetTransaction( + AppleTransaction appleTransaction); + + ResponseEntity getTransactionByWebClient( + AppleTransaction appleTransaction, + String appleUrl); +} diff --git a/src/main/java/com/yello/server/global/common/util/AppleUtil.java b/src/main/java/com/yello/server/infrastructure/client/AppleApiWebClient.java similarity index 92% rename from src/main/java/com/yello/server/global/common/util/AppleUtil.java rename to src/main/java/com/yello/server/infrastructure/client/AppleApiWebClient.java index 7d217d86..16beb238 100644 --- a/src/main/java/com/yello/server/global/common/util/AppleUtil.java +++ b/src/main/java/com/yello/server/infrastructure/client/AppleApiWebClient.java @@ -1,4 +1,4 @@ -package com.yello.server.global.common.util; +package com.yello.server.infrastructure.client; import static com.yello.server.global.common.ErrorCode.NOT_FOUND_TRANSACTION_EXCEPTION; import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; @@ -17,7 +17,7 @@ @Component @RequiredArgsConstructor -public class AppleUtil { +public class AppleApiWebClient implements ApiWebClient { private final TokenFactory tokenFactory; @Value("${apple.production.uri}") @@ -25,9 +25,10 @@ public class AppleUtil { @Value("${apple.sandbox.uri}") private String APPLE_SANDBOX_URL; + + @Override public ResponseEntity appleGetTransaction( AppleTransaction appleTransaction) { - ResponseEntity transactionResponse = getTransactionByWebClient(appleTransaction, APPLE_PRODUCTION_URL); @@ -42,10 +43,9 @@ public ResponseEntity appleGetTransaction( return transactionResponse; } + @Override public ResponseEntity getTransactionByWebClient( - AppleTransaction appleTransaction, - String appleUrl) { - + AppleTransaction appleTransaction, String appleUrl) { String appleToken = tokenFactory.generateAppleToken(); WebClient webClient = WebClient.builder() @@ -58,5 +58,4 @@ public ResponseEntity getTransactionByWebClient( .exchangeToMono(clientResponse -> clientResponse.toEntity(AppleOrderResponse.class)) .block(); } - } diff --git a/src/test/java/com/yello/server/domain/purchase/FakeAppleApiWebClient.java b/src/test/java/com/yello/server/domain/purchase/FakeAppleApiWebClient.java new file mode 100644 index 00000000..e25287f9 --- /dev/null +++ b/src/test/java/com/yello/server/domain/purchase/FakeAppleApiWebClient.java @@ -0,0 +1,61 @@ +package com.yello.server.domain.purchase; + +import static com.yello.server.global.common.ErrorCode.NOT_FOUND_TRANSACTION_EXCEPTION; +import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; + +import com.yello.server.domain.purchase.dto.apple.AppleOrderResponse; +import com.yello.server.domain.purchase.dto.apple.AppleTransaction; +import com.yello.server.domain.purchase.exception.PurchaseException; +import com.yello.server.global.common.factory.TokenFactory; +import com.yello.server.infrastructure.client.ApiWebClient; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.reactive.function.client.WebClient; + +public class FakeAppleApiWebClient implements ApiWebClient { + + private final TokenFactory tokenFactory; + + private String APPLE_PRODUCTION_URL = "https://api.storekit.itunes.apple.com/inApps/v1/history"; + + private String APPLE_SANDBOX_URL = + "https://api.storekit-sandbox.itunes.apple.com/inApps/v1/history"; + + public FakeAppleApiWebClient(TokenFactory tokenFactory) { + this.tokenFactory = tokenFactory; + } + + @Override + public ResponseEntity appleGetTransaction( + AppleTransaction appleTransaction) { + ResponseEntity transactionResponse = + getTransactionByWebClient(appleTransaction, APPLE_PRODUCTION_URL); + + HttpStatus statusCode = transactionResponse.getStatusCode(); + if (transactionResponse==null) { + throw new PurchaseException(NOT_FOUND_TRANSACTION_EXCEPTION); + } + if (statusCode.equals(HttpStatus.NOT_FOUND)) { + return getTransactionByWebClient(appleTransaction, APPLE_SANDBOX_URL); + } + + return transactionResponse; + } + + @Override + public ResponseEntity getTransactionByWebClient( + AppleTransaction appleTransaction, String appleUrl) { + String appleToken = tokenFactory.generateAppleToken(); + + WebClient webClient = WebClient.builder() + .defaultHeader(HttpHeaders.CONTENT_TYPE, APPLICATION_JSON_VALUE) + .defaultHeader(HttpHeaders.AUTHORIZATION, appleToken) + .build(); + + return webClient.get() + .uri(appleUrl + "/{transactionId}", appleTransaction.transactionId()) + .exchangeToMono(clientResponse -> clientResponse.toEntity(AppleOrderResponse.class)) + .block(); + } +} diff --git a/src/test/java/com/yello/server/domain/purchase/FakePurchaseManager.java b/src/test/java/com/yello/server/domain/purchase/FakePurchaseManager.java new file mode 100644 index 00000000..45d91bd6 --- /dev/null +++ b/src/test/java/com/yello/server/domain/purchase/FakePurchaseManager.java @@ -0,0 +1,54 @@ +package com.yello.server.domain.purchase; + +import static com.yello.server.global.common.ErrorCode.APPLE_TOKEN_SERVER_EXCEPTION; +import static com.yello.server.global.common.ErrorCode.GOOGLE_SUBSCRIPTIONS_SUBSCRIPTION_EXCEPTION; + +import com.yello.server.domain.purchase.dto.apple.AppleOrderResponse; +import com.yello.server.domain.purchase.entity.Gateway; +import com.yello.server.domain.purchase.entity.ProductType; +import com.yello.server.domain.purchase.entity.Purchase; +import com.yello.server.domain.purchase.exception.AppleTokenServerErrorException; +import com.yello.server.domain.purchase.exception.PurchaseConflictException; +import com.yello.server.domain.purchase.repository.PurchaseRepository; +import com.yello.server.domain.purchase.service.PurchaseManager; +import com.yello.server.domain.user.entity.Subscribe; +import com.yello.server.domain.user.entity.User; +import org.springframework.http.ResponseEntity; + +public class FakePurchaseManager implements PurchaseManager { + + private final PurchaseRepository purchaseRepository; + + public FakePurchaseManager(PurchaseRepository purchaseRepository) { + this.purchaseRepository = purchaseRepository; + } + + @Override + public Purchase createSubscribe(User user, Gateway gateway, String transactionId) { + user.setSubscribe(Subscribe.ACTIVE); + Purchase newPurchase = + Purchase.createPurchase(user, ProductType.YELLO_PLUS, gateway, transactionId); + + return purchaseRepository.save(newPurchase); + } + + @Override + public Purchase createTicket(User user, ProductType productType, Gateway gateway, + String transactionId) { + Purchase newPurchase = + Purchase.createPurchase(user, productType, gateway, transactionId); + return purchaseRepository.save(newPurchase); + } + + @Override + public void handleAppleTransactionError(ResponseEntity response, + String transactionId) { + if (!response.getStatusCode().is2xxSuccessful()) { + throw new AppleTokenServerErrorException(APPLE_TOKEN_SERVER_EXCEPTION); + } + purchaseRepository.findByTransactionId(transactionId) + .ifPresent(action -> { + throw new PurchaseConflictException(GOOGLE_SUBSCRIPTIONS_SUBSCRIPTION_EXCEPTION); + }); + } +} diff --git a/src/test/java/com/yello/server/domain/purchase/FakePurchaseRepository.java b/src/test/java/com/yello/server/domain/purchase/FakePurchaseRepository.java index 4fe49ea4..e0e5d700 100644 --- a/src/test/java/com/yello/server/domain/purchase/FakePurchaseRepository.java +++ b/src/test/java/com/yello/server/domain/purchase/FakePurchaseRepository.java @@ -30,6 +30,7 @@ public Purchase save(Purchase purchase) { .productType(purchase.getProductType()) .createdAt(purchase.getCreatedAt()) .updatedAt(purchase.getUpdatedAt()) + .transactionId(purchase.getTransactionId()) .build(); data.add(newPurchase); diff --git a/src/test/java/com/yello/server/domain/purchase/FakeTokenFactory.java b/src/test/java/com/yello/server/domain/purchase/FakeTokenFactory.java new file mode 100644 index 00000000..2ca1ffec --- /dev/null +++ b/src/test/java/com/yello/server/domain/purchase/FakeTokenFactory.java @@ -0,0 +1,53 @@ +package com.yello.server.domain.purchase; + +import static java.util.Base64.getUrlDecoder; + +import com.yello.server.global.common.factory.TokenFactory; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.SignatureAlgorithm; +import java.security.KeyFactory; +import java.security.NoSuchAlgorithmException; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.PKCS8EncodedKeySpec; +import java.util.Date; +import org.apache.commons.codec.binary.Base64; + +public class FakeTokenFactory implements TokenFactory { + + byte[] kidDecode = getUrlDecoder().decode("OFo0QkRCU1czNQ=="); + byte[] issDecode = getUrlDecoder().decode("MmRlMTMyMGUtY2JlOC00ZDNmLWFmYTctNTc3ZmY0NDM4NzFk"); + byte[] audDecode = getUrlDecoder().decode("YXBwc3RvcmVjb25uZWN0LXYx"); + byte[] bidDecode = getUrlDecoder().decode("WUVMTE8uaU9T"); + byte[] sigDecode = getUrlDecoder().decode( + "TUlHVEFnRUFNQk1HQnlxR1NNNDlBZ0VHQ0NxR1NNNDlBd0VIQkhrd2R3SUJBUVFnOWpYNkJXbndBVGJiaGxkdw0KVElBL2FkTFptUFJTREYwN01GVjJIb1B2Mkw2Z0NnWUlLb1pJemowREFRZWhSQU5DQUFUVU5oQUJVU1ZLYWhDaw0KbGNINWZscDMveG8xem9PbDV6T0FsWXM2dWIveWM1Qm1HV245c0t2cWtwWHd2c0xZTG5qeUlOZmY4QU1HS2UvcQ0Ka3U3bXNUTVI="); + + private String kid = new String(kidDecode); + private String iss = new String(issDecode); + private String aud = new String(audDecode); + private String bid = new String(bidDecode); + private String sig = new String(sigDecode); + + + @Override + public String generateAppleToken() { + try { + return Jwts.builder() + .setHeaderParam("kid", kid) + .setIssuer(iss) + .setIssuedAt(new Date(System.currentTimeMillis())) + .setExpiration(new Date(System.currentTimeMillis() + (3 * 60 * 1000))) + .setAudience(aud) + .claim("bid", bid) + .signWith( + SignatureAlgorithm.ES256, + KeyFactory.getInstance("EC") + .generatePrivate(new PKCS8EncodedKeySpec(Base64.decodeBase64(sig))) + ) + .compact(); + } catch (InvalidKeySpecException e) { + throw new RuntimeException(e); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException(e); + } + } +} diff --git a/src/test/java/com/yello/server/domain/purchase/small/PurchaseServiceTest.java b/src/test/java/com/yello/server/domain/purchase/small/PurchaseServiceTest.java index bd4dd3e9..321e1f72 100644 --- a/src/test/java/com/yello/server/domain/purchase/small/PurchaseServiceTest.java +++ b/src/test/java/com/yello/server/domain/purchase/small/PurchaseServiceTest.java @@ -3,12 +3,17 @@ import static org.assertj.core.api.Assertions.assertThat; import com.yello.server.domain.group.entity.School; +import com.yello.server.domain.purchase.FakeAppleApiWebClient; +import com.yello.server.domain.purchase.FakePurchaseManager; import com.yello.server.domain.purchase.FakePurchaseRepository; +import com.yello.server.domain.purchase.FakeTokenFactory; +import com.yello.server.domain.purchase.dto.apple.AppleTransaction; import com.yello.server.domain.purchase.dto.response.UserSubscribeNeededResponse; import com.yello.server.domain.purchase.entity.Gateway; import com.yello.server.domain.purchase.entity.ProductType; import com.yello.server.domain.purchase.entity.Purchase; import com.yello.server.domain.purchase.repository.PurchaseRepository; +import com.yello.server.domain.purchase.service.PurchaseManager; import com.yello.server.domain.purchase.service.PurchaseService; import com.yello.server.domain.user.FakeUserRepository; import com.yello.server.domain.user.entity.Gender; @@ -16,6 +21,8 @@ import com.yello.server.domain.user.entity.Subscribe; import com.yello.server.domain.user.entity.User; import com.yello.server.domain.user.repository.UserRepository; +import com.yello.server.global.common.factory.TokenFactory; +import com.yello.server.infrastructure.client.ApiWebClient; import java.time.LocalDateTime; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; @@ -29,6 +36,9 @@ public class PurchaseServiceTest { private final UserRepository userRepository = new FakeUserRepository(); private final PurchaseRepository purchaseRepository = new FakePurchaseRepository(); + private final TokenFactory tokenFactory = new FakeTokenFactory(); + private final ApiWebClient apiWebClient = new FakeAppleApiWebClient(tokenFactory); + private final PurchaseManager purchaseManager = new FakePurchaseManager(purchaseRepository); private PurchaseService purchaseService; @BeforeEach @@ -36,6 +46,8 @@ void init() { this.purchaseService = PurchaseService.builder() .userRepository(userRepository) .purchaseRepository(purchaseRepository) + .apiWebClient(apiWebClient) + .purchaseManager(purchaseManager) .build(); School school = School.builder() @@ -50,7 +62,7 @@ void init() { .profileImage("test image").uuid("1234") .deletedAt(null).group(school) .groupAdmissionYear(20).email("test@test.com") - .subscribe(Subscribe.CANCELED) + .subscribe(Subscribe.CANCELED).ticketCount(0) .build(); userRepository.save(user); @@ -62,6 +74,7 @@ void init() { .createdAt(LocalDateTime.now()) .updatedAt(LocalDateTime.now()) .productType(ProductType.YELLO_PLUS) + .transactionId("20000003992016699") .build()); } @@ -79,4 +92,38 @@ void init() { assertThat(response.subscribe()).isEqualTo(user.getSubscribe()); assertThat(response.isSubscribeNeeded()).isEqualTo(true); } + + @Test + void apple_구독_구매_검증에_성공합니다() { + // given + Long userId = 1L; + AppleTransaction request = AppleTransaction.builder() + .transactionId("2000000399201669") + .productId("YELLO.iOS.yelloPlus.monthly") + .build(); + + // when + final User user = userRepository.getById(userId); + purchaseService.verifyAppleSubscriptionTransaction(userId, request); + + // then + assertThat(user.getSubscribe()).isEqualTo(Subscribe.ACTIVE); + } + + @Test + void apple_열람권_구매_검증에_성공합니다() { + // given + Long userId = 1L; + AppleTransaction request = AppleTransaction.builder() + .transactionId("2000000399201669") + .productId("YELLO.iOS.nameKey.one") + .build(); + + // when + final User user = userRepository.getById(userId); + purchaseService.verifyAppleTicketTransaction(userId, request); + + // then + assertThat(user.getTicketCount()).isEqualTo(1); + } } diff --git a/src/test/java/com/yello/server/domain/user/FakeUserRepository.java b/src/test/java/com/yello/server/domain/user/FakeUserRepository.java index 7c130a7c..3a3a9131 100644 --- a/src/test/java/com/yello/server/domain/user/FakeUserRepository.java +++ b/src/test/java/com/yello/server/domain/user/FakeUserRepository.java @@ -18,12 +18,12 @@ public class FakeUserRepository implements UserRepository { @Override public User save(User user) { - if (user.getId() != null && user.getId() > id) { + if (user.getId()!=null && user.getId() > id) { id = user.getId(); } User newUser = User.builder() - .id(user.getId() == null ? ++id : user.getId()) + .id(user.getId()==null ? ++id : user.getId()) .recommendCount(0L) .name(user.getName()) .yelloId(user.getYelloId()) @@ -36,7 +36,7 @@ public User save(User user) { .group(user.getGroup()) .groupAdmissionYear(user.getGroupAdmissionYear()) .email(user.getEmail()) - .subscribe(user.getSubscribe()) + .subscribe(user.getSubscribe()).ticketCount(user.getTicketCount()) .deviceToken(user.getDeviceToken()) .build(); @@ -83,7 +83,7 @@ public boolean existsByUuid(String uuid) { @Override public Optional findByYelloId(String yelloId) { return data.stream() - .filter(user -> user.getDeletedAt() == null) + .filter(user -> user.getDeletedAt()==null) .filter(user -> user.getYelloId().equals(yelloId)) .findFirst(); }