# Merchant profiles

Select, create, activate and prepare a connected sandbox merchant for a club.

Source: https://docs.champagne.dev-ukab.fr/fr/graphql/merchants

Finance at `/sell?club=<uuid>` lists locally registered PostgreSQL profiles already used by a
club in the selected club's owner space, not all Stripe accounts or profiles belonging to another
space. Each club selects one merchant; clubs in the same space may share it. Replacement is outside
this flow. Merchant operations require an authenticated administrator of the selected club.

```graphql
query MerchantProfiles($clubId: ID!) {
  merchantProfiles(clubId: $clubId) {
    selectedMerchantId
    profiles {
      id
      name
      email
      country
      entityType
      legalForm
      environment
      platformAccountId
      stripeAccountId
      configurationStatus
      paymentsEnabled
      payoutsEnabled
      checkedAt
      catalogReady
    }
  }
}
```

Required `clubId: ID!` is a local club UUID. `MerchantProfiles!` contains nullable
`selectedMerchantId: ID` and `profiles: [MerchantProfile!]!`. No search, filters, sorting
or pagination arguments are accepted. Profiles are ordered by name and local ID. A profile that is
unused or used only by a club in another owner space is not returned as a suggestion,
and `selectMerchant` refuses it like an unknown profile.

## Profile fields [#profile-fields]

| Fields                                                | Types      | Meaning                                                                |
| ----------------------------------------------------- | ---------- | ---------------------------------------------------------------------- |
| `id`                                                  | `ID!`      | Local profile ID used for selection; never send an account ID instead. |
| `name`, `email`, `country`                            | `String!`  | Merchant identity; country is a two-letter uppercase code.             |
| `entityType`                                          | `String!`  | `company`, `individual`, `non_profit` or `government_entity`.          |
| `legalForm`                                           | `String`   | Optional legal form, e.g. SAS, separate from Stripe entity type.       |
| `environment`, `platformAccountId`, `stripeAccountId` | `String!`  | Sandbox and Stripe account identity.                                   |
| `configurationStatus`                                 | `String!`  | `pending` (configuration to complete) or `ready`.                      |
| `paymentsEnabled`, `payoutsEnabled`                   | `Boolean!` | Separate payment and payout capability state.                          |
| `checkedAt`                                           | `String`   | Nullable last verification timestamp.                                  |
| `catalogReady`                                        | `Boolean!` | Whether the connected three-offer catalogue is prepared.               |

## Select and create [#select-and-create]

```graphql
mutation SelectMerchant($clubId: ID!, $merchantProfileId: ID!) {
  selectMerchant(clubId: $clubId, merchantProfileId: $merchantProfileId) {
    id
    configurationStatus
  }
}
mutation CreateMerchant($input: CreateMerchantInput!) {
  createMerchant(input: $input) {
    id
    name
    configurationStatus
    paymentsEnabled
    payoutsEnabled
    catalogReady
  }
}
```

Both return `MerchantProfile!`. Selection validates local UUIDs, club/profile existence,
matching sandbox/platform and compatible Stripe account configuration. A club with a
different selected merchant is rejected. An incomplete profile is selectable to finish
activation, and selection by another club does not exclude it.

`CreateMerchantInput` requires `clubId: ID!`, `requestId: ID!`, `name: String!`,
`email: String!`, `country: String!`, `entityType: String!`; `legalForm: String` is optional.
Use valid UUIDs, a nonblank name of at most 200 characters, an email of at most 254
characters with a local part and dotted domain, a two-letter uppercase country code,
a supported entity type and legal form of at most 100 characters. Keep the request UUID
stable for retries of the exact same input.
Creation provisions Accounts v2, saves the local profile and selects it for the club.
French platforms require account tokens for merchant identity. This sandbox-only
implementation creates a v2 account token with the test secret key, passes country
outside the token, and leaves agreement acceptance to Stripe-hosted onboarding.
Merchant configuration uses a full dashboard, Stripe fees paid by the merchant and losses
handled by Stripe. Live mode is rejected. Errors are returned through GraphQL.

The seeded Ukab Clubs sandbox fixture is intentionally different: bootstrap creates it without a
dashboard, accepts test terms and identity data, and leaves requirement, fee and loss responsibility
with the platform. Runtime accepts that configuration only with the exact versioned fixture metadata;
it does not change merchants created by this mutation.

## Activate, verify and prepare [#activate-verify-and-prepare]

```graphql
mutation MerchantOnboarding($clubId: ID!) {
  merchantOnboarding(clubId: $clubId)
}
mutation RefreshMerchant($clubId: ID!) {
  refreshMerchant(clubId: $clubId) {
    id
    configurationStatus
    paymentsEnabled
    payoutsEnabled
    checkedAt
    catalogReady
  }
}
mutation PrepareMerchantCatalog($clubId: ID!) {
  prepareMerchantCatalog(clubId: $clubId) {
    id
    catalogReady
  }
}
```

Each requires the club UUID and an existing selected merchant on the configured platform.
`merchantOnboarding` returns a non-null Stripe-hosted activation URL (`String!`). After
returning, Settings automatically synchronizes capabilities. It also synchronizes the selected
merchant every minute while visible. `refreshMerchant` and
`prepareMerchantCatalog` return `MerchantProfile!`. Preparation can be resumed/retried and
verifies the three connected Product/Price mappings shared by clubs selecting this merchant.
Checkout requires enabled payments and a valid catalogue; payouts are displayed separately.
The UI invalidates merchant, invoice and affected offer caches after these operations.
