良票开放平台 v2.0
  1. 业务端
良票开放平台 v2.0
  • 业务端
    • 第三方业务服务商-对接必读
    • 业务侧-影票代订-api
      • 获取城市列表
      • 获取区县列表
      • 获取院线列表
      • 获取在映影片列表
      • 获取影片详情
      • 获取影院列表
      • 获取影院详情
      • 获取影院场次信息
      • 获取座位信息(已弃用)
      • 获取座位信息V2
      • 创建订单
      • 支付提交订单
    • 影票-询价提单场景api
      • 获取预估/一口价-出票价格
      • 创建订单(联动使用)
  1. 业务端

第三方业务服务商-对接必读

一、API规范

1.1、api规则

操作规范
传输方式采用 HTTP、HTTPS
提交方式采用 POST 方法提交
请求参数类型Content-Type: application/json
响应参数类型Content-Type: application/json
字符编码统一采用 UTF-8 字符编码
签名算法MD5 (32位)
分页传参page、size(最大限制100)

1.2、请求公共参数

字段必填类型描述
appId是Stringkey
sign是String签名
timestamp是String时间戳

1.3、响应参数格式说明

注:以下接口均为有返回,返回值放在数据实体 data 里,如无实体值,则以 state 判断结果,state为200时表示成功,其他值时为失败#

二、API签名算法

2.1、通用签名生成步骤

  1. 拼接字符串:"自己的secretKey" + "自己的appId" + "当前时间戳(毫秒)timestamp" + "接口请求参数json" + "自己的secretKey" ,得到rawSign
  2. 将rawSign进行md5加密,并转换为大写的十六进制

案例:
appId = abcdef
secretKey = 123456
timestamp = 1745831078000
requestBody = {"cityId": 1111}

rawSign = secretKey+appId+timestamp+requestBody+secretKey
rawSign = 123456abcdef1745831078000{"cityId": 1111}123456
sign = 对rawSign进行md5加密后,转换为大写的十六进制

2.2、通用签名生成代码示例

//php示例

