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

# 您的第一筆禮品卡購買

> 從頭到尾執行完整的快樂路徑——儲值、瀏覽商家、購買禮品卡並揭露卡片資訊——全部在沙盒環境中完成。

本快速入門會帶你從全新的 Fluz 帳戶完成一筆預備環境（staging）的購買流程。你將註冊應用程式、鑄造具範圍的存取權杖，接著儲值、瀏覽商家目錄、購買禮品卡並揭露其兌換細節——全部在沙盒中進行，不會有真實金錢流動。

<Info>
  **先決條件**

  * 一個 **Fluz 帳戶**——你將在下方步驟 1–2 建立預備環境 API 認證並鑄造存取權杖。
  * 請將請求送至 **沙盒** GraphQL 端點。這裡不會向真實卡片收費——參見 [Staging vs. Live Environment](/concepts/environments)。
  * 每個 mutation 都需要唯一的 `idempotencyKey`（由用戶端產生的 UUID），以確保請求只會被處理一次——參見 [Idempotency](/concepts/idempotency)。
</Info>

## 在你開始之前

除了鑄造你的權杖（步驟 2）之外，其餘每個呼叫都是對單一 GraphQL 端點的 `POST` 請求，並以你的 Bearer 權杖進行驗證：

<CodeGroup>
  ```bash Endpoint & headers theme={null}
  POST https://transactional-graph.staging.fluzapp.com/api/v1/graphql

  Authorization: Bearer <YOUR_USER_ACCESS_TOKEN>
  Content-Type: application/json
  ```

  ```bash Example request (cURL) theme={null}
  curl -X POST https://transactional-graph.staging.fluzapp.com/api/v1/graphql \
    -H "Authorization: Bearer <YOUR_USER_ACCESS_TOKEN>" \
    -H "Content-Type: application/json" \
    -d '{
      "query": "query getWallet { getWallet { bankCards { bankCardId lastFourDigits } } }"
    }'
  ```
</CodeGroup>

