向印度用户发送商业短信,API对接是第一道技术门槛。但很多开发者发现,代码写完了、请求发出去了,短信却**一条都没到**——原因往往不在代码本身,而在于**DLT参数缺失或格式错误**。
印度短信API对接与全球其他市场最大的不同在于:**DLT合规参数是请求体中的必填项**。不传`dltEntityId`和`dltTemplateId`,运营商网关会在消息到达用户手机之前就将其拦截,且API可能返回成功状态——你根本不知道消息已经被丢弃了。
本文提供Python、Java、PHP三种语言的完整对接代码,并附DLT参数详解和常见错误排查。
在写第一行代码之前,以下三项准备工作缺一不可:
**① 完成DLT注册**:通过TRAI批准的DLT平台(如Vilpower、Jio TrueConnect、Airtel DLT)完成**Principal Entity注册**、**Sender ID注册**和**Content Template注册**。注册完成后,你将获得三个关键参数:**Entity ID**(实体ID)、**Sender ID/Header**(6位发件人名称)、**Template ID**(模板ID)。
**② 获取API凭证**:从短信服务商控制台获取**API Key**或**userid + password**。SMSGatewayCenter等平台支持两种认证方式:userid+password(适合快速测试)或apiKey HTTP header(适合生产环境,凭证不出现在请求体中)。
**③ 确认请求格式**:主流的REST API支持JSON和form-encoded两种格式。JSON适合结构化调用,form-encoded兼容性更好。本文以SMSGatewayCenter的`https://www.lanlansms.com/api`端点为例,支持两种格式。
无论使用哪种语言,以下参数是印度短信API对接的核心:
| 参数 | 说明 | 是否必填 |
|---|---|---|
| mobile | 接收号码(不含国家代码,10位印度手机号) | 必填 |
| msg | 短信内容,必须与DLT备案模板逐字符一致 | 必填 |
| senderid | 6位DLT注册Sender ID | 必填 |
| dltEntityId | DLT注册获得的实体ID | 印度流量必填 |
| dltTemplateId | DLT注册获得的模板ID | 印度流量必填 |
**关键提醒**:`msg`字段的内容必须与DLT备案模板**完全匹配**——包括空格、标点、变量位置。运营商清洗引擎会逐字符比对,任何细微差异都会触发`template mismatch`错误。
使用`requests`库发送form-encoded请求,显式设置超时并解析JSON响应中的`statusCode`(而非HTTP状态码):
```python
import requests
def send_sms_india(mobile, message, sender_id, dlt_entity_id, dlt_template_id):
url = "https://www.lanlansms.com/api"
payload = {
"userid": "YOUR_USER_ID",
"password": "YOUR_PASSWORD",
"mobile": mobile, # 例如 "9876543210"
"msg": message, # 必须与DLT模板一致
"senderid": sender_id, # 6位Sender ID,如 "MYBRND"
"dltEntityId": dlt_entity_id, # DLT Entity ID
"dltTemplateId": dlt_template_id, # DLT Template ID
"output": "json"
}
response = requests.post(url, data=payload, timeout=15)
result = response.json()
# 检查statusCode,而非HTTP状态码
if result.get("statusCode") == "200":
print(f"发送成功,消息ID: {result.get('messageId')}")
return True
else:
print(f"发送失败: {result.get('statusCode')} - {result.get('statusDesc')}")
return False
# 使用示例
send_sms_india(
mobile="9876543210",
message="Your OTP is 4821. Valid for 10 minutes. - MYBRND",
sender_id="MYBRND",
dlt_entity_id="1100000000000000000",
dlt_template_id="1107000000000000000"
)
```
**关键细节**:`statusCode`是短信平台返回的业务状态码,反映DLT模板匹配是否成功、号码是否有效。如果只检查`response.status_code`(HTTP 200),你会误以为发送成功,但消息可能因为DLT不匹配而被运营商丢弃。
四、Java对接代码
使用`HttpURLConnection`发送form-encoded POST请求:
import java.io.*;
import java.net.*;
import java.net.http.*;
public class SmsApiIndia {
private static final String API_URL = "https://www.lanlansms.com/api";
public static String sendSms(String mobile, String message,
String senderId, String entityId, String templateId) {
try {
HttpClient client = HttpClient.newHttpClient();
// 构建form-encoded请求体
String formData = "userid=YOUR_USER_ID"
+ "&password=" + URLEncoder.encode("YOUR_PASSWORD", "UTF-8")
+ "&mobile=" + mobile
+ "&msg=" + URLEncoder.encode(message, "UTF-8")
+ "&senderid=" + senderId
+ "&dltEntityId=" + entityId
+ "&dltTemplateId=" + templateId
+ "&output=json";
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(API_URL))
.header("Content-Type", "application/x-www-form-urlencoded")
.POST(HttpRequest.BodyPublishers.ofString(formData))
.timeout(java.time.Duration.ofSeconds(15))
.build();
HttpResponse<String> response = client.send(request,
HttpResponse.BodyHandlers.ofString());
System.out.println("响应: " + response.body());
return response.body();
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
public static void main(String[] args) {
sendSms("9876543210",
"Your OTP is 4821. Valid for 10 minutes. - MYBRND",
"MYBRND", "1100000000000000000", "1107000000000000000");
}
}
```
**关键细节**:`msg`参数必须使用`URLEncoder.encode`进行编码,否则消息中的空格、特殊字符会导致请求解析失败。`password`也需要URL编码。
五、PHP对接代码
使用cURL发送form-encoded POST请求:
```php
<?php
function sendSmsIndia($mobile, $message, $senderId, $entityId, $templateId) {
$apiUrl = 'https://www.lanlansms.com/api';
$data = array(
'userid' => 'YOUR_USER_ID',
'password' => 'YOUR_PASSWORD',
'mobile' => $mobile, // 例如 '9876543210'
'msg' => $message, // 必须与DLT模板一致
'senderid' => $senderId, // 如 'MYBRND'
'dltEntityId' => $entityId, // DLT Entity ID
'dltTemplateId' => $templateId, // DLT Template ID
'output' => 'json'
);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $apiUrl);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 15);
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
'Content-Type: application/x-www-form-urlencoded'
));
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
$result = json_decode($response, true);
if ($result['statusCode'] == '200') {
echo "发送成功,消息ID: " . $result['messageId'] . "\n";
return true;
} else {
echo "发送失败: " . $result['statusCode'] . " - " . $result['statusDesc'] . "\n";
return false;
}
}
// 使用示例
sendSmsIndia(
'9876543210',
'Your OTP is 4821. Valid for 10 minutes. - MYBRND',
'MYBRND',
'1100000000000000000',
'1107000000000000000'
);
?>
```
**关键细节**:`http_build_query`自动处理URL编码,`CURLOPT_TIMEOUT`防止请求挂起。同样需要检查`statusCode`而非`$httpCode`。
六、常见错误与排查清单
| 错误现象 | 可能原因 | 解决方案 |
|---|---|---|
| API返回200但短信未送达 | dltEntityId或dltTemplateId缺失/错误 | 核对DLT平台上的Entity ID和Template ID |
| template mismatch错误 | msg内容与DLT备案模板不完全一致 | 逐字符比对,包括空格、标点、变量位置 |
| senderid invalid | Sender ID未在DLT平台注册或类型不匹配 | 确认Sender ID为6位且已关联到正确模板 |
| 请求超时 | 未设置超时或网络问题 | 设置15秒超时,实现指数退避重试 |
| 号码格式错误 | 号码包含国家代码或前导0 | 印度号码使用10位格式(如9876543210) |
**DLT参数获取方式**:`Entity ID`和`Template ID`由DLT平台在注册完成后分配,可以在DLT门户的“Entity Management”和“Template Management”页面找到。
七、总结
印度短信API对接的核心要点可以概括为 **“参数合规、内容匹配、状态验证”**:
1. **DLT参数必填**:`dltEntityId`和`dltTemplateId`是印度流量的硬性要求,缺失即拦截
2. **模板逐字符匹配**:发送的`msg`内容必须与DLT备案模板完全一致
3. **检查业务状态码**:解析JSON中的`statusCode`,而非HTTP状态码
4. **超时与重试**:设置15秒超时,对5xx错误实现指数退避重试
以上三种语言的代码可以直接复制使用,只需替换`YOUR_USER_ID`、`YOUR_PASSWORD`以及三个DLT参数即可。建议先用Message Central的1,000条免费测试额度验证代码和DLT参数是否配置正确,再正式上线。