> ## Documentation Index
> Fetch the complete documentation index at: https://docs.aftersell.com/llms.txt
> Use this file to discover all available pages before exploring further.

# 购物车对象

> Aftersell Cart SDK 中购物车及其行的结构：购物车、购物车行、套装和销售计划上的每个字段。

同一个对象结构贯穿整个 SDK。它是 [`getCart()`](/zh/aftersell/cart/sdk-actions#getcart) 的返回值，是 [`cart_loaded` 和 `cart_updated`](/zh/aftersell/cart/sdk-events) 传给你的处理函数的内容，也是[自定义代码区块](/zh/aftersell/cart/custom-code-blocks)接收到的内容。

<Note>
  **所有金额都以货币的最小单位表示**（USD 为分），绝不是格式化后的字符串。`5779` 表示 \$57.79。使用 [`formatMoney`](/zh/aftersell/cart/sdk-actions#formatmoneycents) 来显示它。
</Note>

<div id="the-cart">
  ## 购物车
</div>

| 字段                     | 类型                       | 描述                                                                    |
| ---------------------- | ------------------------ | --------------------------------------------------------------------- |
| `token`                | `string`                 | Shopify 购物车令牌。                                                        |
| `items`                | `AftersellCartLine[]`    | 行项目。参阅[购物车行](#cart-lines)。                                            |
| `itemCount`            | `number`                 | 商品总数量，与购物者看到的一致。                                                      |
| `hasSubscriptionItems` | `boolean`                | 当 `items` 中至少有一行带有销售计划时为 `true`，包括 `itemCount` 排除的附加行。空购物车时为 `false`。 |
| `totalPrice`           | `number`                 | 当前总额，以分为单位。                                                           |
| `originalTotalPrice`   | `number`                 | 折扣前总额，以分为单位。                                                          |
| `totalDiscount`        | `number`                 | 折扣总额，以分为单位。                                                           |
| `compareAtTotalPrice`  | `number \| null`         | 每行原价（MSRP）× 数量之和，以分为单位。不可用时为 `null`，此时回退到 `originalTotalPrice`。       |
| `currency`             | `string`                 | 货币代码。                                                                 |
| `discountCodes`        | `string[]`               | 购物车上已接受的折扣码，已排序。没有时为 `[]`。                                            |
| `attributes`           | `Record<string, string>` | 购物车属性。从 SDK 读取时为只读。                                                   |

<Warning>
  **`itemCount` 并不总是 `items` 的总和。**`items` 镜像真实的 Shopify 购物车，包括抽屉隐藏的附加行，例如运输保护。`itemCount` 是与购物车徽章一致的面向购物者的数字。要知道"购物者选了多少件商品"，用 `itemCount`；要遍历购物车正在渲染的行，用 `items`。

  有两样东西完全不在 `items` 中：用 [`setHidden`](/zh/aftersell/cart/sdk-hooks#registerlinetransform) 隐藏的行，以及[套装子项](#bundles)（它们被并入其主行）。两者仍计入购物车总额，总额直接来自 Shopify。
</Warning>

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
window.aftersell.cart.events.on('cart_updated', (state) => {
  console.log(state.itemCount, 'items');
  console.log('Total:', window.aftersell.cart.actions.formatMoney(state.totalPrice));
  console.log('Saved:', window.aftersell.cart.actions.formatMoney(state.totalDiscount));
  console.log('Codes:', state.discountCodes.join(', ') || 'none');
});
```

<div id="cart-lines">
  ## 购物车行
</div>

`items` 中的每个条目，以及 [`item_added`](/zh/aftersell/cart/sdk-events#item_added) 和 [`item_removed`](/zh/aftersell/cart/sdk-events#item_removed) 上的 `item`：

| 字段                    | 类型                               | 描述                                                                                                                      |
| --------------------- | -------------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
| `key`                 | `string`                         | 该行的 Shopify key。将它传给商品[操作](/zh/aftersell/cart/sdk-actions)。                                                             |
| `productId`           | `number`                         | Shopify 产品 ID。                                                                                                          |
| `variantId`           | `number`                         | Shopify 变体 ID。                                                                                                          |
| `handle`              | `string`                         | 产品 handle。                                                                                                              |
| `title`               | `string`                         | 显示标题。                                                                                                                   |
| `productTitle`        | `string`                         | 不含变体的产品标题。                                                                                                              |
| `variantTitle`        | `string \| null`                 | 变体标签，或 `null`。                                                                                                          |
| `variantOptions`      | `Array<{ name, value }>`         | 已选选项，例如 `[{ name: 'Size', value: 'Medium' }]`。对单变体产品，Shopify 输出 `Title: Default Title`。                                 |
| `quantity`            | `number`                         | 该行的数量。                                                                                                                  |
| `linePrice`           | `number`                         | 行价格，以分为单位。                                                                                                              |
| `finalLinePrice`      | `number`                         | 折扣后的行价格，以分为单位。                                                                                                          |
| `originalLinePrice`   | `number`                         | 折扣前的行价格，以分为单位。                                                                                                          |
| `compareAtPrice`      | `number \| null`                 | 变体原价（MSRP），**按单件计**，以分为单位。没有时为 `null`。                                                                                  |
| `properties`          | `Record<string, string> \| null` | 行项目属性。                                                                                                                  |
| `internalProperties`  | `Record<string, string>`         | 来自 [`registerLineTransform`](/zh/aftersell/cart/sdk-hooks#registerlinetransform) 的仅用于渲染的覆盖层。永远不会持久化到 Shopify。没有时为 `{}`。 |
| `discountAllocations` | `Array<{ title, amount }>`       | 应用于该行的折扣。`amount` 以分为单位。没有时为 `[]`。                                                                                      |
| `isGiftCard`          | `boolean`                        | 该行是否为礼品卡。                                                                                                               |
| `sellingPlan`         | `{ id, name } \| null`           | 生效的订阅计划，一次性购买时为 `null`。                                                                                                 |
| `bundle`              | `AftersellCartBundle \| null`    | 主行上的[套装](#bundles)视图模型；非套装行和子项上为 `null`。                                                                                |
| `metadata`            | `Record<string, unknown>`        | 以增强器 `id` 为键的[增强](/zh/aftersell/cart/sdk-hooks#registercartenricher)数据。在增强器填充之前为 `{}`。                                  |

<Warning>
  `properties` 可能携带购物者提供的输入，例如产品表单的自定义文本字段。请将其渲染为文本，绝不要渲染为原始 HTML。
</Warning>

<div id="identifying-a-line">
  ### 标识一行
</div>

对行进行操作时使用 `key`，识别*产品*时使用 `variantId` 或 `productId`：

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
// ✅ Acting on a line: use key.
window.aftersell.cart.actions.removeItem(line.key);

// ✅ Recognising a product: use variantId.
const hasGift = state.items.some((line) => line.variantId === GIFT_VARIANT_ID);
```

当属性不同时，同一个变体可以出现在多行上。两个刻字文本不同的刻字马克杯是共享一个 `variantId` 的两行。这就是操作接受 `key` 的原因。

<div id="prices-on-a-line">
  ### 行上的价格
</div>

三种价格，容易混淆：

| 想要的             | 使用                            |
| --------------- | ----------------------------- |
| 购物者为该行支付的金额     | `finalLinePrice`              |
| 购物车折扣前的价格       | `originalLinePrice`           |
| MSRP 删除线价格，按单件计 | `compareAtPrice` × `quantity` |

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
// Is this line discounted?
const isDiscounted = line.finalLinePrice < line.originalLinePrice;

// Is it free? (A common way to detect a gift line.)
const isFree = line.finalLinePrice === 0;
```

<div id="bundles">
  ## 套装
</div>

当多行被组合成套装时，**主行**携带一个 `bundle` 对象。子项被并入其中，不再单独出现在 `items` 中。参阅[组合来自其他应用的套装行](/zh/aftersell/cart/sdk-use-case-bundles)了解如何设置组合。

| 字段             | 类型                       | 描述                              |
| -------------- | ------------------------ | ------------------------------- |
| `id`           | `string`                 | 套装标识符。                          |
| `source`       | `'native' \| 'grouped'`  | Shopify 原生套装，或由 Aftersell 组合的行。 |
| `memberKeys`   | `string[]`               | 套装中每一行的 `key`。                  |
| `children`     | `AftersellBundleChild[]` | 套装的内容。                          |
| `displayPrice` | `number`                 | 套装显示的价格，以分为单位。                  |

每个子项携带 `key`（原生组件为 `null`）、`title`、`variantTitle`、`quantity`、`perAnchorQty`、`imageUrl`、`finalLinePrice`、`originalLinePrice` 和 `compareAtPrice`。

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
// Skip bundle children when totalling your own line list.
const topLevel = state.items.filter((line) => !isBundleChild(line, state));
```

<div id="subscription-plans">
  ## 订阅计划
</div>

行的生效计划是 `sellingPlan`，一次性购买时为 `null`。要获得整个购物车的答案，读取 `hasSubscriptionItems` 而不要自己扫描各行，因为它还会计入 `items` 呈现但 `itemCount` 跳过的附加行：

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
if (state.hasSubscriptionItems) {
  // The cart contains at least one subscription line.
}

const subscriptions = state.items.filter((line) => line.sellingPlan);
console.log(subscriptions.length, 'subscription lines');
```

行上*可用*的计划——选择器中的那些——不在购物车对象上。请用 [`registerSubscriptionOptionsTransform`](/zh/aftersell/cart/sdk-hooks#registersubscriptionoptionstransform) 和 [`registerDefaultSubscriptionOptionSelector`](/zh/aftersell/cart/sdk-hooks#registerdefaultsubscriptionoptionselector) 来塑造它们。

<div id="where-to-go-next">
  ## 后续阅读
</div>

* **[操作](/zh/aftersell/cart/sdk-actions)**：读取和更改购物车。
* **[事件](/zh/aftersell/cart/sdk-events)**：这个对象的来源。
* **[Hooks](/zh/aftersell/cart/sdk-hooks)**：用增强器向行添加你自己的数据。
* **[使用案例](/zh/aftersell/cart/sdk-use-cases)**：读取这些字段的完整解决方案。
