개요
동일 결제(paymentId)에 대한 환불 요청이 동시에 들어오는 경우, 중복 환불 레코드가 생성되고 PG 취소가 중복 실행될 수 있는 경합 조건(TOCTOU)을 방지합니다.
배경
현재 RefundService는 RefundRepository.existsByPaymentId(...)로 중복 여부를 확인한 뒤 환불을 저장합니다. 두 요청이 동시에 해당 검증을 통과하면 각각 저장될 수 있으므로, 애플리케이션 레벨 검증만으로는 중복을 완전히 차단할 수 없습니다.
변경 사항
refunds.payment_id에 유니크 제약을 추가합니다.
- 환불 저장 시 발생하는
DataIntegrityViolationException을 기존의 중복 환불 요청을 의미하는 ResourceException.InvalidArgument으로 변환합니다.
- 동시 요청 상황에서도 하나만 저장되고, 나머지는 일관된 중복 요청 오류로 반환되는지 테스트합니다.
영향 범위
src/main/java/com/kosa/fillinv/payment/service/RefundService.java
src/main/java/com/kosa/fillinv/payment/repository/RefundRepository.java
Refund 엔티티 및 DB 마이그레이션/스키마 정의
- 환불 요청 서비스 및 통합 테스트
완료 기준
- DB 레벨에서 동일
payment_id 환불 레코드를 둘 이상 저장할 수 없다.
- 유니크 제약 위반은 클라이언트에 중복 환불 요청 오류로 일관되게 반환된다.
- 동시 환불 요청 테스트에서 PG 취소 대상으로 생성되는 환불은 하나뿐임이 검증된다.
참고
개요
동일 결제(
paymentId)에 대한 환불 요청이 동시에 들어오는 경우, 중복 환불 레코드가 생성되고 PG 취소가 중복 실행될 수 있는 경합 조건(TOCTOU)을 방지합니다.배경
현재
RefundService는RefundRepository.existsByPaymentId(...)로 중복 여부를 확인한 뒤 환불을 저장합니다. 두 요청이 동시에 해당 검증을 통과하면 각각 저장될 수 있으므로, 애플리케이션 레벨 검증만으로는 중복을 완전히 차단할 수 없습니다.변경 사항
refunds.payment_id에 유니크 제약을 추가합니다.DataIntegrityViolationException을 기존의 중복 환불 요청을 의미하는ResourceException.InvalidArgument으로 변환합니다.영향 범위
src/main/java/com/kosa/fillinv/payment/service/RefundService.javasrc/main/java/com/kosa/fillinv/payment/repository/RefundRepository.javaRefund엔티티 및 DB 마이그레이션/스키마 정의완료 기준
payment_id환불 레코드를 둘 이상 저장할 수 없다.참고