> For the complete documentation index, see [llms.txt](https://docs.innopaas.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.innopaas.com/whatsapp-chan-pin/editor/ling-jie-chu-yan-zheng-ma.md).

# 零接触验证码

## 背景

通过使用包含一键自动填写功能的身份验证模板，您可以将一次性密码或验证码连同一键自动填写按钮一起发送给用户。当 WhatsApp 用户轻触该自动填写按钮时，WhatsApp 客户端将触发一个活动，这会打开您的应用APP并向该应用发送密码或验证码，当集成了WhatsApp一键认证的应用会自动捕获这个验证码，用户无需切换到WhatsApp进行复制验证码。

**WhatsApp Zero-Tap 模板的工作流程可以分几个简单的步骤：**

* ​**发起“握手”**：“握手”是一种您要实现的 Android intent 和公共类，而 WhatsApp 客户端可发起“握手”。使用本功能之前，需要先执行“握手”动作，在收到该验证模板消息后，WhatsApp 客户端将执行资格检查。如果没有错误，该客户端将启动 intent 并向用户显示该消息。当用户点击该消息的一键自动填写按钮时，WhatsApp 会自动加载您的应用并向该应用传递一次性密码或验证码。
* ​**发送验证码**​： 调用 InnoPaaS 的 WhatsApp 模板消息推送API，发送 Zero-Tap 模板消息。
* ​**验证码传输到WhatsApp客户端**​：InnoPaaS 收到请求后，会将验证码通过 WhatsApp 通道发送给用户的 WhatsApp 账户。这一步骤利用了 WhatsApp 的广泛用户基础和高效的消息传输系统。
* ​**自动填充验证码**​：当验证码通过 WhatsApp 发送到用户手机时，集成了WhatsApp一键认证的应用会自动捕获这个验证码。这是通过在该应用中集成特定的接收器实现的，它能够识别并提取 WhatsApp 消息中的验证码。应用自动将捕获的验证码填充到相应的验证字段中。用户无需手动输入或切换到WhatsApp来复制验证码。
* ​**自动完成验证**​： 应用收到自动回填的验证码后将完成验证流程，无需额外的点击或输入，整个过程丝滑流畅。

## 限制

**仅有 Android 系统**支持一键自动填写按钮。如果您向使用非 Android 设备的 WhatsApp 用户发送身份验证模板，WhatsApp 客户端将显示一个复制验证码按钮。

## 创建模板

创建authentication类型的模版，选择零接触填写Zero-Tap验证码模版。

![](https://rkzav3pcv4.feishu.cn/space/api/box/stream/download/asynccode/?code=OWFkMjkyOGY4OTY1MjIyYWE3MjE5ZmY5Njg3NGRlZjRfZzNZaUI3NEZzMnRxUXJnSkdvUnBQWXdGdjZPcGpWUjBfVG9rZW46RnJCdWJFN0xibzJPODB4QUVob2N0UjZBbmw1XzE3ODgzNDM0NTg6MTc4ODM0NzA1OF9WNA\&add_watermark=true\&scene_type=CCM)

主要需填写PackageName 和 App signature hash

并提交meta官方审核。审核通过以后会获取TEMPLATE ID ， 状态必须为审核通过才可使用。

### **应用签名密钥哈希值（App signature hash）**

如要创建使用一键自动填写按钮的身份验证模板，您必须在components数组中加入应用签名密钥哈希值。

如要计算您的哈希值，请遵循Google有关[计算应用的哈希字符串](https://developers.google.com/identity/sms-retriever/verify#computing_your_apps_hash_string)的说明。

或者，如果您按照Google说明下载了应用签名密钥证书，您可以结合使用该证书和[sms\_retriever\_hash\_v9.sh](https://github.com/googlearchive/android-credentials/blob/master/sms-verification/bin/sms_retriever_hash_v9.sh) shell脚本来计算哈希值。

如何使用sms\_retriever\_hash\_v9.sh

1. Mac 电脑

* 把sms\_retriever\_hash\_v9.sh和签名.jks文件放在同一个目录下，在Terminal中cd 到签名.jks所在的目录中执行如下命令

```yaml
sh sms_retriever_hash_v9.sh --package 包名 --keystore 签名.jks
```

* 会提示输入签名文件的密码，输入你的签名文件密码后即可查看密钥哈希值了

![](https://rkzav3pcv4.feishu.cn/space/api/box/stream/download/asynccode/?code=NmNlNzUyNDJkZDFlYjJhODMxZjhlMDIxYjU5M2ZjMThfZDQwT3d2a2FDU3NrWHdna00yODF3SWkxaUwxdEI0TmtfVG9rZW46SGc1T2I1eldQb0pxOER4aWExYWNIZG9ibnBkXzE3ODgzNDM0NTg6MTc4ODM0NzA1OF9WNA\&add_watermark=true\&scene_type=CCM)

* 注意：密码一定要确认是否正确哦，因为密码不正确也能返回哈希值。

{% file src="/files/wpVI23qVBzHX1eoziSZ4" %}

本地下载sms\_retriever\_hash\_v9.sh

[为 Android SMS Retriever API 正确生成哈希密钥 ](https://app.gitbook.com/o/IqMhg9XvjP4HlaF1m4Wz/s/tZIe9DCfJsGdG2Apng7d/~/edit/~/changes/159/whatsapp-chan-pin/editor/ling-jie-chu-yan-zheng-ma/wei-android-sms-retriever-api-zheng-que-sheng-cheng-ha-xi-mi-yao)

## 握手（应用与WhatsApp）

您的App应用必须向 WhatsApp 客户端发出信号，告知其即将收到密码或验证码。可通过发起“握手”来实现此目的。“握手”是一种您要实现的 Android 意图和公共类，而 WhatsApp 客户端可发起“握手”。

如果您应用中的用户请求获取一次性密码或验证码，并选择将其发送到他们的 WhatsApp 电话号码，您首先应执行“握手”，然后调用我们的 API 来发送身份验证模板消息。在收到该消息后，WhatsApp 客户端将执行资格检查。如果没有错误，该客户端将启动意图并向用户显示该消息。最后，当用户点击该消息的一键自动填写按钮时，我们会自动加载您的应用并向该应用传递一次性密码或验证码。

![](https://rkzav3pcv4.feishu.cn/space/api/box/stream/download/asynccode/?code=YjAxOTUzMzNkNWU2ZjZkMGIzOWYyZGU0ZWE1Nzk1NzhfWEJXclVUY0FNS2dLOGxTTmh4VXd3UEJVZUk4cTBFN0lfVG9rZW46TmNxQ2JSd0k2b0lFd214RUFqUGMzV3ZJbjliXzE3ODgzNDM0NTg6MTc4ODM0NzA1OF9WNA\&add_watermark=true\&scene_type=CCM)

注意⚠️：*如果您在发送消息之前不执行“握手”，或消息未通过资格检查，则发送的消息将显示复制代码按钮，而不是一键自动填写按钮。*

### 资格检查

在收到身份验证模板消息之后，WhatsApp 客户端会执行以下检查。如果任意检查失败，一键自动填写按钮将被替换为复制代码按钮。

* 发起“握手”的时间是否在不到 10 分钟之前（如果在模板中加入了 `code_expiration_minutes` 属性，则检查是否不超出该属性中指定的分钟数）。
* 消息中的应用包名（创建模板时在 `components` 数组的 `package_name` 属性中定义）与意图上设置的应用包名是否匹配。WhatsApp 应用或 WhatsApp Business 应用将使用您应用程序提供的 `PendingIntent` 对象中调用的 `getCreatorPackage` 方法判断项目包名称是否匹配。
* 消息中的应用签名密钥哈希（创建模板时在 components 数组的 `signature_hash` 属性中定义）与您所安装应用的签名密钥哈希是否匹配。
* 消息是否包含一键自动填写按钮的文本。
* 您的应用是否已定义用于接收一次性密码或验证码的活动。

### Android 通知

仅在以下情况下，表示已收到 WhatsApp 身份验证模板消息的 Android 通知才会显示在用户的 Android 设备上：

* 用户使用所发送消息的接收电话号码（帐户）登录 WhatsApp 应用或 WhatsApp Business 应用。
* 用户登录您的应用。
* Android 操作系统是 KitKat（Android 4.4，API 级别 24）或更高版本。
* WhatsApp 应用或 WhatsApp Business 应用已启用显示通知（设置 通知）。
* 已为 WhatsApp 应用或 WhatsApp Business 应用启用设备层级通知。
* 在 WhatsApp 应用或 WhatsApp Business 应用中，用户与您的企业之间过去的消息对话未设为静音。

### 使用 SDK（Beta 测试版）

One-Tap 的 Android SDK 目前已推出 Beta 测试版。该版本可用来执行“握手”以及一键式和零轻触式身份验证模板中的其他功能。

如要获取 SDK 功能，请将以下配置添加到您的 Gradle 文件中：

```java
dependencies {
  …
  implementation 'com.whatsapp.otp:whatsapp-otp-android-sdk:0.1.0'
  …
}
```

将 `mavenCentral()` 添加到您的存储库中：

```java
repositories {
  …
  mavenCentral()
  …
}
```

### 活动

声明可接收一次性密码或验证码的活动和意图筛选条件。意图筛选条件必须具有操作名称 `com.whatsapp.otp.OTP_RETRIEVED`。

```xml
<activity
   android:name=".ReceiveCodeActivity"
   android:enabled="true"
   android:exported="true"
   android:launchMode="standard">
   <intent-filter>
       <action android:name="com.whatsapp.otp.OTP_RETRIEVED" />
   </intent-filter>
</activity>
```

在收到身份验证模板消息（且该消息已通过所有[资格检查](https://developers.facebook.com/docs/whatsapp/business-management-api/authentication-templates/autofill-button-authentication-templates#eligibility-checks)）后，WhatsApp 应用或 WhatsApp Business 应用将立即开始执行此活动。

### 注册广播

首先，需要将以下内容添加到您的 `AndroidManifest.xml` 文件中：

```xml
<receiver
    android:name=".receiver.OtpCodeReceiver"
    android:enabled="true"
    android:exported="true">
    <intent-filter>
        <action android:name="com.whatsapp.otp.OTP_RETRIEVED" />
    </intent-filter>
</receiver>
```

实现BroadcastReceiver，当收到短信时会触发此广播：

```java
public class OtpCodeReceiver extends BroadcastReceiver {

    private static final String TAG​ ​= "LOGTAG";
  @Override
  public void onReceive(Context context, Intent intent) {
      WhatsAppOtpIncomingIntentHandler incomingIntentHandler = new WhatsAppOtpIncomingIntentHandler();
      incomingIntentHandler.processOtpCode(intent,
          code -> {
              Log.e(TAG, "接收到的验证码" + code);
              final Context applicationContext = context;
              // broadcast code
​              ​Intent codeBroadcasterIntent = new Intent();
              codeBroadcasterIntent.setAction("otp.code.receiver");
              codeBroadcasterIntent.putExtra("code", code);
              codeBroadcasterIntent.setPackage(context.getPackageName());
              applicationContext.sendBroadcast(codeBroadcasterIntent);
          },
          (whatsAppOtpError, e) -> Log.e(TAG, "whatsAppOtpError" + whatsAppOtpError.name() + "exception=" + e));
  }
}
```

### 活动类

**使用 SDK（首选）**

定义活动公共类并对 `WhatsAppOtpIncomingIntentHandler` 对象进行实例化，以处理意图、验证一次性密码并处理错误。

```java
public class ReceiveCodeActivity extends AppCompatActivity {

  @Override
  protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      WhatsAppOtpIncomingIntentHandler incomingIntentHandler = new WhatsAppOtpIncomingIntentHandler();
      incomingIntentHandler.processOtpCode(
                             intent,
                             // call your function to validate
                             (code) -> validateCode(code),
                             // call your function to handle errors
                             (error, exception) -> handleError(error, exception));
}
```

**不使用 SDK**

定义在一次性密码或验证码传递到您的应用后，可接受该一次性密码或验证码的活动公共类。

```java
public class ReceiveCodeActivity extends AppCompatActivity {

   @Override
   protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       Intent intent = getIntent();
       // retrieve PendingIntent from extras bundle
       PendingIntent pendingIntent = intent.getParcelableExtra("_ci_");
       // verify source of the pendingIntent
       String pendingIntentCreatorPackage = pendingIntent.getCreatorPackage();
       // check if creatorPackage is "com.whatsapp" -> WA consumer app Or
       // "com.whatsapp.w4b" -> WA business app
       if ("com.whatsapp".equals(creatorPackage) || "com.whatsapp.w4b".equals(creatorPackage)) {
         // use OTP code
         String otpCode = intent.getStringExtra("code");
       }
   }
}
```

### 发起“握手”

**使用 SDK（首选）**

执行“握手”的方法是：对 `WhatsAppOtpHandler` 对象进行实例化，并将您的上下文传递到 `.sendOtpIntentToWhatsApp()` 方法中：

```java
WhatsAppOtpHandler whatsAppOtpHandler = new WhatsAppOtpHandler();
whatsAppOtpHandler.sendOtpIntentToWhatsApp(context);
```

**不使用 SDK**

以下示例展示了发起与 WhatsApp 客户端“握手”的一种方法。

```java
public void sendOtpIntentToWhatsApp() {
   // Send OTP_REQUESTED intent to both WA and WA Business App
   sendOtpIntentToWhatsApp("com.whatsapp");
   sendOtpIntentToWhatsApp("com.whatsapp.w4b");
}

private void sendOtpIntentToWhatsApp(String packageName) {

  /**
  * Starting with Build.VERSION_CODES.S, it will be required to explicitly
  * specify the mutability of  PendingIntents on creation with either
  * (@link #FLAG_IMMUTABLE} or FLAG_MUTABLE
  */
  int flags = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S ? FLAG_IMMUTABLE : 0;
  PendingIntent pi = PendingIntent.getActivity(
      getApplicationContext(),
      0,
      new Intent(),
      flags);


  // Send OTP_REQUESTED intent to WhatsApp
  Intent intentToWhatsApp = new Intent();
  intentToWhatsApp.setPackage(packageName);
  intentToWhatsApp.setAction("com.whatsapp.otp.OTP_REQUESTED");
  // WA will use this to verify the identity of the caller app.
  Bundle extras = intentToWhatsApp.getExtras();
  if (extras == null) {
     extras = new Bundle();
  }
  extras.putParcelable("_ci_", pi);
  intentToWhatsApp.putExtras(extras);
  getApplicationContext().sendBroadcast(intentToWhatsApp);
}
```

### 检查是否安装了 WhatsApp

如果您希望 WhatsApp 和您的应用在同一台设备上，在提供 WhatsApp 这一选项之前，您可以先检查是否安装了 WhatsApp。

首先，需要将以下内容添加到您的 `AndroidManifest.xml` 文件中：

```java
<queries>
    <package android:name="com.whatsapp"/>
    <package android:name="com.whatsapp.w4b"/>
</queries>
```

**使用 SDK（首选）**

对 `WhatsAppOtpHandler` 对象进行实例化：

```java
WhatsAppOtpHandler whatsAppOtpHandler = new WhatsAppOtpHandler();
```

通过将 `isWhatsAppInstalled` 方法作为一个 `If` 语句中的分句进行传递，检查是否已安装 WhatsApp 客户端：

```java
If (whatsAppOtpHandler.isWhatsAppInstalled(context)) {
    // ... do something
}
```

**不使用 SDK**

```java
if (this.isWhatsAppInstalled(context)) {
    // ... do something
} 

public boolean isWhatsAppInstalled(final @NonNull Context context){
    return isWhatsAppInstalled(context, "com.whatsapp") || 
           isWhatsAppInstalled(context, "com.whatsapp.w4b");
  }

  public boolean isWhatsAppInstalled(final @NonNull Context context,
      final @NonNull String type){
    final Intent intent = new Intent();
    intent.setPackage(type);
    intent.setAction("com.whatsapp.otp.OTP_REQUESTED");
    PackageManager packageManager = context.getPackageManager();
    List<ResolveInfo> receivers = packageManager.queryBroadcastReceivers(intent, 0);
    return !receivers.isEmpty();
  }
}
```

## 发送One-Tap身份验证模板消息

{% embed url="<https://docs.innopaas.com/api-reference/whatsapp-business-api/whatsapp-business-api>" %}

## 应用示例

访问 GitHub 网站，参阅适用于 Android 平台的 WhatsApp 一次性密码 (OTP) 应用示例。

{% embed url="<https://github.com/WhatsApp/WhatsApp-OTP-Sample-App?fbclid=IwAR3QN7OTeo98qsCjYdBNGGQ7yWDw72xYJsuJSVBZNcd2L2HXY5PzHlHkdbs>" %}

该应用示例展示了如何通过 API 发送和接收一次性密码和验证码、如何集成一键自动填写按钮和复制代码按钮、如何创建模板以及如何启动示例服务器。

## 参考文档

[One-tap autofill authentication templates](https://developers.facebook.com/docs/whatsapp/business-management-api/authentication-templates/autofill-button-authentication-templates#--------whatsapp)

[Authentication templates](https://developers.facebook.com/docs/whatsapp/business-management-api/authentication-templates)

[Zero-tap authentication templates](https://developers.facebook.com/docs/whatsapp/business-management-api/authentication-templates/zero-tap-authentication-templates)

## 总结

InnoPaaS作为WhatsApp的BSP，为企业提供了简单而高效的一次性密码（OTP）或验证码的集成解决方案。通过InnoPaaS的WhatsApp一键认证产品企业能够轻松集成WhatsApp的Zero-Tap消息模板，无需深入了解技术细节。节约宝贵的时间和开发成本，且可以快速提升了App的OTP回填率。
