# API开发简介

# DidaTravel机票提供了以下类型的接口

  1. 搜索预定接口:搜索、价格校验、生单、支付前校验等
  2. 出票操作接口:申请出票、票号通知/订单详情、PNRRetrieve等
  3. 辅营预定接口: 搜索、预定、支付等
  4. 退改操作接口: 退/改申请,退/改确认,退/改取消,退/改详情,附件上传等
  5. 航变接口: 航变推送、航变确认等

# 测试/生产环境开发对接必须信息如下,商务对接完成可提供

  1. CID
  2. APPKEY(AK)
  3. SECRETKEY(SK)

# 接口规范

  1. 所有的通信基于HTTPS POST的同步方式,统一字符编码:UTF-8。
  2. HTTPS接口请求 Content-Type="text/plain", Accept-Encoding: gzip
  3. 所有的接口必须发送符合JSON的body
  4. 所有请求的Header中必须提供自定义的
Header参数 说明
X-Key 即APPKEY(AK),调用API的身份标识,由商务对接完成给出
X-Timestamp 当前时间戳(13位)
X-Signature 签名后的字符串,具体签名方法见:《X-Signature签名方法》, 并且请求的服务器的IP要被列入DidaTravel的IP白名单。
  1. 生单、支付前校验传输数据需要做AES加密后进行BASE64转码,密钥满足128位,生产环境密钥在测试完成后由双方共同协商确定,具体加密方法见:《AES加密方法》
  2. QPS请求量的限制。DidaTravel默认的限制是:QPS=30。高于这个QPS,请求会被拒绝。有需求可以联系我们。
  3. 需要API接入方配置Gzip请求方式,道旅的API接口都会以Gzip压缩方式返回,API接入方需要Gzip解压获取数据。

# X-Signature签名方法

  • 拼接待签名字符串(stringToBeSigned):Http Method&Content-Type&Accept-Encoding&X-Timestamp&MD5(PAYLOAD)

  • 使用SECRETKEY(SK)进行SHA256非对称加密

  • 示例

    SECRETKEY: abcdefghijklmnopqrst

    PAYLOAD:{"cid":"TESTCID","fromCity":"LON","toCity":"SIN","fromDate":"20221010","retDate":"","tripType":"1","adultNumber":"1","childNumber":"0","currency":"CNY"}

    stringToBeSigned: POST&text/plain&gzip&1654742612768&f5c15d19e356eee91dec15366f5c1f01

    X-Signature: MTY5ODFiY2Y4YWI5N2MwMDQ0MmIzY2E3ZWZmNWRhZmM5M2IwMGI2OTgwY2I0MTFlNjUzMzQzN2JlNzNjYTIwYg==

  • 代码样例

String secretKey = "1234567890123456";
String httpMethod = "POST";
String contentType = "text/plain"
String gzip = "gzip";
// String timestamp = String.valueOf(LocalDateTime.now().toInstant(ZoneOffset.ofHours(8)).toEpochMilli())  // eg: UTC+8
String timestamp = "1654742612768";
String payload = "{\"cid\":\"TESTCID\",\"fromCity\":\"LON\",\"toCity\":\"SIN\",\"fromDate\":\"20221010\",\"retDate\":\"\",\"tripType\":\"1\",\"adultNumber\":\"1\",\"childNumber\":\"0\",\"currency\":\"CNY\"}";
String md5Payload = DigestUtils.md5Hex(payload);
String str2Sign = String.join("&", httpMethod, contentType, gzip, timestamp, md5Payload);

String algorithm = HmacAlgorithms.HMAC_SHA_256.getName();
Mac hmacSha256 = Mac.getInstance(algorithm);
SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey.getBytes(), algorithm);
hmacSha256.init(secretKeySpec);

byte[] bytes = hmacSha256.doFinal(str2Sign.getBytes(StandardCharsets.UTF_8));
String hexString = Hex.encodeHexString(bytes);

String xSign = Base64.getEncoder().encodeToString(hexString.getBytes(StandardCharsets.UTF_8));

# AES加密方法

  • 加密模式:AES/CBC/PKCS5Padding

  • 加密初始化向量:长度位16的空字节数组

  • 示例

    测试密钥: 1234567890123456

    加密样例:abcdefghigklmnopqrstuvwxyz0123456789

    加密后:8Z3dZzqn05FmiuBLowExK0CAbs4TY2GorC2dDPVlsn/tP+VuJGePqIMv1uSaVErr

  • 代码样例

String encode = StringUtils.EMPTY;
String secretKey = "1234567890123456";
String original = "abcdefghigklmnopqrstuvwxyz0123456789";

byte[] keyBytes = secretKey.getBytes();
byte[] targetBytes = new byte[16];

for (int i =0; i < keyBytes.length && i < targetBytes.length; i++) {
    targetBytes[i] = keyBytes[i];
}
SecretKeySpec secretKeySpec = new SecretKeySpec(targetBytes, "AES");

Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
IvParameterSpec ivParameterSpec = new IvParameterSpec(new byte[16]);
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);

byte[] encrypt = cipher.doFinal(src.getBytes());
encode = Base64.getEncoder().encodeToString(encrypt);