Logo98网络 · API 文档

98网络 API 文档

98网络提供标准 RESTful API,支持二要素身份校验、人脸核身、企业工商核验、运营商三要素、银行卡要素验证等核心能力,以及短信发送、邮件推送通信服务和微信支付商户进件服务。本文档将帮助您快速完成 API 对接。

接口概述

基础信息

项目说明
接口地址https://sm.nhbml.cn/api/{action}
请求方式POST
数据格式请求:application/x-www-form-urlencoded,响应:application/json
字符编码UTF-8
签名算法MD5
时间窗口请求 timestamp 与服务器时间差不超过 ±300 秒

通用请求参数

所有接口(除特殊说明外)均需携带以下公共参数:

参数名类型必填说明
appidstring应用 AppID,在控制台创建应用后获取
timestampstringUnix 时间戳(秒级),与服务器时间差不超过 300 秒
noncestring随机字符串,建议 16-32 位,防重放攻击
signstring请求签名,算法见下方签名认证章节

通用响应格式

{
    "code": 0,
    "msg": "success",
    "data": { ... }
}

URL 格式

系统支持两种 URL 格式访问 API,效果完全一致:

伪静态模式(推荐)

POST https://sm.nhbml.cn/api/{action}

需要 Nginx 配置 try_files 伪静态规则,路径更简洁美观。

原生参数模式

POST https://sm.nhbml.cn/?route=api/{action}

无需任何服务器配置即可使用,兼容性最佳。

两种格式在功能上完全等价,推荐使用伪静态模式。如果您的服务器未配置伪静态,请使用原生参数模式。

签名认证

所有 API 请求必须携带签名参数,用于验证请求合法性。签名算法如下:

签名生成规则

  1. appidtimestampnonceappsecret 四个值按顺序拼接为一个字符串
  2. 对拼接后的字符串进行 MD5 哈希(32 位小写)

签名公式

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 白名单。配置后,仅白名单内的 IP 可调用该应用的 API。白名单为空时不做限制。

错误码

当请求失败时,响应中的 code 字段为非零值。以下是完整的错误码列表:

错误码含义说明
0成功请求处理成功
1001签名校验失败sign 参数不正确,请检查签名算法和 appsecret
1002IP 受限请求 IP 不在应用白名单中
1003请求频率超限触发速率限制,请降低请求频率
1004应用无效AppID 不存在、应用已停用、未通过审核或账号被封禁
2001余额/流量包不足账户余额不足或无可用流量包(部分接口仅支持流量包)
2002参数错误缺少必要参数或参数格式不正确
2003服务未启用增值服务未启用,请联系管理员
3001接口异常接口异常请联系管理员
3002认证失败身份校验未通过(姓名与身份证号不匹配等)
3003等待中人脸核身场景下,用户尚未完成操作

二要素身份校验

校验姓名与身份证号是否匹配,实时调用百度云二要素认证接口。

POST https://sm.nhbml.cn/api/check2factor

请求参数

参数名类型必填说明
appidstring应用 AppID
timestampstringUnix 时间戳
noncestring随机字符串
signstring请求签名
real_namestring真实姓名
id_cardstring身份证号码(18 位)
out_order_nostring商户订单号,用于对账和防重复提交

成功响应

{
    "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 + "&timestamp=" + 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 发送给终端用户,用户在页面上完成人脸采集后,通过查询结果接口获取核身结果。

POST https://sm.nhbml.cn/api/face_init

请求参数

参数名类型必填说明
appidstring应用 AppID
timestampstringUnix 时间戳
noncestring随机字符串
signstring请求签名
real_namestring真实姓名
id_cardstring身份证号码(18 位)
out_order_nostring商户订单号

成功响应

{
    "code": 0,
    "msg": "人脸核身会话创建成功",
    "data": {
        "verify_url": "https://sm.nhbml.cn/?route=api/face_capture&token=xxxx",
        "verify_id": "xxxx",
        "log_id": 123
    }
}

响应字段说明

字段类型说明
verify_urlstring人脸采集页面地址,需发送给终端用户在浏览器中打开
verify_idstring核身会话标识,查询结果时使用
log_idint认证记录 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 + "&timestamp=" + 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 秒),直到获取到最终结果。

POST https://sm.nhbml.cn/api/face_result

请求参数

参数名类型必填说明
appidstring应用 AppID
timestampstringUnix 时间戳
noncestring随机字符串
signstring请求签名
verify_idstring核身会话标识(face_init 返回的 verify_id)
log_idint认证记录 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 + "&timestamp=" + 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));
}

