规则


短信类型

目前 SendCloud 的短信服务支持验证码, 行业通知和营销短信.

验证码短信:注册密码, 修改密码, 身份有验证等.

行业通知短信:由用户行为触发生产的通知, 如:订单通知, 回复通知等.

营销短信: 向用户统一发送的消息通知等.


接入方式

目前 SendCloud 对短信业务提供 WEBAPI 的接入方式, 开发者可以利用 SendCloud 提供的 HTTP 接口, 调用 SendCloud 的服务.

使用我们的短信API, 开发者需要创建:

SMS_USER 与 SMS_KEY

SMS_USER是调用接口发信时候的账号

用户可以通过【短信语音】-【发送设置】-【发送授权】来创建 SMS_USER, 同时 SendCloud 会自动生成对应的 SMS_KEY. 目前 SendCloud 只允许拥有一个 SMS_USER.

短信模板

短信模板, 用户在发送短信之前,必须在前台页面编辑短信内容, 并提交审核. 审核通过后, 通过模板的 ID 调用发送.

变量替换

SendCloud 支持在短信中使用「变量」.

变量的格式: 首尾使用 % 包围, 即为用户定义的一个变量. 举例:

欢迎使用爱发信. 您的手机验证码是: %code%. # code是变量

变量的用法:

  1. 在短信模板中使用变量, 作为占位符
  2. 在短信 API 中设置变量的值
  3. SendCloud 会根据请求参数, 来替换短信内容中相应变量的值
  4. 变量的值的长度不能超过 16或32 个字符, 变量的值中不能含有 HTTP 链接

变量的命名规则:

  1. 变量名可包含字母(大小写均可)、数字、'_'(下划线)或'-'(中划线)的任意组合,不得出现其他字符
  2. 变量名以字母(大小写均可)、数字、'_'(下划线)或'-'(中划线)开头
  3. 变量名长度不得超过16或32个字符

签名

由于短信发送的特殊性, 以及相关的 ISP 机构的审查制度, 短信内容中必须含有能说明发送者身份的签名. 目前, SendCloud 支持在编辑短信模板时添加签名, 并设置签名位置(目前只支持签名在短信尾部).

注意: 短信模板必须含有「签名」, 否则不能通过审核.

短信备案

短信备案是指在短信运营商处进行信息审核,未备案可能影响用户的发信成功率。

用户第一次调用短信接口请求成功后,可在【短信语音】-【发送设置】-【短信备案】中进行短信备案,步骤如下:

第一步,点击下载备案承诺书,打印成为纸质版;

第二步,仔细阅读承诺书,负责人进行签字,并加盖公司公章;

第三步,通过拍照或者扫描的方式,转换为电子版图片,JPG或PDF格式;

第四步,点击上传备案承诺书,上传图片,上传成功后等待审核,SendCloud将在1个工作日内出审核结果。

拦截列表

为减少用户对空号, 停机等手机号码的无效发送, SendCloud 会根据运营商反馈的发送结果来做相应的拦截处理.

所有发送失败的手机号码都会进入拦截列表, 拦截列表记录的字段如下:

不同的失败原因会产生不同的拦截时间, 不同的拦截范围, 详细见下表:

返回码 失败描述 拦截时间 全局,局部 用户是否可删除
500 发送失败, 手机空号 30 天 全局拦截
510 发送失败, 手机停机 1 小时 全局拦截
520 发送失败,手机号码在黑名单 1 小时 局部拦截
530 发送失败, 对方占线 0 秒
540 发送失败, 无人接听 0 秒
550 发送失败, 该模板内容被拦截 1 小时 局部拦截
560 发送失败, 手机终端问题 1 小时 全局拦截
570 发送失败, 手机不在服务区 1 小时 全局拦截
580 发送失败, 手机关机 0 秒
590 发送失败, 其他原因 0 秒

全局拦截: 此条拦截记录会对 SendCloud 所有用户生效.

局部拦截: 此条拦截记录只会对来源用户生效.

用户可以在拦截列表中删除来源是自己的记录, 对于来源不是自己的拦截手机号, 可以在确认手机号通讯正常之后, 联系客服处理.

API 验证机制

短信发送使用数字签名的验证模式. 这种模式能够有效避免密码在传输途中的泄露, 是安全级别很高的一种加密验证方式.

