TF Fiscal
开发文档

快速开始

快速上手

注册主体、关联证书、注册 Webhook、开具 NF-e 并查询,TF Fiscal 开放 API 的五步接入路径。

本文按顺序演示每个开票集成都会做的五个调用:

  1. 注册开票主体,取得它的 empresaId
  2. 关联主体的 A1 数字证书。
  3. 注册 Webhook,接收开票结果。
  4. 开具一张 NF-e。
  5. 查询这张 NF-e,取得状态与下载链接。

全部请求都发往生产网关基址 https://api.v2.tffiscal.com;完整 URL 为基址加各调用给出的路径。同样的五步也适用于 CT-e 与 DC-e:第 1 至 3 步共用,只有开票与查询端点不同。

前置条件

  • 应用凭证:为你的应用签发的 app_secret。它仅回显一次,请妥善保存;若泄露请申请轮换。
  • 接口订阅:平台为你的应用开通下文用到的全部端点。调用未订阅的端点返回 HTTP 403 与错误码 10009005
  • 签名:每个请求都携带 tokentimestampsign 三个请求头。下面的辅助函数被本页全部示例复用;完整规范见认证与签名。第一次真实调用之前,先用联调回显验证你的实现。
bash
HOST="https://api.v2.tffiscal.com"
APP_SECRET="<APP_SECRET>"
# sign = lowercase hex MD5( token + path + body-without-CR-LF + timestamp )
# usage: sign "<path>" "<body>" "<timestamp>" (pass "" as body for GET / DELETE / multipart)
sign() {
printf '%s%s%s%s' "$APP_SECRET" "$1" "$(printf '%s' "$2" | tr -d '\r\n')" "$3" \
| md5sum | awk '{print $1}'
}

第 1 步:注册主体

POST /openapi/v2/empresas 提交卖家企业资料。响应返回 empresaId,后续每个调用都以它作路径变量,请持久保存。

bash
API_PATH="/openapi/v2/empresas"
BODY='{"cnpj":"14422279000106","inscricaoEstadual":"999999","razaoSocial":"Empresa teste LTDA","nomeFantasia":"Empresa teste","optanteSimplesNacional":true,"mei":false,"email":"empresa-teste@example.com","telefoneComercial":"6122222222","endereco":{"pais":"Brasil","uf":"MG","cidade":"Belo Horizonte","logradouro":"Rua Teste","numero":"999","bairro":"Bairro Teste","cep":"85100000"},"emissaoNFeProduto":{"ambienteProducao":{"sequencialNFe":1,"serieNFe":"10"}}}'
TS=$(date +%s)
curl -sS -X POST "$HOST$API_PATH" \
-H "Content-Type: application/json" \
-H "token: $APP_SECRET" -H "timestamp: $TS" -H "sign: $(sign "$API_PATH" "$BODY" "$TS")" \
-d "$BODY"
json
{ "empresaId": "1934811222334455" }

注册会把主体放入平台的审核队列。运营审批通过且证书关联成功后,主体才能开票;在此之前开票返回 codigo 10004004。同一 CNPJ 重复注册返回 HTTP 400 与 codigo 10003002。逐字段参考:注册主体

第 2 步:关联数字证书

POST /openapi/v1/empresas/{empresaId}/certificadoDigital 接受 A1 证书(.pfx / .p12),既可用 multipart/form-data 上传,也可用 JSON 携带文件内容的 Base64。这里使用 JSON 形态,因为它的请求体与其他 JSON 请求一样参与签名。请生成不带换行的标准 Base64,避免参与签名的请求体与实际发送的请求体不一致。

bash
EMPRESA_ID="1934811222334455"
API_PATH="/openapi/v1/empresas/$EMPRESA_ID/certificadoDigital"
BODY=$(printf '{"senha":"certpass123","arquivoBase64":"%s"}' "$(base64 -w0 certificate.pfx)")
TS=$(date +%s)
curl -sS -X POST "$HOST$API_PATH" \
-H "Content-Type: application/json" \
-H "token: $APP_SECRET" -H "timestamp: $TS" -H "sign: $(sign "$API_PATH" "$BODY" "$TS")" \
--data-binary "$BODY"

成功为 HTTP 200 无响应体,上传成功即替换该主体的旧证书。密码错误返回 HTTP 400 与 codigo CER0005;非法 Base64 或解码后超过 1 MB 返回 10003035;证书 CNPJ 与主体不一致返回 10003010,证书已过期返回 10003011。multipart 形态(以空字符串作为 body 签名)见关联证书

第 3 步:注册 Webhook

POST /openapi/v1/webhooks 登记接收开票结果的 URL。你选定的 token 会在每次回调的 token 请求头中原样带回,供接收端核对来源;此外平台还会用 X-Tffiscal-Signature 请求头对每次投递签名。

