关闭
公众号二维码

# 系统设计要求

变更日志
版本 日期 变更
v0.1 20180703 创建文档
v0.2 20180703 增加了证书交付,明确安装APP后无需打开APP
v0.3 20180704 不在白名单的APP处理方式,增加厂商型号代码
v0.4 20180704 厂商序列号、随机数增加关于重复性的要求
v0.5 20180704 重复性要求描述改进
v0.6 20180709 增加Linux系统,增加厂商和机型
v0.7 20180717 1. 默认静默打开,额外增加不静默打开配置
2. 额外增加静默安装异常的日志记录
3. 增加序列号原则,获取方式
v0.8 20180726 增加厂商和机具
v0.9 20180726 增加厂商和机具
v1.0 20180816 增加本文档适用于微信支付订制机具的描述
v1.1 20180816 增加厂商和机具
v1.2 20180817 增加厂商和机具
v1.3 20180821 增加内置APP需求
v1.4 20180822 增加系统要求
v1.5 20211022 1.0版本发布
v1.6 20211112 Windows打开安全保护指引

# 系统版本

# Android

android v7.1以上,平板版本。

# Windows

Windows 7 SP1以上,支持32位和64位系统

# 系统要求

# Windows

  1. WxpayFaceService运行需要管理员权限

# 预装APP

设备需要预装最新稳定版刷脸支付SDK

预装要求:

  1. 重置系统之后需要保留

# 设备序列号 (All)

设备序列号,用于唯一标识一台设备。生成设备序列号时, 需要确保全局唯一性。

需要满足以下四个要求: 1. 序列号在应用里面可以获取 2. 序列号重装系统后不会变更 3. 序列号更换主板后可以写入 4. 序列号永远和机器后面铭牌上面贴的编号一致

# 序列号生成规则

注: 设备商必须保证设备SN符合微信制定要求, 否则由于SN规则不符导致的设备使用, 数据统计等问题, 设备商自行负责解决。

构成规则:

# Android:

设备商代码(2位) + 系统类型(1位) + 型号代码(2位) + 厂商序列号(10位) + 随机数(8位)

字段名 长度 内容/格式 字符取值范围
设备商代码 2 由微信支付为厂商分配 0-9, A-Z
系统类型 1 A: 表示Android, W: 表示Windows, L表示Linux 0-9, A-Z
型号代码 2 由厂商提供设备型号给微信支付, 微信支付分配型号代码给厂商 0-9, A-Z
厂商序列号 10 格式内容由厂商按自家现有规则来确定。
序列号需要全局唯一,同厂商的任意两台机器的序列号不可重复。
0-9, A-Z
随机数 8 随机数每一位在0-9, A-Z中取值。需要保证随机性,对随机数的唯一性可以不作强校验。
(注意随机数生成时,先初始化)
0-9, A-Z

# Windows:

字段名 长度 内容/格式 字符取值范围
设备商代码 2 由微信支付为厂商分配 0-9, A-Z
系统类型 1 A: 表示Android, W: 表示Windows, L表示Linux 0-9, A-Z
型号代码 2 由厂商提供设备型号给微信支付, 微信支付分配型号代码给厂商 0-9, A-Z
厂商序列号 12 使用网卡MAC地址 0-9, A-Z
随机数 6 随机数每一位在0-9, A-Z中取值。需要保证随机性,对随机数的唯一性可以不作强校验。
(注意随机数生成时,先初始化)
0-9, A-Z

# 微信读取序列号的方法

# Android

Android 8 开始声明, Build.SERIAL获取SN方式将被废弃,将使用Build.getSerial()。 Android 9 已经正式废弃原方式

# Windows

1.27版本以上推荐方式:
SN写入BIOS设置,参考MSDN上WMICIM文档,对应wmic命令 wmic bios get SerialNumber

提示:如果主板使用DMIEDIT,SN对应项是[Type 001] -- System Information项的Serial Number字段

旧方式:
从c:\serial.txt中读取。 在安装人脸SDK之前,需要先生成这个文件。

# 刷脸支付设备软件预检测项的适配指引

# 1. 背景

本页主要针对微信刷脸支付-软件预检测中的检测项提供适配说明,软件预检测项会在人脸设备出厂检查环节进行验收和拦截。

# 2. 适配指引

# 2.1 系统语言、日期和时间

要求:系统语言默认为中文,【设置】-【时间】开启“自动确定日期和时间”和“自动确定时区”。
效果如下图所示:

# 2.2 预置人脸软件和IoTService App

