Skip to content

Commit c88431d

Browse files
authored
Merge pull request #125 from prgrms-web-devcourse-final-project/feat#113
[Feat]: 내 결제 내역 목록, 내 결제 단건 상세 기능 구현
2 parents d910470 + 51a38ea commit c88431d

12 files changed

+167
-64
lines changed

src/main/java/com/backend/domain/cash/repository/CashTransactionRepository.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.backend.domain.cash.repository;
22

3+
import com.backend.domain.cash.constant.RelatedType;
34
import com.backend.domain.cash.entity.Cash;
45
import com.backend.domain.cash.entity.CashTransaction;
56
import org.springframework.data.domain.Page;
@@ -15,4 +16,9 @@ public interface CashTransactionRepository extends JpaRepository<CashTransaction
1516

1617
// 단건 상세..
1718
Optional<CashTransaction> findByIdAndCash(Long id, Cash cash);
19+
20+
// paymentId로 연결된 가장 최신 원장 1건 찾기...
21+
Optional<CashTransaction> findFirstByRelatedTypeAndRelatedIdOrderByIdDesc(
22+
RelatedType relatedType, Long relatedId
23+
);
1824
}

src/main/java/com/backend/domain/payment/controller/ApiV1PaymentController.java

Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,18 @@
22

33
import com.backend.domain.member.entity.Member;
44
import com.backend.domain.member.service.MemberService;
5-
import com.backend.domain.payment.dto.PaymentRequest;
6-
import com.backend.domain.payment.dto.PaymentResponse;
7-
import com.backend.domain.payment.dto.TossIssueBillingKeyRequest;
8-
import com.backend.domain.payment.dto.TossIssueBillingKeyResponse;
5+
import com.backend.domain.payment.dto.*;
96
import com.backend.domain.payment.service.PaymentService;
10-
import com.backend.domain.payment.service.TossBillingClient;
7+
import com.backend.domain.payment.service.TossBillingClientService;
118
import io.swagger.v3.oas.annotations.Operation;
129
import io.swagger.v3.oas.annotations.tags.Tag;
1310
import jakarta.validation.Valid;
1411
import lombok.RequiredArgsConstructor;
1512
import org.springframework.http.HttpStatus;
1613
import org.springframework.security.core.annotation.AuthenticationPrincipal;
1714
import org.springframework.security.core.userdetails.User;
18-
import org.springframework.web.bind.annotation.PostMapping;
19-
import org.springframework.web.bind.annotation.RequestBody;
20-
import org.springframework.web.bind.annotation.RequestMapping;
21-
import org.springframework.web.bind.annotation.RestController;
15+
import org.springframework.transaction.annotation.Transactional;
16+
import org.springframework.web.bind.annotation.*;
2217
import org.springframework.web.server.ResponseStatusException;
2318

2419
@RestController
@@ -29,7 +24,7 @@ public class ApiV1PaymentController {
2924

3025
private final MemberService memberService;
3126
private final PaymentService paymentService;
32-
private final TossBillingClient tossBillingClient;
27+
private final TossBillingClientService tossBillingClientService;
3328

3429
@PostMapping
3530
@Operation(summary="지갑 충전 요청", description="idempotencyKey로 중복 충전 방지, 일단은 idempotencyKey 아무키로 등록해주세요!.")
@@ -56,6 +51,31 @@ public TossIssueBillingKeyResponse issueBillingKey(
5651
Member me = memberService.findMemberByEmail(user.getUsername());
5752
String customerKey = "user-" + me.getId(); // ★ 프런트 값 무시하고 서버에서 생성
5853

59-
return tossBillingClient.issueBillingKey(customerKey, req.getAuthKey());
54+
return tossBillingClientService.issueBillingKey(customerKey, req.getAuthKey());
6055
}
56+
57+
@GetMapping("/me")
58+
@Transactional(readOnly = true)
59+
public MyPaymentsResponse getMyPayments(
60+
@AuthenticationPrincipal User user,
61+
@RequestParam(defaultValue = "1") int page,
62+
@RequestParam(defaultValue = "20") int size
63+
) {
64+
if (user == null) throw new ResponseStatusException(HttpStatus.UNAUTHORIZED, "로그인이 필요합니다.");
65+
Member actor = memberService.findMemberByEmail(user.getUsername());
66+
return paymentService.getMyPayments(actor, page, size); //
67+
}
68+
69+
@GetMapping("/me/{paymentId}")
70+
@Operation(summary = "내 결제 단건 상세")
71+
@Transactional(readOnly = true)
72+
public MyPaymentResponse getMyPaymentDetail(
73+
@AuthenticationPrincipal User user,
74+
@PathVariable Long paymentId
75+
) {
76+
if (user == null) throw new ResponseStatusException(HttpStatus.UNAUTHORIZED, "로그인이 필요합니다.");
77+
Member actor = memberService.findMemberByEmail(user.getUsername());
78+
return paymentService.getMyPaymentDetail(actor, paymentId);
79+
}
80+
6181
}

