自托管开票软件怎么选:Invoice Ninja等5款对比
对比 Invoice Ninja、InvoiceShelf、InvoicePlane、Kimai 和 ERPNext 在单台 VPS 上的周期性计费、税费、客户门户、支付网关、许可证与备份风险。
应选择哪款自托管开票软件?
在一台 VPS 上部署自托管开票软件,实际可选的主要有 5 款,而且它们并不是相近的替代品。Invoice Ninja 是功能最完善的产品:周期性发票、客户门户、支付网关和电子发票格式在安装当天即可使用。InvoiceShelf 是功能更精简、完全开源的选项。InvoicePlane 是一个普通的 PHP 应用,无需编排其他组件。Kimai 用于结算已记录的工时,而不是发票行项目。ERPNext 是完整的会计系统,同时也能生成发票。
两个问题导致自托管开票系统失效的情况,远多于缺少某项功能。第一个是电子邮件。发票进入垃圾邮件文件夹后,就成了未付款发票,而且你不会收到任何提示。第二个是备份。这是唯一一种数据丢失后会引发税务问题,而不只是带来不便的自托管数据库。
先阅读许可证,再查看功能列表。这 5 款软件中,有 3 款采用著作权左派许可证,1 款采用宽松许可证,另 1 款仅提供源代码,并且对使用方式设有实际限制。
五个选项及其适用对象
Invoice Ninja
这是一个 Laravel 应用,目前处于 v5 版本线。自托管版本免费且功能不受限,支持周期性发票、报价单、项目、时间跟踪、费用、客户门户,以及 Stripe 和 PayPal 等支付网关。该项目将自身称为“使用 Laravel 构建的、源代码可用的发票、报价单、项目和时间跟踪应用”。唯一收费部分是品牌标识。按照项目方的说法:“我们提供每年 $40 的白标许可证,用于移除应用面向客户部分中的 Invoice Ninja 品牌标识。”截至 August 2026,该价格仍然有效。
如果您需要功能最完整的产品,并且可以接受运行规模最大的技术栈,请选择它。
InvoiceShelf
这是 Crater 的一个分支,采用 GNU Affero General Public License version 3(AGPL-3.0)授权。它支持发票、估算单、周期性计费、费用、税费、多币种、客户门户和多公司。支付功能和白标功能分别位于官方模块仓库中;两个模块都采用 AGPL-3.0 授权,并且免费。银行卡支付通过 Stripe checkout 完成。
如果您需要一个小型、完全开源的应用,并且不需要三十个支付网关,请选择它。
InvoicePlane
这是一个基于 CodeIgniter 的 PHP 应用,采用 MIT 许可证发布,也是这里唯一可以通过将文件解压到 web 根目录来安装的选项。不需要 Docker,也不需要 Node 构建步骤。PDF 由 mPDF 生成,因此模板使用 HTML 和 CSS。客户可以通过访客链接查看并支付发票。
问题在于支付功能。文档明确说明,InvoicePlane 1.6 默认只支持 Stripe,该版本移除了其他支付提供商。1.6 版本线公布的要求是 PHP 8.0 至 8.1,以及 MySQL 5.5 或对应版本的 MariaDB。请阅读所下载版本的说明,因为 1.7 版本线仍在持续开发。
如果您使用共享主机或 512 MB VPS,请选择它。
Kimai
这是一个采用 AGPL-3.0 授权的时间跟踪工具,可以根据工时表生成发票。对于按小时收费的用户,这是正确的选择。发票模板以 DOCX、ODS 或 XLSX 文件上传;文档确认这三种格式是唯一接受的格式,之后模板会被渲染为 PDF 或 HTML。您可以使用文字处理器排版发票,而不是编写 CSS,这对大多数人来说更容易。
Kimai 没有客户门户,也没有支付网关,因此您需要通过电子邮件发送 PDF。结构化电子发票功能由名为 E-Invoice Plus 的付费商店插件提供。截至 August 2026,自托管安装的价格为每年 99 EUR(不含 VAT),支持 EN16931、XRechnung、ZUGFeRD、Factur-X 和 Peppol 配置文件。
如果发票只是跟踪工时后的输出,请选择它。
ERPNext
它采用 GPL-3.0 授权,属于另一类软件。销售发票只是完整会计系统中的一种单据类型;该系统还包括会计科目表、日记账分录、库存和工资管理。当开票不再是全部工作内容时,它才是正确的选择。对于只有八个客户的自由职业者来说,它包含的功能过多。部署 ERPNext 本身就是一个项目,详见 在 VPS 上运行 ERPNext。
周期性发票依赖一个您看不到的调度器
Invoice Ninja、InvoiceShelf、InvoicePlane 和 ERPNext 都可以生成周期性发票。Kimai 的核心功能不支持周期性发票,因为它会根据某个时间段内的工时表条目生成发票。
所有基于 Laravel 的应用都会以相同方式静默失败。周期性发票由计划任务 php artisan schedule:run 创建,该任务必须由应用外部的组件每分钟调用一次。如果没有组件调用它,周期性发票列表仍会显示下一次发送日期,但该日期会直接过去。不会生成发票。也不会记录错误,因为根本没有代码运行。
因此,创建第一张周期性发票后,将其下一次发送日期设置为几分钟后,然后等待。如果没有收到发票,说明调度器没有运行。在 Docker 安装中,这通常表示 compose 文件中的 cron 或 scheduler 服务从未启动,或者已经退出。
docker compose ps
docker compose logs --tail 50文件中的每个服务都应报告 running。处于 exited 状态的服务就是需要读取其日志的服务。
多币种和税务:提交承诺前需要检查什么
功能列表中的“多币种”可能代表完全不同的功能。请在演示实例中执行以下4项检查,并使用您业务中的真实金额。因为这类错误通常是由会计师发现,而不是由您自己发现。
- 发票是否保存了开具日期当天适用的汇率?必须保存。如果应用根据实时汇率重新计算总额,您每次打开上一季度的报表时,报表数据都会发生变化,账簿也会与已经申报的记录不一致。
- 税费是按行项目、按发票,还是两者都支持?在某一行适用零税率之前,统一的发票级税率通常没有问题。
- 是否既可以录入含税价格,也可以录入不含税价格?事后转换会使每个行项目产生几分钱的舍入差异,而这些差异会在一年中累积。
- 对于来自其他国家且无需缴纳 VAT 的客户,单据会显示什么内容?发票上必须打印免税说明,不能只在税费列中显示0。
Invoice Ninja 和 ERPNext 支持以上4项功能。InvoiceShelf 将税费和多币种列为其功能。InvoicePlane 和 Kimai 更简单,因此请使用您最复杂的发票进行测试,而不要只测试最简单的发票。
客户门户和支付网关
客户门户是客户查看发票、下载 PDF 并付款的页面。它的重要性超出许多人的预期,因为通过电子邮件催款所花的时间,可能比制作发票还多。
Invoice Ninja 在这方面最强,提供完整的客户门户、多个支付网关、已保存的支付方式,以及针对周期性发票的自动扣款。InvoiceShelf 提供客户门户,并通过其支付模块支持 Stripe checkout。InvoicePlane 为每张发票提供访客链接,并支持 Stripe。Kimai 两者都不支持,因此只能将 PDF 作为附件发送。
有一点经常被混淆。自行托管开票应用并不意味着由你保存银行卡号。使用 Stripe checkout 时,客户会重定向到 Stripe,并在那里输入卡号;你的 VPS 从未接触该号码。你的 PCI(支付卡行业)合规范围之所以较小,正是因为你没有自行构建支付表单。如果以后需要接入多个支付处理商,或希望按国家路由支付,则应将其放在单独的层中,例如 使用 Hyperswitch 自行托管支付编排。
PDF 质量以及最终需要编辑的模板
您需要编辑该模板。这里的每个默认设置都可能使地址出现在不符合您所在国家/地区要求的位置。
Invoice Ninja 使用镜像中捆绑的无头 Chromium 将 HTML 和 CSS 模板渲染为 PDF,因此容器体积较大。请为此预留内存:无头浏览器是整个技术栈中占用资源最多的进程,因此 1 GB VPS 的内存空间比较紧张。InvoicePlane 使用 mPDF。mPDF 采用纯 PHP 实现,资源占用较低,但会忽略部分现代 CSS。Kimai 采用相反的方式,要求您上传 DOCX 或 ODS 文件,然后由不一定编写过样式表的人员使用 LibreOffice 或 Word 完成布局。
如果发票的设计对您很重要,而 CSS 不是重点,那么 Kimai 的方式更合适。如果您希望将模板纳入版本控制并支持差异比较,那么 HTML 更合适。
许可证是否允许商业使用?
这 5 个项目都允许您向自己的客户开具账单。只有在进行除此之外的操作时,差异才会出现。
Invoice Ninja 使用 Elastic License 2.0。以下两项条款很重要。
您不得将该软件作为托管服务或管理服务提供给第三方,前提是该服务允许用户访问该软件的大部分功能或功能集。
您不得移动、更改、禁用或绕过软件中的许可证密钥功能。
因此,为自己的企业运行它没有问题,但不能将为客户运行的实例作为产品提供。第二项条款也是去除品牌标识需要购买 $40 许可证,而不是直接修改源代码的原因。
InvoiceShelf、Kimai 和 Crater 使用 AGPL-3.0。商业使用免费,也没有更高的许可等级。相关义务位于第 13 节:如果您修改代码,并通过网络让其他人使用修改后的版本,就必须向他们提供您所做修改的源代码。未修改的实例已经由上游公开源代码覆盖,因此大多数自行托管用户不会遇到这个问题。
InvoicePlane 使用 MIT,是这一组中限制最少的许可证。不过,其 README 说明名称和徽标仍归项目所有。
ERPNext 使用 GPL-3.0。您分发的修改版本必须共享源代码,而私下运行它不产生这项义务。
以上内容是对许可证文本的说明,不构成法律建议。
项目是否仍在维护?检查提交日志
Crater 多年来一直是热门的开源发票应用,许多指南至今仍在推荐它。其默认分支已有 741 天没有提交。
The data behind this chart
[
{
"label": "ERPNext",
"commit_gap": 0
},
{
"label": "Invoice Ninja",
"commit_gap": 1
},
{
"label": "Kimai",
"commit_gap": 1
},
{
"label": "InvoicePlane",
"commit_gap": 3
},
{
"label": "InvoiceShelf",
"commit_gap": 4
},
{
"label": "Crater",
"commit_gap": 741
}
]该区块中的 6 个项目里,只有五个项目在过去一周内有提交。InvoiceShelf 是延续 Crater 开发的 fork,采用相同的 AGPL-3.0 许可证和相同的数据模型,因此迁移过去属于数据迁移,而不是重写应用。请安装 InvoiceShelf,不要安装 Crater。
在 VPS 上分别安装
除 InvoicePlane 外,其他软件使用 Docker Compose 运行最方便。如果你不熟悉 Docker Compose,请先阅读在 VPS 上创建第一个 Docker Compose 堆栈,然后再回来继续。
Invoice Ninja 提供了自己的 Compose 仓库。
git clone https://github.com/invoiceninja/dockerfiles.git
cd dockerfiles
docker run --rm -it invoiceninja/invoiceninja php artisan key:generate --show将该密钥复制到仓库中的 env 文件,并填入真实的 APP_URL。然后设置镜像所需的所有权并启动服务。
chmod 755 docker/app/public
sudo chown -R 1500:1500 docker/app
docker compose up -d1500:1500 的所有权与镜像内的用户一致。如果跳过这一步,应用将无法写入缓存或生成的 PDF。在设置向导中,将 db 指定为数据库主机,因为这是 Compose 网络中的服务名称。这里的 localhost 指向应用容器本身。
InvoiceShelf 按数据库引擎分别提供了可直接使用的 Compose 文件。
curl -fLO https://raw.githubusercontent.com/InvoiceShelf/docker/master/docker-compose.mysql.yml
mv docker-compose.mysql.yml docker-compose.yml启动前先打开该文件。文件中的所有密码都是占位符,文件注释中也明确说明了这一点。APP_URL、SESSION_DOMAIN 和 SANCTUM_STATEFUL_DOMAINS 默认都为 localhost,首次登录前必须为这三项设置真实主机名。然后启动服务。
docker compose up -d它会在主机上发布 8090 端口,并在容器内提供 8080 端口。在将任何客户端连接到它之前,先在其前面配置使用 TLS(传输层安全)的反向代理。
来自项目文档的 Kimai 生产环境 Compose 文件
services:
sqldb:
image: mysql:8.3
volumes:
- mysql:/var/lib/mysql
environment:
- MYSQL_DATABASE=kimai
- MYSQL_USER=kimaiuser
- MYSQL_PASSWORD=kimaipassword
- MYSQL_ROOT_PASSWORD=changemeplease
command: --default-storage-engine innodb
restart: unless-stopped
healthcheck:
test: mysqladmin -uroot -p$$MYSQL_ROOT_PASSWORD ping -h 127.0.0.1 --silent
interval: 10s
timeout: 5s
retries: 12
start_period: 30s
kimai:
image: kimai/kimai2:stable
depends_on:
sqldb:
condition: service_healthy
restart: true
volumes:
- data:/opt/kimai/var/data
- plugins:/opt/kimai/var/plugins
ports:
- 8001:8001
environment:
- APP_SECRET=your-fantastic-long-random-and-ultra-secure-secret
- TRUSTED_HOSTS=kimai.example.com
- ADMINMAIL=admin@example.com
- ADMINPASS=changemeplease
- DATABASE_URL=mysql://kimaiuser:kimaipassword@sqldb/kimai?charset=utf8mb4&serverVersion=8.3.0
restart: unless-stopped
volumes:
data:
mysql:
plugins:将 APP_SECRET 设置为你自行生成的长随机字符串,将 TRUSTED_HOSTS 设置为实际使用的域名。首次启动时,ADMINMAIL 和 ADMINPASS 会创建第一个管理员账户,因此之后应在界面中修改该密码,并将这两项从文件中删除。
InvoicePlane 没有容器。先创建一个空数据库。
sudo mysql -e "CREATE DATABASE invoiceplane CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"从项目的发布页面下载压缩包,将其解压到 Web 根目录,把 ipconfig.php.example 复制为 ipconfig.php,然后按照该文件中的注释在其中设置 URL。接着打开 https://your-domain.example/index.php/setup 并完成安装程序。
为什么您的发票邮件会进入垃圾邮件文件夹
这是会直接造成收入损失的故障,而且应用仍会正常报告发票已发送。
不要使用 VPS 自身的 IP 地址发送发票邮件。新的 VPS IP 没有发送历史记录,大型邮件服务商通常会默认将托管网段中的未知地址发送的第一封邮件视为可疑邮件。请通过您已完成身份验证的域名上的 SMTP(简单邮件传输协议)中继发送。端到端完成此配置需要单独的指南,从自托管应用可靠发送邮件介绍了中继、DNS 记录和测试。
大多数开票配置的问题都出在 From 地址上。该地址必须属于您控制的域名,并且您的中继必须使用 DKIM(域密钥识别邮件)为该域名签名。将客户自己的地址放入 From 看似礼貌,但会导致投递失败,因为接收服务器会检查 From 域名是否与对邮件进行身份验证的域名匹配。Gmail 会直接拒绝不匹配的邮件:
550 5.7.1 Unauthenticated email from example.com is not accepted due to domain's DMARC policy将您自己的地址保留在 From 中,并将客户地址放入 Reply-To。发送真实邮件前,先检查已发布的记录。
dig +short TXT example.com
dig +short TXT _dmarc.example.com
dig +short TXT selector1._domainkey.example.com第一条命令应返回 SPF(发件人策略框架)记录,并指定您的中继。第二条命令应返回 DMARC(基于域的邮件身份验证、报告和一致性)策略。第三条命令取决于中继提供的 selector;如果返回空结果,表示 DKIM 记录缺失,或记录发布在错误的名称下,因此您发送的每封邮件都会在签名检查时失败。
然后向您在大型邮件服务商处拥有的账户发送一封真实发票,并读取原始邮件头。Authentication-Results 头中的 dkim=pass 和 spf=pass 才是唯一有效的证明。应用自身的“已发送”标志只表示它已将邮件交给中继。
备份:不可丢失的唯一数据库
媒体服务器丢失后,您只需重新下载文件。发票数据库丢失后,您将无法证明开具过哪些发票、已收取哪些款项,或仍有多少款项待收。请按同等重要性处理。
请备份以下4项:数据库;存储卷,其中包含徽标、生成的 PDF 和费用收据;应用的环境文件;以及以普通文件保存的已生成 PDF,因为即使没有生成它的应用,PDF 在10年后仍可打开。
环境文件的重要性往往超出预期。Laravel 使用该文件中的 APP_KEY 加密敏感存储值,包括支付网关凭据。将数据库恢复到使用新生成的密钥的环境中后,这些值将无法解密。您的发票仍然完整,但网关设置无法读取。
InvoiceShelf 堆栈的转储命令如下。其数据库服务运行在 mariadb:10,因此容器内使用的工具是 mariadb-dump。
docker compose exec -T database mariadb-dump \
--user=root --password=YOUR_ROOT_PASSWORD \
--single-transaction --databases invoiceshelf > invoiceshelf.sql--single-transaction 会在不锁定 InnoDB 表的情况下创建一致性快照,因此转储期间应用仍可继续提供服务。-T 会阻止 Docker 分配终端,否则终端可能破坏重定向输出,生成的转储文件也将无法导入。
按计划将备份发送到服务器之外,使用加密的 restic 异地备份,并将保留期限设置为足够覆盖您的会计师指定的期间。
然后执行恢复。将转储导入临时容器,登录并打开上个月的发票。在您至少完成一次此操作前,无法确认备份是否有效。
会计师会要求提供的资料
不要根据教程获取税务建议。下面列出的是此软件可以生成的内容,您可以将这份清单交给为您提供建议的专业人士。
可导出的数据。 您应该可以直接在界面中将发票和付款记录导出为 CSV,无需使用数据库客户端,也无需提交支持工单。SQL 转储是备份,不是导出文件,因为您的会计师无法直接打开它。
不可变的编号。 发票编号应按顺序递增,不能重复使用,也不应出现间隔。删除发票会留下编号间隔,之后必须对此作出解释。应改用取消或贷项处理,这里的每个应用都支持这些操作。您是否必须使用无间隔编号,取决于您的注册地,因此最好尽早确认。
保留期限。 按照适用规定要求的期限,保留数据库转储和生成的 PDF。PDF 是实际发送出去的文件;如果发票发生争议,这份文件才是关键凭证。
变更历史。 能够显示谁在何时编辑了发票非常有价值,而这也是这些应用之间差异最大的功能。Invoice Ninja 会为每条记录保留活动日志。
在欧盟部分地区,结构化电子发票已经成为实际要求,而不再只是可选功能。Invoice Ninja 可生成常见格式,包括 ZUGFeRD 和 XRechnung、Facturae、FatturaPA 以及通用 EN16931。通过 Peppol 网络从自托管实例发送时,需要使用 Invoice Ninja 自有的接入点:您需要向其注册企业实体,并按每次提交购买额度;截至 2026 年 8 月,这是一项付费服务。Kimai 通过其付费插件支持相同的格式。其他三个应用目前完全不能生成结构化电子发票。如果您在欧盟境内开具发票,这是选择 Invoice Ninja 的最重要理由。
FAQ
我应该安装 Crater 还是 InvoiceShelf?
InvoiceShelf。截至 21 August 2026,Crater 的默认分支已经 741 天没有提交,其自身的 issue tracker 也引导用户使用该 fork。InvoiceShelf 使用相同的 AGPL-3.0 许可证和相同的数据模型,因此两者之间的迁移是数据库迁移,而不是重写应用。仍然推荐 Crater 的指南,都是在该项目停止活跃之前编写的。
我可以免费将 Invoice Ninja 用于自己的企业吗?
可以。自行托管的 Invoice Ninja 在 Elastic License 2.0 下可免费使用全部功能,包括定期发票、客户门户和支付网关。有两项内容需要付费。要从面向客户的页面中移除 Invoice Ninja 品牌,需要购买白标许可证;按该项目截至 August 2026 的说法,价格为“$40 per year”。通过 Peppol 网络发送电子发票需要单独按量计费。该许可证禁止的行为,是将软件作为托管服务提供给其他人。
为什么我的发票邮件会进入垃圾邮件文件夹?
几乎总是因为邮件没有通过 From 地址所在域名的身份验证。不要直接从 VPS 发送,而应通过 SMTP relay 发送;然后为域名发布 SPF 和 DMARC 记录,并在 relay 指定的 selector 下添加 DKIM 记录。将自己的地址保留在 From 中,并将客户地址放入 Reply-To。将客户地址设为发件人会使 Gmail 返回 550 5.7.1 Unauthenticated email from example.com is not accepted due to domain's DMARC policy 并拒收邮件。在测试邮件的 Authentication-Results 标头中找到 dkim=pass 和 spf=pass,以确认修复成功。
如果我按小时计费,应该选择哪一个?
Kimai。它首先是时间跟踪工具,因此发票根据已批准的工时表条目生成,明细项就是你记录的工作内容。模板使用 DOCX、ODS 或 XLSX 文件,你可以在文字处理器中设计模板;这比编辑 CSS 更容易。代价是 Kimai 没有客户门户和支付网关,因此你需要通过电子邮件发送 PDF,并通过转账收款。Invoice Ninja 也支持时间跟踪,并且提供客户门户;如果在线刷卡支付比工时表细节更重要,应改选 Invoice Ninja。
我究竟需要备份哪些内容?
数据库、保存徽标和生成的 PDF 的存储卷、环境文件,以及以普通文件形式保存的已渲染 PDF。环境文件最容易被遗漏:Invoice Ninja 和 InvoiceShelf 等 Laravel 应用使用 APP_KEY 加密存储的凭据,因此将数据库恢复到使用新生成密钥的环境中,会导致支付网关设置无法读取。将整套数据恢复到一次性实例中,并打开一张旧发票,因为只有这样才能确认备份确实有效。