SDK 与调用示例
Node.js 和 Python SDK 在调用方服务端计算签名,自动生成时间戳、随机数和请求 ID。SDK 不保存密钥、不自动重试,也不会对业务 JSON 额外加密。
统一 POST 地址已部署。请先确认企业凭证、能力及税号权限已开通;具体税务通道仍以验证状态为准。已有调用方继续使用原协议,不会自动切换。
下载与校验
下载 Node / Python SDK v1.0.1运行环境:Node.js 20 及以上、Python 3.10 及以上。解压后进入 yfhl-openapi-sdk 目录;运行时无需安装第三方依赖。包内包含协议说明和固定签名测试向量。
SHA-256:26e83ec8047891cd1d82281e3e46f0f5267faa03dde62bf4d69617bf4ecc5c80
Node.js:发票查验
在服务端环境变量中设置 YFHL_ACCESS_KEY_ID 和 YFHL_ACCESS_KEY_SECRET,不要将 Secret 写入浏览器代码、仓库或日志。示例按金额查验,具体票种的 kpje 含义见接口说明。
JavaScript
import { signRequest } from './node/index.js';
const request = signRequest({
url: 'https://open.yifeihuilian.com/api/v2/invoices/check',
accessKeyId: process.env.YFHL_ACCESS_KEY_ID,
accessKeySecret: process.env.YFHL_ACCESS_KEY_SECRET,
headers: { 'idempotency-key': 'invoice-check-business-001' },
body: JSON.stringify({
tax_no: '<已授权企业税号>',
invoices: [{
fphm: '<发票号码>', kprq: '20261023', kpje: '100.00'
}]
})
});
const response = await fetch(request.url, {
method: request.method,
headers: request.headers,
body: request.body,
signal: AbortSignal.timeout(30000)
});
const result = await response.json();
if (!response.ok || result.code !== 'OK') {
// 按 code 决定后续动作,保留 request_id 便于排查。
// 结果未知时先核实业务状态,不自动更换幂等键重试。
console.error(result.code, result.request_id);
} else {
console.log(result.data);
}Python:发起扫码连接
连接成功后,使用响应中的 ewmdz 获取短期二维码图片,并查询连接状态。应保存返回的 lsh 用于后续流程。
Python
import json
import os
from urllib.error import HTTPError
from urllib.request import Request, urlopen
from python.yfhl_openapi import sign_request
signed = sign_request(
url='https://open.yifeihuilian.com/api/v2/tax-connections/create',
access_key_id=os.environ['YFHL_ACCESS_KEY_ID'],
access_key_secret=os.environ['YFHL_ACCESS_KEY_SECRET'],
body=json.dumps({'method': 'QR', 'nsrsbh': '<已授权企业税号>'},
ensure_ascii=False, separators=(',', ':')),
)
request = Request(signed['url'], data=signed['body'],
headers=signed['headers'], method=signed['method'])
try:
with urlopen(request, timeout=30) as response:
result = json.load(response)
print(result['code'], result['request_id'], result['data'])
except HTTPError as error:
result = json.load(error)
print(result['code'], result['request_id'])
# 不依赖 message 文案判断,也不自动重试写入。签名与重试
先序列化业务数据,签名后发送 SDK 返回的同一份 body。更改空格、字段顺序或字符编码都会改变签名。请求超时需要重试时重新调用签名函数生成新随机数和签名;同一业务保留原幂等键。
BUSINESS_RESULT_UNKNOWN 表示结果尚未确认,须先核实,不得更换幂等键重复提交。签名防重放与业务幂等是两项独立保护。