更新时间:2024-01-10 GMT+08:00

Node.js

样例

发送短信发送分批短信接收状态报告

环境要求

基于Node.js 8.12.0版本,要求Node.js 8.12.0及以上版本。

  • 发送短信为单模板群发短信示例,发送分批短信为多模板群发短信示例。
  • 本文档所述Demo在提供服务的过程中,可能会涉及个人数据的使用,建议您遵从国家的相关法律采取足够的措施,以确保用户的个人数据受到充分的保护。
  • 本文档所述Demo仅用于功能演示,不允许客户直接进行商业使用。
  • 本文档信息仅供参考,不构成任何要约或承诺。

发送短信

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
/*jshint esversion: 6 */
var https = require('https'); //引入https模块
var url = require('url'); //引入url模块
var querystring = require('querystring'); // 引入querystring模块

//必填,请参考"开发准备"获取如下数据,替换为实际值
var realUrl = 'https://smsapi.ap-southeast-1.myhuaweicloud.com:443/sms/batchSendSms/v1'; //APP接入地址+接口访问URI
var appKey = 'c8RWg3ggEcyd4D3p94bf3Y7x1Ile'; //Application Key
// 认证用的appKey和appSecret硬编码到代码中或者明文存储都有很大的安全风险,建议在配置文件或者环境变量中密文存放,使用时解密,确保安全;
var appSecret = 'q4Ii87Bh************80SfD7Al'; //Application Secret
var sender = 'csms12345678'; //中国大陆短信签名通道号或全球短信通道号
var templateId = '8ff55eac1d0b478ab3c06c3c6a492300'; //模板ID

//条件必填,中国大陆短信关注,当templateId指定的模板类型为通用模板时生效且必填,必须是已审核通过的,与模板类型一致的签名名称
//全球短信不用关注该参数
var signature = "华为云短信测试"; //签名名称

//必填,全局号码格式(包含国家码),示例:+8615123456789,多个号码之间用英文逗号分隔
var receiver = '+8615123456789,+8615234567890'; //短信接收人号码

//选填,短信状态报告接收地址,推荐使用域名,为空或者不填表示不接收状态报告
var statusCallBack = '';

/**
 * 选填,使用无变量模板时请赋空值 var templateParas = '';
 * 单变量模板示例:模板内容为"您的验证码是${NUM_6}"时,templateParas可填写为'["369751"]'
 * 双变量模板示例:模板内容为"您有${NUM_2}件快递请到${TXT_20}领取"时,templateParas可填写为'["3","人民公园正门"]'
 * 查看更多模板和变量规范
 */
var templateParas = '["369751"]'; //模板变量,此处以单变量验证码短信为例,请客户自行生成6位验证码,并定义为字符串类型,以杜绝首位0丢失的问题(例如:002569变成了2569)。

/**
 * 构造请求Body体
 * 
 * @param sender
 * @param receiver
 * @param templateId
 * @param templateParas
 * @param statusCallBack
 * @param signature | 签名名称,使用中国大陆短信通用模板时填写
 * @returns
 */
function buildRequestBody(sender, receiver, templateId, templateParas, statusCallBack, signature){
    if (null !== signature && signature.length > 0) {
        return querystring.stringify({
            'from': sender,
            'to': receiver,
            'templateId': templateId,
            'templateParas': templateParas,
            'statusCallback': statusCallBack,
            'signature': signature
        });
    }

    return querystring.stringify({
        'from': sender,
        'to': receiver,
        'templateId': templateId,
        'templateParas': templateParas,
        'statusCallback': statusCallBack
    });
}

/**
 * 构造X-WSSE参数值
 * 
 * @param appKey
 * @param appSecret
 * @returns
 */
function buildWsseHeader(appKey, appSecret){
    var crypto = require('crypto');
    var util = require('util');

    var time = new Date(Date.now()).toISOString().replace(/.[0-9]+\Z/, 'Z'); //Created
    var nonce = crypto.randomBytes(64).toString('hex'); //Nonce
    var passwordDigestBase64Str = crypto.createHash('sha256').update(nonce + time + appSecret).digest('base64'); //PasswordDigest

    return util.format('UsernameToken Username="%s",PasswordDigest="%s",Nonce="%s",Created="%s"', appKey, passwordDigestBase64Str, nonce, time);
}

