IOS에서 자동결제창 호출 시 응답값 문의
안녕하세요. flutter로 자체 구축한 앱에서 IOS로 정기결제 빌링 호출(https://docs.tosspayments.com/sdk/v2/js#paymentrequestbillingauth) 시 응답값이 아래와 같이 오면서 정기결제 페이지가 뜨질 않습니다. key 값이 들어있어서 일부만 드립니다.
data:text/html;charset=utf-8,%20%20%20%20%20%20%20%20%20%20%3Chtml%3E%0A%20%20%20%20%20%20%20%20%20%20%20%20%3Chead%3E%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ctitle%3E%EA%B2%B0%EC%A0%9C%ED%95%98%EA%B8%B0%3C/title%3E%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cmeta%20charset='utf-8'%20/%3E%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cscript%20src='https://js.tosspayments.com/v1/payment'%3E%3C/script%3E%0A%20%20%20%20%20%20%20%20%20%20%20%20%3C/head%3E%0A%20%20%20%20%20%20%20%20%20%20%20%20%3Cbody%3E%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cscript%3E%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20var%
해당 값을 decode 하니 tossPayments 화면 띄우는 html이 그대로 들어있습니다.
Android는 응답값이 아래와 같이 오면서 정상적으로 페이지가 뜹니다.
https://payment-gateway.tosspayments.com/billing/mobile?clientKey=<**>&isMobile=true&payload=%7B%22customerKey%22%3A%22az1701480618331uRwuQ%22%2C%22successUrl%22%3A%22com.qtechai.cncare%3A%2F%2Fsuccesurl%22%2C%22failUrl%22%3A%22com.qtechai.cncare%3A%2F%2Ffailurl%22%2C%22referer%22%3A%22*%22%2C%22payMethod%22%3A%22%EC%B9%B4%EB%93%9C%22%7D>id=a241003JJvWCeXRHQA15WIadpFaZX03PnfkNQVI
답변 부탁드립니다.
좋은 하루 되세요.
토스페이먼츠 JavaScript SDK | 토스페이먼츠 개발자센터
토스페이먼츠 JavaScript SDK를 추가하고 메서드를 사용하는 방법을 알아봅니다.
59 Replies
⏳ 잠시만 기다려주세요! 곧 답변드리겠습니다
오류 문의일 경우 아래 정보를 미리 전달해주시면, 빠른 답변에 도움이 됩니다.
- 주문번호(orderId) :
- 문의 내용 :
(img를 함께 첨부해주시면 도움이됩니다)
* 계약관련 내용은 1544-7772로 문의주세요.
* 주말/공휴일에는 답변이 늦을 수 있어요.
안녕하세요 웹뷰에서 연동하고계신가요?
❤️ 기술문의 경험이 어떠셨나요?!
간단히 코멘트 남겨주세요! 제품 발전에 큰 힘이 됩니다.
네. 웹뷰로 연동하고 있습니다.
GitHub
GitHub - youjun-lee/flutter_webview_sample_app: flutter webview sample
flutter webview sample. Contribute to youjun-lee/flutter_webview_sample_app development by creating an account on GitHub.
웹뷰 띄우는건 문제 없습니다. 안드로이드에서 카드 정보 입력창 띄웠고요. 문제는 ios에서 동일하게 웹뷰를 띄웠을때 return 값이 최초 웹뷰 띄웠을때와 동일한 html 코드로 encoding 되어서 회신이 옵니다.
아래는 소스의 일부입니다.
body: state.status == ScreenStatus.loaded
? WebView(
initialUrl: Uri.dataFromString(
'''
<html>
<head>
<title>결제하기</title>
<meta charset='utf-8' />
<script src='https://js.tosspayments.com/v1/payment'></script>
</head>
<body>
<script>
var clientKey = '${F.tossClientKey}';
var tossPayments = TossPayments(clientKey); tossPayments .requestBillingAuth('카드', { customerKey: '${state.user?.tuyaUid}', successUrl: 'com.qtechai.cncare://succesurl', failUrl: 'com.qtechai.cncare://failurl', }) .catch(function (error) { if (error.code === 'USER_CANCEL') { // 결제 고객이 결제창을 닫았을 때 에러 처리 } }); </script> </body> </html> ''', mimeType: 'text/html', encoding: Encoding.getByName('utf-8'), ).toString(), javascriptMode: JavascriptMode.unrestricted, navigationDelegate: (NavigationRequest request) async { // com.qtechai.cncare://succesurl?customerKey=<...>&authKey=<...> var url = request.url; if (request.url .contains('com.qtechai.cncare://succesurl')) { context.read<TossWebViewCubit>().getResult(url); } return NavigationDecision.navigate; }, )
var tossPayments = TossPayments(clientKey); tossPayments .requestBillingAuth('카드', { customerKey: '${state.user?.tuyaUid}', successUrl: 'com.qtechai.cncare://succesurl', failUrl: 'com.qtechai.cncare://failurl', }) .catch(function (error) { if (error.code === 'USER_CANCEL') { // 결제 고객이 결제창을 닫았을 때 에러 처리 } }); </script> </body> </html> ''', mimeType: 'text/html', encoding: Encoding.getByName('utf-8'), ).toString(), javascriptMode: JavascriptMode.unrestricted, navigationDelegate: (NavigationRequest request) async { // com.qtechai.cncare://succesurl?customerKey=<...>&authKey=<...> var url = request.url; if (request.url .contains('com.qtechai.cncare://succesurl')) { context.read<TossWebViewCubit>().getResult(url); } return NavigationDecision.navigate; }, )
return 값이 최초 웹뷰 띄웠을때와 동일한 html 코드로 encoding 되어서 회신이 옵니다.말씀하시는 리턴값이 어떤걸까요?
return BaseScaffold(
onBack: () {
Navigator.pop(context);
},
body: state.status == ScreenStatus.loaded
? WebView(
initialUrl: Uri.dataFromString(
'''
<html>
<head>
<title>결제하기</title>
<meta charset='utf-8' />
<script src='https://js.tosspayments.com/v1/payment'></script>
</head>
<body>
<script>
var clientKey = '${F.tossClientKey}';
var tossPayments = TossPayments(clientKey); tossPayments .requestBillingAuth('카드', { customerKey: '${state.user?.tuyaUid}', successUrl: 'com.qtechai.cncare://succesurl', failUrl: 'com.qtechai.cncare://failurl', }) .catch(function (error) { if (error.code === 'USER_CANCEL') { // 결제 고객이 결제창을 닫았을 때 에러 처리 } }); </script> </body> </html> ''', mimeType: 'text/html', encoding: Encoding.getByName('utf-8'), ).toString(), javascriptMode: JavascriptMode.unrestricted, navigationDelegate: (NavigationRequest request) async { var url = request.url; if (request.url .contains('com.qtechai.cncare://succesurl')) { context.read<TossWebViewCubit>().getResult(url); } return NavigationDecision.navigate; }, ) : Container(), ); 위 소스에서 context.read<TossWebViewCubit>().getResult(url); 에서 읽은 값입니다.
var tossPayments = TossPayments(clientKey); tossPayments .requestBillingAuth('카드', { customerKey: '${state.user?.tuyaUid}', successUrl: 'com.qtechai.cncare://succesurl', failUrl: 'com.qtechai.cncare://failurl', }) .catch(function (error) { if (error.code === 'USER_CANCEL') { // 결제 고객이 결제창을 닫았을 때 에러 처리 } }); </script> </body> </html> ''', mimeType: 'text/html', encoding: Encoding.getByName('utf-8'), ).toString(), javascriptMode: JavascriptMode.unrestricted, navigationDelegate: (NavigationRequest request) async { var url = request.url; if (request.url .contains('com.qtechai.cncare://succesurl')) { context.read<TossWebViewCubit>().getResult(url); } return NavigationDecision.navigate; }, ) : Container(), ); 위 소스에서 context.read<TossWebViewCubit>().getResult(url); 에서 읽은 값입니다.
context.read<TossWebViewCubit>().getResult(url);
이거는 왜하는건가요?
그냥 requestBillingAuth 실행되면
저희 결제창 뜨는거아닌가요?
requestBillingAuth 실행하고 response 결과값을 가져오기 위함입니다.
문의내용은 위 소스로 호출 시 android는 정상적으로 결과가 회신되어 카드등록 페이지가 뜨는데 ios는 위 페이지 그대로 회신되어서 왜 차이가 있는지입니다.
두가지의 동작 방식이 다릅니다.
안드로이드는 URL 을 응답으로 받고 해당 URL 을 직접 띄우는 방식이고
IOS 는 해당 URL 의 html 을 직접 받는 방식으로 구현되어 있다고 알고 있습니다.
중간에 응답을 context.read<TossWebViewCubit>().getResult(url); 로 받지 마시고 저희 샘플에 있는대로 구현해 주시기 바랍니다.
저희 모듈에서 자체적으로 받아서 새로 웹뷰 열고 처리하도록 되어 있습니다.
equestBillingAuth 실행하고 response 결과값은 success 쪽으로 알아서 전달됩니다. 저렇게 받으실 필요가 없구요.
혹시 샘플 받을수 있을까요? 전에 이실장님이 주신건 단순 웹뷰 띄우는 flutter 샘플을 받아서요.
response 결과값을 가져오기 위함입니다.successUrl로 이동한 후를 말씀하시는건가요? 아니면 빌링결제창을 띄우고 싶으신건가요?
자동결제 카드등록 페이지이고 successUrl 이동 이후 아닐까요?
그리고 ios 의 경우 응답값이 최초 html 코드와 동일해서 자동결제 카드등록 페이지가 뜨질 않습니다.
그럼 context.read<TossWebViewCubit>().getResult(url); 이 값을 받아서 웹뷰에서 이동 시켜 주시는 건가요?
제가 정리해볼게요
넵넵
네. 값 받아서 그냥 바로 띄웁니다. 안드로이드의 경우엔 getResult 값이 자동결제 카드등록 페이지 html 코드가 와서 화면에 자동결제 페이지가 바로 로딩됩니다.
context.read<TossWebViewCubit>().getResult(url); 이게 . 왜필요한지 일단 알수가 없어요
tossPayments.requestBillingAuth이게 실행되면
자동으로 웹뷰가 전환되어야하는거 아닌가요?
일단 저희 샘플은 별도로 없는데, 과거 일반결제창 SDK가 비슷한 형식으로 만들어져 있어요
Dart packages
tosspayments_sdk_flutter package - All Versions
Pub is the package manager for the Dart programming language, containing reusable libraries & packages for Flutter and general Dart programs.
여기서 마지막 버전 다운로드 받은 .
ios는 결과값이 필요한 정보가 부족하다고 에러가 옵니다.
/lib/widget/payment_window_weibview
lib/tosspayments_sdk_flutter
2개한번 참고해봇에ㅛ
네. 일단 참고해서 다시 해보겠습니다. 감사합니다.
결과값이 부족할 수 밖에 없는게 원래 저희가 url을 직접 호출하는 방식이 저희스펙이 아니에요.
requestBillingAuth() method가 자동으로 redirect를 시켜줘야합니다.
url을 직접 받거나, html을 화면에 그려주는게 아니라
웹뷰위에서 requestBillingAuth() 스크립트를 실행시켜준다. 로 방향성을 잡으면 좋을 . 것같습니다.
일단 결제창 부터. 띄운후에 이어서 살펴보시죠
네. 근데 안드로이드는 정상적으로 되기 때문에 ios는 문제 없이 될거라고 생각했는데, ios만 에러 발생해서 문의 드렸던거고, 위 주신 링크 참고해서 다시 작업해 보도록 하겠습니다.
네 일단 flutter 웹뷰가 lib도 여러개이고 하다보니. android만 되는부분은 어떻게 설명드리기가 어렵네요 ㅠ
안녕하세요. 혹시 아래 패키지는 자동결제 구현에 참고가 안될까요. 주신 문서는 deprecated로 나와있어서요.
https://pub.dev/documentation/tosspayments_widget_sdk_flutter/latest/
tosspayments_widget_sdk_flutter - Dart API docs
tosspayments_widget_sdk_flutter API docs, for the Dart programming language.
네 결제위젯은 자동결제 사용하시면 안됩니다.
이거 사용 바랍니다.
현재 버전이 위 링크로 구현된거 확인했습니다. tosspayments webview 사용중이고 위에 언급드린대로 안드로이드는 정상 동작합니다. 제가 잘못 말씀드린듯 한데 2번째 스크린샷에 return NavigationDecision.navigate 여기서 안드로이드는 정상적으로 자동결제 카드추가 페이지가 로딩됩니다. 말씀하신것 처럼 getResult 없애고 ios 를 다시 테스트 해도 동일하게 html code가 그대로 webview에 뜨면서 정보가 부족하다고 나옵니다. 웹뷰에 로딩되는 값은 data:text/html;charset=utf-8,~~ 이 값이 뜹니다.


webview lib는 어떤거쓰시나요?
tosspayments_sdk_flutter 에서 제공하는 webview 사용하고 있습니다.
아무튼 이건 flutter/webview등의 동작문제여서
저희도 도움을드리기가 어렵네요
toss에서 제공하는 sdk의 webview를 이용해서 ios에서 정상 카드추가 화면 뜨는지 자체적으로 확인 되시는건가요?
혹시 requestBillingAuth말고
requestPayment도 동일하게 아이폰에서 안되시나요?
해보고 메시지 드리겠습니다.
일반결제는 아이폰에서 정상적으로 페이지 떴씁니다.
혹시 해결방법 없을까요?

재현이 안되서요.
도움드릴 수 있는 부분이 어렵습니다.
이건 제품 이슈가 아니라 연동과정중에 이슈가 있을 것으로 보여요
샘플 소스를 얻을수 있을까요.
GitHub
GitHub - youjun-lee/flutter_webview_sample_app: flutter webview sample
flutter webview sample. Contribute to youjun-lee/flutter_webview_sample_app development by creating an account on GitHub.
OP.GG
OP.GG - The Best LoL Builds and Tier List. Search Riot ID and Tagli...
The Best LoL Champion Builds and Player Stats by OP.GG - Learn champion builds, runes, and counters. Search Riot ID and Tagline for stats of all game modes.
inappwebview 쓰신건가요? 아니면 webview_flutter 쓰신건가요?
둘다 해봤는데 잘되요
네. 다시 해보겠습니다.
감사합니다.
InAppWebView 로 해도 동일하게 안드로이드는 되는데 아이폰은 결과가 같네요.
같은 아이폰에서 샘플 자체를 실행했을 때는 결제가 되셨나요?
저희 샘플앱실행해서, 웹서버 띄우신게 맞나요?
아뇨. 당사에서 개발한 앱에 InAppWebView 로 구현된 소스 이식하여 테스트 했습니다. 토스에서 제공한 샘플앱을 띄울수 없는게, 자동결제 샘플이 있나요?
자동결제 카드추가 샘플이 있나요? 공유받은 링크는 자동결제 카드 추가 페이지 샘플을 찾을수가 없네요.
GitHub - youjun-lee/flutter_webview_sample_app: flutter webview sample
https://pub.dev/packages/tosspayments_sdk_flutter/versions
GitHub
GitHub - youjun-lee/flutter_webview_sample_app: flutter webview sample
flutter webview sample. Contribute to youjun-lee/flutter_webview_sample_app development by creating an account on GitHub.
Dart packages
tosspayments_sdk_flutter package - All Versions
Pub is the package manager for the Dart programming language, containing reusable libraries & packages for Flutter and general Dart programs.
웹서버에 올려서 띄워주는 방식이에요
샘플앱을 사용하시고 그 안에 띄우는 웹서버상의 html 을 자동결제창 호출하는 것으로 하시면 됩니다.
웹서버 없으세요?
네. 웹서버 따로 없고 backend 서버만 있습니다.
일단 웹뷰 문제인지 확인이 필요할 것 같아요. 위 깃헙에서 웹url입력하면 페이지 로딩되는 것 까지는 이해하셨죠?
네. 이해했습니다. 웹서버가 없기 때문에 강제로 html 코딩을 flutter에 넣었습니다.
https://member.op.gg/membership?hl=ko-KR 이거 이상없는지 한번 확인해주시겠어요?
더불어 계약된 mid도 한번 확인 요청드려요
위 웹페이지를 웹뷰에 넣어서 테스트 해보라는 얘기시죠?
mid는 bill_kweon5kr9 입니다.
네 맞습니다.