身份证云识别服务

对接调试一共分为两部分,一部分为安卓应用对接的SDK,另一部分为应用服务器与商米服务器直接的云对接API。

应用软件集成身份证识别sdk,应用软件后端服务器集成云端api(集成云端api用于保护用户身份信息,保证整个数据链路中,只有应用软件可获取身份证明文信息)

1、接入方 APP 通过 SDK 获取 reqid(有效期5分钟)
2、接入方云通过 reqid 从商米云获取证件信息

注:身份证云识别依赖网络,要求每次网络交互过程不得超过200ms,数据交互延迟高于110ms会降低解码成功率(sdk返回9100X错误),高于200ms会造成读卡失败。使用前请确定网络环境满足要求,sdk提供了延迟检测接口,用户可使用延迟检测接口测试网络。不建议使用物联网卡。

1、APP设计和开发

APP设计

APP设计注意要点:android NFC或者读卡器识别到卡片后会有声音提示,身份证读卡操作是个耗时操作,当声音提示后,不能直接拿走卡片,需要等到读卡成功或者失败再拿开卡片。界面上需要提示用户在有明确反馈前不要拿走卡片,建议:loading+文字提示

APP开发

身份证云识别的Android SDK 的集成、初始化、使用流程、网络延迟检测等。

应用发布前,请确保现场网络环境已达要求,否则将影响识别成功率。

1.1、集成

在模块的build.gradle文件中添加依赖:

dependencies {
    implementation "com.sunmi:SunmiEID-SDK:1.2.2"
}

1.2、接口文档

1.2.1、API 列表

EidSDK方法列表:

API 说明
EidSDK.init(Context context, String appid, EidCall initCallback);初始化sdk,调用时需保证设备已联网
EidSDK.destroy();销毁sdk,释放资源
EidSDK.getSunmiEidSDKVersion();获取sdk版本
EidSDK.getEidSDKVersion();获取sdk读卡模块版本
EidSDK.getEidReaderForNfc(int readTimes, EidCall call);获取eidReader,用来读取卡片信息。
返回EidReader实例。
需在初始化成功后使用

EidReader方法列表:

API说明
void nfcReadCard(Intent var1);识别身份证或通用类型
void readCard(int var1, final EidReadCardCallBack var2);识别电子身份证

EidCall:

public interface EidCall {
    //code为EidConstants.EID_INIT_SUCCESS时初始化成功,此时调用getEidReaderForNfc()方法,其他code见错误码描述
    void onCallData(int code, String message);
}

EidReadCardCallBack:

public interface EidReadCardCallBack {
    byte[] transceiveTypeB(byte[] var1);

    byte[] transceiveTypeA(byte[] var1);
}

1.2.1、初始化

  • 推荐在Activity中调用SDK的初始化(调用时需保证设备已联网):
public class MainActivity extends AppCompatActivity implements EidCall {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        try {
            EidSDK.init(this, "appid", this);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        EidSDK.destroy();
    }

    @Override
    public void onCallData(int code, String msg) {
        Log.d("app", "onCallData: code:" + code + ", msg:" + msg);
    }
}

