다음과 같이 작성할 때 오류가 생긴 이유/원인에 대한 것과 해결방안 문의드립니다.
package com.goldensnitch.qudditch.service;
import com.goldensnitch.qudditch.dto.CustomerOrder;
import com.goldensnitch.qudditch.dto.payment.PaymentResponse;
import com.goldensnitch.qudditch.mapper.CustomerOrderProductMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.*;
import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
import java.util.HashMap;
import java.util.Map;
// 카카오계정
// 카카오 페이( Kakao pay) 기능구현
@Service
public class PaymentService {
private final RestTemplate restTemplate;
@Autowired
private CustomerOrderProductMapper customerOrderProductMapper;
// 카카오페이 결제 요청
@Value("${kakao.pay.ready.url}") private String kakaoPayReadyUrl;
// 결제 승인
@Value("${kakao.pay.approve.url}") private String kakaoPayApproveUrl;
// 결제 취소
@Value("${kakao.pay.cancel.url}") private String kakaoPayCancelUrl;
// 카카오페이 API 사용을 위한 인증 키
@Value("${kakao.pay.authorization}") private String kakaoPayAuthorization;
// 가맹점 코드
@Value("${kakao.pay.cid}") private String cid;
// RestTemplate 주입을 통한 HTTP 클라이언트 초기화
@Autowired
public PaymentService(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
// 결제 준비를 시작하고 사용자를 결제 페이지로 리디렉션하는 URL을 반환하는 메소드
public String initiatePayment(String cid, String partnerOrderId, String partnerUserId,
String itemName, Integer quantity, Integer totalAmount,
Integer taxFreeAmount, String approvalUrl, String cancelUrl,
String failUrl) {
HttpHeaders headers = new HttpHeaders();
// "Authorization" 헤더에 카카오페이 인증 키 추가
headers.add("Authorization", "KakaoAK " + kakaoPayAuthorization);
// 요청 본문의 "Content-Type"을 "application/x-www-form-urlencoded"로 설정
// headers.add("Content-Type", "application/x-www-form-urlencoded;charset=utf-8");
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
// PaymentRequest 객체 대신 MultiValueMap을 사용하여 요청 파라미터를 설정
MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
map.add("cid", cid);
map.add("partner_order_id", partnerOrderId);
map.add("partner_user_id", partnerUserId);
map.add("item_name", itemName);
map.add("quantity", quantity.toString());
map.add("total_amount", totalAmount.toString());
map.add("tax_free_amount", taxFreeAmount.toString());
map.add("approval_url", approvalUrl);
map.add("cancel_url", cancelUrl);
map.add("fail_url", failUrl);
HttpEntity<MultiValueMap<String, String>> entity = new HttpEntity<>(map, headers);
try {
ResponseEntity<PaymentResponse> responseEntity = restTemplate.exchange(
kakaoPayReadyUrl, HttpMethod.POST, entity, PaymentResponse.class);
PaymentResponse paymentResponse = responseEntity.getBody();
if (paymentResponse != null) {
return paymentResponse.getNext_redirect_pc_url();
}
} catch (Exception e) {
e.printStackTrace();
}
return "Error";
}
public String approvePayment(String pgToken, String tid, String partnerOrderId, String partnerUserId) {
HttpHeaders headers = new HttpHeaders();
headers.add("Authorization", "KakaoAK " + kakaoPayAuthorization);
headers.setContentType(MediaType.APPLICATION_JSON);
// JSON 형식으로 요청 바디 구성
Map<String, Object> requestBody = new HashMap<>();
requestBody.put("cid", cid);
requestBody.put("tid", tid);
requestBody.put("pg_token", pgToken);
requestBody.put("partner_order_id", partnerOrderId);
requestBody.put("partner_user_id", partnerUserId);
HttpEntity<Map<String, Object>> entity = new HttpEntity<>(requestBody, headers);
try {
ResponseEntity<PaymentResponse> responseEntity = restTemplate.exchange(
kakaoPayApproveUrl, HttpMethod.POST, entity, PaymentResponse.class);
PaymentResponse paymentResponse = responseEntity.getBody();
if (paymentResponse != null) {
return "Payment approved successfully.";
}
} catch (Exception e) {
e.printStackTrace();
}
return "Error during payment approval.";
}
private void updateOrderStatus(Integer orderId, String tid) {
// Placeholder method. Implement the logic to update the order's status or save the transaction ID (`tid`) to the order in your database.
CustomerOrder order = customerOrderProductMapper.findById(orderId);
if (order != null) {
order.setTid(tid);
customerOrderProductMapper.update(order); // Assuming an `update` method exists to update the order
}
}
// 결제 취소 메서드
public PaymentResponse cancelPayment(String tid, String cancelAmount, String cancelTaxFreeAmount) {
HttpHeaders headers = new HttpHeaders();
headers.add("Authorization", "KakaoAK " + kakaoPayAuthorization);
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
MultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();
parameters.add("cid", cid); // 가맹점 코드
parameters.add("tid", tid); // 결제 고유 번호
parameters.add("cancel_amount", cancelAmount); // 취소 금액
parameters.add("cancel_tax_free_amount", cancelTaxFreeAmount); // 비과세 금액
HttpEntity<MultiValueMap<String, String>> entity = new HttpEntity<>(parameters, headers);
ResponseEntity<PaymentResponse> response = restTemplate.exchange(
kakaoPayCancelUrl, HttpMethod.POST, entity, PaymentResponse.class);
return response.getBody();
}
}
package com.goldensnitch.qudditch.controller;
import com.goldensnitch.qudditch.dto.payment.PaymentRequest;
import com.goldensnitch.qudditch.dto.payment.PaymentResponse;
import com.goldensnitch.qudditch.service.PaymentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping(“api/payment”)
public class PaymentController {
@Autowired
private PaymentService paymentService;
@Autowired
public PaymentController(PaymentService paymentService) {
this.paymentService = paymentService;
}
@PostMapping("/initiate")
public ResponseEntity<?> initiatePayment(@RequestBody PaymentRequest paymentRequest) {
try {
// 사용자로부터 받은 정보와 주문 ID를 기반으로 결제 초기화
paymentRequest.setCid("TC0ONETIME");
String redirectUrl = paymentService.initiatePayment(
paymentRequest.getCid(),
paymentRequest.getPartner_order_id(),
paymentRequest.getPartner_user_id(),
paymentRequest.getItem_name(),
paymentRequest.getQuantity(),
paymentRequest.getTotal_amount(),
paymentRequest.getTax_free_amount(),
"http://localhost:8080/api/payment/approval", // Updated to include orderId in the approvalUrl
paymentRequest.getCancel_url(),
paymentRequest.getFail_url()
);
if (!"Error".equals(redirectUrl)) {
return ResponseEntity.ok().body(redirectUrl);
} else {
return ResponseEntity.badRequest().body("Failed to initiate payment");
}
} catch (Exception e) {
return ResponseEntity.internalServerError().body("Error initiating payment: " + e.getMessage());
}
}
@GetMapping("/approval")
public ResponseEntity<?> approvePayment(@RequestParam("pg_token") String pgToken,
@RequestBody String tid,
@RequestBody String partnerOrderId,
@RequestBody String partnerUserId) {
try {
String result = paymentService.approvePayment(pgToken, tid, partnerOrderId, partnerUserId);
if ("Payment approved successfully.".equals(result)) {
return ResponseEntity.ok().body("Payment approved successfully.");
} else {
return ResponseEntity.badRequest().body(result);
}
} catch (Exception e) {
return ResponseEntity.badRequest().body("Payment approval failed: " + e.getMessage());
}
}
@PostMapping("/cancel")
public ResponseEntity<?> cancelPayment(@RequestParam String tid,
@RequestParam String cancelAmount,
@RequestParam String cancelTaxFreeAmount) {
try {
PaymentResponse paymentResponse = paymentService.cancelPayment(tid, cancelAmount, cancelTaxFreeAmount);
if (paymentResponse != null) {
// 결제 취소가 성공적으로 이루어졌을 경우의 처리 로직
return ResponseEntity.ok().body(paymentResponse);
} else {
// 결제 취소 요청이 실패했을 경우의 처리 로직
return ResponseEntity.badRequest().body("Failed to cancel payment");
}
} catch (Exception e) {
// 예외 처리 로직
return ResponseEntity.internalServerError().body("Error canceling payment: " + e.getMessage());
}
}
}
[오류]
GET “/api/payment/approval?pg_token=d5c100e2bc10d0ac23b4”, parameters={masked}
2024-03-19T11:34:27.531+09:00 DEBUG 18856 — [nio-8080-exec-3] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to com.goldensnitch.qudditch.controller.PaymentController#approvePayment(String, String, String, String)
2024-03-19T11:34:27.562+09:00 DEBUG 18856 — [nio-8080-exec-3] m.m.a.RequestResponseBodyMethodProcessor : Read “application/octet-stream” to []
2024-03-19T11:34:27.566+09:00 WARN 18856 — [nio-8080-exec-3] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.http.converter.HttpMessageNotReadableException: Required request body is missing: public org.springframework.http.ResponseEntity<?> com.goldensnitch.qudditch.controller.PaymentController.approvePayment(java.lang.String,java.lang.String,java.lang.String,java.lang.String)]
2024-03-19T11:34:27.567+09:00 DEBUG 18856 — [nio-8080-exec-3] o.s.web.servlet.DispatcherServlet : Completed 400 BAD_REQUEST
2024-03-19T11:34:27.575+09:00 DEBUG 18856 — [nio-8080-exec-3] o.s.web.servlet.DispatcherServlet : “ERROR” dispatch for GET “/error?pg_token=d5c100e2bc10d0ac23b4”, parameters={masked}
2024-03-19T11:34:27.575+09:00 DEBUG 18856 — [nio-8080-exec-3] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController#errorHtml(HttpServletRequest, HttpServletResponse)
2024-03-19T11:34:27.607+09:00 DEBUG 18856 — [nio-8080-exec-3] o.s.w.s.v.ContentNegotiatingViewResolver : Selected ‘text/html’ given [text/html, text/html;q=0.8]
2024-03-19T11:34:27.608+09:00 DEBUG 18856 — [nio-8080-exec-3] o.s.web.servlet.DispatcherServlet : Exiting from “ERROR” dispatch, status 400