# invoice-sdk-java **Repository Path**: fapiaoapi/invoice-sdk-java ## Basic Information - **Project Name**: invoice-sdk-java - **Description**: 发票sdk 发票api java 电子发票 数电发票 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: https://fa-piao.com - **GVP Project**: No ## Statistics - **Stars**: 8 - **Forks**: 3 - **Created**: 2025-04-15 - **Last Updated**: 2026-04-17 ## Categories & Tags **Categories**: Uncategorized **Tags**: 发票, 发票sdk, 数电发票, 电子发票, 发票接口 ## README # 电子发票/数电发票 Java SDK | 开票、验真、红冲一站式集成 [![Maven Central](https://img.shields.io/maven-central/v/io.github.fapiaoapi/invoice?label=Maven%20Central)](https://central.sonatype.com/artifact/io.github.fapiaoapi/invoice) [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://github.com/fapiaoapi/invoice-sdk-java/blob/master/LICENSE) [![Java 17+](https://img.shields.io/badge/Java-17%2B-orange.svg)](https://docs.oracle.com/en/java/javase/17/) **发票 Java SDK** 专为电子发票、数电发票(全电发票)场景设计,支持**开票、红冲、版式文件下载**等核心功能,快速对接税务平台API。 **关键词**: 电子发票SDK,数电票Java,开票接口,发票api,发票开具,发票红冲,全电发票集成 --- ## 📖 核心功能 ### 基础认证 - ✅ **获取授权** - 快速接入税务平台身份认证 - ✅ **人脸二维码登录** - 支持数电发票平台扫码登录 - ✅ **认证状态查询** - 实时获取纳税人身份状态 ### 发票开具 - 🎫 **数电蓝票开具** - 支持增值税普通/专用电子发票 - 📄 **版式文件下载** - 自动获取销项发票PDF/OFD/XML文件 ### 发票红冲 - 🔍 **红冲前蓝票查询** - 精确检索待红冲的电子发票 - 🛑 **红字信息表申请** - 生成红冲凭证 - 🔄 **负数发票开具** - 自动化红冲流程 --- ## 🚀 快速安装 ### Maven ```xml io.github.fapiaoapi invoice 1.0.23 ``` ### Gradle ```groovy implementation 'io.github.fapiaoapi:invoice:1.0.23' ``` [📦 查看Maven Central最新版本](https://central.sonatype.com/artifact/io.github.fapiaoapi/invoice) 本sdk仅支持Java 17+,其他版本请用旧版本 [java8-java16开发票demo](https://github.com/fapiaoapi/invoice/blob/master/BasicExample.java "java8-java16开发票demo") [java8-java16红冲发票demo](https://github.com/fapiaoapi/invoice/blob/master/RedInvoiceExample.java "java8-java16红冲发票demo") [java8-java16发票税额demo](https://github.com/fapiaoapi/invoice/blob/master/TaxExample.java "java8-java16发票税额demo") --- [📚 查看完整中文文档](https://fa-piao.com/doc.html?source=gitee) | [💡 更多示例代码](https://github.com/fapiaoapi/invoice-sdk-java/tree/master/src/main/java/tax/invoice/example) --- ## 🔍 为什么选择此SDK? - **精准覆盖中国数电发票标准** - 严格遵循国家最新接口规范 - **开箱即用** - 无需处理XML/签名等底层细节,专注业务逻辑 - **企业级验证** - 已在生产环境处理超100万张电子发票 --- ## 📊 支持的开票类型 | 发票类型 | 状态 | |----------------|--------| | 数电发票(普通发票) | ✅ 支持 | | 数电发票(增值税专用发票) | ✅ 支持 | | 数电发票(铁路电子客票) | ✅ 支持 | | 数电发票(航空运输电子客票行程单) | ✅ 支持 | | 数电票(二手车销售统一发票) | ✅ 支持 | | 数电纸质发票(增值税专用发票) | ✅ 支持 | | 数电纸质发票(普通发票) | ✅ 支持 | | 数电纸质发票(机动车发票) | ✅ 支持 | | 数电纸质发票(二手车发票) | ✅ 支持 | --- ## 🤝 贡献与支持 - 提交Issue: [问题反馈](https://github.com/fapiaoapi/invoice-sdk-java/issues) - 商务合作: yuejianghe@qq.com ## 🎯 快速开始 ```java package tax.invoice.example; import com.google.zxing.BarcodeFormat; import com.google.zxing.EncodeHintType; import com.google.zxing.WriterException; import com.google.zxing.client.j2se.MatrixToImageWriter; import com.google.zxing.common.BitMatrix; import com.google.zxing.qrcode.QRCodeWriter; import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel; import redis.clients.jedis.Jedis; import tax.invoice.InvoiceClient; import tax.invoice.model.ApiResponse; import tax.invoice.model.AuthorizationResponse; import java.io.ByteArrayOutputStream; import java.io.PrintStream; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.*; import java.util.concurrent.*; import static java.lang.Thread.sleep; public class BasicExample { public static String appKey = ""; public static String appSecret = ""; public static String nsrsbh = "";// 统一社会信用代码 public static String title = "";// 名称(营业执照) public static String username = "";// 手机号码(电子税务局) public static String password = "";// 个人用户密码(电子税务局) public static String type = "6";// 6 基础 7标准 public static String xhdwdzdh = "重庆市渝北区龙溪街道丽园路2号XXXX 1325580XXXX"; // 地址和电话 空格隔开 public static String xhdwyhzh = "工商银行XXXX 15451211XXXX";// 开户行和银行账号 空格隔开 public static String token = ""; public static boolean debug = true; // 是否打印日志 // 创建客户端 public static InvoiceClient client = new InvoiceClient(appKey, appSecret,debug); // redis public static Jedis redisClient = new Jedis("127.0.0.1", 6379); public static void main(String[] args) { // 配置信息 try { // 显式设置 System.out 的编码为 UTF-8 System.setOut(new PrintStream(System.out, true, "UTF-8")); redisClient.auth("test123456"); System.out.println("java " + System.getProperty("java.version")); ExecutorService executor = Executors.newSingleThreadExecutor(); Future future = executor.submit(() -> new Scanner(System.in).nextLine()); /* * * 一 获取token */ getToken(false); // 二 开具蓝票 /* * 前端模拟数电发票/电子发票开具 (蓝字发票) * @see https://fa-piao.com/fapiao.html?source=gitee * */ ApiResponse> invoiceResponse = blueTicket(); switch (invoiceResponse.getCode()) { case 200: Map invoiceData = invoiceResponse.getData(); // 三 下载发票 downloadPdfOfdXml(invoiceData.get("Fphm").toString(),invoiceData.get("Kprq").toString()); break; case 420: System.out.println("420 登录(短信认证)"); /* * 前端模拟短信认证弹窗 * * @see https://fa-piao.com/fapiao.html?action=sms&source=gitee */ // 1. 发短信验证码 /* * @see https://fa-piao.com/doc.html#api2?source=gitee */ ApiResponse> loginResponse = client.loginDppt(nsrsbh, username, password,""); if (loginResponse.getCode() == 200) { System.out.println("请输入验证码"); try { System.out.print("300秒内(" + LocalDateTime.now().plusSeconds(300) .format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")) + "前)输入内容: "); String smsCode = future.get(300, TimeUnit.SECONDS); System.out.println("🎉 成功获取输入: " + smsCode + " \n" + "当前时间: " + LocalDateTime.now() .format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); // 2. 输入验证码 /* * @see https://fa-piao.com/doc.html#api2?source=gitee */ ApiResponse> loginResponse2 = client.loginDppt(nsrsbh, username, password, smsCode); if (loginResponse2.getCode() == 200) { System.out.println("验证成功"); System.out.println("请再次调用blueTicket"); ApiResponse> invoiceResponse2 = blueTicket(); if(invoiceResponse2.getCode() == 200) { downloadPdfOfdXml(invoiceResponse2.getData().get("Fphm").toString(),invoiceResponse2.getData().get("Kprq").toString()); } } else { System.out.println(loginResponse2.getCode() + "验证失败: " + loginResponse2.getMsg()); } } catch (TimeoutException | InterruptedException | ExecutionException e) { System.out.println("\n短信认证输入超时!"); future.cancel(true); } executor.shutdown(); } else { System.out.println(loginResponse.getCode() + "发短信验证码失败: " + loginResponse.getMsg()); } break; case 430: System.out.println("430 人脸认证"); /* * 前端模拟人脸认证弹窗 * * @see https://fa-piao.com/fapiao.html?action=face&source=gitee */ // 1. 获取人脸二维码 /* * @see https://fa-piao.com/doc.html#api3?source=gitee */ ApiResponse> qrCodeResponse = client.getFaceImg(nsrsbh, username, "1"); Map qrData = qrCodeResponse.getData(); if (qrData == null) { System.out.println("人脸二维码返回为空: " + qrCodeResponse.getMsg()); break; } Object ewmlyObj = qrData.get("ewmly"); String ewmly = ewmlyObj == null ? "" : ewmlyObj.toString(); System.out.println("swj".equals(ewmly) ? "请使用电子税务局app扫码" : "个人所得税app扫码"); Object ewmObj = qrData.get("ewm"); if (ewmObj != null && ewmObj.toString().length() < 500) { // 字符串转二维码图片base64 /* com.google.zxing core 3.5.3 com.google.zxing javase 3.5.3 */ // String base64 = toBase64((String) ewmObj,300); // qrData.put("ewm", base64); // System.out.println("data:image/png;base64," + base64); // String base64Uri = "data:image/png;base64," + base64; // 前端使用示例: } //字符串转二维码图片 命令行终端打印 stringToQrcode((String) ewmObj); System.out.println("成功做完人脸认证,请输入数字 1"); try { System.out.print("300秒内(" + LocalDateTime.now().plusSeconds(300) .format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")) + "前)输入内容: "); String inputNum = future.get(300, TimeUnit.SECONDS); System.out.println("🎉 成功获取输入: " + inputNum + " \n" + "当前时间: " + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); // 2. 认证完成后获取人脸二维码认证状态 /* * @see https://fa-piao.com/doc.html#api4?source=gitee */ String rzid = qrCodeResponse.getData().get("rzid").toString(); ApiResponse> faceStatusResponse = client.getFaceState(nsrsbh, rzid,username, "1"); if (faceStatusResponse.getData() != null) { String slzt = faceStatusResponse.getData().get("slzt").toString(); if ("2".equals(slzt)) { System.out.println("认证状态: 成功"); System.out.println("请再次调用blueTicket"); ApiResponse> invoiceResponse3 = blueTicket(); if(invoiceResponse3.getCode() == 200) { downloadPdfOfdXml(invoiceResponse3.getData().get("Fphm").toString(),invoiceResponse3.getData().get("Kprq").toString()); } } else { System.out.println("认证状态: " + ("1".equals(slzt) ? "未认证" : "二维码过期")); } } } catch (TimeoutException | InterruptedException | ExecutionException e) { System.out.println("\n人脸认证输入超时!"); future.cancel(true); } executor.shutdown(); break; case 401: // token过期 重新获取并缓存token System.out.println(invoiceResponse.getCode() + "授权失败:" + invoiceResponse.getMsg()); System.out.println("401 token过期 重新获取并缓存token"); sleep(1000); getToken(true); System.out.println("再调用blueTicket"); ApiResponse> invoiceResponse4 = blueTicket(); if(invoiceResponse4.getCode() == 200) { downloadPdfOfdXml(invoiceResponse4.getData().get("Fphm").toString(),invoiceResponse4.getData().get("Kprq").toString()); } break; default: System.out.println(invoiceResponse.getCode() + "异常" + invoiceResponse.getMsg()); break; } } catch (Exception e) { System.out.println("系统错误: " + e.getMessage()); e.printStackTrace(); } } // 获取token public static void getToken(Boolean ForceUpdate) throws Exception { /* * * redis.clients * jedis * 3.9.0 * */ String redisKey = nsrsbh + "@" + username + "@TOKEN"; if (ForceUpdate) { /* * 获取授权Token文档 * * @see https://fa-piao.com/doc.html#api1?source=gitee */ ApiResponse authResponse = client.getAuthorization(nsrsbh, type); // ApiResponse authResponse = client.getAuthorization(nsrsbh,type,username,password); if (authResponse.isSuccess()) { token = authResponse.getData().getToken(); redisClient.setex(redisKey, 3600 * 24 * 30, token); // 设置30天过期 client.setAuthorization(token); } } else { // 从Redis获取Token String token = redisClient.get(redisKey); if (token == null || token.isEmpty()) { ApiResponse authResponse = client.getAuthorization(nsrsbh, type); // ApiResponse authResponse = client.getAuthorization(nsrsbh,type,username,password); if (authResponse.isSuccess()) { token = authResponse.getData().getToken(); redisClient.setex(redisKey, 3600 * 24 * 30, token); // 设置30天过期 client.setAuthorization(token); } }else{ client.setAuthorization(token); } } } // 蓝票 public static ApiResponse> blueTicket() throws Exception { /* * * 开票税额计算说明demo * * @see * https://github.com/fapiaoapi/invoice-sdk-java/blob/master/examples/TaxExample * .java */ Map invoiceParams = new LinkedHashMap<>(); invoiceParams.put("fplxdm", "82"); invoiceParams.put("fpqqlsh", appKey + System.currentTimeMillis()); invoiceParams.put("ghdwmc", "张四"); // invoiceParams.put("ghdwsbh", "914208XXXXXXX"); invoiceParams.put("hjje", 9.9); invoiceParams.put("hjse", 0.1); invoiceParams.put("jshj", 10); invoiceParams.put("kplx", 0); invoiceParams.put("username", username); invoiceParams.put("xhdwdzdh", xhdwdzdh); invoiceParams.put("xhdwmc", title); invoiceParams.put("xhdwsbh", nsrsbh); invoiceParams.put("xhdwyhzh", xhdwyhzh); invoiceParams.put("zsfs", 0); invoiceParams.put("fyxm[0][fphxz]", 0); invoiceParams.put("fyxm[0][spmc]", "*玩具*益智玩具"); // invoiceParams.put("fyxm[0][ggxh]", ""); // invoiceParams.put("fyxm[0][dw]", "个"); // invoiceParams.put("fyxm[0][spsl]", 1); // invoiceParams.put("fyxm[0][dj]", 10); invoiceParams.put("fyxm[0][je]", 10); invoiceParams.put("fyxm[0][sl]", 0.01); invoiceParams.put("fyxm[0][se]", 0.1); invoiceParams.put("fyxm[0][hsbz]", 1); invoiceParams.put("fyxm[0][spbm]", "1060408990000000000"); /* * 开具数电发票文档 * @see https://fa-piao.com/doc.html#api6?source=gitee * */ return client.blueTicket(invoiceParams); } // 发票下载 public static void downloadPdfOfdXml(String fphm,String kprq) throws Exception { System.out.println("发票号码: " + fphm); System.out.println("开票日期: " + kprq); /* * 获取销项数电版式文件 * * @see https://fa-piao.com/doc.html#api7?source=gitee * */ Map pdfParams = new HashMap<>(); pdfParams.put("downflag", "4"); pdfParams.put("nsrsbh", nsrsbh); pdfParams.put("username", username); pdfParams.put("fphm", fphm); pdfParams.put("Kprq", kprq); ApiResponse> pdfResponse = client.getPdfOfdXml(pdfParams); if (pdfResponse.isSuccess()) { System.out.println("发票下载成功"); System.out.println(pdfResponse.getData()); } } // 生成二维码图片base64 public static String toBase64(String text, int size) { try { BitMatrix matrix = new QRCodeWriter().encode(text, BarcodeFormat.QR_CODE, size, size); ByteArrayOutputStream out = new ByteArrayOutputStream(); MatrixToImageWriter.writeToStream(matrix, "PNG", out); return Base64.getEncoder().encodeToString(out.toByteArray()); } catch (Exception e) { throw new RuntimeException("生成二维码失败", e); } } // 字符串转二维码 在命令行输出 public static void stringToQrcode(String content) { try { ErrorCorrectionLevel ecLevel = content.length() > 50 ? ErrorCorrectionLevel.L : ErrorCorrectionLevel.M; Map hints = new HashMap<>(); hints.put(EncodeHintType.CHARACTER_SET, "UTF-8"); hints.put(EncodeHintType.ERROR_CORRECTION, ecLevel); hints.put(EncodeHintType.MARGIN, 0); BitMatrix matrix = new QRCodeWriter().encode(content, BarcodeFormat.QR_CODE, 1, 1, hints); int size = matrix.getWidth(); int maxModules = 52; int scale = Math.max(1, (int) Math.ceil((double) size / maxModules)); int renderedSize = (int) Math.ceil((double) size / scale); System.out.println("请扫描下方二维码 (内容长度: " + content.length() + ", 原尺寸: " + size + "x" + size + ", 输出尺寸: " + renderedSize + "x" + renderedSize + ")"); renderQrCompact(matrix, scale); System.out.println("扫描结束"); } catch (WriterException e) { e.printStackTrace(); } } public static void renderQrCompact(BitMatrix matrix, int scale) { int size = matrix.getWidth(); int colIndex; for (int y = 0; y < size; y += scale * 2) { StringBuilder line = new StringBuilder(); colIndex = 0; for (int x = 0; x < size; x += scale) { boolean topBlack = hasBlackInBlock(matrix, x, y, scale); boolean bottomBlack = hasBlackInBlock(matrix, x, y + scale, scale); boolean useNarrowCell = (colIndex + 1) % 5 == 0; if (topBlack && bottomBlack) { line.append(useNarrowCell ? "█" : "██"); } else if (topBlack) { line.append(useNarrowCell ? "▀" : "▀▀"); } else if (bottomBlack) { line.append(useNarrowCell ? "▄" : "▄▄"); } else { line.append(useNarrowCell ? " " : " "); } colIndex++; } System.out.println(line); } } public static boolean hasBlackInBlock(BitMatrix matrix, int startX, int startY, int scale){ int width = matrix.getWidth(); if (startX >= width || startY >= width) { return false; } int endX = Math.min(startX + scale, width); int endY = Math.min(startY + scale, width); for (int y = startY; y < endY; y++) { for (int x = startX; x < endX; x++) { if (matrix.get(x, y)) { return true; } } } return false; } } ``` [发票红冲demo](src/main/java/tax/invoice/example/RedInvoiceExample.java "发票红冲demo") [发票税额计算demo](src/main/java/tax/invoice/example/TaxExample.java "发票税额计算demo") [扫码登录demo](src/main/java/tax/invoice/example/FaceLoginExample.java "扫码登录demo") [先验证后开发票命令行demo](src/main/java/tax/invoice/example/InvoiceExample.java "先验证后开发票命令行demo")