kylin987/zzb-sdk-php

PHP SDK for ZZB cinema, screen, film query APIs and ticket reporting

Maintainers

Package info

github.com/kylin987/zzb-sdk-php

pkg:composer/kylin987/zzb-sdk-php

Statistics

Installs: 8

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v1.0.1 2026-03-31 10:04 UTC

This package is auto-updated.

Last update: 2026-03-31 10:11:31 UTC


README

用于对接专资办接口的 PHP SDK,适配当前已验证可用的现网查询接口:

  • queryCinemaInfo
  • queryScreenInfo
  • queryFilmInfo
  • querySessionInfo
  • queryScreenSeatInfo
  • querySeatStatusInfo
  • lockSeatsInfo
  • releaseSeatsInfo
  • submitOrder
  • queryOrderInfo
  • refundTicket
  • reportTicketByLockOrderId
  • refundTicketByLockOrderId
  • refundReportTicketByLockOrderId

同时保留票房上报与数据比对文件下载能力。

Requirements

  • PHP >=8.1
  • ext-curl
  • ext-openssl

Installation

composer require kylin987/zzb-sdk-php

Supported Capabilities

  • 影院信息查询
  • 影厅信息查询
  • 影片信息查询
  • 影院排片信息查询
  • 影厅座位信息查询
  • 场次座位售出状态查询
  • 座位锁定
  • 座位解锁
  • 确认订单交易
  • 订单信息查询
  • 退票
  • 基于锁座订单号的售票上报
  • 基于锁座订单号的退票
  • 基于锁座订单号的退票上报
  • 票房上报
  • 比对文件下载

Quick Start

<?php

use ZzbSdk\Config;
use ZzbSdk\ZzbService;

$config = new Config([
    'serviceUrl' => 'https://your-host/serverapp',
    'reportUrl' => 'https://your-host/serverapp',
    'channelCode' => 'your-channel-code',
    'certId' => 'your-cert-id',
    'appId' => 'your-app-id',
    'interfaceKey' => 'your-interface-key',
    'certFile' => '/path/to/private_key.pem',
    'certFilePwd' => 'your-cert-password',
    'trustFile' => '/path/to/rootcert.pem',
    'version' => '1.0',
]);

$service = new ZzbService($config);
$result = $service->getCinemaInfo('your-cinema-code');

if ($result->isSuccess()) {
    var_dump($result->data);
}

API Examples

以下示例默认你已经准备好了同一份 $config$service

<?php

use ZzbSdk\Config;
use ZzbSdk\ZzbService;

$config = new Config([
    'serviceUrl' => 'https://your-host/serverapp',
    'reportUrl' => 'https://your-host/serverapp',
    'channelCode' => 'your-channel-code',
    'certId' => 'your-cert-id',
    'appId' => 'your-app-id',
    'interfaceKey' => 'your-interface-key',
    'certFile' => '/path/to/private_key.pem',
    'certFilePwd' => 'your-cert-password',
    'trustFile' => '/path/to/rootcert.pem',
    'version' => '1.0',
]);

$service = new ZzbService($config);

getCinemaInfo

<?php

$result = $service->getCinemaInfo('your-cinema-code');

if ($result->isSuccess()) {
    var_dump($result->data);
} else {
    var_dump($result->code, $result->status);
}

getScreenInfo

<?php

$result = $service->getScreenInfo('your-cinema-code');

if ($result->isSuccess()) {
    var_dump($result->data);
} else {
    var_dump($result->code, $result->status);
}

downloadFilmInfo

<?php

$result = $service->downloadFilmInfo('your-cinema-code', '2026-01-01', '2026-12-31', 1);

if ($result->isSuccess()) {
    var_dump($result->data);
} else {
    var_dump($result->code, $result->status);
}

querySessionInfo

<?php

$result = $service->querySessionInfo('your-cinema-code', '2026-03-31', '2026-03-31', 1);

if ($result->isSuccess()) {
    var_dump($result->data);
} else {
    var_dump($result->code, $result->status);
}

已验证可用参数示例:

<?php

$result = $service->querySessionInfo('your-cinema-code', '2026-04-01', '2026-04-02', 1);

已验证返回示例:

{
  "code": "200",
  "status": "success",
  "pageable": {
    "totalPages": 5,
    "page": 1,
    "size": 6
  },
  "data": {
    "cinemaCode": "your-cinema-code",
    "sessionList": [
      {
        "sessionCode": "your-session-code",
        "lowestPrice": 30.0,
        "standardPrice": 50.0,
        "netServiceFee": 2.0,
        "stopSellTime": "2026-04-02 08:00:00",
        "screenCode": "your-screen-code",
        "filmCode": "your-film-code",
        "filmName": "your-film-name",
        "sessionDatetime": "2026-04-02 08:00:00",
        "duration": 120,
        "layoutVersion": "座位图V1",
        "sectionList": [
          {
            "regionCode": "CQ1",
            "regionName": "一楼",
            "sectionCode": "FQ1",
            "sectionName": "普通区",
            "sectionPrice": 60.0,
            "screenServiceFee": 10.0
          }
        ]
      }
    ]
  }
}

