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 表示结果尚未确认,须先核实,不得更换幂等键重复提交。签名防重放与业务幂等是两项独立保护。

签名算法与请求头 · 统一接口文档 · 错误码与处理建议