var urlobj = url.parse(realUrl); //解析realUrl字符串并返回一个 URL对象

var options = {
    host: urlobj.hostname, //主机名
    port: urlobj.port, //端口
    path: urlobj.pathname, //URI
    method: 'POST', //请求方法为POST
    headers: { //请求Headers
        'Content-Type': 'application/x-www-form-urlencoded',
        'Authorization': 'WSSE realm="SDP",profile="UsernameToken",type="Appkey"',
        'X-WSSE': buildWsseHeader(appKey, appSecret)
    },
    rejectUnauthorized: false //为防止因HTTPS证书认证失败造成API调用失败,需要先忽略证书信任问题
};
// 请求Body,不携带签名名称时,signature请填null
var body = buildRequestBody(sender, receiver, templateId, templateParas, statusCallBack, signature);

var req = https.request(options, (res) => {
    console.log('statusCode:', res.statusCode); //打印响应码

    res.setEncoding('utf8'); //设置响应数据编码格式
    res.on('data', (d) => {
        console.log('resp:', d); //打印响应数据
    });
});
req.on('error', (e) => {
    console.error(e.message); //请求错误时,打印错误信息
});
req.write(body); //发送请求Body数据
req.end(); //结束请求

发送分批短信

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
/*jshint esversion: 6 */
var https = require('https'); //引入https模块
var url = require('url'); //引入url模块

//必填,请参考"开发准备"获取如下数据,替换为实际值
var realUrl = 'https://smsapi.ap-southeast-1.myhuaweicloud.com:443/sms/batchSendDiffSms/v1'; //APP接入地址+接口访问URI
// 认证用的appKey和appSecret硬编码到代码中或者明文存储都有很大的安全风险,建议在配置文件或者环境变量中密文存放,使用时解密,确保安全;
var appKey = 'c8RWg3ggEcyd4D3p94bf3Y7x1Ile'; //Application Key
var appSecret = 'q4Ii87Bh************80SfD7Al'; //Application Secret
var sender = 'csms12345678'; //中国大陆短信签名通道号或全球短信通道号
var templateId1 = '8ff55eac1d0b478ab3c06c3c6a492300'; //模板ID1
var templateId2 = '8ff55eac1d0b478ab3c06c3c6a492300'; //模板ID2
//条件必填,中国大陆短信关注,当templateId指定的模板类型为通用模板时生效且必填,必须是已审核通过的,与模板类型一致的签名名称
//全球短信不用关注该参数
var signature1 = "华为云短信测试"; //签名名称1
var signature2 = "华为云短信测试"; //签名名称2

//必填,全局号码格式(包含国家码),示例:+8615123456789,多个号码之间用英文逗号分隔
var receiver1 = ['+8615123456789','+8615234567890']; //模板1的接收号码
var receiver2 = ['+8615123456789','+8615234567890']; //模板2的接收号码

//选填,短信状态报告接收地址,推荐使用域名,为空或者不填表示不接收状态报告
var statusCallBack = '';

/**
 * 选填,使用无变量模板时请赋空值 var templateParas = [];
 * 单变量模板示例:模板内容为"您的验证码是${NUM_6}"时,templateParas可填写为['369751']
 * 双变量模板示例:模板内容为"您有${NUM_2}件快递请到${TXT_20}领取"时,templateParas可填写为['3','人民公园正门']
 * 查看更多模板和变量规范
 */
var templateParas1 = ['123456']; //模板1变量,此处以单变量验证码短信为例,请客户自行生成6位验证码,并定义为字符串类型,以杜绝首位0丢失的问题(例如:002569变成了2569)。
var templateParas2 = ['234567']; //模板2变量,此处以单变量验证码短信为例,请客户自行生成6位验证码,并定义为字符串类型,以杜绝首位0丢失的问题(例如:002569变成了2569)。

/**
 * 构造smsContent参数值
 * 
 * @param receiver
 * @param templateId
 * @param templateParas
 * @param signature | 签名名称,使用中国大陆短信通用模板时填写
 * @returns
 */