人脸核身 - 对接时序

人脸核身采用异步模式,完整对接流程如下:

1
调用 face_init 创建会话

服务端携带 real_name、id_card、out_order_no 调用 /api/face_init,获取 verify_url、verify_id、log_id。

2
将 verify_url 发送给终端用户

通过短信、微信、App 内嵌 WebView 等方式将人脸采集链接发送给用户。

3
用户完成人脸采集

用户在浏览器中打开链接,授权摄像头,完成活体检测和人脸拍照。系统自动将人脸照片与身份证信息进行比对。

4
轮询 face_result 获取结果

服务端以 3-5 秒间隔轮询 /api/face_result,传入 verify_id 和 log_id。当 code 为 0(通过)或 3002(未通过)时停止轮询。

5
处理业务逻辑

根据核身结果执行后续业务操作(如开通服务、完成注册等)。

企业三要素验证

校验企业名称、统一社会信用代码、法人姓名三要素是否匹配,实时比对工商数据库。适用于企业入驻审核、供应商资质核验等场景。

POST https://sm.nhbml.cn/api/enterprise_3factor

请求参数

参数名类型必填说明
appidstring应用 AppID
timestampstringUnix 时间戳
noncestring随机字符串
signstring请求签名
company_namestring企业名称
credit_nostring统一社会信用代码(18 位)
legal_personstring法人代表姓名
out_order_nostring商户订单号,用于对账和防重复提交

成功响应

{
    "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 + "&timestamp=" + 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(),
});

企业四要素验证

在三要素基础上增加法人身份证号校验,提供更高安全等级的企业身份核验。适用于金融开户、大额交易等高风险场景。

POST https://sm.nhbml.cn/api/enterprise_4factor

请求参数

参数名类型必填说明
appidstring应用 AppID
timestampstringUnix 时间戳
noncestring随机字符串
signstring请求签名
company_namestring企业名称
credit_nostring统一社会信用代码(18 位)
legal_personstring法人代表姓名
legal_person_idstring法人身份证号码(18 位)
out_order_nostring商户订单号

成功响应

{
    "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 + "&timestamp=" + 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(),
});

运营商三要素验证

校验姓名、身份证号、手机号三要素是否匹配,实时比对运营商数据库。适用于手机号实名绑定、风控反欺诈、用户注册验证等场景。

POST https://sm.nhbml.cn/api/carrier_3factor

请求参数

参数名类型必填说明
appidstring应用 AppID
timestampstringUnix 时间戳
noncestring随机字符串
signstring请求签名
namestring真实姓名
idcardstring身份证号码(18 位)
mobilestring手机号码(11 位)
out_order_nostring商户订单号,用于对账和防重复提交

成功响应

{
    "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 + "&timestamp=" + 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(),
});

银行卡三要素验证

校验姓名、身份证号、银行卡号三要素是否匹配,实时比对银联数据。适用于绑卡验证、开户审核、支付风控等场景。

POST https://sm.nhbml.cn/api/bankcard_3factor

请求参数

参数名类型必填说明
appidstring应用 AppID
timestampstringUnix 时间戳
noncestring随机字符串
signstring请求签名
namestring真实姓名
idcardstring身份证号码(18 位)
bankcardstring银行卡号
out_order_nostring商户订单号,用于对账和防重复提交

成功响应

{
    "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 + "&timestamp=" + 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());

银行卡四要素验证

在银行卡三要素基础上增加预留手机号校验,提供更高安全等级的银行卡身份核验。适用于大额支付、快捷支付绑卡等高风险场景。

POST https://sm.nhbml.cn/api/bankcard_4factor

请求参数

参数名类型必填说明
appidstring应用 AppID
timestampstringUnix 时间戳
noncestring随机字符串
signstring请求签名
namestring真实姓名
idcardstring身份证号码(18 位)
bankcardstring银行卡号
mobilestring银行预留手机号(11 位)
out_order_nostring商户订单号

成功响应

{
    "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 + "&timestamp=" + 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());

查询认证模式

查询当前应用配置的认证模式。可用于客户端动态判断应该调用二要素还是人脸核身接口。

POST https://sm.nhbml.cn/api/verify_mode

请求参数

仅需携带公共参数(appid、timestamp、nonce、sign),无额外业务参数。

成功响应

{
    "code": 0,
    "msg": "ok",
    "data": {
        "mode": "2factor"
    }
}

mode 取值说明

说明
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 + "&timestamp=" + 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 协议对接,非 HTTP API 接口。用户在控制台创建 SMTP 账号后,使用任意邮件客户端或 SMTP 库即可发送邮件。

接入流程

  1. 在控制台「SMTP 账号」页面创建 SMTP 账号,获取 SMTP 地址、端口、用户名和密码
  2. 在您的应用中配置 SMTP 参数,使用标准 SMTP 协议发送邮件
  3. 系统自动进行内容安全过滤,发送成功后按封计费

SMTP 参数

参数说明
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',
});

