Skip to content
Closed
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 build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ dependencies {

// prometheus
implementation 'io.micrometer:micrometer-registry-prometheus'

implementation 'com.googlecode.json-simple:json-simple:1.1.1'
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

json-simple은 다소 오래된 라이브러리이며, Spring Boot 프로젝트에서는 이미 Jackson(ObjectMapper)이 기본적으로 포함되어 있습니다. 별도의 의존성을 추가하기보다는 프로젝트에 이미 포함된 Jackson을 활용하는 것을 권장합니다.

}

tasks.named('test') {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@
import org.springframework.stereotype.Repository;

@Repository
public interface EmailVerificationTokenRepository extends CrudRepository<EmailVerificationToken, String> {
public interface EmailVerificationTokenRepository extends CrudRepository<EmailVerificationToken, String> {
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@
import net.studioxai.studioxBe.domain.auth.dto.request.LoginRequest;
import net.studioxai.studioxBe.domain.auth.dto.request.PasswordResetRequest;
import net.studioxai.studioxBe.domain.auth.dto.request.SignUpRequest;
import net.studioxai.studioxBe.domain.auth.dto.response.EmailValidationResponse;
import net.studioxai.studioxBe.domain.auth.dto.response.LoginResponse;
import net.studioxai.studioxBe.domain.auth.dto.response.TokenResponse;
import net.studioxai.studioxBe.domain.auth.entity.VerifiedEmailCode;
import net.studioxai.studioxBe.domain.auth.repository.VerifiedEmailCodeRepository;
import net.studioxai.studioxBe.domain.folder.entity.Folder;
import net.studioxai.studioxBe.domain.folder.service.FolderService;
import net.studioxai.studioxBe.domain.payment.entity.UserPlan;
import net.studioxai.studioxBe.domain.payment.repository.UserPlanRepository;
import net.studioxai.studioxBe.domain.user.entity.enums.RegisterPath;
import net.studioxai.studioxBe.domain.user.entity.User;
import net.studioxai.studioxBe.domain.auth.exception.AuthErrorCode;
Expand Down Expand Up @@ -42,6 +43,7 @@ public class AuthService {

public static final String DEFAULT_PROFILE_IMAGE_URL = "profile-example.com";
private final VerifiedEmailCodeRepository verifiedEmailCodeRepository;
private final UserPlanRepository userPlanRepository;

@Transactional
public void resetPassword(PasswordResetRequest passwordResetRequest) {
Expand Down Expand Up @@ -83,6 +85,7 @@ public LoginResponse signUp(SignUpRequest signUpRequest) {

userRepository.saveAndFlush(user);
provisioningFolder(user);
provisionPlan(user);

return buildLoginResponse(user);
}
Expand All @@ -103,6 +106,11 @@ protected void provisioningFolder(User user) {
String folderName = user.getUsername();
Folder folder = folderService.createRootFolder(folderName, user);
}
@Transactional
protected void provisionPlan(User user) {
UserPlan userPlan = UserPlan.createFree(user);
userPlanRepository.save(userPlan);
}

public User getUserByEmailOrThrow(String email) {
return userRepository.findByEmail(email).orElseThrow(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,9 @@ private User findOrCreateGoogleUser(GoogleUserInfoResponse userInfo) {
passwordEncoder.encode(UUID.randomUUID().toString()),
resolveProfileImage(userInfo)
);
userRepository.save(user);
userRepository.saveAndFlush(user);
authService.provisioningFolder(user);
authService.provisionPlan(user);
return user;
});
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package net.studioxai.studioxBe.domain.payment.controller;

import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import net.studioxai.studioxBe.domain.payment.dto.request.BillingKeyAuthKeyCreateRequest;
import net.studioxai.studioxBe.domain.payment.dto.request.BillingKeyCardCreateRequest;
import net.studioxai.studioxBe.domain.payment.entity.enums.Plan;
import net.studioxai.studioxBe.domain.payment.service.BillingKeyService;
import net.studioxai.studioxBe.global.jwt.JwtUserPrincipal;
import net.studioxai.studioxBe.global.util.IpUtil;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.*;

import java.io.IOException;

@RestController
@RequestMapping("/api")
@RequiredArgsConstructor
public class BillingKeyController {
private final IpUtil ipUtil;
private final BillingKeyService billingKeyService;

@PostMapping("/v1/payment/billingKey/authKey")
public void createBillingKeyAuthKey(
@AuthenticationPrincipal JwtUserPrincipal principal,
@RequestParam Plan plan,
@RequestBody BillingKeyAuthKeyCreateRequest billingKeyCreateRequest,
HttpServletRequest request
) throws IOException {
String clientIp = ipUtil.getClientIp(request);
billingKeyService.createBillingKeyWithAuthKey(principal.userId(), billingKeyCreateRequest, plan, clientIp);
}

@PostMapping("/v1/payment/billingKey/card")
public void createBillingKeyCard(
@AuthenticationPrincipal JwtUserPrincipal principal,
@RequestParam Plan plan,
@RequestBody BillingKeyCardCreateRequest billingKeyCreateRequest,
HttpServletRequest request
) throws IOException {
String clientIp = ipUtil.getClientIp(request);
billingKeyService.createBillingKeyWithCard(principal.userId(), billingKeyCreateRequest, plan, clientIp);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package net.studioxai.studioxBe.domain.payment.dto;

public record CardDto(
String issuerCode,
String acquirerCode,
String number,
String cardType,
String ownerType
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package net.studioxai.studioxBe.domain.payment.dto;

public record FailureDto(
String code,
String message
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package net.studioxai.studioxBe.domain.payment.dto;

import jakarta.validation.constraints.Pattern;
import jakarta.validation.constraints.Size;

public record ThreeDsValueDto (
@Size(max = 2048, message = "masking 값은 2048자 이하여야 합니다.")
@Pattern(
regexp = "^[A-Za-z0-9+/=_\\-.*]*$",
message = "masking 값에 허용되지 않는 문자가 포함되어 있습니다."
)
String masking,

@Size(max = 2048, message = "plain 값은 2048자 이하여야 합니다.")
@Pattern(
regexp = "^[A-Za-z0-9+/=_\\-.*]*$",
message = "plain 값에 허용되지 않는 문자가 포함되어 있습니다."
)
String plain
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package net.studioxai.studioxBe.domain.payment.dto;

public record TransferDto(
String bankName,
String bankAccountNumber
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package net.studioxai.studioxBe.domain.payment.dto.request;

import jakarta.validation.constraints.NotBlank;
import net.studioxai.studioxBe.domain.payment.entity.BillingKey;
import net.studioxai.studioxBe.domain.payment.entity.PaymentHistory;
import net.studioxai.studioxBe.domain.payment.entity.enums.Plan;
import net.studioxai.studioxBe.domain.user.entity.User;

import java.math.BigDecimal;

public record BillingApprovalRequest(
@NotBlank
String billingKey,
@NotBlank
long amount,
@NotBlank
String customerKey,
@NotBlank
String orderId,
@NotBlank
String orderName,
String customerEmail,
String customerName,
String customerIp,
int taxFreeAmount,
int taxExemptionAmount
) {
public static BillingApprovalRequest of(
User user,
Plan plan,
long amount,
BillingKey billingKey,
PaymentHistory paymentHistory,
String customerIp,
int taxFreeAmount,
int taxExemptionAmount
) {
return new BillingApprovalRequest(
billingKey.getBillingKey(),
amount,
user.getCustomerKey(),
paymentHistory.getOrderId(),
plan.name(),
user.getEmail(),
user.getUsername(),
customerIp,
taxFreeAmount,
taxExemptionAmount
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package net.studioxai.studioxBe.domain.payment.dto.request;

import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Pattern;
import jakarta.validation.constraints.Size;

public record BillingKeyAuthKeyCreateRequest(
@NotBlank(message = "authKey는 필수입니다.")
@Size(max = 300, message = "authKey는 300자 이하여야 합니다.")
String authKey,

@NotBlank(message = "customerKey는 필수입니다.")
@Size(min = 2, max = 300, message = "customerKey는 2자 이상 300자 이하여야 합니다.")
@Pattern(
regexp = "^[A-Za-z0-9\\-_=\\.@]+$",
message = "customerKey는 영문, 숫자, -, _, =, ., @ 만 사용할 수 있습니다."
)
String customerKey
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package net.studioxai.studioxBe.domain.payment.dto.request;

import jakarta.validation.Valid;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Pattern;
import jakarta.validation.constraints.Size;
import net.studioxai.studioxBe.domain.payment.dto.ThreeDsValueDto;

public record BillingKeyCardCreateRequest (
@NotBlank(message = "카드 만료 월은 필수입니다.")
@Pattern(
regexp = "^(0[1-9]|1[0-2])$",
message = "카드 만료 월은 01~12 형식이어야 합니다."
)
String cardExpirationMonth,

@NotBlank(message = "카드 만료 연도는 필수입니다.")
@Pattern(
regexp = "^\\d{2}$",
message = "카드 만료 연도는 YY 형식의 숫자 2자리여야 합니다."
)
String cardExpirationYear,

@NotBlank(message = "카드 번호는 필수입니다.")
@Pattern(
regexp = "^\\d{12,19}$",
message = "카드 번호는 숫자 12~19자리여야 합니다."
)
String cardNumber,

@NotBlank(message = "카드 비밀번호 앞 2자리는 필수입니다.")
@Pattern(
regexp = "^\\d{2}$",
message = "카드 비밀번호는 숫자 2자리여야 합니다."
)
String cardPassword,

@NotBlank(message = "고객 식별번호는 필수입니다.")
@Pattern(
regexp = "^(\\d{6}|\\d{10})$",
message = "고객 식별번호는 생년월일 6자리 또는 사업자등록번호 10자리여야 합니다."
)
String customerIdentityNumber,

@NotBlank(message = "customerKey는 필수입니다.")
@Size(min = 2, max = 50, message = "customerKey는 2자 이상 50자 이하여야 합니다.")
@Pattern(
regexp = "^[A-Za-z0-9\\-_=\\.@]+$",
message = "customerKey는 영문, 숫자, -, _, =, ., @ 만 사용할 수 있습니다."
)
String customerKey,

@Email(message = "이메일 형식이 올바르지 않습니다.")
@Size(max = 100, message = "이메일은 100자 이하여야 합니다.")
String customerEmail,

@Size(max = 100, message = "고객 이름은 100자 이하여야 합니다.")
String customerName,

@Valid
ThreeDsValueDto cavv,

@Valid
ThreeDsValueDto eci,

@Valid
ThreeDsValueDto xid
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package net.studioxai.studioxBe.domain.payment.dto.response;

import net.studioxai.studioxBe.domain.payment.dto.CardDto;
import net.studioxai.studioxBe.domain.payment.dto.FailureDto;

import java.math.BigDecimal;

public record BillingApprovalResponse (
String version,
String paymentKey,
String type,
String orderId,
String orderName,
String mId,
String currency,
String method,
BigDecimal totalAmount,
String balanceAmount,
String status,
String requestedAt,
String approvedAt,
boolean useEscrow,
String lastTransactionKey,
int suppliedAmount,
int vat,
boolean cultureExpense,
int taxFreeAmount,
int taxExemptionAmount,
CardDto card,
boolean isPartialCancelable,
String country,
FailureDto failure
) {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package net.studioxai.studioxBe.domain.payment.dto.response;

import com.fasterxml.jackson.annotation.JsonProperty;
import net.studioxai.studioxBe.domain.payment.dto.CardDto;
import net.studioxai.studioxBe.domain.payment.dto.TransferDto;

import java.util.List;

public record BillingKeyResponse (
@JsonProperty("mId")
String mId,

String customerKey,

String authenticatedAt,

String method,

String billingKey,

CardDto card,

List<TransferDto> transfers
) {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package net.studioxai.studioxBe.domain.payment.dto.response;

import com.fasterxml.jackson.annotation.JsonProperty;

import java.math.BigDecimal;
import java.util.Map;

public record ExchangeRateResponse(
String result,

@JsonProperty("time_last_update_utc")
String timeLastUpdateUtc,

@JsonProperty("time_next_update_utc")
String timeNextUpdateUtc,

@JsonProperty("base_code")
String baseCode,

@JsonProperty("conversion_rates")
Map<String, BigDecimal> conversionRates
) {
}
Loading