身份验证模板最佳实践

本文带您了解WhatsApp新的身份验证模板。

背景信息

自2023年5月1日起,WhatsApp推出了包含一次性密码按钮的身份验证模板(Authentication),自2023年5月29日起,所有新建的身份验证模板都必须包含一次性密码按钮。更多关于身份验证模板的信息请参见WhatsApp模板类型说明

身份验证模板

模板组成

包含一次性密码按钮的身份验证模板由以下部分组成:

  • 身份验证模板的预设固定文本:

    • <VERIFICATION_CODE> 是您的验证码。

    • 安全免责声明(可选):为安全起见,请勿共享该验证码。

    • 过期警告(可选):这组验证码将在<NUM_MINUTES>分钟后过期。

  • 按钮:复制验证码按钮(Copy code)或一键自动填写按钮(Autofill)。

示例图:

身份验证模板OTP.png

模板规则限制

  • 包含一次性密码按钮的身份验证模板仅由预设文本和按钮组成。

  • 身份验证模板的内容或参数不允许使用网址、媒体(图片、音频、视频、文件等)和表情符号。

  • 身份验证模板使用参数最多支持15 个字符,即验证码最大长度为15位。

按钮

身份验证模板必须包含复制验证码按钮(Copy code)或一键自动填写按钮(Autofill)。当用户点击按钮时,不同按钮的工作方式不同:

  • 复制验证码按钮:会将一次性密码或验证码复制到用户的剪贴板。然后,用户可以手动切换到您的应用,将一次性密码或验证码粘贴到应用界面。

  • 一键自动填写按钮:会自动加载您的应用,并向该应用传递一次性密码或验证码。

一键自动填写按钮可提供最佳用户体验,所以是首选的解决方案。但是,目前仅安卓系统手机(Android)支持一键自动填写按钮,并且此类按钮需要更改您应用的代码,才能执行“握手”,另外还需要更改应用的签名密钥哈希值。请参阅下方握手应用签名密钥哈希值部分。

最佳实践

  • 在发送一次性密码或验证码前,先确认用户的WhatsApp电话号码,再向该号码发送一次性密码或验证码。

  • 明确告知用户,一次性密码或验证码将发送至他们的WhatsApp电话号码,尤其是当您为用户提供多种方式来接收一次性密码或验证码时。

  • 当用户将一次性密码或验证码粘贴到您的应用中时,或应用在一键自动填写按钮流程中收到一次性密码或验证码时,请明确告知用户应用已获取一次性密码或验证码。

参数说明

参数名

描述

示例值

COMPONENTS

该数组包含描述模板内容和元数据的单个对象。

-

LANGUAGE

模板的语言代码

en_US

NAME

模板的名称。

仅支持小写字母、下划线、数字。

名称长度不超过60个字符。

verification_code

WHATSAPP_BUSINESS_ACCOUNT_ID

要与模板关联的WABA ID。

106***********06

ADD_SECURITY_RECOMMENDATION

可选。

布尔值。

如需在模板中加入以下字符串,请将此占位符设为true:为安全起见,请勿共享该验证码。

否则,请设为false。

说明

字符串内容为固定内容。

true

CODE_EXPIRATION_MINUTES

可选。

整数。

表示一次性密码或验证码的有效期(以分钟为单位)。

如果忽略此占位符,则发送的消息不会显示一次性密码或验证码的过期警告。

最小值为1,最大值为90。

5

COPY_CODE_BUTTON_TEXT

字符串。

复制验证码按钮的文本。

重要

即使模板使用一键自动填写按钮,您仍须提供此值。如果我们无法验证您的握手,身份验证模板消息会显示一个包含此文本的复制验证码按钮。

不超过 25 个字符。

Copy Code

ONE_TAP_BUTTON_TEXT

仅适用于一键自动填写按钮。

字符串。

一键自动填写按钮的文本。

不超过 25 个字符。

Autofill

OTP_TYPE

枚举。

表示按钮类型。

如需让模板使用复制验证码按钮,请将此占位符设为 COPY_CODE;

如需让模板使用一键自动填写按钮,则设为 ONE_TAP。

请参阅上方按钮部分。

ONE_TAP

PACKAGE_NAME

Android应用的应用包名。

com.example.myapplication

SIGNATURE_HASH

应用签名密钥哈希值。

请参阅下方应用签名密钥哈希值

K8a%2FAINcGX7

应用签名密钥哈希值

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

如要计算您的哈希值,请遵循Google有关计算应用的哈希字符串的说明。

或者,如果您按照Google说明下载了应用签名密钥证书,您可以结合使用该证书和sms_retriever_hash_v9.sh shell脚本来计算哈希值。

握手

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

说明

如果您在发送消息之前不执行“握手”,或消息未通过资格检查,则发送的消息将显示复制验证码按钮(Copy code),而不是一键自动填写按钮(Autofill)。