数字签名的验证模式: 调用 API 时, 用户不需要把密码 ( SMS_KEY ) 作为参数明文传输, 而是将数字签名 ( signature ) 作为参数传输给服务器. 服务器端会验证此 signature 的正确性.

短信API验证机制中,提供MD5、SHA256两种验证方法

生成数字签名 ( signature ) 的方法:

1. 将实际调用API的参数以字母升序(A-Z)排列, 不包括 smsKey 和 signature 字段本身
2. 按照排列之后的顺序, 以 'key=value' + '&' + 'key=value' 的方式连接所有参数, 
   得到字符串 param_str
3. 以 SMS_KEY + '&' + param_str + '&' + SMS_KEY 的方式得到字符串 sign_str
4. 计算 sign_str 的MD5值 (32位, 不区分大小写)或sha256, 得到 signature

提示:
1. 生成签名时, 参数不要使用 'urlencode'. 在调用 api 时, 才需要对参数做 'urlencode'
2. '&' 是代码中使用的连接符, '+'是文档显示之用

下面提供了一个代码示例 ( python ):

import hashlib

SMS_USER = 'testuser'
SMS_KEY = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'

param = {
    'smsUser': SMS_USER,
    'templateId' : 1,
    'phone'  : 18888888888,
    'vars'   : {},
}

param_keys = list(param.keys())
param_keys.sort()

param_str = ''

for key in param_keys:
    param_str += key + '=' + str(param[key]) + '&'

param_str = param_str[:-1]

sign_str = SMS_KEY + '&' + param_str + '&' + SMS_KEY
signature = hashlib.sha256(sign_str).hexdigest()

提示: 所有的 API 都支持 HTTPS.

timestamp 时间戳 ( 提升逼格 )

用户可以在每个 API 请求中加入 timestamp 参数, SendCloud 会检查 timestamp 和 服务器当前时间, 如果两者相差大于60秒, 则请求会被拒绝.

用户需要通过调用 API 来获取 SendCloud 服务器的时间戳, 而不是自己的本地时间.

timestamp 参数需要被包含在 signature 中, 参与生成数字签名.


SMSHook

SMSHook 机制

用户将短信、语音请求发送给 SendCloud 之后, SendCloud 会把「请求结果」同步返回给用户, 而短信、语音的「发送结果」和 「其他事件结果」是通过 SMSHook 异步返回给用户的.

目前 SendCloud 支持的事件如下:

事件 触发条件
请求(request) 请求成功
送达(deliver) 发送成功
处理失败(workererror) 处理失败
发送失败(delivererror) 发送失败
点击(click) 用户点击链接
回复(reply) 用户回复(仅短信有)
短信上行(sms_mo) 用户主动提交短信给接口
模板审核(templateVerify) 短信模板的审核结果消息

使用方法:

注意: 我们会对用户提供的 URL 做检测. 需要此 HTTP 服务能够正确响应 get | post 请求, 并且保证返回的 HTTP 状态码 为 200

数字签名验证

为了确保消息的来源身份是 SendCloud, 你可以选择对 POST 数据的来源进行安全认证. ( 不验证, 直接解析 POST 的数据也可以 ).

安全认证的方法如下:

python 代码示例

import hashlib, hmac
def verify(appkey, token, timestamp, signature):
    return signature == hmac.new(
        key=appkey,
        msg='{}{}'.format(timestamp, token),
        digestmod=hashlib.sha256).hexdigest()

Java 代码示例 (依赖 apache codec)

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.codec.binary.Hex;

public boolean verify(String appkey, String token, long timestamp,
            String signature) throws NoSuchAlgorithmException, InvalidKeyException {
    Mac sha256HMAC = Mac.getInstance("HmacSHA256");
    SecretKeySpec secretKey = new SecretKeySpec(appkey.getBytes(),"HmacSHA256");
    sha256HMAC.init(secretKey);
    StringBuffer buf = new StringBuffer();
    buf.append(timestamp).append(token);
    String signatureCal = new String(Hex.encodeHex(sha256HMAC.doFinal(buf
            .toString().getBytes())));
    return signatureCal.equals(signature);
}

php 代码示例