//java示例
    
    public static void main(String[] args) throws Exception {
        // 模拟数据↓↓↓↓↓↓↓↓↓↓↓↓
        // 请求参数
        Object dto = new Object();
        // 自己的AppId
        String appId = "123456789";
        // 自己的secretKey
        String secretKey = "987654321";
        // 模拟数据↑↑↑↑↑↑↑↑↑↑↑↑

        // 生成签名
        Map<String, String> map = new HashMap();
        map.put("appId", appId);
        map.put("timestamp", String.valueOf(System.currentTimeMillis()));
        String sign = signRequest(map, dto == null ? "" : JSON.toJSONString(JSON.parse(JsonUtil.toJson(dto))), secretKey);
        System.out.println("最终获取到的签名:" + sign);
    }

    /**
     * 获取签名
     *
     * @param params    appId和时间戳
     * @param body      请求参数Json
     * @param secretKey 自己的secretKey
     * @return 签名
     * @throws Exception 需要对异常进行处理
     */
    public static String signRequest(Map<String, String> params, String body, String secretKey) throws Exception {
        String[] keys = (String[]) params.keySet().toArray(new String[0]);
        Arrays.sort(keys);
        // 获取原始签名
        String rawSign = joinRequestParams(params, body, secretKey, keys);
        // 获取原始签名Md5
        byte[] rawSignMd5 = digest(rawSign);
        // 获取最终签名
        return byte2Hex(rawSignMd5);
    }

    /**
     * 拼接参数获取原始签名
     *
     * @param params    appId和时间戳
     * @param body      请求参数Json
     * @param secretKey 自己的secretKey
     * @param sortedKes map集合key数组
     * @return 原始签名
     */
    private static String joinRequestParams(Map<String, String> params, String body, String secretKey, String[] sortedKes) {
        StringBuilder sb = new StringBuilder(secretKey);
        String[] var5 = sortedKes;
        int var6 = sortedKes.length;

        for (int var7 = 0; var7 < var6; ++var7) {
            String key = var5[var7];
            if (!"sign".equals(key)) {
                String value = (String) params.get(key);
                if (!StringUtils.isEmpty(key) && !StringUtils.isEmpty(value)) {
                    sb.append(key).append(value);
                }
            }
        }

        sb.append(body);
        sb.append(secretKey);
        return sb.toString();
    }

    /**
     * 获取原始签名MD5
     *
     * @param message 要进行加密的数据
     * @return 加密后的签名MD5
     * @throws Exception 需要处理异常
     */
    private static byte[] digest(String message) throws Exception {
        MessageDigest md5Instance = MessageDigest.getInstance("md5");
        md5Instance.update(message.getBytes("utf-8"));
        return md5Instance.digest();
    }

    /**
     * MD5转换为大写的十六进制
     *
     * @param bytes MD5
     * @return sign
     */
    private static String byte2Hex(byte[] bytes) {
        char[] hexDigits = new char[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
        int j = bytes.length;
        char[] str = new char[j * 2];
        int k = 0;
        byte[] var5 = bytes;
        int var6 = bytes.length;

        for (int var7 = 0; var7 < var6; ++var7) {
            byte byte0 = var5[var7];
            str[k++] = hexDigits[byte0 >>> 4 & 15];
            str[k++] = hexDigits[byte0 & 15];
        }

        return new String(str);
    }

三、API对接流程

3.1、关注【良票】公众号后,点击放单,注册账号,联系平台运营提供业务端账号,开通api接入权限,获取api接入参数

3.2、API地址

环境地址
生产(正式)环境business-api.liangpiao.net.cn
测试环境暂无

四、SDK接入

4.1、 接入流程(这里使用idea2023.2示例)

image.png
2.

image.png
3.

image.png
4.

image.png

4.2、 注意事项

如果返回结果包含乱码,请在启动类添加如下配置:

@Bean
public RestTemplate restTemplate(){
RestTemplate restTemplate = new RestTemplate();
restTemplate.getMessageConverters().stream().filter(converter -> converter instanceof StringHttpMessageConverter).forEach(converter -> ((StringHttpMessageConverter) converter).setDefaultCharset(Charset.forName("UTF-8")));
return restTemplate;
}

五、影票订单信息异步回调

5.1、 简要描述

订单状态发生改变时通知接入方,接入方可以根据订单状态做相应操作。

5.2、回调url

请从创建订单api入参

5.3、请求类型

application/json

5.4、请求方式

POST

5.5、回调参数

参数名类型说明
refundTimeString退款/失败时间
seatInfosList座位信息Json数组
refuseMsgString订单 失败/退票 原因
ticketsList出票信息
seatInfoString座位简要信息
changeSeatStateString换座状态(0 不可换座 1 可换座 2 已换座
timeStampString回调时间戳,13位毫秒级
statusString订单状态
outOrderNumberString订单编号
priceAllInteger原价(总价,单位为分)
finalPriceInteger实付价(单位为分)
finalPriceAllInteger实付价总价(单位为分)
seatCountInteger座位数量

CallbackSeatInfoDto

参数名类型说明
seatNameString座位名
rowString行
colString列

CallbackTicketDto

参数名类型说明
ticketString票根
ticketCodeString取票码
validateCodeString验证码

status

状态值状态说明
UPLOAD_TICKET_CODE出票成功
CHANGE_TICKET_CODE订单票码发生了改变
UPLOAD_TICKET_TIMEOUT出票失败(含流单、弃单、订单关闭)
返回说明

如果正常接收到订单信息,请返回以下json格式字符串

{
    "result":true,
    "message":""
}
  • 我方接收到以上返回将认为接入方已处理完成,不会再进行回调,若返回其他的一律认为没有收到应答,将继续回调。

  • 当前暂无订单逆向状态回调(出票成功后主动订单退款),由客服人员在对接群通知单号,后续将完善

六、关于费用、押金、及合同

【费用押金】

影票代订业务:3000元/年;年满1w单有效订单后返还
实物分销业务:业务暂未上线

【合同/合作协议】

接入方要求必须为:企业或个体工商户,签署合作协议,个人暂不支持接入

修改于 2025-04-28 09:35:10
下一页
获取城市列表
Built with