要求:微信支付相关App(如人脸软件、IoTService App等,请参考附录:微信支付相关App)需要提前预置到/system/priv-app目录。

人脸软件预置方案如下:

1、获取最新版本人脸软件,将其重命名为wxpayface.apk;
2、新建wxpayface目录,把wxpayface.apk放进该目录(注意目录名和apk名称需保持一致);
3、用zip工具打开wxpayface.apk,把里边的lib拉出来放在wxpayface目录下,把lib目录下的armeabi-v7a目录改为arm;
4、将wxpayface目录所有内容预置到/system/priv-app/目录下;
5、最终目录内容如下:
/system/priv-app/wxpayface目录下有一个wxpayface.apk
/system/priv-app/wxpayface/lib/arm目录下有wxpayface自带的so库

IoTService App预置方案如下:

1、获取最新版本IoTService App,将其重命名为iotservice.apk;
2、新建iotservice目录,把iotservice.apk放进该目录(注意目录名和apk名称需保持一致);
3、用zip工具打开iotservice.apk,把里边的lib拉出来放在iotservice目录下,把lib目录下的armeabi-v7a目录改为arm;
4、将iotservice目录所有内容预置到/system/priv-app/目录下;
5、最终目录内容如下:
/system/priv-app/iotservice目录下有一个iotservice.apk
/system/priv-app/iotservice/lib/arm目录下有iotservice自带的so库

实现参考:

以下是支付侧提供的一份在源码目录通过编译脚本预置人脸软件的样例代码(IoTService类似),可供参考:
1、 把人脸软件修改为wxpayface.apk;
2、 在Rom源码的适当位置创建一个目录,该目录包含三个文件,分别是apk文件wxpayface.apk,解压脚本update_so.sh,编译文件Android.mk;
3、 编译Rom源码,查看输出目录是否能产生/system/priv-app/wxpayface目录,apk及相应so是否都正确解压。
update_so.sh内容如下:

#!/bin/bash

rm -rf $1/lib
mkdir -p $1/lib
rm -rf $1/apkUnzip
mkdir -p $1/apkUnzip

unzip -o $1/$2.apk -d $1/apkUnzip

copy_abi_folder() {
  for fileName in $(ls $1/apkUnzip/lib); do
    if [[ -d $1/apkUnzip/lib/$fileName ]]; then
      if [ $fileName == 'arm64-v8a' ]; then
        cp -rf $1/apkUnzip/lib/$fileName $1/lib/arm64
        rm -rf $1/apkUnzip
        return 0
      fi
    fi
  done
  for fileName in $(ls $1/apkUnzip/lib); do
    if [[ -d $1/apkUnzip/lib/$fileName ]]; then
      if [ $fileName == 'armeabi-v7a' ]; then
        cp -rf $1/apkUnzip/lib/$fileName $1/lib/arm
        rm -rf $1/apkUnzip
        return 0
      fi
    fi
  done
  for fileName in $(ls $1/apkUnzip/lib); do
    if [[ -d $1/apkUnzip/lib/$fileName ]]; then
      if [ $fileName == 'arm' ]; then
        cp -rf $1/apkUnzip/lib/$fileName $1/lib
        rm -rf $1/apkUnzip
        return 0
      fi
    fi
  done
  cp -rf $1/apkUnzip/lib $1
  rm -rf $1/apkUnzip
  return 0
}

copy_abi_folder $1

Android.mk内容如下:

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)

###人脸App: wxpayface, IoTService App: iotservice 
LOCAL_MODULE := wxpayface
LOCAL_MODULE_CLASS := APPS
LOCAL_MODULE_TAGS := optional
LOCAL_BUILT_MODULE_STEM := package.apk
LOCAL_MODULE_SUFFIX := $(COMMON_ANDROID_PACKAGE_SUFFIX)
LOCAL_CERTIFICATE := PRESIGNED
LOCAL_PRIVILEGED_MODULE := true
LOCAL_MULTILIB := 32
LOCAL_SRC_FILES := $(LOCAL_MODULE).apk
LOCAL_DEX_PREOPT := false

###解压缩获取so
$(shell `$(LOCAL_PATH)/update_so.sh $(LOCAL_PATH) $(LOCAL_MODULE)`)

###清空临时变量JNI_LIBS
JNI_LIBS :=

###当前目录递归搜索
$(foreach FILE,$(shell find $(LOCAL_PATH)/lib/ -name *.so), $(eval JNI_LIBS += $(FILE)))

