添加企业微信
咨询电话: 4008-753-365
投诉电话: 18021097332

咨询时间与投诉时间为9:00-18:00
资源中心 /Java 调用 SUBMAIL SMS API 发送国际短信
          
2026-06-22 07:35:26
  2
  1

Java 调用 SUBMAIL SMS API 发送国际短信


一、为什么选择 SUBMAIL?

相比自建短信网关或使用 Twilio 等海外平台,SUBMAIL 对中国企业开发者有明显优势:

  • 国内 + 国际一站式覆盖:通过 sms/unionsend单接口自动按国家路由
  • REST API 设计简洁:标准 HTTP POST,Java 无需官方 SDK 也可轻松接入
  • 覆盖全球 200+ 国家和地区,送达率业界领先
  • 支持 normal / MD5 / SHA1 / SHA256 多种认证方式,安全灵活


二、前期准备

1. 注册账号并创建应用

前往 imgmysubmail.com 注册,进入控制台「应用集成」→「国际短信」,创建应用,获取:

  • AppID:应用唯一标识
  • AppKey:应用密钥(请妥善保管,切勿提交至代码仓库)

2. 确认账户余额

国际短信按条计费,发送前请在控制台确认余额充足。



三、环境依赖

JDK 版本要求:JDK 1.8 及以上

使用 OkHttp3 作为 HTTP 客户端,Gson 解析 JSON 响应。

Maven 依赖(pom.xml):

<dependencies>
    <!-- HTTP 客户端 -->
    <dependency>
        <groupId>com.squareup.okhttp3</groupId>
        <artifactId>okhttp</artifactId>
        <version>4.12.0</version>
    </dependency>
    <!-- JSON 解析 -->
    <dependency>
        <groupId>com.google.code.gson</groupId>
        <artifactId>gson</artifactId>
        <version>2.10.1</version>
    </dependency>
</dependencies>

Gradle 依赖(build.gradle):

implementation 'com.squareup.okhttp3:okhttp:4.12.0'
implementation 'com.google.code.gson:gson:2.10.1'


四、核心 API 说明

  • 接口地址https://api-v4.mysubmail.com/internationalsms/send
  • 请求方式:HTTP POST
  • Content-Typeapplication/x-www-form-urlencoded

主要请求参数:

  • appid(必需):国际短信应用 AppID
  • signature(必需):normal 模式填 AppKey 明文;md5 模式填签名字符串
  • to(必需):收件人手机号,必须携带国际区号,格式如 +1xxxxxxxxxx(美国)、+44xxxxxxxxxx(英国)、+81xxxxxxxxxx(日本)
  • content(必需):短信正文
  • sign_type(可选):认证类型,normal(测试用)或 md5(生产推荐)


五、完整 Java 示例代码

方式一:normal 明文认证(快速测试)

import okhttp3.*;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import java.io.IOException;
public class SubmailSmsSender {
    // 从环境变量读取,避免硬编码密钥
    private static final String APPID  = System.getenv("SUBMAIL_APPID");
    private static final String APPKEY = System.getenv("SUBMAIL_APPKEY");
    private static final String API_URL =
        "https://api-v4.mysubmail.com/internationalsms/send";
    private final OkHttpClient client = new OkHttpClient();
    /**
     * 发送国际短信(normal 明文认证模式)
     *
     * @param to      收件人号码,需带国际区号,如 +1xxxxxxxxxx
     * @param content 短信正文
     * @return API 响应的 JsonObject
     */
    public JsonObject sendSms(String to, String content) throws IOException {
        // 构建请求体
        RequestBody body = new FormBody.Builder()
            .add("appid",     APPID)
            .add("signature", APPKEY)   // normal 模式直接传 AppKey
            .add("to",        to)
            .add("content",   content)
            .add("sign_type", "normal")
            .build();
        Request request = new Request.Builder()
            .url(API_URL)
            .post(body)
            .build();
        // 发送请求并解析响应
        try (Response response = client.newCall(request).execute()) {
            if (!response.isSuccessful()) {
                throw new IOException("HTTP 请求失败,状态码:" + response.code());
            }
            String responseBody = response.body().string();
            return JsonParser.parseString(responseBody).getAsJsonObject();
        }
    }
    /**
     * 统一处理 API 响应
     */
    public static void handleResponse(JsonObject result) {
        String status = result.get("status").getAsString();
        if ("success".equals(status)) {
            System.out.println("✅ 发送成功!");
            System.out.println("   send_id: " + result.get("send_id").getAsString());
            System.out.println("   消耗额度: " + result.get("fee").getAsInt());
            System.out.println("   剩余额度: " + result.get("sms_credits").getAsInt());
        } else {
            System.out.println("❌ 发送失败!");
            System.out.println("   错误码: " + result.get("code").getAsString());
            System.out.println("   原因:   " + result.get("msg").getAsString());
        }
    }
    public static void main(String[] args) throws IOException {
        SubmailSmsSender sender = new SubmailSmsSender();
        // 发送给美国号码
        JsonObject result = sender.sendSms(
            "+11234567890",
            "Your verification code is 8866. Valid for 5 minutes. --SUBMAIL"
        );
        handleResponse(result);
    }
}

