更新时间:2023-11-17 GMT+08:00
分享

Go

样例

发送短信发送分批短信

接收状态报告接收上行短信

环境要求

go1.11及以上版本

引用库

github.com/satori/go.uuid

  • 发送短信为单模板群发短信示例,发送分批短信为多模板群发短信示例。
  • 本文档所述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
112
package main

import (
    "bytes"
    "crypto/sha256"
    "crypto/tls"
    "encoding/base64"
    "fmt"
    "github.com/satori/go.uuid"
    "io/ioutil"
    "net/http"
    "net/url"
    "strings"
    "time"
)

//无需修改,用于格式化鉴权头域,给"X-WSSE"参数赋值
const WSSE_HEADER_FORMAT = "UsernameToken Username=\"%s\",PasswordDigest=\"%s\",Nonce=\"%s\",Created=\"%s\""
//无需修改,用于格式化鉴权头域,给"Authorization"参数赋值
const AUTH_HEADER_VALUE = "WSSE realm=\"SDP\",profile=\"UsernameToken\",type=\"Appkey\""

func main()  {
    //必填,请参考"开发准备"获取如下数据,替换为实际值
    apiAddress := "https://smsapi.cn-north-4.myhuaweicloud.com:443/sms/batchSendSms/v1" //APP接入地址(在控制台"应用管理"页面获取)+接口访问URI
    // 认证用的appKey和appSecret硬编码到代码中或者明文存储都有很大的安全风险,建议在配置文件或者环境变量中密文存放,使用时解密,确保安全;
    appKey := "c8RWg3ggEcyd4D3p94bf3Y7x1Ile" //APP_Key
    appSecret := "q4Ii87Bh************80SfD7Al" //APP_Secret
    sender := "csms12345678" //国内短信签名通道号
    templateId := "8ff55eac1d0b478ab3c06c3c6a492300" //模板ID

    //条件必填,国内短信关注,当templateId指定的模板类型为通用模板时生效且必填,必须是已审核通过的,与模板类型一致的签名名称
    
    signature := "华为云短信测试" //签名名称

    //必填,全局号码格式(包含国家码),示例:+86151****6789,多个号码之间用英文逗号分隔
    receiver := "+86151****6789,+86152****7890" //短信接收人号码

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

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

    body := buildRequestBody(sender,receiver,templateId,templateParas,statusCallBack,signature)
    headers := make(map[string]string)
    headers["Content-Type"] = "application/x-www-form-urlencoded"
    headers["Authorization"] = AUTH_HEADER_VALUE;
    headers["X-WSSE"] = buildWsseHeader(appKey, appSecret);
    resp, err := post(apiAddress, []byte(body),headers)
    if err != nil {
        return
    }
    fmt.Println(resp);
}

/**
 * sender,receiver,templateId不能为空
 */
func buildRequestBody(sender, receiver, templateId, templateParas, statusCallBack, signature string) string {
    param := "from=" + url.QueryEscape(sender) + "&to=" + url.QueryEscape(receiver) + "&templateId=" + url.QueryEscape(templateId)
    if templateParas != "" {
        param += "&templateParas=" + url.QueryEscape(templateParas)
    }
    if statusCallBack != "" {
        param += "&statusCallback=" + url.QueryEscape(statusCallBack)
    }
    if signature != "" {
        param += "&signature=" + url.QueryEscape(signature)
    }
    return param
}

func post(url string, param []byte, headers map[string]string)(string,error) {
    tr := &http.Transport{
        TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
    }
    client := &http.Client{Transport: tr}

    req, err := http.NewRequest("POST",url, bytes.NewBuffer(param));
    if err != nil {
        return "", err
    }
    for key, header := range headers {
        req.Header.Set(key, header)
    }

    resp, err := client.Do(req)
    defer resp.Body.Close()
    body, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        return "", err
    }
    return string(body), nil;
}

func buildWsseHeader(appKey,appSecret string)string {
    var cTime = time.Now().Format("2006-01-02T15:04:05Z")
    var nonce = uuid.NewV4().String()
    nonce = strings.ReplaceAll(nonce,"-","")

    h := sha256.New()
    h.Write([]byte(nonce + cTime + appSecret))
    passwordDigestBase64Str := base64.StdEncoding.EncodeToString(h.Sum(nil))

    return fmt.Sprintf(WSSE_HEADER_FORMAT,appKey,passwordDigestBase64Str,nonce, cTime);
}

发送分批短信

  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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
package main

import (
    "bytes"
    "crypto/sha256"
    "crypto/tls"
    "encoding/base64"
    "encoding/json"
    "fmt"
    "github.com/satori/go.uuid"
    "io/ioutil"
    "net/http"
    "strings"
    "time"
)

//无需修改,用于格式化鉴权头域,给"X-WSSE"参数赋值
const WSSE_HEADER_FORMAT = "UsernameToken Username=\"%s\",PasswordDigest=\"%s\",Nonce=\"%s\",Created=\"%s\""
//无需修改,用于格式化鉴权头域,给"Authorization"参数赋值
const AUTH_HEADER_VALUE = "WSSE realm=\"SDP\",profile=\"UsernameToken\",type=\"Appkey\""

