@fiado/api-invoker 5.19.0 → 5.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  import type { IBiometricsBusinessApi } from "./interfaces/IBiometricsBusinessApi.js";
2
2
  import type { BiometricVerificationChangedV1, BiometricVerificationResponse, CreateBiometricVerificationRequest } from "@fiado/type-kit/bin/biometrics/index.js";
3
- import type { ApiGatewayResponse } from "@fiado/gateway-adapter";
3
+ import type { StandardResponse } from "@fiado/gateway-adapter";
4
4
  import type { IHttpRequest } from "@fiado/http-client";
5
5
  /**
6
6
  * Implementación del cliente de `biometrics-business`. Ver `IBiometricsBusinessApi` para el
@@ -21,9 +21,9 @@ export declare class BiometricsBusinessApi implements IBiometricsBusinessApi {
21
21
  */
22
22
  private readonly baseUrl;
23
23
  constructor(httpRequest: IHttpRequest);
24
- createBiometricVerification(data: CreateBiometricVerificationRequest): Promise<ApiGatewayResponse<BiometricVerificationResponse>>;
25
- getBiometricVerification(biometricVerificationId: string): Promise<ApiGatewayResponse<BiometricVerificationResponse>>;
26
- applyBiometricEvent(biometricVerificationId: string, data: BiometricVerificationChangedV1): Promise<ApiGatewayResponse<void>>;
24
+ createBiometricVerification(data: CreateBiometricVerificationRequest): Promise<StandardResponse<BiometricVerificationResponse>>;
25
+ getBiometricVerification(biometricVerificationId: string): Promise<StandardResponse<BiometricVerificationResponse>>;
26
+ applyBiometricEvent(biometricVerificationId: string, data: BiometricVerificationChangedV1): Promise<StandardResponse<void>>;
27
27
  }
28
28
  /**
29
29
  * También como default, por simetría con el resto de los clients del paquete.
@@ -1,5 +1,5 @@
1
1
  import type { BiometricVerificationChangedV1, BiometricVerificationResponse, CreateBiometricVerificationRequest } from "@fiado/type-kit/bin/biometrics/index.js";
2
- import type { ApiGatewayResponse } from "@fiado/gateway-adapter";
2
+ import type { StandardResponse } from "@fiado/gateway-adapter";
3
3
  /**
4
4
  * Cliente de `biometrics-business`, el master de la lógica biométrica de Fiado.
5
5
  *
@@ -26,7 +26,7 @@ export interface IBiometricsBusinessApi {
26
26
  * soportada). **`biometrics-business` NO degrada**: no sabe qué debe pasar en tu flujo si falla.
27
27
  * Esa política es tuya — el wizard, por ejemplo, degrada a KYC completo.
28
28
  */
29
- createBiometricVerification(data: CreateBiometricVerificationRequest): Promise<ApiGatewayResponse<BiometricVerificationResponse>>;
29
+ createBiometricVerification(data: CreateBiometricVerificationRequest): Promise<StandardResponse<BiometricVerificationResponse>>;
30
30
  /**
31
31
  * El estado actual de una verificación. Es el endpoint de polling.
32
32
  *
@@ -36,7 +36,7 @@ export interface IBiometricsBusinessApi {
36
36
  *
37
37
  * La expiración no depende de que llegue ningún evento: este `GET` la calcula y la persiste.
38
38
  */
39
- getBiometricVerification(biometricVerificationId: string): Promise<ApiGatewayResponse<BiometricVerificationResponse>>;
39
+ getBiometricVerification(biometricVerificationId: string): Promise<StandardResponse<BiometricVerificationResponse>>;
40
40
  /**
41
41
  * Avisa que el proveedor reportó algo sobre una verificación. **Su único caller legítimo es
42
42
  * `kyc-metamap-webhook`** — un producto normal no llama acá.
@@ -51,5 +51,5 @@ export interface IBiometricsBusinessApi {
51
51
  * - `5xx` → devolver no-200 al proveedor **para que reintente**. Esto es seguro SOLO en la rama
52
52
  * biométrica, que corta antes de escribir nada y no tiene efectos que duplicar.
53
53
  */
54
- applyBiometricEvent(biometricVerificationId: string, data: BiometricVerificationChangedV1): Promise<ApiGatewayResponse<void>>;
54
+ applyBiometricEvent(biometricVerificationId: string, data: BiometricVerificationChangedV1): Promise<StandardResponse<void>>;
55
55
  }
