iframe 内嵌接入

更新时间:
复制 MD 格式

金融级实人认证 H5 网页集成方式提供 Web SDK,支持在浏览器或内嵌 WebView 中完成验证。本文介绍 PC 端及移动端 H5 网页通过 iframe 接入 Web SDK 的操作流程。

浏览器兼容性说明

浏览器名称

Android版本

iOS版本

Edge

Android 4.0及以上版本

iOS 14.3 及以上版本

FireFox

Chrome

Opera

QQ

百度

Android Browser 5.0+

Safari

不支持

iOS 11.3 及以上版本

UC

Android 4.0及以上版本

不支持

UC极速浏览器

夸克浏览器

Android 7.0 及以上版本

不支持

厂商内置浏览器

小米、三星等部分机型支持

受制于浏览器兼容性碎片化问题,建议在流程设计上引导用户使用推荐的浏览器完成认证。

  • 若您在手机应用App内集成该方案,可能会因为内嵌浏览器原因无法兼容,可以参考App内集成H5移动端SDK兼容性配置减少兼容性问题或集成Native SDK。

  • 若您通过微信公众号或者小程序集成,由于微信运营审核规则的限制,可能出现无法避免的兼容性问题,建议使用纯服务端(API)接入方式。

扫脸认证

为了提升用户刷脸认证体验,URL默认自带扫脸认证引导页,用户同意认证后可以开始使用扫脸认证服务。微信公众号场景下,扫脸认证引导页是必须存在的,否则会导致摄像头无法唤起,引发黑屏现象。如需自定义UI,可参考服务端接口参数UiCustomUrl

用户自定义 iframe 接入

若希望自行控制 iframe 容器的样式与布局,可将CertifyUrl 直接嵌入 iframe。ReturnUrl传入固定参数iframe,认证结束后通过 postMessage 发送结果。

适用场景

  • 需要将认证区域嵌入到页面指定位置,而非全屏覆盖。

  • 需要自定义 iframe 尺寸、边框、层级等样式。

接入流程

  1. 在代码中引入如下JS文件,并调用函数getMetaInfo()获取MetaInfo

    <script type="text/javascript" src="https://o.alicdn.com/yd-cloudauth/cloudauth-cdn/jsvm_all.js" ></script>
    重要
    • 调用实人认证服务端发起认证请求时,必须传入实时获取的 MetaInfo 值。

    • 该值随浏览器和设备环境动态变化,测试阶段也需实时传入,禁止使用硬编码的测试数据,否则可能导致无法获取 CertifyUrl

  2. 调用业务服务端初始化接口获取 CertifyUrl。服务端调用接口时,需在参数中传入:

    • CertifyUrlStyle"L" — 获取长链格式的认证地址。

    • ReturnUrl"iframe" — 页面将通过 postMessage 机制向父页面发送认证结果。

      // 微信小程序请求参数
      {
        ...
        "CertifyUrlStyle": "L",
        "ReturnUrl":"iframe",
        ...
      }
    • 小程序默认的 CertifyUrl 不支持 iframe 嵌入使用,需要更改 URL 路径支持。

      // 将 certifyUrl 中的 "/h5?" 替换为 "/h5iframe?"
      function replaceH5ToIframe(url) {
        if (url.includes('/h5?')) {
          return url.replace('/h5?', '/h5iframe?');
        }
        if (url.includes('/h5') && !url.includes('/h5iframe')) {
          return url.replace('/h5', '/h5iframe');
        }
        return url;
      }
  3. 在页面中创建 iframe 元素,并将获取到的 CertifyUrl 直接赋值给 src 属性。

    <!-- CertifyUrl 示例:https://rp-proxy.antdigital.com/onex/proxy/h5?clientcfg=eyJ6... -->
    <iframe
      id="idv-iframe"
      src="{CertifyUrl}"
      allow="camera"
      style="width: 100%; height: 600px; border: 0;"
    ></iframe>
  4. 监听 message 事件,接收认证结果。

认证结果数据结构

认证流程结束后,子页面将通过 postMessage 机制向父页面发送认证结果。返回的数据结构如下:

{
  "code": "XXXX",
  "subCode": "XXXX",
  "extInfo": {
    "certifyId": "XXXXXX"
  }
}

字段

类型

说明

code

String

结果码,具体信息,请参见返回code

subCode

String

子结果码,具体信息,请参见返回subCode

extInfo

Object

扩展信息。

extInfo.certifyId

String

认证 ID,用于后续服务端查询。

完整代码说明

<!DOCTYPE html>
<html lang="zh-CN">

<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover" />
    <title>金融级实人认证</title>
    <!--   引入该JS,全局注入getMetaInfo方法   -->
    <script src="https://o.alicdn.com/yd-cloudauth/cloudauth-cdn/jsvm_all.js"></script>
    <style>
        * {
            box-sizing: border-box;
            margin: 0;
            padding: 0;
        }

        html,
        body {
            width: 100%;
            height: 100%;
        }

        #idv-container {
            width: 100%;
            height: 100vh;
            background: #fff;
        }

        #idv-container iframe {
            width: 100%;
            height: 100%;
            border: 0;
        }

        .error {
            padding: 16px;
            color: #d93026;
            text-align: center;
        }
    </style>
