Flutter WebView 내에서 KG이니시스를 연동하여 결제 시스템 구현중 각 은행 사 어플로 인텐트 처리가 되지 않는 오류가 발생했다.
WebView 에서 띄운 것이기도 하고 네이티브에서 뭘 설정해야 할 게 있을까 웹쪽에서 해결해야하는 게 아닌가 했지만! 클라이언트 쪽에서 처리해야 하는 로직이 필수적이다!
먼저, 은행 사 어플로 이동하는 버튼 클릭 시 내가 만난 오류 화면이다.
flutter는 http 또는 https로 시작하는 url만 url 처리를 할 수 있다. 하지만 은행 사 어플을 불러오는 url은 intent:// 등 http/https가 아닌 주소로 시작하기 때문에 발생하는 오류페이지다.
이를 해결하기 위해선 'MethodChannel'을 이용해야 한다.
아래의 링크를 통해 공식 홈페이지에서 아키텍처 구조와 MethodChannel 사용 예까지 자세한 정보를 확인할 수 있다.
Platform Channel 공식 홈페이지 이동하기
채널을 이용하기 위해서 다음과 같이 MethodChannel을 정의한다.
static const platform = MethodChannel('fcm_default_channel');
적용시켜 보면, flutter webview의 navigationDelegate가 url이 변경되는 걸 listen하고 있으면서 메서드 채널을 통해 그 url을 네이티브에서 받아 flutter 내에서 다룰 수 있는 url로 파싱한 후, flutter는 그 url값을 받아 navigate 시키는 것이다.
navigationDelegate: (request) async{
LogPrint('navigate url : ${request.url}');
// 2 채널이용
if(!request.url.startsWith('http') && !request.url.startsWith('https')) {
if(Platform.isAndroid) {
getAppUrl(request.url.toString());
return NavigationDecision.prevent;
}else if(Platform.isIOS){
if (await canLaunchUrl(Uri.parse(request.url))) {
LogPrint('navigate url : ${request.url}');
await launchUrl(Uri.parse(request.url),);
return NavigationDecision.prevent;
}
}
}
return NavigationDecision.navigate;
},
IOS의 경우 launchUrl를 사용하여, Android의 경우 채널을 이용하여 처리한다. 이때! return에 NavigationDecision은 꼭 Prevent를 적용해야 한다.
Future getAppUrl(String url) async {
await platform.invokeMethod('getAppUrl', <String, Object>{'url': url}).then((value) async{
LogPrint('paring url : $value');
if(value.toString().startsWith('ispmobile://')) {
await platform.invokeMethod('startAct', <String, Object>{'url': url}).then((value) {
LogPrint('parsing url : $value');
return;
});
}
if (await canLaunchUrl(Uri.parse(value))) {
await launchUrl(Uri.parse(value),);
return;
} else {
showNotiDialog(context, '해당 앱 설치 후 이용바랍니다.');
return;
}
});
}
invokeMethod를 활용하여 해당 url을 네이티브로 보내고 future value값을 체크하는 기능을 담고 있다.
private var CHANNEL = "fcm_default_channel"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
GeneratedPluginRegistrant.registerWith(flutterEngine!!)
MethodChannel(flutterEngine!!.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler { call, result ->
when {
call.method.equals("getAppUrl") -> {
try {
val url: String = call.argument("url")!!
val intent = Intent.parseUri(url, URI_INTENT_SCHEME)
result.success(intent.dataString)
} catch (e: URISyntaxException) {
result.notImplemented()
} catch (e: ActivityNotFoundException) {
result.notImplemented()
}
}
}
}
이렇게 하면 result.success(intent.dataString)값으로 파싱된 url 값이 가고 2번에서 정의한 getAppUrl함수에서 future value값으로 사용할 수 있다.
이 오류를 해결함에 앞서 구글에 있는 레퍼런스를 모두 보다시피 했지만 이렇다하게 작성된 글을 찾을 수 없었고.. 나도 결제 시스템 도입을 처음하다 보니 정말 애를 많이 먹었다. 나와 같은 오류로 애먹고 있는 분들의 시간을 아껴주고자 작성해본다..
해결하고 나면 이렇게 간단한 것을 😫
오늘도 성장중 ...
안녕하세요~!
flutter 웹뷰에서 결제관련 자료 찾다가 방문하였습니다.
혹시 아이폰에서는 info.plist만 추가해도 정상적으로 작동 잘 하셨나요?
저같은 경우에는 현대카드를 예로들면
-canOpenURL: failed for URL: "hdcardappcardansimclick://appcard?acctid=202210201739132058335170737671" - error: "The operation couldn’t be completed. (OSStatus error -10814.)"
와같이 로그가 남으며 앱이 자동으로 뜨지가 않아서요~
hdcardappcardansimclick는 info.plist에 등록이 되어있는 상황입니다.
혹시 다른작업을 하신게 있다면 조언 부탁드립니다.
감사합니다~!
안녕하세요. 플러터 초보가 도움을 구하고자 댓글로 문의 남깁니다. ㅠㅠ
작성하신 소스에
await platform.invokeMethod('startAct', <String, Object>{'url': url}).then((value) {
LogPrint('parsing url : $value');
return;
});
이부분은 어떤 역할을 하는건가요?
mainActivity에 구현되어 있지않아 유추하기가 힘들어서요 ㅠ
저 부분을 무시하고 진행하면 유효하지 않은 거래라 에러가 나서 도움 구하고자 댓글 남깁니다!
안녕하세요. 많은 도움이 됐습니다. 감사합니다.!!
혹시 해당 기능을 이용해서 인텐트를 띄우는 것은 잘 되나
현대카드 -> 일반결제 같은 경우 팝업형태로 백신 앱이 실행되는데
이 과정에서 오류가 발생합니다. 혹시 같은 문제를 겪으신적이 있을까요..?
오랜 시간 찾아본 결과.. 한분은 크롬99 이상일 때 그러한 현상이 일어난다고 하시던데..
도움좀 받을 수 있을까요..
"다음 이유로 intent://%7B"CD":"1c3326" 이런식의 오류 웹페이지가 뜨네요ㅠㅠ..
기술팀에 문의도 해봤지만,, 아무래도 플러터라 시원한 답을 못해주는 것 같네요..
좋은 자료 감사합니다.