导入订单
按订单批量导入报名数据,一笔订单可携带多个营员(儿童营员 + 亲子营成人营员),也可暂不传营员(students 与 big_students 同时缺省 / 为空)先创建订单,后续再通过 编辑/添加营员 补录。导入时逐单独立校验、独立处理,成功后自动生成订单、绑定营员并按需记录支付。订单内营员的表单字段需与 获取营种导入模版 返回的字段定义一致。
POST/api/importOrder
核心语义
- 逐单独立成败:每条订单独立校验、独立处理,某单失败不影响其它单,结果分入
success_list/fail_list/duplicate_list三个清单。部分失败时code仍为0,仅当参数级错误(orders为空 / 超单次上限)时msg非空且data为空数组。 out_order_sn幂等:外部订单号全局唯一。重复推送同一out_order_sn不会重复建单,直接返回已建订单并归入duplicate_list;同一批次内出现重复单号按校验失败处理。- 名额与营员明细:未传名额时,儿童名额默认取
students数量,成人名额默认取big_students数量;也可按下方参数表显式传入购买名额,先建订单、后补录营员。没有营员且未声明名额时默认人数为 0,不应据此认为后续已有可补录名额。 - 单次上限:单批最多 10 条订单,超出直接报参数错误。
请求参数
| 名称 | 类型 | 描述 | 是否必填 | 默认值 | 备注 |
|---|---|---|---|---|---|
| token | String | 身份校验令牌 | 必填 | - | 由 获取访问令牌 获得 |
| orders | String / Array | 订单对象数组 | 必填 | - | 可传 JSON 字符串,或标准 POST 数组(orders[0][out_order_sn]=...),两种形式均兼容;单次最多 10 条 |
订单对象
orders 数组的每个元素为一条订单:
| 键 | 类型 | 是否必填 | 说明 |
|---|---|---|---|
| out_order_sn | String | 必填 | 外部订单号(幂等键),全局唯一,长度 ≤ 64 |
| plu | String | 必填 | 营种规格 PLU,决定营种 / 营期 / 规格;从 获取营种详情 的 spec_period.period_list[].sku_plu 拉取 |
| user_phone | String | 必填 | 会员手机号(1[3-9] 开头 11 位);不存在时自动创建会员 |
| user_name | String | 非必填 | 会员姓名,缺省使用 user_phone |
| number | int | 否 | 总名额(购买人数)。缺省时按营员明细推断;显式传入时可无营员先建单(students 留空)。非亲子营默认全计入儿童名额 |
| small_number | int | 否 | 儿童名额(优先级高于 number);缺省取 number 或 count(students) |
| big_number | int | 否 | 成人名额(仅亲子营生效);缺省取 count(big_students) |
| price | Number | 非必填 | 订单总价(优惠后实收口径)。缺省 / 0 表示按原价建单;大于 0 时按下文"价格摊算"处理 |
| paid | Number | 非必填 | 已支付金额。大于 0 时记录支付流水;0 或缺省不记录 |
| remark | String | 非必填 | 订单备注 |
| createtime | String | 非必填 | 下单时间,如 2026-10-01 10:00:00;缺省为当前时间 |
| students | Array | 非必填 | 儿童营员数组;元素键与模版 form_fields.child 的 name 一致;与 big_students 同时缺省 / 为空时仅建订单(纯订单,营员后续补录) |
| big_students | Array | 非必填 | 成人营员数组,仅亲子营(product_type=1)允许;元素键与模版 form_fields.big 的 name 一致;非亲子营传入时该单报错 |
营员对象
营员的键与取值完全对齐 获取营种导入模版 返回的表单字段定义(其中 isMust=1 的字段服务端会做非空校验)。固定六项口径如下:
| 键 | 说明 |
|---|---|
| name | 姓名 |
| gender | 性别文本:含"男"→男,含"女"→女,其它→未知 |
| phone | 营员手机号 |
| birthday | 出生日期(如 2015-01-01);证件类型为身份证时以证件号反推为准 |
| idcard_type | 证件类型文本:含"身份证"→身份证,含"护照"→护照,其它→其他 |
| idcard | 证件号。建议必填(保险投保依赖)。合法身份证号会自动强制类型为身份证并反推生日 / 性别;缺省时系统生成虚拟号(类型降级为"其他") |
其余自定义字段(如 school_name、medical_history 等)按模版定义的 name 原样传值即可,服务端按表单定义收入营员扩展数据;未在表单定义中的键会被忽略。
CURL
V1
curl --location --request POST 'https://open.xinduobang.cn/api/importOrder' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'token=YOUR_TOKEN' \
--data-urlencode 'orders=[{"out_order_sn":"UP202610090001","plu":"1002345","user_phone":"13800000000","students":[{"name":"张小明","gender":"男","idcard_type":"身份证","idcard":"110105201503010012"}]}]'
V2(appid 为 V2 版本)
curl --location --request POST 'https://open.xinduobang.cn/api/importOrder' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--header 'X-Timestamp: 1770000000' \
--header 'X-Nonce: 3f9c1a7b2e6d4058' \
--header 'X-Signature: <computed_hmac_sha256>' \
--data-urlencode 'token=YOUR_TOKEN' \
--data-urlencode 'orders=[{"out_order_sn":"UP202610090001","plu":"1002345","user_phone":"13800000000","students":[{"name":"张小明","gender":"男","idcard_type":"身份证","idcard":"110105201503010012"}]}]'
信息
V2 签名算法及 X-Signature 计算方式见 V2 接入示例。
请求示例
下方为
orders字段的值(JSON 字符串形式),实际提交即orders=<这段 JSON>。示例含三单:一单亲子营(2 儿童 + 1 成人),一单单飞营(2 营员,无big_students),一单纯订单(不传营员,先建单)。
[
{
"out_order_sn": "UP202610090001",
"plu": "1002345",
"user_phone": "13800000000",
"user_name": "张家长",
"price": 5600,
"paid": 5600,
"remark": "机构渠道导入",
"createtime": "2026-10-09 10:30:00",
"students": [
{ "name": "张小明", "gender": "男", "phone": "13800000000", "birthday": "2015-03-01", "idcard_type": "身份证", "idcard": "110105201503010012", "school_name": "希望小学" },
{ "name": "张小红", "gender": "女", "phone": "13800000000", "birthday": "2017-06-01", "idcard_type": "身份证", "idcard": "110105201706010024" }
],
"big_students": [
{ "name": "张家长", "gender": "男", "phone": "13800000000", "birthday": "1988-01-01", "idcard_type": "身份证", "idcard": "110105198801010033" }
]
},
{
"out_order_sn": "UP202610090002",
"plu": "1006789",
"user_phone": "13900000000",
"students": [
{ "name": "李雷", "gender": "男", "idcard_type": "身份证", "idcard": "110105201401010011" },
{ "name": "韩梅梅", "gender": "女", "idcard_type": "身份证", "idcard": "110105201402020022" }
]
},
{
"out_order_sn": "UP202610090003",
"plu": "1002345",
"user_phone": "13700000000",
"number": 2,
"remark": "先建单,营员后补"
}
]
成功响应
下例同时展示三清单:一单成功、一单校验失败、一单命中历史幂等。
{
"code": 0,
"msg": "",
"data": {
"success_list": [
{ "out_order_sn": "UP202610090001", "order_id": 123456, "order_num": "NO20261009103000123456", "student_count": 3 }
],
"fail_list": [
{ "out_order_sn": "UP202610090002", "errors": ["students[1]营员必填字段[有效证件号(必填)]为空"] }
],
"duplicate_list": [
{ "out_order_sn": "UP202610090000", "order_id": 123400, "order_num": "NO20261008150000654321" }
]
}
}
响应字段说明
| 字段 | 类型 | 说明 |
|---|---|---|
| success_list | Array | 建单成功的订单,元素含 out_order_sn、order_id(平台订单 ID)、order_num(平台订单号)、student_count(营员总数,纯订单为 0) |
| fail_list | Array | 校验失败的订单,元素含 out_order_sn、errors(错误文案数组,可多条) |
| duplicate_list | Array | 命中幂等的订单(out_order_sn 已存在),元素含 out_order_sn、order_id、order_num,不重复建单 |
失败响应(参数级)
{ "code": 0, "msg": "orders不能为空", "data": [] }
{ "code": 0, "msg": "单次最多导入10条订单", "data": [] }
信息
orders 缺失、为空数组,或传入无法解析的非法 JSON,统一返回 orders不能为空。
单条订单校验错误明细(fail_list[].errors 可能出现的文案)
| 错误 | 说明 |
|---|---|
out_order_sn不能为空 / out_order_sn在本批次内重复:xxx | 幂等键缺失或同批次重复 |
plu不存在:xxx / plu对应营种不存在 / plu对应营期不存在 | PLU 无效或关联数据缺失 |
营期已取消,不可导入 | 对应营期已取消 |
user_phone手机号格式错误:xxx | 非 1[3-9] 开头 11 位 |
非亲子营不支持big_students | 非亲子营传入 big_students |
students[i]营员必填字段[xxx]为空 | 模版 isMust=1 字段非空校验失败(含下标与字段中文名) |
找不对应的skuV2Id,sku_str:xxx | 落库阶段规格库存映射缺失 |
价格摊算
当传入 price(导入总价)时,按原价合计与导入总价的关系摊算单价与优惠,口径与后台订单导入一致。设原价合计 = 儿童单价 × 儿童数 + 成人单价 × 成人数(有规格取规格价,无规格取营种价),总人数为分母:
| 分支 | 条件 | 结果 |
|---|---|---|
| 优惠 | 原价合计 > 导入总价,且导入总价 ≠ 0 | 单价按摊算公式计算,优惠额计入订单优惠 |
| 覆盖 | 导入总价 ≥ 原价合计 | 单价 = 导入总价 / 总人数,尾差计入订单优惠 |
| 原价 | 导入总价缺省或 0 | 按原价拆单价,订单金额 = 原价合计 |
导入总价大于 0 时,订单金额取导入总价。
导入成功后系统自动处理
每条订单在校验通过后会依次完成以下动作,上游无需额外调用:
- 会员:按
user_phone查找会员,不存在则自动创建; - 订单:生成订单与平台订单号,导入成功的订单状态默认为「报名成功」;
- 库存:扣减对应规格库存与营期已报名人数(与后台导入一致,不校验库存是否充足);
- 支付:
paid > 0时记录支付流水,不触发分销返佣计算; - 核销码:营种开启核销时,按人数生成核销码;
- 营员:逐个创建 / 更新营员并绑定会员;同一证件号在同一营期已存在时走更新覆盖(与后台营员导入一致),表单数据按营种报名表归档;
- 后置钩子:订单备注、会员等级、自动分班、合同展示等按平台既定流程执行。
使用限制
- 不支持:年卡次数、储值卡金额、优惠券、增值服务(模版未包含这些字段);
- 不触发:分销返佣(即使
paid > 0也不计入返佣)。
幂等与重试建议
- 上游应以
out_order_sn为幂等键重试:网络超时等不确定场景可直接重推同批数据,已成功的单会落入duplicate_list(带回已建order_id/order_num),不会重复建单; fail_list中的订单修正数据后可用同一out_order_sn重推(失败单不占用幂等键);- 建议单批 ≤ 10 条、串行推送,避免同批数据交叉。
相关接口
- 营种导入模版(字段定义来源):
获取营种导入模版 - 营种详情(PLU 候选):
获取营种详情 - 订单数据结构:
订单数据结构 - 修正单条营员(全量覆盖)/ 在已购名额内新增营员(含纯订单补录):
编辑/添加营员 - 更换营期(线下导入订单,允许跨营种):
更换营期 - 整单 / 单营员退款:
导入订单退款
变更记录
| 日期 | 变更 |
|---|---|
| 2026-10-09 | 首次发布:嵌套订单报文(一单多营员);逐单独立成败;out_order_sn 幂等;paid > 0 写支付流水不返佣;人数由营员明细推断 |
| 2026-10-10 | 传参方式由原始 JSON body 改为 POST 表单字段 orders(兼容 JSON 字符串与标准 POST 数组两种形式);非法 / 空 orders 统一归入 orders不能为空 |
| 2026-10-10 | 单次订单上限下调为 10 条,超出返回"单次最多导入10条订单" |
| 2026-10-12 | 补充说明:导入成功的订单状态默认为「报名成功」 |
| 2026-10-12 | 支持无营员导入:students 与 big_students 可同时缺省 / 为空,先创建纯订单(student_count 为 0),营员后续通过 编辑/添加营员 补录 |