func main()  {
	//必填,请参考"开发准备"获取如下数据,替换为实际值
    url := "https://smsapi.cn-north-4.myhuaweicloud.com:443/sms/batchSendDiffSms/v1" //APP接入地址(在控制台"应用管理"页面获取)+接口访问URI
    // 认证用的appKey和appSecret硬编码到代码中或者明文存储都有很大的安全风险,建议在配置文件或者环境变量中密文存放,使用时解密,确保安全;
    appKey := "c8RWg3ggEcyd4D3p94bf3Y7x1Ile" //APP_Key
    appSecret := "q4Ii87Bh************80SfD7Al" //APP_Secret
    sender := "csms12345678" //国内短信签名通道号
    templateId1 := "8ff55eac1d0b478ab3c06c3c6a492300" //模板ID1
    templateId2 := "8ff55eac1d0b478ab3c06c3c6a492300" //模板ID2

    //条件必填,国内短信关注,当templateId指定的模板类型为通用模板时生效且必填,必须是已审核通过的,与模板类型一致的签名名称
    
    signature1 := "华为云短信测试" //签名名称1
    signature2 := "华为云短信测试" //签名名称2

    //必填,全局号码格式(包含国家码),示例:+8615123456789,多个号码之间用英文逗号分隔
    receiver1 := []string{"+86151****6789", "+86152****7890"}; //模板1的接收号码
    receiver2 := []string{"+86151****6789", "+86152****7890"}; //模板2的接收号码

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

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

    item1 := initDiffSms(receiver1, templateId1, templateParas1, signature1);
    item2 := initDiffSms(receiver2, templateId2, templateParas2, signature2);

    item := []map[string]interface{}{item1,item2}
    body := buildRequestBody(sender, item, statusCallBack)

    headers := make(map[string]string)
    headers["Content-Type"] = "application/json;charset=utf-8"
    headers["Authorization"] = AUTH_HEADER_VALUE;
    headers["X-WSSE"] = buildWsseHeader(appKey, appSecret);
    resp, err := post(url,body,headers)
    if err != nil {
        return
    }
    fmt.Println(resp);
}

func buildRequestBody(sender string, item []map[string]interface{}, statusCallBack string) []byte{
    body := make(map[string]interface{})
    body["smsContent"] = item
    body["from"] = sender
    if statusCallBack != "" {
        body["statusCallback"] = statusCallBack
    }
    res, _ := json.Marshal(body)
    return res;
}

func initDiffSms(reveiver []string, templateId string, templateParas []string, signature string) map[string]interface{} {
    diffSms := make(map[string]interface{});
    diffSms["to"] = reveiver
    diffSms["templateId"] = templateId
    if templateParas != nil && len(templateParas) > 0 {
        diffSms["templateParas"] = templateParas
    }
    if signature != "" {
        diffSms["signature"] = signature
    }
    return diffSms;
}

func post(url string, param []byte, headers map[string]string)(string,error) {
    tr := &http.Transport{
        TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
    }
    client := &http.Client{Transport: tr}

    req, err := http.NewRequest("POST",url, bytes.NewBuffer(param));
    if err != nil {
        return "", err
    }
    for key, header := range headers {
        req.Header.Set(key, header)
    }

    resp, err := client.Do(req)
    defer resp.Body.Close()
    body, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        return "", err
    }
    return string(body), nil;
}

func buildWsseHeader(appKey,appSecret string)string {

    var cTime = time.Now().Format("2006-01-02T15:04:05Z")
    var nonce = uuid.NewV4().String()
    nonce = strings.ReplaceAll(nonce,"-","")

    h := sha256.New()
    h.Write([]byte(nonce + cTime + appSecret))
    passwordDigestBase64Str := base64.StdEncoding.EncodeToString(h.Sum(nil))

    return fmt.Sprintf(WSSE_HEADER_FORMAT,appKey,passwordDigestBase64Str,nonce, cTime);
}

接收状态报告

 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
package main

import (
    "fmt"
    "net/url"
    "strings"
)

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

func onSmsStatusReport(data string) {
    ss, _ := url.QueryUnescape(data)
    params := strings.Split(ss, "&")
    keyValues := make(map[string]string)
    for i := range params {
        temp := strings.Split(params[i],"=")
        keyValues[temp[0]] = temp[1];
    }
    status := keyValues["status"];
    if status == "DELIVRD" {
        fmt.Println("Send sms success. smsMsgId: " + keyValues["smsMsgId"])
    } else {
        fmt.Println("Send sms failed. smsMsgId: " + keyValues["smsMsgId"])
        fmt.Println("Failed status:  " + keyValues["status"])
    }
}

接收上行短信

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package main

import (
    "fmt"
    "net/url"
    "strings"
)

func main() {
    upData := "from=%2B86151****6789&to=1069***2019&body=***********&smsMsgId=9692b5be-c427-4525-8e73-cf4a6ac5b3f7";
    onSmsUpData(upData);
}

func onSmsUpData(data string) {
    ss, _ := url.QueryUnescape(data)
    params := strings.Split(ss, "&")
    keyValues := make(map[string]string)
    for i := range params {
        temp := strings.Split(params[i],"=")
        keyValues[temp[0]] = temp[1];
    }
    body := keyValues["body"];
    fmt.Println("Sms up data. Body: " + body)
}

相关推荐

相关文档

相关产品

  • 代码样例:Java/PHP/Python/C#/Node.js
  • 发送短信API
  • 发送分批短信API
  • 接收状态报告API
  • 接收上行短信API
  • 调测问题处理

语音通话

隐私保护通话

分享:

    相关文档

    相关产品