function verify($appkey,$token,$timestamp,$signature){
        $hash="sha256";
            $result=hash_hmac($hash,$timestamp.$token,$appkey);
                return strcmp($result,$signature)==0?1:0;
}

重试机制

如果遇到 URL 访问错误或超时, SendCloud 最多会重试 6 次. 每次重试的时间间隔最快为 3min, 10min, 30min, 1h, 6h, 12h, 24h. 即在消息丢失前, 你有足够的时间来修复 URL.

如果超过重试次数,SendCloud 将会把消息丢弃.

每次事件处理, 数据解析, 你需要在 3s 内返回状态码 200, 否则, SendCloud 将会重发该条消息。

事件说明

目前 SMSHook 支持的事件类型包括: 请求, 送达, 处理失败, 发送失败, 用户回复.

请求 ( request )

参数说明

参数 类型 说明
event string 事件类型:"request"
eventType int 事件类型代码:1
message string 消息内容
smsUser string smsUser
smsIds list 短信(或语音)ID组成的数组
templateId int 模板ID
phones list 手机号组成的数组
timestamp long 时间戳
token string 随机产生的长度为50的字符串
signature string 数字签名字符串
userId int 用户ID
labelId int 预留, 暂不用
tag string 用户自定义标签
msgCount int 条数
msgType int "0"短信, "1"彩信, "2"国际短信, "3"国内语音
smsType int "0"验证码, "1"通知, "2"营销

POST 数据示例

{
"msgType":0,
"signature":"1ff237043487aeb4dc1b21c22b5ead9e4df94a31a3afa0ad53238eb38c2cbeea",
"phones":"[\"13888888888\"]",
"eventType":1,
"templateId":29999,
"message":"request",
"userId":19999,
"smsUser":"App",
"smsIds":"[\"1652150994014_9373_14466_36735_99drnc$13888888888\"]",
"token":"VDymF6ihuJkKZjHiJZkLKGmY6q9qAQ2WGopLh7mBsDeAO6GKV5",
"labelId":0,
"smsType":0,
"tag":0,
"event":"request",
"timestamp":1652150994087
}

送达 ( deliver )

参数说明

参数 类型 说明
event string 事件类型:"deliver"
eventType int 事件类型代码:2
message string 消息内容
smsUser string smsUser
smsId string 短信(或语音)ID
templateId int 模板ID
phone string 手机号
timestamp long 时间戳
token string 随机产生的长度为50的字符串
signature string 数字签名字符串
userId int 用户ID
labelId int 预留, 暂不用
tag string 用户自定义tag
msgCount int 条数
msgType int "0"短信, "1"彩信, "2"国际短信, "3"国内语音
smsType int "0"验证码, "1"通知, "2"营销
outboundTime string 通道时间
receiptTime string 回执时间

POST 数据示例

{
"outboundTime":"2022-05-10 01:29:31",
"msgType":0,
"signature":"9ca96fa072bfa048969aa0cb7bf7baf64100234640a1b9793cca1a419afb9cb8",
"eventType":2,
"templateId":29999,
"message":"Successfully delivered",
"userId":19999,
"smsUser":"APP",
"token":"4mRG9lGhVb3jZhOMnksFPBtX1OLDMNZMfXTFHkFd9eybfdRiHM",
"smsId":"1652117371408_19999_376_4631_qrwnpq$13888888888",
"receiptTime":"2022-05-10 01:29:50",
"labelId":0,
"phone":"13888888888",
"msgCount":1,
"smsType":1,
"tag":0,
"event":"deliver",
"timestamp":1652117390000
}

处理失败 ( workererror)

参数说明

参数 类型 说明
event string 事件类型:"workererror"
eventType int 事件类型代码:4
message string 消息内容
encodeMessage string base64编码的消息内容
statusCode int 错误码
smsUser string smsUser
smsId string 短信(或语音)ID
templateId int 模板ID
phone string 手机号
timestamp long 时间戳
token string 随机产生的长度为50的字符串
signature string 数字签名字符串
userId int 用户ID
labelId int 预留, 暂不用
tag string 用户自定义tag
msgCount int 条数
msgType int "0"短信, "1"彩信, "2"国际短信, "3"国内语音
smsType int "0"验证码, "1"通知, "2"营销
outboundTime string 通道时间