方式二:MD5 签名认证(生产环境推荐)

import okhttp3.*;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import java.io.IOException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Map;
import java.util.TreeMap;
public class SubmailSmsMd5Sender {
    private static final String APPID  = System.getenv("SUBMAIL_APPID");
    private static final String APPKEY = System.getenv("SUBMAIL_APPKEY");
    private static final String API_URL =
        "https://api-v4.mysubmail.com/internationalsms/send";
    private final OkHttpClient client = new OkHttpClient();
    /**
     * 构建 SUBMAIL MD5 数字签名
     * 规则:appid + appkey + 排序后参数字符串 + appid + appkey,取 MD5
     */
    private String buildMd5Signature(Map<String, String> params) {
        // 使用 TreeMap 自动按 key 排序
        TreeMap<String, String> sorted = new TreeMap<>(params);
        sorted.remove("sign_type");
        StringBuilder paramStr = new StringBuilder();
        for (Map.Entry<String, String> entry : sorted.entrySet()) {
            if (paramStr.length() > 0) paramStr.append("&");
            paramStr.append(entry.getKey()).append("=").append(entry.getValue());
        }
        // 拼接签名原文
        String raw = APPID + APPKEY + paramStr + APPID + APPKEY;
        // MD5 加密
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            byte[] hash = md.digest(raw.getBytes("UTF-8"));
            StringBuilder sb = new StringBuilder();
            for (byte b : hash) {
                sb.append(String.format("%02x", b));
            }
            return sb.toString();
        } catch (Exception e) {
            throw new RuntimeException("MD5 签名生成失败:" + e.getMessage());
        }
    }
    /**
     * 发送国际短信(MD5 签名认证模式)
     */
    public JsonObject sendSmsWithMd5(String to, String content) throws IOException {
        // 先组装参数(不含 sign_type 和 signature)
        Map<String, String> params = new TreeMap<>();
        params.put("appid",   APPID);
        params.put("to",      to);
        params.put("content", content);
        // 生成 MD5 签名
        String signature = buildMd5Signature(params);
        // 构建最终请求体
        FormBody.Builder formBuilder = new FormBody.Builder();
        for (Map.Entry<String, String> entry : params.entrySet()) {
            formBuilder.add(entry.getKey(), entry.getValue());
        }
        formBuilder.add("signature", signature);
        formBuilder.add("sign_type", "md5");
        Request request = new Request.Builder()
            .url(API_URL)
            .post(formBuilder.build())
            .build();
        try (Response response = client.newCall(request).execute()) {
            String responseBody = response.body().string();
            return JsonParser.parseString(responseBody).getAsJsonObject();
        }
    }
    public static void main(String[] args) throws IOException {
        SubmailSmsMd5Sender sender = new SubmailSmsMd5Sender();
        // 发送给英国号码
        JsonObject result = sender.sendSmsWithMd5(
            "+447911123456",
            "Hello! Your order #ORD20260323 has been shipped. --SUBMAIL"
        );
        System.out.println("API 响应:" + result.toString());
    }
}

方式三:使用模板发送(internationalsms/xsend)

如果你在 SUBMAIL 控制台预先创建了短信模板,可以使用 xsend 接口,通过变量动态替换模板内容,适合 OTP 验证码、订单通知等标准化场景:

import okhttp3.*;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import java.io.IOException;
import java.util.Map;
public class SubmailSmsTemplateSender {
    private static final String APPID  = System.getenv("SUBMAIL_APPID");
    private static final String APPKEY = System.getenv("SUBMAIL_APPKEY");
    private static final String XSEND_URL =
        "https://api-v4.mysubmail.com/internationalsms/xsend";
    private final OkHttpClient client = new OkHttpClient();
    private final Gson gson = new Gson();
    /**
     * 使用模板发送国际短信
     *
     * @param to         收件人号码(带国际区号)
     * @param project    模板 ID(控制台中的 project 字段)
     * @param variables  模板变量,如 {"code": "8866", "minutes": "5"}
     */
    public JsonObject sendWithTemplate(String to, String project,
                                        Map<String, String> variables)
            throws IOException {
        // 将变量 Map 序列化为 JSON 字符串
        String varsJson = gson.toJson(variables);
        RequestBody body = new FormBody.Builder()
            .add("appid",     APPID)
            .add("signature", APPKEY)
            .add("to",        to)
            .add("project",   project)
            .add("vars",      varsJson)
            .add("sign_type", "normal")
            .build();
        Request request = new Request.Builder()
            .url(XSEND_URL)
            .post(body)
            .build();
        try (Response response = client.newCall(request).execute()) {
            String responseBody = response.body().string();
            return JsonParser.parseString(responseBody).getAsJsonObject();
        }
    }
    public static void main(String[] args) throws IOException {
        SubmailSmsTemplateSender sender = new SubmailSmsTemplateSender();
        // 模板内容示例:【SUBMAIL】你好 @var(name),验证码 @var(code),@var(minutes) 分钟有效
        Map<String, String> vars = Map.of(
            "name",    "张三",
            "code",    "9527",
            "minutes", "5"
        );
        JsonObject result = sender.sendWithTemplate(
            "+8613812345678",      // 中国大陆号码
            "your_template_id",
            vars
        );
        System.out.println("API 响应:" + result);
    }
}


