电子发票/数电发票 nodejs SDK | 开票、验真、红冲一站式集成|云开票,开票系统

2026-07-17
电子发票/数电发票 nodejs SDK | 开票、验真、红冲一站式集成|云开票,开票系统

电子发票/数电发票 nodejs SDK | 开票、验真、红冲一站式集成

发票 nodejs SDK 专为电子发票、数电发票(全电发票)场景设计,支持开票、红冲、版式文件下载等核心功能,快速对接税务平台API。

关键词: 电子发票SDK,数电票nodejs,开票接口,发票api,发票开具,发票红冲,全电发票集成


⚡ 核心功能

基础认证

  • 获取授权 - 快速接入税务平台身份认证
  • 人脸二维码登录 - 支持数电发票平台扫码登录
  • 认证状态查询 - 实时获取纳税人身份状态

发票开具

  • 🧾 数电蓝票开具 - 支持增值税普通/专用电子发票
  • 📥 版式文件下载 - 自动获取销项发票PDF/OFD/XML文件

发票红冲

  • 🔍 红冲前蓝票查询 - 精确检索待红冲的电子发票
  • 📋 红字信息表申请 - 生成红冲凭证
  • 负数发票开具 - 自动化红冲流程

📦 快速安装

npm install tax-invoice

📦 查看npm最新版本


📚 查看中文文档 | 💡 更多示例代码


🚀 为什么选择此SDK?

  • 精准覆盖中国数电发票标准 - 严格遵循国家最新接口规范
  • 开箱即用 - 无需处理XML/签名等底层细节,专注业务逻辑
  • 企业级验证 - 已在生产环境处理超100万张电子发票

🧾 支持的开票类型

发票类型 状态
数电发票(普通发票) ✅ 支持
数电发票(增值税专用发票) ✅ 支持
数电发票(铁路电子客票) ✅ 支持
数电发票(航空运输电子客票行程单) ✅ 支持
数电票(二手车销售统一发票) ✅ 支持
数电纸质发票(增值税专用发票) ✅ 支持
数电纸质发票(普通发票) ✅ 支持
数电纸质发票(机动车发票) ✅ 支持
数电纸质发票(二手车发票) ✅ 支持

🤝 贡献与支持

开票

