프로젝트 결제 기능을 구현 하기 위해 토스페이먼츠 결제 연동을 사용했다.
백엔드 기준에서 작성된 글입니다. 나중에 제가 다시 보려고 기록합니다!
1. FE (프론트엔드) - 결제 요청 인증 위젯 연동하기
결제창을 띄우고 성공시 successUrl / 실패시 failureUrl 로 이동한다.
성공하면 successUrl에 paymentKey, orderId, amount 값이 포함되어 있다.
2. BE (백엔드) - 결제 승인 API 연동하기
FE가 넘겨준 payment, orderId, amount 값으로 토스 서버에 결제 승인 요청 보낸다.
결제를 요청하면 차례로 인증과 승인 과정이 진행된다.
첫 번째 단계인 인증 결과를 받아 다음 단계로 넘어가기 위해 리다이렉트 URL처리가 필요하다. 토스페이먼츠에서 인증 결과를 URL의 쿼리 파라미터에 담아 리다이렉트하면, 상점(현재 내가 만든 서버)에서는 리다이렉트 URL을 받아 결제 승인을 처리할 수 있게 되는 것!
첫 번째 단계인 인증에 성공한다면?
성공 리다이렉트 URL(successUrl)에 쿼리 파라미터로 담긴 결제 정보를 이용해 승인 API를 호출한다.
인증에 실패한다면?
실패 리다이렉트 URL(failUrl)에 쿼리 파라미터로 담긴 에러 정보를 보며 디버깅한다.
SDK 준비 파트를 FE 분들이 구현해 성공한다면, paymentKey, orderId, amount 값을 보내주실 것이다. 이 값들을 BE가 받아서 처리하면 된다!
토스 개발자센터 홈페이지에 보면 결제 요청 결과 확인을 발견할 수 있다.
결제 요청이 성공하면 requestPayment()의 파라미터로 설정했던 결제 성공 페이지(successUrl)로 이동합니다. 성공 리다이렉트 URL을 나타내는 success 뒤에 네 가지 쿼리 파라미터가 들어있습니다.
처음에는 paymentKey가 클라이언트 키인줄 알았다.... 아후..나의 시간이여
paymentKey는 결제 위젯 SDK로 클라이언트 키로 요청을 주었을 때 토스에서 발급해주는 키다!
토스페이먼츠 개발자센터에서 공유해준 html 코드를 이용해서 결제창을 띄워보았다!
<head>
<meta charset="utf-8" />
<!-- 결제위젯 SDK 추가 -->
<script src="https://js.tosspayments.com/v1/payment-widget"></script>
</head>
<body>
<!-- 결제위젯, 이용약관 영역 -->
<div id="payment-method"></div>
<div id="agreement"></div>
<!-- 결제하기 버튼 -->
<button id="payment-button">결제하기</button>
<script>
const clientKey = 'test_ck_YZ1aOwX7K8m29JWqKLP3yQxzvNPG';
const customerKey = 'G2MD0ghzwL7EwQH01EOfK'; // 내 상점에서 고객을 구분하기 위해 발급한 고객의 고유 ID
const button = document.getElementById('payment-button');
// ------ 결제위젯 초기화 ------
// 비회원 결제에는 customerKey 대신 ANONYMOUS를 사용하세요.
const paymentWidget = PaymentWidget(clientKey, customerKey); // 회원 결제
// const paymentWidget = PaymentWidget(clientKey, PaymentWidget.ANONYMOUS) // 비회원 결제
// ------ 결제위젯 렌더링 ------
// 결제수단 UI를 렌더링할 위치를 지정합니다. `#payment-method`와 같은 CSS 선택자와 결제 금액 객체를 추가하세요.
// DOM이 생성된 이후에 렌더링 메서드를 호출하세요.
// https://docs.tosspayments.com/reference/widget-sdk#renderpaymentmethods선택자-결제-금액-옵션
paymentWidget.renderPaymentMethods('#payment-method', { value: 300 });
// ------ 이용약관 렌더링 ------
// 이용약관 UI를 렌더링할 위치를 지정합니다. `#agreement`와 같은 CSS 선택자를 추가하세요.
// https://docs.tosspayments.com/reference/widget-sdk#renderagreement선택자
paymentWidget.renderAgreement('#agreement');
// ------ '결제하기' 버튼 누르면 결제창 띄우기 ------
// 더 많은 결제 정보 파라미터는 결제위젯 SDK에서 확인하세요.
// https://docs.tosspayments.com/reference/widget-sdk#requestpayment결제-정보
button.addEventListener('click', function () {
paymentWidget.requestPayment({
orderId: '4TITFDV0Qqs0xzmtqqtjD', // 주문 ID(직접 만들어주세요)
orderName: '토스 티셔츠 외 2건', // 주문명
successUrl: 'http://localhost:3000/payments/success', // 결제에 성공하면 이동하는 페이지(간단하게 직접 만들었다)
failUrl: 'http://localhost:3000/fail', // 결제에 실패하면 이동하는 페이지(어차피 에러니까 안 만들었다)
customerEmail: 'customer123@gmail.com',
customerName: '김토스',
});
});
</script>
</body>
src/payments/payments.controller.ts
main.ts
짜잔 - ! 결제창이 뜬다! 실제 결제되는 거 아니니까 안심 주의
나는 휴대폰 결제(휴대폰 결제는 최소 300원 이상)로 요청을 보냈고, 결제 요청이 완료되면!
짜잔 - ! x2 내가 만든 success 페이지가 뜬다! url을 보면 success라고 뜬게 보인다!
http://localhost:3000/payments/success?paymentKey={PAYMENT_KEY}&orderId={ORDER_ID}&amount={AMOUNT}&paymentType={PAYMENT_TYPE}
url에 paymentKey값이 포함되어있다! 이제 paymentKey 값으로 백엔드 로직을 처리해주면 된다!
토스페이먼츠에 회원가입을 하지 않아도 테스트 키로 결제 연동을 할 수 있지만, 결제 내역을 확인할 수 없다. 일단 토스페이먼츠 회원가입을 하자!
회원 가입을 마쳤다면, 토스페이먼츠 개발자센터에 내 개발정보를 누르면 API 키를 확인할 수 있다. 키는 총 두 가지가 있다!
1. 클라이언트 키 - FE 분들이 SDK 연동을 위해 사용
2. 시크릿 키 - BE가 토스 서버로 요청을 보낼 때 사용 (토스페이먼츠 API Basic 인증)
클라이언트 키와 시크릿 키는 짝꿍이다! 항상 둘을 쌍으로 사용해야한다.
키 관련해서 에러가 계속 발생한다면 클라이언트 키와 시크릿 키가 짝꿍이 맞는지 확인해보자!
테스트 키는 실제 결제가 아닌 테스트용이다! 실제 결제를 하고 싶다면 사업자 등록 후 토스에게 라이브 키를 발급받아 사용해야한다!
API 키외에도 방화벽 설정이나 지원 브라우저 환경 확인 등 절차가 있었는데, 로컬 환경에서는 따로 설정을 안 해도 괜찮았다! (혹시 문제가 생긴다면 방화벽 설정, 지원 브라우저 환경을 확인해보세요)
배포 환경에서는 사업자 등록을 통해 발급 받은 테스트키 or 라이브키를 사용해야 된다고 합니다!
토스페이먼츠 개발자 문서를 보면, 토스에서는 Basic 인증방식을 사용한다고 적혀져 있다. 시크릿 키를 사용할 때 콜론을 포함한 값을 Base64로 인코딩해서 사용해야한다.
src/payments/dto/payment.dto.ts
src/payments/payments.controller.ts
src/payments/payments.service.ts
여기서 주의할 점!은 시크릿 키를 보낼 때 발급 받았던 시크릿 키 뒤에 :를 추가하고 base64로 인코딩 해야된다는 것이다! service 코드를 살펴보면 Authorization에 인코딩을 위한 코드가 적혀있다!
코드를 다 작성한 후, Postman으로 요청을 보내보자!
성공쓰!
Nest.js 코드 없이 curl 명령어를 통해 토스페이먼츠에 결제 승인이 잘 되는지 확인하려면, 시크릿 키를 직접 인코딩 한 후에, 토스페이먼츠 url로 요청을 보내야한다.
- 인코딩된 값 얻는 명령어
echo -n '시크릿키:' | base64
- toss로 직접 결제 승인 요청 보내는 curl 명령어
curl --request POST \
--url https://api.tosspayments.com/v1/payments/confirm \
--header 'Authorization: Basic {인코딩 한 시크릿 키}' \
--header 'Content-Type: application/json' \
--data '{"paymentKey":"{아까 받은 paymentKey}","amount":{요청 보냈던 결제 금액},"orderId":"{요청 보냈었던 orderId}"}'
토스페이먼츠 개발자센터
휴.. 공식문서랑 언제쯤 친해질 수 있는 걸까? 공식 문서에 정답이 다 있는 걸 알지만 그 정답까지 가는 시간이 조금.. 걸린다. 내가 바로 공식 문서가 되는 그날까지.. 많이 읽어보는 게 답이겠죠? 오열
공식 문서를 읽다 보면 나라면 이렇게 작성했을 텐데.. 라는 부분도 보이고! 문서화에 대해 자연스럽게 고민해 보게 되는 것 같아 또 다른 배움이 있었다!
토스페이먼츠는 공식 문서가 친절하게 나와 있는 편이었따! 감사합니다 🙇♀️
그런데 왜 테스트 환경에는.. 신한카드가 없는 거죠? 저는 신한카드 뿐인데,,🥹
글을 읽다가 궁금한게 있어서 질문드립니다. 프론트(public/toss.html)에서 스크립트 태그 실행을 하게 되고 성공을 한다면 successUrl에 명시된 url로 이동되어서 paymentKey가 쿼리 스트링에 표시되는것은 알겠습니다. 그리고 이 paymentKey를 백엔드로 보낸 후 백엔드에서 토스 api에 ajax 요청을 보내는 것 까진 알겠어요. 이해가 안가는게 있는데
질문. 1 successUrl에 이동된것이 완전히 결제가 이루어진 상태인가요?
질문. 2 만약 결제가 이루어진 상태가 아니라면 paymentKey를 백엔드에 보내지 않은 상태인거 같은데 successUrl 페이지에 있는 스크립트 어딘가에서 백엔드로 ajax 요청을 보내나요?
질문. 3 src/payments/payments.service.ts이 파일에서 13 ~ 35 줄 사이에서 axios를 통한 ajax 통신이 이루어지는데 제가 알기로는 axios가 응답이 200이 아니라 400에서 500이라면 에러로 반환되어서 캐치로 가게 될것입니다. 그리고 응답으로 { title: "결제 성공", body: response.data }로 이런식으로 응답을 보내는데 저 응답이 보내지는 상황이라면 무조건 결제가 성공된것일까요?
답변을 해주시면 감사하겠습니다!