###获取搜索文件目录集(相对目录)
LOCAL_PREBUILT_JNI_LIBS := $(subst $(LOCAL_PATH),,$(JNI_LIBS))

include $(BUILD_PREBUILT)

# 2.3 默认授予人脸软件和IoTService App应用权限

要求:微信支付相关App(如人脸软件、IoTService App等,请参考文末附录:微信支付相关App)需要提前授予应用权限。

实现参考:

以下是支付侧提供的通过修改framework代码实现静默授权,可供参考:

Android 9以下版本:

修改framework/base/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java,增加动态权限授予:

private void grantDefaultSystemHandlerPermissions(int userId) {

//这里给我们应用权限和添加白名单
//如果是人脸,包名修改为:com.tencent.wxpayface
//如果是IoTService,包名修改为:com.tencent.wxpayface.iotservice
//其他包名参考附录
PackageParser.Package wxpayfacePackage = getSystemPackageLPr("com.tencent.wxpayface");
if (wxpayfacePackage != null && doesPackageSupportRuntimePerissions(wxpayfacePackage)) {
    if (DEBUG) {
        Log.d(TAG, "grantRuntimePermissionsLPw com.tencent.wxpayface");
    }
    grantRuntimePermissionsLPw(wxpayfacePackage, PHONE_PERMISSIONS, userId);
    grantRuntimePermissionsLPw(wxpayfacePackage, STORAGE_PERMISSIONS, userId);
    grantRuntimePermissionsLPw(wxpayfacePackage, LOCATION_PERMISSIONS, userId);
    grantRuntimePermissionsLPw(wxpayfacePackage, CAMERA_PERMISSIONS, userId);
    grantRuntimePermissionsLPw(wxpayfacePackage, SENSORS_PERMISSIONS, userId);
}
PackageParser.Package iotPackage = getSystemPackageLPr("com.tencent.wxpayface.iotservice");
if (iotPackage != null && doesPackageSupportRuntimePerissions(iotPackage)) {
    if (DEBUG) {
        Log.d(TAG, "grantRuntimePermissionsLPw com.tencent.wxpayface.iotservice");
    }
    grantRuntimePermissionsLPw(iotPackage, PHONE_PERMISSIONS, userId);
    grantRuntimePermissionsLPw(iotPackage, STORAGE_PERMISSIONS, userId);
    grantRuntimePermissionsLPw(iotPackage, LOCATION_PERMISSIONS, userId);
    grantRuntimePermissionsLPw(iotPackage, CAMERA_PERMISSIONS, userId);
    grantRuntimePermissionsLPw(iotPackage, SENSORS_PERMISSIONS, userId);
    }
}

Android 10以上版本:

修改framework/base/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java,增加动态权限授予:

public void grantDefaultPermissions(int userId) {
  
      //这里给我们应用权限和添加白名单
      //如果是人脸,包名修改为:com.tencent.wxpayface
      //如果是IoTService,包名修改为:com.tencent.wxpayface.iotservice
      
      try {
        //com.tencent.wxpayface
        PackageInfo pkg = mContext.getPackageManager().getPackageInfo("com.tencent.wxpayface", DEFAULT_PACKAGE_INFO_QUERY_FLAGS);
        grantRuntimePermissionsForSystemPackage(userId, pkg);
      } catch (Throwable e) {
        Log.e(TAG, "grantRuntimePermissionsForSystemPackage com.tencent.wxpayface err:", e);
      }

      try {
        //com.tencent.wxpayface.iotservice
        PackageInfo pkg = mContext.getPackageManager().getPackageInfo("com.tencent.wxpayface.iotservice", DEFAULT_PACKAGE_INFO_QUERY_FLAGS);
        grantRuntimePermissionsForSystemPackage(userId, pkg);
      } catch (Throwable e) {
        Log.e(TAG, "grantRuntimePermissionsForSystemPackage com.tencent.wxpayface.iotservice err:", e);
      }
      
      // 这里结束
      grantPermissionsToSysComponentsAndPrivApps(userId);
      grantDefaultSystemHandlerPermissions(userId);
      grantDefaultPermissionExceptions(userId);
      synchronized (mLock) {
        mDefaultPermissionsGrantedUsers.put(userId, userId);
      }
  }

# 2.4 实现人脸软件和IoTService App静默升级

要求:微信支付相关App(如人脸软件、IoTService App等,请参考文末附录:微信支付相关App)进行升级时,不显示升级界面,直接进行升级。

实现参考:

以下是人脸软件升级调用逻辑(其他App升级调用逻辑类似),可创建一个Demo App,通过以下代码调用人脸软件升级,查看是否可以静默升级:

public class APKUtils {
    private final static String TAG = "APKUtils";
    public static final int FLAG_PRIVILEGED = 1<<30;

    public static int getVersionCode(Context context, String str) {
        try {
            return context.getPackageManager().getPackageInfo(str, 0).versionCode;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return 0;
    }

    public static String getVersionName(Context context, String str) {
        try {
            return context.getPackageManager().getPackageInfo(str, 0).versionName;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "";
    }

    public static void installApk(Context context, String apkFileString) {
        File apkFile = new File(apkFileString);
        if (!apkFile.exists()) {
            Log.d(TAG, "apk file is not exist: " + apkFileString);
            Toast.makeText(context, "文件不存在", Toast.LENGTH_SHORT).show();
            return;
        }

        Log.d(TAG, "use normal install");
        Intent intent = new Intent(Intent.ACTION_VIEW);
        //AndroidN以上版本需通过FileProvider进行升级
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            Log.d(TAG, "use provider url");
		   intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
            Uri contentUri = FileProvider.getUriForFile(context, context.getPackageName() + ".fileProvider", apkFile);
            intent.setDataAndType(contentUri, "application/vnd.android.package-archive");
            List<ResolveInfo> resInfoList = context.getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
            for (ResolveInfo resolveInfo : resInfoList) {
                String packageName = resolveInfo.activityInfo.packageName;
                context.grantUriPermission(packageName, contentUri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
            }
        } else {
            Log.d(TAG, "use path url");
            intent.setDataAndType(Uri.fromFile(apkFile), "application/vnd.android.package-archive");
            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        }
        context.startActivity(intent);
        Log.d(TAG, "use normal install end");
    }
}

静默升级异常处理:
对于安装失败的情况,需要把安装异常记录在指定位置的文件里。
异常日志目录

Environment.getExternalStorageDirectory().getAbsolutePath() + "/wxpayface/install_log/"

异常日志文件名

包名.log

例如

com.tencent.wxpayface.log

异常日志记录格式

  1. 只需要一行,每次覆盖;
  2. 数据格式:
时间戳,包名,当前版本,新安装版本,错误码

例如

1531821994,com.tencent.wxpayface,1.0.9,1.1.0,-18

# 2.5 关闭USB授权弹框

要求:人脸软件会开启3D摄像头,在开启时不会弹出USB授权框。
修改/frameworks/base/core/res/res/values/config.xml文件,将config_disableUsbPermissionDialogs修改为true,如下所示:

<!-- If true, then we do not ask user for permission for apps to connect to USB devices.
         Do not set this to true for production devices. Doing so will cause you to fail CTS. -->
    <bool name="config_disableUsbPermissionDialogs">true</bool>

# 2.6 开启系统定位

要求:【设置】-【位置信息】默认开启,模式为高精度,同时开启WLAN扫描、蓝牙扫描。
备注:需同时默认授予人脸软件、IoTService App 定位权限,详情见"默认授予人脸软件和IoTService App应用权限"。
效果如下图所示:

# 3. 相关附录

# 3.1 微信支付相关App

App 包名 功能说明 使用场景 是否需要预置 是否需要静默授权 是否需要静默升级
人脸App/SDK com.tencent.wxpayface 刷脸支付 青蛙设备、SDK设备
IoTService com.tencent.wxpayface.iotservice IoT设备管理 青蛙设备、SDK设备
备注:不同场景需要预置的App不完全相同,具体请咨询微信支付技术支持

# 3.2 常见出厂检查拦截提示与适配指引

检查失败提示语 适配指引
人脸APP/人脸SDK没有预置进系统 见适配指引-预置人脸软件和IoTService App
IoTService没有预置进系统 见适配指引-预置人脸软件和IoTService App
备注:实际提示语可能有所差异,如遇提示语不一致请咨询微信支付技术支持

# 系统开启UAC

# 1. 方式一

# 1.1 设备系统需在系统设置里开启UAC

# 1.2 设置路径:控制面板->用户账户->用户账户->更改用户账户控制设置

# 1.3 将UAC等级调为默认或者始终通知

详细设置步骤如下图所示:

# 2. 方式二

# 2.1 使用注册表导入

首先,请点击进入注册表;
其次,双击文件->确认导入,重启系统即可。

上次更新: 11/16/2021, 2:20:21 PM