Henu
Henu2y ago

Javascript SDK 결제 모듈 연동에서 requestPayment 메소드 호출 성공 후 처리 방식에 대한 피드백

안녕하세요. Java 언어 진영의 스프링 프레임워크를 사용하며 토스페이먼츠에서 제공하는 Javascript SDK 연동 가이드를 바탕으로 결제 모듈을 연동하고 있습니다. (사용중인 API 버전: 2202-07-27) SDK 통해 결제 모듈을 사용하기 위해 우선 TossPayments 인스턴스를 생성하고 requestPayment('결제수단[카드/계좌이체 등]', '결제 요청 데이터') 메소드를 호출함으로써 결제창이 출력되고 있습니다. 이 쯤해서 가이드 문서(API > Javascript SDK > 일반결제)를 확인해 보니 requestPayment 메소드의 경우 Javascript 'Promise' 객체가 응답으로 돌아오기 때문에 결제 모듈에서 발생하는 에러에 대한 핸들링(Javascript 수준의)을 할 수 있는 것으로 확인이 되었습니다.
/**
* (예제코드) 토스페이먼츠 결제창 호출
*
* @param method 결제 수단
* @param data 결제 요청 데이터
*/
function pay(method, data) {
tossPayments
.requestPayment(method, data)
.catch(function (err) {
// TODO 대표적으로 결제창이 닫히는 경우 호출 되는 것으로 보임.
// console.log(err.code);
// console.log(err.message);
});
}
/**
* (예제코드) 토스페이먼츠 결제창 호출
*
* @param method 결제 수단
* @param data 결제 요청 데이터
*/
function pay(method, data) {
tossPayments
.requestPayment(method, data)
.catch(function (err) {
// TODO 대표적으로 결제창이 닫히는 경우 호출 되는 것으로 보임.
// console.log(err.code);
// console.log(err.message);
});
}
이것 외에 결제 처리가 모두 성공했을 때에 대한 핸들링도 할 수 있었으면 하는데요. 여기서의 '성공'이란 '카드 결제수단을 예를 들어, 요청 데이터로 전달하는 'successUrl' 및 결제 승인 AP까지 모두 처리된 상태로 의미를 두었습니다. 저의 경우 successUrl를 처리하는 Back-end 소스코드에서 결제승인 API까지 호출하여 최종 승인 처리를 진행하고 있는데요. 스프링 프레임워크를 사용하는 특성상 successUrl 호출 후 최종 응답으로는 '결제 성공/실패 페이지' 로 전환되고 있습니다. 다만, 이를 페이지 전환이 아닌 JSON 문자열 데이터로 결제 모듈을 호출하는 페이지쪽으로 반환하여 해당 페이지에서 '후 처리'를 진행하고 싶다는 생각이 들었습니다.
/**
* 바라는 방향성(?)
* (예제코드) 토스페이먼츠 결제창 호출
*
* @param method 결제 수단
* @param data 결제 요청 데이터
*/
function pay(method, data) {
tossPayments
.requestPayment(method, data)
.then(function (res) {
// TODO 서버로부터 반환된 res 데이터를 가지고 후 처리
})
.catch(function (err) {
// TODO 대표적으로 결제창이 닫히는 경우 호출 되는 것으로 보임.
// console.log(err.code);
// console.log(err.message);
});
}
/**
* 바라는 방향성(?)
* (예제코드) 토스페이먼츠 결제창 호출
*
* @param method 결제 수단
* @param data 결제 요청 데이터
*/
function pay(method, data) {
tossPayments
.requestPayment(method, data)
.then(function (res) {
// TODO 서버로부터 반환된 res 데이터를 가지고 후 처리
})
.catch(function (err) {
// TODO 대표적으로 결제창이 닫히는 경우 호출 되는 것으로 보임.
// console.log(err.code);
// console.log(err.message);
});
}
이러한 요구사항에 대한 변경이 가능하실지 궁금합니다! P.S. 제가 가이드 문서를 꼼꼼히 살펴본 상황은 아니기에 해당 포스트에 언급한 피드백이 이미 처리 가능한 상황일 수도 있다는 점 양해바랍니다. 또한, 작성하다보니 QA 느낌이 있는 점도 양해 바랍니다..! 참고한 API 문서
일반 결제 JavaScript SDK | 토스페이먼츠 개발자센터
토스페이먼츠 일반 결제 JavaScript SDK 사용을 위한 준비와 메서드 사용법, 결제 실패 및 에러 처리 방법을 알아봅니다.
20 Replies
토스페이먼츠 BOT
⏳ 잠시만 기다려주세요! 곧 답변드리겠습니다
오류 문의일 경우 아래 정보를 미리 전달해주시면, 빠른 답변에 도움이 됩니다.
- 주문번호(orderId) : - 문의 내용 :
(img를 함께 첨부해주시면 도움이됩니다)
* 계약관련 내용은 1544-7772로 문의주세요. * 주말/공휴일에는 답변이 늦을 수 있어요.
이실장
이실장2y ago
안녕하세요! 항상 문의사항도 자세하게 남겨주시고, 가이드문서도 꼼꼼하게 읽어주셔서 감사드립니다. 연동은 잘 마무리 하셨나요? 보내주신 의견은 저희 제품담당 개발자 분께서 확인해주실 예정입니다! 토스페이먼츠 결제제품 사용하시면서 궁금하신 점은 언제든 채널 찾아와서 남겨주세요 🙂
Henu
Henu2y ago
네, 현재 만들고 있는 서비스에서는 카드/가상계좌/계좌이체 결제수단만 지원할 것이기에 해당 수단들에 대한 결제하기(입금) 연동은 이전 질문에 대한 답변들을 토대로 성공적으로 끝마쳤습니다! 피드백에 대한 관심 감사드립니다.
Kimoon Lee
Kimoon Lee2y ago
@Henu 님 요청 주신 내용에 대해 문의가 있는데요
다만, 이를 페이지 전환이 아닌 JSON 문자열 데이터로 결제 모듈을 호출하는 페이지쪽으로 반환하여 해당 페이지에서 '후 처리'를 진행하고 싶다는 생각이 들었습니다.
라고 하셨는데, successURL 로 redirect 이동하는 방식이 아닌 promise로 받고 싶으신 것이라고 이해하면 될까요?
Henu
Henu2y ago
답변이 늦어졌네요..! 담당자님께서 말씀해주신 내용이 맞습니다. 피드백 내용을 좀 더 이해하는데 도움이 될까 싶어 구현한 소스코드를 아래와 같이 공유드립니다.
/**
* (successUrl 백엔드 소스코드(예))결제 성공 처리
*
* @param paymentKey 결제건에 대한 고유한 키 값
* @param orderId 상점에서 주문건을 구분하기 위해 발급한 고유 ID
* @param amount 실제로 결제된 금액
* @param model
* @return
* @throws Exception
*/
@RequestMapping(value = "/success.do")
public String confirmPayment(@RequestParam(value = "paymentKey", required = true) String paymentKey,
@RequestParam(value = "orderId", required = true) String orderId,
@RequestParam(value = "amount", required = true) Integer amount,
Model model) {
return pgPaymentsService.handlePayment(paymentKey, orderId, amount) ?
"redirect:{성공 시 전환되는 페이지}" : "redirect:{실패 시 전환되는 페이지}";
}
/**
* (successUrl 백엔드 소스코드(예))결제 성공 처리
*
* @param paymentKey 결제건에 대한 고유한 키 값
* @param orderId 상점에서 주문건을 구분하기 위해 발급한 고유 ID
* @param amount 실제로 결제된 금액
* @param model
* @return
* @throws Exception
*/
@RequestMapping(value = "/success.do")
public String confirmPayment(@RequestParam(value = "paymentKey", required = true) String paymentKey,
@RequestParam(value = "orderId", required = true) String orderId,
@RequestParam(value = "amount", required = true) Integer amount,
Model model) {
return pgPaymentsService.handlePayment(paymentKey, orderId, amount) ?
"redirect:{성공 시 전환되는 페이지}" : "redirect:{실패 시 전환되는 페이지}";
}
위 코드는 상점의 주문 페이지에서 토스페이먼츠 결제 창을 통해 성공적으로 결제가 이루어 진 후 successUrl을 호출하면서 토스페이먼츠로부터 전달된 데이터를 pgPaymentsService.handlePayment() 메소드의 파라미터로 전달하는 것을 확인해 볼 수 있습니다. 다음 코드는 pgPaymentsService.handlePayment() 메소드 구현 코드의 일부입니다.
/**
* successUrl 호출 시 동작하는 메소드 간략설명
* 1. 결제 금액 유효성 검사
* 2. 결제 승인 API 호출
* 3. API 호출 결과에 따라 결제/결제실패 정보 DB에 저장
* 4. 주문 데이터 '입금상태' 업데이트
* 기타 등등
*
* @param paymentKey 결제건에 대한 고유한 키 값
* @param orderId 상점에서 주문건을 구분하기 위해 발급한 고유 ID
* @param amount 실제로 결제된 금액
* @param model
* @return
* @throws Exception
*/
@Override
public boolean handlePayment(String paymentKey, String orderId, Integer amount) {
boolean paymentSuccess = false;

// 이전 code...

// 결제 API 호출
HttpEntity<String> request = new HttpEntity<>(objectMapper.writeValueAsString(payloadMap), headers);
responseEntity = restTemplate.postForEntity("https://api.tosspayments.com/v1/payments/confirm", request, JsonNode.class);

try {
// 결제 API 호출 결과 처리
if (responseEntity.getStatusCode() == HttpStatus.OK) {
// TODO 성공 처리
paymentSuccess = true;
} else {
// TODO 실패 처리
throw new Exception("결제 승인 API 결과 ==> 실패");
}

// 나머지 code...
} catch (Exception e) {

}

return paymentSuccess;
}
/**
* successUrl 호출 시 동작하는 메소드 간략설명
* 1. 결제 금액 유효성 검사
* 2. 결제 승인 API 호출
* 3. API 호출 결과에 따라 결제/결제실패 정보 DB에 저장
* 4. 주문 데이터 '입금상태' 업데이트
* 기타 등등
*
* @param paymentKey 결제건에 대한 고유한 키 값
* @param orderId 상점에서 주문건을 구분하기 위해 발급한 고유 ID
* @param amount 실제로 결제된 금액
* @param model
* @return
* @throws Exception
*/
@Override
public boolean handlePayment(String paymentKey, String orderId, Integer amount) {
boolean paymentSuccess = false;

// 이전 code...

// 결제 API 호출
HttpEntity<String> request = new HttpEntity<>(objectMapper.writeValueAsString(payloadMap), headers);
responseEntity = restTemplate.postForEntity("https://api.tosspayments.com/v1/payments/confirm", request, JsonNode.class);

try {
// 결제 API 호출 결과 처리
if (responseEntity.getStatusCode() == HttpStatus.OK) {
// TODO 성공 처리
paymentSuccess = true;
} else {
// TODO 실패 처리
throw new Exception("결제 승인 API 결과 ==> 실패");
}

// 나머지 code...
} catch (Exception e) {

}

return paymentSuccess;
}
위 코드를 보면 결제 승인 API를 호출하며 API 호출 결과에 따른 처리가 이루어집니다. 결제 승인 성공: 결제 정보 DB 등록 등 결제 승인 실패: 강제 에러 발생 처리 등 메소드의 반환 타입을 확인해 보시면 boolean(참/거짓) 타입으로 설정하였으며, 이 반환 값을 가지고 결제 성공/실패 페이지로 전환(Redirect)된다고 생각해 주시면 되겠습니다. 서론이 길었지만 결국 하고자 하는 것은 페이지 전환이 아닌 pgPaymentsService.handlePayment() 메소드 반환 값에 따라 상점에서 생성한 임의의 JSON 문자열로 응답 데이터를 반환하고 싶다라는 것이었습니다. [JSON 문자열(예)] true: {"status": "success", message: "결제가 성공적으로 완료되었습니다.", "orderId": "XXXXXXXX"} false: {"status": "fail", message: "결제 실패하였습니다. 실패 이유를 확인하시어 다시 결제해 주십시오.", errMessage: "잔액부족", "orderId": "XXXXXXXX"} (해당 댓글에서 언급된 코드들은 추후 개선이 필요한 코드인 점을 감안하시고, 스프링 프레임워크 소스코드를 예로 든 점 이해 부탁드리겠습니다!)
Ayaan
Ayaan2y ago
음, 혹시 아임포트 이용시처럼 성공한 경우 callback으로 값을 받아 처리하고(모바일은 기존 방식 사용) 이외 오류도 모두 캐치콜백으로 전달받기를 원하신다는 말씀이신가요?
Henu
Henu2y ago
제가 아임포트 서비스를 이용해 본 적은 없어서요! 다만, 말씀해 주신데로 자바스크립트 수준에서의 Callback 처리를 하고 싶다라는 것이었습니다. 마침, requestPayment() 메소드에서의 에러 Catch는 지원하는 것으로 확인이 되었거든요.
Ayaan
Ayaan2y ago
아 넵! 아임포트를 통해 서비스를 이용하시는 경우 콜백으로 받으실 수 있습니다. SDK 단에서의 지원 계획 여부는 토스페이먼츠쪽에서 결정해야할 사안인거 같습니다. 만약 지원하더라도 모바일 환경에서는 페이지 전환이 불가피 한점 양해 부탁드립니다 🙏 아임포트 서비스도 모바일만 따로 페이지를 전환하여 확인하도록 하고 있습니다
Ayaan
Ayaan2y ago
참고: 아임포트 연동 가이드의 코드스니펫
No description
Ayaan
Ayaan2y ago
위 코드를 보시면 Promise는 아닌거 같지만 콜백단위로 받으실 수 있습니다. 이를 sdk에서 지원하는 것을 원하시는거 같네요
Henu
Henu2y ago
내용 공유 감사드립니다. 언급해주신데로 토스페이먼츠측에서 자체적으로 콜백을 받아 처리할 수는 없을지 해서 피드백 포스트를 올리게 되었네요!
No description
Ayaan
Ayaan2y ago
네네, 저는 제대로 이해했습니다. 대신 말씀드렸다시피 모바일에서는 기존방식대로 화면이 전환되어야 합니다. 그 부분이 가장 흠인거 같네요
이현섭
이현섭2y ago
@Ayaan 님이 말씀하신대로 모바일에서의 일관성 때문에 인터페이스가 애매하게 설계되게 되어서 진행중이었던 아이템이었지만 드랍하게 되었습니다 ㅠ
Henu
Henu2y ago
아... 그렇군요. 그렇다면 페이지 전환은 불가피하겠네요! 알겠습니다. 피드백 읽어주시느라 수고 많으셨습니다!
토스페이먼츠 BOT
❤️ 기술문의 경험이 어떠셨나요?!
간단히 코멘트 남겨주세요! 제품 발전에 큰 힘이 됩니다.
Ayaan
Ayaan2y ago
한가지 아이디어가 생겼는데, UA값이 모바일 device로 보이거나 특정 width가 낮은경우 mobile로 간주하고 iframe창을 height: 100vh; width: 100vw;로 화면에 꽉 채워 로드하게 하면 리다이렉트랑 동일한 효과를 볼 수 있지 않을까 싶습니다. 이때는 PC처럼 callback함수나 Promise로 반환받을 수 있으니까요. 저는 지금 프로젝트에서 아임포트를 자주 사용하다 보니, 타 PG에 접할 일이 많습니다. 몇몇 PG는 시범적으로나마 PC/Mobile모두 iframe으로 로드하는 모습을 보았습니다.
Kimoon Lee
Kimoon Lee2y ago
해당 방식이 카드사가 권장하는 방식이 아닙니다 특히 일반결제처럼 카드 번호를 직접 입력하는 경우 모바일에서 iframe으로 창을 띄우면 보안키패드가 CORS 오류로 표시 되지 않는 등의 문제가 발생할수 있습니다. 카드사 창이 어떻게 계속 수정될지 알수 없는 부분이라 iframe 을 사용하는 부분은 조심스럽습니다.
Ayaan
Ayaan2y ago
그렇군요. 확인 감사드립니다.
이실장
이실장2y ago
@Henu 님 안녕하세요! requestPayments에서 Promise 방식이 지원하게 되어 태그드립니다.