</head>

<body>
    <div id="idv-container"></div>

    <script>
        (() => {
            const containerElement = document.getElementById('idv-container');
            let messageListener = null;

            function showError(message) {
                containerElement.innerHTML = `<div class="error">${message}</div>`;
            }

            // 将 certifyUrl 中的 "/h5?" 替换为 "/h5iframe?"
            function replaceH5ToIframe(url) {
                if (url.includes('/h5?')) {
                    return url.replace('/h5?', '/h5iframe?');
                }
                if (url.includes('/h5') && !url.includes('/h5iframe')) {
                    return url.replace('/h5', '/h5iframe');
                }
                return url;
            }

            async function initAuth() {
                try {
                    if (!window.getMetaInfo) {
                        throw new Error('getMetaInfo 未注入,请检查 SDK 脚本加载');
                    }

                    const metaInfo = window.getMetaInfo();
                    const response = await fetch('https://你的服务端初始化接口', {
                        method: 'POST',
                        headers: { 'Content-Type': 'application/json' },
                        body: JSON.stringify({
                            metaInfo,
                            certifyUrlStyle: 'L',
                            returnUrl: 'iframe'
                        })
                    });

                    if (!response.ok) {
                        throw new Error(`初始化接口异常: ${response.status}`);
                    }

                    const result = await response.json();

                    if (!result?.resultObject?.certifyUrl) {
                        throw new Error('未获取到 certifyUrl');
                    }
                  
                    const certifyUrl = replaceH5ToIframe(result?.resultObject?.certifyUrl);

                    const allowedOrigin = new URL(certifyUrl).origin;

                    const iframeElement = document.createElement('iframe');
                    iframeElement.src = certifyUrl;
                    iframeElement.allow = 'camera;microphone;fullscreen';
                    iframeElement.setAttribute('allowusermedia', 'true');
                    containerElement.innerHTML = '';
                    containerElement.appendChild(iframeElement);

                    messageListener = async (event) => {
                        if (event.origin !== allowedOrigin) return;

                        const payload = typeof event.data === 'string'
                            ? (() => { try { return JSON.parse(event.data); } catch { return null; } })()
                            : event.data;

                        if (!payload || typeof payload !== 'object' || payload.code === undefined) return;

                        console.log('认证回调:', payload);

                        window.removeEventListener('message', messageListener);
                    };

                    window.addEventListener('message', messageListener, { passive: true });
                } catch (error) {
                    console.error('[IDV] init error:', error);
                    showError('认证加载失败,请刷新后重试');
                }
            }

            window.addEventListener('beforeunload', () => {
                if (messageListener) {
                    window.removeEventListener('message', messageListener);
                }
            });

            initAuth();
        })();
    </script>
</body>

</html>
说明
  • 页面必须通过 HTTPS 协议部署,且 iframe 必须设置 allow="camera" 属性,否则将无法调用摄像头。

  • 调用 InitFaceVerify 接口时,ReturnUrl 参数必须设置为 iframe,同时父页面需监听 message 事件以接收认证结果。

  • 认证结束后,系统将检测当前是否处于 iframe 环境,并校验 ReturnUrl 参数是否为 iframe,若条件均满足则向父页面发送认证结果。

  • 为防范盗链及数据篡改风险,建议在解析结果后向服务端发起请求以获取最终验证结果,具体操作请参见DescribeFaceVerify-获取认证详细数据

错误码说明

返回code

错误码

是否计费

错误码文案

1000

刷脸成功。

说明

表示用户完成了刷脸过程,认证建议结果为通过。

该结果仅供参考。最终认证结果请以调用服务端 DescribeFaceVerify 接口的返回值为准。

1001

系统错误。

1003

验证中断。

2002

网络错误。

2003

客户端设备时间错误。

2006

是(仅认证不通过的场景计费)

刷脸提交认证数据失败,或刷脸验证失败。

说明

该结果仅供参考。最终认证结果请以调用服务端 DescribeFaceVerify 接口的返回值为准。

返回subCode

错误码

subCode

详细描述

1000

Z5050

人脸验证成功。

1001

-

表示系统错误。

1003

Z5051

上传刷脸图片失败。

Z5052

数据错误或程序异常。

Z5053

网络错误。

Z5054

摄像头无权限或无法获取摄像头数据。

Z5055

用户退出。

Z5056

重试次数过多。

Z5058

视频格式不满足要求。

Z5059

视频中无有效人脸。

Z5060

降级页面等待状态。用户确认完成人脸识别后,可主动调用服务端接口查询认证结果,以进入后续业务页面。

2002

-

表示网络错误。

2003

-

表示客户端设备时间错误。

2006

Z5128

Z5128(计费):人脸识别验证失败,认证未通过。具体原因请通过服务端查询接口获取。

Z5052

Z5052(不计费):客户端向服务端提交认证数据时发生异常,未进入认证流程。