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

# 开放式卡片概览

> 从你的平台生成托管的虚拟卡链接（“开放式”发送卡片）。你只需调用一个 API 即可铸造一个或多个链接，然后按你喜欢的方式将这些链接交付给收件人——通过电子邮件、短信，或直接回传原始 URL 以嵌入到你自己的流程中。收件人打开链接后，会进入 Fluz 托管的页面，完成身份验证，并领取由你的账户出资的一次性充值虚拟卡。

<Info>
  **先决条件：** 一个携带 `CREATE_SHARE_LINK` 范围的 Bearer 访问令牌。基本认证会被拒绝。联系你的销售代表以开启访问权限。参见 [Authentication](/concepts/authentication)。
</Info>

<Note>
  **“托管”/“开放式”的含义。** *托管* 链接指向 Fluz 托管的激活页面。*开放式* 意味着生成的虚拟卡是网络卡（Visa/Mastercard 类型），可在多家商户消费，受你的项目规则约束——而非单一品牌的封闭式礼品卡。
</Note>

export const ensureCardStyles = () => {
  if (typeof document === 'undefined') return;
  if (document.getElementById('fjs-om-card-css')) return;
  const st = document.createElement('style');
  st.id = 'fjs-om-card-css';
  st.textContent = `
@keyframes omGlisten{0%{transform:translateX(0) skewX(-18deg)}26%{transform:translateX(900%) skewX(-18deg)}100%{transform:translateX(900%) skewX(-18deg)}}
.om-wrap{--omh:0;container-type:inline-size;display:block;margin:1.6rem 0;text-decoration:none!important;border-bottom:none!important;background-image:none!important;box-shadow:none!important}
.om-wrap:hover,.om-wrap:focus,.om-wrap:active{text-decoration:none!important;border-bottom:none!important;background-image:none!important;box-shadow:none!important;text-decoration-thickness:0!important}
.om-wrap::before,.om-wrap::after{content:none!important;display:none!important}
.om-wrap:hover,.om-wrap:focus-visible{--omh:1}
.om-wrap:focus-visible{outline:2px solid #FEC251;outline-offset:3px}
.om-card{position:relative;display:block;border:1px solid #EAEAEA;border-radius:20px;overflow:hidden;background:#F5F4F3;cursor:pointer;aspect-ratio:684/448;padding:36px 0 0;transition:box-shadow .45s cubic-bezier(.2,.8,.2,1)}
.om-glow{position:absolute;inset:0;pointer-events:none;background:radial-gradient(72cqw 72cqw at -14% 22%,rgba(142,112,72,.18),transparent 78%),radial-gradient(72cqw 72cqw at 114% 22%,rgba(142,112,72,.18),transparent 78%)}
.om-grid{position:absolute;inset:0;pointer-events:none;opacity:.13;background-image:linear-gradient(rgba(26,0,0,.5) 1px,transparent 1px),linear-gradient(90deg,rgba(26,0,0,.5) 1px,transparent 1px);background-size:3.51cqw 3.51cqw;background-position:0 3.29cqw,3.29cqw 0;mask-image:radial-gradient(34cqw 34cqw at -14% 16%,#000 32%,rgba(0,0,0,.45) 72%,transparent 100%),radial-gradient(34cqw 34cqw at 114% 16%,#000 32%,rgba(0,0,0,.45) 72%,transparent 100%);-webkit-mask-image:radial-gradient(34cqw 34cqw at -14% 16%,#000 32%,rgba(0,0,0,.45) 72%,transparent 100%),radial-gradient(34cqw 34cqw at 114% 16%,#000 32%,rgba(0,0,0,.45) 72%,transparent 100%)}
.om-text{position:relative;display:block;text-align:left;padding:0 30% 0 5.4%}
.om-title{display:block;font-family:'Fjs Greed','Greed',ui-sans-serif,sans-serif;font-weight:600;font-size:34px;line-height:1.12;letter-spacing:-.02em;color:#1A0000}
.om-sub{display:block;font-family:'Fjs Area','Area',ui-sans-serif,sans-serif;font-size:16px;line-height:1.35;font-weight:600;color:#6E6862;margin-top:9px}
.om-frame{position:relative;display:block;margin:7.2cqw auto 0;width:85.7cqw;border:1.05cqw solid #0A0A0A;border-bottom:0;border-radius:1.9cqw 1.9cqw 0 0;background:#0A0A0A;box-shadow:0 2.4cqw 5cqw -1.6cqw rgba(26,0,0,.34);transform:translateY(calc(var(--omh,0) * -9px));transition:transform .55s cubic-bezier(.2,.8,.2,1)}
.om-shot{display:block;border-radius:.95cqw .95cqw 0 0;overflow:hidden;background:#fff;aspect-ratio:3444/2081}
.om-shot img{display:block;width:100%;height:100%;object-fit:cover;object-position:top center;margin:0}
.om-img-dark{display:none}
.om--dark .om-img-light{display:none}
.om--dark .om-img-dark{display:block}
html.dark .om--system .om-img-light{display:none}
html.dark .om--system .om-img-dark{display:block}
.om-fade{position:absolute;left:0;right:0;bottom:0;height:13cqw;pointer-events:none;background:linear-gradient(to bottom,rgba(245,244,243,0) 0%,rgba(245,244,243,.7) 46%,#F5F4F3 100%)}
.om-dim{position:absolute;inset:0;pointer-events:none;background:linear-gradient(to bottom,rgba(20,20,22,.1) 0%,rgba(20,20,22,.22) 60%,rgba(20,20,22,.3) 100%);opacity:var(--omh,0);transition:opacity .45s ease}
.om-pill{position:absolute;right:3.4%;top:9px;pointer-events:none;display:inline-flex;align-items:center;justify-content:center;gap:9px;height:48px;padding:0 26px;border-radius:100px;overflow:hidden;background:#FEC251;color:#1A0000;font-family:'Fjs Greed','Greed',ui-sans-serif,sans-serif;font-weight:600;font-size:19px;letter-spacing:-.01em;transform:scale(calc(1 + .05 * var(--omh,0)));transition:transform .5s cubic-bezier(.2,.8,.2,1),box-shadow .5s ease;box-shadow:0px 0px 2.4px 0.4px #FEC25159,0 0 calc(22px * var(--omh,0)) calc(6px * var(--omh,0)) rgba(254,194,81,calc(.5 * var(--omh,0)))}
.om-pill svg{width:18px;height:18px;flex:none}
.om-glisten{position:absolute;top:0;left:-22%;width:16%;height:100%;background:linear-gradient(100deg,rgba(255,255,255,0) 0%,rgba(255,255,255,.42) 50%,rgba(255,255,255,0) 100%);filter:blur(2px);animation:omGlisten 3.6s cubic-bezier(.45,0,.55,1) infinite}
@container (max-width:659px){
  .om-card{aspect-ratio:684/588;padding-top:26px}
  .om-title{font-size:27px}
  .om-sub{font-size:13px;margin-top:6px}
  .om-text{padding:0 7% 0 5.4%}
  .om-pill{position:static;margin-top:22px;height:40px;padding:0 20px;gap:7px;font-size:16px}
  .om-pill svg{width:15px;height:15px}
  .om-frame{margin-top:32px}
}
.om--dark .om-card,html.dark .om--system .om-card{background:#221919;border-color:#2E2823}
.om--dark .om-glow,html.dark .om--system .om-glow{background:radial-gradient(72cqw 72cqw at -14% 22%,rgba(235,222,196,.24),transparent 80%),radial-gradient(72cqw 72cqw at 114% 22%,rgba(235,222,196,.24),transparent 80%)}
.om--dark .om-grid,html.dark .om--system .om-grid{opacity:.09;background-image:linear-gradient(rgba(229,223,195,.85) 1px,transparent 1px),linear-gradient(90deg,rgba(229,223,195,.85) 1px,transparent 1px)}
.om--dark .om-title,html.dark .om--system .om-title{color:#EAEAEA}
.om--dark .om-sub,html.dark .om--system .om-sub{color:#9C9391}
.om--dark .om-frame,html.dark .om--system .om-frame{box-shadow:0 2.4cqw 5cqw -1.6cqw rgba(0,0,0,.5)}
.om--dark .om-shot,html.dark .om--system .om-shot{background:#17110C}
.om--dark .om-fade,html.dark .om--system .om-fade{background:linear-gradient(to bottom,rgba(34,25,25,0) 0%,rgba(34,25,25,.7) 46%,#221919 100%)}
.om--dark .om-dim,html.dark .om--system .om-dim{background:linear-gradient(to bottom,rgba(8,5,4,.18) 0%,rgba(8,5,4,.34) 60%,rgba(8,5,4,.44) 100%)}
`;
  document.head.appendChild(st);
};

