토스페이먼츠 결제 연동2 결제하기
토스 페이먼츠 에 결제를 요청하고 결제 완료 후 응답 처리 데이터를 받아볼려고 합니다.
토스페이먼츠의 결제프로세스를 봐보자

이전포스팅에서
https://codestudy11.tistory.com/12
토스페이먼츠 결제 연동하기 1
토스페이먼츠 결제 api 연동하기 1 https://app.tosspayments.com/ 토스페이먼츠 전자결제 app.tosspayments.com 먼저 토스페이먼츠 홈페이지에 들어가셔서 회원가입 후 결제 서비스 신청하기와 개발테스트가
codestudy11.tistory.com
토스페이먼츠에서 발급해준 클라이언트 키를 가지고 Payments.requestPayment(결제 수단, 결제 정보) 형식으로 파라미터를 넣고 실행하면 결제창이 호출하게 됩니다.
이제 결제를 요청하고 결제 완료 후 응답 처리 데이터를 받아보자
- 일단 저는 클라이언트에서 결제 이벤트를 통해 api를 호출한다.
- 클라이언트에서 보낸 데이터를 서버에서 데이터를 받을 객체를 받아서 유효성검사가 성공하면 데이터베이스 주문 정보를 저장 후 토스페이먼츠에 지불, 가격, 주문고유번호, 주문상품명,구매자 아이디 또는 구매자 이름를 응답데이터와 성공시 콜백주소와 실패시 콜백주소를 넘겨준다.
- 응답시 위의과정에서 에외가 없다면 클라이언트단에 페이먼츠 객체에 ('카드', (서버에서 응답된 데이터정보))를 통해
토스페이먼츠의 결제창을 호출하게 된다.
- 결제창에서 최종적으로 결제를 완료를 하면 이후에 토스페이먼츠에서 결제의 성공여부 및 관련된 파라미터 데이터를 콜백주소로 리다이렉트를 한다.
- 클라이언트에서 셋팅된 url로 성공 실패여부 에따른 controller로 응답을 받는다.
- 결제가 성공적으로 이뤄진다면 백엔드에서 토스페이먼츠에 [최종적으로 결제 승인요청]을 보낸다.
카드 결제 파트를 구현해보자
데이터들을 유효성검사와 데이터베이스에 주문정보를 저장을 다했다는
먼저 결제 요청을 받을 dto
@Getter
@Setter
public static class Request{
private String userId;
private String facilityGoodsCd;
private String goodsCtgCd;
private String goodsName;
private int goodsPrice;
private int userPoint;
private int usedPoint;
private int orderPayPrice;
private PayType payType; // 결제 타입
}
결제타입은 토스페이먼츠 결제 수단으로 사용할 수 있는 ENUM 코드 CARD(카드), VIRTUAL_ACCOUNT(가상계좌)이 셋팅되어 있습니다.
| 카드 | CARD |
| 가상계좌 | VIRTUAL_ACCOUNT |
| 휴대폰 | MOBILE_PHONE |
| 계좌이체 | TRANSFER |
| 문화상품권 | CULTURE_GIFT_CERTIFICATE |
| 도서문화상품권 | BOOK_GIFT_CERTIFICATE |
| 게임문화상품권 | GAME_GIFT_CERTIFICATE |
데이터베이스의 데이터를 넣은 후 응답데이터 dto
@Getter
@Builder
public static class Response{
private String orderId;
private String orderName;
private String customerName;
private int totalPrice;
private String payTypeName;
}
일단 클라이언트에서 결제 api를 호출 했다는 과정으로 구현했다.
주문 번호는 고유아이디
위의 값을 Payments.requestPayment(결제 수단, 결제 정보)에 결제정보와 결제수단을 클라이언트단에 넘겨서
토스페이먼츠 결제창을 호출한다.
- amount 필수 · number
결제되는 금액입니다. - orderId 필수 · string
상점에서 주문 건을 구분하기 위해 발급한 고유 ID입니다. - orderName 필수 · string
결제에 대한 주문명입니다. 예를 들면 생수 외 1건 같은 형식입니다. - successUrl 필수 · string
결제가 성공하고 나면 리다이렉트(Redirect)되는 URL입니다. 결제 승인 처리에 필요한 값들이 쿼리 파라미터(Query Parameter)로 함께 전달됩니다. 반드시 오리진(origin)을 포함해야 합니다. 예를 들면 https://www.example.com/success와 같은 형태입니다. - failUrl 필수 · string
결제가 실패하면 리다이렉트되는 URL입니다. 에러 코드 및 에러 메시지가 쿼리 파라미터로 함께 전송됩니다. 반드시 오리진(origin)을 포함해야 합니다.
let method = data.payTypeName;
const paymentData = {
amount: data.totalPrice,
orderId: data.orderId,
orderName: data.customerName,
customerName: data.userName,
method : data.payTypeName,
successUrl: window.location.origin + "/pay/success",
failUrl: window.location.origin + "/pay/fail",
};
if (method === '가상계좌') {
paymentData.virtualAccountCallbackUrl = window.location.origin + '/virtual-account/callback'
}
그럼이제 토스페이먼츠에서 성공시 데이터를 받아보자
응답데이터는 결제완료후 결제가 성공한다면 셋팅된 성공url로 orderId, paymentKey, amount데이터를 controller로 응답을한다. 응답한데이터로 토스페이먼츠에 결제를 승인을 요청하고 성공을 결제정보를 db에 정보를 저장한다는 과정으로 하였다. 이때 paymentKey는 중요하다 결제 취소와 결제 조회에 사용되므로 칼럼을 추가할 필요가 있습니다.
먼저 응답 데이터를 받을 dto를 만들어보자.
응답데이터를 받을 dto
@Data
public class PaymentResponseDto {
private String mId; //"mId": "tosspayments",
private String version; // "version": "1.4",
private String paymentKey;// "paymentKey": "q8TAdB4AXbl_8Z8nAHcMB",
private String orderId; //"orderId": "_en8MVAhWzLeE1SXnC9uh",
private String orderName; //"orderName": "토스 티셔츠 외 2건",
private String currency; //"currency": "KRW",
private String method; //"method": "카드",
private PayStatus status; // "status": "DONE",
private String requestedAt; // "requestedAt": "2021-01-01T10:01:30+09:00",
private String approvedAt; // "approvedAt": "2021-01-01T10:05:40+09:00",
private String useEscrow; // "useEscrow": false,
private String cultureExpense; // "cultureExpense": false,
private String totalAmount; //"totalAmount": 15000,
private String balanceAmount; // "balanceAmount": 15000,
private String suppliedAmount; // "suppliedAmount": 13636,
private String vat; // "vat": 1364,
private String taxFreeAmount; // "taxFreeAmount": 0
private PaymentCardResponseDto card; // 카드
private String type;
private PaymentVirtualResponseDto virtualAccount; // 가상계좌
카드 정보를 받을 dto
@Data
public class PaymentCardResponseDto {
private String company; //"company": "현대",
private String number; //"number": "433012******1234",
private String installmentPlanMonths; //"installmentPlanMonths": 0,
private String isInterestFree; //"isInterestFree": false,
private String interestPayer; //"interestPayer": null,
private String approveNo; //"approveNo": "00000000",
private String useCardPoint; //"useCardPoint": false,
private String cardType; //"cardType": "신용",
private String ownerType; //"ownerType": "개인",
private String acquireStatus; //"acquireStatus": "READY",
private String receiptUrl; //"receiptUrl": "https://merchants.tosspayments.com/web/serve/merchant/test_ck_jkYG57Eba3GoelzLmLQ3pWDOxmA1/receipt/rlOqkp81hUb-VW5L6995D"
}
가상계좌 정보를 받을 dto
@Data
public class PaymentVirtualResponseDto {
private String accountNumber; //"accountNumber": "X6505636518308",
private String accountType; // "accountType": "일반",
private String bank; // "bank": "우리",
private String customerName; // "customerName": "박토스",
private String dueDate; // "dueDate": "2022-01-15T21:05:09+09:00",
private String expired; // "expired": false,
private String settlementStatus; // "settlementStatus": "INCOMPLETED",
private String refundStatus; // "refundStatus": "NONE"
}
응답할 dto를 만들었으면 이ㄷㄴ제 승인 절차를 밟으면 된다. 응답데이터를 받은후 ㅇ db저장과 주문정보 수정을 하였다
public PaymentResDto confirmPayment(String paymentKey, String orderId, Long amount) throws JsonProcessingException {
//주문 정보 확인 한다.
Order order = orderMapper.getOrderByOrderUUID(orderId)
.orElseThrow(()-> new CustomException(ErrorMessage.NOT_FOUND_ORDER));
// toss 결제 최종 확인을 위한 준비.
RestTemplate restTemplate = new RestTemplate();
ObjectMapper objectMapper = new ObjectMapper();
HttpHeaders headers = new HttpHeaders();
String secretKey = SECRET_KEY + ":";
String encodingAuth = new String(Base64.getEncoder().encode(secretKey.getBytes()));
headers.setBasicAuth(encodingAuth);
// headers.set("Authorization", "Basic " + Base64.getEncoder().encodeToString((SECRET_KEY + ":").getBytes()));
headers.setContentType(MediaType.APPLICATION_JSON);
log.info("headers data : {}", headers);
Map<String, String> payloadMap = new HashMap<>();
payloadMap.put("orderId", orderId);
payloadMap.put("amount", String.valueOf(amount));
HttpEntity<String> request = new HttpEntity<>(objectMapper.writeValueAsString(payloadMap), headers);
PaymentResDto paymentResDto= restTemplate.postForEntity(
"https://api.tosspayments.com/v1/payments/" + paymentKey,
request,
PaymentResDto.class
).getBody();
if(paymentResDto == null) {
log.info("결제 정보를 찾지 못함 ");
throw new CustomException(ErrorMessage.NOT_FOUND_PAYMENT);
}
log.info("토스 응답데이터 : {}", paymentResDto);
paymentResDto.setUserId(order.getUserId());
String status = paymentResDto.getStatus().getMessage();
orderMapper.modifyOrder(order);
payMapper.addPay(paymentResDto);
//통과후 토스 응답데이터 리턴
return paymentResDto;
}
결제 요청에 이상이 없고 토스의 결제 paymentkey가 잘 들어오고 apikey가 모두 일치한다면 토스페이먼츠에서 결제를 최종승인요청을 받는다.
최종적으로 결제요청이 성공한다면
"mId": "tosspayments",
"version": "2022-06-08",
"transactionKey": "B7103F204998813B889C77C043D09502",
"lastTransactionKey": "B7103F204998813B889C77C043D09502",
"paymentKey": "5zJ4xY7m0kODnyRpQWGrN2xqGlNvLrKwv1M9ENjbeoPaZdL6",
"orderId": "a4CWyWY5m89PNh7xJwhk1",
"orderName": "토스 티셔츠 외 2건",
"currency": "KRW",
"method": "카드",
"status": "DONE",
"requestedAt": "2021-01-01T10:01:30+09:00",
"approvedAt": "2021-01-01T10:05:40+09:00",
"useEscrow": false,
"cultureExpense": false,
"card": {
"amount": 15000,
"company": "현대",
"number": "433012******1234",
"installmentPlanMonths": 0,
"isInterestFree": false,
"interestPayer": null,
"approveNo": "00000000",
"useCardPoint": false,
"cardType": "신용",
"ownerType": "개인",
"acquireStatus": "READY",
"receiptUrl": "https://merchants.tosspayments.com/web/serve/merchant/test_ck_jkYG57Eba3GoelzLmLQ3pWDOxmA1/receipt/5zJ4xY7m0kODnyRpQWGrN2xqGlNvLrKwv1M9ENjbeoPaZdL6"
},
"virtualAccount": null,
"transfer": null,
"mobilePhone": null,
"giftCertificate": null,
"foreignEasyPay": null,
"cashReceipt": null,
"receipt": {
"url": "https://merchants.tosspayments.com/web/serve/merchant/test_ck_jkYG57Eba3GoelzLmLQ3pWDOxmA1/receipt/5zJ4xY7m0kODnyRpQWGrN2xqGlNvLrKwv1M9ENjbeoPaZdL6"
},
"discount": null,
"cancels": null,
"secret": null,
"type": "NORMAL",
"easyPay": null,
"country": "KR",
"failure": null,
"totalAmount": 15000,
"balanceAmount": 15000,
"suppliedAmount": 13636,
"vat": 1364,
"taxFreeAmount": 0
}
응답데이터를 받을 수 있게된다.
https://docs.tosspayments.com/
토스페이먼츠 결제 연동 문서
온라인 비즈니스의 성공, 토스페이먼츠가 도울게요.
docs.tosspayments.com