计费规则

  • 按发送成功的邮件数量计费,发送失败不扣费
  • 支持流量包和余额两种计费方式
  • 每封邮件按收件人数量计算(1 个收件人 = 1 封)
邮件内容需符合平台内容安全规范,包含违规内容的邮件将被拦截且不退费。

发送短信

通过 API 发送短信,支持验证码、通知、营销等类型。需先在控制台创建短信模板并通过审核,选择可用签名后方可调用。

POST https://sm.nhbml.cn/api/sms_send

前置条件

  1. 在控制台开通短信服务
  2. 完成企业资质认证(资质管理)
  3. 创建短信签名并通过审核
  4. 创建短信模板并通过审核
  5. 确保账户余额充足

请求参数

参数名类型必填说明
appidstring应用 AppID
timestampstringUnix 时间戳
noncestring随机字符串
signstring请求签名
template_idint短信模板 ID(控制台创建模板后获取)
phonestring接收手机号(11 位国内手机号)
sign_namestring短信签名名称(不含【】)
template_paramstring模板变量,JSON 格式,如 {"code":"123456"}

成功响应

{
    "code": 0,
    "msg": "发送成功",
    "data": {
        "log_id": 123,
        "biz_id": "900619746936498xxxx"
    }
}

响应字段说明

字段类型说明
log_idint发送记录 ID,可在控制台查看详情
biz_idstring运营商返回的消息 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 + "&timestamp=" + 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、平台公钥、商户私钥,支持随时重置
状态同步实时同步微信审核状态,支持驳回后重新编辑提交

对接流程

从创建应用到完成收款对接的完整流程:

1
创建应用

在控制台「应用管理」页面创建应用,系统自动生成 App Key 和 RSA 密钥对。

2
商户进件

点击应用的「去进件」按钮,选择服务商渠道,按步骤填写主体信息、经营信息、行业资质、结算账户、超级管理员等资料。支持 OCR 自动识别营业执照和身份证。

3
提交审核

资料填写完成后提交至微信审核。审核期间可在列表页同步最新状态。

4
签约激活

审核通过后,微信会返回签约链接。超级管理员需在微信中完成签约,签约后商户号正式生效。

5
获取密钥对接

进件通过后,在应用列表点击「密钥」查看接口地址、App Key、平台公钥、商户私钥,用于对接收款。

进件流程采用分步表单设计,每一步必须填写完必填项后才能进入下一步,确保资料完整性。

密钥说明

每个应用创建时自动生成以下密钥,用于后续支付接口的签名验证和数据加密:

密钥用途安全建议
App Key应用唯一标识,调用接口时作为 appid 参数传入可重置,重置后旧 Key 立即失效
平台公钥用于验证平台返回数据的签名,确保数据未被篡改可公开分发给客户端
商户私钥用于对请求数据进行签名,证明请求来自合法商户严禁泄露,仅在服务端使用
商户私钥是最核心的安全凭证,请务必妥善保管。切勿在客户端代码、日志、版本控制中暴露私钥。如怀疑泄露,请立即在控制台重置密钥。

签名算法

支付接口采用 RSA-SHA256 签名算法,流程如下:

  1. 将请求参数按 key 字母序排列,拼接为 key1=value1&key2=value2 格式
  2. 使用商户私钥对拼接字符串进行 RSA-SHA256 签名
  3. 将签名结果 Base64 编码后作为 sign 参数传入

PHP 签名示例

$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 即可。

POST https://sm.nhbml.cn/api/pay_create
支付接口使用 RSA-SHA256 签名,与认证接口的 MD5 签名不同。公共参数为 app_key(非 appid),详见上方「密钥与签名」章节。

公共参数

参数名类型必填说明
app_keystring应用 App Key,在控制台「应用管理」中获取
timestampstringUnix 时间戳(秒级),与服务器时间差不超过 300 秒
noncestring随机字符串,建议 16-32 位
signstringRSA-SHA256 签名,算法见「密钥与签名」章节

业务参数

