OTP API 对接文档
Base URL:https://sms.cashgit.com/api
OTP 路径:/otp;鉴权:X-API-Key: <你的API密钥>
概览与鉴权
请求体:JSON(UTF-8)
手机号:支持 +91xxxxxxxxxx / 91xxxxxxxxxx / 10位本地号,服务端会标准化为 +91...
时间格式:平台返回的所有时间字段统一为 UTC ISO 8601 格式(以 Z 结尾,例如 2026-06-18T10:00:05.000Z)。商户提交的 verified_at 可使用带时区的 ISO 8601 格式,平台会按实际时间解析。
{ success: boolean, data?: object, error?: string, message?: string }X-API-Key: <你的API密钥>。未携带或错误会返回 401。1. 发送 OTP
接口:POST /otp/send
Headers:X-API-Key: <你的API密钥>
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| mobile | string | 是 | 手机号 |
| app_id | string | 是 | OTP 应用 ID(商户调用必填) |
| otp | string | 否 | 商户自定义 OTP,必须为 4-8 位数字。传入后平台仅发送,不做验证。 |
{
"mobile": "+918699583803",
"app_id": "your-otp-app-id",
"otp": "123456"
}
{
"success": true,
"message": "OTP发送成功",
"data": {
"otp_id": "uuid",
"mobile": "+918699583803",
"expires_at": "2026-04-03T14:30:00.000Z",
"validity_seconds": 300,
"session_id": "provider-session-id-or-null",
"verification_mode": "merchant",
"invalidated_otp_id": null
}
}
curl -X POST "https://sms.cashgit.com/api/otp/send" \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-d '{
"mobile": "+918699583803",
"app_id": "your-otp-app-id",
"otp": "123456"
}'
1) 平台生成 OTP(不传
otp,verification_mode=platform,可调 /otp/verify);2) 商户自定义 OTP(传
otp,4-8 位数字,verification_mode=merchant,需商户侧自行校验)。同一手机号存在未过期、未验证 OTP 时发送会返回 429。若上游短信供应商返回失败,接口可能返回 HTTP 200 且 success=false。OTP 应用商户名变量(选填)
部分 OTP 模板支持在短信正文中显示商户名称。该配置在商户后台或管理员后台编辑 OTP 应用时设置,跟随 app_id 生效,发送 /otp/send 或 /otp/resend 时不需要额外传入。
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| use_custom_merchant_name | boolean | 否 | 是否启用自定义商户名变量,默认 false |
| custom_merchant_name | string | 否 | 商户名称;启用后未填写时使用默认名称 CashGit |
2. 验证 OTP
接口:POST /otp/verify
Headers:X-API-Key: <你的API密钥>
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| mobile | string | 是 | 发送时同一手机号 |
| otp | string | 是 | 验证码 |
| app_id | string | 是 | OTP 应用 ID |
{
"mobile": "+918699583803",
"otp": "123456",
"app_id": "your-otp-app-id"
}
{
"success": true,
"message": "OTP验证成功",
"data": {
"otp_id": "uuid",
"mobile": "+918699583803",
"verified_at": "2026-04-03T14:28:16.000Z"
}
}
curl -X POST "https://sms.cashgit.com/api/otp/verify" \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-d '{
"mobile": "+918699583803",
"otp": "123456",
"app_id": "your-otp-app-id"
}'
verification_mode=platform。若发送时传了 otp(商户侧校验),调用本接口会返回 400。3. 重发 OTP
接口:POST /otp/resend
Headers:X-API-Key: <你的API密钥>
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| mobile | string | 是 | 手机号 |
| app_id | string | 是 | OTP 应用 ID |
| otp | string | 否 | 商户自定义 OTP,必须为 4-8 位数字(商户校验模式) |
{
"mobile": "+918699583803",
"app_id": "your-otp-app-id",
"otp": "123456"
}
{
"success": true,
"message": "旧OTP已失效,已发送新OTP",
"data": {
"otp_id": "new-uuid",
"mobile": "+918699583803",
"expires_at": "2026-04-03T14:35:00.000Z",
"validity_seconds": 300,
"session_id": "new-session-id-or-null",
"verification_mode": "merchant",
"invalidated_otp_id": "old-uuid"
}
}
curl -X POST "https://sms.cashgit.com/api/otp/resend" \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-d '{
"mobile": "+918699583803",
"app_id": "your-otp-app-id",
"otp": "123456"
}'
4. OTP 验证结果回填
接口:POST /otp/callback
Headers:X-API-Key: <你的API密钥>
当发送接口返回 verification_mode=merchant 时,商户在自己系统完成 OTP 校验后,可调用本接口回填校验结果,用于平台统计 OTP 回填率、回填时间和回填耗时。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| otp_id | string | 推荐 | /otp/send 或 /otp/resend 返回的 OTP 记录 ID。推荐始终传入。 |
| verify_status | string | 是 | 商户侧校验结果,仅支持 success 或 failed |
| verified_at | string | 否 | 商户侧完成校验的时间,建议 ISO 8601,例如 2026-05-14T15:16:19+08:00 |
| mobile | string | 否 | 未传 otp_id 时用于兜底匹配 |
| app_id | string | 否 | 未传 otp_id 时用于兜底匹配 |
| merchant_user_id | string | 否 | 商户侧用户 ID,最长保留 128 字符 |
| session_id | string | 否 | 商户侧会话 ID,最长保留 128 字符 |
{
"otp_id": "otp-record-id-from-send-response",
"verify_status": "success",
"verified_at": "2026-05-14T15:16:19+08:00",
"merchant_user_id": "user_12345",
"session_id": "merchant_session_abc"
}
{
"success": true,
"message": "OTP验证结果回填成功",
"data": {
"otp_id": "otp-record-id",
"callback_status": "success",
"callback_at": "2026-05-14T07:16:19.210Z",
"callback_duration_seconds": 16.8,
"idempotent": false
}
}
curl -X POST "https://sms.cashgit.com/api/otp/callback" \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-d '{
"otp_id": "otp-record-id-from-send-response",
"verify_status": "success",
"verified_at": "2026-05-14T15:16:19+08:00",
"merchant_user_id": "user_12345",
"session_id": "merchant_session_abc"
}'
idempotent=true,不会覆盖第一次结果。生产对接建议始终传 otp_id,避免使用 mobile + app_id 兜底匹配导致误匹配。5. 查询 OTP 投递回执
接口:GET /otp/status/{otp_id}
Headers:X-API-Key: <你的API密钥>
商户可主动查询某一条 OTP 的短信投递状态。该接口只返回平台当前已经同步到的上游回执结果,不会触发重新发送。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| otp_id | string | 是 | /otp/send 或 /otp/resend 返回的 OTP 记录 ID |
curl -X GET "https://sms.cashgit.com/api/otp/status/otp-record-id-from-send-response" \ -H "X-API-Key: YOUR_API_KEY"
{
"success": true,
"data": {
"otp_id": "otp-record-id",
"app_id": "your-otp-app-id",
"app_name": "Login",
"mobile": "+918699583803",
"delivery_status": "delivered",
"sms_status": "delivered",
"provider": "02",
"provider_status": "delivered",
"provider_message_id": "provider-message-id",
"request_source": "api",
"request_time": "2026-06-18T10:00:00.000Z",
"sent_at": "2026-06-18T10:00:01.000Z",
"provider_sent_at": "2026-06-18T10:00:01.000Z",
"delivered_at": "2026-06-18T10:00:05.000Z",
"provider_delivered_at": "2026-06-18T10:00:05.000Z",
"delivery_duration_seconds": 4,
"retry_provider": null,
"retry_sent_at": null,
"retry_duration_seconds": null,
"verification_status": "pending",
"verified_at": null,
"expires_at": "2026-06-18T10:05:00.000Z",
"attempts": 0,
"created_at": "2026-06-18T10:00:00.000Z"
}
}
| delivery_status | 说明 |
|---|---|
| pending | 平台已记录请求,但还没有最终投递结果 |
| sent | 已提交到上游或上游已受理,但未确认最终送达 |
| delivered | 已送达 |
| failed | 发送或投递失败 |
| unknown | 平台未识别到明确状态 |
sent 或 pending,请稍后重试。6. OTP 投递结果自动回调
配置位置:商户后台「API 访问信息」->「OTP 回执」->「回调地址」。
当 OTP 短信出现最终投递状态(delivered 或 failed)后,平台会向商户配置的地址 POST JSON。
回调 Header 示例:
Content-Type: application/json X-CashGit-Event: otp.delivery X-CashGit-Client-Id: client-id X-CashGit-Signature: hex-hmac-sha256
回调 Body 示例:
{
"event": "otp.delivery",
"otp_id": "otp-record-id",
"app_id": "your-otp-app-id",
"app_name": "Login",
"merchant_no": "31759593",
"mobile": "+918699583803",
"delivery_status": "delivered",
"sms_status": "delivered",
"provider": "02",
"provider_status": "delivered",
"provider_message_id": "provider-message-id",
"request_source": "api",
"request_time": "2026-06-18T10:00:00.000Z",
"sent_at": "2026-06-18T10:00:01.000Z",
"provider_sent_at": "2026-06-18T10:00:01.000Z",
"delivered_at": "2026-06-18T10:00:05.000Z",
"provider_delivered_at": "2026-06-18T10:00:05.000Z",
"delivery_duration_seconds": 4,
"retry_provider": null,
"retry_sent_at": null,
"retry_duration_seconds": null,
"verification_status": "pending",
"verified_at": null,
"expires_at": "2026-06-18T10:05:00.000Z",
"created_at": "2026-06-18T10:00:00.000Z"
}
- 商户接口返回 HTTP
2xx表示已接收并确认本次通知,不代表短信送达成功。短信投递结果请查看delivery_status。建议在验签并可靠保存通知后返回2xx。 - 平台会对并发触发和已确认的重复通知去重,但网络超时、响应丢失或失败重试仍可能导致重复回调,不保证严格只推送一次。商户应按
otp_id + delivery_status做幂等处理;同一 OTP 后续出现不同状态时,应按新的状态更新记录。 - 平台不会在回调中返回 OTP 明文验证码。
回调签名密钥与验签
密钥获取:回调签名密钥与发送短信使用的 X-API-Key 不同。在商户后台「API 访问信息」->「OTP 回执」->「回执签名密钥」点击「查看密钥」,验证当前登录密码后即可查看、复制本人商户的现有密钥,不会生成或重置密钥。页面一分钟后自动隐藏,离开页面或切换到后台也会清除显示。无法验证登录密码时,请联系平台通过双方确认的安全方式领取;不要在公开群组、截图或日志中泄露。
签名算法:平台以签名密钥的原始字符串作为 HMAC 密钥,对原始 JSON Body 的 UTF-8 字节计算 HMAC-SHA256,结果为 64 位十六进制字符串,通过 X-CashGit-Signature 返回。不要将密钥按十六进制解码,也不要先解析、重新序列化或修改请求体后再验签。该 Header 在平台有可用签名密钥时返回;启用验签的接收端应拒绝缺少或不匹配的签名,并联系平台排查。
密钥变更:当前重置商户 API Key 会同时更新回调签名密钥。请提前与平台协调更新接收端密钥,避免回调验签失败;不要为获取签名密钥而直接重置 API Key。
Node.js 验签示例:rawBody 必须是接收 HTTP 请求时保留的原始 Body(Buffer),不是 JSON.stringify(req.body)。密钥应保存在服务端环境变量或密钥管理系统中。
const crypto = require('node:crypto');
function verifyOtpCallback(rawBody, signature, secret) {
if (!Buffer.isBuffer(rawBody) || !secret
|| typeof signature !== 'string'
|| !/^[0-9a-f]{64}$/i.test(signature)) {
return false;
}
const expected = crypto.createHmac('sha256', secret).update(rawBody).digest();
return crypto.timingSafeEqual(expected, Buffer.from(signature, 'hex'));
}
// rawBody: original request bytes; signature: X-CashGit-Signature header.
const valid = verifyOtpCallback(rawBody, signature, process.env.CASHGIT_CALLBACK_SECRET);
7. 查询商户余额
GET /api/client/balance
请求头:X-API-Key: YOUR_API_KEY。无需参数、OTP 应用 ID 或商户 ID;仅返回该密钥所属有效商户的余额,不能查询其他商户。仅后台登录会话不能调用。
本接口只读,不发送短信、不扣费、不修改账户。金额单位为 USD,返回保留四位小数的字符串。
curl https://sms.cashgit.com/api/client/balance \ -H "X-API-Key: YOUR_API_KEY"
{
"success": true,
"data": {
"merchant_no": "12345678",
"currency": "USD",
"balance": "12.3456",
"credit_limit": "10.0000",
"available_balance": "22.3456"
}
}
| 字段 | 含义 |
|---|---|
| balance | 账户余额,不含授信额度;使用授信后可能为负数。 |
| credit_limit | 授信额度。 |
| available_balance | 当前可用余额,等于 balance + credit_limit,可以为负数。 |
限流与重试
- 商户级限流:按商户配置执行。
- 全局保护限流:平台侧可开启洪峰保护(429)。
- 上游通道侧:系统内置队列与熔断,建议客户端做指数退避重试。
错误码说明
| HTTP | 常见原因 | 建议处理 |
|---|---|---|
| 400 | 参数缺失、验证码错误、次数超限、或商户校验模式误调 /otp/verify | 检查参数与 verification_mode,并按流程重试 |
| 400 | /otp/callback 的 verify_status 不是 success 或 failed | 按枚举值传入商户侧校验结果 |
| 401 | X-API-Key 缺失、错误或已禁用 | 检查 API Key |
| 402 | 余额不足(发送场景) | 充值后重试 |
| 403 | 权限不足(常见于令牌鉴权下角色不匹配) | 检查鉴权角色或联系平台管理员 |
| 404 | 资源不存在(如重发无历史记录、app_id 不存在/未激活,或回填未匹配到 OTP 记录) | 确认 app_id 可用,并优先使用发送响应中的 otp_id 回填 |
| 429 | 触发冷却/限流 | 按返回秒数等待再重试 |
| 500 | 平台内部错误或上游异常 | 稍后重试并记录时间点 |
| 200 + success=false | 上游短信供应商发送失败 | 以 success 字段为准,不仅看 HTTP 状态码 |
| 200 / 502 | OTP_SUBMISSION_UNCERTAIN:提交结果待确认 | retry_allowed=false,禁止自动重发或切换备用重发同一请求。 |
上游提交结果待确认的请求按商户 OTP 单价计费,但不代表受理或送达。正常返回中的 billing.state=charged、billing.amount 表示本次已扣费用;记录保持待确认,不伪造发送、送达时间。明确拒绝的提交退回预扣;提交后数据库异常时须检查记录,不应重复发送。
应用快速故障检测默认在 60 秒内连续 3 个不同号码出现上游系统提交故障时,将后续新请求切到已配置的备用 OTP 应用。成功提交会打断连续故障;探测、补发、重复号码、号码侧失败及仍在处理的请求不作为故障样本。原有成熟样本质量检测继续运行,历史待确认请求不自动重发。
对接步骤
- 在商户后台创建 OTP 应用并获取
app_id。 - 获取商户
X-API-Key。 - 平台校验模式:调用
/otp/send(不传otp)后,调用/otp/verify。 - 商户校验模式:调用
/otp/send(传otp),由商户系统自行校验,并调用/otp/callback回填结果。 - 未收到验证码时,满足 60 秒后调用
/otp/resend。 - 如需要短信投递结果,可在后台配置 OTP 回执地址自动接收,也可通过
/otp/status/{otp_id}主动查询。