HAPPYTRAVEL 开放接口 v1

给合作方系统用的接口:把游客的实名资料直接推送到 HAPPYTRAVEL 平台激活印尼电话卡,并实时拿到激活结果。合作方不用再让游客打开我们的网页填写。

接口地址
https://api.happytravel.lwytech.top/v1
数据格式
JSON(UTF-8),照片用 Base64 编码的 JPEG
时间
北京时间(UTC+8),格式 2026-11-04 14:30;日期 2026-11-04
频率限制
每个密钥每分钟 60 次,超出返回 429
文档语言
中文 / English(正式版会附英文版)
1提交订单合作方系统调用 POST /orders,订单直接进入供应商「待处理」
2供应商处理在供应商端点「成功」或「退回」,和现在的流程完全一样
3回调通知平台马上推送结果到合作方的回调地址
4退回重提交被退回时,合作方改好资料调用 PUT /orders/{no}
开通方式
  • 每个代理在创建时自动开通 API,管理员会把 API 密钥 和 回调密钥 发给你。密钥只显示一次,丢了可以请管理员重置。
  • 通过接口提交的订单算在你的代理名下,和网页提交的订单一样处理、一样结算。
  • 回调地址(Webhook)请告诉管理员登记,状态变化时平台会推送过去。
  • 防重复规则和网页一致:同一个印尼号码或条码号只能提交一次。

认证与安全

每个代理创建时自动生成一把 API 密钥,管理员可以随时停用或重新生成。每个请求都在请求头里带上:

Authorization: Bearer ht_live_7Qm2xK9pL4vR8sT1wY6zA3bC5dE0fG
Content-Type: application/json

安全措施

措施说明
只能看到自己的订单密钥绑定到一个代理,查询、修改都限制在这个代理名下
全程 HTTPS非加密请求一律拒绝
IP 白名单(可选)管理员可以填写合作方服务器 IP,其他 IP 用这把密钥也调不通
幂等请求里带合作方自己的单号 external_id,网络重试不会重复下单
回调签名每次回调都带 X-HT-Signature,合作方用回调密钥验证是不是我们发的

订单状态

status中文含义会触发的回调
pending待处理已提交,等供应商激活—
activated已激活供应商已激活成功order.activated
returned已退回资料有问题,return_reason 写明原因,需要修改重提交order.returned
resubmitted重提交已修改重提交,等供应商再处理—
deleted已删除被管理员删除,不再处理order.deleted

提交订单

POST/v1/orders

提交一位游客的资料。成功后订单直接进入供应商「待处理」,在代理端、供应商端、管理端都能看到。

字段类型必填说明
external_idstring是合作方自己的单号,最长 64 位;同一个单号重复提交会返回第一次的结果
namestring是英文姓名,与护照一致,如 ZHANG SAN
passport_nostring是护照号码
nationalitystring否国籍,三位国家代码,如 CHN
birth_datedate否出生日期 1990-01-31
msisdnstring是印尼号码,包装上 08 开头那串,如 085283673294
package_serialstring否包装条码号(ICCID),如 0150000066492291
imeistring是手机 IMEI,15 位,会校验最后一位校验码
arrival_datedate是抵达印尼日期
contactstring是游客联系方式:手机号或邮箱
remarkstring否备注,最长 200 字
passport_photobase64是护照照片页,JPEG,建议长边 1600 像素,最大 8 MB
package_photobase64是电话卡包装正面照片,JPEG

请求示例

{
  "external_id": "TRIP-20261104-0081",
  "name": "ZHANG SAN",
  "passport_no": "E12345678",
  "nationality": "CHN",
  "birth_date": "1990-01-01",
  "msisdn": "085283673294",
  "package_serial": "0150000066492291",
  "imei": "866641057096095",
  "arrival_date": "2026-11-06",
  "contact": "+86 138 0000 0000",
  "remark": "晚上航班",
  "passport_photo": "/9j/4AAQSkZJRg...",
  "package_photo": "/9j/4AAQSkZJRg..."
}

成功返回 201

{
  "ok": true,
  "order": {
    "order_no": "#1104-012",
    "external_id": "TRIP-20261104-0081",
    "status": "pending",
    "created_at": "2026-11-04 14:30"
  }
}

重复提交 409

{
  "ok": false,
  "error": "duplicate_msisdn",
  "message": "这张电话卡已经提交过了",
  "existing_order_no": "#1103-004"
}

查询订单

