本文目录导读:

- 核心概念:你处理的是“XML”或“OFD”
- 场景一:作为“收票方”(报销/入账)
- 场景二:作为“开票方”(对接税局平台)
- 关键库与工具推荐(PHP生态)
- 避坑指南(实战经验)
- 总结代码框架(收票方入库)
- 实现建议
在 PHP 中处理电子发票(尤其是中国的全面数字化的电子发票,即“数电票”),通常不涉及生成发票(由税务系统生成),而是围绕获取、解析、验签、查验、归集这几个核心环节。
根据你的业务场景(是作为开票方对接税局,还是作为收票方处理报销),方案截然不同,以下是全流程的技术指南:
核心概念:你处理的是“XML”或“OFD”
中国的电子发票(数电票)底层是板式文件(OFD/PDF)和结构化数据文件(XML)。
- OFD:国标版式文件(类似于PDF),用于打印和预览。
- XML:包含发票全部结构化数据(抬头、金额、税额、购销方信息),是财务系统入账和验签唯一法定依据。
PHP处理电子发票,本质上是处理 XML字符串 和 OFD压缩包。
场景一:作为“收票方”(报销/入账)
这是最常见的需求(企业内部报销系统、费控系统),你需要解析用户上传的电子发票。
A. 解析 XML(数电票)
数电票的XML文件可以直接通过PHP的 simplexml_load_string() 解析。
<?php
$xmlContent = file_get_contents('invoice.xml');
$xml = simplexml_load_string($xmlContent);
// 注册命名空间(数电票的命名空间是 urn:gb:266:2022-1)
$xml->registerXPathNamespace('inv', 'urn:gb:266:2022-1');
// 提取关键字段
$invoiceCode = $xml->xpath('//inv:InvoiceCode');
$totalAmount = $xml->xpath('//inv:TotalAmount');
$buyerName = $xml->xpath('//inv:BuyerName');
$sellerName = $xml->xpath('//inv:SellerName');
// 提取明细
$items = $xml->xpath('//inv:ItemList/inv:Item');
foreach ($items as $item) {
$name = $item->xpath('//inv:Name')[0];
$price = $item->xpath('//inv:Price')[0];
// ... 循环获取
}
?>
B. 解析 OFD(关键点:OFD 是 ZIP 压缩包)
OFD 文件本质是一个 ZIP 压缩包,核心的发票数据在 OFD.xml 文件中。
- 技术方案:推荐使用
ZipArchive类(需开启php_zip扩展)。
<?php
$zip = new ZipArchive;
if ($zip->open('invoice.ofd') === TRUE) {
// 1. 读取 OFD.xml (包含文件清单)
$ofdXml = $zip->getFromName('OFD.xml');
// 2. 解析OFD.xml,找到Document/XML的文件路径,通常是 Doc_0/Document.xml
// 简易写法(需查具体路径):
$docXml = $zip->getFromName('Doc_0/Document.xml');
// 3. 用 SimpleXML 解析 $docXml,提取发票数据 (同上XML解析)
$invoiceXml = simplexml_load_string($docXml);
$zip->close();
}
?>
C. 发票验真(防伪)
重要安全提示:仅解析XML不够,必须验证发票是否被篡改。
- 国家公示平台:调用
国家税务总局全国增值税发票查验平台的接口(企业需申请接口权限)。 - 本地验签:数电票的XML末尾包含
Signature字段(数字签名),你可以使用openssl_verify()配合税局公布的CA根证书进行验签。// 伪代码示例 $publicKey = openssl_pkey_get_public('file://path/to/ca.cer'); $signature = base64_decode($xml->SignatureValue); $dataToVerify = $xml->CanonicalizedContent; // 需要按国标算法规范化 $result = openssl_verify($dataToVerify, $signature, $publicKey, OPENSSL_ALGO_SHA256); if ($result === 1) { // 验签成功 }
场景二:作为“开票方”(对接税局平台)
如果你是电商平台或企业系统,需要给客户自动开票。
技术路线:
- 税局接口对接:需要向当地税务局申请“全电发票试点”接入资格,获取AppKey/Secret(密钥)。
- 组装XML:按照国家标准,组装开票XML报文(包含销方、购方、明细、价税分离)。
- 数字签名:使用保险库(UKey)或云证书,使用
openssl_sign()对报文进行签名。 - HTTP通信:使用
cURL向税局公共服务平台发送请求,获取“开票成功”的回执(内含OFD文件和XML文件)。
// 签名示例
$privateKey = openssl_pkey_get_private('file://private_key.pem', 'password');
$data = $requestXml; // 待签名的XML
openssl_sign($data, $signature, $privateKey, OPENSSL_ALGO_SHA256);
$signatureBase64 = base64_encode($signature);
// 之后将 $data 和 $signatureBase64 打包发给税局。
关键库与工具推荐(PHP生态)
不要重复造轮子,以下库能极大提高效率:
- FPDI / TCPDF:用于在PDF/OFD基础上添加水印或生成打印版PDF(通常只用于预览效果)。
- PHPOffice/PhpSpreadsheet:如果你需要将发票明细导出为Excel对账。
- 只读OFD解析:目前没有特别成熟的纯PHP库专门解析OFD(因为OFD在中国使用较多),多数情况下直接用ZipArchive提取XML即可,不需要复杂的图形渲染库。
- Validation:可使用
symfony/validator校验发票代码、金额等格式。
避坑指南(实战经验)
① 关于金额精度
绝对禁止直接使用浮点数 float 计算金额,必须使用 字符串 或 BCMath 函数。
$amount1 = "100.00";
$amount2 = "13.00";
$tax = bcadd($amount1, $amount2, 2); // "113.00"
// 判断是否相等
if (bccomp($total, $sumOfItems, 2) === 0) {
// 平账
}
② 关于编码
- 发票XML的默认编码是 UTF-8,如果包含生僻字或特殊符号(如“①”、“※”),要确保你的数据库连接和文件读取都使用 UTF-8MB4。
③ 关于OFD的坑
- 有些OFD文件结构复杂(多附件、嵌套目录),不要硬编码路径,必须解析
OFD.xml中的FilePath来动态定位Document.xml。
④ 关于状态管理
- 如果对接税局,开票通常不是同步返回的(有异步回调),你的系统需要有定时任务(Cron Job)去轮询开票状态或接收回调Webhook。
总结代码框架(收票方入库)
public function importInvoice($filePath) {
$extension = pathinfo($filePath, PATHINFO_EXTENSION);
$data = [];
if ($extension === 'ofd') {
$zip = new ZipArchive();
$zip->open($filePath);
// 关键:找到Document.xml
$documentPath = $this->findDocumentXml($zip); // 自定义查找方法
$xmlString = $zip->getFromName($documentPath);
$zip->close();
} else { // xml or pdf
// PDF需要先提取OCR或解析水印,通常不建议直接解析PDF获取数据,应要求提供XML或OFD
return false;
}
// 解析
$invoicData = simplexml_load_string($xmlString);
$data['invoice_code'] = (string)$invoicData->InvoiceCode;
$data['total_amount'] = (string)$invoicData->TotalAmount;
// ... 存储到你的数据库
// 3. 校验(调用国家平台或本地验签)
// $isValid = $this->verify($invoicData);
return $data;
}
实现建议
- 确定你的税务属地:使用哪个省的电子税务局接口(如广东、上海等),接口文档略有差异。
- 加入发票查验API:如果是企业内部报销,强烈建议对接第三方或税局的查验API,防止PS过的PDF或黑客伪造的XML通过审核。
- 不要用PDF解析数据:PDF仅供人类阅读,机器解析应以OFD或XML为准。
如果你需要处理的是数电票(全电发票),我还可以协助你细化具体的XML字段含义(如如何区分专票与普票、如何识别“原名”与“数电票”),欢迎提供更多背景细节。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播