参数名类型必填说明
out_trade_nostring商户订单号,同一应用下不可重复,建议 6-32 位字母数字组合
descriptionstring商品描述,如「会员充值」
total_amountint订单金额,单位:分。如 100 表示 1.00 元
notify_urlstring支付结果回调地址,不传则使用渠道默认配置
attachstring附加数据,回调时原样返回,最长 128 字符
return_urlstring支付完成后的跳转地址,支付成功后 3 秒自动跳转到此 URL

成功响应

{
    "code": 0,
    "msg": "下单成功",
    "data": {
        "out_trade_no": "ORDER_20260314001",
        "cashier_url": "https://pay.siyun.com/api/cashier?token=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
    }
}

响应字段说明

字段类型说明
out_trade_nostring商户订单号
cashier_urlstring平台收银台页面 URL,将用户重定向到此地址即可完成支付

错误响应示例

{
    "code": 2002,
    "msg": "商户订单号重复"
}
收银台支付流程:商户无需拥有服务号或处理 openid,只需将用户重定向到 cashier_url。平台收银台会根据用户环境自动选择最优支付方式:
① 微信内打开 → 自动 OAuth 获取 openid → JSAPI 拉起支付
② PC 浏览器打开 → 展示二维码 + 付款链接,用户在微信中扫码或粘贴链接完成支付
③ 手机浏览器打开 → 展示二维码 + 付款链接,用户保存二维码到微信识别或复制链接到微信打开
收银台页面有效期 30 分钟,支付成功后自动跳转 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

查询订单

根据商户订单号查询订单状态。系统会同时向微信支付发起查询并同步最新状态。

POST https://sm.nhbml.cn/api/pay_query

请求参数

参数名类型必填说明
app_keystring应用 App Key
timestampstringUnix 时间戳
noncestring随机字符串
signstringRSA-SHA256 签名
out_trade_nostring商户订单号(下单时传入的 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_nostring商户订单号
transaction_idstring微信支付订单号,支付成功后返回
trade_statestring交易状态,见下方状态枚举
trade_state_descstring交易状态描述
total_amountint订单金额(分)
pay_timestring支付完成时间,未支付时为 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)时无法关闭,已关闭的订单重复调用会直接返回成功。

POST https://sm.nhbml.cn/api/pay_close

请求参数

参数名类型必填说明
app_keystring应用 App Key
timestampstringUnix 时间戳
noncestring随机字符串
signstringRSA-SHA256 签名
out_trade_nostring商户订单号

成功响应

{
    "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());

计费说明

计费模式

平台支持两种计费方式,不同接口适用的计费模式不同:

  1. 流量包计费:用户在控制台购买对应接口的流量包,调用时消耗流量包额度。企业工商核验、运营商三要素、银行卡要素验证接口仅支持此模式
  2. 按量付费:二要素校验、人脸核身等基础认证接口支持流量包优先 + 余额兜底的双轨模式

扣费流程:每次 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 协议发送邮件按封计费,单价由管理员在后台配置
SMTP 邮件推送通过标准 SMTP 协议对接,非 HTTP API 接口。用户在控制台创建 SMTP 账号后,使用任意邮件客户端或 SMTP 库即可发送邮件,详见控制台使用指南。
企业工商核验、运营商三要素、银行卡要素验证接口仅支持流量包计费,无流量包时调用将返回错误码 2001。其他付费接口在余额不足且无流量包时也将返回 2001

更新日志

日期版本更新内容
2026-03-14v1.9统一下单接口新增 trade_type=cashier 收银台模式,商户无需服务号即可完成移动端微信支付;平台自动处理 OAuth 授权和支付拉起
2026-03-14v1.8统一下单接口新增 trade_type 参数,支持 JSAPI、Native(扫码)、H5 三种支付方式;Native 和 H5 模式无需 openid
2026-03-13v1.7新增微信支付商户进件功能,支持多渠道进件、OCR 智能填充、应用密钥管理;新增支付服务 API 文档章节
2026-03-11v1.6新增短信发送 API 接口(sms_send),支持通过 API 发送短信
2026-03-07v1.5新增运营商三要素验证、银行卡三要素验证、银行卡四要素验证接口
2026-03-07v1.4新增企业三要素验证、企业四要素验证接口
2026-02-28v1.3新增 SMTP 邮件推送计费说明
2026-02-24v1.2新增卡密充值功能,新增 Node.js 签名示例,完善人脸核身对接时序文档
2026-02-23v1.1新增人脸核身接口(face_init / face_result),新增 verify_mode 接口
2026-02-20v1.0初始版本,支持二要素身份校验