- 최종프로젝트 15일차, 오늘은 경매가 끝나고 수행되는 결제 시스템을 구현하였다.
- 결제 API로 TossPayments를 적용하였다.
Client
는Toss
에게 결제 요청을 한다.
- 결제 요청에는
clientKey
,orderId(주문번호)
,amount(가격)
등을 포함한다.- 결제 요청은 URL을 통해 이루어지지 않고,
Toss
의결제 SDK
를 통해 이루어진다.
Toss
는 결제 요청에 대한인가코드(paymentKey)
와orderId
,amount
등을Client
에게 반환한다.
Client
는Toss
에게 반환받은paymentKey
,orderId
,amount
를서버
에 전달한다.
서버
는Client
에게 받은paymentKey
,orderId
,amount
를 통해Toss
에게 결제 승인 요청을 한다.
Toss
는서버
에게결제 승인 정보(TossPayment)
반환한다.
서버
는Toss
에게 반환받은결제 승인 정보
를 DB에 저장하고,Client
에게 반환해준다.
- 결제과정의 전체적인 흐름은 이와 같다. 이제 단계별로 코드로 구현해보자.
- 프론트엔드에 대한 지식은 많이 부족하지만, 최종프로젝트의 프론트엔드가 React로 구성되기에, React기준으로 설명하려고 한다.
- 🖥️ SDK 준비 및 요청
#설치 커맨드 npm install @tosspayments/payment-sdk --save
- 설치가 완료되었다면 컴포넌트에 아래와 같이 import를 해주고, loadTossPayments()를 통해 UI 및 결체요청을 진행하면된다.
import { loadTossPayments } from "@tosspayments/payment-sdk"; //페이호출 메소드 const handlePayment = (subject) => { const random = new Date().getTime() + Math.random(); //난수생성 const randomId = btoa(random); //난수를 btoa(base64)로 인코딩한 orderID if (subject === "카드") { //간편결제 함수 실행 loadTossPayments(client_id).then(tossPayments => { tossPayments.requestPayment(subject, { amount: reserv.amount, //주문가격 orderId: `${randomId}`, //문자열 처리를 위한 ``사용 orderName: orderName(), //결제 이름(여러건일 경우 복수처리) customerName: '테스트', //판매자, 판매처 이름 successUrl: process.env.REACT_APP_TOSS_SUCCESS, failUrl: process.env.REACT_APP_TOSS_FAIL, }) }); } }
- 🖥️ RedirectUrl
https://{ORIGIN}/success?paymentKey={PAYMENT_KEY}&orderId={ORDER_ID}&amount={AMOUNT}
- 해당 코드로 결제요청을 하면, Toss API에서 paymentKey, orderId, amount를 포함하여 RedirectUrl의 파라미터로 전달해준다. 이때 해당 정보를 포함하는 객체를 생성하여 Spring 서버로 요청을하는 것이다.
- 서버는 Client로 부터 받은 paymentKey, orderId, amount를 통해 Toss에게 결제 승인 요청을 한다.
curl --request POST \ --url https://api.tosspayments.com/v1/payments/confirm \ --header 'Authorization: Basic dGVzdF9za196WExrS0V5cE5BcldtbzUwblgzbG1lYXhZRzVSOg==' \ --header 'Content-Type: application/json' \ --data '{"paymentKey":"5zJ4xY7m0kODnyRpQWGrN2xqGlNvLrKwv1M9ENjbeoPaZdL6","orderId":"a4CWyWY5m89PNh7xJwhk1","amount":15000}'
- 나는 다음과 같이 구현하였다.
Toss
에게 결제 승인 요청을 하고, 반환받은 결제 승인 정보를TossPayment
객체에 저장하여
기존 결제 테이블을 업데이트 하고, 업데이트 된 결제 정보를 반환해주도록 구현하였다.Toss
가 반환해주는 결제 승인 정보의 예시는 다음과 같다. (엄청나게 많다){ "mId": "tosspayments", "lastTransactionKey": "9C62B18EEF0DE3EB7F4422EB6D14BC6E", "paymentKey": "5EnNZRJGvaBX7zk2yd8ydw26XvwXkLrx9POLqKQjmAw4b0e1", "orderId": "MC4wODU4ODQwMzg4NDk0", "orderName": "토스 티셔츠 외 2건", "taxExemptionAmount": 0, "status": "DONE", "requestedAt": "2024-02-13T12:17:57+09:00", "approvedAt": "2024-02-13T12:18:14+09:00", "useEscrow": false, "cultureExpense": false, "card": { "issuerCode": "71", "acquirerCode": "71", "number": "12345678****000*", "installmentPlanMonths": 0, "isInterestFree": false, "interestPayer": null, "approveNo": "00000000", "useCardPoint": false, "cardType": "신용", "ownerType": "개인", "acquireStatus": "READY", "receiptUrl": "https://dashboard.tosspayments.com/receipt/redirection?transactionId=tviva20240213121757MvuS8&ref=PX", "amount": 1000 }, "virtualAccount": null, "transfer": null, "mobilePhone": null, "giftCertificate": null, "cashReceipt": null, "cashReceipts": null, "discount": null, "cancels": null, "secret": null, "type": "NORMAL", "easyPay": { "provider": "토스페이", "amount": 0, "discountAmount": 0 }, "easyPayAmount": 0, "easyPayDiscountAmount": 0, "country": "KR", "failure": null, "isPartialCancelable": true, "receipt": { "url": "https://dashboard.tosspayments.com/receipt/redirection?transactionId=tviva20240213121757MvuS8&ref=PX" }, "checkout": { "url": "https://api.tosspayments.com/v1/payments/5EnNZRJGvaBX7zk2yd8ydw26XvwXkLrx9POLqKQjmAw4b0e1/checkout" }, "currency": "KRW", "totalAmount": 1000, "balanceAmount": 1000, "suppliedAmount": 909, "vat": 91, "taxFreeAmount": 0, "method": "카드", "version": "2022-11-16" }
- 이제 API 잘 작동하는지 테스트를 해봐야 한다.
- 다행히도 토스 payments에서 API 테스트 기능을 제공했다. API 테스트
- 그런데 한가지 문제에 직면했다.
- 아무리 찾아봐도 paymentKey를 생성하는 테스트가 없었다.
- 한마디로 Client와 Toss간의 테스트가 없었다.
- 사실 서버측에서 결제 승인 요청을 보내는 테스트는 토스에서 지원해주는 API테스트 툴을 사용하지 않고, 내가 구현한 코드가 잘 작동하는지 테스트해야 했다.
- 내가 구현한 코드를 테스트하는건 Client가 서버에 요청하듯 Postman을 통해 테스트하면 되는데, 문제는 테스트에 필요한 인가코드(paymentKey)를 생성 할 수 없다는 것이다.
- Client가 Toss에 요청을 보낼때 SDK로만 작동하기에, 프론트를 모르는 나에게는 끔찍한 소식이었다.
- 토스 payments 개발자 센터에 샌드박스라는 것이 있었다. 토스 payments 개발자 센터 샌드박스
- 웹에서 결제 API를 쉽게 설명하기 위해서 존재하는 예시를 보여주는 기능이었다.
- 오고가는 네트워크 요청을 뜯어보면 인가코드를 건질 수 있지 않을까 해서 시도해보았다.
- 성공했다! 결제 요청이 끝나고 logs에서 Toss가 Client에게 반환해주는 내용을 건질 수 있었다!
- 다행히 프론트엔드를 구현하지 않고 테스트 할 수 있는 방법을 찾아서 다행이다.
오늘은 토스 결제 API를 적용하고, 테스트 해보았다.
개발자를 위한 실제로 결제가 되지는 않는 API였지만, 결제가 완료되면 핸드폰으로 알림까지 오는걸 보고 놀랐다.
결제 API를 간단하게 적용하도록 토스에서 지원해줘서 어렵지 않게 적용할 수 있었던것 같다.
외부 API를 적용하는것이 굉장히 재미있었다!