系统接口主要分为 存证类(POST提交,Content-Type 为 multipart/form-data) 和 查询类(GET查询,采用 URL Query 参数) 两种形式。具体请参考“接口详情”说明。
https://block.weickcn.com
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| appId | String | 是 | 平台分配的第三方应用ID |
| timestamp | String | 是 | Unix毫秒级时间戳(有效期±10分钟) |
| sign | String | 是 | 签名字符串 |
将 appId、appSecret、timestamp 拼接成一个字符串,然后计算 国密SM3 哈希值。
伪代码示例
raw_str = appId + appSecret + timestamp
sign = SM3(raw_str)
为应对高并发场景,系统对存证请求(文件与文本)采用异步回调模式。请求成功提交后,系统立即返回 HTTP 202 Accepted 及任务ID(taskId)。实际的上链存证过程在后台排队处理。
处理完成后,系统会向第三方应用配置的回调地址 (Callback URL) 发送 POST 请求。为保证数据来源可靠,回调请求的 Header 中包含 X-Callback-Signature。第三方应用需使用平台分配的 回调密钥 (Callback Secret) 对 Payload 进行 HMAC-SHA256 签名校验。
Python 回调签名校验示例
import hmac
import hashlib
import json
# payload_dict 为回调请求的 JSON Body (不含多余空格)
payload_str = json.dumps(payload_dict, separators=(',', ':'), sort_keys=True)
expected_signature = hmac.new(
callback_secret.encode('utf-8'),
payload_str.encode('utf-8'),
hashlib.sha256
).hexdigest()
if request.headers.get("X-Callback-Signature") == expected_signature:
print("签名校验通过")
接口地址: /api/third/evidence/create
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| dataName | String | 是 | 存证数据名称 |
| files | File | 是 | 要存证的文件(支持多文件上传,仅限标准图片格式如 png, jpg, jpeg) |
存证请求提交后,系统将返回异步处理的任务ID(taskId)和预估时间。
成功响应 (202 Accepted)
{
"code": 202,
"msg": "Accepted: 任务已接收,正在异步处理",
"data": {
"taskId": "TASK-611b299699764c32b17183f02077d0a6", // 异步任务ID
"estimated_time": "1-3s" // 预估处理时间
}
}
如果短时间内重复提交相同签名的请求,将被限流拦截。
限流响应 (429 Too Many Requests)
{
"code": 429,
"msg": "请求处理中或已处理,请勿重复提交",
"data": {}
}
如果请求签名错误或文件格式受限,将返回具体的错误信息。
错误响应 (200 OK,业务报错)
{
"code": 4017,
"msg": "SM3签名校验错误",
"data": {}
}
接口地址: /api/third/evidence/create-text
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| dataName | String | 是 | 存证数据名称 |
| content | String | 是 | 要存证的文本内容 |
存证请求提交后,系统将返回异步处理的任务ID(taskId)和预估时间。
成功响应 (202 Accepted)
{
"code": 202,
"msg": "Accepted: 任务已接收,正在异步处理",
"data": {
"taskId": "TASK-8a2c134556784d90b32190f123456789", // 异步任务ID
"estimated_time": "1-3s" // 预估处理时间
}
}
当第三方应用余额(调用次数)不足时,接口会拦截并返回错误。
错误响应 (200 OK,业务报错)
{
"code": 5001,
"msg": "调用剩余次数不足",
"data": {}
}
接口地址: /api/third/evidence/detail
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| serialNo | String | 是 | 存证流水号 |
根据流水号查询存证的详细信息,包含区块链上的哈希值和时间戳等嵌套结构数据(storageList 数组支持多文件/文本项展示)。
成功响应 (200 OK)
{
"code": 200,
"msg": "操作成功",
"data": {
"appId": "FIO78S31K8O3Y2M197", // 存证归属的应用ID,String
"appName": "国唐盛世大健康产业有限公司消费数据存证", // 存证归属的应用名称,String
"blockChainHash": "", // 链上交易哈希,String(若为空说明上链中或未返回)
"dataName": "测试文本存证", // 存证名称,String
"evidenceStatus": 1, // 存证状态,Integer枚举 (1: 已上链, 0: 上链中/未上链)
"evidenceTime": "2026-03-29 20:00:50", // 存证时间,String,格式 YYYY-MM-DD HH:mm:ss
"serialNo": "ef04b44d-21aa-4bc6-a0b5-325578338a92", // 业务流水号,String
"storageList": [ // 存证文件/文本列表,Array嵌套结构
{
"content": "测试内容", // 文本内容(仅文本存证时返回),String
"fileHash": "b27f229c812da4e6420795e69d14bb87", // 文件或内容的哈希值,String
"name": "TEXT_测试文本存证.txt", // 文件显示名称,String
"realName": "TEXT_测试文本存证-20260329080049737.txt", // 系统存储真实名称,String
"size": "12B", // 文件大小,String
"suffix": "txt", // 文件后缀/格式,String
"type": "文档" // 文件类型描述,String
}
]
}
}
如果传入的流水号在链上不存在或尚未被系统记录,将返回空数据。
空数据响应 (200 OK)
{
"code": 200,
"msg": "操作成功",
"data": {}
}
接口地址: /api/third/quota
请求方式: GET
此接口用于查询当前第三方应用账户的剩余可用存证条数以及历史已消耗条数。该接口不受账户可用余额的限制,即使剩余条数为 0 也可正常调用查询。
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
该接口采用 URL Query 传参方式,只需传递 1.1 节中的公共参数(appId、timestamp、sign)即可。 | |||
成功响应 (200 OK)
{
"code": 200,
"msg": "查询成功",
"data": {
"remaining_count": 95, // 当前账户剩余可用的存证次数
"used_count": 5 // 当前账户历史累计已成功扣减的存证次数
}
}
| 错误码 | 含义说明 |
|---|---|
| 200 | 操作成功 |
| 4011 | 缺少签名参数 |
| 4012 | 缺少时间戳参数 |
| 4013 | 时间戳格式非法 |
| 4014 | 缺少appId参数 |
| 4015 | 签名时间戳过期 |
| 4016 | 应用ID无效 |
| 4017 | SM3签名校验错误 |
| 5001 | 调用剩余次数不足 |
| 5002 | 第三方应用已冻结/禁用 |
| 5003 | 调用方IP不在白名单限制内 |
| 5004 | 人民链接口请求重试失败 |
| 5005 | 系统全局异常 |
Python (requests)
import time
import requests
from gmssl import sm3
app_id = "APP1234567890ABCDEF"
app_secret = "your_app_secret"
timestamp = str(int(time.time() * 1000))
# 计算签名
raw_str = f"{app_id}{app_secret}{timestamp}"
sign = sm3.sm3_hash(list(raw_str.encode('utf-8')))
# ====================
# 1. 文本存证接口调用示例
# ====================
data = {
"appId": app_id,
"timestamp": timestamp,
"sign": sign,
"dataName": "测试文本存证",
"content": "这是一段需要存证的重要文本"
}
response = requests.post("https://block.weickcn.com/api/third/evidence/create-text", data=data)
print("文本存证响应:", response.json())
# ====================
# 2. 账户配额查询接口调用示例
# ====================
query_params = {
"appId": app_id,
"timestamp": timestamp,
"sign": sign
}
response_quota = requests.get("https://block.weickcn.com/api/third/quota", params=query_params)
print("账户配额响应:", response_quota.json())
Java
import org.bouncycastle.crypto.digests.SM3Digest;
import org.bouncycastle.util.encoders.Hex;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
public class EvidenceClient {
public static void main(String[] args) throws Exception {
String appId = "APP1234567890ABCDEF";
String appSecret = "your_app_secret";
String timestamp = String.valueOf(System.currentTimeMillis());
// 计算国密SM3签名
String rawStr = appId + appSecret + timestamp;
byte[] msg = rawStr.getBytes(StandardCharsets.UTF_8);
SM3Digest sm3 = new SM3Digest();
sm3.update(msg, 0, msg.length);
byte[] hash = new byte[sm3.getDigestSize()];
sm3.doFinal(hash, 0);
String sign = Hex.toHexString(hash);
// 构建请求参数 (简单表单格式)
String urlParameters = "appId=" + appId + "×tamp=" + timestamp + "&sign=" + sign +
"&dataName=测试文本存证&content=这是一段需要存证的重要文本";
// 发送POST请求 (文本存证示例)
URL url = new URL("https://block.weickcn.com/api/third/evidence/create-text");
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
conn.setDoOutput(true);
try(OutputStream os = conn.getOutputStream()) {
byte[] input = urlParameters.getBytes(StandardCharsets.UTF_8);
os.write(input, 0, input.length);
}
// 读取存证响应
try(BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8))) {
StringBuilder response = new StringBuilder();
String responseLine;
while ((responseLine = br.readLine()) != null) {
response.append(responseLine.trim());
}
System.out.println("存证响应: " + response.toString());
}
// ====================
// 查询账户配额示例
// ====================
String quotaUrlStr = "https://block.weickcn.com/api/third/quota?appId=" + appId +
"×tamp=" + timestamp + "&sign=" + sign;
URL quotaUrl = new URL(quotaUrlStr);
HttpURLConnection quotaConn = (HttpURLConnection) quotaUrl.openConnection();
quotaConn.setRequestMethod("GET");
try(BufferedReader br = new BufferedReader(new InputStreamReader(quotaConn.getInputStream(), StandardCharsets.UTF_8))) {
StringBuilder response = new StringBuilder();
String responseLine;
while ((responseLine = br.readLine()) != null) {
response.append(responseLine.trim());
}
System.out.println("配额响应: " + response.toString());
}
}
}
PHP
<?php
// 注意:PHP需要安装或引入支持国密SM3的扩展库 (例如: Rtgm/sm-crypto)
// 此处仅提供逻辑演示
require 'vendor/autoload.php';
use Rtgm\sm\RtSm3;
$appId = "APP1234567890ABCDEF";
$appSecret = "your_app_secret";
// 获取毫秒级时间戳
$timestamp = (string)(microtime(true) * 1000);
// 计算签名
$rawStr = $appId . $appSecret . $timestamp;
$rtSm3 = new RtSm3();
$sign = $rtSm3->sign($rawStr);
// 准备表单数据
$data = [
'appId' => $appId,
'timestamp' => $timestamp,
'sign' => $sign,
'dataName' => '测试文本存证',
'content' => '这是一段需要存证的重要文本'
];
// 初始化cURL (文本存证示例)
$ch = curl_init('https://block.weickcn.com/api/third/evidence/create-text');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data));
// 执行并获取结果
$response = curl_exec($ch);
if(curl_errno($ch)){
echo 'Curl error: ' . curl_error($ch);
}
curl_close($ch);
echo "存证响应: " . $response . "\n";
// ====================
// 查询账户配额示例
// ====================
$query_params = [
'appId' => $appId,
'timestamp' => $timestamp,
'sign' => $sign
];
$quota_url = 'https://block.weickcn.com/api/third/quota?' . http_build_query($query_params);
$ch_quota = curl_init($quota_url);
curl_setopt($ch_quota, CURLOPT_RETURNTRANSFER, true);
// 默认即为 GET 请求
$response_quota = curl_exec($ch_quota);
curl_close($ch_quota);
echo "配额响应: " . $response_quota . "\n";
?>