# KCP 휴대폰 본인인증 연동 (iOS/AOS)

휴대폰 본인인증 모듈은 iOS/AOS 어플리케이션 내 webview를 이용하여 연동합니다.

* [iOS 매뉴얼](#ios)
* [AOS 매뉴얼](#aos)

{% hint style="info" %}
휴대폰 본인인증 모듈에 대한 가이드는 [휴대폰 본인인증](https://workspace-help.nhn-commerce.com/aurora-guide/api-1/sms-authentication) 문서를 참고하시길 바랍니다.&#x20;
{% endhint %}

***

### iOS 매뉴얼

#### 🅐 Xcode 설정 (iOS PASS 앱 관련 스키마 등록) <a href="#f0-9f-85-90-xcode-ec-84-a4-ec-a0-95-ios-pass-ec-95-b1-ea-b4-80-eb-a0-a8-ec-8a-a4-ed-82-a4-eb-a7-88-e" id="f0-9f-85-90-xcode-ec-84-a4-ec-a0-95-ios-pass-ec-95-b1-ea-b4-80-eb-a0-a8-ec-8a-a4-ed-82-a4-eb-a7-88-e"></a>

통신사의 PASS 앱(간편본인확인 앱)이 업데이트됨에 따라 iOS에서 가맹점 앱 서비스를 제공하는 경우, iOS9부터 보안을 강화하는 목적으로 앱을 호출할 때 앱 스키마를 등록해주어야 합니다.

| 통신사 | 앱 스키마              |
| --- | ------------------ |
| SKT | tauthlink          |
| KT  | ktauthexternalcall |
| LG  | upluscorporation   |

\
\&#xNAN;**ⓐ Info.plist 파일에 LSApplicationQueriesSchemes 배열을 정의하여 앱 스키마를 등록합니다.**

* ㉠ Information Property List에 LSApplicationQueriesSchemes를 <mark style="background-color:yellow;">Array</mark> 타입으로 추가합니다.
* ㉡ LSApplicationQueriesSchemes 하위 리스트에 String 타입으로 <mark style="background-color:yellow;">Item</mark>을 추가합니다.
* ㉢ `Item` 마다 앱 스키마를 입력합니다.

<div align="left"><figure><img src="https://67612295-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FLt0GowPleXLWRjzGIAiV%2Fuploads%2FTTZnbafoBnFGM3DTwXz6%2Fkcp_cert_ios_2.jpg?alt=media&#x26;token=4b2c1dd2-5861-471d-80f6-bb3185e05410" alt=""><figcaption></figcaption></figure></div>

**ⓑ KCP 휴대폰 본인인증 완료 후 key값을 넘겨받기 위해 스키마를 등록합니다.**

<figure><img src="https://67612295-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FLt0GowPleXLWRjzGIAiV%2Fuploads%2FxiSyEiexLX6nvPMTJVCC%2Fkcp_cert_ios_1.jpg?alt=media&#x26;token=7292ce32-d8ef-4121-b595-968b37a10e97" alt=""><figcaption></figcaption></figure>

#### 🅑 HTML form 획득 <a href="#f0-9f-85-91html-form-ed-9a-8d-eb-93-9d" id="f0-9f-85-91html-form-ed-9a-8d-eb-93-9d"></a>

KCP 휴대폰 본인인증 모듈 팝업을 출력하기 위해서 GET /kcp/id-verification을 통해 KCP로 제출할 HTML form을 요청해야 합니다.

> [GET /kcp/id-verification/form](https://docs.shopby.co.kr/?url.primaryName=auth/#/KCPCertification/get-kcp-id-verification-form)
>
> ▶ KCP 본인인증 요청하기\
> 본인 인증을 위한 form을 생성합니다.

{% hint style="info" %}
&#x20;위 API가 아닌 임의로 form을 작성할 경우, NHN커머스 샵바이 측으로 callback을 받을 수 없습니다.

반드시 위 API 응답값으로 받은 form을 사용하시길 바랍니다.
{% endhint %}

* 응답값은 text/html 형식의 html form 양식입니다.
* returnURI는 <mark style="background-color:yellow;">scheme://</mark> 형식으로 작성합니다. (예 shopbyexample://form 🅐 - ⓑ 이미지 참고)
* 해당 API 호출시 <mark style="background-color:yellow;">request header</mark>내 <mark style="background-color:yellow;">platform</mark> 정보에 반드시 iOS를 입력하셔야 PASS 앱을 위한 아래의 추가변수가 form양식에 넘어옵니다.

<mark style="color:purple;">**✓ 예시코드**</mark>

```
<input type="hidden" name="kcp_cert_pass_use" value="Y"/>
```

#### 🅒 구현하기 <a href="#f0-9f-85-92-ea-b5-ac-ed-98-84-ed-95-98-ea-b8-b0" id="f0-9f-85-92-ea-b5-ac-ed-98-84-ed-95-98-ea-b8-b0"></a>

**ⓐ WKWebview 구성**

KCP 휴대폰본인인증 모듈은 웹으로 지원되기 때문에 WKWebview로 구성해야 합니다.

{% code overflow="wrap" %}

```
wkWebView.navigationDelegate = self
wkWebView.uiDelegate = self
wkWebView.configuration.preferences.javaScriptCanOpenWindowsAutomatically = true
```

{% endcode %}

**ⓑ webview - LoadData**

GET /kcp/id-verification호출로 전달받은 form을 webview에 로드합니다.

{% code overflow="wrap" %}

```
var htmlString = """
<html><title>KCPCert</title>
<body>\(formData)</body>  // formData 는 html form 양식입니다.
</html>
"""wkWebView.loadHTMLString(htmlString, baseURL: Bundle.main.bundleURL)
```

{% endcode %}

**ⓒ form.submit()**

webview의 <mark style="background-color:yellow;">evaluateJavaScript</mark>을 이용하여 form을 submit합니다.\
form이 submit되면 webview에서 새 창으로 KCP 휴대폰 본인인증 페이지가 노출됩니다.

{% hint style="info" %}
휴대폰 본인인증 화면은 WKWebview에서 새 창으로 열릴 수 있도록 구성해 주셔야 합니다. (ⓒ참고)

`evaluateJavaScript`는 html 폼 양식이 완전히 로드된 후( `webview didFinish`시점) 호출해 주시면 됩니다.
{% endhint %}

{% code overflow="wrap" %}

```
let injectJavaScript: String = """
// form id : form_auth
document.getElementById("form_auth").submit();
"""
var jsStr = injectJavaScript.trimmingCharacters(in: .whitespaces)
jsStr = jsStr.trimmingCharacters(in: .whitespacesAndNewlines)
jsStr = jsStr.trimmingCharacters(in: .newlines)
wkWebView.evaluateJavaScript(jsStr, completionHandler: nil)
```

{% endcode %}

**ⓓ WKWebview 구현**

WKWebview를 새 창으로 구현합니다.

아래 구현방식은 KCP 휴대폰 본인인증 연동을 위한 샘플이며, 개발 시 참고용으로 사용하시길 바랍니다.

{% code overflow="wrap" %}

```
func webView(_ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration, for navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures) -> WKWebView? {
    let createWebView = WKWebView(frame: self.wkWebView.frame, configuration: configuration)
    createWebView.navigationDelegate = self
    createWebView.uiDelegate = self
    self.view.addSubview(createWebView)
    return createWebView
}
```

{% endcode %}

**ⓔ Key 파라미터 획득**

<mark style="background-color:yellow;">returnURI</mark>로 전달받은 <mark style="background-color:yellow;">URI</mark>를 parsing하여 <mark style="background-color:yellow;">key</mark>값을 추출합니다.\
WKWebview Delegate (WKNavigationDelegate)

{% code overflow="wrap" %}

```
public func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, preferences: WKWebpagePreferences, decisionHandler: @escaping (WKNavigationActionPolicy, WKWebpagePreferences) -> Void) {
    var policy = WKNavigationActionPolicy.allow
    if let requestURL = navigationAction.request.url {
        if requestURL.scheme == "shopbyexample"  {
                        if let host = requestURL.host, host == "form" {
                            if let _ = requestURL.query as String? {
                                if let urlComponent = URLComponents(url: requestURL, resolvingAgainstBaseURL: true) {
                                    let queryItems = urlComponent.queryItems
                                    if let key = queryItems?.first(where: { $0.name == "key" })?.value {
                                        print("key : \(key)")
                                    }
                                }
                            }
                            policy = .allow
                            decisionHandler(policy, preferences)
                            return
                        }
                    }
                    UIApplication.shared.open(requestURL, options: [:], completionHandler: nil)
                    policy = .cancel
    }
    decisionHandler(policy, preferences)
}
```

{% endcode %}

#### 🅓 본인인증 결과 조회 <a href="#f0-9f-85-93-eb-b3-b8-ec-9d-b8-ec-9d-b8-ec-a6-9d-ea-b2-b0-ea-b3-bc-ec-a1-b0-ed-9a-8c" id="f0-9f-85-93-eb-b3-b8-ec-9d-b8-ec-9d-b8-ec-a6-9d-ea-b2-b0-ea-b3-bc-ec-a1-b0-ed-9a-8c"></a>

해당 <mark style="background-color:yellow;">key</mark>로 본인인증 인증 성공 및 실패 여부를 판단한 뒤, 화면에 성공 및 실패 결과를 전달합니다.

> [GET /kcp/id-verification/response](https://docs.shopby.co.kr/?url.primaryName=auth/#/KCPCertification/get-kcp-id-verification-response)
>
> ▶ KCP 본인인증 결과 조회하기\
> NHN KCP 본인인증 결과를 확인합니다.

#### 🅔 샘플코드(참고) <a href="#f0-9f-85-94-ec-83-98-ed-94-8c-ec-bd-94-eb-93-9c-ec-b0-b8-ea-b3-a0" id="f0-9f-85-94-ec-83-98-ed-94-8c-ec-bd-94-eb-93-9c-ec-b0-b8-ea-b3-a0"></a>

[KCPCertViewController.swift](https://nhnent.dooray.com/share/tree/WoOk8q6KT1K3MZcLSuyZsw/pages/3615381963904646601/files/3615931128094936280) 참고

***

### AOS 매뉴얼

#### 🅐 webview 세팅 <a href="#f0-9f-85-90-webview-ec-84-b8-ed-8c-85" id="f0-9f-85-90-webview-ec-84-b8-ed-8c-85"></a>

KCP 본인인증을 모바일로 사용하기 위해 webview를 세팅합니다.

{% code overflow="wrap" %}

```
binding.webView.apply {
    settings.apply {
        javaScriptEnabled = true
        javaScriptCanOpenWindowsAutomatically = true
        supportMultipleWindows()
    }
}
```

{% endcode %}

#### 🅑 HTML form 획득 <a href="#f0-9f-85-91-html-form-ed-9a-8d-eb-93-9d" id="f0-9f-85-91-html-form-ed-9a-8d-eb-93-9d"></a>

KCP 휴대폰 본인인증 모듈 팝업을 출력하기 위해서 GET /kcp/id-verification을 통해 KCP로 제출할 HTML form을 요청해야 합니다.

> [GET /kcp/id-verification/form](https://docs.shopby.co.kr/?url.primaryName=auth/#/KCPCertification/get-kcp-id-verification-form)
>
> ▶ KCP 본인인증 요청하기\
> 본인 인증을 위한 form을 생성합니다.

{% hint style="info" %}
위 API가 아닌 임의로 form을 작성할 경우, NHN커머스 샵바이 측으로 callback을 받을 수 없습니다.

반드시 위 API 응답값으로 받은 form을 사용하시길 바랍니다.
{% endhint %}

* 응답값은 text/html 형식의 html form 양식입니다.
* returnURI는 <mark style="background-color:yellow;">scheme://</mark> 형식으로 작성합니다. (예 shopbyexample://form.html)

**ⓐ webview - LoadData**

GET /kcp/id-verification을 호출하여 전달받은 form을 webview에 로드합니다.

{% code overflow="wrap" %}

```
loadDataWithBaseURL(baseUrl, apiResponseData, "text/html", "UTF-8", "")
```

{% endcode %}

**ⓑ form.submit()**

webview의 <mark style="background-color:yellow;">evaluateJavaScript</mark>을 이용하여 form을 submit합니다.\
form이 submit되면 webview에서 KCP 휴대폰 본인인증 페이지로 리다이렉트 됩니다.

{% code overflow="wrap" %}

```
binding.webView.evaluateJavascript("document.form_auth.submit()") {}
```

{% endcode %}

<mark style="background-color:yellow;">evaluateJavaScript</mark>는 html 폼 양식이 완전히 로드된 후 <mark style="background-color:yellow;">WebViewClient</mark>를 이용하여 호출할 수 있습니다.

{% code overflow="wrap" %}

```
binding.webView.apply {
    webViewClient = object : WebViewClient() {
        override fun onPageFinished(view: WebView?, url: String?) {
            super.onPageFinished(view, url)
            if (url?.startsWith(baseUrl) == true) {
                binding.webView.evaluateJavascript("document.form_auth.submit()") {}
            }

        }
    }
}
```

{% endcode %}

**ⓒ Key 파라미터 획득**

본인인증이 정상적으로 끝났다면, <mark style="background-color:yellow;">returnURI</mark>로 <mark style="background-color:yellow;">key</mark>값을 전달 받습니다.

```
{YOUR_RETURN_URL}?key=XXXXXXXXXX
```

<mark style="background-color:yellow;">WebViewClient</mark>의 <mark style="background-color:yellow;">shouldOverrideUrlLoading</mark>을 통해 <mark style="background-color:yellow;">key</mark>값을 추출할 수 있습니다.

{% code overflow="wrap" %}

```
binding.webView.apply {
    webViewClient = object : WebViewClient() {
        override fun shouldOverrideUrlLoading(
            view: WebView?,
            request: WebResourceRequest?
        ): Boolean {
            if (request?.url.toString().startsWith(returnUrl)) {
                val uri = Uri.parse(request?.url.toString())
                val isContainsKey = uri.queryParameterNames.contains("key") // 파라미터에 key가 포함되어있는지 확인
                if (isContainsKey) println("key = ${uri.getQueryParameter("key")}") // key 추출

                //TODO:  key 추출 이후 과정을 이 곳에서 진행하시면 됩니다.
                return isContainsKey
            }
            return super.shouldOverrideUrlLoading(view, request)
        }
    }
}
```

{% endcode %}

#### 🅓 본인인증 결과 조회 <a href="#f0-9f-85-93-eb-b3-b8-ec-9d-b8-ec-9d-b8-ec-a6-9d-ea-b2-b0-ea-b3-bc-ec-a1-b0-ed-9a-8c" id="f0-9f-85-93-eb-b3-b8-ec-9d-b8-ec-9d-b8-ec-a6-9d-ea-b2-b0-ea-b3-bc-ec-a1-b0-ed-9a-8c"></a>

해당 <mark style="background-color:yellow;">key</mark>로 본인인증 인증 성공 및 실패 여부를 판단한 뒤, 화면에 성공 및 실패 결과를 전달합니다.

> [GET /kcp/id-verification/response](https://docs.shopby.co.kr/?url.primaryName=auth/#/KCPCertification/get-kcp-id-verification-response)
>
> ▶ KCP 본인인증 결과 조회하기\
> NHN KCP 본인인증 결과를 확인합니다.

#### 🅔 샘플코드(참고) <a href="#f0-9f-85-94-ec-83-98-ed-94-8c-ec-bd-94-eb-93-9c-ec-b0-b8-ea-b3-a0" id="f0-9f-85-94-ec-83-98-ed-94-8c-ec-bd-94-eb-93-9c-ec-b0-b8-ea-b3-a0"></a>

[MainActivity.kt](https://nhnent.dooray.com/share/tree/WoOk8q6KT1K3MZcLSuyZsw/pages/3615381963904646601/files/3615929480452935780) 참고