POST 数据示例

{
"outboundTime":"2022-05-10 00:00:54",
"msgType":0,
"signature":"8a3a030169decf0134010b8d7443cfbbf16b08a6aee524291706dacfb127be95",
"eventType":4,
"templateId":-3,
"message":"smsworker:address in unsubscribe list(取消订阅)",
"userId":19999,
"smsUser":"APP",
"token":"sGfR3yMheseXBxkPt3NnIuDQK5aYdzbfyUO8i0oz6IJI07pNHj",
"smsId":"1652112054796_19999_167_-3_ty8pqn$13888888888",
"labelId":0,
"encodeMessage":"c21zd29ya2VyOmFkZHJlc3MgaW4gdW5zdWJzY3JpYmUgbGlzdCjlj5bmtojorqLpmIUp",
"phone":"13888888888",
"msgCount":1,
"smsType":1,
"tag":0,
"event":"workererror",
"timestamp":1652112054846,
"statusCode":430
}

发送失败 ( delivererror)

参数说明

参数 类型 说明
event string 事件类型:"delivererror"
eventType int 事件类型代码:5
message string 消息内容
encodeMessage string base64编码的消息内容
statusCode int 错误码
smsUser string smsUser
smsId string 短信(或语音)ID
templateId int 模板ID
phone string 手机号
timestamp long 时间戳
token string 随机产生的长度为50的字符串
signature string 数字签名字符串
userId int 用户ID
labelId int 预留, 暂不用
tag string 用户自定义tag
msgCount int 条数
msgType int "0"短信, "1"彩信, "2"国际短信, "3"国内语音
smsType int "0"验证码, "1"通知, "2"营销
outboundTime string 通道时间
receiptTime string 回执时间

POST 数据示例

{
"outboundTime":"2022-05-10 09:31:12",
"msgType":0,
"signature":"785370449703a5dfca4cb773a5a266f6be1db8bdbe713b8ce74ca572f9788a7a",
"eventType":5,
"templateId":29999,
"message":"REJECTD(其他)",
"userId":19999,
"smsUser":"APP",
"token":"MTha34FTrBRBmJXdZK4qVCqRxh8N4IlAJlM11sd1FSfCk9jmo3",
"smsId":"1652146271665_19999_8755_3883_37059m$13888888888",
"receiptTime":"2022-05-10 09:31:17",
"labelId":0,
"encodeMessage":"UkVKRUNURCjlhbbku5Yp",
"msgCount":1,
"smsType":0,
"tag":0,
"event":"delivererror",
"timestamp":1652146277000,
"statusCode":590
}

点击 (click)

参数说明

参数 类型 说明
event string 事件类型:"click"
eventType int 事件类型代码:10
message string 消息内容:click sms
smsUser string smsUser
smsId string 短信(或语音)ID
templateId int 模板ID
clickUrl string 点击链接
phone string 手机号
timestamp long 时间戳
token string 随机产生的长度为50的字符串
signature string 数字签名字符串
userId int 用户ID
labelId int 预留, 暂不用
tag string 用户自定义tag
msgType int "0"短信, "1"彩信, "2"国际短信, "3"国内语音
smsType int "0"验证码, "1"通知, "2"营销
ip string ip
deviceName tring 设备名称
deviceType int 设备类型(1:PC,2:手机,3:平板,0:其他)
oSName string 操作系统名称
oSVer string 操作系统版本
explorerName string 浏览器名称
explorerVer string 浏览器版本

POST 数据示例

{
    "clickUrl": "https://ifaxin.com", 
    "deviceType": "1", 
    "msgType": "0", 
    "signature": "ee7a01678cdc69a53074a0f5516c93541c7d0bd60758d5344ece26b8347002e8", 
    "ip": "124.127.61.82", 
    "eventType": "10", 
    "templateId": "868058", 
    "message": "click sms", 
    "userId": "15", 
    "smsUser": "sms_ss", 
    "deviceName": "Other", 
    "token": "uTuoR0IzT1OSQqP1ykjG5QP1cQF9rdomhLkGkse3FbZwhE7UF7", 
    "oSName": "Windows 7", 
    "explorerName": "Chrome", 
    "smsId": "1668413622360_15_9_868058_uny9w1$13437150000", 
    "explorerVer": "86.0.4240", 
    "labelId": "0", 
    "tag": "0",
    "phone": "13437150000", 
    "smsType": "1", 
    "event": "click", 
    "oSVer": "", 
    "timestamp": "1668413648109"
}

