Merchant API 2026-06 校对

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密钥>

参数类型必填说明
mobilestring是手机号
app_idstring是OTP 应用 ID(商户调用必填)
otpstring否商户自定义 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_nameboolean否是否启用自定义商户名变量,默认 false
custom_merchant_namestring否商户名称;启用后未填写时使用默认名称 CashGit
只有平台已标记支持商户名变量的 OTP 模板会使用该配置;其他模板会忽略该配置。

2. 验证 OTP

接口:POST /otp/verify

Headers:X-API-Key: <你的API密钥>

参数类型必填说明
mobilestring是发送时同一手机号
otpstring是验证码
app_idstring是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密钥>

参数类型必填说明
mobilestring是手机号
app_idstring是OTP 应用 ID
otpstring否商户自定义 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"
  }'
重发规则为固定 60 秒最小间隔,不是“必须等旧 OTP 过期”。

4. OTP 验证结果回填

接口:POST /otp/callback

Headers:X-API-Key: <你的API密钥>

当发送接口返回 verification_mode=merchant 时,商户在自己系统完成 OTP 校验后,可调用本接口回填校验结果,用于平台统计 OTP 回填率、回填时间和回填耗时。

参数类型必填说明
otp_idstring推荐/otp/send 或 /otp/resend 返回的 OTP 记录 ID。推荐始终传入。
verify_statusstring是商户侧校验结果,仅支持 success 或 failed
verified_atstring否商户侧完成校验的时间,建议 ISO 8601,例如 2026-05-14T15:16:19+08:00
mobilestring否未传 otp_id 时用于兜底匹配
app_idstring否未传 otp_id 时用于兜底匹配
merchant_user_idstring否商户侧用户 ID,最长保留 128 字符
session_idstring否商户侧会话 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"
  }'
本接口是幂等接口。同一条 OTP 已经回填过时,再次调用会返回 idempotent=true,不会覆盖第一次结果。生产对接建议始终传 otp_id,避免使用 mobile + app_id 兜底匹配导致误匹配。

5. 查询 OTP 投递回执

接口:GET /otp/status/{otp_id}

Headers:X-API-Key: <你的API密钥>

商户可主动查询某一条 OTP 的短信投递状态。该接口只返回平台当前已经同步到的上游回执结果,不会触发重新发送。

参数类型必填说明
otp_idstring是/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,可以为负数。
每个商户每分钟最多查询 60 次,建议每 30-60 秒查询一次;HTTP 429 时按 Retry-After 响应头的秒数等待。缺少、错误、已撤销的密钥或商户已禁用时返回 HTTP 401。余额响应不允许缓存,余额会随并发扣费、充值等操作变化,不保证后续发送一定有足够余额。

限流与重试

  • 商户级限流:按商户配置执行。
  • 全局保护限流:平台侧可开启洪峰保护(429)。
  • 上游通道侧:系统内置队列与熔断,建议客户端做指数退避重试。

错误码说明

HTTP常见原因建议处理
400参数缺失、验证码错误、次数超限、或商户校验模式误调 /otp/verify检查参数与 verification_mode,并按流程重试
400/otp/callback 的 verify_status 不是 success 或 failed按枚举值传入商户侧校验结果
401X-API-Key 缺失、错误或已禁用检查 API Key
402余额不足(发送场景)充值后重试
403权限不足(常见于令牌鉴权下角色不匹配)检查鉴权角色或联系平台管理员
404资源不存在(如重发无历史记录、app_id 不存在/未激活,或回填未匹配到 OTP 记录)确认 app_id 可用,并优先使用发送响应中的 otp_id 回填
429触发冷却/限流按返回秒数等待再重试
500平台内部错误或上游异常稍后重试并记录时间点
200 + success=false上游短信供应商发送失败以 success 字段为准,不仅看 HTTP 状态码
200 / 502OTP_SUBMISSION_UNCERTAIN:提交结果待确认retry_allowed=false,禁止自动重发或切换备用重发同一请求。

上游提交结果待确认的请求按商户 OTP 单价计费,但不代表受理或送达。正常返回中的 billing.state=charged、billing.amount 表示本次已扣费用;记录保持待确认,不伪造发送、送达时间。明确拒绝的提交退回预扣;提交后数据库异常时须检查记录,不应重复发送。

应用快速故障检测默认在 60 秒内连续 3 个不同号码出现上游系统提交故障时,将后续新请求切到已配置的备用 OTP 应用。成功提交会打断连续故障;探测、补发、重复号码、号码侧失败及仍在处理的请求不作为故障样本。原有成熟样本质量检测继续运行,历史待确认请求不自动重发。

对接步骤

  1. 在商户后台创建 OTP 应用并获取 app_id。
  2. 获取商户 X-API-Key。
  3. 平台校验模式:调用 /otp/send(不传 otp)后,调用 /otp/verify。
  4. 商户校验模式:调用 /otp/send(传 otp),由商户系统自行校验,并调用 /otp/callback 回填结果。
  5. 未收到验证码时,满足 60 秒后调用 /otp/resend。
  6. 如需要短信投递结果,可在后台配置 OTP 回执地址自动接收,也可通过 /otp/status/{otp_id} 主动查询。