对接调试一共分为两部分,一部分为安卓应用对接的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 的集成、初始化、使用流程、网络延迟检测等。
应用发布前,请确保现场网络环境已达要求,否则将影响识别成功率。
演示demo
1.1、集成
在模块的build.gradle文件中添加依赖:
dependencies {
implementation "com.sunmi:SunmiEID-SDK:1.3.7"
}
1.2、接口文档
1.2.1、API 列表
SDK方法列表:
API | 说明 |
EidSDK.init(Context context, String appid, EidCall initCallback); | 初始化sdk |
EidSDK.destroy(); | 销毁sdk,释放资源 |
EidSDK.getSunmiEidSDKVersion(); | 获取sdk版本 |
EidSDK.getEidSDKVersion(); | 获取sdk读卡模块版本 |
EidSDK.startCheckCard(Activity act, EidCall call) | 开始识别身份证 |
EidSDK.getIDCardInfo(String reqId, String appKey, EidCall call) | 根据reqId解码身份证(建议走云对云方案) |
Bitmap EidSDK.parseCardPhoto(String photo) | 解密获取身份证后的图片 |
EidSDK.stopCheckCard(Activity act) | 停止读身份证 |
获取eidReader,用来读取卡片信息。 返回EidReader实例。 需在初始化成功后使用 |
EidReader方法列表:不建议使用
API | 说明 |
识别身份证或通用类型 | |
识别电子身份证 |
EidCall:
public interface EidCall {
//初始化 - code为EidConstants.EID_INIT_SUCCESS时初始化成功,其他code见错误码描述
//读卡时 - code、msg见错误码描述
//解码时 - code为EidConstants.DECODE_SUCCESS时解码成功,message为身份证信息(json格式,对应SDK中的ResultInfo类)
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 IApp extends Application {
@Override
public void onCreate() {
super.onCreate();
//Step 1 初始化SDK 传入AppId
EidSDK.init(this, Constant.APP_ID, new EidCall() {
@Override
public void onCallData(int code, String msg) {
switch (code) {
case EidConstants.EID_INIT_SUCCESS:
//初始化成功
break;
default:
//初始化失败,只有参数错误时会失败,回调其他code请检查传入参数。
break;
}
}
});
}
}
注:appid需在商米合作伙伴平台(https://partner.sunmi.com)上申请。
1.2.2、开始读取身份证
private void startCheckCard() { //Step 2 开启读卡 -> 调用startCheckCard方法,通过回调结果处理业务逻辑 //注:默认循环读卡,只会回调一次EidConstants.READ_CARD_READY EidSDK.startCheckCard(this, new EidCall() { @Override public void onCallData(int code, String msg) { Log.e(TAG, "onCallData-" + code + " , " + msg); switch (code) { case EidConstants.ERR_NFC_NOT_SUPPORT: Log.e(TAG, "机器不支持NFC"); // 该机器不支持NFC功能,无法使用SDK break; case EidConstants.ERR_NETWORK_NOT_CONNECTED: Log.e(TAG, "网络未连接,连接网络后重新调用 startCheckCard 方法"); setEditText(mState, String.format(Locale.getDefault(), "网络未连接,请联网后重试")); // *** 异常处理: 连接网络后,需要重新调用 startCheckCard 方法 (手动触发,非自动)*** break; case EidConstants.ERR_NFC_CLOSED: Log.e(TAG, "NFC 未打开,打开后重试 :" + msg); setEditText(mState, String.format(Locale.getDefault(), "NFC未打开,请打开后重试")); // *** 异常处理: 打开NFC后,需要重新调用 startCheckCard 方法 (手动触发,非自动)*** break; case EidConstants.READ_CARD_READY: //Step 3 读卡准备完成 -> 业务方可以引导用户开始进行刷卡操作 Log.e(TAG, "SDK准备完成,请刷卡"); clearData(); setEditText(mState, "请刷卡,刷卡时请勿移动卡片"); break; case EidConstants.READ_CARD_START: //Step 4 读卡中 -> 业务方可以提醒用户"读卡中,请勿移动卡片" Log.e(TAG, "开始读卡,请勿移动"); clearData(); setEditText(mState, "开始读卡,请勿移动"); break; case EidConstants.READ_CARD_SUCCESS: //Step 5 读卡成功 -> 返回的msg为reqId,通过 reqId 业务方走云对云方案获取身份证信息 //注:如不需要循环读卡,可在此处调用stopCheckCard方法 Log.e(TAG, "读卡成功,reqId:" + msg); setEditText(mRequestId, String.format("reqId:%s", msg)); mockServerDecode(msg); break; case EidConstants.READ_CARD_FAILED: //*** 异常处理: 读卡失败,请重新读卡 *** Log.e(TAG, "读卡失败:" + msg); setEditText(mState, String.format(Locale.getDefault(), "读卡错误,请重新贴卡:%s", msg)); break; default: //*** 异常处理: 其他失败 - code为错误码,msg为详细错误原因 需要重新调用 startCheckCard 方法 (手动触发,非自动)*** Log.e(TAG, "读卡失败:code:" + code + ",msg:" + msg); setEditText(mState, String.format(Locale.getDefault(), "其他错误:%d,%s", code, msg)); break; } } }); }
1.2.3、停止读身份证
EidSDK.stopCheckCard(ReadCardActivity.this);
1.2.4、解码身份证信息
//调用SDK的解码,存在泄漏key的风险,建议使用云对云方案
//传入读卡获取的reqId,商米partner平台上的appkey,以及结果callback
EidSDK.getIDCardInfo(reqId, Constant.APP_KEY, new EidCall() {
@Override
public void onCallData(int code, String data) {
//EidConstants.DECODE_SUCCESS -› 解码成功,data为身份证信息的gson格式,可直接解析成SDK中提供的 ResultInfo 实体类
if (code == EidConstants.DECODE_SUCCESS) {
setEditText(mState, String.format(Locale.getDefault(), "身份证解析成功,业务状态:%d:%s", code, data));
ResultInfo result = new Gson().fromJson(data, ResultInfo.class);
parseData(result);
} else {
//解码失败,code 为错误吗,data为错误原因
setEditText(mState, String.format(Locale.getDefault(), "身份证解析失败,请重试(%d:%s)", code, data));
}
}
});
1.2.5、解析身份证头像
通过身份证服务 API 接口获取到返回参数后,其中的 picture 参数为加密数据,需要按照如下步骤解码成图片格式。
将 picture 作为参数传入到解码方法
Bitmap photo = EidSDK.parseCardPhoto(picture);
注:其他端解码方式详见下方zip包
- PC:PC-windows解码.zip
- Linux:Linux-X64照片解码.zip
1.2.6、释放SDK资源
@Override
protected void onDestroy() {
super.onDestroy();
EidSDK.destroy();
}
1.2.7、获取 EidReader 实例
private void initEidReader() {
Log.d("Eid", String.format("商米SDK.Ver:%s, 读卡模块Ver:%s", EidSDK.getSunmiEidSDKVersion(), EidSDK.getEidSDKVersion()));
//读卡次数建议传3
EidReader eid = EidSDK.getEidReaderForNfc(3, this);
}
1.2.8、实现 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.9、调用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.3、参考DEMO
获取 reqid 方式详见:EidCardDemo-OpenApi (develop_v2分支)
获取到 reqid 后需要调用身份证服务 API 接口进行解析,详见下文。
1.4、错误码ErrorCode
错误码 | 错误信息 | 建议操作 |
4001 | 初始化-appId参数错误 | 确认初始化的appId |
4003 | 初始化-网络连接异常 | 检查网络后重试 |
4008 | 初始化-网络未连接 | 检查网络后重试 |
4009 | 初始化-获取设备信息失败 | 更新系统版本 |
4010 | 设备不支持NFC | 机型不支持,更换机型 |
4011 | 未打开NFC | 打开NFC |
4012 | 缺少权限 | 给APP权限 |
4013 | 缺少金融读卡SDK | 安装金融读卡服务 |
4015 | 未先调用init方法 | 先调用init方法 |
4016 | 旅行证件参数错误 | 检查旅行证件参数 |
5001 | 初始化-权限错误 (无身份证服务套餐) | 请在parter平台上 购买套餐 |
-1 | 读卡错误/初始化-服务 器错误 | 读卡时请勿移动, 请将证件对准NFC区域, 重新读卡 |
-3 | 读卡错误,sdk返回为空 | 请尝试重新读卡解决 |
-13001 | 未获取到客户ID | 请填写正确的CID或APPID |
-13002 | 未获取到设备标识 | 请检查动态权限,开启“android.permission. READ_PHONE_STATE”权限 |
-13003 | 获取到的客户ID无效 | 填写正确的CID或APPID |
-13004 | 获取到的设备标识无效 | 获取设备标识无效, 如多次尝试仍无法使用, 请联系客服 |
-13008 | 输入参数为空 | 对照开发文档,检查所填写参数 |
-13009 | 输入参数无效 | 对照开发文档,检查所填写参数 |
-13010 | 解析域名异常 | 填写正确的域名并检查网络是否连接 |
-13011 | 重复次数无效 | 填写重复次数无效,建议 重复次数范围1-5次 |
-13012 | 客户端数据错误 | 读取错误,如多次尝试仍无法 使用,请联系客服 |
-13013 | 获取到的客户ID无效 | 请填写正确的CID或APPID |
-13014 | SN号无效 | 设备未授权,请联系客服 |
-13015 | 无法获取APP包名 | APP未授权,请联系客服 |
-20001 | 网络连接异常 | 请确保网络连接正常,重新读卡 |
-20002 | 网络异常中断 | 请确保网络连接正常,重新读卡 |
-20003 | 连接时延服务超时 | 请确保网络连接正常,重新读卡 |
-20004 | 连接时延服务异常 | 请确保网络连接正常,重新读卡 |
-20005 | 连接后台服务超时 | 请确保网络连接正常,重新读卡 |
-22003 | 数据传输错误 | 请确保网络连接正常,重新读卡 |
-23001 | 调用应用服务端地址 访问异常(前端返电子证照) | 请确认提供的应用服务端地址正确 |
-31001 | 实证服务未接收到数据 | 请确保网络连接正常,重新读卡 |
-31006 | 读取证件失败 | 请尝试重新读卡解决 |
-33001 | 电子证照外部认证失败 | 请尝试重新读卡解决 |
-33003 | 获取eIDReqPacket失败 | 请尝试重新读卡解决 |
-33004 | 获取eidSignPacket失败 | 请尝试重新读卡解决 |
-35001 | 重复请求 | 重复请求,请重新读卡 |
-35002 | 活体鉴权失败 | 对照开发文档,检查所填写参数 |
-35003 | eid电子证照内部认证失败 | eid电子证照认证失败,请重试 |
-35005 | 未获得服务端数据 | 请重试,如多次尝试仍无 法使用,请联系客服 |
-35006 | 服务端数据不正确 | 请重试,如多次尝试仍无 法使用,请联系客服 |
-35007 | 后台指令执行失败 | 请重试,如多次尝试仍无 法使用,请联系客服 |
-35008 | 签名验证失败 | 请重试,如多次尝试仍无 法使用,请联系客服 |
-36001 | 前端返电子证照获取 pass包失败(应用侧) | 调试错误,请应用对照开 发文档进行检查 |
-43003 | 未获取到电子证照个 人信息 | 未获取到电子证照个人信息, 如多次尝试仍无法使用,请 联系客服 |
-45001 | 环境识别码配置错误 | 请检查环境识别码是否正确 |
-45003 | P10加密失败 | |
-53001 | 旅行证件读取异常 | 请核对三要素是否正确 |
-53002 | NFC-B连接异常 | NFC读取异常,请重试 |
-53006 | intent对象为空 | 请参考开发文档,正确使用sdk |
-53007 | Context对象为空 | 请参考开发文档,正确使用sdk |
-53009 | ip或域名为空 | 请参考开发文档,正确使用sdk |
-53010 | port错误 | 请参考开发文档,正确使用sdk |
-53011 | ip或域名格式错误 | 请参考开发文档,正确使用sdk |
-53020 | 参数为空 | 请参考开发文档,正确使用sdk |
-53030 | 蓝牙对象未初始化 | 请参考开发文档,正确使用sdk |
-53032 | IDOCRBTCallBack不能为空 | 请参考开发文档,正确使用sdk |
-53033 | BluetoothDevice不能为空 | 请参考开发文档,正确使用sdk |
-53034 | 错误的蓝牙读卡器 | 请确保读卡器支持sdk功能 |
-53036 | 蓝牙未连接 | 请连接蓝牙读卡器再进行读卡 |
-53037 | 获取SN为空 | 不支持此设备,请使用实证 服务提供的读卡器 |
-53038 | 设备不可连接 | 检查设备是否已经被连接, 重启设备后尝试。 |
-54003 | 旅行证件读取失败 | 请重试,如多次尝试仍无 法使用,请联系客服 |
-91001 | 读取证件数据解析异常 | 请重试,如多次尝试仍无 法使用,请联系客服 |
-91005 | 实证服务器未收到读卡 信息或收到读卡信息已超时 | 请确保网络环境良好,可 尝试切换网络或移动位置 调整网络情况,重新读卡 |
-91006 | 服务繁忙,无可用资源 | 服务繁忙,请稍后重试 |
-91007 | 数据解析异常 | 请重试,如多次尝试仍无 法使用,请联系客服 |
-93001 | 实证服务未获取到应用 返回数据或数据为空 | 读取超时,请确保网络环境 正常,读取时请勿移动卡片 |
-93002 | 错误的卡类型 | 请使用正确的证件 |
-93003 | 读卡初始化异常 (050000失败) | 寻找卡片失败,请检查读卡 设备是否支持 |
-93005 | 卡认证失败 | 请确保网络环境良好,可尝试切 换网络或移动位置调整网络情况,重新读卡 |
-93006 | 卡片异常返回 | 读取数据异常,请重新读卡 |
-93007 | 不支持的读卡器 | 读卡器不具备相应功能 |
-93008 | 不支持的设备 | 此设备不支持当前功能,请咨询客服 |
-99001 | 其他错误 | 请重试,如多次尝试仍无法使用,请联系客服 |
-99002 | 电子证照服务异常 | 请重试,如多次尝试仍无法使用,请联系客服 |
-99008 | 服务未开通 | 服务未开通,请联系客服 |
-99009 | 包年过期 | 设备未授权或已过期,请联系客服 |
2、身份证服务 API 接口
2.1、公共参数说明
2.1.1、概要
请求域名:https://openapi.sunmi.com
openapi的sdk:
golang 版本:https://github.com/sunmi-OS/sunmi-openapi-go-sdk
java 版本:https://github.com/sunmi-OS/sunmi-openapi-java-sdk
- 商米身份证服务的接口采用 HTTPS 协议
- 请求方式统一使用 POST
- 请求数据格式为 application/json
2.1.2、公共header说明
- 请求必传header
请求公共参数
参数名 | 类型 | 说明 |
Sunmi-Timestamp | Integer | 当前时间戳单位秒 |
Sunmi-Sign | string | 签名内容 |
Sunmi-Nonce | Integer | 6位随机数 |
Sunmi-Appid | string | 申请的APPID |
返回公共参数
参数名 | 类型 | 说明 |
code | Integer | 返回码,参见公共错误码 |
data | object | 正常返回,如有错误不返回 |
msg | string | 错误提示信息,如有错误此字段返回错误描述 |
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_id | string | android端生成的 request_id |
encrypt_factor | string | 加密因子(8 位大小写字母和数字组成的的随机字符串,建议每次访问随机生成) |
2.2.2、返回参数
成功返回
参数名 | 类型 | 说明 |
code | string | 返回码,参见常用错误码表 |
data | object | 返回身份证信息,参见下方 data 域内容 |
msg | string | 错误提示信息,如有错误此字段返回错误描述 |
成功返回 data 域内容
参数名 | 类型 | 说明 |
info | string | 身份证信息密文,解密方式参见解密说明 |
示例:
{
"code": 1,
"data": {
"info": "xxxxxxxxxxxxx"
},
"msg": ""
}
失败返回
参数名 | 类型 | 说明 |
code | Integer | 返回码,参见常见业务错误码 |
data | string | 空字符串 |
msg | string | 错误提示信息,如有错误此字段返回错误描述 |
{
"code": 20000,
"data": "",
"msg": "Missing required parameters"
}
2.2.3、解密说明
- 加密方式:DES_CBC_PKCS5Padding 加密
- 加密因子:传入的 encrypt_factor
- 加密key:开发者平台申请的AppKey
解密流程:
- 对 info 字符串 base64 解码(standard 标准解码),解码完为 stringA
- 对 stringA 字符串进行 des 解密,截取AppKey前8位作为秘钥key,向量 iv 作为 encrypt_factor 加密因子,解密完为 stringB
- stringB 即是身份证信息的 json 格式,内容参见下方身份证云解码信息
身份证云解码信息:
参数名 | 类型 | 说明 |
base_info | object | 身份证基础信息,参见下方身份证基础信息说明 |
dn | string | 指纹信息 |
picture | string | 身份证头像照片 |
appeidcode | string | 应用网络身份标记,同一个身份有一个编码 |
身份证基础信息:
参数名 | 类型 | 说明 |
name | string | 姓名 |
nation | string | 民族(如:汉) |
sex | string | 性别(如:男) |
idnum | string | 身份证号码 |
idType | string | 证件类型,见下方证件类型说明 |
birthDate | string | 出生年月日(如:20010305) |
address | string | 身份证住址 |
beginTime | string | 身份证有效期限开始时间(如:20180305) |
endTime | string | 身份证有效期限结束时间(如:20180305) |
signingOrganization | string | 签发机关 |
示例:
"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 | 没有权限(request_id异常) |
14201 | sdk版本过低 |
14202 | 无可使用次数 |
14203 | 读卡错误 |
14204 | 参数异常 |