订单与履约
从下单到签收的完整生命周期,以及你该如何向用户转述。
订单状态机
created ──▶ pending_payment ──▶ paid ──▶ order_pushed ──▶ shipping ──▶ shipped ──▶ completed
│ (30min未付) │ │ 商家仓备货/发货 物流运输 签收
▼ │ ▼
closed │ push_blocked(平台风控拦截,见下)
▼
refunding ──▶ refunded ──▶ closed(售后驱动)
给用户的人话对照(推荐转述口径)
| status | 对用户说 |
|---|---|
pending_payment |
待支付(附二维码/跳转) |
paid |
支付成功,正在通知商家发货 |
order_pushed |
商家已接单,备货中 |
shipping |
商家仓打包发货中 |
shipped |
已发货,运单号 XXX(附轨迹查询) |
completed |
已签收 |
refunded |
退款已原路退回 |
NOTE push_blocked 是平台风控状态(如亏损拦截、商家预存余额不足),平台会在后台处理或走售后兜底——不要向用户转述这个内部状态,用户侧体验是"发货稍有延迟"。
事件时间线
get_order_status 响应中的 events 是订单的事实记录,每条含 event / payload / created_at:
{ "events": [
{ "event": "paid", "created_at": "..." },
{ "event": "merchant_accepted", "created_at": "..." },
{ "event": "shipped", "payload": { "waybill_no": "7830xxxxxx", "express_co": "ZTO" }, "created_at": "..." },
{ "event": "delivered", "created_at": "..." }
] }
可订阅的事件:created / paid / push_blocked / merchant_accepted / shipped / delivered / refund_initiated / refunded / closed。
物流轨迹
订单进入 shipped 后查询全程轨迹:
curl
curl "https://api.yuantoutao.com/v1/orders/YM20261003100003/logistics" \
-H "Authorization: Bearer $YUANTOUTAO_API_KEY"
node
const tr = await (await fetch(`${API}/orders/${orderNo}/logistics`, {
headers: { Authorization: `Bearer ${KEY}` }
})).json();
console.log(tr.express_co, tr.waybill_no);
tr.traces.forEach(t => console.log(t.time, t.context));
python
tr = requests.get(f"{API}/orders/{order_no}/logistics",
headers={'Authorization': f'Bearer {KEY}'}).json()
print(tr['express_co'], tr['waybill_no'])
for t in tr['traces']: print(t['time'], t['context'])
WARNING 未发货订单调用物流接口返回 INVALID_PARAM——先看 get_order_status 里的 shipped 事件再查轨迹。
幂等:重试的正确姿势
POST /orders 必须带 Idempotency-Key。网络超时/响应丢失时,用同一个 key 原样重试——平台保证返回同一订单(duplicate:true),绝不重复扣单。每次新的用户意图才用新 key。
node
async function createOrderWithRetry(body, idem, tries = 3) {
for (let i = 0; i < tries; i++) {
try { return await callCreateOrder(body, idem); }
catch (e) { if (i === tries - 1) throw e; await new Promise(r => setTimeout(r, 1000 * 2 ** i)); }
}
}
python
def create_order_with_retry(body, idem, tries=3):
for i in range(tries):
try:
return call_create_order(body, idem)
except Exception:
if i == tries - 1: raise
time.sleep(2 ** i)
库存与价格的有效性
available_qty是快照不是实时锁位;下单时平台会再校验,失败返回OUT_OF_STOCK。- 价格以订单成交快照为准(
order_items.unit_price),货架价格变动不影响已建订单。 - 高频刷价场景:
search_goods结果缓存不超过 60 秒。