export const ensureLoader = () => new Promise((resolve, reject) => {
  if (window.FluzDemo) return resolve();
  let s = document.querySelector('script[data-fluz-loader]');
  if (!s) {
    s = document.createElement('script');
    s.src = 'https://demos.fluz.app/loader-v1.js';
    s.async = true;
    s.setAttribute('data-fluz-loader', '1');
    document.head.appendChild(s);
  }
  s.addEventListener('load', () => resolve());
  s.addEventListener('error', () => reject(new Error('loader failed')));
});

export const DemoCard = ({demo, url, theme = 'light', image, imageDark, title = 'Demo open loop cards', subtitle = 'Step-by-step walkthrough of the API and user experience.', cta = 'Launch demo', presentation = 'full', mode}) => {
  ensureCardStyles();
  if (typeof window !== 'undefined') {
    ensureLoader().catch(() => {});
    try {
      const origin = new URL(url, 'https://demos.fluz.app').origin;
      if (!document.querySelector(`link[rel="preconnect"][href="${origin}"]`)) {
        const pc = document.createElement('link');
        pc.rel = 'preconnect';
        pc.href = origin;
        pc.crossOrigin = 'anonymous';
        document.head.appendChild(pc);
      }
    } catch (e) {}
  }
  const imgLight = image || '/images/demos/demo-shot.png';
  const imgDark = imageDark || '/images/demos/demo-shot-dark.png';
  return <a className={`om-wrap${theme === 'dark' ? ' om--dark' : theme === 'system' ? ' om--system' : ''}`} href={url || '#'} target="_blank" rel="noopener" aria-label={`Launch the interactive ${title} demo`} data-fluz-demo-open={demo} data-fluz-demo-url={url} data-fluz-demo-src="docs" data-fluz-demo-presentation={presentation} data-fluz-demo-mode={mode} onClick={e => {
    if (url && window.innerWidth < 600) return;
    e.preventDefault();
    const opener = e.currentTarget;
    ensureLoader().then(() => window.FluzDemo.open(demo, {
      url,
      src: 'docs',
      presentation,
      mode,
      opener
    })).catch(() => {
      if (url) window.open(url, '_blank', 'noopener');
    });
  }}>
      <span className="om-card">
        <span className="om-glow" /><span className="om-grid" />
        <span className="om-text">
          <span className="om-title">{title}</span>
          <span className="om-sub">{subtitle}</span>
          <span className="om-pill">
            {cta}
            <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.6" strokeLinecap="round" aria-hidden="true">
              <path d="M14 4h6v6M20 4l-8 8M10 20H4v-6M4 20l8-8" />
            </svg>
            <span className="om-glisten" />
          </span>
        </span>
        <span className="om-frame"><span className="om-shot">
          <img className="om-img-light" src={imgLight} alt={`${title} — interactive demo`} />
          <img className="om-img-dark" src={imgDark} alt="" aria-hidden="true" />
        </span></span>
        <span className="om-fade" /><span className="om-dim" />
      </span>
    </a>;
};