六、批量发送建议

需要向多个用户批量发送时,推荐以下方案:

使用线程池实现异步并发发送:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.List;
public class BulkSmsSender {
    // 线程池大小根据 SUBMAIL 套餐 QPS 限制调整
    private static final int THREAD_POOL_SIZE = 10;
    public void sendBulk(List<String> phoneNumbers, String content) {
        ExecutorService executor = Executors.newFixedThreadPool(THREAD_POOL_SIZE);
        SubmailSmsSender sender = new SubmailSmsSender();
        for (String phone : phoneNumbers) {
            executor.submit(() -> {
                try {
                    var result = sender.sendSms(phone, content);
                    SubmailSmsSender.handleResponse(result);
                } catch (Exception e) {
                    System.err.println("❌ 发送失败 [" + phone + "]:" + e.getMessage());
                }
            });
        }
        executor.shutdown();
    }
}

或使用 internationalsms/batchsend 接口,支持单次请求发送最多 10,000 个号码,性能更佳,建议超过 100 条时优先使用。



七、错误处理与常见错误码

成功响应示例:

{
  "status": "success",
  "send_id": "abcdef1234567890",
  "fee": 1,
  "sms_credits": 99
}

失败响应示例:

{
  "status": "error",
  "code": "103",
  "msg": "Unauthorized"
}

常见错误码:

  • 103:AppID 或 AppKey 错误 → 检查环境变量配置是否正确
  • 104:签名验证失败 → 检查 MD5 签名算法,注意参数排序和编码格式(UTF-8)
  • 108:短信额度不足 → 前往控制台充值
  • 401:收件人号码格式错误 → 国际号码必须以 + 开头加区号
  • 402:内容违规 → 检查短信正文是否含敏感词


八、安全建议

🔐 绝对不要将 AppKey 硬编码在代码或 Git 仓库中,推荐以下方案:

Linux / macOS 环境变量:

export SUBMAIL_APPID="your_appid"
export SUBMAIL_APPKEY="your_appkey"

Spring Boot 项目(application.yml):

submail:
  appid: ${SUBMAIL_APPID}
  appkey: ${SUBMAIL_APPKEY}
@Value("${submail.appid}")
private String appId;
@Value("${submail.appkey}")
private String appKey;

生产环境推荐方案:

  • Docker / Kubernetes → 通过 Secret 注入环境变量
  • 阿里云 → 使用 KMS 密钥管理服务
  • AWS → 使用 AWS Secrets Manager
  • 本地开发 → 使用 .env 文件,并将其加入 .gitignore


九、小结

  • 注册账号imgmysubmail.com
  • JDK 要求:JDK 1.8 及以上
  • 核心依赖:OkHttp3 4.12.0 + Gson 2.10.1
  • 发送接口https://api-v4.mysubmail.com/internationalsms/send
  • 模板接口https://api-v4.mysubmail.com/internationalsms/xsend
  • 号码格式:必须带 + 国际区号
  • 认证方式:测试用 normal,生产用 md5

与 Python 版本相比,Java 接入 SUBMAIL SMS API 同样简单直接,借助 OkHttp3 和 Gson,整个核心发送逻辑不超过 50 行代码。从注册到发出第一条国际短信,通常只需 30 分钟

官方 Java SDK 参考:imggithub.com

官方 API 文档:imgen.mysubmail.com

了解更多:

SUBMAIL SMS API 国内

SUBMAIL SMS API 国际

申请测试



1
转化率
营销
EDM
短信文案
 推荐文章

Hello Friend!一触即发,开启云通信之旅!

欢迎使用 SUBMAIL 赛邮,点击下方进行注册,开启您的云通信之旅!

免费注册
一触即发,开启云通信之旅!
电话咨询
语音
上海赛邮云计算有限公司
致电:4008-753-365
您可以添加赛邮微信公众号、微博关注或联系我们
语音 微信公众号
语音 赛邮微博
SUBMAIL 赛邮·云通信    ©️ 2021 ALL RIGHTS RESERVED.
保留所有权利 |    开发者公约 | 使用协议 沪 ICP 备 16035411 号 -1