@@ -2,10 +2,12 @@ import type { StandardResponse } from "@fiado/gateway-adapter";
2
2
  import type { IHttpRequest } from "@fiado/http-client";
3
3
  import type { KycByDirectoryResponse } from "@fiado/type-kit/bin/kyc/index.js";
4
4
  import type { IKycVerificationsBusinessApi } from "./interfaces/IKycVerificationsBusinessApi.js";
5
+ import type { CountryWorkflowResponse } from "./dtos/CountryWorkflowResponse.js";
5
6
  /** Contrato y semántica completos → `IKycVerificationsBusinessApi` (`StandardResponse` → leer `result.data`). */
6
7
  export declare class KycVerificationsBusinessApi implements IKycVerificationsBusinessApi {
7
8
  private httpRequest;
8
9
  private readonly baseUrl;
9
10
  constructor(httpRequest: IHttpRequest);
10
11
  getByDirectory(directoryId: string): Promise<StandardResponse<KycByDirectoryResponse>>;
12
+ getFlowById(id: string): Promise<StandardResponse<CountryWorkflowResponse>>;
11
13
  }
@@ -28,6 +28,12 @@ let KycVerificationsBusinessApi = class KycVerificationsBusinessApi {
28
28
  const url = `${this.baseUrl}/verifications/by-directory/${encodeURIComponent(directoryId)}`;
29
29
  return await this.httpRequest.get(url);
30
30
  }
31
+ async getFlowById(id) {
32
+ // SIN prefijo `/private`, por lo mismo que `getByDirectory`: la API que resuelve el SSM es
33
+ // PRIVADA COMPLETA y sus paths cuelgan de la raíz.
34
+ const url = `${this.baseUrl}/flows/${encodeURIComponent(id)}`;
35
+ return await this.httpRequest.get(url);
36
+ }
31
37
  };