回复 (reply)

参数 类型 说明
event string 事件类型:"reply"
eventType int 事件类型代码:6
smsUser string smsUser
templateId int 模板ID
phone string 手机号
timestamp long 时间戳
token string 随机产生的长度为50的字符串
signature string 数字签名字符串
userId int 用户ID
labelId int 预留, 暂不用
replyContent string 回复内容
encodeReplyContent string base64编码的回复内容
replyTime Datetime 回复时间
tag string 用户自定义tag
msgCount int 条数
msgType int "0"短信, "1"彩信, "2"国际短信, "3"国内语音
smsType int "0"验证码, "1"通知, "2"营销

POST 数据示例

{
"msgType":0,
"signature":"3a9ab5dd4ca5ee56dee8c8998f11a1ec33ce5bcd720987aff86b6608fc967a3d",
"eventType":6,
"templateId":29999,
"userId":19999,
"smsUser":"APP",
"encodeReplyContent":"5a6i5pyN55S16K+d5piv5ZOq5Liq5Y+356CB",
"token":"MTY8WxQIUUWyKjke1MTWpPu88mxxmHINTsT7x2DwJZQq2VynNl",
"replyTime":"2022-05-10 08:49:14",
"labelId":0,
"phone":"13888888888",
"smsType":0,
"replyContent":"客服电话是哪个号码",
"tag":0,
"event":"reply",
"timestamp":1652143756604
}

短信上行 (sms_mo)

参数 类型 说明
event string 事件类型:"sms_mo"
eventType int 事件类型代码:7
smsUser string smsUser
templateId int 模板ID
phone string 手机号
timestamp long 时间戳
token string 随机产生的长度为50的字符串
signature string 数字签名字符串
userId int 用户ID
labelId int 预留, 暂不用
replyContent string 上行回复内容
encodeReplyContent string base64编码的回复内容
replyTime Datetime 上行回复时间
tag string 用户自定义tag
msgCount int 条数
msgType int "0"短信, "1"彩信, "2"国际短信, "3"国内语音
smsType int "0"验证码, "1"通知, "2"营销

POST 数据示例

{
"msgType":0,
"token":"0IwiTzCRJFwlS40cwQRPXP0j61xg3B9RNLFu5WpC9jD0CruWdT",
"timestamp":"1566293197107",
"labelId": 0,
"phone":"13888888888",
"replyContent":"test_mo",
"encodeReplyContent":"dGVzdF9tbw==",
"replyTime":"2019-08-16 16:16:16",
"userId":19999,
"templateId":-1,
"smsUser":"smsuser",
"event":"sms_mo",
"signature":"da7339b7ddbd1228c2bcd8abdc80e28ad37fba3dd5c73dfb18e6ca91e6ae7715",
"eventType": 7,
"tag":0,
"smsType":0
}

模板审核 (templateVerify)

参数 类型 说明
event string 事件类型:"templateVerify"
eventType int 事件类型代码:8
verfiyResult int 审核结果:0审核中,1通过,-1不通过
verfiyComment string 审核意见.审核不通过时有verfiyComment
templateId int 模板ID
name string 模板名称
msgType int 模板类型:0短信,1彩信,2国际短信,3语音
smsType int 短信类型:0验证码,1行业通知,2营销
timestamp long 时间戳
token string 随机产生的长度为50的字符串
signature string 数字签名字符串
userId int 用户ID

POST 数据示例

{
    "msgType":0,
    "signature":"b80266d81f527c1ce870ba36c4c9e79fd9725c5a7c943577a25d8116aa67c927",
    "verfiyResult":1,
    "name":"感谢莅临上海车展展台",
    "smsType":1,
    "eventType":8,
    "templateId":6255,
    "event":"templateVerify",
    "userId":102,
    "token":"M1dgqmmOsM3BC9dCqBsMjtDh6I5jYwXngPEtcVV9v8XoplF1VQ",
    "timestamp":1646628597226
}