중앙정보기술인재개발원/Spring

[Spring] 카카오페이 단건결제 API

soidev 2025. 6. 21. 02:30

  • 카카오페이 api사용하려면 일단 카카오페이 파트너에 가입 후 
  • 애플리케이션 등록
  • web 테스트용 도메인 등록
  • 테스트용으로서 Secret key(dev) 사용

 

 

내 예제 → 기부버튼 누르면 자동으로 만원씩 후원하게끔 할 것!<단건결제>

 

내 예제 hml

 

참고용 문서 : https://developers.kakaopay.com/docs/payment/online/single-payment

 

카카오페이 | 개발자센터

새로운 기회와 가치를 함께 만들어봐요

developers.kakaopay.com

 

application.properties

# application.properties

kakao.api.secret-key=카카오에서발급받은REST_API_키
kakao.api.cid=TC0ONETIME
kakao.api.approval-url=http://localhost:8080/donation/success
kakao.api.cancel-url=http://localhost:8080/donation/cancel
kakao.api.fail-url=http://localhost:8080/donation/fail

 

각자 발급받은 테스트용 어플리케이션키 넣기

kakao.api ~ 이건 변수명이라 다른거로 가능

applicaiotn.properties에선 다 변수명이라 자유롭게 쓸수있음!

 

https://velog.io/@ryuneng2/%EC%B9%B4%EC%B9%B4%EC%98%A4%ED%8E%98%EC%9D%B4-API-%EC%97%B0%EB%8F%99-%ED%8C%9D%EC%97%85%EC%B0%BD%EB%9D%84%EC%9A%B0%EA%B8%B0-%EA%B2%B0%EC%A0%9C%EC%8A%B9%EC%9D%B8-%EA%B5%AC%ED%98%84

 

[Spring Boot] 카카오페이 API 연동 - 팝업창 띄우기 및 결제승인까지

카카오페이를 구현하기 위해 여러 자료를 참고해서 코드를 작성해보았으나, 작동하지 않았다. 꽤 최신 블로그 글(약 6개월 전 포스팅)을 참고해보기도 했지만, 여전히 오류만 날 뿐이었다. 도대

velog.io

이거 보고 참고

 

 

 

계속 오류가 떴음 왜냐 구버전인 예제가 많아서 초보개발자인 나에겐... 오류 고치는게 너무 오래걸렸음

오류가 났던 코드..

package com.choongang.univ.barrierfree.donation.service;

import java.util.HashMap;
import java.util.Map;

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;

@Service
public class DonationService {

    @Value("${kakao.api.secret-key}")
    private String secretKey;

    @Value("${kakao.api.cid}")
    private String cid;

    @Value("${kakao.api.approval-url}")
    private String approvalUrl;

    @Value("${kakao.api.cancel-url}")
    private String cancelUrl;

    @Value("${kakao.api.fail-url}")
    private String failUrl;

    public Map<String, Object> kakaoPayReady() {
        RestTemplate restTemplate = new RestTemplate();

        // 1. Header
        HttpHeaders headers = new HttpHeaders();
        headers.set("Authorization", "SecretKey " + secretKey);  
        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); // ✅ 중요

        System.out.println("Headers: " + headers.toString());

        // 2. Body
        MultiValueMap<String, String> body = new LinkedMultiValueMap<>();
        body.add("cid", cid);
        body.add("partner_order_id", "donation_001");
        body.add("partner_user_id", "anonymous_donor");
        body.add("item_name", "장애학생 기부");
        body.add("quantity", "1");
        body.add("total_amount", "10000");
        body.add("vat_amount", "0");
        body.add("tax_free_amount", "0");
        body.add("approval_url", approvalUrl);
        body.add("cancel_url", cancelUrl);
        body.add("fail_url", failUrl);

        HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(body, headers);

        String url = "https://open-api.kakaopay.com/online/v1/payment/ready";

        ResponseEntity<Map> response = restTemplate.postForEntity(url, request, Map.class);

        return response.getBody();
    }

    public Map<String, Object> kakaoPayApprove(String tid, String pgToken) {
        RestTemplate restTemplate = new RestTemplate();

        HttpHeaders headers = new HttpHeaders();
        // headers.set("Authorization", secretKey);
        headers.set("Authorization", "KakaoAK " + secretKey);
        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
        MultiValueMap<String, String> body = new LinkedMultiValueMap<>();
        body.add("cid", cid);
        body.add("tid", tid);
        body.add("partner_order_id", "donation_001");
        body.add("partner_user_id", "anonymous_donor");
        body.add("pg_token", pgToken);

        HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(body, headers);

        // String url = "https://open-api.kakaopay.com/online/v1/payment/approve";
        String url = "https://kapi.kakao.com/v1/payment/approve";
        


        ResponseEntity<Map> response = restTemplate.postForEntity(url, request, Map.class);

        return response.getBody();
    }

    private HttpHeaders getHeaders() {
        HttpHeaders headers = new HttpHeaders();
        headers.set("Authorization", "카카오페이 개발자센터에서 발급받은 Secret key(dev) 입력");
        headers.set("Content-type", "application/json");

        return headers;
    }
}

오류가 난 이유  

  • url도 다르고 header.set하는게 구버전으로 했었음
  • https://open-api.kakaopay.com/online/v1/payment/~
  • content-type도 구버전으로..
  • HashMap으로도 해야함
  • 만약에 하시면 이제 지금 버전으로 꼭 문서 꼼꼼히 읽으셔야겠음..!!
  • adminkey가 아니라 이제 secret-key

 

 