32
38
  KycVerificationsBusinessApi = __decorate([
33
39
  injectable(),
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Una fila del catálogo `CountryWorkflow` de `kyc-verifications-business`: el mapa
3
+ * `id` → `workflowId` de Metamap.
4
+ *
5
+ * ⚠️ El `id` **no siempre es un país**. La tabla arrancó como "flow por país" (`484` = México,
6
+ * `USA`, `724` = pasaportes de España) pero ya lleva llaves sintéticas para flows que no dependen
7
+ * del país: `PROOF_OF_ADDRESS` (comprobante de domicilio), `000` (agentes) y `FACEMATCH`. Tratalo
8
+ * como una llave opaca de catálogo, no como un `CountryId`.
9
+ *
10
+ * El backoffice edita estas filas en caliente, así que el valor **puede cambiar sin un deploy**.
11
+ * Ese es justamente el motivo de leerlo de acá y no de una env var.
12
+ */
13
+ export interface CountryWorkflowResponse {
14
+ /** La llave del catálogo: un `CountryId`, o una llave sintética como `FACEMATCH`. */
15
+ id: string;
16
+ /** El flow ID de Metamap. Es lo que se manda como `flowId` al widget hospedado. */
17
+ workflowId: string;
18
+ /** Texto libre que escribe el backoffice. Puede venir `null`. */
19
+ description: string | null;
20
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -1,5 +1,6 @@
1
1
  import type { StandardResponse } from "@fiado/gateway-adapter";
2
2
  import type { KycByDirectoryResponse } from "@fiado/type-kit/bin/kyc/index.js";
3
+ import type { CountryWorkflowResponse } from "../dtos/CountryWorkflowResponse.js";
3
4
  /**
4
5
  * Publisher del privado de `kyc-verifications-business`.
5
6
  *
@@ -29,4 +30,24 @@ export interface IKycVerificationsBusinessApi {
29
30
  * @throws propaga el error del destino; nunca devuelve fallback.
30
31
  */
31
32
  getByDirectory(directoryId: string): Promise<StandardResponse<KycByDirectoryResponse>>;
33
+ /**
34
+ * Una fila del catálogo `CountryWorkflow`: la traducción `id` → `workflowId` de Metamap.
35
+ *
36
+ * Es la **fuente canónica** del flow ID. El backoffice edita estas filas en caliente, así que
37
+ * un consumidor que guarde el flow ID en una env var queda desincronizado en cuanto alguien lo
38
+ * cambia ahí — sin que nadie se entere hasta que Metamap rechaza la verificación.
39
+ *
40
+ * El `id` es una llave opaca de catálogo, NO necesariamente un país: además de los `CountryId`
41
+ * (`484`, `USA`, `724`) la tabla lleva llaves sintéticas como `PROOF_OF_ADDRESS`, `000` y
42
+ * `FACEMATCH`.
43
+ *
44
+ * ⚠️ Si la llave no existe en el catálogo el destino responde **400**, no 404 — verificado
45
+ * contra el PR #92 de `kyc-verifications-business`. Es el comportamiento que ya tenía
46
+ * `CountryWorkflowManager` para el endpoint público y no se cambió para no romperle el
47
+ * contrato. Un flow que falta es un error de configuración, no un caso vacío: el consumidor
48
+ * debe fallar ruidoso, nunca degradar a un default.
49
+ *
50
+ * @throws propaga el error del destino; nunca devuelve fallback.
51
+ */
52
+ getFlowById(id: string): Promise<StandardResponse<CountryWorkflowResponse>>;
32
53
  }
@@ -1,4 +1,5 @@
1
1
  export * from './api/interfaces/IKycVerificationsBusinessApi.js';
2
+ export * from './api/dtos/CountryWorkflowResponse.js';
2
3
  export * from './api/KycVerificationsBusinessApi.js';
3
4
  export * from './queue/interfaces/IRetailKycInboundPublisher.js';
4
5
  export * from './queue/RetailKycInboundPublisher.js';
package/bin/kyc/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  export * from './api/interfaces/IKycVerificationsBusinessApi.js';
2
+ export * from './api/dtos/CountryWorkflowResponse.js';
2
3
  export * from './api/KycVerificationsBusinessApi.js';
3
4
  export * from './queue/interfaces/IRetailKycInboundPublisher.js';
4
5
  export * from './queue/RetailKycInboundPublisher.js';
@@ -1,6 +1,6 @@
1
1
  import type { IHttpRequest } from "@fiado/http-client";
2
2
  import { StandardResponse } from "@fiado/gateway-adapter";
3
- import { RetailerValidationDto, StoreValidationDto, RetailUserValidationDto, CollectorValidationDto } from "@fiado/type-kit/bin/retailOrg/index.js";
3
+ import { RetailerValidationDto, StoreValidationDto, RetailUserValidationDto, CollectorValidationDto, PrivateSellerListResponse, RetailUserStatusEnum } from "@fiado/type-kit/bin/retailOrg/index.js";
4
4
  import { IRetailOrgBusinessApi } from "./interfaces/IRetailOrgBusinessApi.js";
5
5
  /**
6
6
  * Publisher HTTP del lambda `retail-org-business` (componente 04 SureKeep Fase 1) para sus 6
@@ -26,4 +26,13 @@ export default class RetailOrgBusinessApi implements IRetailOrgBusinessApi {
26
26
  getStoreUsers(retailerId: string, storeId: string, tenantId: string): Promise<StandardResponse<RetailUserValidationDto[]>>;
27
27
  getUserByCognitoSub(cognitoSub: string, tenantId: string): Promise<StandardResponse<RetailUserValidationDto>>;
28
28
  listActiveCollectorsByRetailer(retailerId: string, tenantId: string): Promise<StandardResponse<CollectorValidationDto[]>>;
29
+ /**
30
+ * SureKeep F3 — el padrón de vendedores que consume el motor de comisiones al cerrar el corte.
31
+ * Contrato y semántica completos (incluido por qué `status` NO tiene default y por qué
32
+ * `callerService` es obligatorio) → `IRetailOrgBusinessApi.listSellers`.
33
+ *
34
+ * ⚠️ Es el único método de este publisher con el `retailerId` en el QUERY y no anidado en el
35
+ * path: el caller no está validando un recurso, está pidiendo una colección entera.
36
+ */
37
+ listSellers(retailerId: string, tenantId: string, callerService: string, status?: RetailUserStatusEnum): Promise<StandardResponse<PrivateSellerListResponse>>;
29
38
  }
@@ -55,6 +55,22 @@ let RetailOrgBusinessApi = class RetailOrgBusinessApi {
55
55
  const url = `${this.baseUrl}/private/retailers/${encodeURIComponent(retailerId)}/collectors?tenantId=${encodeURIComponent(tenantId)}`;
56
56
  return await this.httpRequest.get(url);
57
57
  }
58
+ /**
59
+ * SureKeep F3 — el padrón de vendedores que consume el motor de comisiones al cerrar el corte.
60
+ * Contrato y semántica completos (incluido por qué `status` NO tiene default y por qué
61
+ * `callerService` es obligatorio) → `IRetailOrgBusinessApi.listSellers`.
62
+ *
63
+ * ⚠️ Es el único método de este publisher con el `retailerId` en el QUERY y no anidado en el
64
+ * path: el caller no está validando un recurso, está pidiendo una colección entera.
65
+ */
66
+ async listSellers(retailerId, tenantId, callerService, status) {
67
+ // `URLSearchParams` en vez de concatenar: `status` es opcional y armar el `&status=` a mano
68
+ // deja un `&status=undefined` en cuanto alguien se distrae.
69
+ const query = new URLSearchParams({ retailerId, tenantId, callerService });
70
+ if (status)
71
+ query.set("status", status);
72
+ return await this.httpRequest.get(`${this.baseUrl}/private/sellers?${query.toString()}`);
73
+ }
58
74
  };