注:appid需在商米合作伙伴平台(https://partner.sunmi.com)上申请。

1.2.2、获取 EidReader 实例

private void initEidReader() {
    Log.d("Eid", String.format("商米SDK.Ver:%s, 读卡模块Ver:%s", EidSDK.getSunmiEidSDKVersion(), EidSDK.getEidSDKVersion()));
    EidReader eid = EidSDK.getEidReaderForNfc(1, this);
}

1.2.3、实现 EidCall 接口

@Override
public void onCallData(int code, String msg) {
    switch (code) {
        case EidConstants.READ_CARD_START:
            mState.setText("开始读卡,请勿移动");
            Log.i(TAG, "开始读卡,请勿移动");
            break;
        case EidConstants.READ_CARD_SUCCESS:
            closeNFCReader();//电子身份证需要关闭
            Log.e("TAG", String.format(Locale.getDefault(), "正在获取身份信息(%s),请稍等.....", msg));
            mState.setText(String.format(Locale.getDefault(), "正在获取身份信息(%s),请稍等.....", msg));
            File file = new File(fileNameBase, "zp.bmp");
            if (file.exists()) {
                file.deleteOnExit();
            }
            getIDCardInfo(msg); //通过card_id请求识读卡片的信息
            break;
        case EidConstants.READ_CARD_FAILED:
            closeNFCReader();//电子身份证需要关闭
            Log.i(TAG, String.format(Locale.getDefault(), "读卡错误,请重新贴卡:%s", msg));
            mState.setText(String.format(Locale.getDefault(), "读卡错误,请重新贴卡:%s", msg));
            break;
        case EidConstants.READ_CARD_DELAY:
            Log.e("TAG", String.format(Locale.getDefault(), "延迟 %sms", msg));
            mState.setText(String.format(Locale.getDefault(), "延迟 %sms", msg));
            break;
        default:
            break;
    }
}

1.2.4、调用NFC识读卡片

  • 识读身份证:
@Override
protected void onNewIntent(Intent intent) {
    super.onNewIntent(intent);
    eid.nfcReadCard(intent);
 }
  • 识读电子身份证:
@Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        try {
           Tag tagFromIntent = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
           try {
              isodep = IsoDep.get(tagFromIntent);
              isodep.connect();
              if (isodep.isConnected()) {
                  eid.readCard(IDCardType.ECCARD, new EidReadCardCallBack() {
                      @Override
                      public byte[] transceiveTypeB(byte[] data) {
                          return data;
                      }
                      @Override
                      public byte[] transceiveTypeA(byte[] data) {
                          byte[] outData = new byte[data.length];
                          try {
                              outData = isodep.transceive(data);
                          } catch (Exception e) {
                              e.printStackTrace();
                          }
                          return outData;
                      }
                  });
             } else {
                  closeNFCReader()
             }
        } catch (Exception e) {
             e.printStackTrace();
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
 }

    private void closeNFCReader() {
        if (isodep != null) {
            try {
                isodep.close();
                isodep = null;
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

1.2.5、解析身份证头像

通过身份证服务 API 接口获取到返回参数后,其中的 picture 参数为加密数据,需要按照如下步骤解码成图片格式。

1. 集成图片解码库,在模块的build.gradle文件中添加依赖:

dependencies {
    implementation 'com.github.cloud-reader-jinlian:cloudreader-picture:v1.0.0'
}

2. 将 picture 作为参数传入到解码方法

Bitmap photo = IDCardPhoto.getIDCardPhoto(picture);

注:其他端解码方式详见下方zip包

1.3、参考DEMO

注:金融机的NFC功能非安卓标准NFC,需要调用金融SDK进行非接读卡。具体可参考DEMO中的金融读卡内容。

获取 reqid 方式详见:EidCardDemo-OpenApi (develop_v2分支)

获取到 reqid 后需要调用身份证服务 API 接口进行解析,详见下文。

1.4、错误码ErrorCode

错误码错误描述
4001appId参数错误/未初始化
4002初始化-解析域名异常
4003初始化-网络连接异常
4008初始化-网络未连接
5001 初始化-设备权限异常
-1读卡错误/初始化-服务器错误
-13007终端存储权限不足
-13009输入参数无效
-13010解析域名异常
-13011重复次数无效
-13012客户端数据错误
-20001网络连接异常
-20002网络异常断开
-20003连接时延服务超时
-20004连接时延服务异常
-22003数据传输错误
-33001卡端外部认证失败
-53002NFC连接异常
-53006intent对象为空
-53007Context对象为空
-53008EidCall对象为空
-53012NfcAdapter对象为空
-53013Activity对象为空
-53020外部输入参数为空.
-54001JNI环境异常
-54002JNI加载异常
-54003资源释放异常
-54004初始化网关失败
-54005JNI对象异常
-54006JNI参数异常
-91004SAM其他异常
-91005SAM认证失败
-91006无空闲SAM
-93002错误的卡类型
-93004回调函数数据错误
-93005卡认证失败
-93006卡片异常返回

2、身份证服务 API 接口

2.1、公共参数说明

2.1.1、概要

请求域名:https://openapi.sunmi.com

openapi的sdk:

golang 版本:https://github.com/sunmi-OS/sunmi-openapi-go-sdk

  1. 商米身份证服务的接口采用 HTTPS 协议
  2. 请求方式统一使用 POST
  3. 请求数据格式为 application/json

2.1.2、公共header说明

  1. 请求必传header

请求公共参数

参数名 类型 说明
Sunmi-Timestamp Integer 当前时间戳单位秒
Sunmi-Sign string 签名内容
Sunmi-Nonce Integer 6位随机数
Sunmi-Appid string 申请的APPID

返回公共参数

参数名类型说明
codeInteger返回码,参见公共错误码
dataobject正常返回,如有错误不返回
msgstring错误提示信息,如有错误此字段返回错误描述

2.1.3、签名生成说明

签名算法:hmac256

签名生成方式:

1、拼接stringA(即 stringA=json-body + Sunmi-Appid + Sunmi-Timestamp + Sunmi-Nonce)

2、获取申请的AppKey作为hmac256算法的密钥key

3、最后进行 hmac256(stringA, Appkey)最终获取到sign值

2.1.4、公共错误码

返回码说明
1成功返回
20000网关校验缺少参数
20001请求超过有效期
30000开发者身份验证失败
30001开发者权限不足
40000签名验证失败
40001签名失败
50000服务器异常
50001网关异常

2.2、身份证云识别接口

  • 接口地址:/v2/eid/eid/idcard/decode
  • 请求方式:POST

2.2.1、请求参数

参数名类型说明
request_idstringandroid端生成的 request_id
encrypt_factorstring加密因子(8 位大小写字母和数字组成的的随机字符串,建议每次访问随机生成)

2.2.2、返回参数

成功返回

参数名类型说明
codestring返回码,参见常用错误码表
dataobject返回身份证信息,参见下方 data 域内容
msgstring错误提示信息,如有错误此字段返回错误描述

成功返回 data 域内容

参数名类型说明
infostring身份证信息密文,解密方式参见解密说明

示例:

{
    "code": 1,
    "data": {
        "info": "xxxxxxxxxxxxx"
    },
    "msg": ""
}

失败返回

参数名类型说明
codeInteger返回码,参见常见业务错误码
datastring空字符串
msgstring错误提示信息,如有错误此字段返回错误描述
{
    "code": 20000,
    "data": "",
    "msg": "Missing required parameters"
}

2.2.3、解密说明

  • 加密方式:DES_CBC_PKCS5Padding 加密
  • 加密因子:传入的 encrypt_factor
  • 加密key:开发者平台申请的AppKey

解密流程:

  1. 对 info 字符串 base64 解码(standard 标准解码),解码完为 stringA
  2. 对 stringA 字符串进行 des 解密,截取AppKey前8位作为秘钥key,向量 iv 作为 encrypt_factor 加密因子,解密完为 stringB
  3. stringB 即是身份证信息的 json 格式,内容参见下方身份证云解码信息

身份证云解码信息:

参数名类型说明
base_infoobject身份证基础信息,参见下方身份证基础信息说明
dnstring指纹信息
picturestring身份证头像照片
appeidcodestring应用网络身份标记,同一个身份有一个编码

身份证基础信息:

参数名类型说明
namestring姓名
nationstring民族(如:汉)
sexstring性别(如:男)
idnumstring身份证号码
idTypestring证件类型,见下方证件类型说明
birthDatestring出生年月日(如:20010305)
addressstring身份证住址
beginTimestring身份证有效期限开始时间(如:20180305)
endTimestring身份证有效期限结束时间(如:20180305)
signingOrganizationstring签发机关

示例:

 "base_info": {
      "address": "xx省xx市xxxx路xx号",
      "beginTime": "20180305",
      "endTime": "20180305",
      "birthDate": "20010305",
      "idType": "01",
      "idnum": "xxxxxxx",
      "name": "孙小红",
      "nation": "汉",
      "sex": "男",
      "signingOrganization": "xx市xx公安局"
    },
    "dn": "xxxxxxxxxx",
    "picture": "xxxxxxxxx",
    "appeidcode": "xxxxxxxxx",

 2.2.4、常见业务错误码

返回码说明
50000服务器异常
14200没有权限
14201sdk版本过低
14202无可使用次数
14203读卡错误
14204参数异常