<Tip>
  你的沙盒帳戶隨附一張**預先加入的測試銀行卡**，可立即執行整個流程。你也可以從 [Sandbox Accounts page](https://uni.staging.fluzapp.com/accounts-and-cards) 新增更多測試卡片或銀行帳戶，所需數值請參考 [Test Bank Cards](/test-bank-cards) 清單。
</Tip>

## 完整流程

<Steps>
  <Step title="註冊並註冊應用程式" icon="user-plus">
    前往 [fluz.app](https://fluz.app) 建立 Fluz 帳戶，接著開啟 [Developer Console](https://uni.staging.fluzapp.com/developers) 並建立新的 **Staging** 應用程式。當憑證出現時，複製你的 `clientId` 與 `clientSecret`——secret 只會顯示一次。

    完整操作指南：[準備你的帳戶](/get-started/prepare-accounts)。
  </Step>

  <Step title="以認證交換存取權杖" icon="key">
    使用你的應用程式認證鑄造一個短效、具範圍的使用者存取權杖。此呼叫會送往 OAuth 服務；後續每個呼叫都使用回傳的權杖作為 Bearer 憑證。

    <CodeGroup>
      ```bash Mint token (cURL) theme={null}
      curl -X POST https://oauth-service.fluzapp.com/graphql \
        -H "Content-Type: application/json" \
        -d '{
          "query": "mutation ($clientId: String!, $clientSecret: String!, $scopes: [Scope!]!) { generateUserAccessToken(clientId: $clientId, clientSecret: $clientSecret, scopes: $scopes) { accessToken expiresIn scopes } }",
          "variables": {
            "clientId": "<APPLICATION_CLIENT_ID>",
            "clientSecret": "<APPLICATION_CLIENT_SECRET>",
            "scopes": ["LIST_OFFERS", "LIST_PAYMENT", "MANAGE_PAYMENT", "PURCHASE_GIFTCARD", "REVEAL_GIFTCARD"]
          }
        }'
      ```

      ```json Response theme={null}
      {
        "data": {
          "generateUserAccessToken": {
            "accessToken": "eyJhbGciOi...",
            "expiresIn": 3600,
            "scopes": ["LIST_OFFERS", "LIST_PAYMENT", "MANAGE_PAYMENT", "PURCHASE_GIFTCARD", "REVEAL_GIFTCARD"]
          }
        }
      }
      ```
    </CodeGroup>

    儲存回傳的 `accessToken`，並在以下每個請求以 `Authorization: Bearer <accessToken>` 夾帶。權杖為短效（`expiresIn` 為秒）——請在伺服器端鑄造並於到期前更新。完整細節：[API credentials](/get-started/api-credentials)。

    <Tip>
      Scopes 會限制權杖可執行的動作——只包含你的流程所需：`MANAGE_PAYMENT` 用於新增資金來源與儲值、`LIST_OFFERS` 用於瀏覽目錄，`PURCHASE_GIFTCARD` / `REVEAL_GIFTCARD` 用於購買與揭露。
    </Tip>

    <Warning>
      切勿在瀏覽器或行動端用戶端暴露 `clientSecret`。請於伺服器端鑄造權杖，並只轉發該權杖。
    </Warning>
  </Step>

  <Step title="將資金存入你的 Fluz 餘額" icon="wallet">
    預先將資金存入 Fluz 餘額通常能讓禮品卡購買更快，並可繞過部分速度限制檢查。你可以用兩種方式儲值：

    * **手動**，透過 [sandbox Fluz website](https://uni.staging.fluzapp.com/manage-money)。
    * **程式化**，使用 `depositCashBalance` mutation（如下所示）。

    <Accordion title="首先，擷取付款方式 ID（getWallet）" icon="id-card">
      若要透過 API 儲值，你需要資金來源的 ID（例如 `bankCardId`）。執行 `getWallet` 並保存你要使用的 ID。

      ```bash theme={null}
      curl -X POST https://transactional-graph.staging.fluzapp.com/api/v1/graphql \
        -H "Authorization: Bearer <YOUR_USER_ACCESS_TOKEN>" \
        -H "Content-Type: application/json" \
        -d '{
          "query": "query getWallet { getWallet { bankCards { bankCardId lastFourDigits } bankAccounts { bankAccountId } } }"
        }'
      ```

      從回應中擷取 `bankCardId` 或 `bankAccountId`。透過 API 管理資金來源需要 `MANAGE_PAYMENT` scope。更多細節：[View Funding Sources](/features/view-funding-sources)。
    </Accordion>

    ### 進行儲值

    使用 `depositCashBalance` mutation。它接收一個 `DepositCashBalanceInput` 物件。

    <CodeGroup>
      ```graphql Mutation theme={null}
      mutation depositCashBalance($input: DepositCashBalanceInput!) {
        depositCashBalance(input: $input) {
          cashBalanceDeposits {
            cashBalanceDepositId
            depositDisplayId
            depositAmount
            status
            cashBalanceDepositType
          }
          balances {
            cashBalance { availableBalance totalBalance pendingBalance }
            giftCardCashBalance { availableBalance totalBalance pendingBalance }
          }
        }
      }
      ```

      ```json Variables theme={null}
      {
        "input": {
          "idempotencyKey": "0284be6f-1a69-44f7-9da0-5b5edaf45d19",
          "amount": 100.00,
          "depositType": "CASH_BALANCE",
          "bankCardId": "0284be6f-1a69-44f7-9da0-5b5edaf45d19"
        }
      }
      ```
    </CodeGroup>

    #### 輸入欄位

    <ParamField body="idempotencyKey" type="string" required>
      由用戶端產生的唯一 UUID，可確保儲值只被處理一次。
    </ParamField>

    <ParamField body="amount" type="Float" required>
      要儲值的金額。
    </ParamField>

    <ParamField body="depositType" type="CashBalanceDepositType">
      目標餘額。可為 `CASH_BALANCE`、`GIFT_CARD_BALANCE` 或 `RESERVE_BALANCE`。
    </ParamField>

    <ParamField body="Funding source" type="UUID">
      資金來源——提供 `bankAccountId`、`bankCardId` 或 `paypalVaultId` 三者之一即可。
    </ParamField>

    <ParamField body="merchantCategoryCode" type="Int">
      僅適用於 `GIFT_CARD_BALANCE`。用於分類商家的四位數 MCC。使用 `getMccList` 取得有效值。
    </ParamField>

    <ParamField body="userCashBalanceId" type="UUID">
      當選擇 `CASH_BALANCE` 時，指定要儲值的特定消費帳戶。
    </ParamField>

    <Accordion title="範例回應" icon="code">
      ```json theme={null}
      {
        "data": {
          "depositCashBalance": {
            "cashBalanceDeposits": [
              {
                "cashBalanceDepositId": "5f5c5b5f-4c4b-4e4e-9e9e-4f4f4f4f4f4f",
                "depositDisplayId": "102370",
                "depositAmount": "100.00",
                "status": "COMPLETED",
                "cashBalanceDepositType": "CASH_BALANCE"
              }
            ],
            "balances": {
              "cashBalance": {
                "availableBalance": "100.00",
                "totalBalance": "100.00",
                "pendingBalance": "0.00"
              },
              "giftCardCashBalance": {
                "availableBalance": "0.00",
                "totalBalance": "0.00",
                "pendingBalance": "0.00"
              }
            }
          }
        }
      }
      ```
    </Accordion>

    <Note>
      視資金來源與結算方式不同，儲值可能即時或於 2–5 個工作天內入帳。回應中的 `balances` 物件會反映你目前的可用餘額。隨時重新查詢請參見 [Check Account Balance](/check-account-balance)。
    </Note>
  </Step>

  <Step title="瀏覽商家並選擇優惠" icon="store">
    準備好餘額後，使用 `getMerchants` 取得可用商家與其現金回饋優惠的目錄。

    <CodeGroup>
      ```graphql Query theme={null}
      query GetMerchantCatalog(
        $paginate: OffsetInput
        $offerTypes: OfferTypesInput
        $filterBy: FilterByInput
      ) {
        getMerchants(paginate: $paginate, offerTypes: $offerTypes, filterBy: $filterBy) {
          merchantId
          name
          slug
          logoUrl
          faceplateUrl
          offers {
            offeringMerchantId
            offerId
            type
            deliveryFormat
            barcodeType
            offerRates {
              maxUserRewardValue
              cashbackVoucherRewardValue
              boostRewardValue
              denominations
              allowedPaymentMethods
            }
          }
        }
      }
      ```

      ```json Variables theme={null}
      {
        "paginate": { "limit": 20, "offset": 0 },
        "offerTypes": { "giftCardOffer": true, "cardLinkedOffer": false }
      }
      ```
    </CodeGroup>

    <Tip>
      目錄預設依現金回饋百分比排序，因此最高回饋會優先顯示。
    </Tip>

    #### 實用參數

    <ParamField query="name" type="String">
      以名稱篩選商家。
    </ParamField>

    <ParamField query="paginate" type="OffsetInput">
      `{ limit, offset }`。預設與最大 `limit` 皆為 `20`。
    </ParamField>

    <ParamField query="offerTypes" type="OfferTypesInput">
      指定回傳哪些優惠型別的布林旗標，例如 `{ giftCardOffer: true, cardLinkedOffer: false }`。
    </ParamField>

    <ParamField query="filterBy" type="FilterByInput">
      篩選每個商家內的優惠，例如依 `deliveryFormat`（`URL`、`CODES`、`PIN_AS_CODE`、`PIN_WITH_URL`）。
    </ParamField>

    <Note>
      **分頁機制：** 回應可能回傳少於你設定的 `limit`。若要拉取**完整**目錄，請持續將 `offset` 以 `limit` 遞增，直到 API 回傳空陣列（`[]`）為止。未篩選的目錄很大——最多每日抓取一次，並盡量使用 `name` 或 `offerTypes` 進行精準查找。
    </Note>

    <Accordion title="選配：取得單一商家的最佳優惠（getOfferQuote）" icon="badge-percent">
      若你已知道商家與金額，`getOfferQuote` 會直接回傳最佳可用優惠——包含即時庫存資訊。

      <CodeGroup>
        ```graphql Query theme={null}
        query getOfferQuote($input: GetOfferQuoteInput!) {
          getOfferQuote(input: $input) {
            offeringMerchantId
            offerId
            type
            hasStockInfo
            denominationsType
            termsAndConditions
            offerRates {
              maxUserRewardValue
              denominations
              allowedPaymentMethods
            }
            stockInfo {
              ... on StockInfoVariableType {
                __typename
                description
                maxDenomination
                minDenomination
              }
              ... on StockInfoFixedType {
                __typename
                denomination
                availableStock
              }
            }
          }
        }
        ```

        ```json Variables theme={null}
        {
          "input": {
            "slug": "starbucks",
            "denomination": 50.00,
            "paymentMethod": "FLUZPAY"
          }
        }
        ```
      </CodeGroup>

      `slug` 與 `denomination` 為必填。`paymentMethod` 預設為 `FLUZPAY`（你的 Fluz 餘額），亦接受 `BANK_CARD`、`BANK_ACCOUNT`、`PAYPAL`、`APPLE_PAY` 與 `GOOGLE_PAY`。
    </Accordion>

    <Warning>
      現金回饋比率會定期變動。購買前請務必確認當前比率。
    </Warning>
  </Step>

  <Step title="購買禮品卡" icon="credit-card">
    使用 `purchaseGiftCard` mutation。你可以用兩種方式指定要購買的目標：

    <Tabs>
      <Tab title="選項 A — 商家 slug（建議）">
        傳入 `merchantSlug`，Fluz 會自動套用該商家的最佳可用優惠。

        <CodeGroup>
          ```graphql Mutation theme={null}
          mutation purchaseGiftCard($input: PurchaseGiftCardInput!) {
            purchaseGiftCard(input: $input) {
              purchaseDisplayId
              purchaseAmount
              giftCard {
                giftCardId
                status
                termsAndConditions
              }
            }
          }
          ```

          ```json Variables theme={null}
          {
            "input": {
              "idempotencyKey": "0284be6f-1a69-44f7-9da0-5b5edaf45d19",
              "merchantSlug": "starbucks",
              "amount": 50.00,
              "balanceAmount": 50.00
            }
          }
          ```
        </CodeGroup>
      </Tab>

      <Tab title="選項 B — 指定 offer ID">
        傳入你從目錄中選擇的 `offerId`，以購買該特定優惠。

        <CodeGroup>
          ```graphql Mutation theme={null}
          mutation purchaseGiftCard($input: PurchaseGiftCardInput!) {
            purchaseGiftCard(input: $input) {
              purchaseDisplayId
              purchaseAmount
              giftCard {
                giftCardId
                status
                termsAndConditions
              }
            }
          }
          ```

          ```json Variables theme={null}
          {
            "input": {
              "idempotencyKey": "0284be6f-1a69-44f7-9da0-5b5edaf45d19",
              "offerId": "0284be6f-1a69-44f7-9da0-5b5edaf45d19",
              "amount": 50.00,
              "balanceAmount": 50.00
            }
          }
          ```
        </CodeGroup>
      </Tab>
    </Tabs>

    #### 輸入欄位

    <ParamField body="idempotencyKey" type="string" required>
      由用戶端產生的唯一 UUID，確保購買只被處理一次。
    </ParamField>

    <ParamField body="offerId / merchantSlug" type="UUID / String" required>
      僅需提供**其一**。`merchantSlug` 會自動選擇最佳回饋；`offerId` 則指定特定優惠。
    </ParamField>

    <ParamField body="amount" type="Float" required>
      要購買的禮品卡金額。
    </ParamField>

    <ParamField body="Funding source" type="UUID / Float">
      付款來源。使用 `balanceAmount`（Fluz 餘額）、`bankAccountId`、`bankCardId` 或 `paypalVaultId`。你可將 Fluz 餘額與另一來源合併支付。
    </ParamField>

    <ParamField body="defaultToBalance" default="true" type="Boolean">
      若其他付款方式失敗，則退回使用你的 Fluz 餘額。設為 `false` 可停用此後援機制。
    </ParamField>

    <ParamField body="minRewardRate" type="Float">
      以 `merchantSlug` 購買時可接受的最低回饋比率。
    </ParamField>

    <ParamField body="exclusiveRateId" type="UUID">
      強制使用特定的專屬比率。可在 `getMerchants` 中找到 `EXCLUSIVE_RATE_OFFER` 類型的 `exclusiveRateId`。
    </ParamField>

    <ParamField body="userCashBalanceId" type="UUID">
      要扣款的消費帳戶（現金餘額）。
    </ParamField>

    <ParamField body="memo / transactionCategory / attachmentId" type="String / String / UUID">
      選用的支出中繼資料。`memo` 最多 255 字元；類別會在首次使用時建立。參見 [Add Expense Details](/features/add-expense-details)。
    </ParamField>

    <Accordion title="範例回應" icon="code">
      ```json theme={null}
      {
        "data": {
          "purchaseGiftCard": {
            "purchaseDisplayId": "1019688",
            "purchaseAmount": 50.00,
            "giftCard": {
              "giftCardId": "85de8b3e-4e72-462c-8ed1-a6f4982e22f7",
              "status": "ACTIVE",
              "termsAndConditions": "Except as required by law, Gift Cards cannot be transferred for..."
            }
          }
        }
      }
      ```
    </Accordion>

    <Info>
      請保留回應中的 `giftCardId`——你將在下一步使用它來揭露卡片。若購買失敗，請查看 [Gift Card Error Codes](/gift-card-error-codes)。
    </Info>
  </Step>

  <Step title="揭露禮品卡詳細資訊" icon="eye">
    最後，取回可兌換的詳細資料（代碼、PIN 和/或 URL）。

    <Accordion title="沒有 giftCardId？先列出你的卡片（getGiftCards）" icon="list">
      若你剛於步驟 3 取得 `giftCardId`，可略過此步。否則請先列出你的禮品卡：

      ```graphql theme={null}
      query GetGiftCards {
        getGiftCards(paginate: { limit: 20, offset: 0 }) {
          giftCardId
          status
          createdAt
          deliveryFormat
          termsAndConditions
          merchant { merchantId name slug }
        }
      }
      ```

      你可以使用 `status` 與 `userCashBalanceId` 進行篩選，並透過 `paginate` 分頁。
    </Accordion>

    ### 揭露兌換細節

    使用 `giftCardId` 呼叫 `revealGiftCardByGiftCardId`。

    <CodeGroup>
      ```graphql Mutation theme={null}
      mutation RevealGiftCard($giftCardId: UUID!) {
        revealGiftCardByGiftCardId(giftCardId: $giftCardId) {
          code
          pin
          url
          termsAndConditions
        }
      }
      ```

      ```json Variables theme={null}
      {
        "giftCardId": "7c57c381-4b19-49e4-bbb0-404a45166ee4"
      }
      ```
    </CodeGroup>

    <Accordion title="範例回應" icon="code">
      ```json theme={null}
      {
        "data": {
          "revealGiftCardByGiftCardId": {
            "code": "9877890000000000",
            "pin": "2014",
            "url": null,
            "termsAndConditions": "Except as required by law, Gift Cards cannot be transferred for..."
          }
        }
      }
      ```
    </Accordion>

    <Note>
      兌換欄位會依商家而異。有些卡只回傳英數 `code` 而無 `pin`；有些則只回傳 `url`。請一律依 **`getGiftCards`** 回傳的 `deliveryFormat` 來呈現——因為商家的有效優惠在購買後可能更動，而 `getGiftCards` 反映的是實際購買時的格式。
    </Note>

    <Warning>
      **尚未立即回傳詳細資料？** 請以指數退避輪詢 `revealGiftCardByGiftCardId`：由 **300ms** 開始，每次加倍（300 → 600 → 1200 → 2400ms…）直到最大延遲 **180000ms（3 分鐘）**。一旦取得資料即停止。這能在回應速度、系統負載與避免不必要逾時之間取得平衡。
    </Warning>
  </Step>
</Steps>

## 大功告成 🎉

你已完成一次完整交易——儲值餘額、瀏覽優惠、購買禮品卡並揭露其資訊。接下來可探索其餘 API：

<CardGroup cols={2}>
  <Card title="Virtual Cards" icon="credit-card" href="/features/virtual-cards">
    核發並管理可用於支付網路的虛擬卡。
  </Card>

  <Card title="Wallets & Transfers" icon="wallet" href="/features/create-spend-accounts">
    開立消費帳戶並在其間轉移資金。
  </Card>

  <Card title="Transaction Activity" icon="receipt" href="/features/get-all-transactions">
    擷取、篩選並標註交易紀錄。
  </Card>

  <Card title="Embedded Widgets" icon="book-copy" href="/developers/widgets">
    將 Fluz 的流程直接嵌入你的自有介面。
  </Card>
</CardGroup>

<Note>
  **想了解更多？** 聯絡我們： [support@fluz.app](mailto:support@fluz.app)，與我們的專家對談或申請示範。
</Note>
