PaymentController.java

package com.newbit.payment.controller;

import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import com.newbit.common.dto.ApiResponse;
import com.newbit.payment.command.application.dto.request.PaymentApproveRequest;
import com.newbit.payment.command.application.dto.request.PaymentPrepareRequest;
import com.newbit.payment.command.application.dto.response.PaymentApproveResponse;
import com.newbit.payment.command.application.dto.response.PaymentPrepareResponse;
import com.newbit.payment.command.application.service.PaymentCommandService;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@RestController
@RequestMapping("/api/v1/payments")
@RequiredArgsConstructor
@Tag(name = "결제 API", description = "결제 관련 API")
public class PaymentController extends AbstractApiController {

    private final PaymentCommandService paymentCommandService;

    @Operation(summary = "결제 준비", description = "결제에 필요한 정보를 준비하고 결제 위젯 URL을 반환합니다.")
    @PostMapping("/prepare")
    public ResponseEntity<ApiResponse<PaymentPrepareResponse>> preparePayment(
            @RequestBody PaymentPrepareRequest request) {
        PaymentPrepareResponse response = paymentCommandService.preparePayment(request);
        return successResponse(response);
    }

    @Operation(summary = "결제 승인", description = "결제 승인을 요청합니다.")
    @PostMapping("/approve")
    public ResponseEntity<ApiResponse<PaymentApproveResponse>> approvePayment(
            @RequestBody PaymentApproveRequest request) {
        PaymentApproveResponse response = paymentCommandService.approvePayment(request);
        return successResponse(response);
    }

    @Operation(summary = "결제 정보 조회", description = "결제 ID로 결제 정보를 조회합니다.")
    @GetMapping("/{paymentId}")
    public ResponseEntity<ApiResponse<PaymentApproveResponse>> getPayment(
            @Parameter(description = "결제 ID") @PathVariable Long paymentId) {
        PaymentApproveResponse response = paymentCommandService.getPayment(paymentId);
        return successResponse(response);
    }

    @Operation(summary = "주문 ID로 결제 정보 조회", description = "주문 ID로 결제 정보를 조회합니다.")
    @GetMapping("/order/{orderId}")
    public ResponseEntity<ApiResponse<PaymentApproveResponse>> getPaymentByOrderId(
            @Parameter(description = "주문 ID") @PathVariable String orderId) {
        PaymentApproveResponse response = paymentCommandService.getPaymentByOrderId(orderId);
        return successResponse(response);
    }
    
    @Operation(summary = "결제 성공 콜백", description = "토스페이먼츠에서 결제 성공 시 호출되는 콜백 엔드포인트입니다.")
    @GetMapping("/success")
    public ResponseEntity<ApiResponse<PaymentApproveResponse>> paymentSuccess(
            @Parameter(description = "결제 키") @RequestParam String paymentKey,
            @Parameter(description = "주문 ID") @RequestParam String orderId,
            @Parameter(description = "결제 금액") @RequestParam Long amount) {
        
        log.info("결제 성공 콜백: paymentKey={}, orderId={}, amount={}", paymentKey, orderId, amount);
        
        PaymentApproveRequest approveRequest = PaymentApproveRequest.builder()
                .paymentKey(paymentKey)
                .orderId(orderId)
                .amount(amount)
                .build();
        
        PaymentApproveResponse response = paymentCommandService.approvePayment(approveRequest);
        
        return successResponse(response);
    }
    
    @Operation(summary = "결제 실패 콜백", description = "토스페이먼츠에서 결제 실패 시 호출되는 콜백 엔드포인트입니다.")
    @GetMapping("/fail")
    public ResponseEntity<ApiResponse<String>> paymentFail(
            @Parameter(description = "에러 코드") @RequestParam String code,
            @Parameter(description = "에러 메시지") @RequestParam String message,
            @Parameter(description = "주문 ID") @RequestParam String orderId) {
        
        log.error("결제 실패 콜백: code={}, message={}, orderId={}", code, message, orderId);
        
        return successMessage("결제에 실패했습니다. 사유: " + message);
    }
}