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

# 引导并验证企业

> 端到端跑通企业主路径：配置一个仅限企业账户的应用、引导所有者完成 OAuth、注册法律实体，并跟踪 KYB 直到企业账户获批。

本快速入门是面向企业版的[引导并连接客户](/quickstart/onboard-customers)。你将配置一个只能落在企业账户上的应用，引导一位所有者走完 OAuth 授权，注册其法律实体与受益所有人名单，跟踪 KYB 案件直至出结果，并通过在企业账户上发行一张卡来证明连接已打通。

你面前的人始终首先是一个自然人。他以本人身份登录、以本人身份完成身份验证，\_然后\_才注册企业。下面的所有步骤都遵循这个顺序。

<Info>
  **前提条件**

  * 一个**带有预发布应用的 Fluz 账户** —— 参见[准备你的账户](/get-started/prepare-accounts)和 [API 凭证](/get-started/api-credentials)。
  * 一个 `client_id`、一个 `client_secret` 和一个已注册的 `redirect_uri` —— 参见[配置 OAuth 应用](/create-an-o-auth-app)。
  * 请求指向**沙盒** —— 没有真实资金、没有真实 PII。参见[预发布与生产环境](/concepts/environments)。
</Info>

## 完整流程

<Steps>
  <Step title="为企业配置应用" icon="sliders">
    你的应用在 **Permissions** 标签页下有两份权限清单，二者独立编辑。

    | 清单                       | 应放入的内容                                                            |
    | :----------------------- | :---------------------------------------------------------------- |
    | **Permissions**          | 你的应用可以在**个人**账户上执行的操作。请包含**注册企业**的权限 —— 该权限由个人账户授予，因此只属于这里，不属于别处。 |
    | **Business permissions** | 企业账户一旦存在，你的应用可以在其上执行的操作。                                          |

    **Business permissions** 清单非空，才会让你的应用启用企业能力。留空的话，无论你还配置了什么，用户都不会看到申请企业账户的选项。

    趁着还在应用编辑器里，把你的 `redirect_uri` 注册到 **OAuth** 标签页，并添加一个订阅了 **KYB 状态更新**的 webhook URL —— 这样你不用轮询就能知道审核已结束。

    <Warning>
      同意页面是依据这两份清单构建的，而不是依据你的 authorize URL。你没有在此处勾选的权限，永远不会展示给用户，也永远不会出现在令牌里。完整说明：[OAuth 中的企业账户](/business-accounts-in-o-auth)。
    </Warning>
  </Step>

  <Step title="请 Fluz 将应用限制为仅企业账户" icon="building-lock">
    Fluz 可以为你的应用做配置，使该流程永远不会落在个人账户上。配置到位后，没有企业账户的用户会完全跳过账户选择器，直接进入企业注册。

    <Warning>
      **这一项不是自助的。** 请联系你的 Fluz 客户经理，为你的应用启用该限制。在此之前，没有企业账户的用户会同时看到自己的个人账户和申请企业账户的选项 —— 其中一些人会选个人账户。
    </Warning>

    如果你的应用确实同时服务于消费者与企业，请跳过此步。提出这个要求，等于声明落在个人账户上的授权对你而言永远是一个 bug。
  </Step>

  <Step title="引导所有者完成 OAuth 授权" icon="plug">
    构建 authorize URL：

    ```text Authorization URL theme={null}
        https://uni.staging.fluzapp.com/authorize
          ?response_type=code
          &client_id=<YOUR_OAUTH_CLIENT_ID>
          &redirect_uri=<YOUR_REGISTERED_REDIRECT_URI>
          &state=<UNGUESSABLE_VALUE_YOU_STORED>
          &external_id=<YOUR_ID_FOR_THIS_ACCOUNT>
    ```

    在应用被限制为仅企业账户的情况下，首次进入的用户会看到：登录与 2FA，然后是一个包含**两组权限**的同意页面 —— 现在授予的消费者权限，以及为其即将创建的企业预先批准的企业权限。该清单是只读的；用户要么全部接受，要么无法完成。

    随后用户会被重定向回你的 `redirect_uri`，并带有 `?code=...&state=...`。先校验 `state`，再在服务端捕获这个一次性的 `code`。

    <Tip>
      给**企业**分配它自己的 `external_id`，取自你系统里的企业记录 —— 而不是取自所有者用户。外部 ID 在首次使用时就会绑定到唯一一个 Fluz 账户，所以一个已经用在某人个人账户上的 ID，无法再用于他的企业。
    </Tip>

    完整参数参考：[面向客户端的 OAuth 授权流程](/client-facing-o-auth-grant-flow)。
  </Step>

  <Step title="用授权码交换申请人的令牌" icon="key">
    与其他任何授权的交换过程相同 —— 使用 `client_id:client_secret` 的 base64 做基本认证：

    ```bash Exchange (cURL) theme={null}
        curl -X GET "https://uni.staging.fluzapp.com/token/exchange?code=<AUTH_CODE>&redirect_uri=<YOUR_REGISTERED_REDIRECT_URI>" \
          -H "Authorization: Basic <base64 of CLIENT_ID:CLIENT_SECRET>"
    ```

    `redirect_uri` 必须与你在 `/authorize` 使用的完全一致（逐字节相同）。

    <Note>
      企业此时尚不存在，因此这个令牌属于申请人的**个人**账户 —— 这是正确的，也正是 `registerBusiness` 所需要的令牌。请从交换响应中读取账户并持久化，而不要根据你自己关于“谁发起了流程”的记录去推断。
    </Note>
  </Step>

  <Step title="验证申请人身份（KYC）" icon="id-card">
    KYB 验证的是企业和\_其他\_所有者。它不验证申请人，所以申请人必须在你注册任何东西之前就已通过验证 —— 否则注册会以 `ARG-0001` 失败。

    使用申请人的令牌调用 `verifyUserInformation`。在预发布环境中，这个测试身份始终返回 `APPROVED`：

    <CodeGroup>
      ```graphql Mutation theme={null}
          mutation VerifyUserInformation(
            $firstName: String!
            $lastName: String!
            $streetLine1: String!
            $city: String!
            $state: String!
            $postalCode: String!
            $country: String!
            $dateOfBirth: String!
            $ssnLast4: String!
          ) {
            verifyUserInformation(
              firstName: $firstName
              lastName: $lastName
              streetLine1: $streetLine1
              city: $city
              state: $state
              postalCode: $postalCode
              country: $country
              dateOfBirth: $dateOfBirth
              ssnLast4: $ssnLast4
            ) {
              status
              message
            }
          }
      ```

      ```json Variables (approved test identity) theme={null}
          {
            "firstName": "John",
            "lastName": "Smith",
            "streetLine1": "222333 Peachtree Place",
            "city": "Atlanta",
            "state": "GA",
            "postalCode": "30318",
            "country": "United States",
            "dateOfBirth": "02/28/1975",
            "ssnLast4": "3333"
          }
      ```
    </CodeGroup>

    申请人需要哪种检查，取决于你在下一步为他填写的 `isUsPerson`：`true` 要求档案中已有一次成功的 SSN（CIP）验证，`false` 要求一次成功的证件验证。参见[用户 KYC 验证](/user-kyc-verification)和[测试 KYC 流程](/test-kyc-flows)。

    <Warning>
      同一用户最多可提交 **3 次**，之后将返回 `ERROR`。不要把尝试次数浪费在你即将用作申请人的那个用户身上。
    </Warning>
  </Step>

  <Step title="注册企业" icon="building">
    先解析该实体所属的经营类别 —— 千万不要把这些 UUID 写死：

    ```graphql theme={null}
        query {
          getBusinessCategories {
            id
            name
            subCategories { id name }
          }
        }
    ```

    然后在一次调用中提交实体信息和完整的所有者名单，仍然使用申请人的**个人账户**令牌：

    <CodeGroup>
      ```graphql Mutation theme={null}
          mutation RegisterBusiness($input: RegisterBusinessInput!) {
            registerBusiness(input: $input) {
              accountId
              kybStatus
              success
              error { code message }
            }
          }
      ```

      ```json Variables theme={null}
          {
            "input": {
              "businessName": "Acme Corporation",
              "dbaName": "Acme Co",
              "businessStructure": "LLC",
              "businessLegalAddress": {
                "streetAddressLine1": "123 Main Street",
                "city": "San Francisco",
                "state": "California",
                "postalCode": "94102",
                "country": "United States"
              },
              "stateOfIncorporation": "California",
              "taxId": "12-3456789",
              "businessCategoryId": "<FROM_getBusinessCategories>",
              "businessSubCategoryId": "<FROM_getBusinessCategories>",
              "natureOfBusiness": "E-commerce retail",
              "businessAccountUsage": ["CORPORATE_SPENDING_ADMIN"],
              "externalReferenceId": "your-business-id-123",
              "confirm": {
                "allOwnersWith25PercentageOwnershipListed": true,
                "noOwnersMoreThan25Percentage": false
              },
              "owners": [
                {
                  "firstName": "John",
                  "lastName": "Smith",
                  "emailAddress": "john@example.com",
                  "phoneNumber": "+14155551234",
                  "title": "Chief Executive Officer",
                  "ownershipPercentage": 100,
                  "isControlPerson": true,
                  "isInvited": false,
                  "isUsPerson": true
                }
              ]
            }
          }
      ```
    </CodeGroup>

    成功的响应会返回一个 `accountId` 和值为 `SUBMITTED` 的 `kybStatus`。请立刻保存 `accountId` —— 它是你掌握这份申请的唯一句柄。

    三件最常导致首次提交被拒的事：

    * **每一位所有者都必须填写 `isUsPerson`**，包括申请人和受邀所有者。这是名单被拒的最常见原因。
    * **必须恰好有一位所有者是申请人** —— 通过邮箱或电话与令牌所属用户匹配 —— 且必须恰好有一位是控制人。
    * **法定地址会经过地址校验服务商核验。** 编造的街道会以 `BS-0002` 失败；请使用[测试地址](/test-addresses)。

    <Warning>
      错误是**放在响应负载内部**返回的，而不是作为 GraphQL 错误 —— 请根据 `success` 和 `error` 对象分支处理。完整的参数与错误参考：[注册企业](/business-registration)。
    </Warning>
  </Step>

  <Step title="让其余所有者完成验证，然后等待" icon="users">
    `SUBMITTED` 表示负载通过了校验并且案件已建立。它不表示已获批准。

    生成一个**企业账户**令牌 —— 用申请人的 `userId` 和新企业的 `accountId` 调用 `generateUserAccessToken` —— 然后读取所有者名单：

    ```graphql theme={null}
        query GetBusiness {
          getBusiness {
            accountId
            kybStatus
            owners { id name verificationType status }
          }
        }
    ```

    走证件路径的所有者可通过 [requestOwnerDocumentVerificationLink](/owner-verification-link) 获取链接；被你标记为 `isInvited: true` 的所有者由 Fluz 发邮件邀请并自行完成验证。持续跟踪，直到 `kybStatus` 为终态**且**每位所有者都报告 `READY`。

    状态会从 `PENDING` 变为 `APPROVED` 或 `DECLINED`，通常在一到两个工作日内。请从你在第 1 步配置的 **KYB 状态更新** webhook 获取结果，并用 `getBusiness` 做对账 —— 每小时一次即可，不要每次页面加载都查。

    <Note>
      向用户如实展示“审核中”的状态。不要把他丢进一个还不能交易的企业面板，也不要在被拒后自动重试 —— 第二次提交会被 `BS-0007` 阻止。完整生命周期：[注册企业](/business-registration)。
    </Note>
  </Step>

  <Step title="在企业账户上操作 —— 证明它" icon="credit-card">
    一旦 `kybStatus` 变为 `APPROVED`，用**企业账户**令牌运行任意 Fluz 操作，它就会针对该企业执行。没有单独的企业 API。

    ```graphql theme={null}
        mutation {
          createVirtualCard(
            input: {
              idempotencyKey: "9f2c4d61-77aa-4b0e-8f2a-1c9d3e5b7a04"
              offerId: "ed669305-5e43-40a0-9a25-7a15ed174628"
              spendLimit: 250.00
              lockCardNextUse: true
              cardNickname: "First card on a verified business"
            }
          ) {
            virtualCardId
            virtualCardLast4
            status
          }
        }
    ```

    返回一张 `ACTIVE` 卡片，意味着闭环已完成：已配置 → 已授权 → 已验证 → 已注册 → 已批准 → 可操作。
  </Step>
</Steps>

## 大功告成 🎉

你已经把一家企业从一个空白应用带到了可以消费的已验证账户。接下来：

<CardGroup cols={2}>
  <Card title="OAuth 中的企业账户" icon="building-lock" href="/business-accounts-in-o-auth">
    两份权限清单、账户选择器，以及授权码最终解析到哪个账户。
  </Card>

  <Card title="注册企业" icon="clipboard-check" href="/business-registration">
    前提条件、KYB 状态生命周期，以及如何跟踪案件直到出结果。
  </Card>

  <Card title="提交企业文件" icon="file-arrow-up" href="/submit-business-documents">
    授权签署人文件上传，以及如何响应补充材料请求。
  </Card>

  <Card title="引导并连接客户" icon="user-plus" href="/quickstart/onboard-customers">
    面向个人的同一段旅程。
  </Card>
</CardGroup>

<Note>
  **想了解更多？** 通过 [support@fluz.app](mailto:support@fluz.app) 联系我们，与专家沟通或申请演示。
</Note>