GET/v1/orders/{order_no}
GET/v1/orders?external_id=TRIP-20261104-0081

用平台单号或合作方单号查询。订单号里的 # 可以省略,1104-012 和 %231104-012 都可以。

{
  "ok": true,
  "order": {
    "order_no": "#1104-012",
    "external_id": "TRIP-20261104-0081",
    "status": "returned",
    "return_reason": "护照照片反光,看不清号码",
    "msisdn": "085283673294",
    "imei": "866641057096095",
    "arrival_date": "2026-11-06",
    "created_at": "2026-11-04 14:30",
    "updated_at": "2026-11-04 15:02",
    "history": [
      {"at": "2026-11-04 14:30", "event": "submitted"},
      {"at": "2026-11-04 15:02", "event": "returned", "reason": "护照照片反光,看不清号码"}
    ]
  }
}

订单列表

GET/v1/orders?status=activated&date_field=updated&from=2026-11-01&to=2026-11-30&page=1
参数说明
statuspending / activated / returned / resubmitted,不填为全部
date_fieldcreated 提交日期、arrival 抵达日期、updated 处理日期
from / to日期范围,含首尾
page / page_size分页,每页默认 50,最多 200

返回 {"ok":true,"total":128,"page":1,"orders":[…]},每条订单字段和「查询订单」一样,不含 history。

修改重提交

PUT/v1/orders/{order_no}

只有 returned 状态的订单可以重提交。只传要改的字段即可,照片不传就沿用原来的。成功后状态变为 resubmitted,进入供应商「重提交」。

{
  "passport_photo": "/9j/4AAQSkZJRg...",
  "remark": "已重拍护照照片"
}

识别辅助(可选)

POST/v1/recognize/passport
POST/v1/recognize/package
POST/v1/recognize/imei

合作方系统如果没有自己的识别功能,可以把照片发过来,用我们平台的识别结果预填表单。传 {"photo":"base64…"},返回识别出的字段,和网页上的识别是同一套。

// POST /v1/recognize/imei 返回
{"ok": true, "imeis": ["866641057096095", "866641057096103"]}

回调通知

合作方在管理端登记一个回调地址,例如 https://partner.example.com/ht/callback。订单状态一变,平台马上向这个地址发送 POST。合作方返回 HTTP 200 即视为收到;没收到会在 1 分钟、5 分钟、30 分钟、2 小时、12 小时后重试,最多 5 次。

回调内容

POST /ht/callback
X-HT-Event: order.activated
X-HT-Timestamp: 1793750400
X-HT-Signature: sha256=5f2c…e91a

{
  "event": "order.activated",
  "order_no": "#1104-012",
  "external_id": "TRIP-20261104-0081",
  "status": "activated",
  "msisdn": "085283673294",
  "occurred_at": "2026-11-04 15:20"
}

验证签名(Python)

import hmac, hashlib

def verify(body: bytes, ts: str, sig: str, secret: str) -> bool:
    msg = ts.encode() + b"." + body
    expect = "sha256=" + hmac.new(
        secret.encode(), msg, hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(expect, sig)

时间戳超过 5 分钟的回调建议丢弃,防止被重放。

事件类型

event什么时候发额外字段
order.activated供应商点「成功」—
order.returned供应商点「退回」return_reason
order.reopened供应商点「返回待办事项」,订单回到待处理—
order.deleted管理员删除订单—

错误码

HTTPerror原因怎么处理
400invalid_field字段缺失或格式不对,fields 里列出具体字段按提示改正后重发
400invalid_imeiIMEI 不是 15 位,或校验码不对让游客核对 IMEI
401unauthorized密钥错误、已停用,或 IP 不在白名单联系管理员
404not_found订单不存在,或不属于这把密钥检查单号
409duplicate_msisdn这个印尼号码已经提交过不要重复提交;需要时联系管理员
409duplicate_serial这个条码号已经提交过同上
409invalid_state订单当前状态不能做这个操作,比如未退回的订单不能重提交先查询订单状态
413photo_too_large照片超过 8 MB压缩后重发
429rate_limited请求太频繁按 Retry-After 秒数后重试

在线试用

下面是模拟的接口,数据只在这个页面里,不会连到正式平台,用来熟悉请求和返回的格式。正式调用请用你自己的密钥请求上面的接口地址。可以先提交订单,再用右边按钮模拟供应商「成功」或「退回」,看回调会推送什么内容。

还没有发送请求
—
还没有订单
还没有回调