src/main/java/com/backend/domain/payment/controller/ApiV1PaymentMockController.java

Lines changed: 17 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import org.springframework.http.ResponseEntity;
99
import org.springframework.web.bind.annotation.*;
1010

11+
import java.time.LocalDateTime;
1112
import java.util.List;
1213

1314
@Tag(name = "Payment", description = "결제 관련 API")
@@ -32,8 +33,8 @@ public ResponseEntity<RsData<List<PaymentMethodResponse>>> getPaymentMethods() {
3233
.bankCode(null)
3334
.bankName(null)
3435
.acctLast4(null)
35-
.createDate("2025-09-23T12:34:56Z")
36-
.modifyDate("2025-09-23T12:34:56Z")
36+
.createDate(LocalDateTime.parse("2025-09-23T12:34:56"))
37+
.modifyDate(LocalDateTime.parse("2025-09-23T12:34:56Z"))
3738
.expireDate("2027-12")
3839
.build();
3940

@@ -50,8 +51,8 @@ public ResponseEntity<RsData<List<PaymentMethodResponse>>> getPaymentMethods() {
5051
.bankCode("004")
5152
.bankName("KB국민은행")
5253
.acctLast4("5678")
53-
.createDate("2025-08-11T09:10:00Z")
54-
.modifyDate("2025-08-11T09:10:00Z")
54+
.createDate(LocalDateTime.parse("2025-08-11T09:10:00"))
55+
.modifyDate(LocalDateTime.parse("2025-08-11T09:10:00Z"))
5556
.expireDate(null)
5657
.build();
5758

@@ -84,8 +85,8 @@ public ResponseEntity<RsData<PaymentMethodResponse>> getPaymentMethod(@PathVaria
8485
.bankCode(null)
8586
.bankName(null)
8687
.acctLast4(null)
87-
.createDate("2025-09-23T12:34:56Z")
88-
.modifyDate("2025-09-23T12:34:56Z")
88+
.createDate(LocalDateTime.parse("2025-09-23T12:34:56"))
89+
.modifyDate(LocalDateTime.parse("2025-09-23T12:34:56Z"))
8990
.expireDate("2027-12")
9091
.build();
9192
} else {
@@ -103,8 +104,8 @@ public ResponseEntity<RsData<PaymentMethodResponse>> getPaymentMethod(@PathVaria
103104
.bankCode("004")
104105
.bankName("KB국민은행")
105106
.acctLast4("5678")
106-
.createDate("2025-08-11T09:10:00Z")
107-
.modifyDate("2025-08-11T09:10:00Z")
107+
.createDate(LocalDateTime.parse("2025-08-11T09:10:00"))
108+
.modifyDate(LocalDateTime.parse("2025-08-11T09:10:00Z"))
108109
.expireDate(null)
109110
.build();
110111
}
@@ -131,8 +132,8 @@ public ResponseEntity<RsData<PaymentMethodResponse>> createPaymentMethod(
131132
.bankCode(null)
132133
.bankName(null)
133134
.acctLast4(null)
134-
.createDate("2025-09-23T13:20:00Z")
135-
.modifyDate("2025-09-23T13:20:00Z")
135+
.createDate(LocalDateTime.parse("2025-09-23T13:20:00"))
136+
.modifyDate(LocalDateTime.parse("2025-09-23T13:20:00Z"))
136137
.expireDate("2028-03")
137138
.build();
138139

@@ -151,7 +152,7 @@ public ResponseEntity<RsData<PaymentMethodEditResponse>> editPaymentMethod(
151152
.id(id)
152153
.alias("경조사용 카드")
153154
.isDefault(false)
154-
.modifyDate("2025-09-23T13:45:00Z")
155+
.modifyDate(LocalDateTime.parse("2025-09-23T13:45:00"))
155156
.build();
156157

157158
RsData<PaymentMethodEditResponse> body =
@@ -189,8 +190,8 @@ public ResponseEntity<RsData<PaymentResponse>> charge() {
189190
.provider("toss")
190191
.methodType("CARD")
191192
.transactionId("pg_tx_abc123")
192-
.createdAt("2025-09-23T12:35:10Z")
193-
.paidAt("2025-09-23T12:35:10Z")
193+
.createdAt(LocalDateTime.parse("2025-09-23T12:35:10"))
194+
.paidAt(LocalDateTime.parse("2025-09-23T12:35:10Z"))
194195
.idempotencyKey("topup-20250923-uid123-001")
195196
.cashTransactionId(98765L)
196197
.balanceAfter(155000L)
@@ -211,27 +212,21 @@ public ResponseEntity<RsData<MyPaymentsResponse>> getMyPayments(
211212
// 성공..
212213
MyPaymentListItemResponse item1 = MyPaymentListItemResponse.builder()
213214
.paymentId(101L)
214-
.paymentMethodId(12L)
215215
.status("SUCCESS")
216216
.amount(50000L)
217-
.currency("KRW")
218217
.provider("toss")
219218
.methodType("CARD")
220-
.transactionId("pg_tx_abc123") // 성공이라 있음..
221-
.createdAt("2025-09-23T12:35:10Z")
219+
.createdAt(LocalDateTime.parse("2025-09-23T12:35:10"))
222220
.build();
223221

224222
// 실패..
225223
MyPaymentListItemResponse item2 = MyPaymentListItemResponse.builder()
226224
.paymentId(99L)
227-
.paymentMethodId(21L)
228225
.status("FAILED")
229226
.amount(30000L)
230-
.currency("KRW")
231227
.provider("toss")
232228
.methodType("CARD")
233-
.transactionId(null) // 실패라서 없음..
234-
.createdAt("2025-09-15T08:10:00Z")
229+
.createdAt(LocalDateTime.parse("2025-09-15T08:10:00"))
235230
.build();
236231

237232
List<MyPaymentListItemResponse> items = List.of(item1, item2);
@@ -258,12 +253,10 @@ public ResponseEntity<RsData<MyPaymentResponse>> getMyPaymentDetail(
258253
.paymentMethodId(12L)
259254
.status("SUCCESS")
260255
.amount(50000L)
261-
.currency("KRW")
262256
.provider("toss")
263257
.methodType("CARD")
264258
.transactionId("pg_tx_abc123")
265-
.createdAt("2025-09-23T12:35:10Z")
266-
.modifyDate("2025-09-23T12:35:10Z")
259+
.createdAt(LocalDateTime.parse("2025-09-23T12:35:10"))
267260
.idempotencyKey("topup-20250923-uid123-001")
268261
.cashTransactionId(98765L)
269262
.balanceAfter(155000L)

src/main/java/com/backend/domain/payment/dto/MyPaymentListItemResponse.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,20 @@
44
import lombok.Builder;
55
import lombok.Getter;
66

7+
import java.time.LocalDateTime;
8+
79
@Getter
810
@Builder
911
@JsonInclude(JsonInclude.Include.NON_NULL)
1012

1113
// 내 결제 내역..
1214
public class MyPaymentListItemResponse {
1315
private final Long paymentId; // 결제 건 번호..
14-
private final Long paymentMethodId; // 사용한 결제수단 번호..
1516
private final String status; // SUCCESS / FAILED / ...
1617
private final Long amount; // 금액(원)..
17-
private final String currency; // "KRW"..
1818
private final String provider; // PG 이름(예: toss)..
1919
private final String methodType; // "CARD"/"BANK"..
20-
private final String transactionId; // PG 트랜잭션 ID(성공건만 존재, 실패면 null)..
21-
private final String createdAt; // 생성/승인 시각(문자열, Z 유지)..
20+
private final LocalDateTime createdAt; // 생성/승인 시각(문자열, Z 유지)..
21+
private final Long cashTransactionId; // 연결된 지갑 원장 ID(성공 시)
22+
private final Long balanceAfter; // 충전 후 잔액(성공 시)
2223
}

src/main/java/com/backend/domain/payment/dto/MyPaymentResponse.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
import lombok.Builder;
44
import lombok.Getter;
55

6+
import java.time.LocalDateTime;
7+
68
@Getter
79
@Builder
810

@@ -12,14 +14,12 @@ public class MyPaymentResponse {
1214
private final Long paymentMethodId; // 사용한 결제수단 번호..
1315
private final String status; // 결제 상태(SUCCESS 등)..
1416
private final Long amount; // 금액(원)..
15-
private final String currency; // "KRW"..
1617
private final String provider; // PG 이름(예: toss)..
1718
private final String methodType; // "CARD"/"BANK"..
1819
private final String transactionId; // PG 트랜잭션 ID..
19-
private final String createdAt; // 생성/승인 시각(문자열, Z 유지)..
20-
private final String modifyDate; // 마지막 갱신 시각(문자열, Z 유지)..
20+
private final LocalDateTime createdAt; // 생성/승인 시각..
2121
private final String idempotencyKey; // 멱등키..
22-
22+
private final LocalDateTime paidAt;
2323
private final Long cashTransactionId; // 원장 '입금' 줄 번호..
2424
private final Long balanceAfter; // 이 결제 반영 후 잔액..
2525
}

src/main/java/com/backend/domain/payment/dto/PaymentMethodEditResponse.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
import lombok.Builder;
44
import lombok.Getter;
55

6+
import java.time.LocalDateTime;
7+
68
@Getter
79
@Builder
810
// 결제 수단 수정..
@@ -26,8 +28,8 @@ public class PaymentMethodEditResponse {
2628
private final String acctLast4; // 계좌 끝 4자리..
2729

2830
// 시간..
29-
private final String createDate; // 생성..
30-
private final String modifyDate; // 변경..
31+
private final LocalDateTime createDate; // 생성..
32+
private final LocalDateTime modifyDate; // 변경..
3133

3234
private final String expireDate; // "YYYY-MM"
3335
}

src/main/java/com/backend/domain/payment/dto/PaymentMethodResponse.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
import lombok.Builder;
55
import lombok.Getter;
66

7+
import java.time.LocalDateTime;
8+
79
@Getter
810
@Builder
911
@JsonInclude(JsonInclude.Include.NON_NULL)
@@ -30,8 +32,8 @@ public class PaymentMethodResponse {
3032
private final String acctLast4; // 계좌 끝 4자리..
3133

3234
// 시간..
33-
private final String createDate; // 생성..
34-
private final String modifyDate; // 변경..
35+
private final LocalDateTime createDate; // 생성..
36+
private final LocalDateTime modifyDate; // 변경..
3537

3638
private final String expireDate; // "YYYY-MM"
3739
}

src/main/java/com/backend/domain/payment/dto/PaymentResponse.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
import lombok.Builder;
44
import lombok.Getter;
55

6+
import java.time.LocalDateTime;
7+
68
@Getter
79
@Builder
810

@@ -16,8 +18,8 @@ public class PaymentResponse {
1618
private final String provider; // 예: "toss"..
1719
private final String methodType; // "CARD"/"BANK"..
1820
private final String transactionId; // PG 트랜잭션 ID..
19-
private final String createdAt; // 생성/승인 시각(문자열, Z 유지)..
20-
private final String paidAt; // 성공 승인 시각..
21+
private final LocalDateTime createdAt; // 생성/승인 시각(문자열, Z 유지)..
22+
private final LocalDateTime paidAt; // 성공 승인 시각..
2123
private final String idempotencyKey; // 멱등키(그대로 에코)..
2224

2325
private final Long cashTransactionId; // 원장 ID(입금 줄)..

src/main/java/com/backend/domain/payment/repository/PaymentRepository.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,21 @@
22

33
import com.backend.domain.member.entity.Member;
44
import com.backend.domain.payment.entity.Payment;
5+
import org.springframework.data.domain.Page;
6+
import org.springframework.data.domain.Pageable;
57
import org.springframework.data.jpa.repository.JpaRepository;
68

79
import java.util.Optional;
810

911
public interface PaymentRepository extends JpaRepository<Payment, Long> {
1012

11-
Optional<Payment> findByMemberAndIdempotencyKey(Member member, String idempotencyKey); // 멱등 선조회
13+
// 멱등 선조회..
14+
Optional<Payment> findByMemberAndIdempotencyKey(Member member, String idempotencyKey);
15+
16+
// 내 결제 내역 목록..
17+
Page<Payment> findAllByMemberOrderByIdDesc(Member member, Pageable pageable);
18+
19+
// 내 결제 단건 상세..
20+
Optional<Payment> findByIdAndMember(Long id, Member member);
1221
}
1322

src/main/java/com/backend/domain/payment/service/PaymentMethodService.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -313,8 +313,8 @@ private PaymentMethodResponse toResponse(PaymentMethod e) {
313313
.bankName(e.getBankName())
314314
.acctLast4(e.getAcctLast4())
315315

316-
.createDate(e.getCreateDate() == null ? null : e.getCreateDate().format(DATE_TIME))
317-
.modifyDate(e.getModifyDate() == null ? null : e.getModifyDate().format(DATE_TIME))
316+
.createDate(e.getCreateDate())
317+
.modifyDate(e.getModifyDate())
318318
.expireDate((e.getExpYear() != null && e.getExpMonth() != null)
319319
? String.format("%04d-%02d", e.getExpYear(), e.getExpMonth())
320320
: null)

0 commit comments

Comments
 (0)