资格检查

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

  • “握手”是在发送消息前的10分钟内发起。

  • 消息中的应用包名(创建模板时在components数组的package_name属性中定义)与意图上设置的应用包名匹配。WhatsApp应用将使用您的应用程序提供的PendingIntent对象中调用的getCreatorPackage方法判断项目包名称是否匹配。

  • 消息中的应用签名密钥哈希值(创建模板时在components数组的signature_hash属性中定义)与您所安装应用的签名密钥哈希值匹配。

  • 消息包含一键自动填写按钮的文本。

  • 您的应用已定义用于接收一次性密码或验证码的活动。请参阅下方活动(Activity)部分。

Android通知

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

  • 用户使用所发送消息的接收电话号码(账户)登录WhatsApp应用。

  • 用户登录您的应用。

  • Android操作系统是 KitKat(Android 4.4,API 19)或更高版本。

  • WhatsApp应用已启用显示通知(设置>通知)。

  • 已为WhatsApp应用启用设备层级通知。

  • 在WhatsApp应用中,用户与您的企业之间过去的消息对话未设为静音。

客户端实现

在您的应用中实现以下活动(activity)和类(class)。

活动(Activity)

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

在收到身份验证模板消息(且该消息已通过所有资格检查)后,WhatsApp应用将立即开始执行此活动。

公共类(Public Class)

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

发起“握手”

以下示例展示了使用WhatsApp应用发起“握手”的一种方法。

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应用会通过传递com.whatsapp.OTP_ERROR意图来显示一些错误信息。在这种情况下,您将收到错误密钥和消息,而不是用户的一次性密码或验证码。

请注意,其中某些错误信号只有在Android模拟器中运行WhatsApp应用时才会出现。

描述

incompatible_os_version

Android 版本不兼容。

如果发起“握手”(发送 com.whatsapp.otp.OTP_REQUESTED 意图),但设备所运行的 Android 版本低于 v19,则会出现此情况。

incorrect_signature_hash

说明

仅限模拟器

签名哈希不正确。

如果发起“握手”(发送 com.whatsapp.otp.OTP_REQUESTED 意图),并且WhatsApp应用收到使用一键自动填写按钮的身份验证模板消息,而消息中的包名未生成消息的签名哈希,则会出现此情况。

missing_handshake_or_disorder

缺少“握手”/操作指令。

如果WhatsApp应用收到带有一键自动填写按钮的身份验证模板消息,但未发起“握手”,则会出现此情况。

otp_request_expired

一次性密码请求过期。

如果使用一键自动填写按钮的身份验证模板已送达用户,但此时距您发起握手已超过 10 分钟,则会出现此情况。在此情况下,会改为显示复制验证码按钮。

whatsapp_message_notification_disabled

说明

仅限模拟器

WhatsApp应用中设置了禁用消息通知。

如果发起“握手”(发送 com.whatsapp.otp.OTP_REQUESTED 意图),但用户已在WhatsApp应用中禁用通知,则会出现此情况。

whatsapp_notification_disabled

说明

仅限模拟器

已在设备层级禁用WhatsApp通知。

如果发起“握手”(发送 com.whatsapp.otp.OTP_REQUESTED 意图),但用户已在WhatsApp(设备层级设置)中禁用应用通知,则会出现此情况。

集成

错误信号通过广播意图送达,因此必须运行BroadcastReceiver才能收到错误信号。

使用 manifest.xml

<receiver
 android:name=".app.otp.OtpErrorReceiver"
 android:enabled="true"
 android:exported="true" >
   <intent-filter>
       <action android:name="com.whatsapp.otp.OTP_ERROR"/>
   </intent-filter>
</receiver>

使用接收器类

<![CDATA[ public class OtpErrorReceiver extends BroadcastReceiver { public static final String OTP_ERROR_KEY = "error"; public static final String OTP_ERROR_MESSAGE_KEY = "error_message"; @Override public void onReceive(Context context, Intent intent) { try { PendingIntent pendingIntent = intent.getParcelableExtra("_ci_"); if (pendingIntent != null) { String packageName = pendingIntent.getCreatorPackage(); if (packageName.equalsIgnoreCase("com.whatsapp") || packageName.equalsIgnoreCase("com.whatsapp.w4b")) { String otpErrorKey = intent.getStringExtra(OTP_ERROR_KEY);String otpErrorMessage = intent.getStringExtra(OTP_ERROR_MESSAGE_KEY); // Handle errors } } } catch (BadParcelableException e) { Log.e("OtpErrorReceiver", e.getLocalizedMessage()); } } } ]]>

应用示例

请访问Github网站,参阅适用于Android平台的WhatsApp一次性密码 (OTP) 应用示例。该应用示例展示了如何通过 API 发送和接收一次性密码和验证码、如何集成一键自动填写按钮和复制验证码按钮、如何创建模板以及如何启动示例服务器。