kylin987 / netsale-report-sdk
PHP SDK for cinema netsale box-office reporting (2025 protocol)
v1.0.0
2026-06-30 03:43 UTC
Requires
- php: >=8.1
- ext-curl: *
- ext-openssl: *
Requires (Dev)
- phpunit/phpunit: ^9.5
README
网售票房上报 SDK(2025 协议)— 用于专资办正式票房上报,包含售票 / 退票上报和数据比对文件下载。
功能
| 方法 | 说明 |
|---|---|
reportTicket |
票房数据上报(售票 operation=1 / 退票 operation=2),一次最多 50 条 |
downloadReportRecord |
数据比对文件下载(需 V-STK p7AttachSign 签名),返回 zip 文件流 |
环境要求
- PHP >= 8.1
ext-opensslext-curl- V-STK 签名服务(
downloadReportRecord需要,通过VstkSignerInterface接入)
安装
composer require kylin987/netsale-report-sdk
快速开始
<?php use NetsaleReportSdk\Config; use NetsaleReportSdk\Model\ReportTicket; use NetsaleReportSdk\NetsaleReportClient; // --- 配置 --- $config = new Config([ 'reportUrl' => 'https://your-host/report/report', 'serviceUrl' => 'https://your-host/service', 'channelCode' => 'your-channel-code', 'certId' => 'your-cert-id', 'certFile' => '/path/to/client_cert.pem', 'keyFile' => '/path/to/client_key.pem', 'trustFile' => '/path/to/rootcert.pem', 'vstkSigner' => new HttpVstkSigner('http://127.0.0.1:18080/sign'), ]); $client = new NetsaleReportClient($config); // --- 售票上报 --- $ticket = new ReportTicket(); $ticket->numberByDay = 1; $ticket->parentChannelCode = '13090401'; $ticket->childChannelCode = '00000000'; $ticket->ticketNo = '130904010Ba0102'; $ticket->cinemaCode = '13090401'; $ticket->screenCode = '0000000000000001'; $ticket->seatCode = '88888888010010011101'; $ticket->filmCode = '000000252022'; $ticket->sessionCode = 'SE00001234567890'; $ticket->sessionDatetime = '2026-03-31 21:20:00'; $ticket->ticketPrice = 56.00; $ticket->screenServiceFee = 0.00; $ticket->netServiceFee = 8.00; $ticket->saleChannelCode = 'your-channel-code'; $ticket->operation = 1; // 1=售票, 2=退票 $ticket->operationDatetime = '2026-03-31 21:20:00'; $result = $client->reportTicket([$ticket]); if ($result->isSuccess()) { echo '上报成功, traceId: ' . $result->traceId . PHP_EOL; } else { echo '上报失败: ' . $result->code . ' ' . $result->status . PHP_EOL; } // --- 退票上报(operation 改为 2) --- $ticket->operation = 2; $ticket->operationDatetime = date('Y-m-d H:i:s'); $client->reportTicket([$ticket]); // --- 数据比对文件下载 --- $zipContent = $client->downloadReportRecord('2026-03-01', '2026-03-31'); file_put_contents('report_record.zip', $zipContent);
配置参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
reportUrl |
string | 是 | 上报接口地址(SDK 自动追加 /reportTicket) |
serviceUrl |
string | 是 | 下载接口地址(SDK 自动追加 /data/downloadReportRecord) |
channelCode |
string | 是 | 网售商编码(sendChannelCode) |
certId |
string | 是 | 证书 ID(通常等于 channelCode,V-STK 签名时使用) |
certFile |
string | 是 | 客户端证书路径(PEM 格式) |
keyFile |
string | 是 | 客户端私钥路径(PEM 格式,与证书分离) |
keyFilePwd |
string | 否 | 私钥密码(PEM 加密时使用,通常为空) |
trustFile |
string | 否 | 信任根证书路径(CA 链 PEM) |
proxy |
string | 否 | 代理地址(不填时默认禁用系统代理) |
vstkSigner |
object | 下载时必填 | V-STK 签名器,需实现 VstkSignerInterface |
httpLogger |
callable | 否 | HTTP 日志回调,接收 array 上下文 |
reportToken |
string | 否 | 业务层上报令牌(SDK 不使用,供业务层读取) |
V-STK 签名器
downloadReportRecord 需要调用 V-STK 的 p7AttachSign 方法进行签名。PHP 无法直接调用 V-STK COM 组件,通过 VstkSignerInterface 接入外部签名服务。
接口定义
interface VstkSignerInterface { /** * @param string $certId 证书标识(通常为网售商编码) * @param string $plainText 签名原文(JSON 字符串) * @return string V-STK 返回的 Base64 签名字符串 */ public function p7AttachSign(string $certId, string $plainText): string; }
HTTP 签名桥实现示例
<?php use NetsaleReportSdk\Signer\VstkSignerInterface; class HttpVstkSigner implements VstkSignerInterface { public function __construct(private string $url) {} public function p7AttachSign(string $certId, string $plainText): string { $ch = curl_init($this->url); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([ 'certId' => $certId, 'plainText' => $plainText, ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_TIMEOUT, 30); $response = curl_exec($ch); if (curl_errno($ch)) { throw new RuntimeException('V-STK signer error: ' . curl_error($ch)); } $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if ($httpCode >= 400) { throw new RuntimeException("V-STK signer HTTP {$httpCode}: {$response}"); } $decoded = json_decode((string) $response, true); if (is_array($decoded) && ($decoded['ok'] ?? false) === true) { return (string) ($decoded['signData'] ?? ''); } return trim((string) $response); } }
V-STK 签名桥配置要点
- SM2 证书需设置
DigestAlg=SM3、EncAlg=SM4、asymmalg=SM2 - SSL 协议需
TLSv1.2 - PHP 端通过 HTTP 调用 Java 签名桥(如
cn.com.jit.new_vstk.AdvanceSignClient.p7AttachSign)
与 2025 协议的差异
与旧 zzb-sdk-php 的 netsale2025 模式相比:
reportTicket根级直接发送sendChannelCode+ticketList,不加业务签名downloadReportRecord需要 V-STKp7AttachSign签名- 证书只支持 PEM 格式(证书 + 私钥分离),不再处理 PFX/P12
联调脚本
包内附带冒烟测试脚本:
# dry-run(不实际请求) php examples/smoke.php # 真实上报 php examples/smoke.php --send-report --confirm-submit-test-data # 下载比对文件(需 V-STK 签名桥) php examples/smoke.php --download-record --vstk-signer-url=http://127.0.0.1:18080/sign
环境变量参见脚本头部注释。
数据模型
| 模型 | 说明 |
|---|---|
ReportTicket |
上报票据(numberByDay, ticketNo, cinemaCode, operation 等 16 个字段) |
ReportResult |
上报响应(code, status, data, traceId),isSuccess() 判断是否 code === '200' |
版本
| 版本 | 说明 |
|---|---|
| v1.0.0 | 从 zzb-sdk-php 2.x 提取 netsale2025 逻辑,独立发布 |
License
MIT