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
1 change: 1 addition & 0 deletions external/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ dependencies {
// implementation 'org.springframework:spring-beans'
// implementation 'org.springframework:spring-context'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-aop'
}

bootJar {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.samhap.kokomen.global.aop;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.util.StopWatch;

@Slf4j
@Order(1)
@Aspect
@Component
Comment on lines +11 to +14
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

어노테이션 순서: Spring 어노테이션을 Lombok 어노테이션보다 앞에 배치해주세요.

코딩 가이드라인에 따르면 Spring 어노테이션이 Lombok 어노테이션보다 먼저 위치해야 합니다.

♻️ 수정 제안
-@Slf4j
 `@Order`(1)
 `@Aspect`
 `@Component`
+@Slf4j
 public class TosspaymentsLoggingAspect {

As per coding guidelines, "Use Lombok for constructors, getters/setters with Spring annotations before Lombok annotations".

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@Slf4j
@Order(1)
@Aspect
@Component
`@Order`(1)
`@Aspect`
`@Component`
`@Slf4j`
🤖 Prompt for AI Agents
In
`@external/src/main/java/com/samhap/kokomen/global/aop/TosspaymentsLoggingAspect.java`
around lines 12 - 15, Reorder the class annotations so Spring annotations come
before Lombok: move `@Aspect`, `@Component` and `@Order`(1) to appear above `@Slf4j` in
the TosspaymentsLoggingAspect class; this ensures the Spring annotations
(`@Aspect`, `@Component`, `@Order`) are declared before the Lombok `@Slf4j` to comply
with the project coding guideline.

public class TosspaymentsLoggingAspect {

@Around("execution(* com.samhap.kokomen.payment.external.TosspaymentsClient.*(..))")
public Object logTosspaymentsApiCall(ProceedingJoinPoint joinPoint) throws Throwable {
String methodName = joinPoint.getSignature().getName();
Object[] args = joinPoint.getArgs();

log.info("[토스페이먼츠 API 요청] {} - args: {}", methodName, args);

Choose a reason for hiding this comment

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

security-medium medium

The logging statement includes the full request arguments (args), which contain sensitive information such as paymentKey and orderId. Logging these identifiers can lead to the exposure of sensitive transaction data and violates security policies. It is recommended to log only non-sensitive metadata or to implement a masking mechanism for sensitive fields, perhaps by overriding the toString() method of related DTOs.

Suggested change
log.info("[토스페이먼츠 API 요청] {} - args: {}", methodName, args);
log.info("[토스페이먼츠 API 요청] {}", methodName);

Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

결제 요청 인자(args)를 그대로 로깅하면 민감한 결제 정보가 노출될 수 있습니다.

args 배열에는 결제 금액, 키 등 민감 데이터가 포함될 수 있습니다. 마찬가지로 Line 31의 result 응답 객체도 민감 정보를 포함할 수 있습니다. 민감 필드를 마스킹하거나, toString()에서 민감 필드를 제외하는 방식을 고려해주세요.

🤖 Prompt for AI Agents
In
`@external/src/main/java/com/samhap/kokomen/global/aop/TosspaymentsLoggingAspect.java`
at line 23, The logging in TosspaymentsLoggingAspect currently prints raw
request arguments and responses (args and result), which can expose sensitive
payment data; update the code to sanitize/redact sensitive fields before logging
by introducing or using a sanitizer utility (e.g., redactSensitiveFields(Object)
or a TosspaymentsLoggingSanitizer) and call it where methodName and args are
logged and where result is logged, ensuring sensitive keys (amounts, card
numbers, keys, tokens, PII) are masked or omitted and that you do not rely on
raw toString() of request/response objects.


StopWatch stopWatch = new StopWatch();
stopWatch.start();
Object result = joinPoint.proceed();
stopWatch.stop();
log.info("[토스페이먼츠 API 응답] {} - {}ms - response: {}",
methodName, stopWatch.getTotalTimeMillis(), result);
return result;
}
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,11 @@
package com.samhap.kokomen.payment.external;

import com.samhap.kokomen.global.annotation.ExecutionTimer;
import com.samhap.kokomen.payment.external.dto.TosspaymentsConfirmRequest;
import com.samhap.kokomen.payment.external.dto.TosspaymentsPaymentCancelRequest;
import com.samhap.kokomen.payment.external.dto.TosspaymentsPaymentResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.RestClient;

@Slf4j
@ExecutionTimer
@Component
public class TosspaymentsClient {

Expand All @@ -21,45 +16,21 @@ public TosspaymentsClient(TossPaymentsClientBuilder tossPaymentsClientBuilder) {
}

public TosspaymentsPaymentResponse confirmPayment(TosspaymentsConfirmRequest request) {
try {
// TODO: interceptor로 로깅 처리
log.info("토스페이먼츠 결제 승인 API 호출 - paymentKey: {}, orderId: {}, amount: {}",
request.paymentKey(), request.orderId(), request.amount());

TosspaymentsPaymentResponse response = restClient.post()
.uri("/v1/payments/confirm")
.body(request)
.retrieve()
.body(TosspaymentsPaymentResponse.class);
log.info("토스페이먼츠 결제 승인 완료 - response: {}", response);

return response;

} catch (HttpClientErrorException e) {
log.error("토스페이먼츠 결제 승인 실패 - paymentKey: {}, status: {}, responseBody: {}",
request.paymentKey(), e.getStatusCode(), e.getResponseBodyAsString(), e);
throw e;
}
return restClient.post()
.uri("/v1/payments/confirm")
.body(request)
.retrieve()
.body(TosspaymentsPaymentResponse.class);
}

public TosspaymentsPaymentResponse cancelPayment(String paymentKey, TosspaymentsPaymentCancelRequest request) {
try {
log.info("토스페이먼츠 결제 취소 API 호출 - paymentKey: {}, cancelReason: {}",
paymentKey, request.cancelReason());

TosspaymentsPaymentResponse response = restClient.post()
.uri("/v1/payments/{paymentKey}/cancel", paymentKey)
.body(request)
.retrieve()
.body(TosspaymentsPaymentResponse.class);
log.info("토스페이먼츠 결제 취소 완료 - response: {}", response);

return response;

} catch (HttpClientErrorException e) {
log.error("토스페이먼츠 결제 취소 실패 - paymentKey: {}, status: {}, responseBody: {}",
paymentKey, e.getStatusCode(), e.getResponseBodyAsString(), e);
throw e;
}
public TosspaymentsPaymentResponse cancelPayment(
String paymentKey,
TosspaymentsPaymentCancelRequest request
) {
return restClient.post()
.uri("/v1/payments/{paymentKey}/cancel", paymentKey)
.body(request)
.retrieve()
.body(TosspaymentsPaymentResponse.class);
}
}