From 53b799eb21a5906247bf2fa606142d70c1e4f90a Mon Sep 17 00:00:00 2001 From: Jiexi Luan-Huang Date: Fri, 28 Aug 2026 14:36:19 -0700 Subject: [PATCH 1/2] rename fetchDisclaimers to fetchVendorDisclaimers --- packages/kyc-controller/ARCHITECTURE.md | 6 +- packages/kyc-controller/CHANGELOG.md | 3 +- .../kyc-controller/src/KycController.test.ts | 72 +++++++++---------- packages/kyc-controller/src/KycController.ts | 2 +- .../src/KycService-method-action-types.ts | 10 +-- .../kyc-controller/src/KycService.test.ts | 22 +++--- packages/kyc-controller/src/KycService.ts | 12 ++-- packages/kyc-controller/src/index.ts | 2 +- 8 files changed, 65 insertions(+), 64 deletions(-) diff --git a/packages/kyc-controller/ARCHITECTURE.md b/packages/kyc-controller/ARCHITECTURE.md index c81b079e0d..975a59f889 100644 --- a/packages/kyc-controller/ARCHITECTURE.md +++ b/packages/kyc-controller/ARCHITECTURE.md @@ -121,7 +121,7 @@ Exposed messenger actions (`MESSENGER_EXPOSED_METHODS`): Exposed messenger actions (`MESSENGER_EXPOSED_METHODS`): -`getGeoCountry`, `fetchDisclaimers`, `createSession`, `checkKycRequired`, +`getGeoCountry`, `fetchVendorDisclaimers`, `createSession`, `checkKycRequired`, `createVendorCustomer`, `submitVendorDisclaimers`, `fetchSessionDisclaimers`, `submitSessionDisclaimers`, `fetchKycStatus`, `fetchIdosEnclaveJwks`, `fetchIdosRelayJwks`, `createUkycSession`, `setAuthorizations`, `createJourney`, `getSessionStatus`. @@ -131,7 +131,7 @@ Endpoints: | Method | HTTP | Endpoint | Purpose | | -------------------------- | ------ | -------------------------------------------- | -------------------------------------------------------------------------------------- | | `getGeoCountry` | — | (geolocation action) | Resolve alpha-3 country | -| `fetchDisclaimers` | `GET` | `/vendors/{vendor}/disclaimers?country=` | Vendor T&Cs to accept (`vendor` defaults to `moonpay`) | +| `fetchVendorDisclaimers` | `GET` | `/vendors/{vendor}/disclaimers?country=` | Vendor T&Cs to accept (`vendor` defaults to `moonpay`) | | `createSession` | `POST` | `/vendors/moonpay/sessions` | Create MoonPay vendor session | | `checkKycRequired` | `POST` | `/vendors/{vendor}/kyc-required` | Is KYC required? (normalizes `required` → `kycRequired`) | | `createVendorCustomer` | `POST` | `/vendors/{vendor}/customers` | Create or resume an empty-shell vendor customer | @@ -342,7 +342,7 @@ sequenceDiagram Ctrl->>Svc: getGeoCountry() Svc->>Geo: getGeolocation() Note over Svc: map alpha-2 → alpha-3 locally - Ctrl->>Svc: fetchDisclaimers({ country }) + Ctrl->>Svc: fetchVendorDisclaimers({ country }) Svc->>API: GET /disclaimers Ctrl-->>UI: phase = terms (+ disclaimers) diff --git a/packages/kyc-controller/CHANGELOG.md b/packages/kyc-controller/CHANGELOG.md index 6ab5cc1eba..777b83505d 100644 --- a/packages/kyc-controller/CHANGELOG.md +++ b/packages/kyc-controller/CHANGELOG.md @@ -16,7 +16,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add `KycService.submitVendorDisclaimers({ vendor, disclaimerIds })` (`POST /vendors/{vendor}/disclaimers`) to record Iron T&C signings, plus the `KycVendorSigning` response type. The consents path calls this alongside session-scoped disclaimers; vendor T&C ids are no longer sent on the session disclaimer POST. ([#9979](https://github.com/MetaMask/core/pull/9979)) - Add `KycConsentDocument`, `KycConsentRecord`, and `KycSessionDisclaimers` types for that catalog, plus in-memory `credentialReusabilityConsentGiven` and `sessionDisclaimers` controller state. `acceptTermsAndStartSession` forwards optional `credentialReusabilityConsentGiven` (default `false`). ([#9979](https://github.com/MetaMask/core/pull/9979)) - Parameterize Universal KYC vendor HTTP on `KycService` so identity vendors share one client surface instead of vendor-branded methods ([#9908](https://github.com/MetaMask/core/pull/9908)): - - `fetchDisclaimers({ vendor, country })` and `checkKycRequired({ vendor, ... })` call `/vendors/{vendor}/disclaimers` and `/vendors/{vendor}/kyc-required` (`vendor` defaults to `moonpay`) + - `fetchVendorDisclaimers({ vendor, country })` and `checkKycRequired({ vendor, ... })` call `/vendors/{vendor}/disclaimers` and `/vendors/{vendor}/kyc-required` (`vendor` defaults to `moonpay`) - `createVendorCustomer({ vendor, email })` calls `POST /vendors/{vendor}/customers` - `fetchKycStatus()` reads `GET /kyc/status` - Add a consents-path KYC flow on `KycController` for non-MoonPay vendors (currently `iron`): empty-shell customer → disclaimers → consents → SumSub, skipping MoonPay Check/Auth frames. `initialize({ vendor })` and `createVendorCustomer({ vendor, email })` drive the path; `acceptTermsAndStartSession` requires `sumsubTncSigned` / `idosTncSigned`. ([#9908](https://github.com/MetaMask/core/pull/9908)) @@ -30,6 +30,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- **BREAKING:** Rename `KycService.fetchDisclaimers` / `KycService:fetchDisclaimers` / `KycServiceFetchDisclaimersAction` to `fetchVendorDisclaimers` / `KycService:fetchVendorDisclaimers` / `KycServiceFetchVendorDisclaimersAction`. - **BREAKING:** Rename `fractalEncryptionBaseUrl` to `idosEnclaveBaseUrl`, `KycService.fetchJwks` / `KycService:fetchJwks` / `KycServiceFetchJwksAction` to `fetchIdosEnclaveJwks` / `KycService:fetchIdosEnclaveJwks` / `KycServiceFetchIdosEnclaveJwksAction`, and related Fractal encryption naming to idOS enclave. ([#10008](https://github.com/MetaMask/core/pull/10008)) - **BREAKING:** Verify `encryptionDataKey` against idOS enclave JWKS (`KycService:fetchIdosEnclaveJwks` / `idosEnclaveBaseUrl`) and `ukycCapabilityToken` against idOS relay JWKS (`KycService:fetchIdosRelayJwks` / `idosRelayBaseUrl`) when wrapping UKYC authorizations, instead of validating both schemas against Fractal. Hosts must supply `idosRelayBaseUrl` on `KycService` construction (same class of requirement as `idosEnclaveBaseUrl`). ([#10008](https://github.com/MetaMask/core/pull/10008)) - **BREAKING:** Require `sessionClientPublicKey` (unpadded base64url X25519 public key) and `residenceCountry` (ISO 3166-1 alpha-3) on `KycService.createUkycSession` (`POST /sessions`). The controller generates the per-session keypair before creating the session and uses the private half to wrap authorizations; residence country is taken from the resolved geo country. ([#9993](https://github.com/MetaMask/core/pull/9993)) diff --git a/packages/kyc-controller/src/KycController.test.ts b/packages/kyc-controller/src/KycController.test.ts index d39fa78ae4..5651378d5e 100644 --- a/packages/kyc-controller/src/KycController.test.ts +++ b/packages/kyc-controller/src/KycController.test.ts @@ -169,7 +169,7 @@ describe('KycController', () => { it('captures the active product for the automatic post-auth continuation', async () => { await withController(async ({ controller, handlers }) => { handlers.getGeoCountry.mockResolvedValue('USA'); - handlers.fetchDisclaimers.mockResolvedValue([]); + handlers.fetchVendorDisclaimers.mockResolvedValue([]); await controller.initialize({ product: 'card' }); @@ -182,7 +182,7 @@ describe('KycController', () => { { options: { state: { activeProduct: 'card' } } }, async ({ controller, handlers }) => { handlers.getGeoCountry.mockResolvedValue('USA'); - handlers.fetchDisclaimers.mockResolvedValue([]); + handlers.fetchVendorDisclaimers.mockResolvedValue([]); await controller.initialize({ email: 'a@b.co' }); @@ -239,7 +239,7 @@ describe('KycController', () => { }, async ({ controller, handlers }) => { handlers.getGeoCountry.mockResolvedValue('USA'); - handlers.fetchDisclaimers.mockResolvedValue([]); + handlers.fetchVendorDisclaimers.mockResolvedValue([]); await controller.initialize(); @@ -253,7 +253,7 @@ describe('KycController', () => { it('loads disclaimers for a provided country', async () => { await withController(async ({ controller, handlers }) => { const disclaimers = [{ id: '1', display_name: 'T', url: 'u' }]; - handlers.fetchDisclaimers.mockResolvedValue(disclaimers); + handlers.fetchVendorDisclaimers.mockResolvedValue(disclaimers); await controller.loadDisclaimers({ country: 'USA' }); @@ -264,7 +264,7 @@ describe('KycController', () => { it('caches the provided country override in geoCountry', async () => { await withController(async ({ controller, handlers }) => { - handlers.fetchDisclaimers.mockResolvedValue([]); + handlers.fetchVendorDisclaimers.mockResolvedValue([]); await controller.loadDisclaimers({ country: 'USA' }); @@ -276,7 +276,7 @@ describe('KycController', () => { await withController( { options: { state: { accessToken: 'a' } } }, async ({ controller, handlers }) => { - handlers.fetchDisclaimers.mockResolvedValue([]); + handlers.fetchVendorDisclaimers.mockResolvedValue([]); handlers.checkKycRequired.mockResolvedValue({ kycRequired: true }); await controller.loadDisclaimers({ country: 'USA' }); @@ -297,12 +297,12 @@ describe('KycController', () => { await withController( { options: { state: { geoCountry: 'USA' } } }, async ({ controller, handlers }) => { - handlers.fetchDisclaimers.mockResolvedValue([]); + handlers.fetchVendorDisclaimers.mockResolvedValue([]); await controller.loadDisclaimers(); expect(handlers.getGeoCountry).not.toHaveBeenCalled(); - expect(handlers.fetchDisclaimers).toHaveBeenCalledWith({ + expect(handlers.fetchVendorDisclaimers).toHaveBeenCalledWith({ vendor: 'moonpay', country: 'USA', }); @@ -313,12 +313,12 @@ describe('KycController', () => { it('resolves the country when neither param nor cache is available', async () => { await withController(async ({ controller, handlers }) => { handlers.getGeoCountry.mockResolvedValue('FRA'); - handlers.fetchDisclaimers.mockResolvedValue([]); + handlers.fetchVendorDisclaimers.mockResolvedValue([]); await controller.loadDisclaimers(); expect(controller.state.geoCountry).toBe('FRA'); - expect(handlers.fetchDisclaimers).toHaveBeenCalledWith({ + expect(handlers.fetchVendorDisclaimers).toHaveBeenCalledWith({ vendor: 'moonpay', country: 'FRA', }); @@ -327,7 +327,7 @@ describe('KycController', () => { it('records an error when loading fails', async () => { await withController(async ({ controller, handlers }) => { - handlers.fetchDisclaimers.mockRejectedValue(new Error('boom')); + handlers.fetchVendorDisclaimers.mockRejectedValue(new Error('boom')); await controller.loadDisclaimers({ country: 'USA' }); @@ -516,7 +516,7 @@ describe('KycController', () => { }, async ({ controller, handlers }) => { handlers.createSession.mockRejectedValue(new Error('nope')); - handlers.fetchDisclaimers.mockResolvedValue([]); + handlers.fetchVendorDisclaimers.mockResolvedValue([]); await controller.acceptTermsAndStartSession({ sumsubTncSigned: true, @@ -569,7 +569,7 @@ describe('KycController', () => { expect(controller.state.phase).toBe('idle'); expect(controller.state.error).toBeNull(); - expect(handlers.fetchDisclaimers).not.toHaveBeenCalled(); + expect(handlers.fetchVendorDisclaimers).not.toHaveBeenCalled(); }, ); }); @@ -587,7 +587,7 @@ describe('KycController', () => { }, async ({ controller, handlers }) => { handlers.createSession.mockRejectedValue(new Error('nope')); - handlers.fetchDisclaimers.mockResolvedValue([]); + handlers.fetchVendorDisclaimers.mockResolvedValue([]); await controller.acceptTermsAndStartSession({ product: 'ramps', @@ -2353,7 +2353,7 @@ describe('KycController', () => { await pending; expect(controller.state.phase).toBe('idle'); - expect(handlers.fetchDisclaimers).not.toHaveBeenCalled(); + expect(handlers.fetchVendorDisclaimers).not.toHaveBeenCalled(); }); }); }); @@ -2424,7 +2424,7 @@ describe('KycController', () => { expect(controller.state).toStrictEqual(getDefaultKycControllerState()); // The superseded flow must not resume the terms step either. - expect(handlers.fetchDisclaimers).not.toHaveBeenCalled(); + expect(handlers.fetchVendorDisclaimers).not.toHaveBeenCalled(); }); }); @@ -2530,7 +2530,7 @@ describe('KycController', () => { it('creates an Iron customer and loads Iron disclaimers on initialize', async () => { await withController(async ({ controller, handlers }) => { handlers.getGeoCountry.mockResolvedValue('USA'); - handlers.fetchDisclaimers.mockResolvedValue([ + handlers.fetchVendorDisclaimers.mockResolvedValue([ { id: 'd1', display_name: 'Iron T&C', url: 'https://t' }, ]); @@ -2544,7 +2544,7 @@ describe('KycController', () => { vendor: 'iron', email: 'a@b.co', }); - expect(handlers.fetchDisclaimers).toHaveBeenCalledWith({ + expect(handlers.fetchVendorDisclaimers).toHaveBeenCalledWith({ vendor: 'iron', country: 'USA', }); @@ -2709,7 +2709,7 @@ describe('KycController', () => { }, }, async ({ controller, handlers }) => { - handlers.fetchDisclaimers.mockResolvedValue([ + handlers.fetchVendorDisclaimers.mockResolvedValue([ { id: 'iron-d1', display_name: 'T', url: 'u' }, ]); @@ -2719,7 +2719,7 @@ describe('KycController', () => { expect(controller.state.termsAcceptedAt).toBeNull(); expect(controller.state.acceptedDisclaimerIds).toStrictEqual([]); expect(controller.state.termsAcceptedVendor).toBeNull(); - expect(handlers.fetchDisclaimers).toHaveBeenCalledWith({ + expect(handlers.fetchVendorDisclaimers).toHaveBeenCalledWith({ vendor: 'iron', country: 'USA', }); @@ -2743,7 +2743,7 @@ describe('KycController', () => { }, }, async ({ controller, handlers }) => { - handlers.fetchDisclaimers.mockResolvedValue([ + handlers.fetchVendorDisclaimers.mockResolvedValue([ { id: 'd1', display_name: 'T', url: 'u' }, ]); @@ -3255,7 +3255,7 @@ describe('KycController', () => { "Fetching 'disclaimers' failed with status '409'", ), ); - handlers.fetchDisclaimers.mockResolvedValue([]); + handlers.fetchVendorDisclaimers.mockResolvedValue([]); await controller.acceptTermsAndStartSession({ email: 'a@b.co', @@ -3292,7 +3292,7 @@ describe('KycController', () => { "Fetching 'disclaimers' failed with status '409'", ), ); - handlers.fetchDisclaimers.mockResolvedValue([]); + handlers.fetchVendorDisclaimers.mockResolvedValue([]); await controller.acceptTermsAndStartSession({ email: 'a@b.co', @@ -3553,7 +3553,7 @@ describe('KycController', () => { handlers.createUkycSession.mockRejectedValue( new Error('sumsub down'), ); - handlers.fetchDisclaimers.mockResolvedValue([]); + handlers.fetchVendorDisclaimers.mockResolvedValue([]); await controller.acceptTermsAndStartSession({ email: 'a@b.co', @@ -3580,7 +3580,7 @@ describe('KycController', () => { }, async ({ controller, handlers }) => { handlers.createJourney.mockRejectedValue(new Error('journey down')); - handlers.fetchDisclaimers.mockResolvedValue([]); + handlers.fetchVendorDisclaimers.mockResolvedValue([]); await controller.acceptTermsAndStartSession({ email: 'a@b.co', @@ -3610,7 +3610,7 @@ describe('KycController', () => { onStatusChange?.('idle', 'InProgress'); return { ok: false }; }); - handlers.fetchDisclaimers.mockResolvedValue([]); + handlers.fetchVendorDisclaimers.mockResolvedValue([]); await controller.acceptTermsAndStartSession({ email: 'a@b.co', @@ -3933,7 +3933,7 @@ describe('KycController', () => { handlers.submitVendorDisclaimers.mockRejectedValue( new Error('iron signings down'), ); - handlers.fetchDisclaimers.mockResolvedValue([]); + handlers.fetchVendorDisclaimers.mockResolvedValue([]); await controller.acceptTermsAndStartSession({ email: 'a@b.co', @@ -3995,7 +3995,7 @@ describe('KycController', () => { "Fetching 'disclaimers' failed with status '500'", ), ); - handlers.fetchDisclaimers.mockResolvedValue([]); + handlers.fetchVendorDisclaimers.mockResolvedValue([]); await controller.acceptTermsAndStartSession({ email: 'a@b.co', @@ -4028,7 +4028,7 @@ describe('KycController', () => { kycProvider: MOCK_SESSION_DISCLAIMERS.kycProvider, credentialReusabilityConsentGiven: false, }); - handlers.fetchDisclaimers.mockResolvedValue([]); + handlers.fetchVendorDisclaimers.mockResolvedValue([]); await controller.acceptTermsAndStartSession({ email: 'a@b.co', @@ -4063,7 +4063,7 @@ describe('KycController', () => { kycProvider: [], credentialReusabilityConsentGiven: false, }); - handlers.fetchDisclaimers.mockResolvedValue([]); + handlers.fetchVendorDisclaimers.mockResolvedValue([]); await controller.acceptTermsAndStartSession({ email: 'a@b.co', @@ -4108,7 +4108,7 @@ describe('KycController', () => { "Fetching 'disclaimers' failed with status '409'", ), ); - handlers.fetchDisclaimers.mockResolvedValue([]); + handlers.fetchVendorDisclaimers.mockResolvedValue([]); await controller.acceptTermsAndStartSession({ email: 'a@b.co', @@ -4638,7 +4638,7 @@ type RootMessenger = Messenger< type ServiceHandlers = { getGeoCountry: jest.Mock; - fetchDisclaimers: jest.Mock; + fetchVendorDisclaimers: jest.Mock; createSession: jest.Mock; checkKycRequired: jest.Mock; createVendorCustomer: jest.Mock; @@ -4674,7 +4674,7 @@ type WithControllerOptions = { const SERVICE_ACTIONS = [ 'KycService:getGeoCountry', - 'KycService:fetchDisclaimers', + 'KycService:fetchVendorDisclaimers', 'KycService:createSession', 'KycService:checkKycRequired', 'KycService:createVendorCustomer', @@ -4775,7 +4775,7 @@ function withController( const handlers: ServiceHandlers = { getGeoCountry: jest.fn().mockResolvedValue('USA'), - fetchDisclaimers: jest.fn().mockResolvedValue([]), + fetchVendorDisclaimers: jest.fn().mockResolvedValue([]), createSession: jest.fn().mockResolvedValue({ sessionToken: 'sess' }), checkKycRequired: jest.fn().mockResolvedValue({ kycRequired: false }), createVendorCustomer: jest.fn().mockResolvedValue({ @@ -4820,8 +4820,8 @@ function withController( handlers.getGeoCountry, ); rootMessenger.registerActionHandler( - 'KycService:fetchDisclaimers', - handlers.fetchDisclaimers, + 'KycService:fetchVendorDisclaimers', + handlers.fetchVendorDisclaimers, ); rootMessenger.registerActionHandler( 'KycService:createSession', diff --git a/packages/kyc-controller/src/KycController.ts b/packages/kyc-controller/src/KycController.ts index d1c914178b..b5a9295ab7 100644 --- a/packages/kyc-controller/src/KycController.ts +++ b/packages/kyc-controller/src/KycController.ts @@ -986,7 +986,7 @@ export class KycController extends BaseController< }); } const disclaimers = await this.messenger.call( - 'KycService:fetchDisclaimers', + 'KycService:fetchVendorDisclaimers', { vendor: this.state.activeVendor, country, diff --git a/packages/kyc-controller/src/KycService-method-action-types.ts b/packages/kyc-controller/src/KycService-method-action-types.ts index 4da7d075de..276164a200 100644 --- a/packages/kyc-controller/src/KycService-method-action-types.ts +++ b/packages/kyc-controller/src/KycService-method-action-types.ts @@ -26,9 +26,9 @@ export type KycServiceGetGeoCountryAction = { * @param params.country - ISO 3166-1 alpha-3 country code. * @returns The disclaimers. */ -export type KycServiceFetchDisclaimersAction = { - type: `KycService:fetchDisclaimers`; - handler: KycService['fetchDisclaimers']; +export type KycServiceFetchVendorDisclaimersAction = { + type: `KycService:fetchVendorDisclaimers`; + handler: KycService['fetchVendorDisclaimers']; }; /** @@ -90,7 +90,7 @@ export type KycServiceSubmitVendorDisclaimersAction = { /** * Fetches the session-scoped idOS + KYC-provider disclaimer catalog * (`GET /sessions/{sessionId}/disclaimers`). Requires an existing UKYC - * session; vendor T&Cs continue to come from {@link fetchDisclaimers}. + * session; vendor T&Cs continue to come from {@link fetchVendorDisclaimers}. * * @param params - The parameters. * @param params.sessionId - The UKYC session id. @@ -218,7 +218,7 @@ export type KycServiceGetSessionStatusAction = { */ export type KycServiceMethodActions = | KycServiceGetGeoCountryAction - | KycServiceFetchDisclaimersAction + | KycServiceFetchVendorDisclaimersAction | KycServiceCreateSessionAction | KycServiceCheckKycRequiredAction | KycServiceCreateVendorCustomerAction diff --git a/packages/kyc-controller/src/KycService.test.ts b/packages/kyc-controller/src/KycService.test.ts index dec4ed331b..a90022da51 100644 --- a/packages/kyc-controller/src/KycService.test.ts +++ b/packages/kyc-controller/src/KycService.test.ts @@ -31,7 +31,7 @@ describe('KycService', () => { .reply(200, disclaimers); const { service } = getService({ omitFetch: true }); - expect(await service.fetchDisclaimers({ country: 'USA' })).toStrictEqual( + expect(await service.fetchVendorDisclaimers({ country: 'USA' })).toStrictEqual( disclaimers, ); }); @@ -102,7 +102,7 @@ describe('KycService', () => { }); }); - describe('fetchDisclaimers', () => { + describe('fetchVendorDisclaimers', () => { it('returns the disclaimers for a country', async () => { const disclaimers = [ { id: '1', display_name: 'Terms', url: 'https://t' }, @@ -113,7 +113,7 @@ describe('KycService', () => { .reply(200, disclaimers); const { service } = getService(); - expect(await service.fetchDisclaimers({ country: 'USA' })).toStrictEqual( + expect(await service.fetchVendorDisclaimers({ country: 'USA' })).toStrictEqual( disclaimers, ); }); @@ -126,14 +126,14 @@ describe('KycService', () => { const { service } = getService(); await expect( - service.fetchDisclaimers({ country: 'USA' }), + service.fetchVendorDisclaimers({ country: 'USA' }), ).rejects.toThrow(/Malformed response received from disclaimers API/u); }); it('throws when no bearer token is available', async () => { const { service } = getService({ bearerToken: '' }); await expect( - service.fetchDisclaimers({ country: 'USA' }), + service.fetchVendorDisclaimers({ country: 'USA' }), ).rejects.toThrow(/Unable to obtain an authentication bearer token/u); }); @@ -145,7 +145,7 @@ describe('KycService', () => { const { service } = getService(); await expect( - service.fetchDisclaimers({ country: 'USA' }), + service.fetchVendorDisclaimers({ country: 'USA' }), ).rejects.toThrow(/failed with status '500'/u); }); }); @@ -672,7 +672,7 @@ describe('KycService', () => { }); }); - describe('fetchDisclaimers for a non-MoonPay vendor', () => { + describe('fetchVendorDisclaimers for a non-MoonPay vendor', () => { it('returns Iron disclaimers for a country', async () => { const disclaimers = [ { id: '1', display_name: 'Iron Terms', url: 'https://t' }, @@ -684,7 +684,7 @@ describe('KycService', () => { const { service } = getService(); expect( - await service.fetchDisclaimers({ vendor: 'iron', country: 'USA' }), + await service.fetchVendorDisclaimers({ vendor: 'iron', country: 'USA' }), ).toStrictEqual(disclaimers); }); @@ -696,7 +696,7 @@ describe('KycService', () => { const { service } = getService(); await expect( - service.fetchDisclaimers({ vendor: 'iron', country: 'USA' }), + service.fetchVendorDisclaimers({ vendor: 'iron', country: 'USA' }), ).rejects.toThrow(/Malformed response received from disclaimers API/u); }); }); @@ -983,7 +983,7 @@ describe('KycService', () => { .reply(200, disclaimers); const { service } = getService({ baseUrl: customUrl }); - expect(await service.fetchDisclaimers({ country: 'USA' })).toStrictEqual( + expect(await service.fetchVendorDisclaimers({ country: 'USA' })).toStrictEqual( disclaimers, ); }); @@ -1004,7 +1004,7 @@ describe('KycService', () => { const { rootMessenger } = getService(); expect( - await rootMessenger.call('KycService:fetchDisclaimers', { + await rootMessenger.call('KycService:fetchVendorDisclaimers', { country: 'USA', }), ).toStrictEqual([]); diff --git a/packages/kyc-controller/src/KycService.ts b/packages/kyc-controller/src/KycService.ts index 5d028c34e6..f039537a96 100644 --- a/packages/kyc-controller/src/KycService.ts +++ b/packages/kyc-controller/src/KycService.ts @@ -48,7 +48,7 @@ export const serviceName = 'KycService'; const MESSENGER_EXPOSED_METHODS = [ 'getGeoCountry', - 'fetchDisclaimers', + 'fetchVendorDisclaimers', 'createSession', 'checkKycRequired', 'createVendorCustomer', @@ -300,7 +300,7 @@ export type CreateVendorCustomerParams = { export type SubmitVendorDisclaimersParams = { /** Identity vendor whose T&Cs were accepted (currently `iron`). */ vendor: KycVendor; - /** Disclaimer ids from {@link KycService.fetchDisclaimers}. */ + /** Disclaimer ids from {@link KycService.fetchVendorDisclaimers}. */ disclaimerIds: string[]; }; @@ -381,7 +381,7 @@ export type GetSessionStatusParams = { * It extends {@link BaseDataService}, so every request is routed through * `fetchQuery`: it is wrapped in the shared service policy (retries, circuit * breaker) and its result is exposed via the service's `QueryClient`. Read-only - * endpoints (`fetchDisclaimers`, `fetchIdosEnclaveJwks`, `fetchIdosRelayJwks`) are cached + * endpoints (`fetchVendorDisclaimers`, `fetchIdosEnclaveJwks`, `fetchIdosRelayJwks`) are cached * with a `staleTime`; vendor-disclaimer, session-scoped disclaimer, * session-creating, and status-polling endpoints opt out of caching * (`staleTime`/`gcTime` of `0`) so they never serve a stale result. @@ -497,7 +497,7 @@ export class KycService extends BaseDataService< * @param params.country - ISO 3166-1 alpha-3 country code. * @returns The disclaimers. */ - async fetchDisclaimers({ + async fetchVendorDisclaimers({ vendor = 'moonpay', country, }: { @@ -507,7 +507,7 @@ export class KycService extends BaseDataService< const url = new URL(`/vendors/${vendor}/disclaimers`, this.#baseUrl); url.searchParams.set('country', country); const data = await this.fetchQuery({ - queryKey: [`${this.name}:fetchDisclaimers`, vendor, country], + queryKey: [`${this.name}:fetchVendorDisclaimers`, vendor, country], queryFn: async () => this.#requestJson(url, { method: 'GET' }), staleTime: inMilliseconds(5, Duration.Minute), }); @@ -692,7 +692,7 @@ export class KycService extends BaseDataService< /** * Fetches the session-scoped idOS + KYC-provider disclaimer catalog * (`GET /sessions/{sessionId}/disclaimers`). Requires an existing UKYC - * session; vendor T&Cs continue to come from {@link fetchDisclaimers}. + * session; vendor T&Cs continue to come from {@link fetchVendorDisclaimers}. * * @param params - The parameters. * @param params.sessionId - The UKYC session id. diff --git a/packages/kyc-controller/src/index.ts b/packages/kyc-controller/src/index.ts index 985991a7d3..8fbd396347 100644 --- a/packages/kyc-controller/src/index.ts +++ b/packages/kyc-controller/src/index.ts @@ -64,7 +64,7 @@ export type { KycServiceCreateJourneyAction, KycServiceCreateSessionAction, KycServiceCreateUkycSessionAction, - KycServiceFetchDisclaimersAction, + KycServiceFetchVendorDisclaimersAction, KycServiceFetchIdosEnclaveJwksAction, KycServiceFetchIdosRelayJwksAction, KycServiceFetchKycStatusAction, From d58a971435fa49a73bcec4a3b8f94a435e7cffa2 Mon Sep 17 00:00:00 2001 From: Jiexi Luan-Huang Date: Fri, 28 Aug 2026 14:57:11 -0700 Subject: [PATCH 2/2] add GET `/disclaimers` --- packages/kyc-controller/ARCHITECTURE.md | 4 +- packages/kyc-controller/CHANGELOG.md | 3 +- packages/kyc-controller/src/KycController.ts | 6 +- .../src/KycService-method-action-types.ts | 17 ++- .../kyc-controller/src/KycService.test.ts | 108 ++++++++++++++++- packages/kyc-controller/src/KycService.ts | 111 +++++++++++++++--- packages/kyc-controller/src/index.ts | 1 + packages/kyc-controller/src/types.ts | 22 +++- 8 files changed, 235 insertions(+), 37 deletions(-) diff --git a/packages/kyc-controller/ARCHITECTURE.md b/packages/kyc-controller/ARCHITECTURE.md index 975a59f889..35b2ed84a4 100644 --- a/packages/kyc-controller/ARCHITECTURE.md +++ b/packages/kyc-controller/ARCHITECTURE.md @@ -136,7 +136,7 @@ Endpoints: | `checkKycRequired` | `POST` | `/vendors/{vendor}/kyc-required` | Is KYC required? (normalizes `required` → `kycRequired`) | | `createVendorCustomer` | `POST` | `/vendors/{vendor}/customers` | Create or resume an empty-shell vendor customer | | `submitVendorDisclaimers` | `POST` | `/vendors/{vendor}/disclaimers` | Record vendor T&C signings (`disclaimerIds`) | -| `fetchSessionDisclaimers` | `GET` | `/sessions/{id}/disclaimers` | Session-scoped idOS + KYC-provider catalog | +| `fetchSessionDisclaimers` | `GET` | `/disclaimers?country=` or `/sessions/{id}/disclaimers` | idOS + KYC-provider catalog (`country` XOR `sessionId`; global omits credential-reuse) | | `submitSessionDisclaimers` | `POST` | `/sessions/{id}/disclaimers` | Record `{ idOS, kycProvider, credentialReusabilityConsentGiven }` consents | | `fetchKycStatus` | `GET` | `/kyc/status` | User-keyed simplified KYC status | | `fetchIdosEnclaveJwks` | `GET` | `{idosEnclaveBaseUrl}/.well-known/jwks.json` | idOS enclave JWKS for `encryptionDataKey` attestation | @@ -343,7 +343,7 @@ sequenceDiagram Svc->>Geo: getGeolocation() Note over Svc: map alpha-2 → alpha-3 locally Ctrl->>Svc: fetchVendorDisclaimers({ country }) - Svc->>API: GET /disclaimers + Svc->>API: GET /vendors/moonpay/disclaimers?country= Ctrl-->>UI: phase = terms (+ disclaimers) User->>Ctrl: acceptTermsAndStartSession({ email, sumsubTncSigned, idosTncSigned }) diff --git a/packages/kyc-controller/CHANGELOG.md b/packages/kyc-controller/CHANGELOG.md index 777b83505d..a6b0631946 100644 --- a/packages/kyc-controller/CHANGELOG.md +++ b/packages/kyc-controller/CHANGELOG.md @@ -11,7 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add `idosRelayBaseUrl` on `KycService` and `KycService.fetchIdosRelayJwks()` to fetch the idOS relay well-known JWKS used to verify the `ukycCapabilityToken` encryption schema. `encryptionDataKey` continues to verify against idOS enclave JWKS via `fetchIdosEnclaveJwks` / `idosEnclaveBaseUrl`. ([#10008](https://github.com/MetaMask/core/pull/10008)) - Add session-scoped disclaimer APIs on `KycService` for the idOS / KYC-provider catalog ([#9979](https://github.com/MetaMask/core/pull/9979)): - - `fetchSessionDisclaimers({ sessionId })` calls `GET /sessions/{sessionId}/disclaimers` + - `fetchSessionDisclaimers({ sessionId })` calls `GET /sessions/{sessionId}/disclaimers` (includes `credentialReusabilityConsentGiven`), or `fetchSessionDisclaimers({ country })` calls `GET /disclaimers?country=` (catalog only; `country` is ISO 3166-1 alpha-3) - `submitSessionDisclaimers({ sessionId, idOS, kycProvider, credentialReusabilityConsentGiven })` calls `POST /sessions/{sessionId}/disclaimers` - Add `KycService.submitVendorDisclaimers({ vendor, disclaimerIds })` (`POST /vendors/{vendor}/disclaimers`) to record Iron T&C signings, plus the `KycVendorSigning` response type. The consents path calls this alongside session-scoped disclaimers; vendor T&C ids are no longer sent on the session disclaimer POST. ([#9979](https://github.com/MetaMask/core/pull/9979)) - Add `KycConsentDocument`, `KycConsentRecord`, and `KycSessionDisclaimers` types for that catalog, plus in-memory `credentialReusabilityConsentGiven` and `sessionDisclaimers` controller state. `acceptTermsAndStartSession` forwards optional `credentialReusabilityConsentGiven` (default `false`). ([#9979](https://github.com/MetaMask/core/pull/9979)) @@ -30,6 +30,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- Make `KycService.fetchSessionDisclaimers` accept either `{ sessionId }` (`GET /sessions/{sessionId}/disclaimers`) or `{ country }` (`GET /disclaimers?country=`, ISO 3166-1 alpha-3). The two params are mutually exclusive; `country` is required for the global catalog. Adds the `KycDisclaimersCatalog` type for the global response (no `credentialReusabilityConsentGiven`); `KycSessionDisclaimers` remains the session-scoped shape. - **BREAKING:** Rename `KycService.fetchDisclaimers` / `KycService:fetchDisclaimers` / `KycServiceFetchDisclaimersAction` to `fetchVendorDisclaimers` / `KycService:fetchVendorDisclaimers` / `KycServiceFetchVendorDisclaimersAction`. - **BREAKING:** Rename `fractalEncryptionBaseUrl` to `idosEnclaveBaseUrl`, `KycService.fetchJwks` / `KycService:fetchJwks` / `KycServiceFetchJwksAction` to `fetchIdosEnclaveJwks` / `KycService:fetchIdosEnclaveJwks` / `KycServiceFetchIdosEnclaveJwksAction`, and related Fractal encryption naming to idOS enclave. ([#10008](https://github.com/MetaMask/core/pull/10008)) - **BREAKING:** Verify `encryptionDataKey` against idOS enclave JWKS (`KycService:fetchIdosEnclaveJwks` / `idosEnclaveBaseUrl`) and `ukycCapabilityToken` against idOS relay JWKS (`KycService:fetchIdosRelayJwks` / `idosRelayBaseUrl`) when wrapping UKYC authorizations, instead of validating both schemas against Fractal. Hosts must supply `idosRelayBaseUrl` on `KycService` construction (same class of requirement as `idosEnclaveBaseUrl`). ([#10008](https://github.com/MetaMask/core/pull/10008)) diff --git a/packages/kyc-controller/src/KycController.ts b/packages/kyc-controller/src/KycController.ts index b5a9295ab7..94a7af58c5 100644 --- a/packages/kyc-controller/src/KycController.ts +++ b/packages/kyc-controller/src/KycController.ts @@ -185,9 +185,9 @@ export type KycControllerState = { /** Error encountered while loading disclaimers, or `null`. */ disclaimersError: string | null; /** - * Session-scoped idOS / KYC-provider disclaimer catalog from - * `GET /sessions/{sessionId}/disclaimers`. `null` until a UKYC session - * exists and the catalog has been fetched. + * idOS / KYC-provider disclaimer catalog from `GET /disclaimers` or + * `GET /sessions/{sessionId}/disclaimers`. `null` until the catalog has + * been fetched (typically after a UKYC session exists). */ sessionDisclaimers: KycSessionDisclaimers | null; diff --git a/packages/kyc-controller/src/KycService-method-action-types.ts b/packages/kyc-controller/src/KycService-method-action-types.ts index 276164a200..ef4ed25fec 100644 --- a/packages/kyc-controller/src/KycService-method-action-types.ts +++ b/packages/kyc-controller/src/KycService-method-action-types.ts @@ -88,13 +88,18 @@ export type KycServiceSubmitVendorDisclaimersAction = { }; /** - * Fetches the session-scoped idOS + KYC-provider disclaimer catalog - * (`GET /sessions/{sessionId}/disclaimers`). Requires an existing UKYC - * session; vendor T&Cs continue to come from {@link fetchVendorDisclaimers}. + * Fetches the idOS + KYC-provider disclaimer catalog. * - * @param params - The parameters. - * @param params.sessionId - The UKYC session id. - * @returns The catalog, including which documents are already consented. + * - With `sessionId`: `GET /sessions/{sessionId}/disclaimers` (includes + * `credentialReusabilityConsentGiven`). Do not pass `country`. + * - With `country` (ISO 3166-1 alpha-3): `GET /disclaimers?country=` (catalog + * only). Do not pass `sessionId`. + * + * Vendor T&Cs continue to come from {@link fetchVendorDisclaimers}. + * + * @param params - Either `{ sessionId }` or `{ country }`. + * @returns The catalog. Session fetches include + * `credentialReusabilityConsentGiven`; global fetches do not. */ export type KycServiceFetchSessionDisclaimersAction = { type: `KycService:fetchSessionDisclaimers`; diff --git a/packages/kyc-controller/src/KycService.test.ts b/packages/kyc-controller/src/KycService.test.ts index a90022da51..0454210a85 100644 --- a/packages/kyc-controller/src/KycService.test.ts +++ b/packages/kyc-controller/src/KycService.test.ts @@ -740,7 +740,7 @@ describe('KycService', () => { }); describe('fetchSessionDisclaimers', () => { - const catalog = { + const documents = { idOS: [ { key: 'idos-tos', @@ -759,6 +759,9 @@ describe('KycService', () => { consented: false, }, ], + }; + const catalog = { + ...documents, credentialReusabilityConsentGiven: false, }; @@ -771,6 +774,96 @@ describe('KycService', () => { ).toStrictEqual(catalog); }); + it('returns the global disclaimer catalog for a country', async () => { + nock(MOCK_API_URL) + .get('/disclaimers') + .query({ country: 'USA' }) + .reply(200, documents); + const { service } = getService(); + + expect( + await service.fetchSessionDisclaimers({ country: 'USA' }), + ).toStrictEqual(documents); + }); + + it('uses the global catalog for an empty sessionId when country is set', async () => { + nock(MOCK_API_URL) + .get('/disclaimers') + .query({ country: 'FRA' }) + .reply(200, documents); + const { service } = getService(); + + expect( + await service.fetchSessionDisclaimers({ + sessionId: '', + country: 'FRA', + }), + ).toStrictEqual(documents); + }); + + it('throws when neither sessionId nor country is provided', async () => { + const { service } = getService(); + + await expect(service.fetchSessionDisclaimers()).rejects.toThrow( + /country.*required/iu, + ); + }); + + it('throws when country is not a 3-character code', async () => { + const { service } = getService(); + + await expect( + service.fetchSessionDisclaimers({ country: 'US' }), + ).rejects.toThrow(/ISO 3166-1 alpha-3/u); + }); + + it('throws when both sessionId and country are provided', async () => { + const { service } = getService(); + + await expect( + service.fetchSessionDisclaimers({ + sessionId: 'sid-1', + country: 'USA', + }), + ).rejects.toThrow(/must not be provided together/iu); + }); + + it('throws on a malformed global catalog response', async () => { + nock(MOCK_API_URL) + .get('/disclaimers') + .query({ country: 'USA' }) + .reply(200, {}); + const { service } = getService(); + + await expect( + service.fetchSessionDisclaimers({ country: 'USA' }), + ).rejects.toThrow(/Malformed response received from disclaimers API/u); + }); + + it('throws when the global catalog is missing kycProvider', async () => { + nock(MOCK_API_URL) + .get('/disclaimers') + .query({ country: 'USA' }) + .reply(200, { idOS: [] }); + const { service } = getService(); + + await expect( + service.fetchSessionDisclaimers({ country: 'USA' }), + ).rejects.toThrow(/Malformed response received from disclaimers API/u); + }); + + it('throws an HttpError on a non-ok global catalog response', async () => { + nock(MOCK_API_URL) + .get('/disclaimers') + .query({ country: 'USA' }) + .reply(500); + const { service } = getService(); + + await expect( + service.fetchSessionDisclaimers({ country: 'USA' }), + ).rejects.toThrow(/failed with status '500'/u); + }); + it('throws on a malformed response', async () => { nock(MOCK_API_URL).get('/sessions/sid-1/disclaimers').reply(200, {}); const { service } = getService(); @@ -782,6 +875,19 @@ describe('KycService', () => { ); }); + it('throws when the session catalog omits credentialReusabilityConsentGiven', async () => { + nock(MOCK_API_URL) + .get('/sessions/sid-1/disclaimers') + .reply(200, documents); + const { service } = getService(); + + await expect( + service.fetchSessionDisclaimers({ sessionId: 'sid-1' }), + ).rejects.toThrow( + /Malformed response received from session disclaimers API/u, + ); + }); + it('throws an HttpError on a non-ok response', async () => { nock(MOCK_API_URL).get('/sessions/sid-1/disclaimers').reply(500); const { service } = getService(); diff --git a/packages/kyc-controller/src/KycService.ts b/packages/kyc-controller/src/KycService.ts index f039537a96..10e35ced38 100644 --- a/packages/kyc-controller/src/KycService.ts +++ b/packages/kyc-controller/src/KycService.ts @@ -29,6 +29,7 @@ import type { KycServiceMethodActions } from './KycService-method-action-types.j import type { KycConsentRecord, KycDisclaimer, + KycDisclaimersCatalog, KycSessionDisclaimers, KycSessionStatus, KycUserStatusResponse, @@ -261,9 +262,17 @@ const ConsentDocumentStruct = type({ consented: boolean(), }); -const SessionDisclaimersResponseStruct = type({ +const DisclaimersCatalogFields = { idOS: array(ConsentDocumentStruct), kycProvider: array(ConsentDocumentStruct), +} as const; + +/** Global catalog from `GET /disclaimers` (no credential-reuse flag). */ +const GlobalDisclaimersResponseStruct = type(DisclaimersCatalogFields); + +/** Session catalog from `GET`/`POST /sessions/{id}/disclaimers`. */ +const SessionDisclaimersResponseStruct = type({ + ...DisclaimersCatalogFields, credentialReusabilityConsentGiven: boolean(), }); @@ -305,8 +314,18 @@ export type SubmitVendorDisclaimersParams = { }; export type FetchSessionDisclaimersParams = { - /** UKYC session id from {@link KycService.createUkycSession}. */ - sessionId: string; + /** + * UKYC session id from {@link KycService.createUkycSession}. When set, + * fetches `GET /sessions/{sessionId}/disclaimers`. Mutually exclusive with + * `country`. + */ + sessionId?: string; + /** + * ISO 3166-1 alpha-3 country code. Required for the global catalog + * (`GET /disclaimers?country=`). Must not be set when `sessionId` is + * provided. + */ + country?: string; }; export type SubmitSessionDisclaimersParams = { @@ -690,32 +709,88 @@ export class KycService extends BaseDataService< } /** - * Fetches the session-scoped idOS + KYC-provider disclaimer catalog - * (`GET /sessions/{sessionId}/disclaimers`). Requires an existing UKYC - * session; vendor T&Cs continue to come from {@link fetchVendorDisclaimers}. + * Fetches the idOS + KYC-provider disclaimer catalog. + * + * - With `sessionId`: `GET /sessions/{sessionId}/disclaimers` (includes + * per-session `consented` flags and `credentialReusabilityConsentGiven`). + * Do not pass `country`. + * - With `country` (ISO 3166-1 alpha-3): `GET /disclaimers?country=` for the + * global catalog (no credential-reuse field). Do not pass `sessionId`. + * + * Vendor T&Cs continue to come from {@link fetchVendorDisclaimers}. * * @param params - The parameters. - * @param params.sessionId - The UKYC session id. - * @returns The catalog, including which documents are already consented. + * @returns The catalog. Session fetches include + * `credentialReusabilityConsentGiven`; global fetches do not. */ + fetchSessionDisclaimers(params: { + sessionId: string; + }): Promise; + + fetchSessionDisclaimers(params: { + country: string; + }): Promise; + async fetchSessionDisclaimers( - params: FetchSessionDisclaimersParams, - ): Promise { - const url = new URL( - `/sessions/${encodeURIComponent(params.sessionId)}/disclaimers`, - this.#baseUrl, - ); + params: FetchSessionDisclaimersParams = {}, + ): Promise { + const sessionId = + typeof params.sessionId === 'string' && params.sessionId.length > 0 + ? params.sessionId + : undefined; + const country = + typeof params.country === 'string' && params.country.length > 0 + ? params.country + : undefined; + + if (sessionId && country) { + throw new Error( + 'KycService.fetchSessionDisclaimers: sessionId and country must not be provided together.', + ); + } + + if (sessionId) { + const url = new URL( + `/sessions/${encodeURIComponent(sessionId)}/disclaimers`, + this.#baseUrl, + ); + const data = await this.fetchQuery({ + queryKey: [`${this.name}:fetchSessionDisclaimers`, sessionId], + queryFn: async () => this.#requestJson(url, { method: 'GET' }), + // Consent state can change after a POST, so always re-fetch. + staleTime: 0, + gcTime: 0, + }); + return this.#validateResponse( + data, + SessionDisclaimersResponseStruct, + 'session disclaimers', + ); + } + + if (!country) { + throw new Error( + 'KycService.fetchSessionDisclaimers: country is required when sessionId is omitted.', + ); + } + if (country.length !== 3) { + throw new Error( + `KycService.fetchSessionDisclaimers: country must be an ISO 3166-1 alpha-3 code (received "${country}").`, + ); + } + + const url = new URL('/disclaimers', this.#baseUrl); + url.searchParams.set('country', country); const data = await this.fetchQuery({ - queryKey: [`${this.name}:fetchSessionDisclaimers`, params.sessionId], + queryKey: [`${this.name}:fetchSessionDisclaimers`, country], queryFn: async () => this.#requestJson(url, { method: 'GET' }), - // Consent state can change after a POST, so always re-fetch. staleTime: 0, gcTime: 0, }); return this.#validateResponse( data, - SessionDisclaimersResponseStruct, - 'session disclaimers', + GlobalDisclaimersResponseStruct, + 'disclaimers', ); } diff --git a/packages/kyc-controller/src/index.ts b/packages/kyc-controller/src/index.ts index 8fbd396347..bb03d07c11 100644 --- a/packages/kyc-controller/src/index.ts +++ b/packages/kyc-controller/src/index.ts @@ -96,6 +96,7 @@ export type { KycConsentRecord, KycCustomerIdentity, KycDisclaimer, + KycDisclaimersCatalog, KycPhase, KycProduct, KycSessionDisclaimers, diff --git a/packages/kyc-controller/src/types.ts b/packages/kyc-controller/src/types.ts index ef4198ad0d..240f8d6eb0 100644 --- a/packages/kyc-controller/src/types.ts +++ b/packages/kyc-controller/src/types.ts @@ -164,8 +164,8 @@ export type KycVendorSigning = { }; /** - * A legal document in the session-scoped idOS / KYC-provider catalog - * (`GET`/`POST /sessions/{sessionId}/disclaimers`). + * A legal document in the idOS / KYC-provider catalog + * (`GET /disclaimers`, or `GET`/`POST /sessions/{sessionId}/disclaimers`). */ export type KycConsentDocument = { /** Stable identifier of the legal document. */ @@ -176,7 +176,10 @@ export type KycConsentDocument = { title: string; /** URL the document body is hosted at. */ url: string; - /** Whether this session already consented to this document version. */ + /** + * Whether the document version has already been consented to (session-scoped + * fetches). For the global catalog this is typically `false`. + */ consented: boolean; }; @@ -190,14 +193,21 @@ export type KycConsentRecord = { }; /** - * Session-scoped disclaimer catalog returned by - * `GET`/`POST /sessions/{sessionId}/disclaimers`. + * idOS / KYC-provider disclaimer catalog returned by + * `GET /disclaimers?country=` (no session — no credential-reuse consent state). */ -export type KycSessionDisclaimers = { +export type KycDisclaimersCatalog = { /** idOS legal documents. */ idOS: KycConsentDocument[]; /** KYC provider (SumSub) legal documents. */ kycProvider: KycConsentDocument[]; +}; + +/** + * Session-scoped disclaimer catalog returned by + * `GET`/`POST /sessions/{sessionId}/disclaimers`. + */ +export type KycSessionDisclaimers = KycDisclaimersCatalog & { /** Whether the user consented to reuse existing idOS credentials. */ credentialReusabilityConsentGiven: boolean; };