<!-- Source: https://docs.infraio.xyz/zh-CN/concepts/orders -->
<!-- Last updated: 2026-10-04 -->

# 订单

如果说 [CheckoutSession](https://docs.infraio.xyz/zh-CN/concepts/sessions) 是买家看到的东西,
那么 **Order** 就是*你*关心的东西。它是以下事实的永久记录:

- 买了什么(明细项)
- 应付多少、实际收到多少
- 待处理与已执行的退款
- 你的外部引用(`external_ref`)— 通常是你自己的订单 ID,存储在
  Order 上并通过 `GET /b2b/v1/orders/{id}` 返回(不会在 Webhook
  负载中回显 — 见下文)

订单不一定需要行项目：发送 `amount` 代替 `items` 即可只收取一个金额（发票、定金、自定义金额的支付链接）。两者只能二选一。

CheckoutSession 在其某个 PaymentIntent 结算或 TTL 过期后就死掉了。
Order 永远存在。

## 何时创建 Order

当你调用 `POST /b2b/v1/checkout-sessions/quick` 时,InfraIO Pay
在一次事务里**同时**创建一个新 Order *和*一个新 CheckoutSession。
如果你已经有 Order 并想重试结账(例如买家放弃后),改用
`POST /b2b/v1/checkout-sessions` 在现有订单上挂一个新会话 — 保留
审计轨迹。

## 生命周期

```mermaid
stateDiagram-v2
    [*] --> PENDING: order created
    PENDING --> PAID:              full payment settled
    PENDING --> PARTIAL_PAID:      underpayment settled
    PENDING --> CANCELED:          session expired or canceled
    PARTIAL_PAID --> PAID:         merchant resolves OR remainder paid
    PAID --> PARTIALLY_REFUNDED:   partial refund executed
    PAID --> REFUNDED:             full refund executed
    PARTIALLY_REFUNDED --> REFUNDED: subsequent refund covers the rest
    PAID --> [*]
    REFUNDED --> [*]
    CANCELED --> [*]
```

| 状态 | 含义 |
| --- | --- |
| `PENDING` | 一个 CheckoutSession 在运行且未决出。 |
| `PAID` | 全额结算。**Webhook `payment.settled` 在此触发。** 可以放心履约。 |
| `PARTIAL_PAID` | 资金到达但少于总额。见下面"欠付"。 |
| `CANCELED` | 会话过期或商户取消。`metadata.canceled_reason` 解释原因(`payment_timeout`、`merchant_canceled`、……)。 |
| `REFUNDED` | 所有已付款金额都已退款。 |
| `PARTIALLY_REFUNDED` | 部分退款已执行但仍有余额保持已付。 |

> **Note:**
>
> **你履约逻辑要切换的状态是 `PAID`** — 不是 CheckoutSession 的
> `COMPLETED`。`payment.settled` Webhook 才是规范信号。

## `external_ref` 字段

创建会话时你可以包含 `external_ref`(任意字符串,最长 255 字符 —
通常是你自己的订单 ID)。它穿过整个管道:

- 存储在 Order 上
- 在商户仪表板可见,便于支持查询
- 在 `GET /b2b/v1/orders/{id}` 返回,这样 Webhook 处理函数收到
  `payment.settled` 后可以读取

> **Warning:**
>
> Webhook 负载**不**包含 `external_ref`。要把一个
> `payment.*` Webhook 映射回你自己的记录,请从负载里取 `order_id`,
> 然后反查订单。

## 欠付

如果买家的链上转账清算金额少于订单总额,Order 进入 `PARTIAL_PAID`。
你有三种选择:

1. **接受并解决。** 把订单翻为 `PAID`,发出 `order.resolved`。这不能
   通过 REST API 完成,所以自助流程请使用选项 2(收齐剩款)。
2. **等待剩款。** 用 `amount_due` = 剩余值,对同一个 Order 创建一个
   新的 CheckoutSession。买家付差额;结算后,Order 转为 `PAID`。
3. **取消并退款。** 退还部分金额并取消 Order。任何网络手续费由买家承担。

## 退款

退款是独立的 API 表面和独立的概念页。
见 [概念 → 退款](https://docs.infraio.xyz/zh-CN/concepts/refunds)。

## API 端点

| 方法 | 路径 | 备注 |
| --- | --- | --- |
| `POST` | `/b2b/v1/orders` | 不带会话创建 Order(罕见) |
| `GET` | `/b2b/v1/orders/:order_id` | 读取含明细项 + 付款历史的完整订单 |
| `PATCH` | `/b2b/v1/orders/:order_id/cancel` | 取消未支付订单 |
| `PATCH` | `/b2b/v1/orders/:order_id/reopen` | 重新打开一个自动取消(`payment_timeout`)的订单 |

## 下一步

- [概念 → 会话](https://docs.infraio.xyz/zh-CN/concepts/sessions) — 包装 Order 的买家向外壳。
- [概念 → 退款](https://docs.infraio.xyz/zh-CN/concepts/refunds) — 退款状态与手动链上提交步骤。
- [Webhooks → 概览](https://docs.infraio.xyz/zh-CN/webhooks/overview) — Order 生命周期内
  发出的所有事件。