<DemoCard demo="card-issuing" url="https://demos.fluz.app/card-issuing/" theme="system" />

## 工作原理

<Steps>
  <Step title="你生成链接">
    调用 `generateVCShareLinks`，传入优惠、卡片限额、数量、资金来源和一种交付方式。每个链接代表一张卡，拥有各自的限额，并从你指定的消费账户出资。
  </Step>

  <Step title="Fluz 为每个链接创建一个分享请求">
    每个链接映射到一个分享请求（`PENDING`）和一个托管 URL。
  </Step>

  <Step title="链接被送达">
    使用 `GENERATE_URL` 时，你会获得可自行分发的 URL。使用 `EMAIL` 或 `PHONE_NUMBER` 时，Fluz 会为你向每位收件人发送一个链接。
  </Step>

  <Step title="收件人激活并领取卡片">
    收件人打开链接，并通过一次性验证码验证其手机号——无需下载 App，无需密码。卡片限额会在领取时而非生成链接时，从你的消费账户中预扣。卡片在领取后不会自动展示；展示卡片会提示收件人输入其 PIN，若尚未设置则需先创建。完整流程参见 [Recipient Experience](/features/open-loop-cards/open-loop-cards-recipient-experience)。
  </Step>
</Steps>

收件人仅成为该虚拟卡对象的授权用户——他们不会获得对你账户、余额或任何其他卡的访问权限。