그래서 지금 고친 버전

package com.choongang.univ.barrierfree.donation.service;

import java.util.HashMap;
import java.util.Map;

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;

@Service
public class DonationService {

    @Value("${kakao.api.secret-key}")
    private String secretKey;

    @Value("${kakao.api.cid}")
    private String cid;

    @Value("${kakao.api.approval-url}")
    private String approvalUrl;

    @Value("${kakao.api.cancel-url}")
    private String cancelUrl;

    @Value("${kakao.api.fail-url}")
    private String failUrl;

    public Map<String, Object> kakaoPayReady() {
        RestTemplate restTemplate = new RestTemplate();

        // 1. Header
        HttpHeaders headers = new HttpHeaders();
        headers.set("Authorization", "SECRET_KEY " + secretKey);
        headers.setContentType(MediaType.APPLICATION_JSON);

        Map<String, Object> body = new HashMap<>();
        body.put("cid", cid);
        body.put("partner_order_id", "donation_001");
        body.put("partner_user_id", "anonymous_donor");
        body.put("item_name", "장애학생 기부");
        body.put("quantity", 1);
        body.put("total_amount", 10000);
        body.put("vat_amount", 0);
        body.put("tax_free_amount", 0);
        body.put("approval_url", approvalUrl);
        body.put("cancel_url", cancelUrl);
        body.put("fail_url", failUrl);

        HttpEntity<Map<String, Object>> request = new HttpEntity<>(body, headers);

        String url = "https://open-api.kakaopay.com/online/v1/payment/ready";

        ResponseEntity<Map> response = restTemplate.postForEntity(url, request, Map.class);

        return response.getBody();
    }

    public Map<String, Object> kakaoPayApprove(String tid, String pgToken) {
        RestTemplate restTemplate = new RestTemplate();

        HttpHeaders headers = new HttpHeaders();
        headers.set("Authorization", "SECRET_KEY " + secretKey);
        headers.setContentType(MediaType.APPLICATION_JSON);

        Map<String, Object> body = new HashMap<>();
        body.put("cid", cid);
        body.put("tid", tid);
        body.put("partner_order_id", "donation_001");
        body.put("partner_user_id", "anonymous_donor");
        body.put("pg_token", pgToken);

        HttpEntity<Map<String, Object>> request = new HttpEntity<>(body, headers);

        // String url = "https://open-api.kakaopay.com/online/v1/payment/approve";
        // String url = "https://kapi.kakao.com/v1/payment/approve";
        String url = "https://open-api.kakaopay.com/online/v1/payment/approve";

        ResponseEntity<Map> response = restTemplate.postForEntity(url, request, Map.class);

        return response.getBody();
    }

    private HttpHeaders getHeaders() {
        HttpHeaders headers = new HttpHeaders();
        headers.set("Authorization", "카카오페이 개발자센터에서 발급받은 Secret key(dev) 입력");
        headers.set("Content-type", "application/json");

        return headers;
    }
}

 

Controller

package com.choongang.univ.barrierfree.donation.controller;

import java.util.Map;

import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.HttpClientErrorException;

import com.choongang.univ.barrierfree.donation.service.DonationService;

import jakarta.servlet.http.HttpSession;
import lombok.RequiredArgsConstructor;

@RestController
@RequestMapping("api/donation")
@RequiredArgsConstructor
public class DonationRestController {

    private final DonationService donationService;

    // 카카오페이 결제 준비
    @PostMapping("/kakaoPayReady")
    public ResponseEntity<?> kakaoPayReady(HttpSession session) {
        try {
            Map<String, Object> response = donationService.kakaoPayReady();
            session.setAttribute("tid", response.get("tid")); // 결제 승인용 TID 저장
            return ResponseEntity.ok(response);
        } catch (HttpClientErrorException e) {
            return ResponseEntity
                .status(e.getStatusCode())
                .body(Map.of("error", "카카오페이 준비 요청 실패", "message", e.getResponseBodyAsString()));
        } catch (Exception e) {
            return ResponseEntity
                .internalServerError()
                .body(Map.of("error", "서버 오류", "message", e.getMessage()));
        }
    }

    // 카카오페이 결제 성공 후 호출
    @GetMapping("/success")
    public ResponseEntity<?> kakaoPaySuccess(@RequestParam("pg_token") String pgToken, HttpSession session) {
        String tid = (String) session.getAttribute("tid");
        if (tid == null) {
            return ResponseEntity
                .badRequest()
                .body(Map.of("error", "TID 누락", "message", "결제 준비가 선행되지 않았습니다."));
        }

        try {
            Map<String, Object> response = donationService.kakaoPayApprove(tid, pgToken);
            return ResponseEntity.ok(response);
        } catch (HttpClientErrorException e) {
            return ResponseEntity
                .status(e.getStatusCode())
                .body(Map.of("error", "카카오페이 승인 요청 실패", "message", e.getResponseBodyAsString()));
        } catch (Exception e) {
            return ResponseEntity
                .internalServerError()
                .body(Map.of("error", "서버 오류", "message", e.getMessage()));
        }
    }
}

 

 

 

성공!!!!

 

근데 내가 response로 받아서 결제완료하면 페이지가 html이 아니라 json 데이터값들 보여지는게 뜸

 이제 성공 페이지 구현

완료!!!! 휴우 드디어 끝났다!!!

날짜만 약간 바꾸면 결제 test 끝@@