querySeatStatusInfo

<?php

$result = $service->querySeatStatusInfo('your-cinema-code', 'your-session-code', ['LOCKED', 'SOLD']);

if ($result->isSuccess()) {
    var_dump($result->data);
} else {
    var_dump($result->code, $result->status);
}

已验证返回示例:

{
  "code": "200",
  "status": "success",
  "data": {
    "cinemaCode": "your-cinema-code",
    "sessionCode": "your-session-code",
    "seatList": []
  }
}

queryScreenSeatInfo

<?php

$result = $service->queryScreenSeatInfo('your-cinema-code', 'your-screen-code');

if ($result->isSuccess()) {
    var_dump($result->data);
} else {
    var_dump($result->code, $result->status);
}

lockSeatsInfo

<?php

$result = $service->lockSeatsInfo(
    'your-cinema-code',
    'your-session-code',
    'your-app-lock-id',
    [
        ['seatCode' => 'your-seat-code-1'],
        ['seatCode' => 'your-seat-code-2'],
    ]
);

if ($result->isSuccess()) {
    var_dump($result->data);
} else {
    var_dump($result->code, $result->status);
}

submitOrder

<?php

$result = $service->submitOrder(
    'your-lock-order-id',
    'your-cinema-code',
    'your-session-code',
    [
        [
            'seatCode' => 'your-seat-code',
            'regionCode' => 'your-region-code',
            'sectionCode' => 'your-section-code',
            'ticketPrice' => 60.00,
            'screenServiceFee' => 10.00,
            'netServiceFee' => 0.00,
        ],
    ]
);

if ($result->isSuccess()) {
    var_dump($result->data);
} else {
    var_dump($result->code, $result->status);
}

queryOrderInfo

<?php

$result = $service->queryOrderInfo('your-cinema-code', 'your-lock-order-id');

if ($result->isSuccess()) {
    var_dump($result->data);
} else {
    var_dump($result->code, $result->status);
}

refundTicket

<?php

$result = $service->refundTicket(
    'your-cinema-code',
    'your-order-id',
    [
        ['ticketNo' => 'your-ticket-no-1'],
        ['ticketNo' => 'your-ticket-no-2'],
    ]
);

if ($result->isSuccess()) {
    var_dump($result->data);
} else {
    var_dump($result->code, $result->status);
}

reportTicketByLockOrderId

<?php

$result = $service->reportTicketByLockOrderId('your-cinema-code', 'your-lock-order-id', 10001);

if ($result->isSuccess()) {
    var_dump($result->traceId ?? null);
} else {
    var_dump($result->code, $result->status);
}

refundTicketByLockOrderId

<?php

$result = $service->refundTicketByLockOrderId('your-cinema-code', 'your-lock-order-id');

if ($result->isSuccess()) {
    var_dump($result->data);
} else {
    var_dump($result->code, $result->status);
}

refundReportTicketByLockOrderId

<?php

$result = $service->refundReportTicketByLockOrderId(
    'your-cinema-code',
    'your-lock-order-id',
    10001,
    ['your-ticket-no'],
    '2026-03-31 14:30:00'
);

if ($result->isSuccess()) {
    var_dump($result->traceId ?? null);
} else {
    var_dump($result->code, $result->status);
}

releaseSeatsInfo

<?php

$result = $service->releaseSeatsInfo('your-cinema-code', 'your-lock-order-id', 2);

if ($result->isSuccess()) {
    var_dump($result->data);
} else {
    var_dump($result->code, $result->status);
}

downloadReportRecord

<?php

$content = $service->downloadReportRecord('2026-03-01', '2026-03-31');
var_dump($content);

reportTicket

<?php

use ZzbSdk\Model\ZzbTicket;

$ticket = new ZzbTicket();
$ticket->numberByDay = 1;
$ticket->parentChannelCode = 'your-parent-channel-code';
$ticket->childChannelCode = 'your-child-channel-code';
$ticket->ticketNo = 'your-ticket-no';
$ticket->cinemaCode = 'your-cinema-code';
$ticket->screenCode = 'your-screen-code';
$ticket->seatCode = 'your-seat-code';
$ticket->filmCode = 'your-film-code';
$ticket->sessionCode = 'your-session-code';
$ticket->sessionDatetime = '2026-03-31 21:20:00';
$ticket->ticketPrice = 30.00;
$ticket->screenServiceFee = 0.00;
$ticket->netServiceFee = 8.00;
$ticket->saleChannelCode = 'your-sale-channel-code';
$ticket->operation = 1;
$ticket->operationDatetime = '2026-03-31 21:20:00';