59
75
  RetailOrgBusinessApi = __decorate([
60
76
  injectable(),
@@ -1,5 +1,5 @@
1
1
  import { StandardResponse } from "@fiado/gateway-adapter";
2
- import { RetailerValidationDto, StoreValidationDto, RetailUserValidationDto, CollectorValidationDto } from "@fiado/type-kit/bin/retailOrg/index.js";
2
+ import { RetailerValidationDto, StoreValidationDto, RetailUserValidationDto, CollectorValidationDto, PrivateSellerListResponse, RetailUserStatusEnum } from "@fiado/type-kit/bin/retailOrg/index.js";
3
3
  /**
4
4
  * Contrato del publisher HTTP del lambda `retail-org-business` (componente 04 SureKeep Fase 1)
5
5
  * para sus 6 endpoints privados de VALIDACIÓN (service-to-service, VPC-only).
@@ -102,4 +102,39 @@ export interface IRetailOrgBusinessApi {
102
102
  * `result.data`, NO `result.body`). Retorna `[]` (no 404) si el retailer no tiene cobradores activos.
103
103
  */
104
104
  listActiveCollectorsByRetailer(retailerId: string, tenantId: string): Promise<StandardResponse<CollectorValidationDto[]>>;
105
+ /**
106
+ * `GET /private/sellers?retailerId=…` (SureKeep F3) — el **padrón de vendedores** de una cadena.
107
+ *
108
+ * ── 🔴 Por qué este método existe ────────────────────────────────────────────────────────────
109
+ * Lo consume el **motor de comisiones** al cerrar el corte del periodo, para saber a quién hay
110
+ * que pagarle y abrir una fila de devengo por cada uno. **Sin esta lista el fan-out del cierre
111
+ * no arranca.** Publicarlo acá es lo que evita que el motor caiga en `DIRECT_HTTP` (antipatrón
112
+ * duro): entre lambdas de Fiado la comunicación va SIEMPRE por `@fiado/api-invoker`.
113
+ *
114
+ * Devuelve el shape mínimo —`sellerId`, `displayName`, `homeStoreId`, `commissionTier`,
115
+ * `status`— **sin PII de contacto**: nada de teléfono ni `cognitoSub`. `commissionTier` no es
116
+ * adorno: es lo que el motor usa para resolver CUÁNTO le toca a cada quien según el plan vigente.
117
+ *
118
+ * Excluye a los COBRADORES (`commissionTier === 'collector'`), que tienen su propio método
119
+ * (`listActiveCollectorsByRetailer`) — la cobranza es del otro dominio.
120
+ *
121
+ * ── ⚠️ `status` NO tiene default ─────────────────────────────────────────────────────────────
122
+ * Omitirlo devuelve **TODOS** los estados, no solo los activos. Es deliberado: un vendedor dado
123
+ * de baja el día 20 **sí tiene comisión** por lo que vendió hasta el 19, y un default `ACTIVE`
124
+ * lo perdería en silencio sin dejar ninguna forma de pedir el padrón completo. Si el motor
125
+ * quiere solo activos, que lo diga explícito.
126
+ *
127
+ * ── `callerService`: quién exportó la nómina ─────────────────────────────────────────────────
128
+ * El endpoint es `Feature.ANONIMUS` (sin token de usuario), así que **no hay `actorId`** al que
129
+ * atribuirle la lectura — y esto exporta la nómina completa de una cadena en una sola llamada.
130
+ * El lambda destino registra el acceso en `SharedPiiAccessLog_GT` y usa este valor para saber
131
+ * QUIÉN lo pidió. Si se omite, el registro queda como `m2m:unknown` (con WARN en CloudWatch);
132
+ * **no lo omitas**: es la única traza que existe de una exportación masiva de datos de empleados.
133
+ *
134
+ * @throws `404 RETAILER_NOT_FOUND` si el retailer no existe en ese silo. Un `[]` no distinguiría
135
+ * "esta cadena no tiene vendedores" de "preguntaste por una cadena que no existe", y lo segundo
136
+ * cerraría el corte sin devengos sin que nadie se entere hasta el día de pago.
137
+ * @throws `400 VALIDATION_ERROR` si `retailerId` no es un ULID · `400 UNKNOWN_TENANT` sin `tenantId`.
138
+ */
139
+ listSellers(retailerId: string, tenantId: string, callerService: string, status?: RetailUserStatusEnum): Promise<StandardResponse<PrivateSellerListResponse>>;
105
140
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fiado/api-invoker",
3
- "version": "5.19.0",
3
+ "version": "5.21.0",
4
4
  "description": "Sirve como un puente entre diferentes funciones lambda, facilitando la comunicación entre ellas a través de invocaciones http",
5
5
  "type": "module",
6
6
  "main": "bin/index.js",
@@ -34,7 +34,7 @@
34
34
  "@fiado/gateway-adapter": "^3.9.0",
35
35
  "@fiado/http-client": "^2.0.1",
36
36
  "@fiado/logger": "^1.1.3",
37
- "@fiado/type-kit": "^3.276.0",
37
+ "@fiado/type-kit": "^3.278.0",
38
38
  "dotenv": "^16.4.7"
39
39
  },
40
40
  "peerDependencies": {
@@ -5,7 +5,7 @@ import type {
5
5
  BiometricVerificationResponse,
6
6
  CreateBiometricVerificationRequest,
7
7
  } from "@fiado/type-kit/bin/biometrics/index.js";
8
- import type { ApiGatewayResponse } from "@fiado/gateway-adapter";
8
+ import type { StandardResponse } from "@fiado/gateway-adapter";
9
9
  // ⚠️ `import type` y NO un import de valor. `@fiado/http-client` se publica como CJS; importar
10
10
  // `IHttpRequest` como valor hace que jest falle al cargar el módulo bajo ESM
11
11
  // ("does not provide an export named 'IHttpRequest'"). Con `import type` se borra al compilar.
@@ -35,14 +35,14 @@ export class BiometricsBusinessApi implements IBiometricsBusinessApi {
35
35
 
36
36
  async createBiometricVerification(
37
37
  data: CreateBiometricVerificationRequest,
38
- ): Promise<ApiGatewayResponse<BiometricVerificationResponse>> {
38
+ ): Promise<StandardResponse<BiometricVerificationResponse>> {
39
39
  const url = `${this.baseUrl}/biometric-verifications`;
40
40
  return await this.httpRequest.post(url, data);
41
41
  }
42
42
 
43
43
  async getBiometricVerification(
44
44
  biometricVerificationId: string,
45
- ): Promise<ApiGatewayResponse<BiometricVerificationResponse>> {
45
+ ): Promise<StandardResponse<BiometricVerificationResponse>> {
46
46
  // encodeURIComponent y no interpolación pelada: el id va en el path, y aunque hoy lo genera
47
47
  // biometrics-business, el día que un caller pase basura preferimos un 404 a una URL rota.
48
48
  const url = `${this.baseUrl}/biometric-verifications/${encodeURIComponent(biometricVerificationId)}`;
@@ -52,7 +52,7 @@ export class BiometricsBusinessApi implements IBiometricsBusinessApi {
52
52
  async applyBiometricEvent(
53
53
  biometricVerificationId: string,
54
54
  data: BiometricVerificationChangedV1,
55
- ): Promise<ApiGatewayResponse<void>> {
55
+ ): Promise<StandardResponse<void>> {
56
56
  const url = `${this.baseUrl}/biometric-verifications/${encodeURIComponent(biometricVerificationId)}/events`;
57
57
  return await this.httpRequest.post(url, data);
58
58
  }
@@ -3,7 +3,7 @@ import type {
3
3
  BiometricVerificationResponse,
4
4
  CreateBiometricVerificationRequest,
5
5
  } from "@fiado/type-kit/bin/biometrics/index.js";
6
- import type { ApiGatewayResponse } from "@fiado/gateway-adapter";
6
+ import type { StandardResponse } from "@fiado/gateway-adapter";
7
7
 
8
8
  /**
9
9
  * Cliente de `biometrics-business`, el master de la lógica biométrica de Fiado.
@@ -33,7 +33,7 @@ export interface IBiometricsBusinessApi {
33
33
  */
34
34
  createBiometricVerification(
35
35
  data: CreateBiometricVerificationRequest,
36
- ): Promise<ApiGatewayResponse<BiometricVerificationResponse>>;
36
+ ): Promise<StandardResponse<BiometricVerificationResponse>>;
37
37
 
38
38
  /**
39
39
  * El estado actual de una verificación. Es el endpoint de polling.
@@ -46,7 +46,7 @@ export interface IBiometricsBusinessApi {
46
46
  */
47
47
  getBiometricVerification(
48
48
  biometricVerificationId: string,
49
- ): Promise<ApiGatewayResponse<BiometricVerificationResponse>>;
49
+ ): Promise<StandardResponse<BiometricVerificationResponse>>;
50
50
 
51
51
  /**
52
52
  * Avisa que el proveedor reportó algo sobre una verificación. **Su único caller legítimo es
@@ -65,5 +65,5 @@ export interface IBiometricsBusinessApi {
65
65
  applyBiometricEvent(
66
66
  biometricVerificationId: string,
67
67
  data: BiometricVerificationChangedV1,
68
- ): Promise<ApiGatewayResponse<void>>;
68
+ ): Promise<StandardResponse<void>>;
69
69
  }
@@ -3,6 +3,7 @@ import type { StandardResponse } from "@fiado/gateway-adapter";
3
3
  import type { IHttpRequest } from "@fiado/http-client";
4
4
  import type { KycByDirectoryResponse } from "@fiado/type-kit/bin/kyc/index.js";
5
5
  import type { IKycVerificationsBusinessApi } from "./interfaces/IKycVerificationsBusinessApi.js";
6
+ import type { CountryWorkflowResponse } from "./dtos/CountryWorkflowResponse.js";
6
7
 
7
8
  /** Contrato y semántica completos → `IKycVerificationsBusinessApi` (`StandardResponse` → leer `result.data`). */
8
9
  @injectable()
@@ -22,4 +23,11 @@ export class KycVerificationsBusinessApi implements IKycVerificationsBusinessApi
22
23
  const url = `${this.baseUrl}/verifications/by-directory/${encodeURIComponent(directoryId)}`;
23
24
  return await this.httpRequest.get<StandardResponse<KycByDirectoryResponse>>(url);
24
25
  }
26
+
27
+ async getFlowById(id: string): Promise<StandardResponse<CountryWorkflowResponse>> {
28
+ // SIN prefijo `/private`, por lo mismo que `getByDirectory`: la API que resuelve el SSM es
29
+ // PRIVADA COMPLETA y sus paths cuelgan de la raíz.
30
+ const url = `${this.baseUrl}/flows/${encodeURIComponent(id)}`;
31
+ return await this.httpRequest.get<StandardResponse<CountryWorkflowResponse>>(url);
32
+ }
25
33
  }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Una fila del catálogo `CountryWorkflow` de `kyc-verifications-business`: el mapa
3
+ * `id` → `workflowId` de Metamap.
4
+ *
5
+ * ⚠️ El `id` **no siempre es un país**. La tabla arrancó como "flow por país" (`484` = México,
6
+ * `USA`, `724` = pasaportes de España) pero ya lleva llaves sintéticas para flows que no dependen
7
+ * del país: `PROOF_OF_ADDRESS` (comprobante de domicilio), `000` (agentes) y `FACEMATCH`. Tratalo
8
+ * como una llave opaca de catálogo, no como un `CountryId`.
9
+ *
10
+ * El backoffice edita estas filas en caliente, así que el valor **puede cambiar sin un deploy**.
11
+ * Ese es justamente el motivo de leerlo de acá y no de una env var.
12
+ */
13
+ export interface CountryWorkflowResponse {
14
+ /** La llave del catálogo: un `CountryId`, o una llave sintética como `FACEMATCH`. */
15
+ id: string;
16
+ /** El flow ID de Metamap. Es lo que se manda como `flowId` al widget hospedado. */
17
+ workflowId: string;
18
+ /** Texto libre que escribe el backoffice. Puede venir `null`. */
19
+ description: string | null;
20
+ }
@@ -1,5 +1,6 @@
1
1
  import type { StandardResponse } from "@fiado/gateway-adapter";
2
2
  import type { KycByDirectoryResponse } from "@fiado/type-kit/bin/kyc/index.js";
3
+ import type { CountryWorkflowResponse } from "../dtos/CountryWorkflowResponse.js";
3
4
 
4
5
  /**
5
6
  * Publisher del privado de `kyc-verifications-business`.
@@ -30,4 +31,25 @@ export interface IKycVerificationsBusinessApi {
30
31
  * @throws propaga el error del destino; nunca devuelve fallback.
31
32
  */
32
33
  getByDirectory(directoryId: string): Promise<StandardResponse<KycByDirectoryResponse>>;
34
+
35
+ /**
36
+ * Una fila del catálogo `CountryWorkflow`: la traducción `id` → `workflowId` de Metamap.
37
+ *
38
+ * Es la **fuente canónica** del flow ID. El backoffice edita estas filas en caliente, así que
39
+ * un consumidor que guarde el flow ID en una env var queda desincronizado en cuanto alguien lo
40
+ * cambia ahí — sin que nadie se entere hasta que Metamap rechaza la verificación.
41
+ *
42
+ * El `id` es una llave opaca de catálogo, NO necesariamente un país: además de los `CountryId`
43
+ * (`484`, `USA`, `724`) la tabla lleva llaves sintéticas como `PROOF_OF_ADDRESS`, `000` y
44
+ * `FACEMATCH`.
45
+ *
46
+ * ⚠️ Si la llave no existe en el catálogo el destino responde **400**, no 404 — verificado
47
+ * contra el PR #92 de `kyc-verifications-business`. Es el comportamiento que ya tenía
48
+ * `CountryWorkflowManager` para el endpoint público y no se cambió para no romperle el
49
+ * contrato. Un flow que falta es un error de configuración, no un caso vacío: el consumidor
50
+ * debe fallar ruidoso, nunca degradar a un default.
51
+ *
52
+ * @throws propaga el error del destino; nunca devuelve fallback.
53
+ */
54
+ getFlowById(id: string): Promise<StandardResponse<CountryWorkflowResponse>>;
33
55
  }