![发送卡片流程图](https://files.readme.io/c19029bfeaca196f7eadc2f020ab56f2aad893261f561307bec543e700f0d74b-diagram.svg)

## 可用性与范围

| 能力            | 状态            |
| ------------- | ------------- |
| 一次性充值虚拟卡      | ✅ 支持          |
| 一次性使用虚拟卡      | ✅ 支持          |
| 可重复充值卡        | ❌ 不支持         |
| 通过 API 生成链接   | ✅ 支持          |
| 通过 CSV 导入生成链接 | ❌ 即将推出        |
| 托管的**礼品卡**链接  | ❌ 不在范围内（仅虚拟卡） |

卡片分享链接对象类型为 `VIRTUAL_CARD`，卡片类型为 `SINGLE_LOAD`。

<Warning>
  **礼品卡：** 尽管更广泛的计划包含“虚拟卡与礼品卡”的表述，但目前没有托管的礼品卡领取流程。礼品卡余额仅作为托管虚拟卡的\_潜在资金来源\_出现（规划中，尚未启用）。文档与集成仅面向虚拟卡。
</Warning>

## 操作参考

共有三项公开操作，均受 `CREATE_SHARE_LINK` 范围控制：

| 操作                       | 类型 | 目的             |
| ------------------------ | -- | -------------- |
| `generateVCShareLinks`   | 变更 | 创建一个或多个托管虚拟卡链接 |
| `getVCShareLinks`        | 查询 | 列出/查看先前生成的链接   |
| `deactivateVCShareLinks` | 变更 | 停用（使过期）你生成的链接  |

所有“发送卡片”操作均位于 Fluz GraphQL API：`POST https://<your-fluz-api-host>/api/v1/graphql`，并带 `Authorization: Bearer <access_token>` 头。令牌必须携带 `CREATE_SHARE_LINK` 范围——否则每个操作都会返回 *"Missing permissions! Please contact your sales rep to get access to generate VC share links."*

## generateVCShareLinks

创建 `quantity` 个分享请求，并为每个请求返回一个托管链接。

```graphql theme={null}
mutation GenerateVCShareLinks($input: GenerateVCShareLinksInput!) {
  generateVCShareLinks(input: $input) {
    shareLinks
  }
}
```

### 输入字段

| 字段                       | 类型                                      | 必填  | 说明                                                                                                                     |
| ------------------------ | --------------------------------------- | --- | ---------------------------------------------------------------------------------------------------------------------- |
| `cardLimit`              | `Int!`                                  | 是   | 每张卡的消费限额（也是充值金额），以整币单位表示。必须是一个不小于项目最小值的整数。                                                                             |
| `offerId`                | `String!`                               | 是   | 该卡绑定的商户优惠的 UUID v4。优惠必须为激活状态且其商户必须可分享。                                                                                 |
| `quantity`               | `Int!`                                  | 是   | 要生成的链接数量。每个单位创建一个独立的托管 URL。                                                                                            |
| `shareMethod`            | `ShareMethodType!`                      | 是   | 收件人识别与链接交付方式：`GENERATE_URL`、`EMAIL`、`PHONE_NUMBER`、`EXISTING_USER` 或 `REGISTER_USER`。                                  |
| `userCashBalanceId`      | `UUID`                                  | 是\* | 用于为卡片出资的消费账户。*在模式中标记为可选，但实际上必填——省略将导致验证失败。*                                                                            |
| `daysUntilExpiration`    | `Int`                                   | 否   | 链接到期的天数。最小为 1。若省略，默认为项目默认值（30 天）。**该日期同时作为卡片的锁定/冻结日期**——参见[到期与冻结](#expiration--freeze)。                                |
| `recipientListEmail`     | `[String]`                              | 条件  | 当 `shareMethod = EMAIL` 时必填且非空。长度必须等于 `quantity`。否则必须为空。                                                               |
| `recipientListPhone`     | `[String]`                              | 条件  | 当 `shareMethod = PHONE_NUMBER` 时必填且非空。长度必须等于 `quantity`。否则必须为空。                                                        |
| `recipientUserIds`       | `[UUID]`                                | 条件  | 当 `shareMethod = EXISTING_USER` 时，指定已知 Fluz 用户 ID 以绑定为收件人。长度必须等于 `quantity`。与 `recipientRegistrations` 互斥。             |
| `recipientRegistrations` | `[ShareLinkRecipientRegistrationInput]` | 条件  | 当 `shareMethod = REGISTER_USER` 时的内联预注册负载——Fluz 在生成链接前会为每条目创建或复用一个占位用户（无席位）。长度必须等于 `quantity`。与 `recipientUserIds` 互斥。 |
| `usePrepaymentBalance`   | `Boolean`                               | 否   | 是否使用你的预付余额作为额外资金来源。默认 false。其使用方式详见：[资金如何运作](/features/virtual-cards#how-funding-works)。                               |
| `useRewardsBalance`      | `Boolean`                               | 否   | 是否使用你的 Fluz 奖励余额作为额外资金来源。默认 false。其使用方式详见：[资金如何运作](/features/virtual-cards#how-funding-works)。                         |

<Note>
  **资金来源。** `userCashBalanceId`（属于你作为发送方账户的消费账户）是主要且必需的资金来源。可选地将 `usePrepaymentBalance` 和/或 `useRewardsBalance` 设为 `true`，以便在领取时消费账户余额不足时，Fluz 回退使用你的预付或奖励余额。银行账户与银行卡目前不支持作为资金来源。
</Note>

### 收件人识别与交付方式（`shareMethod`）

| 值               | 行为                                                     | 收件人字段                                          | 卡片创建时机      |
| --------------- | ------------------------------------------------------ | ---------------------------------------------- | ----------- |
| `GENERATE_URL`  | Fluz 在响应中返回托管 URL。你自行分发。                               | 两个列表都必须为空/省略。                                  | 领取时         |
| `EMAIL`         | Fluz 向每位收件人发送邮件链接。                                     | 需要 `recipientListEmail`；长度必须等于 `quantity`。     | 领取时         |
| `PHONE_NUMBER`  | Fluz 向每位收件人发送短信链接。                                     | 需要 `recipientListPhone`；长度必须等于 `quantity`。     | 领取时         |
| `EXISTING_USER` | 链接从一开始就绑定到已知 Fluz 用户；仅该用户可领取。通过该用户档案中已有的联系方式进行投递。      | 需要 `recipientUserIds`；长度必须等于 `quantity`。       | **立即**，在生成时 |
| `REGISTER_USER` | Fluz 为每条目创建或复用一个占位用户，然后将链接绑定到该用户，与 `EXISTING_USER` 相同。 | 需要 `recipientRegistrations`；长度必须等于 `quantity`。 | **立即**，在生成时 |

<Note>
  `EXISTING_USER` 与 `REGISTER_USER` 都会在 `generateVCShareLinks` 调用中创建虚拟卡，而非将卡片创建延后到领取时。完整的 `EXISTING_USER` 流程，包括如何先用 `registerUser` 注册收件人，参见 [Register & Send](/features/open-loop-cards/register-and-send)。
</Note>

### 校验规则

* `cardLimit` 必须为整数且不小于项目最小值。
* `offerId` 必须是一个针对**激活**优惠且其**商户可分享**的有效 UUID v4。
* `quantity` 必须为整数。
* 与 `shareMethod` 对应的收件人字段（`recipientListEmail`、`recipientListPhone`、`recipientUserIds` 或 `recipientRegistrations`）长度必须**等于** `quantity`。不匹配将返回清晰错误，且**不会**创建任何记录。
* `recipientUserIds` 与 `recipientRegistrations` 互斥，且与交付列表字段互斥。
* `recipientUserIds` 中的每个 ID 必须是有效、存在的 Fluz 用户。
* `userCashBalanceId` 为必填，且必须是发送方账户所拥有的有效 UUID v4。`usePrepaymentBalance` 与 `useRewardsBalance` 为可选回退资金来源，可同时启用。
* 无效卡片类型或畸形输入会返回清晰错误，且不会创建记录。

### 示例

<CodeGroup>
  ```json Generate URLs theme={null}
    {
      "input": {
        "cardLimit": 25,
        "offerId": "11111111-2222-3333-4444-555555555555",
        "daysUntilExpiration": 30,
        "quantity": 3,
        "shareMethod": "GENERATE_URL",
        "userCashBalanceId": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"
      }
    }
  ```

  ```json Email theme={null}
    {
      "input": {
        "cardLimit": 25,
        "offerId": "11111111-2222-3333-4444-555555555555",
        "daysUntilExpiration": 30,
        "quantity": 2,
        "shareMethod": "EMAIL",
        "recipientListEmail": ["recipient1@example.com", "recipient2@example.com"],
        "userCashBalanceId": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"
      }
    }
  ```

  ```json SMS theme={null}
    {
      "input": {
        "cardLimit": 25,
        "offerId": "11111111-2222-3333-4444-555555555555",
        "daysUntilExpiration": 30,
        "quantity": 2,
        "shareMethod": "PHONE_NUMBER",
        "recipientListPhone": ["+12125550101", "+12125550102"],
        "userCashBalanceId": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"
      }
    }
  ```

  ```json Existing user theme={null}
    {
      "input": {
        "cardLimit": 25,
        "offerId": "11111111-2222-3333-4444-555555555555",
        "daysUntilExpiration": 30,
        "quantity": 1,
        "shareMethod": "EXISTING_USER",
        "recipientUserIds": ["f1320ac4-52dc-4c67-9e80-24e506b18450"],
        "userCashBalanceId": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"
      }
    }
  ```
</CodeGroup>

<Tip>
  对于 `EXISTING_USER`，请先注册收件人（或直接使用现有用户的 ID）——完整演练（包括 `registerUser` 调用与响应处理）参见 [Register & Send](/features/open-loop-cards/register-and-send)。
</Tip>

### 响应

```json theme={null}
{
  "data": {
    "generateVCShareLinks": {
      "shareLinks": [
        "https://fluz.app/virtual-prepaid-card/3f8a...c1",
        "https://fluz.app/virtual-prepaid-card/9b2d...77",
        "https://fluz.app/virtual-prepaid-card/0c41...e3"
      ]
    }
  }
}
```

`shareLinks` 是一个托管 URL 数组，数量等于 `quantity`，每个形式为 `https://fluz.app/virtual-prepaid-card/{share_request_id}`。

<Tip>
  响应仅返回 URL。要检索你刚创建的链接的**批次 ID**和**展示 ID**（用于列表与停用），请使用按状态过滤的 `getVCShareLinks`。
</Tip>

## getVCShareLinks

列出先前生成的分享链接，以便你查看状态、收件人、到期时间以及已发行的卡片。

```graphql theme={null}
query GetVCShareLinks($input: GetVCShareLinksInput!) {
  getVCShareLinks(input: $input) {
    senderAppId
    shareRequestBatchId
    shareRequestDisplayId
    shareObjectStatus
    recipientPhone
    recipientEmail
    linkExpirationDate
    virtualCardId
    linkUrl
    shareRequestDetails {
      cardLimit
      offerId
      daysUntilExpiration
      quantity
      shareMethod
      recipientListEmail
      recipientListPhone
      userCashBalanceId
    }
  }
}
```

### 输入字段

| 字段                       | 类型                    | 说明                                         |
| ------------------------ | --------------------- | ------------------------------------------ |
| `shareObjectStatuses`    | `[ShareObjectStatus]` | 按状态过滤：`PENDING`、`ISSUED`、`USED`、`EXPIRED`。 |
| `shareRequestBatchIds`   | `[String]`            | 仅返回这些批次中的链接。                               |
| `shareRequestDisplayIds` | `[String]`            | 仅返回这些展示 ID 的链接。                            |

<Tip>
  **推荐流程。** 首次调用时，仅按 `shareObjectStatuses` 过滤。响应会提供 `shareRequestBatchId` 和 `shareRequestDisplayId`；在后续调用（以及执行停用）中，使用这些值进行精确过滤。
</Tip>

### 响应字段（`GeneratedShareLink`）

| 字段                      | 类型                    | 说明                                     |
| ----------------------- | --------------------- | -------------------------------------- |
| `senderAppId`           | `String`              | 生成该链接的应用/开发者应用。                        |
| `shareRequestBatchId`   | `String`              | 在一次调用中生成的所有链接共享的批次标识符。                 |
| `shareRequestDisplayId` | `String`              | 便于人读的单链接标识符。                           |
| `shareObjectStatus`     | `ShareObjectStatus`   | `PENDING`、`ISSUED`、`USED` 或 `EXPIRED`。 |
| `recipientEmail`        | `String`              | 若通过邮件投递，收件人邮箱。                         |
| `recipientPhone`        | `String`              | 若通过短信投递，收件人手机号。                        |
| `linkExpirationDate`    | `DateTime`            | 链接到期/卡片冻结时间。                           |
| `virtualCardId`         | `String`              | 一旦被领取的已发行虚拟卡 ID。                       |
| `linkUrl`               | `String`              | 该链接的托管 URL。                            |
| `shareRequestDetails`   | `ShareRequestDetails` | 原始配置（卡限额、优惠、数量、投递、资金）。                 |

### 示例

<CodeGroup>
  ```json By status theme={null}
    { "input": { "shareObjectStatuses": ["PENDING", "ISSUED"] } }
  ```

  ```json By batch theme={null}
    { "input": { "shareRequestBatchIds": ["ABC123", "XYZ789"] } }
  ```

  ```json By display ID theme={null}
    { "input": { "shareRequestDisplayIds": ["SR-000001", "SR-000002"] } }
  ```
</CodeGroup>

## deactivateVCShareLinks

停用（使过期）你生成的链接——例如某个批次误发，或你需要撤销未被领取的链接。停用后链接状态将设为 `EXPIRED`；未被领取的链接将无法再被领取。

```graphql theme={null}
mutation DeactivateVCShareLinks($input: DeactivateVCShareLinksInput!) {
  deactivateVCShareLinks(input: $input)
}
```

### 输入字段

| 字段                       | 类型         | 说明                |
| ------------------------ | ---------- | ----------------- |
| `shareRequestBatchIds`   | `[String]` | 停用这些批次中的所有链接。     |
| `shareRequestDisplayIds` | `[String]` | 仅停用具有这些展示 ID 的链接。 |

从 `getVCShareLinks` 获取批次 ID。

```json theme={null}
{ "input": { "shareRequestBatchIds": ["ABC123"] } }
```

返回一个可读的确认字符串，例如 `"3 share requests successfully deactivated!"`。

<Warning>
  如果收件人已**领取**链接（状态为 `ISSUED`/`USED`），停用链接不会回收已发行的卡。若需停止已发行卡的消费，请使用相关的卡生命周期/冻结控制。
</Warning>

## 到期与冻结

链接的到期日期具有双重作用：

* **链接到期**——在此日期之后，**未领取**的链接将无法再被领取。
* **卡片冻结/锁定日期**——对于**已发行**的卡，此日期为锁定日期（当天结束时）。此后，卡片被冻结且无法消费。
* **卡片失效日**与冻结日期所在月份的月末对齐（例如，冻结日为 2026/6/15，则卡片失效日为 2026/6/30）。

在生成时通过 `daysUntilExpiration` 设置窗口。若省略，则使用项目默认（30 天）。该日期会展示给收件人（通常作为“有效期至”）——参见 [Recipient Experience](/features/open-loop-cards/open-loop-cards-recipient-experience)。

## 状态与错误参考

### 分享对象状态

| 状态        | 含义               |
| --------- | ---------------- |
| `PENDING` | 已生成链接，尚未被领取。     |
| `ISSUED`  | 收件人已领取链接；虚拟卡已发行。 |
| `USED`    | 已发行的卡已被使用。       |
| `EXPIRED` | 链接已到期或被停用；不可再领取。 |

对于当链接过期、被撤销或已被领取时收件人所见状态，参见[面向收件人的链接错误](/features/open-loop-cards/open-loop-cards-recipient-experience#recipient-facing-link-errors)。

### 常见 API 错误

| 原因                                                                                                            | 结果             |
| ------------------------------------------------------------------------------------------------------------- | -------------- |
| 缺失/无效的 Bearer 令牌或缺少 `CREATE_SHARE_LINK` 范围                                                                    | 请求被拒（未授权）。     |
| 收件人字段（`recipientListEmail`、`recipientListPhone`、`recipientUserIds` 或 `recipientRegistrations`）长度 ≠ `quantity` | 清晰的校验错误；不创建记录。 |
| `recipientUserIds` 引用了不存在的 Fluz 用户                                                                            | 校验错误；不创建记录。    |
| 优惠未激活、商户不可分享或无效的 `offerId`                                                                                    | 校验错误；不创建记录。    |
| 缺失/无效的 `userCashBalanceId`                                                                                    | 校验错误；不创建记录。    |

## 注意事项与限制

* **返回的 URL 为托管目的地而非短链。** 内部上，链接也会由短链服务包装，但 API 返回规范的托管 URL（`/virtual-prepaid-card/{share_request_id}`）。请按原样分发该 URL。
* 尽管模式标记为可选，`userCashBalanceId` 实际上是必填的。
* **隐藏/内部字段不属于此 API。** 对象类型与卡片类型固定为（`VIRTUAL_CARD` / `SINGLE_LOAD`）。银行账户与银行卡出资尚未启用；请勿发送。当前仅支持 `usePrepaymentBalance` 与 `useRewardsBalance` 作为额外资金来源。
* **不支持托管的礼品卡链接。** 此 API 仅用于虚拟卡。

## 下一步

<CardGroup cols={2}>
  <Card title="收件人体验" icon="user" href="/features/open-loop-cards/open-loop-cards-recipient-experience">
    收件人在打开托管链接时所见内容，以及管理其卡片的规则。
  </Card>

  <Card title="注册并发送" icon="id-card" href="/features/open-loop-cards/register-and-send">
    使用 `EXISTING_USER` 预先注册收件人并立即创建其卡片，而不是等到领取时。
  </Card>

  <Card title="创建批量订单" icon="layers" href="/features/create-bulk-order">
    一次性发行多张卡，用于程序化分发。
  </Card>
</CardGroup>