$result = $service->reportTicket([$ticket]);

if ($result->isSuccess()) {
    var_dump($result->traceId ?? null);
} else {
    var_dump($result->code, $result->status);
}

退票上报同样使用 reportTicket,只需将单票的 operation 改为 2,并传入真实退票时间作为 operationDatetime

Query API Notes

当前现网 query* 接口的已验证行为如下:

  • 仍然要求 HTTPS 双向 TLS
  • 请求体包含 appIdversiontimestampdatasignature
  • interfaceKey 作为签名原文中的 password
  • 签名前需要对对象字段递归排序
  • 摘要算法为 SM3
  • signature = Base64(SM3(json_string))

这套行为来自现网验证结果,可能与标准 PDF 中的路径定义不同。

reportTicket API Notes

reportTicket 与当前现网 query* 接口的报文结构不同,不能直接复用查询接口的完整根级结构。

当前联调结果表明:

  • 请求仍然走 HTTPS 双向 TLS
  • 签名仍然通过 X-Signature 请求头传递
  • 请求体不接受根级字段 appIdversiontimestampsignature
  • 请求体应发送为 data 包裹的业务结构

当前 reportTicket 实际发送结构如下:

{
  "data": {
    "sendChannelCode": "your-channel-code",
    "ticketList": [
      {
        "numberByDay": 1,
        "parentChannelCode": "your-parent-channel-code",
        "childChannelCode": "your-child-channel-code",
        "ticketNo": "your-ticket-no",
        "cinemaCode": "your-cinema-code",
        "screenCode": "your-screen-code",
        "seatCode": "your-seat-code",
        "filmCode": "your-film-code",
        "sessionCode": "your-session-code",
        "sessionDatetime": "2026-03-31 21:20:00",
        "ticketPrice": 30.00,
        "screenServiceFee": 0.00,
        "netServiceFee": 8.00,
        "saleChannelCode": "your-sale-channel-code",
        "operation": 1,
        "operationDatetime": "2026-03-31 21:20:00"
      }
    ]
  }
}

对应 PHP 调用方式保持不变:

<?php

use ZzbSdk\Config;
use ZzbSdk\Model\ZzbTicket;
use ZzbSdk\ZzbService;

$config = new Config([
    'serviceUrl' => 'https://your-host/serverapp',
    'reportUrl' => 'https://your-host/serverapp',
    'channelCode' => 'your-channel-code',
    'certId' => 'your-cert-id',
    'appId' => 'your-app-id',
    'interfaceKey' => 'your-interface-key',
    'certFile' => '/path/to/private_key.pem',
    'certFilePwd' => 'your-cert-password',
    'trustFile' => '/path/to/rootcert.pem',
    'version' => '1.0',
]);

$ticket = new ZzbTicket();
$ticket->numberByDay = 1;
$ticket->parentChannelCode = 'your-parent-channel-code';
$ticket->childChannelCode = 'your-child-channel-code';
$ticket->ticketNo = 'your-ticket-no';
$ticket->cinemaCode = 'your-cinema-code';
$ticket->screenCode = 'your-screen-code';
$ticket->seatCode = 'your-seat-code';
$ticket->filmCode = 'your-film-code';
$ticket->sessionCode = 'your-session-code';
$ticket->sessionDatetime = '2026-03-31 21:20:00';
$ticket->ticketPrice = 30.00;
$ticket->screenServiceFee = 0.00;
$ticket->netServiceFee = 8.00;
$ticket->saleChannelCode = 'your-sale-channel-code';
$ticket->operation = 1;
$ticket->operationDatetime = '2026-03-31 21:20:00';

$service = new ZzbService($config);
$result = $service->reportTicket([$ticket]);

说明:

  • sendChannelCode 来自 SDK 配置中的 channelCode
  • saleChannelCode 由业务侧票务数据决定
  • reportTicketByLockOrderId / refundReportTicketByLockOrderId 需要业务侧传入连续维护的 numberByDay 起始值
  • reportTicket 当前为现网兼容实现,不建议直接套用查询接口报文结构
  • 如果后续上游切换正式标准报文,建议为 reportTicket 增加独立兼容模式

Running Tests

composer install
vendor/bin/phpunit

External Requirements

本包不包含以下外部材料,需要接入方自行准备:

  • 客户端证书与私钥
  • 根证书或信任证书
  • appIdchannelCodeinterfaceKey 等接口参数
  • 对接方提供的标准文档或补充说明

Security

  • 不要将真实证书、私钥、密码提交到仓库。
  • 建议通过环境变量或部署系统注入敏感配置。
  • 如上游接口切换到标准 PDF 路径,建议新增兼容模式,不要直接覆盖现网实现。