> ## 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.

# Open Loop Cards 概覽

> 從你的平台產生受控託管的虛擬卡連結（「open-loop」Send Cards）。你只需呼叫一個 API 即可鑄造一或多個連結，然後以你喜歡的方式將連結交付給收件人——透過 email、SMS，或回傳原始 URL 以嵌入你自有流程。當收件人開啟連結時，他們會進入由 Fluz 託管的頁面、完成身分驗證，並領取由你的帳戶資助的一次性加值虛擬卡。

<Info>
  **先決條件：** 具備 `CREATE_SHARE_LINK` 權限範圍的 Bearer 存取權杖。Basic auth 會被拒絕。請聯絡你的業務代表以啟用存取。請參閱 [Authentication](/concepts/authentication)。
</Info>

<Note>
  **什麼是「託管」/「open-loop」。** *託管* 連結會導向 Fluz 託管的啟用頁面。*Open-loop* 指產生的虛擬卡為網路卡（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`，帶入 offer、卡片限額、數量、資金來源與遞送方式。每個連結代表一張卡，各自擁有其限額，並由你指定的消費帳戶資助。
  </Step>

  <Step title="Fluz 為每個連結建立一個分享請求">
    每個連結會對應一個分享請求（`PENDING`）與一個託管 URL。
  </Step>

  <Step title="連結被遞送">
    使用 `GENERATE_URL` 時，你會取得可自行分發的 URL。使用 `EMAIL` 或 `PHONE_NUMBER` 時，Fluz 會替你將連結發送給各收件人。
  </Step>

  <Step title="收件人啟用並領取卡片">
    收件人開啟連結並以一次性驗證碼驗證手機號碼——無須下載 App、無須密碼。卡片限額會在「領取時」而非「連結產生時」自你的消費帳戶扣抵。卡片在領取後不會自動顯示卡號；顯示時會提示收件人輸入其 PIN，或若尚未設定則先建立 PIN。完整流程請參見 [Recipient Experience](/features/open-loop-cards/open-loop-cards-recipient-experience)。
  </Step>
</Steps>

收件人僅成為該虛擬卡物件的授權使用者——他們不會取得你帳戶、餘額或其他卡片的存取權。

![Send cards flow diagram](https://files.readme.io/c19029bfeaca196f7eadc2f020ab56f2aad893261f561307bec543e700f0d74b-diagram.svg)

## 可用性與範圍

| 能力            | 狀態            |
| ------------- | ------------- |
| 單次加值虛擬卡       | ✅ 支援          |
| 單次使用虛擬卡       | ✅ 支援          |
| 可重複加值卡        | ❌ 不支援         |
| 透過 API 產生連結   | ✅ 支援          |
| 透過 CSV 匯入產生連結 | ❌ 即將推出        |
| 託管的「禮品卡」連結    | ❌ 不在範圍內（僅虛擬卡） |

卡片分享連結的物件類型為 `VIRTUAL_CARD`，卡片類型為 `SINGLE_LOAD`。

<Warning>
  **禮品卡：** 儘管更廣義的計畫以「虛擬卡與禮品卡」為框架，目前沒有託管的禮品卡領取流程。禮品卡餘額僅作為「託管虛擬卡的潛在資金來源」出現（規劃中，尚未啟用）。文件與開發請僅以虛擬卡為準。
</Warning>

## 作業參考

共有三個公開作業，皆受 `CREATE_SHARE_LINK` 權限範圍控管：

| 作業                       | 類型       | 目的             |
| ------------------------ | -------- | -------------- |
| `generateVCShareLinks`   | Mutation | 建立一或多個託管虛擬卡連結  |
| `getVCShareLinks`        | Query    | 列出/檢視先前產生的連結   |
| `deactivateVCShareLinks` | Mutation | 停用（使失效）你所產生的連結 |

所有 Send Cards 作業皆在 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!`                               | 是   | 此卡綁定的商家 offer 之 UUID v4。該 offer 必須為啟用中，且其商家須可分享。                                                                         |
| `quantity`               | `Int!`                                  | 是   | 要產生的連結數量。每個單位都會建立一個獨立的託管 URL。                                                                                            |
| `shareMethod`            | `ShareMethodType!`                      | 是   | 識別收件人並遞送連結的方式：`GENERATE_URL`、`EMAIL`、`PHONE_NUMBER`、`EXISTING_USER` 或 `REGISTER_USER`。                                   |
| `userCashBalanceId`      | `UUID`                                  | 是\* | 用於資助卡片的消費帳戶。*雖於 schema 標示為選填，但實務上必填——省略將未通過驗證。*                                                                          |
| `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。如何使用的更多資訊請參見：[How funding works](/features/virtual-cards#how-funding-works)。              |
| `useRewardsBalance`      | `Boolean`                               | 否   | 設定是否使用你的 Fluz 獎勵餘額作為額外資金來源。預設為 false。如何使用的更多資訊請參見：[How funding works](/features/virtual-cards#how-funding-works)。        |

<Note>
  **資金來源。** `userCashBalanceId`（屬於你作為發送者帳戶的消費帳戶）是主要且必須的資金來源。可選擇將 `usePrepaymentBalance` 與/或 `useRewardsBalance` 設為 `true`，以便在領取時若消費帳戶餘額不足時，Fluz 可回退使用你的預付或獎勵餘額。銀行帳戶與銀行卡不支援作為資金來源。
</Note>

### 收件人識別與遞送方式（`shareMethod`）

| 值               | 行為                                                      | 收件人欄位                                         | 建卡時機        |
| --------------- | ------------------------------------------------------- | --------------------------------------------- | ----------- |
| `GENERATE_URL`  | Fluz 於回應中回傳託管 URL。你自行分發。                                | 兩個清單皆須為空/省略。                                  | 於領取時        |
| `EMAIL`         | Fluz 以 email 將連結寄給各收件人。                                 | 需提供 `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` 必須是 **啟用中** 的 offer 的有效 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`              | 若以 email 遞送，則為收件人 email。               |
| `recipientPhone`        | `String`              | 若以 SMS 遞送，則為收件人電話。                     |
| `linkExpirationDate`    | `DateTime`            | 連結到期／卡片凍結的時間。                          |
| `virtualCardId`         | `String`              | 一旦領取後，所發行的虛擬卡 ID。                      |
| `linkUrl`               | `String`              | 連結的託管 URL。                             |
| `shareRequestDetails`   | `ShareRequestDetails` | 原始設定（卡片限額、offer、數量、遞送、資金）。             |

### 範例

<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 使用者                                                                            | 驗證錯誤；不會建立任何紀錄。    |
| offer 未啟用、商家不可分享，或 `offerId` 無效                                                                               | 驗證錯誤；不會建立任何紀錄。    |
| 缺少/無效的 `userCashBalanceId`                                                                                    | 驗證錯誤；不會建立任何紀錄。    |

## 備註與限制

* **回傳的 URL 為託管目標，非短連結。** 內部會以短連結服務包裹，但 API 回傳的是正規託管 URL（`/virtual-prepaid-card/{share_request_id}`）。請按回傳值原樣分發。
* 儘管 schema 標示為選填，`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="Register & Send" 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>