function initDiffSms(receiver, templateId, templateParas, signature){
    if (null !== signature && signature.length > 0) {
        return {'to': receiver, 'templateId': templateId, 'templateParas': templateParas, 'signature': signature};
    }
    return {'to': receiver, 'templateId': templateId, 'templateParas': templateParas};
}

/**
 * 构造X-WSSE参数值
 * 
 * @param appKey
 * @param appSecret
 * @returns
 */
function buildWsseHeader(appKey, appSecret){
    var crypto = require('crypto');
    var util = require('util');

    var time = new Date(Date.now()).toISOString().replace(/.[0-9]+\Z/, 'Z'); //Created
    var nonce = crypto.randomBytes(64).toString('hex'); //Nonce
    var passwordDigestBase64Str = crypto.createHash('sha256').update(nonce + time + appSecret).digest('base64'); //PasswordDigest

    return util.format('UsernameToken Username="%s",PasswordDigest="%s",Nonce="%s",Created="%s"', appKey, passwordDigestBase64Str, nonce, time);
}

var body = JSON.stringify({ //请求Body
    'from': sender,
    'statusCallback': statusCallBack,
    'smsContent': [
        //smsContent,不携带签名名称时,signature请填null
        initDiffSms(receiver1, templateId1, templateParas1, signature1),
        initDiffSms(receiver2, templateId2, templateParas2, signature2)
    ]}
);

var urlobj = url.parse(realUrl); //解析realUrl字符串并返回一个 URL对象

var options = {
    host: urlobj.hostname, //主机名
    port: urlobj.port, //端口
    path: urlobj.pathname, //URI
    method: 'POST', //请求方法为POST
    headers: { //请求Headers
        'Content-Type': 'application/json',
        'Authorization': 'WSSE realm="SDP",profile="UsernameToken",type="Appkey"',
        'X-WSSE': buildWsseHeader(appKey, appSecret)
    },
    rejectUnauthorized: false //为防止因HTTPS证书认证失败造成API调用失败,需要先忽略证书信任问题
};

var req = https.request(options, (res) => {
    console.log('statusCode:', res.statusCode); //打印响应码

    res.setEncoding('utf8'); //设置响应数据编码格式
    res.on('data', (d) => {
        console.log('resp:', d); //打印响应数据
    });
});
req.on('error', (e) => {
    console.error(e.message); //请求错误时,打印错误信息
});
req.write(body); //发送请求Body数据
req.end(); //结束请求

接收状态报告

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
/*jshint esversion: 6 */

// 短信平台上报状态报告数据样例(urlencode)
//var success_body = "sequence=1&total=1&updateTime=2018-10-31T08%3A43%3A41Z&source=2&smsMsgId=2ea20735-f856-4376-afbf-570bd70a46ee_11840135&status=DELIVRD";
var failed_body = "orgCode=E200027&sequence=1&total=1&updateTime=2018-10-31T08%3A43%3A41Z&source=2&smsMsgId=2ea20735-f856-4376-afbf-570bd70a46ee_11840135&status=RTE_ERR";

/**
 * 解析状态报告数据
 * 
 * @param data 短信平台上报的状态报告数据
 * @returns
 */
function onSmsStatusReport(data) {
    var querystring = require('querystring');
    var keyValues = querystring.parse(data); // 解析状态报告数据

    /**
     * Example: 此处已解析status为例,请按需解析所需参数并自行实现相关处理
     * 
     * 'smsMsgId': 短信唯一标识
     * 'total': 长短信拆分条数
     * 'sequence': 拆分后短信序号
     * 'source': 状态报告来源
     * 'updateTime': 资源更新时间
     * 'status': 状态报告枚举值
     * 'orgCode': 状态码
     */
    var status = keyValues.status; // 状态报告枚举值
    // 通过status判断短信是否发送成功
    if ('DELIVRD' === status.toUpperCase()) {
        console.log('Send sms success. smsMsgId: ', keyValues.smsMsgId);
    } else {
        // 发送失败,打印status和orgCode
        console.log('Send sms failed. smsMsgId: ', keyValues.smsMsgId);
        console.log('Failed status: ', status);
        console.log('Failed orgCode: ', keyValues.orgCode);
    }
}

// onSmsStatusReport(success_body);
onSmsStatusReport(failed_body);