package/src/kyc/index.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export * from './api/interfaces/IKycVerificationsBusinessApi.js';
2
+ export * from './api/dtos/CountryWorkflowResponse.js';
2
3
  export * from './api/KycVerificationsBusinessApi.js';
3
4
  export * from './queue/interfaces/IRetailKycInboundPublisher.js';
4
5
  export * from './queue/RetailKycInboundPublisher.js';
@@ -6,6 +6,8 @@ import {
6
6
  StoreValidationDto,
7
7
  RetailUserValidationDto,
8
8
  CollectorValidationDto,
9
+ PrivateSellerListResponse,
10
+ RetailUserStatusEnum,
9
11
  } from "@fiado/type-kit/bin/retailOrg/index.js";
10
12
  import { IRetailOrgBusinessApi } from "./interfaces/IRetailOrgBusinessApi.js";
11
13
 
@@ -78,4 +80,25 @@ export default class RetailOrgBusinessApi implements IRetailOrgBusinessApi {
78
80
  const url = `${this.baseUrl}/private/retailers/${encodeURIComponent(retailerId)}/collectors?tenantId=${encodeURIComponent(tenantId)}`;
79
81
  return await this.httpRequest.get(url);
80
82
  }
83
+
84
+ /**
85
+ * SureKeep F3 — el padrón de vendedores que consume el motor de comisiones al cerrar el corte.
86
+ * Contrato y semántica completos (incluido por qué `status` NO tiene default y por qué
87
+ * `callerService` es obligatorio) → `IRetailOrgBusinessApi.listSellers`.
88
+ *
89
+ * ⚠️ Es el único método de este publisher con el `retailerId` en el QUERY y no anidado en el
90
+ * path: el caller no está validando un recurso, está pidiendo una colección entera.
91
+ */
92
+ async listSellers(
93
+ retailerId: string,
94
+ tenantId: string,
95
+ callerService: string,
96
+ status?: RetailUserStatusEnum,
97
+ ): Promise<StandardResponse<PrivateSellerListResponse>> {
98
+ // `URLSearchParams` en vez de concatenar: `status` es opcional y armar el `&status=` a mano
99
+ // deja un `&status=undefined` en cuanto alguien se distrae.
100
+ const query = new URLSearchParams({ retailerId, tenantId, callerService });
101
+ if (status) query.set("status", status);
102
+ return await this.httpRequest.get(`${this.baseUrl}/private/sellers?${query.toString()}`);
103
+ }
81
104
  }
