Java SDK
https://github.com/wxxsxxGit/sms-sdk-java
自助API报备模板及模板发送
https://www.yuque.com/docs/share/8446f03b-5132-4e87-b8d6-48b9cee0846a?
发布时间 | 版本 | 修订人 | 变更摘要 |
2020-05-28 | 1.0 | 郑维君 | |
2020-07-28 | 1.1 | 关宏新 | 1、增加实际提交接口地址域名 2、新增4.7、4.8、4.9章节内容 |
2020-08-25 | 1.2 | 郑维君 | 修复导出word格式时部分内容由于格式不支持导致丢失的问题 |
2020-08-28 | 1.3 | 郑维君 | 1、统一修正msgId为msgid 2、增加spId术语 |
2021-04-12 | 1.4 | 夏帅 | 增加定时短信字段sendTime |
2021-06-15 | 1.5 | 夏帅 | 上行增加签名字段sign |
2022-09-07 | 1.6 | 钟祥 | 增加4.9加密方式发送 |
2022-09-30 | 1.7 | 关宏新 | 增加API参考 |
提供对接短信发送平台HTTP/HTTPS协议的统一规范,便于客户侧进行接口的对接开发
平台对外提供的HTTP/HTTPS协议短信下发接口,以及状态、上行的推送和主动获取接口
术语 | 英文 | 中文 |
HTTP | Hyper Text Transfer Protocol | 超文本传输协议 |
HTTPS | Secure Hypertext Transfer Protocol | 安全超文本传输协议 |
JSON | JavaScript Object Notation | 一种轻量级的数据交换格式 |
spId | Service Provider Id | 我方提供的发送账号的唯一标识 |
平台采用预共享密钥的方式来进行鉴权认证。即我方和客户侧之间共享spKey密钥,客户侧在发起请求时对请求信息进行签名,然后把构造好的签名放入请求头中,我方在接收到请求信息时采用同样的方式对请求信息进行签名,并根据签名是否一致进行认证
构造公式如下:
signature_origin=${body_content} + ${timestamp}
构造参数说明如下:
将signature_origin字符串使用HMAC-SHA256算法(使用我方提供的预共享密钥spKey作为算法的key)计算签名HMAC值,然后对生成的HMAC值进行base64编码即生成最终传递的签名signatue
将生成的签名signatue放入请求头中,格式如下:
Authorization: HMAC-SHA256 ${timestamp},${signature}
构造参数说明如下:
支持客户侧单内容多号码的短信发送
参数名称 | 类型 | 是否必须 | 最大长度 | 说明 |
content | string | 必须 | 1005 | 短信内容,例如: 【线上线下】您的验证码为123456,在10分钟内有效。 |
mobile | string | 必须 | 由数量决定 | 短信号码,多个用“,”分割,号码数量<=10000 |
extCode | string | 可选 | 12 | 扩展码,必须可解析为数字 |
msgid | string | 可选 | 64 | 自定义msgid,若不传,由我们平台生成一个msgid返回,若设置此值,平台将使用此msgid作为此次提交的唯一编号并返回此msgid |
sId | string | 可选 | 64 | 批次号,可用于客户侧按照批次号对短信进行分组 |
sendTime | string | 可选 | 时间戳(long) | 定时时间的毫秒数,最长不超过30天,不传或者时间在5分钟之内都视为即时下发短信,立马发出去 |
{
"content" : "【线上线下】您的验证码为123456,在10分钟内有效。",
"mobile" : "13800001111,8613955556666,+8613545556666",
"extCode" : "123456",
"msgid":"8629637681836384963",
"sId" : "123456789abcdefg"
}
{
"status" : 0, // 请求成功,详细代码参考4.8.1
"msgid" : "8629637681836384963"
}
参数说明
平台将短信下发的状态报告推送给客户
参数名称 | 类型 | 是否必须 | 说明 |
status | int | 必须 | 提交响应状态 |
result | JSON Array | 必须 | 状态反馈信息(打包数组) |
result参数说明
参数名称 | 类型 | 是否必须 | 说明 |
phone | string | 必须 | 手机号码 |
msgid | string | 必须 | 与提交响应的msgid一致 |
status | string | 必须 | 短信状态 |
fee | int | 必须 | 短信计费条数 |
sId | string | 可选(提交时传才会有) | 批次号,可用于客户侧按照批次号对短信进行分组 |
donetime | string | 必须 | 短信到达时间,格式:yyyyMMddHHmmss |
{
"status" : 0, // 固定值0
"result" :
[
{
"phone" : "13921350591", // 手机号码
"msgid" : "-8629637681836384963", // 与提交中的msgid 一致
"status" : "DELIVRD" , // 短信状态,参考附表2
"donetime" : "20170816153922", // 短信到达时间
"fee" : 2, // 短信计费条数
"sId" : "123456789abcdefg" // 批次号
}
//这里是数组会有多条,默认最大500条 ,平台可配置
]
}
{
"status" : 0,
"msg" : ""
}
参数说明
避免重复推送,客户侧正确接收到后请返回成功码0。
平台支持客户主动来获取短信的状态报告,平台对状态报告最大缓存48小时,超过48小时未获取就会丢弃
参数名称 | 类型 | 是否必须 | 说明 |
maxSize | int | 可选 | 默认500,支持范围[10, 1000],参数超出范围按照默认算 |
{
"maxSize" : 100
}
见4.2.4
平台将短信回复的上行推送给客户
参数名称 | 类型 | 是否必须 | 说明 |
status | int | 必须 | 上行响应状态 |
result | JSON Array | 必须 | 上行回复内容(打包数组) |
result参数说明
参数名称 | 类型 | 是否必须 | 说明 |
phone | string | 必须 | 手机号码 |
extCode | string | 必须 | 用户提交短信时候带的extCode |
content | string | 必须 | 上行内容 |
receivetime | string | 必须 | 短信到达时间,格式:yyyyMMddHHmmss |
sId | string | 可选(提交时传才会有) | 对应短信提交时的sId |
sign | string | 可选 | 对应短信提交时的签名(无下发上行没有此字段) |
msgid | string | 必须 | 对应短信提交时的msgid |
{
"status" : 0, // 固定值0
"result":
[
{
"phone" : "13921350591", // 手机号码
"extCode" : "682", // 用户提交短信时候带的extCode
"content" : "上行回复内容" , // 上行内容
"receivetime" : "20170816153922",// 短信到达时间
"sId" : "123456789abcdefg", // 批次号
"sign" : "签名",
"msgid" : "-8629637681836384963" // 短信唯一编号
}
// 这里是数组会有多条,默认最大500条 ,平台可配置
]
}
{
"status" : 0, //状态码 0表示成功,非0表示失败
"msg" : "" // 错误信息
}
参数说明
避免重复推送,客户侧正确接收到后请返回成功码0。
平台支持客户主动来获取短信的上行报告,平台对上行报告最大缓存48小时,超过48小时未获取就会丢弃
参数名称 | 类型 | 是否必须 | 最大长度 | 说明 |
maxSize | int | 可选 | 默认500,支持范围[10, 1000],参数超出范围按照默认算 |
{
"maxSize" : 100
}
见4.4.4
预付费账号剩余余额查询。
推荐使用登陆 【客户发送平台】 在[用户中心/个人资料]模块中扫码绑定微信公众号,后期余额不足时会自动提醒推送。
无
无
{
"status": 0 // 请求成功,详细代码参考4.9.1
"result": 10000 //当前余额条数
}
获取发送账号spId的每日短信发送情况统计
参数名称 | 类型 | 是否必须 | 最大长度 | 说明 |
date | String | 必选 | 8 | 日期格式化:yyyyMMdd 示例: 20200101 |
{
"date": "20200101"
}
{
"status" : 0, // 请求成功,详细代码参考4.8.1
"result":
{
"spId" : "apiSendUser01", // 发送账号
"date" : 20200101, // 查询的日期
"sendCount" : 1000, // 提交条数
"feeCount" : 1500 , // 发送条数
"successCount" : 1400,// 成功条数
"failCount" : 80, // 失败条数
"exceptionCount" : 0, // 异常条数
"noRespCount" : 20, // 未反馈条数
}
}
status 代码 | 代码含义 |
0 | 成功 |
100001 | 鉴权失败 |
100002 | 必选参数为空 |
100003 | 参数格式错误 |
100004 | 系统错误 |
100005 | timestamp过期 |
1001 | 账号不能为空 |
1002 | 账号不合法 |
1003 | ip不能为空 |
1004 | ip校验失败 |
1005 | 密码不能为空 |
1006 | 密码错误 |
1007 | 手机号码不能为空或者手机号码位数不正确或者数量过多 |
1008 | 缺少手机参数 |
1009 | 扩展必须为数字 |
10010 | msgid长度限制 |
10011 | 内容为空 |
10012 | 内容含关键字 |
10013 | 内容超过系统支持最大长度 |
10014 | 签名超长或为空 |
10016 | 余额不足 |
10017 | 账号已禁用 |
10018 | 该用户此时间段禁止提交 |
10019 | 不允许主动获取和主动推送同时调用 |
10020 | 打包短信错误 |
10021 | 批量短信数量超限 |
10022 | 定时短信添加错误 |
10023 | sId过长 |
10024 | 定时短信时间异常 |
10025 | 国际短信接口不支持国内号码 |
10026 | 不支持国际短信 |
仅针对当前短信发送平台返回的内部反馈代码释义,不在此表中的代表运营商返回的状态码。
代码 | 含义 |
BLACK | 黑名单 |
NOWAY | 未匹配通道 |
CHECK | 审核不通过 |
SIGN | 签名错误,未报备 |
KEYWORD | 内容含有非法关键词 |
LIMIT | 超发限制 |
PHONERR | 无归属地 |
SWITCH | 通道切出 |
DELIVRD | 反馈成功 |
TDBLACK | 退订黑 |
TSBLACK | 投诉黑 |
RDBLACK | 红名单 |
AICHECK | 智能拦截,一般是发送账号有误 |
INSUFFICIENT_BALANCE | 余额不足 |
UNDELIV | 反馈失败 |
TESTER_DELIVRD | 压测专用码 |
ERRLMSG | CMPP长短信异常 |
支持客户侧单内容多号码的短信加密传输发送
使用AES对称加密,使用我方提供的预共享密钥spKey作为算法的key,发送时使用该秘钥对请求体进行加密
参数名称 | 类型 | 是否必须 | 最大长度 | 说明 |
content | string | 必须 | 请求体加密后的Base64字符串 |
public class CryptoUtil {
private static Logger logger = LoggerFactory.getLogger(CryptoUtil.class);
private final static int AES_KEY_LENGTH = 16;
private final static String ALGORITHM = "AES/ECB/PKCS5Padding";
/**
* AES 密钥长度规定为16位,这里判断用户名 长度不满足16位的统一前置填充字符'a',大于长度的统一截取前16位
*/
private static byte[] getBytesBySkey(String key) {
if (key.length() >= AES_KEY_LENGTH) {
return StrUtil.sub(key, 0, 16).getBytes(CharsetUtil.CHARSET_UTF_8);
}
return StrUtil.fill(key, 'a', AES_KEY_LENGTH, true).getBytes(CharsetUtil.CHARSET_UTF_8);
}
/**
* Aes加密
* @param encryptString 要加密的字符创
* @param key 秘钥
* @return 加密数据base64之后的字符串
* @throws Exception
*/
public static String encrypt(String encryptString, String key) throws Exception{
byte[] raw = getBytesBySkey(key);
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
byte[] encrypted = cipher.doFinal(encryptString.getBytes("utf-8"));
return Base64.encode(encrypted);
}
}
{
"content" : "【线上线下】您的验证码为123456,在10分钟内有效。",
"mobile" : "13800001111,8613955556666,+8613545556666",
"extCode" : "123456",
"sId" : "123456789abcdefg"
}
注意:构造请求头 signature_origin=${body_content} + ${timestamp} 中的 body_content使用加密后的json字符串!
//假设秘钥为 Z345_#$
{
"content":"eMHvnXPmLoJDRtAStEGHtRzNbsPzduN6XmM3F08b6EszuRlXdEw3FPSGpjVphixEeYh7LJkJpmP0OoaVI5kRXRTAT/Ny1a1Anzy2Ri6pgBpaYkzlHljPFBfgPNlGnDYSINebgUFr5wbgDvSGtcIoHdPoByb+srwaovZoose7q0Ms2pdn/kZOhym2hBNUta95EXfXDYKX4FjqFW2mENhFJEZUpsTSjfTpose6+ctt0NKocANGk14mk8qglHlyYebk"
}
{
"status" : 0, // 请求成功,详细代码参考4.8.1
"msgid" : "8629637681836384963"
}
https://github.com/wxxsxxGit/sms-sdk-java
Java SDK 基于Java8 版本开发,主要依赖三方组件有OkHttp/Hutool等,有Okhttp3 几个版本可供依赖选择,请移步GitHub 专区参考阅读Readme
添加SDK依赖
<dependency>
<groupId>cn.wxxsxx</groupId>
<artifactId>sms-sdk-java</artifactId>
<version>选择依赖的版本</version>
</dependency>
//参考Maven中央库 https://search.maven.org/artifact/cn.wxxsxx/sms-sdk-java
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.xsxx.sms.V4Client;
import com.xsxx.sms.model.BatchSubmitResp;
import com.xsxx.sms.model.DeliverResp;
import com.xsxx.sms.model.ReportResp;
import com.xsxx.sms.model.Sms;
import com.xsxx.sms.model.template.SmsSubimtByTemplateReqVo;
import com.xsxx.sms.model.template.SmsTemplate;
import com.xsxx.sms.util.AES;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 四代API提交接口
* https://thoughts.teambition.com/share/5f1f8ebc865e26001a7536b7
* 四代接口增加:
* 1. 加密验证
* 2. 防重放
* 3. 推送验证
*
* @author momo
* @date 2020-01-03 15:14:54
*/
public class DemoV4Client {
/**
* 并发线程数,取值范围(1-60)
* 理论速度 = 1000/ping * threadCount
* 默认10线程发送:理论速度500;
* 线程数选择需要考虑客户机性能,不是越大越好
* <p>
* 更高的性能可以 new 多个ApiClient 协同处理短消息
*/
private static final int httpWinSize = 20;
public static void main(String[] args) {
try {
// 具体【url/spId/spKey/fetchURL/templateURL】参数请找商务或者我司技术支持
String spId, url, spKey, fetchURL, templateURL;
V4Client v4Client = new V4Client(url, spId, spKey, httpWinSize, fetchURL, templateURL);
// 添加模板
// addTemplate(v4Client);
// 修改模板
// modifyTemplate(v4Client);
// 删除模板
// deleteTemplate(v4Client);
// 查询模板状态
// queryTemplateStatus(v4Client);
// 单模板发送
// submitByTemplateCode(v4Client);
// 批量模板发送
// batchSubmitByTemplateCode(v4Client);
// 单内容发送
// submit(v4Client);
// 单内容AES
// submitAES(v4Client);
// 多内容多号码发送 不推荐
// batchSms(v4Client);
//日统计查询
// v4Client.getDailyStats("20220811");
//余额查询
// v4Client.getBalance();
// 测速
// testSpeed(v4Client, 1_0000);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 单内容多号码发送
* 最多100个号码,每个手机号保证是11位数字有效手机号
* Sms sms = new Sms("11000000000","【签名】不带扩展码号");
* Sms sms = new Sms("11000000000,11000000000","【签名】带扩展码号","666");
* Sms sms = new Sms("11000000000,11000000000","【签名】带扩展码号,自定义字段","666","customId-whatever-except-emoji");
* apiClient.submit(sms);
*
* @param v4Client
*/
public static void submit(V4Client v4Client) {
Sms sms = new Sms("11000000000", "【线上线下submit SDK DEMO】验证码 " + System.currentTimeMillis() + ",5分钟内有效。如非本人操作,请忽略。", "666");
boolean isSync = v4Client.submit(sms, resp -> {
System.out.println(JSONUtil.toJsonStr(resp));
});
}
/**
* 报备模板
*
* @param v4Client
* @throws Exception
*/
public static void addTemplate(V4Client v4Client) throws Exception {
SmsTemplate smsTemplate = new SmsTemplate();
smsTemplate.setTemplateName("线上线下addTemplate SDK DEMO " + RandomUtil.randomString(10));
smsTemplate.setTemplateContent("线上线下addTemplate SDK DEMO template content ${" + RandomUtil.randomString(4) + "} template " + UUID.fastUUID().toString());
smsTemplate.setTemplateType(RandomUtil.randomInt(0, 3));
smsTemplate.setRemark("线上线下addTemplate SDK DEMO template " + DateTime.now().toString());
System.out.println("add params:\n" + JSONUtil.toJsonStr(smsTemplate));
boolean isSync = v4Client.addOrModifyTemplate(true, smsTemplate, resp -> {
System.out.println(JSONUtil.toJsonStr(resp));
});
}
/**
* 修改模板
*
* @param v4Client
* @throws Exception
*/
public static void modifyTemplate(V4Client v4Client) throws Exception {
SmsTemplate smsTemplate = new SmsTemplate();
smsTemplate.setTemplateName("线上线下modifyTemplate SDK DEMO " + RandomUtil.randomString(10));
smsTemplate.setTemplateContent("线上线下modifyTemplate SDK DEMO template content ${" + RandomUtil.randomString(4) + "} template ");
smsTemplate.setTemplateType(RandomUtil.randomInt(0, 3));
smsTemplate.setRemark("线上线下modifyTemplate SDK DEMO template " + DateTime.now().toString());
smsTemplate.setTemplateCode(-7476205602580885282L);
System.out.println("modify params:\n" + JSONUtil.toJsonStr(smsTemplate));
boolean isSync = v4Client.addOrModifyTemplate(false, smsTemplate, resp -> {
System.out.println(JSONUtil.toJsonStr(resp));
});
}
/**
* 删除模板
*
* @param v4Client
* @throws Exception
*/
public static void deleteTemplate(V4Client v4Client) throws Exception {
boolean isSync = v4Client.deleteTemplate(-7476205602580885282L, resp -> {
System.out.println(JSONUtil.toJsonStr(resp));
});
}
/**
* 查询模板状态
*
* @param v4Client
* @throws Exception
*/
public static void queryTemplateStatus(V4Client v4Client) throws Exception {
boolean isSync = v4Client.queryTemplateStatus(CollUtil.newArrayList(
-7481228446574445708L, -7479127829609579659L), resp -> {
System.out.println(JSONUtil.toJsonStr(resp));
});
}
/**
* 短信模板单条发送
*
* @param v4Client
* @throws Exception
*/
public static void submitByTemplateCode(V4Client v4Client) throws Exception {
SmsSubimtByTemplateReqVo smsSubimtByTemplateReqVo = new SmsSubimtByTemplateReqVo();
smsSubimtByTemplateReqVo.setSignName("线上线下submit by template SDK DEMO");
List<String> mobiles = new ArrayList<String>();
int count = RandomUtil.randomInt(4, 5);
for (int i = 0; i < count; i++) {
mobiles.add(RandomUtil.randomEle(CollUtil.newArrayList("", "86", "0086", "+86")) + RandomUtil.randomString("12", 1) + RandomUtil.randomString("0123456789", 10));
}
smsSubimtByTemplateReqVo.setMobile(CollUtil.join(mobiles, StrUtil.COMMA));
smsSubimtByTemplateReqVo.setExtCode(RandomUtil.randomNumbers(RandomUtil.randomInt(1, 12)));
smsSubimtByTemplateReqVo.setMsgid(UUID.randomUUID().toString(true));
smsSubimtByTemplateReqVo.setTemplateCode(-7479043716970054794L);
smsSubimtByTemplateReqVo.setParams(JSONUtil.toJsonStr(MapUtil.of("9ju9", RandomUtil.randomString(RandomUtil.randomInt(1, 12)))));
smsSubimtByTemplateReqVo.setsId(RandomUtil.randomString("0123456789", 5));
System.out.println("submit params: \n " + JSONUtil.toJsonStr(smsSubimtByTemplateReqVo));
boolean isSync = v4Client.submitByTemplateCode(smsSubimtByTemplateReqVo, resp -> {
System.out.println(JSONUtil.toJsonStr(resp));
});
}
/**
* 短信模板单条发送
*
* @param v4Client
* @throws Exception
*/
public static void batchSubmitByTemplateCode(V4Client v4Client) throws Exception {
List<SmsSubimtByTemplateReqVo> reqVoList = new ArrayList<>();
int count = RandomUtil.randomInt(4, 5);
for (int i = 0; i < count; i++) {
SmsSubimtByTemplateReqVo smsSubimtByTemplateReqVo = new SmsSubimtByTemplateReqVo();
smsSubimtByTemplateReqVo.setSignName("线上线下 batch submit by templateCode SDK DEMO");
smsSubimtByTemplateReqVo.setMobile(RandomUtil.randomEle(CollUtil.newArrayList("", "86", "0086", "+86")) + RandomUtil.randomString("12", 1) + RandomUtil.randomString("0123456789", 10));
smsSubimtByTemplateReqVo.setExtCode(RandomUtil.randomNumbers(RandomUtil.randomInt(1, 12)));
smsSubimtByTemplateReqVo.setMsgid(UUID.randomUUID().toString(true));
smsSubimtByTemplateReqVo.setTemplateCode(RandomUtil.randomEle(CollUtil.newArrayList(
-7479043716970054794L, -7479127829609579659L)));
Map<String, Object> params = new HashMap<>();
params.put("9ju9", RandomUtil.randomString(RandomUtil.randomInt(1, 12)));
params.put("f8xk", RandomUtil.randomString(RandomUtil.randomInt(1, 12)));
smsSubimtByTemplateReqVo.setParams(JSONUtil.toJsonStr(params));
smsSubimtByTemplateReqVo.setsId(RandomUtil.randomString("0123456789", 5));
reqVoList.add(smsSubimtByTemplateReqVo);
}
System.out.println("batch submit params: \n" + JSONUtil.toJsonStr(reqVoList));
boolean isSync = v4Client.batchSubmitByTemplateCode(reqVoList, resp -> {
System.out.println(JSONUtil.toJsonStr(resp));
});
}
/**
* 短信AES加密发送
*
* @param v4Client
* @throws Exception
*/
public static void submitAES(V4Client v4Client) throws Exception {
Sms sms = new Sms("13800000001", "【线上线下submit SDK DEMO】验证码 " + System.currentTimeMillis() + ",5分钟内有效。如非本人操作,请忽略。", "666");
boolean isSync = v4Client.submitByAes(sms, resp -> {
System.out.println(JSONUtil.toJsonStr(resp));
});
}
/**
* 速度测试,注意修改内容,否则会驳回
*
* @param v4Client
* @param amount 测试数量
*/
public static void testSpeed(V4Client v4Client, int amount) {
Long phone = 11000000000l;
long diff = 0;
// 同步计数
int syncCount = 0;
Long start = System.currentTimeMillis();
for (int i = 1; i <= amount; i++) {
if (i % 1000 == 0) {
diff = System.currentTimeMillis() - start;
System.out.println(i * 100 / amount + "%\t" + (i * 1000 / diff) + "条/秒");
}
phone++;
Sms sms = new Sms(phone.toString(), "【线上线下testSpeed SDK DEMO】带扩展码号" + i, String.valueOf(i), String.valueOf(i));
boolean isSync = v4Client.submit(sms, resp -> {
if (resp.getStatus() != 0) {
System.out.println(sms.getMobile() + "\t" + resp.getStatus() + "\t" + resp.getMsg());
}
});
if (isSync) {
syncCount++;
}
}
diff = System.currentTimeMillis() - start;
System.out.println("共" + diff + "秒\t " + (amount * 1000 / diff) + "条/秒");
System.out.println("同步占比:" + syncCount * 100 / amount + "%");
}
/**
* 主动获取未读短信状态
* 成功为DELIVRD,无特殊要求一次最多返回500条,可以用msgId来匹配返回的状态
* 可提供回调URL自动推送(推荐)
*
* @param v4Client
*/
@Deprecated
public static void getReport(V4Client v4Client) {
ReportResp report = v4Client.getReport();
System.out.println(JSONUtil.toJsonStr(report));
}
/**
* 主动获取未读上行
* 无特殊要求一次最多返回500条
* 可提供回调URL自动推送(推荐)
*
* @param v4Client
*/
@Deprecated
public static void getDeliver(V4Client v4Client) {
DeliverResp deliver = v4Client.getDeliver();
System.out.println(JSONUtil.toJsonStr(deliver));
}
/**
* 多内容打包发送接口(同步模式)最多100个内容,
* msgId和extCode可以为空
*/
@Deprecated
public static void batchSms(V4Client v4Client) {
//多内容打包数组,!!每个内容只支持1个手机号码
List<Sms> smsContents = new ArrayList<>();
Long phone = 18888888888L;
for (int i = 0; i < 100; i++) {
smsContents.add(new Sms(phone.toString(), "【线上线下batchSms SDK DEMO】内容不确定-V4 batchSms-" + i, "666", "sid-it's me"));
phone++;
}
BatchSubmitResp batchSubmitResp = v4Client.submit(smsContents);
System.out.println(JSONUtil.toJsonStr(batchSubmitResp));
}
}
https://github.com/wxxsxxGit/sms-sdk-go
GO SDK 基于Go 1.16 版本开发,主要依赖三方组件有color v1.13.0 / viper v1.13.0等 请移步GitHub 专区参考阅读Readme
进入下载的sms-sdk-go目录执行
go mod tidy
package main
import (
"bytes"
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"log"
"net/http"
"os"
"strconv"
"strings"
"time"
"v4sms/pkg/httputils"
"v4sms/pkg/strutils"
"v4sms/smsutils"
"github.com/fatih/color"
"github.com/spf13/viper"
)
var globalTemplateCode int64
var smsSigner *smsutils.SmsSigner
var logFile *os.File
// var smsSigner *smsutils.SmsSigner = smsutils.NewSmsSigner(spId, spKey, smsSendUrl, reportUrl, templateUrl)
func init() {
logFile, _ = os.Create("http_details.txt")
viper.SetConfigName("sms") // name of config file (without extension)
viper.SetConfigType("yaml") // REQUIRED if the config file does not have the extension in the name
viper.AddConfigPath("/etc") // call multiple times to add many search paths
viper.AddConfigPath(".") // path to look for the config file in
viper.AddConfigPath("./config/")
viper.AddConfigPath("../config/")
viper.AddConfigPath("../../config/")
viper.AddConfigPath("../../../config/")
if err := viper.ReadInConfig(); err != nil {
if _, ok := err.(viper.ConfigFileNotFoundError); ok {
fmt.Println(color.RedString("config file not found:" + err.Error()))
configPrompt()
os.Exit(111)
} else {
fmt.Println(color.RedString("config file found,can not use:" + err.Error()))
configPrompt()
os.Exit(112)
}
}
spId := viper.GetString("spId")
spKey := viper.GetString("spKey")
smsSendUrl := viper.GetString("smsSendUrl")
reportUrl := viper.GetString("reportUrl")
templateUrl := viper.GetString("templateUrl")
if len(spId) == 0 ||
len(spKey) == 0 ||
len(smsSendUrl) == 0 ||
len(reportUrl) == 0 ||
len(templateUrl) == 0 {
configPrompt()
os.Exit(113)
}
smsSigner = smsutils.NewSmsSigner(spId, spKey, smsSendUrl, reportUrl, templateUrl)
}
func main() {
//单条内容发送
log.Println("短信发送接口(单内容多号码)")
demoSingleSend()
sperator(1)
//单条内容加密发送
log.Println("短信加密发送接口")
demoSingleSecureSend()
sperator(1)
//多内容批量发送
log.Println("短信多发接口")
demoMultiSend()
sperator(1)
//主动获取状态报告
log.Println("状态报告主动获取")
demoStatusFetch()
sperator(1)
//主动获取上行
log.Println("上行主动获取")
demoUpstreamFetch()
sperator(1)
//查询余额
log.Println("预付费账号余额查询")
demoBalanceFetch()
sperator(1)
//查询每日发送统计
log.Println("获取发送账号spId的每日短信发送情况统计")
demoDailyStatsFetch()
sperator(1)
//模板报备
log.Println("模板报备")
templateId, err := demoTemplateAdd()
if err != nil {
fmt.Println(err.Error())
return
}
sperator(1)
//把服务端生成的templateCode设置为公共的值
globalTemplateCode = templateId
log.Println("模板报备的code为", globalTemplateCode)
log.Println("等待模板审核...")
//判断是否审核成功
var tempValue uint8
for {
m, err := demoTemplateStatus(globalTemplateCode)
if err != nil {
time.Sleep(5 * time.Second)
}
value, ok := m[globalTemplateCode]
if !ok {
fmt.Println("出现错误", globalTemplateCode, "不存在")
return
}
if value == 0 {
log.Println(globalTemplateCode, "联系管理员审核")
time.Sleep(10 * time.Second)
continue
} else {
tempValue = value
break
}
}
//审核成功提交模板短信
if tempValue == 1 {
log.Println(globalTemplateCode, "审核通过")
sperator(1)
} else if tempValue == 2 {
//审核失败修改模板,只有在模板审核失败时才可以修改模板
log.Println("模板审核失败,模板修改后提交")
err = demoTemplateModify(globalTemplateCode)
if err != nil {
fmt.Println(err.Error())
return
}
//模板被禁用
} else {
log.Println(globalTemplateCode, "审核状态为", tempValue, "退出")
}
//模板发送单条短信
log.Println("模板发送单条短信")
err = demoTemplateSendSms(globalTemplateCode)
if err != nil {
fmt.Println("demoTemplateSendSms", err.Error())
return
}
sperator(1)
//模板批量发送短信
log.Println("模板发送批量短信")
err = demoTemplateSendBatchSms(globalTemplateCode)
if err != nil {
fmt.Println("demoTemplateSendBatchSms", err.Error())
return
}
sperator(1)
//删除模板
log.Println("10秒后将删除模板", globalTemplateCode)
time.Sleep(10 * time.Second)
err = demoTemplateDelete(globalTemplateCode)
if err != nil {
fmt.Println("demoTemplateDelete", err.Error())
return
}
log.Println("删除模板", globalTemplateCode, "成功")
logFile.Close()
}
func demoSingleSend() {
requestBody := &smsutils.SingleSendRequestBody{
Content: "【线上线下】您的验证码为123456,在10分钟内有效。",
Mobile: "13800001111,13955556666,13545556666",
ExtCode: "123456",
SId: "123456789abcdefg"}
finalReqBody, err := json.Marshal(requestBody)
if err != nil {
fmt.Println("json.Marshal error", err.Error())
return
}
r, _ := http.NewRequest("POST", smsSigner.SingleSendUrl(), bytes.NewReader(finalReqBody))
smsSigner.Sign(r, finalReqBody)
client := http.DefaultClient
resp, err := client.Do(r)
if err != nil {
fmt.Println(err)
}
defer resp.Body.Close()
finalRespBody, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Println(err)
}
logFile.WriteString(httputils.CurlStyleOutput(r, resp, finalReqBody, finalRespBody))
}
func demoSingleSecureSend() {
requestBody := &smsutils.SingleSendRequestBody{
Content: "【线上线下】您的验证码为123456,在10分钟内有效。",
Mobile: "13800001111,13955556666,13545556666",
ExtCode: "123456",
SId: "123456789abcdefg"}
jsonByte, err := json.Marshal(requestBody)
if err != nil {
fmt.Println("requestBody json.Marshal error", err.Error())
return
}
bAfterEncrypt, err := smsutils.AesECBEncrypt([]byte(jsonByte), []byte(smsutils.NormalizeKey(smsSigner.SpKey)))
if err != nil {
fmt.Println(err.Error())
return
}
contentString := base64.StdEncoding.EncodeToString(bAfterEncrypt)
ssrb := &smsutils.SecureSendRequestBody{Content: contentString}
finalReqBody, err := json.Marshal(ssrb)
if err != nil {
fmt.Println("ssrb json.Marshal error", err.Error())
return
}
r, _ := http.NewRequest("POST", smsSigner.SingleSecureSendUrl(), bytes.NewReader(finalReqBody))
smsSigner.Sign(r, finalReqBody)
client := http.DefaultClient
resp, err := client.Do(r)
if err != nil {
fmt.Println(err)
}
defer resp.Body.Close()
finalRespBody, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Println(err)
}
logFile.WriteString(httputils.CurlStyleOutput(r, resp, finalReqBody, finalRespBody))
}
func demoMultiSend() {
smsBody1 := &smsutils.BatchSendItemReuqestBody{
Content: "【线上线下】线上线下欢迎你参观1",
Mobile: "13800001111,8613955556666,+8613545556666",
ExtCode: "123456",
MsgId: "123456787"}
smsBody2 := &smsutils.BatchSendItemReuqestBody{
Content: "【线上线下】线上线下欢迎你参观2",
Mobile: "13800001111,8613955556666,+8613545556666",
ExtCode: "123456",
MsgId: "123456788"}
smsBody3 := &smsutils.BatchSendItemReuqestBody{
Content: "【线上线下】线上线下欢迎你参观3",
Mobile: "13800001111,8613955556666,+8613545556666",
ExtCode: "123456",
MsgId: "123456789"}
requestBody := []*smsutils.BatchSendItemReuqestBody{}
requestBody = append(requestBody, smsBody1)
requestBody = append(requestBody, smsBody2)
requestBody = append(requestBody, smsBody3)
finalReqBody, err := json.Marshal(requestBody)
if err != nil {
fmt.Println("json.Marshal error", err.Error())
return
}
//fmt.Println(string(jsonByte))
r, _ := http.NewRequest("POST", smsSigner.MultiSendUrl(), bytes.NewReader(finalReqBody))
smsSigner.Sign(r, finalReqBody)
client := http.DefaultClient
resp, err := client.Do(r)
if err != nil {
fmt.Println(err)
}
defer resp.Body.Close()
finalRespBody, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Println(err)
}
logFile.WriteString(httputils.CurlStyleOutput(r, resp, finalReqBody, finalRespBody))
}
func demoStatusFetch() {
requestBody := &smsutils.ActiveFetchRequestBody{
MaxSize: 500,
}
finalReqBody, err := json.Marshal(requestBody)
if err != nil {
fmt.Println("json.Marshal error", err.Error())
return
}
r, _ := http.NewRequest("POST", smsSigner.StatusFetchUrl(), bytes.NewReader(finalReqBody))
smsSigner.Sign(r, finalReqBody)
client := http.DefaultClient
resp, err := client.Do(r)
if err != nil {
fmt.Println(err)
}
defer resp.Body.Close()
finalRespBody, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Println(err)
}
logFile.WriteString(httputils.CurlStyleOutput(r, resp, finalReqBody, finalRespBody))
}
func demoUpstreamFetch() {
requestBody := &smsutils.ActiveFetchRequestBody{
MaxSize: 500,
}
finalReqBody, err := json.Marshal(requestBody)
if err != nil {
fmt.Println("json.Marshal error", err.Error())
return
}
r, _ := http.NewRequest("POST", smsSigner.UpstreamFetchUrl(), bytes.NewReader(finalReqBody))
smsSigner.Sign(r, finalReqBody)
client := http.DefaultClient
resp, err := client.Do(r)
if err != nil {
fmt.Println(err)
}
defer resp.Body.Close()
finalRespBody, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Println(err)
}
logFile.WriteString(httputils.CurlStyleOutput(r, resp, finalReqBody, finalRespBody))
}
func demoBalanceFetch() {
r, _ := http.NewRequest("POST", smsSigner.BalanceFetchUrl(), nil)
smsSigner.Sign(r, nil)
client := http.DefaultClient
resp, err := client.Do(r)
if err != nil {
fmt.Println(err)
}
defer resp.Body.Close()
finalRespBody, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Println(err)
}
logFile.WriteString(httputils.CurlStyleOutput(r, resp, nil, finalRespBody))
}
func demoDailyStatsFetch() {
requestBody := &smsutils.DailyStatsRequestBody{
Date: time.Now().Format("20060102"),
}
finalReqBody, err := json.Marshal(requestBody)
if err != nil {
fmt.Println("json.Marshal error", err.Error())
return
}
r, _ := http.NewRequest("POST", smsSigner.DailyStatsUrl(), bytes.NewReader(finalReqBody))
smsSigner.Sign(r, finalReqBody)
client := http.DefaultClient
resp, err := client.Do(r)
if err != nil {
fmt.Println(err)
}
defer resp.Body.Close()
finalRespBody, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Println(err)
}
logFile.WriteString(httputils.CurlStyleOutput(r, resp, finalReqBody, finalRespBody))
}
//模板报备http请求demo
func demoTemplateAdd() (int64, error) {
requestBody := &smsutils.TemplateAddRequestBody{
TemplateName: "线上线下addTemplate SDK DEMO " + strutils.RandString(10),
TemplateType: 2,
TemplateContent: "线上线下addTemplate SDK DEMO template content ${code} template " + strutils.RandString(20),
Remark: "线上线下addTemplate SDK DEMO template " + time.Now().Format("2006-01-02 15:04:05"),
}
finalReqBody, err := json.Marshal(requestBody)
if err != nil {
return 0, err
}
r, _ := http.NewRequest("POST", smsSigner.TemplateAddUrl(), bytes.NewReader(finalReqBody))
smsSigner.Sign(r, finalReqBody)
client := http.DefaultClient
resp, err := client.Do(r)
if err != nil {
return 0, err
}
defer resp.Body.Close()
finalRespBody, err := ioutil.ReadAll(resp.Body)
if err != nil {
return 0, err
}
logFile.WriteString(httputils.CurlStyleOutput(r, resp, finalReqBody, finalRespBody))
tarb := &smsutils.TemplateAddRespBody{}
err = json.Unmarshal(finalRespBody, &tarb)
if err != nil {
return 0, err
}
return tarb.TemplateCode, nil
}
//模板修改 http请求demo
func demoTemplateModify(templateCode int64) error {
requestBody := &smsutils.TemplateModifyRequestBody{
TemplateCode: templateCode,
TemplateType: 2,
TemplateName: "线上线下addTemplate SDK DEMO " + strutils.RandString(10),
TemplateContent: "线上线下addTemplate SDK DEMO template content ${code} modify template " + strutils.RandString(20),
Remark: "线上线下addTemplate SDK DEMO template " + time.Now().Format("2006-01-02 15:04:05"),
}
finalReqBody, err := json.Marshal(requestBody)
if err != nil {
return err
}
r, _ := http.NewRequest("POST", smsSigner.TemplateModifyUrl(), bytes.NewReader(finalReqBody))
smsSigner.Sign(r, finalReqBody)
client := http.DefaultClient
resp, err := client.Do(r)
if err != nil {
return err
}
defer resp.Body.Close()
finalRespBody, err := ioutil.ReadAll(resp.Body)
if err != nil {
return err
}
logFile.WriteString(httputils.CurlStyleOutput(r, resp, finalReqBody, finalRespBody))
tmrb := &smsutils.TemplateModifyRespBody{}
err = json.Unmarshal(finalRespBody, &tmrb)
if err != nil {
return err
}
if tmrb.Status != 0 {
return errors.New("status为" + strconv.FormatInt(int64(tmrb.Status), 10) + ",msg为" + tmrb.Msg)
}
return nil
}
//模板查询 http请求demo
func demoTemplateStatus(templateCodes ...int64) (map[int64]uint8, error) {
tSlice := []string{}
for _, v := range templateCodes {
tSlice = append(tSlice, strconv.FormatInt(v, 10))
}
requestBody := &smsutils.TemplateStatusRequestBody{
TemplateCodes: strings.Join(tSlice, ","),
}
finalReqBody, err := json.Marshal(requestBody)
if err != nil {
return nil, err
}
r, _ := http.NewRequest("POST", smsSigner.TemplateStatusUrl(), bytes.NewReader(finalReqBody))
smsSigner.Sign(r, finalReqBody)
client := http.DefaultClient
resp, err := client.Do(r)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return nil, errors.New("http状态码为" + strconv.FormatInt(int64(resp.StatusCode), 10))
}
finalRespBody, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
logFile.WriteString(httputils.CurlStyleOutput(r, resp, finalReqBody, finalRespBody))
tsrb := &smsutils.TemplateStatusRespBody{}
err = json.Unmarshal(finalRespBody, &tsrb)
if err != nil {
return nil, err
}
if tsrb.Status != 0 {
return nil, errors.New("status为" + strconv.FormatInt(int64(tsrb.Status), 10) + ",msg为" + tsrb.Msg)
}
m := make(map[int64]uint8)
for _, v := range tsrb.TemplateList {
m[v.TemplateCode] = v.AuditStatus
}
return m, nil
}
//模板删除 请求demo
func demoTemplateDelete(templateCode int64) error {
requestBody := &smsutils.TemplateDeleteRequestBody{
TemplateCode: templateCode,
}
finalReqBody, err := json.Marshal(requestBody)
if err != nil {
return err
}
r, _ := http.NewRequest("POST", smsSigner.TemplateDeleteUrl(), bytes.NewReader(finalReqBody))
smsSigner.Sign(r, finalReqBody)
client := http.DefaultClient
resp, err := client.Do(r)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return errors.New("http状态码为" + strconv.FormatInt(int64(resp.StatusCode), 10))
}
finalRespBody, err := ioutil.ReadAll(resp.Body)
if err != nil {
return err
}
logFile.WriteString(httputils.CurlStyleOutput(r, resp, finalReqBody, finalRespBody))
tdrb := &smsutils.TemplateDeleteRespBody{}
err = json.Unmarshal(finalRespBody, &tdrb)
if err != nil {
return err
}
if tdrb.Status != 0 {
return errors.New("status为" + strconv.FormatInt(int64(tdrb.Status), 10) + ",msg为" + tdrb.Msg)
}
return nil
}
//模板单条发送 请求demo
func demoTemplateSendSms(templateCode int64) error {
params := make(map[string]string)
params["code"] = "普通的一个"
paramsByte, err := json.Marshal(params)
if err != nil {
return err
}
requestBody := &smsutils.TemplateSendSmsRequestItem{
SignName: "模板测试",
TemplateCode: templateCode,
Params: string(paramsByte),
Mobile: "18799991367,12899190876,13914117531",
}
finalReqBody, err := json.Marshal(requestBody)
if err != nil {
return err
}
r, _ := http.NewRequest("POST", smsSigner.TemplateSendSmsUrl(), bytes.NewReader(finalReqBody))
smsSigner.Sign(r, finalReqBody)
client := http.DefaultClient
resp, err := client.Do(r)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return errors.New("http状态码为" + strconv.FormatInt(int64(resp.StatusCode), 10))
}
finalRespBody, err := ioutil.ReadAll(resp.Body)
if err != nil {
return err
}
logFile.WriteString(httputils.CurlStyleOutput(r, resp, finalReqBody, finalRespBody))
tdrb := &smsutils.TemplateSendSmsRespBody{}
err = json.Unmarshal(finalRespBody, &tdrb)
if err != nil {
return err
}
if tdrb.Status != 0 {
return errors.New("status为" + strconv.FormatInt(int64(tdrb.Status), 10) + ",msg为" + tdrb.Msg)
}
fmt.Println("成功号码为", tdrb.SuccessList)
fmt.Println("失败号码为", tdrb.FailList)
fmt.Println("短信分片为", tdrb.SplitCount)
fmt.Println("msgid为", tdrb.Msgid)
return nil
}
//模板批量发送 请求demo
func demoTemplateSendBatchSms(templateCode int64) error {
params := make(map[string]string)
params["code"] = "普通的一个"
paramsByte, err := json.Marshal(params)
if err != nil {
return err
}
item1 := &smsutils.TemplateSendSmsRequestItem{
SignName: "模板测试1",
TemplateCode: templateCode,
Params: string(paramsByte),
Mobile: "18505101387",
}
item2 := &smsutils.TemplateSendSmsRequestItem{
SignName: "模板测试2",
TemplateCode: templateCode,
Params: string(paramsByte),
Mobile: "12899190872",
}
item3 := &smsutils.TemplateSendSmsRequestItem{
SignName: "模板测试3",
TemplateCode: templateCode,
Params: string(paramsByte),
Mobile: "18799991362",
}
item4 := &smsutils.TemplateSendSmsRequestItem{
SignName: "模板测试4",
TemplateCode: templateCode,
Params: string(paramsByte),
Mobile: "13914117532",
}
item5 := &smsutils.TemplateSendSmsRequestItem{
SignName: "模板测试5",
TemplateCode: templateCode,
Params: string(paramsByte),
Mobile: "1895606996",
}
requestBody := &smsutils.TemplateSendBatchSmsRequestBody{
item1,
item2,
item3,
item4,
item5,
}
finalReqBody, err := json.Marshal(requestBody)
if err != nil {
return err
}
r, _ := http.NewRequest("POST", smsSigner.TemplateSendBatchSmsUrl(), bytes.NewReader(finalReqBody))
smsSigner.Sign(r, finalReqBody)
client := http.DefaultClient
resp, err := client.Do(r)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return errors.New("http状态码为" + strconv.FormatInt(int64(resp.StatusCode), 10))
}
finalRespBody, err := ioutil.ReadAll(resp.Body)
if err != nil {
return err
}
logFile.WriteString(httputils.CurlStyleOutput(r, resp, finalReqBody, finalRespBody))
tdrb := &smsutils.TemplateSendBatchSmsRespBody{}
err = json.Unmarshal(finalRespBody, &tdrb)
if err != nil {
return err
}
if tdrb.Status != 0 {
return errors.New("status为" + strconv.FormatInt(int64(tdrb.Status), 10) + ",msg为" + tdrb.Msg)
}
for _, v := range tdrb.Result {
fmt.Println(v)
}
return nil
}
func sperator(sec int) {
// fmt.Println(strings.Repeat("*", 30) + "\n")
fmt.Printf("\n")
time.Sleep(time.Duration(sec) * time.Second)
}
func configPrompt() {
log.Println("配置文件默认为/etc/sms.yaml\n" +
"需要5个配置项spId,spKey,smsSendUrl,reportUrl,templateUrl联系管理员获取")
}
https://github.com/wxxsxxGit/sms-sdk-python
Python3 SDK 基于python3.10.7 版本开发,主要依赖三方组件, 具体请移步GitHub 专区参考阅读Readme
详见https://github.com/wxxsxxGit/sms-sdk-python#操作步骤
# -*- coding: utf-8 -*-
from wxxsxx.pkg.httputils import CurlStyleOutput
from wxxsxx.pkg.strutils import NormalizeKey,RandString
from wxxsxx.pkg.encrypt import AesECBEncrypt,HmacSha256AndBase64
from wxxsxx.smsutils.signer import SmsSigner
import yaml
import requests
import json
import time
import logging
import datetime
import os
with open("config/sms.yaml", encoding='utf-8') as fr:
config = yaml.safe_load(fr)
smsSigner = SmsSigner(config["spId"],config["spKey"],config["smsSendUrl"],config["reportUrl"],config["templateUrl"])
fileToWrite = open("http_details.txt",'w',encoding=("utf-8"))
def demoSingleSend():
requestBody = {
"content": "【线上线下】您的验证码为123456,在10分钟内有效。",
"mobile": "13800001111,13955556666,13545556666",
"extCode": "123456",
"sId": "123456789abcdefg"}
finalReqBody = json.dumps(requestBody,ensure_ascii=False)
headers = smsSigner.Sign(finalReqBody)
resp = requests.post(smsSigner.SingleSendUrl(),headers=headers,data=finalReqBody.encode("utf-8"),timeout=3)
output = CurlStyleOutput(headers,finalReqBody,resp)
fileToWrite.write(output)
if resp.status_code != 200:
logging.info("demoSingleSend()状态码为"+resp.status_code)
return
respDict = json.loads(resp._content)
if respDict["status"] != 0:
logging.info("status状态为"+str(respDict["status"]))
def demoSingleSecureSend():
requestBody = {
"content": "【线上线下】您的验证码为123456,在10分钟内有效。我当然没问题",
"mobile": "13800001111",
"extCode": "123456",
"sId": "123456789abcdefg"}
tempReqBody = json.dumps(requestBody,ensure_ascii=False)
# tempReqBody = json.dumps(requestBody,ensure_ascii=False)
tempReqBody = '''{"content":"【线上线下】您的验证码为123456,在10分钟内有效。","mobile":"13800001111,13955556666,13545556666","extCode":"123456","sId":"123456789abcdefg"}'''
contentString = AesECBEncrypt(tempReqBody, NormalizeKey(smsSigner.SpKey))
secondReqBody = {"content":contentString}
finalReqBody = json.dumps(secondReqBody,ensure_ascii=False)
headers = smsSigner.Sign(finalReqBody)
resp = requests.post(smsSigner.SingleSecureSendUrl(),headers=headers,data=finalReqBody.encode("utf-8"),timeout=3)
output = CurlStyleOutput(headers,finalReqBody,resp)
fileToWrite.write(output)
if resp.status_code != 200:
logging.info("demoSingleSecureSend()状态码为"+resp.status_code)
return
respDict = json.loads(resp._content)
if respDict["status"] != 0:
logging.info("status状态为"+str(respDict["status"]))
def demoMultiSend():
smsBody1 = {
"content": "【线上线下】线上线下欢迎你参观1",
"mobile": "13800001111,8613955556666,+8613545556666",
"extCode": "123456",
"msgId": "123456787"}
smsBody2 = {
"content": "【线上线下】线上线下欢迎你参观2",
"mobile": "13800001111,8613955556666,+8613545556666",
"extCode": "123456",
"msgId": "123456787"}
smsBody3 = {
"content": "【线上线下】线上线下欢迎你参观3",
"mobile": "13800001111,8613955556666,+8613545556666",
"extCode": "123456",
"msgId": "123456787"}
requestBody = [smsBody1, smsBody2,smsBody3]
finalReqBody = json.dumps(requestBody,ensure_ascii=False)
headers = smsSigner.Sign(finalReqBody)
resp = requests.post(smsSigner.MultiSendUrl(),headers=headers,data=finalReqBody.encode("utf-8"),timeout=3)
output = CurlStyleOutput(headers,finalReqBody,resp)
fileToWrite.write(output)
if resp.status_code != 200:
logging.info("demoMultiSend()状态码为"+resp.status_code)
return
respDict = json.loads(resp._content)
if respDict["status"] != 0:
logging.info("status状态为"+str(respDict["status"]))
def demoStatusFetch():
requestBody = {
"maxSize": 500,
}
finalReqBody = json.dumps(requestBody,ensure_ascii=False)
headers = smsSigner.Sign(finalReqBody)
resp = requests.post(smsSigner.StatusFetchUrl(),headers=headers,data=finalReqBody.encode("utf-8"),timeout=3)
output = CurlStyleOutput(headers,finalReqBody,resp)
fileToWrite.write(output)
if resp.status_code != 200:
logging.info("demoStatusFetch()状态码为"+resp.status_code)
return
respDict = json.loads(resp._content)
if respDict["status"] != 0:
logging.info("status状态为"+str(respDict["status"]))
def demoUpstreamFetch():
requestBody = {
"maxSize": 500,
}
finalReqBody = json.dumps(requestBody,ensure_ascii=False)
headers = smsSigner.Sign(finalReqBody)
resp = requests.post(smsSigner.UpstreamFetchUrl(),headers=headers,data=finalReqBody.encode("utf-8"),timeout=3)
output = CurlStyleOutput(headers,finalReqBody,resp)
fileToWrite.write(output)
if resp.status_code != 200:
logging.info("demoUpstreamFetch()状态码为"+resp.status_code)
return
respDict = json.loads(resp._content)
if respDict["status"] != 0:
logging.info("status状态为"+str(respDict["status"]))
def demoBalanceFetch():
headers = smsSigner.Sign("")
resp = requests.post(smsSigner.StatusFetchUrl(),headers=headers,timeout=3)
output = CurlStyleOutput(headers,"",resp)
fileToWrite.write(output)
if resp.status_code != 200:
logging.info("demoBalanceFetch()状态码为"+resp.status_code)
return
respDict = json.loads(resp._content)
if respDict["status"] != 0:
logging.info("status状态为"+str(respDict["status"]))
def demoDailyStatsFetch():
requestBody = {
"date": datetime.datetime.now().strftime("%Y%m%d"),
}
finalReqBody = json.dumps(requestBody,ensure_ascii=False)
headers = smsSigner.Sign(finalReqBody)
resp = requests.post(smsSigner.DailyStatsUrl(),headers=headers,data=finalReqBody.encode("utf-8"),timeout=3)
output = CurlStyleOutput(headers,finalReqBody,resp)
fileToWrite.write(output)
if resp.status_code != 200:
logging.info("demoDailyStatsFetch状态码为"+resp.status_code)
return
respDict = json.loads(resp._content)
if respDict["status"] != 0:
logging.info("status状态为"+str(respDict["status"]))
def demoTemplateAdd():
requestBody = {
"templateName": "线上线下addTemplate SDK DEMO " + RandString(10),
"templateType": 2,
"templateContent": "线上线下addTemplate SDK DEMO template content ${code} template " + RandString(20),
"remark": "线上线下addTemplate SDK DEMO template " + datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
}
finalReqBody = json.dumps(requestBody,ensure_ascii=False)
headers = smsSigner.Sign(finalReqBody)
resp = requests.post(smsSigner.TemplateAddUrl(),headers=headers,data=finalReqBody.encode("utf-8"),timeout=3)
output = CurlStyleOutput(headers,finalReqBody,resp)
fileToWrite.write(output)
if resp.status_code != 200:
logging.info("demoTemplateAdd状态码为"+resp.status_code)
return
respDict = json.loads(resp._content)
if respDict["status"] != 0:
logging.info("status状态为"+str(respDict["status"]))
else:
return respDict["templateCode"]
def demoTemplateModify(templateCode):
requestBody = {
"templateCode": templateCode,
"templateType": 2,
"templateName": "线上线下addTemplate SDK DEMO " + RandString(10),
"templateContent": "线上线下addTemplate SDK DEMO template content ${code} modify template " + RandString(20),
"remark": "线上线下addTemplate SDK DEMO template " + datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
}
finalReqBody = json.dumps(requestBody,ensure_ascii=False)
headers = smsSigner.Sign(finalReqBody)
resp = requests.post(smsSigner.TemplateModifyUrl(),headers=headers,data=finalReqBody.encode("utf-8"),timeout=3)
output = CurlStyleOutput(headers,finalReqBody,resp)
fileToWrite.write(output)
if resp.status_code != 200:
logging.info("demoTemplateModify状态码为"+resp.status_code)
return
respDict = json.loads(resp._content)
if respDict["status"] != 0:
logging.info("status状态为"+str(respDict["status"]))
return respDict["status"]
logging.info("模板修改成功")
#返回状态0,1,2,3 0是没审核 1审核通过 2审核失败 3禁用
def demoTemplateStatus(templateCode):
tList = [str(templateCode)]
requestBody = {
"templateCodes": ",".join(tList),
}
finalReqBody = json.dumps(requestBody,ensure_ascii=False)
headers = smsSigner.Sign(finalReqBody)
resp = requests.post(smsSigner.TemplateStatusUrl(),headers=headers,data=finalReqBody.encode("utf-8"),timeout=3)
output = CurlStyleOutput(headers,finalReqBody,resp)
fileToWrite.write(output)
if resp.status_code != 200:
logging.info("demoTemplateStatus状态码为"+resp.status_code)
return
respDict = json.loads(resp._content)
if respDict["status"] != 0:
logging.info("status状态为"+str(respDict["status"]))
return 0
for v in respDict["templateList"]:
if v["templateCode"] == templateCode and v["status"] == 0:
return v["auditStatus"]
return 0
def demoTemplateDelete(templateCode):
requestBody = {
"templateCode": templateCode,
}
finalReqBody = json.dumps(requestBody,ensure_ascii=False)
headers = smsSigner.Sign(finalReqBody)
resp = requests.post(smsSigner.TemplateDeleteUrl(),headers=headers,data=finalReqBody.encode("utf-8"),timeout=3)
output = CurlStyleOutput(headers,finalReqBody,resp)
fileToWrite.write(output)
if resp.status_code != 200:
logging.info("demoTemplateDelete状态码为"+resp.status_code)
return False
respDict = json.loads(resp._content)
if respDict["status"] != 0:
logging.info("status状态为"+str(respDict["status"]))
return False
else:
return True
def demoTemplateSendSms(templateCode):
params = {
"code": "本届预选赛"
}
paramsString = json.dumps(params)
requestBody = {
"signName": "模板测试",
"templateCode": templateCode,
"params": paramsString,
"mobile": "18799991367,12899190876,13914117531",
}
finalReqBody = json.dumps(requestBody,ensure_ascii=False)
headers = smsSigner.Sign(finalReqBody)
resp = requests.post(smsSigner.TemplateSendSmsUrl(),headers=headers,data=finalReqBody.encode("utf-8"),timeout=3)
output = CurlStyleOutput(headers,finalReqBody,resp)
fileToWrite.write(output)
if resp.status_code != 200:
logging.info("demoTemplateSendSms()状态码为"+resp.status_code)
return False
respDict = json.loads(resp._content)
if respDict["status"] != 0:
logging.info("status状态为"+str(respDict["status"]))
return False
else:
return True
def demoTemplateSendBatchSms(templateCode):
params = {
"code": "本届预选赛"
}
paramsString = json.dumps(params)
item1 = {
"signName": "模板测试1",
"templateCode": templateCode,
"params": paramsString,
"mobile": "18505101387",
}
item2 = {
"signName": "模板测试2",
"templateCode": templateCode,
"params": paramsString,
"mobile": "12899190872",
}
item3 = {
"signName": "模板测试3",
"templateCode": templateCode,
"params": paramsString,
"mobile": "18799991362",
}
item4 = {
"signName": "模板测试4",
"templateCode": templateCode,
"params": paramsString,
"mobile": "13914117532",
}
item5 = {
"signName": "模板测试5",
"templateCode": templateCode,
"params": paramsString,
"mobile": "1895606996",
}
requestBody = [item1,item2,item3,item4,item5]
finalReqBody = json.dumps(requestBody,ensure_ascii=False)
headers = smsSigner.Sign(finalReqBody)
resp = requests.post(smsSigner.TemplateSendBatchSmsUrl(),headers=headers,data=finalReqBody.encode("utf-8"),timeout=3)
output = CurlStyleOutput(headers,finalReqBody,resp)
fileToWrite.write(output)
if resp.status_code != 200:
logging.info("demoTemplateSendSms()状态码为"+resp.status_code)
return False
respDict = json.loads(resp._content)
if respDict["status"] != 0:
logging.info("status状态为"+str(respDict["status"]))
return False
else:
return True
def sperator(sec):
print("\n")
time.sleep(sec)
def configPrompt():
logging.info("配置文件默认为/etc/sms.yaml\n" +
"需要5个配置项spId,spKey,smsSendUrl,reportUrl,templateUrl联系管理员获取")
if __name__ == "__main__":
globalTemplateCode = ""
console_fmt = "%(asctime)s--->%(message)s"
logging.basicConfig(level="INFO",format=console_fmt)
logging.info("短信发送接口(单内容多号码)")
demoSingleSend()
sperator(1)
#单条内容加密发送
logging.info("短信加密发送接口")
demoSingleSecureSend()
sperator(1)
# #多内容批量发送
logging.info("短信多发接口")
demoMultiSend()
sperator(1)
# #主动获取状态报告
logging.info("状态报告主动获取")
demoStatusFetch()
sperator(1)
# #主动获取上行
logging.info("上行主动获取")
demoUpstreamFetch()
sperator(1)
# #查询余额
logging.info("预付费账号余额查询")
demoBalanceFetch()
sperator(1)
# #查询每日发送统计
logging.info("获取发送账号spId的每日短信发送情况统计")
demoDailyStatsFetch()
sperator(1)
# #模板报备
logging.info("模板报备")
globalTemplateCode = demoTemplateAdd()
print("模板报备的code为{0}".format(globalTemplateCode))
sperator(1)
logging.info("等待模板审核...")
finalAuditCode = 0
while True:
auditCode = demoTemplateStatus(globalTemplateCode)
if auditCode != 0:
finalAuditCode = auditCode
break
logging.info("模板还未审核,请联系管理员审核...")
time.sleep(10)
if finalAuditCode == 2:
logging.info("模板审核未通过,尝试重新提交模板")
demoTemplateModify(globalTemplateCode)
os._exit(11)
elif finalAuditCode == 3:
logging.info("模板被禁用")
os._exit(12)
else:
logging.info("模板审核通过")
sperator(1)
#模板发送单条短信
logging.info("模板发送单条短信")
demoTemplateSendSms(globalTemplateCode)
sperator(1)
#模板批量发送短信
logging.info("模板发送批量短信")
demoTemplateSendBatchSms(globalTemplateCode)
sperator(1)
logging.info("10秒后将删除模板{0:d}".format(globalTemplateCode))
time.sleep(10)
deleteResult = demoTemplateDelete(globalTemplateCode)
if deleteResult:
logging.info("删除模板{0:d}成功".format(globalTemplateCode))
fileToWrite.close()
sperator(1)
logging.info("http请求内容保存在http_details.txt文件中")