98网络提供标准 RESTful API,支持二要素身份校验、人脸核身、企业工商核验、运营商三要素、银行卡要素验证等核心能力,以及短信发送、邮件推送通信服务和微信支付商户进件服务。本文档将帮助您快速完成 API 对接。
| 项目 | 说明 |
|---|---|
| 接口地址 | https://sm.nhbml.cn/api/{action} |
| 请求方式 | POST |
| 数据格式 | 请求:application/x-www-form-urlencoded,响应:application/json |
| 字符编码 | UTF-8 |
| 签名算法 | MD5 |
| 时间窗口 | 请求 timestamp 与服务器时间差不超过 ±300 秒 |
所有接口(除特殊说明外)均需携带以下公共参数:
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
appid | string | 是 | 应用 AppID,在控制台创建应用后获取 |
timestamp | string | 是 | Unix 时间戳(秒级),与服务器时间差不超过 300 秒 |
nonce | string | 是 | 随机字符串,建议 16-32 位,防重放攻击 |
sign | string | 是 | 请求签名,算法见下方签名认证章节 |
{
"code": 0,
"msg": "success",
"data": { ... }
}
系统支持两种 URL 格式访问 API,效果完全一致:
https://sm.nhbml.cn/api/{action}
需要 Nginx 配置 try_files 伪静态规则,路径更简洁美观。
https://sm.nhbml.cn/?route=api/{action}
无需任何服务器配置即可使用,兼容性最佳。
所有 API 请求必须携带签名参数,用于验证请求合法性。签名算法如下:
appid、timestamp、nonce、appsecret 四个值按顺序拼接为一个字符串sign = md5(appid + timestamp + nonce + appsecret)
// 假设参数如下: appid = "app_abc123" timestamp = "1700000000" nonce = "a1b2c3d4e5f6" appsecret = "secret_xyz789" // 拼接字符串: raw = "app_abc1231700000000a1b2c3d4e5f6secret_xyz789" // MD5 哈希: sign = md5(raw) = "e10adc3949ba59abbe56e057f20f883e"
appsecret 仅用于服务端签名计算,切勿在客户端代码或前端页面中暴露。
可在控制台为应用配置 IP 白名单。配置后,仅白名单内的 IP 可调用该应用的 API。白名单为空时不做限制。
当请求失败时,响应中的 code 字段为非零值。以下是完整的错误码列表:
| 错误码 | 含义 | 说明 |
|---|---|---|
| 0 | 成功 | 请求处理成功 |
| 1001 | 签名校验失败 | sign 参数不正确,请检查签名算法和 appsecret |
| 1002 | IP 受限 | 请求 IP 不在应用白名单中 |
| 1003 | 请求频率超限 | 触发速率限制,请降低请求频率 |
| 1004 | 应用无效 | AppID 不存在、应用已停用、未通过审核或账号被封禁 |
| 2001 | 余额/流量包不足 | 账户余额不足或无可用流量包(部分接口仅支持流量包) |
| 2002 | 参数错误 | 缺少必要参数或参数格式不正确 |
| 2003 | 服务未启用 | 增值服务未启用,请联系管理员 |
| 3001 | 接口异常 | 接口异常请联系管理员 |
| 3002 | 认证失败 | 身份校验未通过(姓名与身份证号不匹配等) |
| 3003 | 等待中 | 人脸核身场景下,用户尚未完成操作 |
校验姓名与身份证号是否匹配,实时调用百度云二要素认证接口。
https://sm.nhbml.cn/api/check2factor
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
appid | string | 是 | 应用 AppID |
timestamp | string | 是 | Unix 时间戳 |
nonce | string | 是 | 随机字符串 |
sign | string | 是 | 请求签名 |
real_name | string | 是 | 真实姓名 |
id_card | string | 是 | 身份证号码(18 位) |
out_order_no | string | 是 | 商户订单号,用于对账和防重复提交 |
{
"code": 0,
"msg": "认证成功",
"data": {
"score": 100
}
}
{
"code": 3002,
"msg": "认证失败",
"data": null
}
$appId = 'your_app_id'; $appSecret = 'your_app_secret'; $timestamp = (string)time(); $nonce = bin2hex(random_bytes(16)); $sign = md5($appId . $timestamp . $nonce . $appSecret); $ch = curl_init('https://sm.nhbml.cn/api/check2factor'); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 30, CURLOPT_POSTFIELDS => http_build_query([ 'appid' => $appId, 'timestamp' => $timestamp, 'nonce' => $nonce, 'sign' => $sign, 'real_name' => '张三', 'id_card' => '110101199001011234', 'out_order_no' => 'ORDER_001', ]), ]); $result = json_decode(curl_exec($ch), true); curl_close($ch);
String appId = "your_app_id", appSecret = "your_app_secret"; String timestamp = String.valueOf(System.currentTimeMillis() / 1000); String nonce = UUID.randomUUID().toString().replace("-", ""); String sign = md5(appId + timestamp + nonce + appSecret); String body = "appid=" + appId + "×tamp=" + timestamp + "&nonce=" + nonce + "&sign=" + sign + "&real_name=" + URLEncoder.encode("张三", "UTF-8") + "&id_card=110101199001011234" + "&out_order_no=ORDER_001"; HttpRequest req = HttpRequest.newBuilder() .uri(URI.create("https://sm.nhbml.cn/api/check2factor")) .header("Content-Type", "application/x-www-form-urlencoded") .POST(HttpRequest.BodyPublishers.ofString(body)).build(); String resp = HttpClient.newHttpClient() .send(req, HttpResponse.BodyHandlers.ofString()).body();
import hashlib, time, uuid, requests app_id, app_secret = 'your_app_id', 'your_app_secret' timestamp = str(int(time.time())) nonce = uuid.uuid4().hex sign = hashlib.md5((app_id + timestamp + nonce + app_secret).encode()).hexdigest() resp = requests.post('https://sm.nhbml.cn/api/check2factor', data={ 'appid': app_id, 'timestamp': timestamp, 'nonce': nonce, 'sign': sign, 'real_name': '张三', 'id_card': '110101199001011234', 'out_order_no': 'ORDER_001', }) print(resp.json())
const crypto = require('crypto'); const appId = 'your_app_id', appSecret = 'your_app_secret'; const timestamp = String(Math.floor(Date.now() / 1000)); const nonce = crypto.randomBytes(16).toString('hex'); const sign = crypto.createHash('md5') .update(appId + timestamp + nonce + appSecret).digest('hex'); const resp = await fetch('https://sm.nhbml.cn/api/check2factor', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: new URLSearchParams({ appid: appId, timestamp, nonce, sign, real_name: '张三', id_card: '110101199001011234', out_order_no: 'ORDER_001', }).toString(), }); console.log(await resp.json());
创建一个人脸核身会话,返回人脸采集页面 URL。将该 URL 发送给终端用户,用户在页面上完成人脸采集后,通过查询结果接口获取核身结果。
https://sm.nhbml.cn/api/face_init
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
appid | string | 是 | 应用 AppID |
timestamp | string | 是 | Unix 时间戳 |
nonce | string | 是 | 随机字符串 |
sign | string | 是 | 请求签名 |
real_name | string | 是 | 真实姓名 |
id_card | string | 是 | 身份证号码(18 位) |
out_order_no | string | 是 | 商户订单号 |
{
"code": 0,
"msg": "人脸核身会话创建成功",
"data": {
"verify_url": "https://sm.nhbml.cn/?route=api/face_capture&token=xxxx",
"verify_id": "xxxx",
"log_id": 123
}
}
| 字段 | 类型 | 说明 |
|---|---|---|
verify_url | string | 人脸采集页面地址,需发送给终端用户在浏览器中打开 |
verify_id | string | 核身会话标识,查询结果时使用 |
log_id | int | 认证记录 ID,查询结果时使用 |
verify_url 需要在移动端浏览器中打开(需要调用摄像头)。建议通过短信、微信等方式将链接发送给用户。
$appId = 'your_app_id'; $appSecret = 'your_app_secret'; $timestamp = (string)time(); $nonce = bin2hex(random_bytes(16)); $sign = md5($appId . $timestamp . $nonce . $appSecret); $ch = curl_init('https://sm.nhbml.cn/api/face_init'); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 30, CURLOPT_POSTFIELDS => http_build_query([ 'appid' => $appId, 'timestamp' => $timestamp, 'nonce' => $nonce, 'sign' => $sign, 'real_name' => '张三', 'id_card' => '110101199001011234', 'out_order_no' => 'ORDER_001', ]), ]); $result = json_decode(curl_exec($ch), true); curl_close($ch); // $result['data']['verify_url'] 发送给用户打开
String body = "appid=" + appId + "×tamp=" + timestamp + "&nonce=" + nonce + "&sign=" + sign + "&real_name=" + URLEncoder.encode("张三", "UTF-8") + "&id_card=110101199001011234&out_order_no=ORDER_001"; HttpRequest req = HttpRequest.newBuilder() .uri(URI.create("https://sm.nhbml.cn/api/face_init")) .header("Content-Type", "application/x-www-form-urlencoded") .POST(HttpRequest.BodyPublishers.ofString(body)).build(); // 响应中 data.verify_url 发送给用户打开
resp = requests.post('https://sm.nhbml.cn/api/face_init', data={ 'appid': app_id, 'timestamp': timestamp, 'nonce': nonce, 'sign': sign, 'real_name': '张三', 'id_card': '110101199001011234', 'out_order_no': 'ORDER_001', }) # resp.json()['data']['verify_url'] 发送给用户打开
const resp = await fetch('https://sm.nhbml.cn/api/face_init', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: new URLSearchParams({ appid: appId, timestamp, nonce, sign, real_name: '张三', id_card: '110101199001011234', out_order_no: 'ORDER_001', }).toString(), }); // result.data.verify_url 发送给用户打开
查询人脸核身会话的处理结果。建议在创建会话后轮询此接口(间隔 3-5 秒),直到获取到最终结果。
https://sm.nhbml.cn/api/face_result
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
appid | string | 是 | 应用 AppID |
timestamp | string | 是 | Unix 时间戳 |
nonce | string | 是 | 随机字符串 |
sign | string | 是 | 请求签名 |
verify_id | string | 是 | 核身会话标识(face_init 返回的 verify_id) |
log_id | int | 是 | 认证记录 ID(face_init 返回的 log_id) |
{
"code": 0,
"msg": "人脸核身通过",
"data": {
"score": 80
}
}
{
"code": 3002,
"msg": "人脸核身未通过",
"data": null
}
{
"code": 3003,
"msg": "用户尚未完成人脸核身",
"data": null
}
code: 3003 表示用户尚未完成人脸采集,请继续轮询。建议设置最大轮询时间为 5 分钟,超时后提示用户重新发起。
// 轮询查询人脸核身结果 $maxWait = 300; // 最多等待5分钟 $start = time(); while (time() - $start < $maxWait) { $timestamp = (string)time(); $nonce = bin2hex(random_bytes(16)); $sign = md5($appId . $timestamp . $nonce . $appSecret); $ch = curl_init('https://sm.nhbml.cn/api/face_result'); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_POSTFIELDS => http_build_query([ 'appid' => $appId, 'timestamp' => $timestamp, 'nonce' => $nonce, 'sign' => $sign, 'verify_id' => $verifyId, // face_init 返回的 verify_id 'log_id' => $logId, // face_init 返回的 log_id ]), ]); $result = json_decode(curl_exec($ch), true); curl_close($ch); if ($result['code'] !== 3003) break; // 非等待状态,退出轮询 sleep(3); }
// 轮询查询(间隔3秒,最多5分钟) long start = System.currentTimeMillis(); while (System.currentTimeMillis() - start < 300_000) { String ts = String.valueOf(System.currentTimeMillis() / 1000); String nc = UUID.randomUUID().toString().replace("-", ""); String sg = md5(appId + ts + nc + appSecret); String body = "appid=" + appId + "×tamp=" + ts + "&nonce=" + nc + "&sign=" + sg + "&verify_id=" + verifyId + "&log_id=" + logId; HttpRequest req = HttpRequest.newBuilder() .uri(URI.create("https://sm.nhbml.cn/api/face_result")) .header("Content-Type", "application/x-www-form-urlencoded") .POST(HttpRequest.BodyPublishers.ofString(body)).build(); String resp = HttpClient.newHttpClient() .send(req, HttpResponse.BodyHandlers.ofString()).body(); // 解析 resp,code != 3003 时退出 Thread.sleep(3000); }
import time # 轮询查询(间隔3秒,最多5分钟) start = time.time() while time.time() - start < 300: timestamp = str(int(time.time())) nonce = uuid.uuid4().hex sign = hashlib.md5((app_id + timestamp + nonce + app_secret).encode()).hexdigest() resp = requests.post('https://sm.nhbml.cn/api/face_result', data={ 'appid': app_id, 'timestamp': timestamp, 'nonce': nonce, 'sign': sign, 'verify_id': verify_id, # face_init 返回 'log_id': log_id, # face_init 返回 }) result = resp.json() if result['code'] != 3003: break time.sleep(3)
// 轮询查询(间隔3秒,最多5分钟) const start = Date.now(); while (Date.now() - start < 300000) { const ts = String(Math.floor(Date.now() / 1000)); const nc = crypto.randomBytes(16).toString('hex'); const sg = crypto.createHash('md5') .update(appId + ts + nc + appSecret).digest('hex'); const resp = await fetch('https://sm.nhbml.cn/api/face_result', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: new URLSearchParams({ appid: appId, timestamp: ts, nonce: nc, sign: sg, verify_id: verifyId, log_id: logId, }).toString(), }); const result = await resp.json(); if (result.code !== 3003) break; await new Promise(r => setTimeout(r, 3000)); }
人脸核身采用异步模式,完整对接流程如下:
服务端携带 real_name、id_card、out_order_no 调用 /api/face_init,获取 verify_url、verify_id、log_id。
通过短信、微信、App 内嵌 WebView 等方式将人脸采集链接发送给用户。
用户在浏览器中打开链接,授权摄像头,完成活体检测和人脸拍照。系统自动将人脸照片与身份证信息进行比对。
服务端以 3-5 秒间隔轮询 /api/face_result,传入 verify_id 和 log_id。当 code 为 0(通过)或 3002(未通过)时停止轮询。
根据核身结果执行后续业务操作(如开通服务、完成注册等)。
校验企业名称、统一社会信用代码、法人姓名三要素是否匹配,实时比对工商数据库。适用于企业入驻审核、供应商资质核验等场景。
https://sm.nhbml.cn/api/enterprise_3factor
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
appid | string | 是 | 应用 AppID |
timestamp | string | 是 | Unix 时间戳 |
nonce | string | 是 | 随机字符串 |
sign | string | 是 | 请求签名 |
company_name | string | 是 | 企业名称 |
credit_no | string | 是 | 统一社会信用代码(18 位) |
legal_person | string | 是 | 法人代表姓名 |
out_order_no | string | 是 | 商户订单号,用于对账和防重复提交 |
{
"code": 0,
"msg": "企业三要素验证通过",
"data": {
"match": true
}
}
{
"code": 3002,
"msg": "企业信息不匹配",
"data": null
}
$ch = curl_init('https://sm.nhbml.cn/api/enterprise_3factor'); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 30, CURLOPT_POSTFIELDS => http_build_query([ 'appid' => $appId, 'timestamp' => $timestamp, 'nonce' => $nonce, 'sign' => $sign, 'company_name' => '北京思云科技有限公司', 'credit_no' => '91110108MA01XXXXX', 'legal_person' => '张三', 'out_order_no' => 'ORDER_001', ]), ]);
String body = "appid=" + appId + "×tamp=" + timestamp + "&nonce=" + nonce + "&sign=" + sign + "&company_name=" + URLEncoder.encode("北京思云科技有限公司", "UTF-8") + "&credit_no=91110108MA01XXXXX" + "&legal_person=" + URLEncoder.encode("张三", "UTF-8") + "&out_order_no=ORDER_001"; HttpRequest req = HttpRequest.newBuilder() .uri(URI.create("https://sm.nhbml.cn/api/enterprise_3factor")) .header("Content-Type", "application/x-www-form-urlencoded") .POST(HttpRequest.BodyPublishers.ofString(body)).build();
resp = requests.post('https://sm.nhbml.cn/api/enterprise_3factor', data={ 'appid': app_id, 'timestamp': timestamp, 'nonce': nonce, 'sign': sign, 'company_name': '北京思云科技有限公司', 'credit_no': '91110108MA01XXXXX', 'legal_person': '张三', 'out_order_no': 'ORDER_001', })
const resp = await fetch('https://sm.nhbml.cn/api/enterprise_3factor', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: new URLSearchParams({ appid: appId, timestamp, nonce, sign, company_name: '北京思云科技有限公司', credit_no: '91110108MA01XXXXX', legal_person: '张三', out_order_no: 'ORDER_001', }).toString(), });
在三要素基础上增加法人身份证号校验,提供更高安全等级的企业身份核验。适用于金融开户、大额交易等高风险场景。
https://sm.nhbml.cn/api/enterprise_4factor
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
appid | string | 是 | 应用 AppID |
timestamp | string | 是 | Unix 时间戳 |
nonce | string | 是 | 随机字符串 |
sign | string | 是 | 请求签名 |
company_name | string | 是 | 企业名称 |
credit_no | string | 是 | 统一社会信用代码(18 位) |
legal_person | string | 是 | 法人代表姓名 |
legal_person_id | string | 是 | 法人身份证号码(18 位) |
out_order_no | string | 是 | 商户订单号 |
{
"code": 0,
"msg": "企业四要素验证通过",
"data": {
"match": true
}
}
{
"code": 3002,
"msg": "企业信息不匹配",
"data": null
}
$ch = curl_init('https://sm.nhbml.cn/api/enterprise_4factor'); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 30, CURLOPT_POSTFIELDS => http_build_query([ 'appid' => $appId, 'timestamp' => $timestamp, 'nonce' => $nonce, 'sign' => $sign, 'company_name' => '北京思云科技有限公司', 'credit_no' => '91110108MA01XXXXX', 'legal_person' => '张三', 'legal_person_id' => '110101199001011234', 'out_order_no' => 'ORDER_001', ]), ]);
String body = "appid=" + appId + "×tamp=" + timestamp + "&nonce=" + nonce + "&sign=" + sign + "&company_name=" + URLEncoder.encode("北京思云科技有限公司", "UTF-8") + "&credit_no=91110108MA01XXXXX" + "&legal_person=" + URLEncoder.encode("张三", "UTF-8") + "&legal_person_id=110101199001011234" + "&out_order_no=ORDER_001"; HttpRequest req = HttpRequest.newBuilder() .uri(URI.create("https://sm.nhbml.cn/api/enterprise_4factor")) .header("Content-Type", "application/x-www-form-urlencoded") .POST(HttpRequest.BodyPublishers.ofString(body)).build();
resp = requests.post('https://sm.nhbml.cn/api/enterprise_4factor', data={ 'appid': app_id, 'timestamp': timestamp, 'nonce': nonce, 'sign': sign, 'company_name': '北京思云科技有限公司', 'credit_no': '91110108MA01XXXXX', 'legal_person': '张三', 'legal_person_id': '110101199001011234', 'out_order_no': 'ORDER_001', })
const resp = await fetch('https://sm.nhbml.cn/api/enterprise_4factor', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: new URLSearchParams({ appid: appId, timestamp, nonce, sign, company_name: '北京思云科技有限公司', credit_no: '91110108MA01XXXXX', legal_person: '张三', legal_person_id: '110101199001011234', out_order_no: 'ORDER_001', }).toString(), });
校验姓名、身份证号、手机号三要素是否匹配,实时比对运营商数据库。适用于手机号实名绑定、风控反欺诈、用户注册验证等场景。
https://sm.nhbml.cn/api/carrier_3factor
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
appid | string | 是 | 应用 AppID |
timestamp | string | 是 | Unix 时间戳 |
nonce | string | 是 | 随机字符串 |
sign | string | 是 | 请求签名 |
name | string | 是 | 真实姓名 |
idcard | string | 是 | 身份证号码(18 位) |
mobile | string | 是 | 手机号码(11 位) |
out_order_no | string | 是 | 商户订单号,用于对账和防重复提交 |
{
"code": 0,
"msg": "运营商三要素验证通过",
"data": {
"match": true
}
}
{
"code": 3002,
"msg": "运营商信息不匹配",
"data": null
}
$ch = curl_init('https://sm.nhbml.cn/api/carrier_3factor'); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 30, CURLOPT_POSTFIELDS => http_build_query([ 'appid' => $appId, 'timestamp' => $timestamp, 'nonce' => $nonce, 'sign' => $sign, 'name' => '张三', 'idcard' => '110101199001011234', 'mobile' => '13800138000', 'out_order_no' => 'ORDER_001', ]), ]);
String body = "appid=" + appId + "×tamp=" + timestamp + "&nonce=" + nonce + "&sign=" + sign + "&name=" + URLEncoder.encode("张三", "UTF-8") + "&idcard=110101199001011234" + "&mobile=13800138000" + "&out_order_no=ORDER_001"; HttpRequest req = HttpRequest.newBuilder() .uri(URI.create("https://sm.nhbml.cn/api/carrier_3factor")) .header("Content-Type", "application/x-www-form-urlencoded") .POST(HttpRequest.BodyPublishers.ofString(body)).build();
resp = requests.post('https://sm.nhbml.cn/api/carrier_3factor', data={ 'appid': app_id, 'timestamp': timestamp, 'nonce': nonce, 'sign': sign, 'name': '张三', 'idcard': '110101199001011234', 'mobile': '13800138000', 'out_order_no': 'ORDER_001', })
const resp = await fetch('https://sm.nhbml.cn/api/carrier_3factor', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: new URLSearchParams({ appid: appId, timestamp, nonce, sign, name: '张三', idcard: '110101199001011234', mobile: '13800138000', out_order_no: 'ORDER_001', }).toString(), });
校验姓名、身份证号、银行卡号三要素是否匹配,实时比对银联数据。适用于绑卡验证、开户审核、支付风控等场景。
https://sm.nhbml.cn/api/bankcard_3factor
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
appid | string | 是 | 应用 AppID |
timestamp | string | 是 | Unix 时间戳 |
nonce | string | 是 | 随机字符串 |
sign | string | 是 | 请求签名 |
name | string | 是 | 真实姓名 |
idcard | string | 是 | 身份证号码(18 位) |
bankcard | string | 是 | 银行卡号 |
out_order_no | string | 是 | 商户订单号,用于对账和防重复提交 |
{
"code": 0,
"msg": "银行卡三要素验证通过",
"data": {
"match": true
}
}
{
"code": 3002,
"msg": "银行卡信息不匹配",
"data": null
}
$appId = 'your_app_id'; $appSecret = 'your_app_secret'; $timestamp = (string)time(); $nonce = bin2hex(random_bytes(16)); $sign = md5($appId . $timestamp . $nonce . $appSecret); $ch = curl_init('https://sm.nhbml.cn/api/bankcard_3factor'); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 30, CURLOPT_POSTFIELDS => http_build_query([ 'appid' => $appId, 'timestamp' => $timestamp, 'nonce' => $nonce, 'sign' => $sign, 'name' => '张三', 'idcard' => '110101199001011234', 'bankcard' => '6222021234567890123', 'out_order_no' => 'ORDER_001', ]), ]); $result = json_decode(curl_exec($ch), true); curl_close($ch);
String appId = "your_app_id", appSecret = "your_app_secret"; String timestamp = String.valueOf(System.currentTimeMillis() / 1000); String nonce = UUID.randomUUID().toString().replace("-", ""); String sign = md5(appId + timestamp + nonce + appSecret); String body = "appid=" + appId + "×tamp=" + timestamp + "&nonce=" + nonce + "&sign=" + sign + "&name=" + URLEncoder.encode("张三", "UTF-8") + "&idcard=110101199001011234" + "&bankcard=6222021234567890123" + "&out_order_no=ORDER_001"; HttpRequest req = HttpRequest.newBuilder() .uri(URI.create("https://sm.nhbml.cn/api/bankcard_3factor")) .header("Content-Type", "application/x-www-form-urlencoded") .POST(HttpRequest.BodyPublishers.ofString(body)).build(); String resp = HttpClient.newHttpClient() .send(req, HttpResponse.BodyHandlers.ofString()).body();
import hashlib, time, uuid, requests app_id, app_secret = 'your_app_id', 'your_app_secret' timestamp = str(int(time.time())) nonce = uuid.uuid4().hex sign = hashlib.md5((app_id + timestamp + nonce + app_secret).encode()).hexdigest() resp = requests.post('https://sm.nhbml.cn/api/bankcard_3factor', data={ 'appid': app_id, 'timestamp': timestamp, 'nonce': nonce, 'sign': sign, 'name': '张三', 'idcard': '110101199001011234', 'bankcard': '6222021234567890123', 'out_order_no': 'ORDER_001', }) print(resp.json())
const crypto = require('crypto'); const appId = 'your_app_id', appSecret = 'your_app_secret'; const timestamp = String(Math.floor(Date.now() / 1000)); const nonce = crypto.randomBytes(16).toString('hex'); const sign = crypto.createHash('md5') .update(appId + timestamp + nonce + appSecret).digest('hex'); const resp = await fetch('https://sm.nhbml.cn/api/bankcard_3factor', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: new URLSearchParams({ appid: appId, timestamp, nonce, sign, name: '张三', idcard: '110101199001011234', bankcard: '6222021234567890123', out_order_no: 'ORDER_001', }).toString(), }); console.log(await resp.json());
在银行卡三要素基础上增加预留手机号校验,提供更高安全等级的银行卡身份核验。适用于大额支付、快捷支付绑卡等高风险场景。
https://sm.nhbml.cn/api/bankcard_4factor
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
appid | string | 是 | 应用 AppID |
timestamp | string | 是 | Unix 时间戳 |
nonce | string | 是 | 随机字符串 |
sign | string | 是 | 请求签名 |
name | string | 是 | 真实姓名 |
idcard | string | 是 | 身份证号码(18 位) |
bankcard | string | 是 | 银行卡号 |
mobile | string | 是 | 银行预留手机号(11 位) |
out_order_no | string | 是 | 商户订单号 |
{
"code": 0,
"msg": "银行卡四要素验证通过",
"data": {
"match": true
}
}
{
"code": 3002,
"msg": "银行卡信息不匹配",
"data": null
}
$appId = 'your_app_id'; $appSecret = 'your_app_secret'; $timestamp = (string)time(); $nonce = bin2hex(random_bytes(16)); $sign = md5($appId . $timestamp . $nonce . $appSecret); $ch = curl_init('https://sm.nhbml.cn/api/bankcard_4factor'); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 30, CURLOPT_POSTFIELDS => http_build_query([ 'appid' => $appId, 'timestamp' => $timestamp, 'nonce' => $nonce, 'sign' => $sign, 'name' => '张三', 'idcard' => '110101199001011234', 'bankcard' => '6222021234567890123', 'mobile' => '13800138000', 'out_order_no' => 'ORDER_001', ]), ]); $result = json_decode(curl_exec($ch), true); curl_close($ch);
String appId = "your_app_id", appSecret = "your_app_secret"; String timestamp = String.valueOf(System.currentTimeMillis() / 1000); String nonce = UUID.randomUUID().toString().replace("-", ""); String sign = md5(appId + timestamp + nonce + appSecret); String body = "appid=" + appId + "×tamp=" + timestamp + "&nonce=" + nonce + "&sign=" + sign + "&name=" + URLEncoder.encode("张三", "UTF-8") + "&idcard=110101199001011234" + "&bankcard=6222021234567890123" + "&mobile=13800138000" + "&out_order_no=ORDER_001"; HttpRequest req = HttpRequest.newBuilder() .uri(URI.create("https://sm.nhbml.cn/api/bankcard_4factor")) .header("Content-Type", "application/x-www-form-urlencoded") .POST(HttpRequest.BodyPublishers.ofString(body)).build(); String resp = HttpClient.newHttpClient() .send(req, HttpResponse.BodyHandlers.ofString()).body();
import hashlib, time, uuid, requests app_id, app_secret = 'your_app_id', 'your_app_secret' timestamp = str(int(time.time())) nonce = uuid.uuid4().hex sign = hashlib.md5((app_id + timestamp + nonce + app_secret).encode()).hexdigest() resp = requests.post('https://sm.nhbml.cn/api/bankcard_4factor', data={ 'appid': app_id, 'timestamp': timestamp, 'nonce': nonce, 'sign': sign, 'name': '张三', 'idcard': '110101199001011234', 'bankcard': '6222021234567890123', 'mobile': '13800138000', 'out_order_no': 'ORDER_001', }) print(resp.json())
const crypto = require('crypto'); const appId = 'your_app_id', appSecret = 'your_app_secret'; const timestamp = String(Math.floor(Date.now() / 1000)); const nonce = crypto.randomBytes(16).toString('hex'); const sign = crypto.createHash('md5') .update(appId + timestamp + nonce + appSecret).digest('hex'); const resp = await fetch('https://sm.nhbml.cn/api/bankcard_4factor', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: new URLSearchParams({ appid: appId, timestamp, nonce, sign, name: '张三', idcard: '110101199001011234', bankcard: '6222021234567890123', mobile: '13800138000', out_order_no: 'ORDER_001', }).toString(), }); console.log(await resp.json());
查询当前应用配置的认证模式。可用于客户端动态判断应该调用二要素还是人脸核身接口。
https://sm.nhbml.cn/api/verify_mode
仅需携带公共参数(appid、timestamp、nonce、sign),无额外业务参数。
{
"code": 0,
"msg": "ok",
"data": {
"mode": "2factor"
}
}
| 值 | 说明 |
|---|---|
2factor | 二要素身份校验(姓名 + 身份证号) |
face | 人脸核身(姓名 + 身份证号 + 人脸比对) |
enterprise_3factor | 企业三要素验证(企业名称 + 信用代码 + 法人姓名) |
enterprise_4factor | 企业四要素验证(企业名称 + 信用代码 + 法人姓名 + 法人身份证号) |
carrier_3factor | 运营商三要素验证(姓名 + 身份证号 + 手机号) |
bankcard_3factor | 银行卡三要素验证(姓名 + 身份证号 + 银行卡号) |
bankcard_4factor | 银行卡四要素验证(姓名 + 身份证号 + 银行卡号 + 手机号) |
$appId = 'your_app_id'; $appSecret = 'your_app_secret'; $timestamp = (string)time(); $nonce = bin2hex(random_bytes(16)); $sign = md5($appId . $timestamp . $nonce . $appSecret); $ch = curl_init('https://sm.nhbml.cn/api/verify_mode'); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 30, CURLOPT_POSTFIELDS => http_build_query([ 'appid' => $appId, 'timestamp' => $timestamp, 'nonce' => $nonce, 'sign' => $sign, ]), ]); $result = json_decode(curl_exec($ch), true); curl_close($ch); // $result['data']['mode'] => '2factor' | 'face' | 'enterprise_3factor' ...
String appId = "your_app_id", appSecret = "your_app_secret"; String timestamp = String.valueOf(System.currentTimeMillis() / 1000); String nonce = UUID.randomUUID().toString().replace("-", ""); String sign = md5(appId + timestamp + nonce + appSecret); String body = "appid=" + appId + "×tamp=" + timestamp + "&nonce=" + nonce + "&sign=" + sign; HttpRequest req = HttpRequest.newBuilder() .uri(URI.create("https://sm.nhbml.cn/api/verify_mode")) .header("Content-Type", "application/x-www-form-urlencoded") .POST(HttpRequest.BodyPublishers.ofString(body)).build(); String resp = HttpClient.newHttpClient() .send(req, HttpResponse.BodyHandlers.ofString()).body();
import hashlib, time, uuid, requests app_id, app_secret = 'your_app_id', 'your_app_secret' timestamp = str(int(time.time())) nonce = uuid.uuid4().hex sign = hashlib.md5((app_id + timestamp + nonce + app_secret).encode()).hexdigest() resp = requests.post('https://sm.nhbml.cn/api/verify_mode', data={ 'appid': app_id, 'timestamp': timestamp, 'nonce': nonce, 'sign': sign, }) print(resp.json())
const crypto = require('crypto'); const appId = 'your_app_id', appSecret = 'your_app_secret'; const timestamp = String(Math.floor(Date.now() / 1000)); const nonce = crypto.randomBytes(16).toString('hex'); const sign = crypto.createHash('md5') .update(appId + timestamp + nonce + appSecret).digest('hex'); const resp = await fetch('https://sm.nhbml.cn/api/verify_mode', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: new URLSearchParams({ appid: appId, timestamp, nonce, sign, }).toString(), }); console.log(await resp.json());
基于 SMTP 代理的企业级邮件推送服务,支持 SSL/TLS 加密传输。适用于验证码发送、订单通知、营销邮件等场景。
| 参数 | 说明 |
|---|---|
| SMTP 地址 | 控制台「SMTP 账号」页面显示的服务器地址 |
| 端口 | 465(SSL)或 587(STARTTLS) |
| 用户名 | 控制台创建的 SMTP 账号 |
| 密码 | 创建账号时生成的密码 |
| 加密方式 | SSL / STARTTLS |
// 使用 PHPMailer use PHPMailer\PHPMailer\PHPMailer; $mail = new PHPMailer(true); $mail->isSMTP(); $mail->Host = 'smtp.yoursite.com'; // 控制台获取 $mail->SMTPAuth = true; $mail->Username = 'your_smtp_user'; // 控制台获取 $mail->Password = 'your_smtp_pass'; // 控制台获取 $mail->SMTPSecure = PHPMailer::ENCRYPTION_SMTPS; $mail->Port = 465; $mail->setFrom('noreply@yoursite.com', '发件人名称'); $mail->addAddress('user@example.com'); $mail->Subject = '验证码'; $mail->Body = '您的验证码是 123456'; $mail->send();
import smtplib from email.mime.text import MIMEText msg = MIMEText('您的验证码是 123456') msg['Subject'] = '验证码' msg['From'] = 'noreply@yoursite.com' msg['To'] = 'user@example.com' with smtplib.SMTP_SSL('smtp.yoursite.com', 465) as s: s.login('your_smtp_user', 'your_smtp_pass') s.send_message(msg)
const nodemailer = require('nodemailer'); const transporter = nodemailer.createTransport({ host: 'smtp.yoursite.com', port: 465, secure: true, auth: { user: 'your_smtp_user', pass: 'your_smtp_pass' }, }); await transporter.sendMail({ from: 'noreply@yoursite.com', to: 'user@example.com', subject: '验证码', text: '您的验证码是 123456', });
通过 API 发送短信,支持验证码、通知、营销等类型。需先在控制台创建短信模板并通过审核,选择可用签名后方可调用。
https://sm.nhbml.cn/api/sms_send
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
appid | string | 是 | 应用 AppID |
timestamp | string | 是 | Unix 时间戳 |
nonce | string | 是 | 随机字符串 |
sign | string | 是 | 请求签名 |
template_id | int | 是 | 短信模板 ID(控制台创建模板后获取) |
phone | string | 是 | 接收手机号(11 位国内手机号) |
sign_name | string | 是 | 短信签名名称(不含【】) |
template_param | string | 否 | 模板变量,JSON 格式,如 {"code":"123456"} |
{
"code": 0,
"msg": "发送成功",
"data": {
"log_id": 123,
"biz_id": "900619746936498xxxx"
}
}
| 字段 | 类型 | 说明 |
|---|---|---|
log_id | int | 发送记录 ID,可在控制台查看详情 |
biz_id | string | 运营商返回的消息 ID,用于状态追踪 |
{
"code": 3001,
"msg": "余额不足,当前余额 ¥0.00,单条短信 ¥0.0450",
"data": null
}
$appId = 'your_app_id'; $appSecret = 'your_app_secret'; $timestamp = (string)time(); $nonce = bin2hex(random_bytes(16)); $sign = md5($appId . $timestamp . $nonce . $appSecret); $ch = curl_init('https://sm.nhbml.cn/api/sms_send'); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 30, CURLOPT_POSTFIELDS => http_build_query([ 'appid' => $appId, 'timestamp' => $timestamp, 'nonce' => $nonce, 'sign' => $sign, 'template_id' => 1, 'phone' => '13800138000', 'sign_name' => '思云科技', 'template_param' => json_encode(['code' => '123456']), ]), ]); $result = json_decode(curl_exec($ch), true); curl_close($ch);
String appId = "your_app_id", appSecret = "your_app_secret"; String timestamp = String.valueOf(System.currentTimeMillis() / 1000); String nonce = UUID.randomUUID().toString().replace("-", ""); String sign = md5(appId + timestamp + nonce + appSecret); String body = "appid=" + appId + "×tamp=" + timestamp + "&nonce=" + nonce + "&sign=" + sign + "&template_id=1" + "&phone=13800138000" + "&sign_name=" + URLEncoder.encode("思云科技", "UTF-8") + "&template_param=" + URLEncoder.encode("{\"code\":\"123456\"}", "UTF-8"); HttpRequest req = HttpRequest.newBuilder() .uri(URI.create("https://sm.nhbml.cn/api/sms_send")) .header("Content-Type", "application/x-www-form-urlencoded") .POST(HttpRequest.BodyPublishers.ofString(body)).build(); String resp = HttpClient.newHttpClient() .send(req, HttpResponse.BodyHandlers.ofString()).body();
import hashlib, time, uuid, json, requests app_id, app_secret = 'your_app_id', 'your_app_secret' timestamp = str(int(time.time())) nonce = uuid.uuid4().hex sign = hashlib.md5((app_id + timestamp + nonce + app_secret).encode()).hexdigest() resp = requests.post('https://sm.nhbml.cn/api/sms_send', data={ 'appid': app_id, 'timestamp': timestamp, 'nonce': nonce, 'sign': sign, 'template_id': 1, 'phone': '13800138000', 'sign_name': '思云科技', 'template_param': json.dumps({'code': '123456'}), }) print(resp.json())
const crypto = require('crypto'); const appId = 'your_app_id', appSecret = 'your_app_secret'; const timestamp = String(Math.floor(Date.now() / 1000)); const nonce = crypto.randomBytes(16).toString('hex'); const sign = crypto.createHash('md5') .update(appId + timestamp + nonce + appSecret).digest('hex'); const resp = await fetch('https://sm.nhbml.cn/api/sms_send', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: new URLSearchParams({ appid: appId, timestamp, nonce, sign, template_id: '1', phone: '13800138000', sign_name: '思云科技', template_param: JSON.stringify({ code: '123456' }), }).toString(), }); console.log(await resp.json());
平台提供微信支付商户进件能力,帮助用户快速完成微信支付商户号申请。用户在控制台创建应用后,通过进件流程提交商户资料,审核通过后即可获得接口密钥进行收款对接。
| 能力 | 说明 |
|---|---|
| 商户进件 | 支持个体工商户、企业、事业单位等多种主体类型的微信支付商户号申请 |
| 多渠道支持 | 管理员可配置多个服务商渠道,用户自主选择渠道进件 |
| OCR 智能填充 | 上传营业执照、身份证时自动识别关键信息,减少手动输入 |
| 应用密钥管理 | 每个应用独立生成 App Key、平台公钥、商户私钥,支持随时重置 |
| 状态同步 | 实时同步微信审核状态,支持驳回后重新编辑提交 |
从创建应用到完成收款对接的完整流程:
在控制台「应用管理」页面创建应用,系统自动生成 App Key 和 RSA 密钥对。
点击应用的「去进件」按钮,选择服务商渠道,按步骤填写主体信息、经营信息、行业资质、结算账户、超级管理员等资料。支持 OCR 自动识别营业执照和身份证。
资料填写完成后提交至微信审核。审核期间可在列表页同步最新状态。
审核通过后,微信会返回签约链接。超级管理员需在微信中完成签约,签约后商户号正式生效。
进件通过后,在应用列表点击「密钥」查看接口地址、App Key、平台公钥、商户私钥,用于对接收款。
每个应用创建时自动生成以下密钥,用于后续支付接口的签名验证和数据加密:
| 密钥 | 用途 | 安全建议 |
|---|---|---|
App Key | 应用唯一标识,调用接口时作为 appid 参数传入 | 可重置,重置后旧 Key 立即失效 |
平台公钥 | 用于验证平台返回数据的签名,确保数据未被篡改 | 可公开分发给客户端 |
商户私钥 | 用于对请求数据进行签名,证明请求来自合法商户 | 严禁泄露,仅在服务端使用 |
支付接口采用 RSA-SHA256 签名算法,流程如下:
key1=value1&key2=value2 格式sign 参数传入$params = [
'app_key' => 'ak_xxxxxxxxxxxxxxxx',
'timestamp' => (string)time(),
'nonce' => bin2hex(random_bytes(16)),
'amount' => '100',
// ... 其他业务参数
];
// 按 key 排序拼接
ksort($params);
$signStr = http_build_query($params);
// RSA-SHA256 签名
$privateKey = openssl_pkey_get_private(file_get_contents('merchant_private_key.pem'));
openssl_sign($signStr, $signature, $privateKey, OPENSSL_ALGO_SHA256);
$params['sign'] = base64_encode($signature);
// 使用平台公钥验证响应签名 $publicKey = openssl_pkey_get_public(file_get_contents('platform_public_key.pem')); $data = /* 响应中的 data 字段 JSON 字符串 */; $respSign = /* 响应中的 sign 字段 */; $valid = openssl_verify($data, base64_decode($respSign), $publicKey, OPENSSL_ALGO_SHA256); // $valid === 1 表示验签通过
创建微信支付订单,所有支付请求统一通过平台托管收银台完成。商户无需处理 openid、服务号授权等复杂流程,只需将用户重定向到返回的收银台 URL 即可。
https://sm.nhbml.cn/api/pay_create
app_key(非 appid),详见上方「密钥与签名」章节。
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
app_key | string | 是 | 应用 App Key,在控制台「应用管理」中获取 |
timestamp | string | 是 | Unix 时间戳(秒级),与服务器时间差不超过 300 秒 |
nonce | string | 是 | 随机字符串,建议 16-32 位 |
sign | string | 是 | RSA-SHA256 签名,算法见「密钥与签名」章节 |
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
out_trade_no | string | 是 | 商户订单号,同一应用下不可重复,建议 6-32 位字母数字组合 |
description | string | 是 | 商品描述,如「会员充值」 |
total_amount | int | 是 | 订单金额,单位:分。如 100 表示 1.00 元 |
notify_url | string | 否 | 支付结果回调地址,不传则使用渠道默认配置 |
attach | string | 否 | 附加数据,回调时原样返回,最长 128 字符 |
return_url | string | 否 | 支付完成后的跳转地址,支付成功后 3 秒自动跳转到此 URL |
{
"code": 0,
"msg": "下单成功",
"data": {
"out_trade_no": "ORDER_20260314001",
"cashier_url": "https://pay.siyun.com/api/cashier?token=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
}
}
| 字段 | 类型 | 说明 |
|---|---|---|
out_trade_no | string | 商户订单号 |
cashier_url | string | 平台收银台页面 URL,将用户重定向到此地址即可完成支付 |
{
"code": 2002,
"msg": "商户订单号重复"
}
cashier_url。平台收银台会根据用户环境自动选择最优支付方式:return_url(如有设置)。
$appKey = 'ak_xxxxxxxxxxxxxxxx'; $privateKey = openssl_pkey_get_private(file_get_contents('merchant_private_key.pem')); $timestamp = (string)time(); $nonce = bin2hex(random_bytes(16)); $params = [ 'app_key' => $appKey, 'timestamp' => $timestamp, 'nonce' => $nonce, 'out_trade_no' => 'ORDER_' . date('YmdHis'), 'description' => '会员充值', 'total_amount' => '100', 'return_url' => 'https://your-site.com/pay/result', ]; ksort($params); $signStr = http_build_query($params); openssl_sign($signStr, $signature, $privateKey, OPENSSL_ALGO_SHA256); $params['sign'] = base64_encode($signature); $ch = curl_init('https://sm.nhbml.cn/api/pay_create'); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 30, CURLOPT_POSTFIELDS => http_build_query($params), ]); $result = json_decode(curl_exec($ch), true); curl_close($ch); // 将用户重定向到收银台 header('Location: ' . $result['data']['cashier_url']); exit;
const crypto = require('crypto'); const fs = require('fs'); const params = { app_key: 'ak_xxxxxxxxxxxxxxxx', timestamp: String(Math.floor(Date.now() / 1000)), nonce: crypto.randomBytes(16).toString('hex'), out_trade_no: 'ORDER_' + Date.now(), description: '会员充值', total_amount: '100', return_url: 'https://your-site.com/pay/result', }; const sorted = Object.keys(params).sort() .map(k => k + '=' + params[k]).join('&'); const privateKey = fs.readFileSync('merchant_private_key.pem'); const sign = crypto.createSign('RSA-SHA256') .update(sorted).sign(privateKey, 'base64'); params.sign = sign; const resp = await fetch('https://sm.nhbml.cn/api/pay_create', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: new URLSearchParams(params).toString(), }); const result = await resp.json(); // 将用户重定向到 result.data.cashier_url
根据商户订单号查询订单状态。系统会同时向微信支付发起查询并同步最新状态。
https://sm.nhbml.cn/api/pay_query
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
app_key | string | 是 | 应用 App Key |
timestamp | string | 是 | Unix 时间戳 |
nonce | string | 是 | 随机字符串 |
sign | string | 是 | RSA-SHA256 签名 |
out_trade_no | string | 是 | 商户订单号(下单时传入的 out_trade_no) |
{
"code": 0,
"msg": "success",
"data": {
"out_trade_no": "ORDER_20260313001",
"transaction_id": "4200002098202603130012345678",
"trade_state": "SUCCESS",
"trade_state_desc": "支付成功",
"total_amount": 100,
"pay_time": "2026-03-13 14:30:00"
}
}
| 字段 | 类型 | 说明 |
|---|---|---|
out_trade_no | string | 商户订单号 |
transaction_id | string | 微信支付订单号,支付成功后返回 |
trade_state | string | 交易状态,见下方状态枚举 |
trade_state_desc | string | 交易状态描述 |
total_amount | int | 订单金额(分) |
pay_time | string | 支付完成时间,未支付时为 null |
| 状态值 | 说明 |
|---|---|
SUCCESS | 支付成功 |
NOTPAY | 未支付 |
CLOSED | 已关闭 |
USERPAYING | 用户支付中 |
REFUND | 转入退款 |
PAYERROR | 支付失败 |
$params = [
'app_key' => $appKey,
'timestamp' => (string)time(),
'nonce' => bin2hex(random_bytes(16)),
'out_trade_no' => 'ORDER_20260313001',
];
ksort($params);
openssl_sign(http_build_query($params), $sig, $privateKey, OPENSSL_ALGO_SHA256);
$params['sign'] = base64_encode($sig);
$ch = curl_init('https://sm.nhbml.cn/api/pay_query');
curl_setopt_array($ch, [
CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true,
CURLOPT_POSTFIELDS => http_build_query($params),
]);
$result = json_decode(curl_exec($ch), true);
curl_close($ch);
params = {
'app_key': app_key, 'timestamp': str(int(time.time())),
'nonce': uuid.uuid4().hex,
'out_trade_no': 'ORDER_20260313001',
}
sign_str = urlencode(sorted(params.items()))
signature = private_key.sign(sign_str.encode(), padding.PKCS1v15(), hashes.SHA256())
params['sign'] = base64.b64encode(signature).decode()
resp = requests.post('https://sm.nhbml.cn/api/pay_query', data=params)
print(resp.json())
const params = { app_key: appKey, timestamp: String(Math.floor(Date.now() / 1000)), nonce: crypto.randomBytes(16).toString('hex'), out_trade_no: 'ORDER_20260313001', }; const sorted = Object.keys(params).sort() .map(k => k + '=' + params[k]).join('&'); const sign = crypto.createSign('RSA-SHA256') .update(sorted).sign(privateKey, 'base64'); params.sign = sign; const resp = await fetch('https://sm.nhbml.cn/api/pay_query', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: new URLSearchParams(params).toString(), }); console.log(await resp.json());
关闭未支付的订单。订单已支付(SUCCESS)时无法关闭,已关闭的订单重复调用会直接返回成功。
https://sm.nhbml.cn/api/pay_close
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
app_key | string | 是 | 应用 App Key |
timestamp | string | 是 | Unix 时间戳 |
nonce | string | 是 | 随机字符串 |
sign | string | 是 | RSA-SHA256 签名 |
out_trade_no | string | 是 | 商户订单号 |
{
"code": 0,
"msg": "订单已关闭"
}
{
"code": 2002,
"msg": "订单已支付,无法关闭"
}
$params = [
'app_key' => $appKey,
'timestamp' => (string)time(),
'nonce' => bin2hex(random_bytes(16)),
'out_trade_no' => 'ORDER_20260313001',
];
ksort($params);
openssl_sign(http_build_query($params), $sig, $privateKey, OPENSSL_ALGO_SHA256);
$params['sign'] = base64_encode($sig);
$ch = curl_init('https://sm.nhbml.cn/api/pay_close');
curl_setopt_array($ch, [
CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true,
CURLOPT_POSTFIELDS => http_build_query($params),
]);
$result = json_decode(curl_exec($ch), true);
curl_close($ch);
params = {
'app_key': app_key, 'timestamp': str(int(time.time())),
'nonce': uuid.uuid4().hex,
'out_trade_no': 'ORDER_20260313001',
}
sign_str = urlencode(sorted(params.items()))
signature = private_key.sign(sign_str.encode(), padding.PKCS1v15(), hashes.SHA256())
params['sign'] = base64.b64encode(signature).decode()
resp = requests.post('https://sm.nhbml.cn/api/pay_close', data=params)
print(resp.json())
const params = { app_key: appKey, timestamp: String(Math.floor(Date.now() / 1000)), nonce: crypto.randomBytes(16).toString('hex'), out_trade_no: 'ORDER_20260313001', }; const sorted = Object.keys(params).sort() .map(k => k + '=' + params[k]).join('&'); const sign = crypto.createSign('RSA-SHA256') .update(sorted).sign(privateKey, 'base64'); params.sign = sign; const resp = await fetch('https://sm.nhbml.cn/api/pay_close', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: new URLSearchParams(params).toString(), }); console.log(await resp.json());
平台支持两种计费方式,不同接口适用的计费模式不同:
扣费流程:每次 API 调用时系统先冻结对应费用,调用成功后确认扣费,失败则回滚冻结金额。
充值方式:支持在线支付和卡密充值。
| 接口 | 说明 | 计费规则 |
|---|---|---|
check2factor | 二要素身份校验 | 流量包 + 余额,¥0.3/次,调用成功扣费,失败不计费 |
face_init | 人脸核身 | 流量包 + 余额,¥0.5/次,调用成功扣费,失败不计费 |
face_result | 查询核身结果 | 免费,不产生费用 |
enterprise_3factor | 企业三要素验证 | 仅流量包,调用成功扣费,失败不计费 |
enterprise_4factor | 企业四要素验证 | 仅流量包,调用成功扣费,失败不计费 |
carrier_3factor | 运营商三要素验证 | 仅流量包,调用成功扣费,失败不计费 |
bankcard_3factor | 银行卡三要素验证 | 仅流量包,调用成功扣费,失败不计费 |
bankcard_4factor | 银行卡四要素验证 | 仅流量包,调用成功扣费,失败不计费 |
verify_mode | 查询认证模式 | 免费,不产生费用 |
sms_send | 短信发送 | 流量包 + 余额,单价由管理员在后台配置 |
SMTP 邮件推送服务采用独立计费模式,按发送邮件数量计费:
| 服务 | 说明 | 计费规则 |
|---|---|---|
| SMTP 邮件推送 | 通过 SMTP 协议发送邮件 | 按封计费,单价由管理员在后台配置 |
2001。其他付费接口在余额不足且无流量包时也将返回 2001。
| 日期 | 版本 | 更新内容 |
|---|---|---|
| 2026-03-14 | v1.9 | 统一下单接口新增 trade_type=cashier 收银台模式,商户无需服务号即可完成移动端微信支付;平台自动处理 OAuth 授权和支付拉起 |
| 2026-03-14 | v1.8 | 统一下单接口新增 trade_type 参数,支持 JSAPI、Native(扫码)、H5 三种支付方式;Native 和 H5 模式无需 openid |
| 2026-03-13 | v1.7 | 新增微信支付商户进件功能,支持多渠道进件、OCR 智能填充、应用密钥管理;新增支付服务 API 文档章节 |
| 2026-03-11 | v1.6 | 新增短信发送 API 接口(sms_send),支持通过 API 发送短信 |
| 2026-03-07 | v1.5 | 新增运营商三要素验证、银行卡三要素验证、银行卡四要素验证接口 |
| 2026-03-07 | v1.4 | 新增企业三要素验证、企业四要素验证接口 |
| 2026-02-28 | v1.3 | 新增 SMTP 邮件推送计费说明 |
| 2026-02-24 | v1.2 | 新增卡密充值功能,新增 Node.js 签名示例,完善人脸核身对接时序文档 |
| 2026-02-23 | v1.1 | 新增人脸核身接口(face_init / face_result),新增 verify_mode 接口 |
| 2026-02-20 | v1.0 | 初始版本,支持二要素身份校验 |