@@ -4,6 +4,8 @@ import {
4
4
  StoreValidationDto,
5
5
  RetailUserValidationDto,
6
6
  CollectorValidationDto,
7
+ PrivateSellerListResponse,
8
+ RetailUserStatusEnum,
7
9
  } from "@fiado/type-kit/bin/retailOrg/index.js";
8
10
 
9
11
  /**
@@ -133,4 +135,45 @@ export interface IRetailOrgBusinessApi {
133
135
  retailerId: string,
134
136
  tenantId: string,
135
137
  ): Promise<StandardResponse<CollectorValidationDto[]>>;
138
+
139
+ /**
140
+ * `GET /private/sellers?retailerId=…` (SureKeep F3) — el **padrón de vendedores** de una cadena.
141
+ *
142
+ * ── 🔴 Por qué este método existe ────────────────────────────────────────────────────────────
143
+ * Lo consume el **motor de comisiones** al cerrar el corte del periodo, para saber a quién hay
144
+ * que pagarle y abrir una fila de devengo por cada uno. **Sin esta lista el fan-out del cierre
145
+ * no arranca.** Publicarlo acá es lo que evita que el motor caiga en `DIRECT_HTTP` (antipatrón
146
+ * duro): entre lambdas de Fiado la comunicación va SIEMPRE por `@fiado/api-invoker`.
147
+ *
148
+ * Devuelve el shape mínimo —`sellerId`, `displayName`, `homeStoreId`, `commissionTier`,
149
+ * `status`— **sin PII de contacto**: nada de teléfono ni `cognitoSub`. `commissionTier` no es
150
+ * adorno: es lo que el motor usa para resolver CUÁNTO le toca a cada quien según el plan vigente.
151
+ *
152
+ * Excluye a los COBRADORES (`commissionTier === 'collector'`), que tienen su propio método
153
+ * (`listActiveCollectorsByRetailer`) — la cobranza es del otro dominio.
154
+ *
155
+ * ── ⚠️ `status` NO tiene default ─────────────────────────────────────────────────────────────
156
+ * Omitirlo devuelve **TODOS** los estados, no solo los activos. Es deliberado: un vendedor dado
157
+ * de baja el día 20 **sí tiene comisión** por lo que vendió hasta el 19, y un default `ACTIVE`
158
+ * lo perdería en silencio sin dejar ninguna forma de pedir el padrón completo. Si el motor
159
+ * quiere solo activos, que lo diga explícito.
160
+ *
161
+ * ── `callerService`: quién exportó la nómina ─────────────────────────────────────────────────
162
+ * El endpoint es `Feature.ANONIMUS` (sin token de usuario), así que **no hay `actorId`** al que
163
+ * atribuirle la lectura — y esto exporta la nómina completa de una cadena en una sola llamada.
164
+ * El lambda destino registra el acceso en `SharedPiiAccessLog_GT` y usa este valor para saber
165
+ * QUIÉN lo pidió. Si se omite, el registro queda como `m2m:unknown` (con WARN en CloudWatch);
166
+ * **no lo omitas**: es la única traza que existe de una exportación masiva de datos de empleados.
167
+ *
168
+ * @throws `404 RETAILER_NOT_FOUND` si el retailer no existe en ese silo. Un `[]` no distinguiría
169
+ * "esta cadena no tiene vendedores" de "preguntaste por una cadena que no existe", y lo segundo
170
+ * cerraría el corte sin devengos sin que nadie se entere hasta el día de pago.
171
+ * @throws `400 VALIDATION_ERROR` si `retailerId` no es un ULID · `400 UNKNOWN_TENANT` sin `tenantId`.
172
+ */
173
+ listSellers(
174
+ retailerId: string,
175
+ tenantId: string,
176
+ callerService: string,
177
+ status?: RetailUserStatusEnum,
178
+ ): Promise<StandardResponse<PrivateSellerListResponse>>;
136
179
  }