import readline from "readline";
  const { TaxInvoice } = require('tax-invoice');
  const qrcode = require('qrcode-terminal');
  
  const { createClient } = require('redis');
  const QRCode = require('qrcode');
  
  // 配置信息
  const appKey = '';
  const appSecret = '';
  
  const nsrsbh = ''; // 统一社会信用代码
  const title = ''; // 名称(营业执照)
  const username = ''; // 手机号码(电子税务局)
  const password = ''; // 个人用户密码(电子税务局)
  const type = '7'; //6基础 7 标准版
  const xhdwdzdh = '重庆市渝北区龙溪街道丽园路2号XXXX 1325580XXXX' //地址和电话 空格隔开
  const xhdwyhzh = '工商银行XXXX 15451211XXXX'  // 开户行和银行账号 空格隔开
  
  let token = "";
  let debug = false;
  
  // 初始化SDK
  const taxInvoice = new TaxInvoice({
    appKey: appKey,
    appSecret: appSecret,
    debug: debug
  });
  // 初始化redis客户端
  const client = createClient({
    url: 'redis://:test123456@localhost:6379'
  });
  
  
  // 主函数
  async function main() {
    try {
  
      //一 获取授权 可从缓存redis中获取Token
      await getToken(false);
      /*
       * 前端模拟数电发票/电子发票开具 (蓝字发票)
       * @link https://fa-piao.com/fapiao.html?source=github
       */
  
      //二 开具蓝票
      const invoiceResponse = await blueTicket()
      switch (invoiceResponse.code) {
        case 200:
          // 三 下载发票
          await downloadPdfOfdXml(invoiceResponse.data.Fphm);
          break;
        case 420:
          console.log("登录(短信认证)");
          /*
           * 前端模拟短信认证弹窗
           * @link https://fa-piao.com/fapiao.html?action=sms&source=github
           */
          // 1. 发短信验证码
          /*
          * @link https://fa-piao.com/doc.html#api2?source=github
          */
          const loginResponse = await taxInvoice.api.loginDppt({ nsrsbh, username, password, sms: "" });
          if (loginResponse.code === 200) {
            const smsCode = await new Promise<string>((resolve, reject) => {
              const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
              console.log('请在300秒内(' + getFutureTime(300) + '前)输入验证码:');
              const timer = setTimeout(() => {
                rl.close();
                const timeoutError: any = new Error('短信验证码输入超时');
                timeoutError.errorCode = 'TIMEOUT';
                reject(timeoutError);
              }, 300000);
              rl.once('line', (input) => {
                clearTimeout(timer);
                rl.close();
                resolve(input.trim());
              });
            });
            const smsLoginResponse = await taxInvoice.api.loginDppt({ nsrsbh, username, password, sms: smsCode });
            if (smsLoginResponse.code === 200) {
              console.log("短信验证成功");
              console.log("再次调用blueTicket");
              const retryInvoiceResponse = await blueTicket();
              if (retryInvoiceResponse.code === 200) {
                await downloadPdfOfdXml(retryInvoiceResponse.data.Fphm);
              } else {
                const invoiceError: any = new Error('开票失败');
                invoiceError.errorCode = retryInvoiceResponse.code;
                throw invoiceError;
              }
            } else {
              const authError: any = new Error('短信验证失败');
              authError.errorCode = smsLoginResponse.code;
              throw authError;
            }
          }
          break;
        case 430:
          console.log("人脸认证");
          /* 前端模拟人脸认证弹窗
           * @link https://fa-piao.com/fapiao.html?action=face&source=github
           */
          // 1. 获取人脸二维码
          /*
           * @link https://fa-piao.com/doc.html#api3?source=github
           */
          const qrCodeResponse = await taxInvoice.api.getFaceImg({ nsrsbh, username, type: "1" });
          if (qrCodeResponse.data.ewmly === 'swj') {
            console.log("请使用电子税务局app扫码");
          } else if (qrCodeResponse.data.ewmly === 'grsds') {
            console.log("个人所得税app扫码");
          }
          if (qrCodeResponse.data.ewm.length < 500) {
            // //字符串转二维码图片base64   安装 npm install qrcode
            // const base64 = await QRCode.toDataURL(qrCodeResponse.data.ewm);
            // console.log("二维码base64:", base64);
            // qrCodeResponse.data.ewm = base64;
            // // let base64Uri = 'data:image/png;base64,' + base64;
            // // 前端使用示例: <img src="base64Uri" />
          }
          console.log("成功做完人脸认证,请输入数字 1")
          console.log('请在300秒内(' + getFutureTime(300) + '前)输入:');
          qrcode.generate(qrCodeResponse.data.ewm, {
            small: true, // 关键设置:true 会让二维码缩小(使用半高字符)
            margin: 1    // 边距:设为 1 减少空白
          }, (qrCode: string, err: Error | null = null) => {
            console.log(qrCode);
          });
          await new Promise<void>((resolve, reject) => {
            const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
            const timer = setTimeout(() => {
              rl.close();
              const timeoutError: any = new Error('人脸认证等待输入超时');
              timeoutError.errorCode = 'TIMEOUT';
              reject(timeoutError);
            }, 300000);
            rl.once('line', (input) => {
              clearTimeout(timer);
              rl.close();
              console.log(`✅ 收到: ${input}`);
              resolve();
            });
          });
          // 2. 认证完成后获取人脸二维码认证状态
          /*
           * @link https://fa-piao.com/doc.html#api4?source=github
           */
          const rzid = qrCodeResponse.data.rzid;
          const faceStatusResponse = await taxInvoice.api.getFaceState({ nsrsbh, rzid, username, type: "1" });
          if (faceStatusResponse.data) {
            switch (faceStatusResponse.data.slzt) {
              case '1':
                console.log("人脸未认证");
                const faceStatusError: any = new Error('人脸未认证');
                faceStatusError.errorCode = faceStatusResponse.code;
                throw faceStatusError;
              case '2':
                console.log("人脸认证成功");
                const invoiceResponseAfterFace = await blueTicket();
                if (invoiceResponseAfterFace.code === 200) {
                  await downloadPdfOfdXml(invoiceResponseAfterFace.data.Fphm);
                } else {
                  const authError: any = new Error('人脸认证失败');
                  authError.errorCode = invoiceResponseAfterFace.code;
                  throw authError;
                }
                break;
              case '3':
                console.log("人脸认证二维码过期-->重新获取人脸二维码");
                const faceStatusError2: any = new Error('人脸认证二维码过期-->重新获取人脸二维码');
                faceStatusError2.errorCode = faceStatusResponse.code;
                throw faceStatusError2;
            }
          }
          break;
        case 401:
          //token过期 重新获取并缓存token
          console.log(`${invoiceResponse.code} 授权失败: ${invoiceResponse.msg}`);
          console.log("重新授权 获取token 缓存到redis");
           await getToken(true);
           console.log("再请求blueTicket");
           const invoiceResponseAfterToken = await blueTicket();
           if (invoiceResponseAfterToken.code === 200) {
              await downloadPdfOfdXml(invoiceResponseAfterToken.data.Fphm);
           } else {
              const invoiceError: any = new Error('开票失败');
              invoiceError.errorCode = invoiceResponseAfterToken.code;
              throw invoiceError;
           }
          break;
        default:
          console.log('其他错误:', `${invoiceResponse.code} ${invoiceResponse.msg}`);
          break;
      }
    } catch (error: unknown) {
      const err = error as any;
      const errorCode = err?.errorCode ?? err?.code ?? 'UNKNOWN';
      const errorMessage =
        err?.message ??
        (typeof error === 'string' ? error : JSON.stringify(error)) ??
        '未知异常';
      console.error(`错误码: ${errorCode}`);
      console.error(`错误信息: ${errorMessage}`);
      if (Array.isArray(err?.errors) && err.errors.length > 0) {
        console.error("根因列表:");
        err.errors.forEach((item: any, index: number) => {
          const rootCode = item?.code ?? item?.errorCode ?? 'UNKNOWN';
          const rootMessage = item?.message ?? String(item);
          console.error(`  ${index + 1}. [${rootCode}] ${rootMessage}`);
        });
      }
      if (err?.stack) {
        console.error("堆栈:");
        console.error(err.stack);
      }
      console.error("=========================================\n");
    }
  }
  
  // 获取token 并缓存到redis
  async function getToken(forceUpdate = false) {
    await client.connect();
    // 从缓存redis中获取Token 安装npm install redis
    const key = nsrsbh + "@" + username + "@TOKEN";
    if (forceUpdate) {
      /*
      * 获取授权Token文档
      * @link https://fa-piao.com/doc.html#api1?source=github
      */
      const authResult = await taxInvoice.api.getAuthorization(nsrsbh, type);
      // const authResult = await taxInvoice.api.getAuthorization(nsrsbh,type,username,password);
      if (authResult.code === 200) {
        const newToken = authResult.data.token;
        taxInvoice.setToken(newToken);
        await client.set(key, authResult.data.token, { EX: 3600 * 24 * 30 });// 设置过期时间为30天
      } else {
        const authError: any = new Error(`授权失败: ${authResult.msg}`);
        authError.errorCode = authResult.code;
        throw authError;
      }
    } else {
      token = await client.get(key);
      if (token) {
        taxInvoice.setToken(token);
        console.log("Token From Redis");
      } else {
        const authResult = await taxInvoice.api.getAuthorization(nsrsbh, type);
        // const authResult = await taxInvoice.api.getAuthorization(nsrsbh,type,username,password);
        if (authResult.code === 200) {
          const newToken = authResult.data.token;
          taxInvoice.setToken(newToken);
          await client.set(key, authResult.data.token, { EX: 3600 * 24 * 30 });// 设置过期时间为30天
        } else {
          const authError: any = new Error(`授权失败: ${authResult.msg}`);
          authError.errorCode = authResult.code;
          throw authError;
        }
      }
    }
  }
  // 开票
  async function blueTicket() {
        /*
       * 开具数电发票文档
       * @link https://fa-piao.com/doc.html#api6?source=github
       *
       * 税额计算demo
       * @link https://github.com/fapiaoapi/invoice-sdk-nodejs/blob/master/examples/tax_example.ts
       */
    const invoiceParams = {
      username: username,
      fpqqlsh: appKey + Date.now(),// 建议用你的订单号
      fplxdm: '82',
      kplx: '0',
      xhdwsbh: nsrsbh,
      xhdwmc: title,
      xhdwdzdh: xhdwdzdh,
      xhdwyhzh: xhdwyhzh,
      ghdwmc: '个人',
      zsfs: '0',
      fyxm: [
        {
          "fphxz": 0,
          "hsbz": 1,
          "spmc": "*软件维护服务*接口服务费",
          "spbm": "3040201030000000000",
          "je": 100,
          "sl": 0.01,
          "se": 0.99,
          "spsl": 100,
          "dj": 1,
          "dw": "次"
        },
        {
          "fphxz": 0,
          "hsbz": 1,
          "spmc": "*软件维护服务*接口服务费",
          "spbm": "3040201030000000000",
          "je": 100,
          "sl": 0.01,
          "se": 0.99,
          "spsl": 200,
          "dj": 0.5,
          "dw": "次"
        }
      ],
      hjje: 198.02,
      hjse: 1.98,
      jshj: 200
    };
  
    /*
     * 数电蓝票开具接口 文档
     * @link https://fa-piao.com/doc.html#api6?source=github
     */
    return await taxInvoice.api.blueTicket(invoiceParams);
  }
  
  /**
   * 获取pdf ofd xml 下载链接
   * @param fphm 发票号
   */
  async function downloadPdfOfdXml(fphm: string) {
    /*
     * 获取销项数电版式文件 文档 PDF/OFD/XML
     * @link https://fa-piao.com/doc.html#api7?source=github
     */
    const pdfResponse = await taxInvoice.api.getPdfOfdXml({
      "nsrsbh": nsrsbh,
      "username": username,
      "fphm": fphm,
      "downflag": "4"
    });
    if (pdfResponse.code === 200) {
      console.log("发票下载结果:", pdfResponse.data);
    }
  }
  
  /**
   * 获取指定秒数后的时间字符串 (格式: YYYY-MM-DD HH:mm:ss)
   * @param addSeconds 需要增加的秒数 (例如: 300)
   */
  function getFutureTime(addSeconds: number): string {
    const d = new Date();
    d.setSeconds(d.getSeconds() + addSeconds); // 在当前时间基础上增加秒数
  
    // 辅助函数:补零
    const pad = (n: number) => n.toString().padStart(2, '0');
  
    return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;
  }
  
  // 执行主函数
  main();

发票税额计算demo |
发票红冲demo