bash
API_PATH="/openapi/v1/webhooks"
BODY='{"uri":"https://example.com/tffiscal/callback","contentType":"application/json","token":"dGt6eXp5ZGRra2tzc3Nra2hoaGFha2tha2FhamFoaGFoNzc3Nz"}'
TS=$(date +%s)
curl -sS -X POST "$HOST$API_PATH" \
-H "Content-Type: application/json" \
-H "token: $APP_SECRET" -H "timestamp: $TS" -H "sign: $(sign "$API_PATH" "$BODY" "$TS")" \
-d "$BODY"
json
{ "webHookId": "550001" }

每个应用只有一份回调配置,再次调用会覆盖。注册即订阅授权通过与被驳回两个结果事件。接收端必须返回 2xx,否则平台按退避重试。见注册 WebhookWebhooks

第 4 步:开具 NF-e

主体审批通过后,POST /openapi/v2/empresas/{empresaId}/nf-e 即可受理开票请求。id 由你生成,是查询、作废与幂等的键。ambienteEmissao 必须与主体当前环境一致;新注册主体从 Homologacao 开始。

bash
API_PATH="/openapi/v2/empresas/$EMPRESA_ID/nf-e"
BODY='{"id":"NFe-000014553","ambienteEmissao":"Homologacao","pedido":{"presencaConsumidor":"OperacaoPelaInternet","pagamento":{"formas":[{"tipo":"CartaoDeCredito","valor":28.47}]}},"cliente":{"tipoPessoa":"F","nome":"Demo Client","email":"demo.client@mail.com","cpfCnpj":"88533234775","endereco":{"uf":"PR","cidade":"4106902","logradouro":"Rua Presidente Wilson","numero":"911","bairro":"Uberaba","cep":"81570440"}},"itens":[{"cfop":"6403","codigo":"000068","descricao":"Kingston DataTraveler SE9 DTSE9H 16GB USB Drive","ncm":"85235190","ean":"619659000424","quantidade":1,"unidadeMedida":"UN","valorUnitario":28.47,"impostos":{"icms":{"situacaoTributaria":"101"},"pis":{"situacaoTributaria":"49"},"cofins":{"situacaoTributaria":"49"}}}]}'
TS=$(date +%s)
curl -sS -X POST "$HOST$API_PATH" \
-H "Content-Type: application/json" \
-H "token: $APP_SECRET" -H "timestamp: $TS" -H "sign: $(sign "$API_PATH" "$BODY" "$TS")" \
-d "$BODY"

受理即返回 HTTP 200 无响应体,进入异步开票流程。结果通过第 3 步注册的 Webhook 或第 5 步的查询获知。同一 id 重复提交复用原任务;若上一次开票被驳回(Negada),同一 id 修正字段后重提会按新报文重新开票。完整请求字典:开具 NF-e

第 5 步:查询 NF-e

GET /openapi/v2/empresas/{empresaId}/nf-e/{nfeId} 返回当前状态、票面数据,授权后还返回 DANFE 与 XML 下载链接。nfeId 即第 4 步发送的 id。GET 请求以空字符串作为 body 签名;路径变量属于参与签名的路径。

bash
NFE_ID="NFe-000014553"
API_PATH="/openapi/v2/empresas/$EMPRESA_ID/nf-e/$NFE_ID"
TS=$(date +%s)
curl -sS -X GET "$HOST$API_PATH" \
-H "token: $APP_SECRET" -H "timestamp: $TS" -H "sign: $(sign "$API_PATH" "" "$TS")"

status 字段会从 AguardandoAutorizacao 变为 Autorizada(或 Negada,并由 motivoStatus 说明原因)。授权后 linkDanfelinkDownloadXml 直接 GET 即可下载,见文件下载。响应参考:查询 NF-e

验证失败路径

下表每一行都是对上述流程的一处改动,用于确认你的客户端按预期失败:

改动预期结果
token 取值错误HTTP 401,信封 code 10009002(token 无效)
计算 sign 之后再改动 BODYHTTP 401,信封 code 10009003(签名不匹配)
复用超过 300 秒前的 timestampHTTP 401,信封 code 10009001(时间戳)
调用应用未订阅的端点HTTP 403,信封 code 10009005(未订阅)
同一 CNPJ 注册两次HTTP 400,[{"codigo":"10003002", ...}]
主体仍在测试环境时用 ambienteEmissaoProducao 开票HTTP 400,[{"codigo":"10004030", ...}]
查询不存在的 nfeIdHTTP 404,[{"codigo":"NFe0001", ...}]

鉴权失败使用平台信封;开票类端点的业务失败使用错误数组。两种形状见通用约定

下一步

  • NF-e:在上述调用之外的作废与更正函(CC-e)。
  • CT-eDC-e:其他开票凭证类型,共用同一主体、证书与 Webhook。
  • NF-e 验证:按 XML 或访问密钥核验第三方 NF-e。
  • 身份核验:CNPJ 与 CPF 登记查询。
  • Webhooks:回调载荷、签名校验与重试。
  • 环境:主体如何从测试切换到生产。