@fiado/api-invoker 5.38.0 → 5.40.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.
Files changed (36) hide show
  1. package/bin/container.config.js +5 -0
  2. package/bin/index.d.ts +2 -0
  3. package/bin/index.js +2 -0
  4. package/bin/retailCatalog/api/interfaces/IRetailCatalogBusinessApi.d.ts +7 -0
  5. package/bin/retailNotifications/api/RetailNotificationsWebsocketApi.d.ts +17 -0
  6. package/bin/retailNotifications/api/RetailNotificationsWebsocketApi.js +36 -0
  7. package/bin/retailNotifications/api/dtos/NotifySubjectRequest.d.ts +18 -0
  8. package/bin/retailNotifications/api/dtos/NotifySubjectRequest.js +1 -0
  9. package/bin/retailNotifications/api/dtos/NotifySubjectResult.d.ts +12 -0
  10. package/bin/retailNotifications/api/dtos/NotifySubjectResult.js +1 -0
  11. package/bin/retailNotifications/api/interfaces/IRetailNotificationsWebsocketApi.d.ts +43 -0
  12. package/bin/retailNotifications/api/interfaces/IRetailNotificationsWebsocketApi.js +1 -0
  13. package/bin/retailNotifications/index.d.ts +4 -0
  14. package/bin/retailNotifications/index.js +4 -0
  15. package/bin/transactionProcessor/api/TransactionProcessorApi.d.ts +2 -2
  16. package/bin/transactionProcessor/api/interfaces/ITransactionProcessorApi.d.ts +2 -2
  17. package/bin/webhook-business/index.d.ts +2 -0
  18. package/bin/webhook-business/index.js +2 -0
  19. package/bin/webhook-business/queue/WebhookBusinessPublisher.d.ts +6 -0
  20. package/bin/webhook-business/queue/WebhookBusinessPublisher.js +32 -0
  21. package/bin/webhook-business/queue/interfaces/IWebhookBusinessPublisher.d.ts +20 -0
  22. package/bin/webhook-business/queue/interfaces/IWebhookBusinessPublisher.js +1 -0
  23. package/package.json +2 -2
  24. package/src/container.config.ts +6 -0
  25. package/src/index.ts +2 -0
  26. package/src/retailCatalog/api/interfaces/IRetailCatalogBusinessApi.ts +10 -0
  27. package/src/retailNotifications/api/RetailNotificationsWebsocketApi.ts +24 -0
  28. package/src/retailNotifications/api/dtos/NotifySubjectRequest.ts +19 -0
  29. package/src/retailNotifications/api/dtos/NotifySubjectResult.ts +12 -0
  30. package/src/retailNotifications/api/interfaces/IRetailNotificationsWebsocketApi.ts +44 -0
  31. package/src/retailNotifications/index.ts +4 -0
  32. package/src/transactionProcessor/api/TransactionProcessorApi.ts +2 -2
  33. package/src/transactionProcessor/api/interfaces/ITransactionProcessorApi.ts +2 -2
  34. package/src/webhook-business/index.ts +2 -0
  35. package/src/webhook-business/queue/WebhookBusinessPublisher.ts +26 -0
  36. package/src/webhook-business/queue/interfaces/IWebhookBusinessPublisher.ts +21 -0
@@ -128,6 +128,8 @@ import { ResearchSheetsApi } from "./research-sheets/index.js";
128
128
  import { AuthBusinessApi } from "./auth-business/index.js";
129
129
  // KYC verifications business — lectura confirmatoria server-side del KYC por directoryId (SureKeep F2)
130
130
  import { KycVerificationsBusinessApi } from "./kyc/index.js";
131
+ // Webhook business — publisher de la cola compartida de entrega de eventos a suscriptores externos
132
+ import { WebhookBusinessPublisher } from "./webhook-business/index.js";
131
133
  import { CentralPaymentsWebhookApi } from "./central-payments-webhook/index.js";
132
134
  import CollectionEngineApi from "./collection-engine/api/CollectionEngineApi.js";
133
135
  import DomainCallbackApi from "./domain-callback/api/DomainCallbackApi.js";
@@ -274,6 +276,9 @@ export const apiInvokerBindings = new ContainerModule(({ bind }) => {
274
276
  // KYC verifications business — lectura confirmatoria server-side del KYC por directoryId.
275
277
  // La consume el wizard de SureKeep F2 tras el aviso async del stream de `KycVerifications`.
276
278
  bind("IKycVerificationsBusinessApi").to(KycVerificationsBusinessApi);
279
+ // Webhook business — lo produce cualquier lambda que tenga algo que avisarle a un externo.
280
+ // El productor necesita WEBHOOK_BUSINESS_QUEUE_URL y sqs:SendMessage sobre la cola.
281
+ bind("IWebhookBusinessPublisher").to(WebhookBusinessPublisher);
277
282
  bind("ICentralPaymentsWebhookApi").to(CentralPaymentsWebhookApi);
278
283
  // Collection engine — motor de cobro central multitenant (collect/registerCollectible/deregister)
279
284
  bind("ICollectionEngineApi").to(CollectionEngineApi);
package/bin/index.d.ts CHANGED
@@ -85,6 +85,7 @@ export * from "./datalakeQuery/index.js";
85
85
  export * from "./retailCustomer/index.js";
86
86
  export * from "./retailCatalog/index.js";
87
87
  export * from "./retailCards/index.js";
88
+ export * from "./retailNotifications/index.js";
88
89
  export * from "./shortlink/index.js";
89
90
  export * from "./loanConfig/index.js";
90
91
  export * from "./loanOfferings/index.js";
@@ -111,3 +112,4 @@ export * from "./central-payments-webhook/index.js";
111
112
  export * from "./collection-engine/index.js";
112
113
  export * from "./domain-callback/index.js";
113
114
  export * from "./datacultrConnector/index.js";
115
+ export * from "./webhook-business/index.js";
package/bin/index.js CHANGED
@@ -85,6 +85,7 @@ export * from "./datalakeQuery/index.js";
85
85
  export * from "./retailCustomer/index.js";
86
86
  export * from "./retailCatalog/index.js";
87
87
  export * from "./retailCards/index.js";
88
+ export * from "./retailNotifications/index.js";
88
89
  export * from "./shortlink/index.js";
89
90
  export * from "./loanConfig/index.js";
90
91
  export * from "./loanOfferings/index.js";
@@ -111,3 +112,4 @@ export * from "./central-payments-webhook/index.js";
111
112
  export * from "./collection-engine/index.js";
112
113
  export * from "./domain-callback/index.js";
113
114
  export * from "./datacultrConnector/index.js";
115
+ export * from "./webhook-business/index.js";
@@ -1,4 +1,5 @@
1
1
  import { StandardResponse } from "@fiado/gateway-adapter";
2
+ import type { MdmLockModeEnum, MdmProviderEnum } from "@fiado/type-kit/bin/retailCatalog/index.js";
2
3
  /**
3
4
  * Contrato del publisher HTTP del lambda `retail-catalog-business` (SureKeep Fase 1, pista Retail)
4
5
  * para sus endpoints privados del ciclo de venta del IMEI, consumidos por el wizard F2.
@@ -45,6 +46,12 @@ export interface StoreCatalogItem {
45
46
  tier: string;
46
47
  /** Financieras alternativas habilitadas (derivación externa, paso 04 del wizard). */
47
48
  eligibleFinanciers: string[];
49
+ /** `true` si el producto se vende con MDM. */
50
+ mdmEnabled: boolean;
51
+ /** Proveedor MDM; `null` si el producto no lleva MDM. */
52
+ mdmProvider: MdmProviderEnum | null;
53
+ /** Modo de bloqueo pactado; `null` si el producto no lleva MDM. */
54
+ mdmLockMode: MdmLockModeEnum | null;
48
55
  }
49
56
  /** Resultado de la reserva (espeja `ReserveInventoryResponse` del destino). */
50
57
  export interface ReserveInventoryResult {
@@ -0,0 +1,17 @@
1
+ import type { IHttpRequest } from "@fiado/http-client";
2
+ import { StandardResponse } from "@fiado/gateway-adapter";
3
+ import { IRetailNotificationsWebsocketApi } from "./interfaces/IRetailNotificationsWebsocketApi.js";
4
+ import { NotifySubjectRequest } from "./dtos/NotifySubjectRequest.js";
5
+ import { NotifySubjectResult } from "./dtos/NotifySubjectResult.js";
6
+ /**
7
+ * Publisher HTTP del lambda `retail-notifications-websocket` (SureKeep Fase 2) para su privado M2M de
8
+ * avisos al vendedor. Contrato y semántica completos → `IRetailNotificationsWebsocketApi`.
9
+ *
10
+ * Env var requerida en el consumer: `RETAIL_NOTIFICATIONS_WEBSOCKET_URL`.
11
+ */
12
+ export default class RetailNotificationsWebsocketApi implements IRetailNotificationsWebsocketApi {
13
+ private httpRequest;
14
+ private readonly baseUrl;
15
+ constructor(httpRequest: IHttpRequest);
16
+ notifySubject(payload: NotifySubjectRequest): Promise<StandardResponse<NotifySubjectResult>>;
17
+ }
@@ -0,0 +1,36 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __metadata = (this && this.__metadata) || function (k, v) {
8
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
+ };
10
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
11
+ return function (target, key) { decorator(target, key, paramIndex); }
12
+ };
13
+ import { inject, injectable } from "inversify";
14
+ /**
15
+ * Publisher HTTP del lambda `retail-notifications-websocket` (SureKeep Fase 2) para su privado M2M de
16
+ * avisos al vendedor. Contrato y semántica completos → `IRetailNotificationsWebsocketApi`.
17
+ *
18
+ * Env var requerida en el consumer: `RETAIL_NOTIFICATIONS_WEBSOCKET_URL`.
19
+ */
20
+ let RetailNotificationsWebsocketApi = class RetailNotificationsWebsocketApi {
21
+ httpRequest;
22
+ // El buildspec publica la URL en SSM con "/" final — se normaliza para no armar "//private".
23
+ baseUrl = (process.env.RETAIL_NOTIFICATIONS_WEBSOCKET_URL || "").replace(/\/+$/, "");
24
+ constructor(httpRequest) {
25
+ this.httpRequest = httpRequest;
26
+ }
27
+ async notifySubject(payload) {
28
+ return await this.httpRequest.post(`${this.baseUrl}/private/notifications`, payload);
29
+ }
30
+ };
31
+ RetailNotificationsWebsocketApi = __decorate([
32
+ injectable(),
33
+ __param(0, inject("IHttpRequest")),
34
+ __metadata("design:paramtypes", [Object])
35
+ ], RetailNotificationsWebsocketApi);
36
+ export default RetailNotificationsWebsocketApi;
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Catálogo de avisos que el canal WebSocket de SureKeep sabe empujar. Espeja el
3
+ * `NotificationTypeEnum` de `retail-notifications-websocket`, que valida el cuerpo con `@IsEnum`: un tipo
4
+ * fuera del catálogo se rechaza con 400 `INVALID_PAYLOAD`.
5
+ */
6
+ export type RetailNotificationType = 'SALE_UPDATED';
7
+ /**
8
+ * Lo que viaja por el privado M2M: a qué vendedor avisarle, de qué y sobre cuál de sus ventas.
9
+ * NO lleva ningún dato del comprador — el navegador recibe solo el tipo y el id de la venta, y el
10
+ * detalle se lo pide después al wizard.
11
+ */
12
+ export interface NotifySubjectRequest {
13
+ /** `sub` de Cognito del vendedor destinatario. Máx. 128 caracteres. */
14
+ subjectId: string;
15
+ type: RetailNotificationType;
16
+ /** Id de la venta, para que el vendedor sepa de cuál de las suyas se trata. Máx. 128 caracteres. */
17
+ sessionId: string;
18
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Resultado de una entrega. `delivered: 0` significa que el vendedor NO tiene ningún socket abierto
3
+ * —tiene la pestaña cerrada— y NO es un error: el llamador no debe reintentar ni alarmar.
4
+ */
5
+ export interface NotifySubjectResult {
6
+ /** Conexiones a las que llegó el aviso. */
7
+ delivered: number;
8
+ /** Filas de conexiones muertas que el destino limpió de paso. */
9
+ staleRemoved: number;
10
+ /** Conexiones vivas a las que el envío falló. */
11
+ failed: number;
12
+ }
@@ -0,0 +1,43 @@
1
+ import { StandardResponse } from "@fiado/gateway-adapter";
2
+ import { NotifySubjectRequest } from "../dtos/NotifySubjectRequest.js";
3
+ import { NotifySubjectResult } from "../dtos/NotifySubjectResult.js";
4
+ /**
5
+ * Contrato del publisher HTTP del lambda `retail-notifications-websocket` (SureKeep Fase 2): el canal por
6
+ * el que el portal le avisa al VENDEDOR que su venta cambió de estado.
7
+ *
8
+ * Consumidor previsto:
9
+ * - `retail-wizard-business` → al asentar el desenlace de una venta (stream de la cuenta PCF).
10
+ *
11
+ * ─────────────────────────────────────────────────────────────────────────────
12
+ * SIN TENANT — a diferencia de los clients retail, este NO manda `?tenantId=`.
13
+ * ─────────────────────────────────────────────────────────────────────────────
14
+ * El destino resuelve el silo de SU PROPIO entorno (`resolveTenantId`), nunca del request: el
15
+ * `tenantId` es el hash del GSI de ruteo, así que el aislamiento no puede depender de quien llama.
16
+ *
17
+ * ⚠️ Retorno `StandardResponse<T>` — el consumer lee `result.data`, NO `result.body`.
18
+ *
19
+ * Los DTOs viven acá y no en `@fiado/type-kit` porque el destino tampoco depende de type-kit: sus
20
+ * DTOs son locales. Si algún día el contrato se comparte con un tercer repo, mudarlos a type-kit.
21
+ *
22
+ * Env var requerida en el consumer: `RETAIL_NOTIFICATIONS_WEBSOCKET_URL`.
23
+ * El template.yml del consumer la setea con:
24
+ *
25
+ * RETAIL_NOTIFICATIONS_WEBSOCKET_URL: '{{resolve:ssm:retail-notifications-websocket}}'
26
+ *
27
+ * Convención CLAUDE.md global: SSM key = nombre del lambda owner de la URL.
28
+ */
29
+ export interface IRetailNotificationsWebsocketApi {
30
+ /**
31
+ * POST `/private/notifications` — empuja un aviso al vendedor por su WebSocket abierto.
32
+ *
33
+ * Semántica de la respuesta, que decide qué hacer ante cada desenlace:
34
+ * · 200 con `delivered: 0` → el vendedor tiene la pestaña cerrada. NO es error, NO reintentar.
35
+ * · 400 `INVALID_PAYLOAD` → el cuerpo está mal (falta un campo, tipo fuera del catálogo).
36
+ * NO reintentar: reintentarlo igual da lo mismo.
37
+ * · 500 `INTERNAL_ERROR` → falló el canal. El request estaba bien y se PUEDE reintentar.
38
+ *
39
+ * Avisar es un efecto de borde: si falla, el aviso se pierde y lo cubre el barrido del wizard.
40
+ * Nunca debe bloquear ni deshacer la operación que lo disparó.
41
+ */
42
+ notifySubject(payload: NotifySubjectRequest): Promise<StandardResponse<NotifySubjectResult>>;
43
+ }
@@ -0,0 +1,4 @@
1
+ export * from "./api/dtos/NotifySubjectRequest.js";
2
+ export * from "./api/dtos/NotifySubjectResult.js";
3
+ export * from "./api/interfaces/IRetailNotificationsWebsocketApi.js";
4
+ export { default as RetailNotificationsWebsocketApi } from "./api/RetailNotificationsWebsocketApi.js";
@@ -0,0 +1,4 @@
1
+ export * from "./api/dtos/NotifySubjectRequest.js";
2
+ export * from "./api/dtos/NotifySubjectResult.js";
3
+ export * from "./api/interfaces/IRetailNotificationsWebsocketApi.js";
4
+ export { default as RetailNotificationsWebsocketApi } from "./api/RetailNotificationsWebsocketApi.js";
@@ -1,6 +1,6 @@
1
1
  import { ITransactionProcessorApi } from "./interfaces/ITransactionProcessorApi.js";
2
2
  import type { IHttpRequest } from "@fiado/http-client";
3
- import { AuthorizeCollectorOrderRequest, AuthorizeCollectorOrderResponse, AuthorizeCollectorTransactionRequest, AuthorizeCreditCardPurchaseRequest, AuthorizeSpeiBankTransferNcRequest, AuthorizeSpeiMexBankTransferInRequest, AuthorizeRenewalPreAuthRequest, AuthorizeRenewalPreAuthResponse, AuthorizeWelcomeBonusRequest, TransactionProcessorResponse } from "@fiado/type-kit/bin/transactionProcessor/index.js";
3
+ import { AuthorizeCollectorOrderRequest, AuthorizeCollectorOrderResponse, AuthorizeCollectorTransactionRequest, AuthorizeCreditCardPurchaseRequest, AuthorizeSpeiBankTransferNcRequest, AuthorizeSpeiMexBankTransferInRequest, AuthorizeRenewalPreAuthRequest, AuthorizeRenewalPreAuthResponse, AuthorizeBonusRequest, TransactionProcessorResponse } from "@fiado/type-kit/bin/transactionProcessor/index.js";
4
4
  import { ApiGatewayResponse } from "@fiado/gateway-adapter";
5
5
  import { ValidateWalletFundingRequest, ValidateWalletFundingResponse, CreditWalletFundingRequest, CreditWalletFundingResponse, ReverseWalletFundingRequest, ReverseWalletFundingResponse, GetWalletFundingLimitRequest, GetWalletFundingLimitResponse } from "@fiado/type-kit/bin/walletFunding/index.js";
6
6
  import { RemittanceCreditBackRequest, RemittanceCreditBackResponse } from "@fiado/type-kit/bin/remittance/index.js";
@@ -24,6 +24,6 @@ export default class TransactionProcessorApi implements ITransactionProcessorApi
24
24
  walletFundingGetLimit(request: GetWalletFundingLimitRequest): Promise<ApiGatewayResponse<GetWalletFundingLimitResponse>>;
25
25
  creditBack(request: RemittanceCreditBackRequest): Promise<FiadoApiResponse<RemittanceCreditBackResponse>>;
26
26
  authorizeCollectionMovement(request: AuthorizeCollectionMovementRequest): Promise<FiadoApiResponse<AuthorizeCollectionMovementResponse>>;
27
- authorizeWelcomeBonus(request: AuthorizeWelcomeBonusRequest): Promise<FiadoApiResponse<TransactionProcessorResponse>>;
27
+ authorizeWelcomeBonus(request: AuthorizeBonusRequest): Promise<FiadoApiResponse<TransactionProcessorResponse>>;
28
28
  }
29
29
  export {};
@@ -1,5 +1,5 @@
1
1
  type FiadoApiResponse<T> = import("@fiado/type-kit/bin/apiResponse/dtos/FiadoApiResponse.js").default<T>;
2
- import { AuthorizeCollectorOrderRequest, AuthorizeCollectorOrderResponse, AuthorizeCollectorTransactionRequest, AuthorizeCreditCardPurchaseRequest, AuthorizeSpeiBankTransferNcRequest, AuthorizeSpeiMexBankTransferInRequest, AuthorizeRenewalPreAuthRequest, AuthorizeRenewalPreAuthResponse, AuthorizeWelcomeBonusRequest, TransactionProcessorResponse } from "@fiado/type-kit/bin/transactionProcessor/index.js";
2
+ import { AuthorizeCollectorOrderRequest, AuthorizeCollectorOrderResponse, AuthorizeCollectorTransactionRequest, AuthorizeCreditCardPurchaseRequest, AuthorizeSpeiBankTransferNcRequest, AuthorizeSpeiMexBankTransferInRequest, AuthorizeRenewalPreAuthRequest, AuthorizeRenewalPreAuthResponse, AuthorizeBonusRequest, TransactionProcessorResponse } from "@fiado/type-kit/bin/transactionProcessor/index.js";
3
3
  import { ApiGatewayResponse } from "@fiado/gateway-adapter";
4
4
  import { ValidateWalletFundingRequest, ValidateWalletFundingResponse, CreditWalletFundingRequest, CreditWalletFundingResponse, ReverseWalletFundingRequest, ReverseWalletFundingResponse, GetWalletFundingLimitRequest, GetWalletFundingLimitResponse } from "@fiado/type-kit/bin/walletFunding/index.js";
5
5
  import { RemittanceCreditBackRequest, RemittanceCreditBackResponse } from "@fiado/type-kit/bin/remittance/index.js";
@@ -59,6 +59,6 @@ export interface ITransactionProcessorApi {
59
59
  * Backend: POST /transaction-processor/bonus/authorize (privado VPC).
60
60
  * Idempotente por `idempotencyKey` (el `bonusId` del asiento).
61
61
  */
62
- authorizeWelcomeBonus(request: AuthorizeWelcomeBonusRequest): Promise<FiadoApiResponse<TransactionProcessorResponse>>;
62
+ authorizeWelcomeBonus(request: AuthorizeBonusRequest): Promise<FiadoApiResponse<TransactionProcessorResponse>>;
63
63
  }
64
64
  export {};
@@ -0,0 +1,2 @@
1
+ export * from './queue/interfaces/IWebhookBusinessPublisher.js';
2
+ export * from './queue/WebhookBusinessPublisher.js';
@@ -0,0 +1,2 @@
1
+ export * from './queue/interfaces/IWebhookBusinessPublisher.js';
2
+ export * from './queue/WebhookBusinessPublisher.js';
@@ -0,0 +1,6 @@
1
+ import { WebhookOutboundMessage } from "@fiado/type-kit/bin/webhookBusiness/index.js";
2
+ import { IWebhookBusinessPublisher } from "./interfaces/IWebhookBusinessPublisher.js";
3
+ export declare class WebhookBusinessPublisher implements IWebhookBusinessPublisher {
4
+ private readonly WEBHOOK_BUSINESS_QUEUE_URL;
5
+ publish(message: WebhookOutboundMessage): Promise<void>;
6
+ }
@@ -0,0 +1,32 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ import { SendMessageCommand, SQSClient } from "@aws-sdk/client-sqs";
8
+ import { injectable } from "inversify";
9
+ let WebhookBusinessPublisher = class WebhookBusinessPublisher {
10
+ WEBHOOK_BUSINESS_QUEUE_URL = process.env.WEBHOOK_BUSINESS_QUEUE_URL;
11
+ async publish(message) {
12
+ try {
13
+ const client = new SQSClient();
14
+ // Cola ESTÁNDAR (no FIFO): sin MessageGroupId/MessageDeduplicationId ni orden garantizado.
15
+ // El consumidor deduplica por `eventId` del sobre y reordena por `occurredAt`.
16
+ const sendMessageRequest = {
17
+ QueueUrl: this.WEBHOOK_BUSINESS_QUEUE_URL,
18
+ MessageBody: JSON.stringify(message),
19
+ };
20
+ const command = new SendMessageCommand(sendMessageRequest);
21
+ await client.send(command);
22
+ }
23
+ catch (error) {
24
+ const errorMessage = error instanceof Error ? error.message : String(error);
25
+ throw new Error(`Error publishing message to WebhookBusinessQueue: ${errorMessage}`);
26
+ }
27
+ }
28
+ };
29
+ WebhookBusinessPublisher = __decorate([
30
+ injectable()
31
+ ], WebhookBusinessPublisher);
32
+ export { WebhookBusinessPublisher };
@@ -0,0 +1,20 @@
1
+ import { WebhookOutboundMessage } from "@fiado/type-kit/bin/webhookBusiness/index.js";
2
+ /**
3
+ * Publisher de Fiado hacia `WebhookBusinessQueue`, que consume `webhook-business` para entregarle
4
+ * el evento a los suscriptores externos que tengan el tag `wh:*` del `directoryId`.
5
+ *
6
+ * Lo produce cualquier lambda de Fiado que tenga algo que avisarle a un externo (la emisión de la
7
+ * cuenta PCF, el dictamen de cumplimiento). Vive aquí —y no como SDK crudo en cada lambda— porque la
8
+ * cola es COMPARTIDA cross-lambda: el estándar Fiado publica a una cola compartida vía Publisher de
9
+ * `@fiado/api-invoker` (mismo patrón que `IRetailKycInboundPublisher`).
10
+ *
11
+ * El productor necesita la env var `WEBHOOK_BUSINESS_QUEUE_URL` en su `template.yml` (la misma que
12
+ * ya declara `webhook-business` del lado consumidor) y permiso `sqs:SendMessage` sobre la cola.
13
+ *
14
+ * El tipo del mensaje es la unión cerrada `WebhookOutboundMessage` del `@fiado/type-kit`: agregar un
15
+ * evento obliga a tocar también el router del consumidor, que es la conversación que tiene que
16
+ * pasar antes de meter un mensaje nuevo en una cola compartida.
17
+ */
18
+ export interface IWebhookBusinessPublisher {
19
+ publish(message: WebhookOutboundMessage): Promise<void>;
20
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fiado/api-invoker",
3
- "version": "5.38.0",
3
+ "version": "5.40.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.329.0",
37
+ "@fiado/type-kit": "^3.360.0",
38
38
  "dotenv": "^16.4.7"
39
39
  },
40
40
  "peerDependencies": {
@@ -192,6 +192,8 @@ import { IResearchSheetsApi, ResearchSheetsApi } from "./research-sheets/index.j
192
192
  import { IAuthBusinessApi, AuthBusinessApi } from "./auth-business/index.js";
193
193
  // KYC verifications business — lectura confirmatoria server-side del KYC por directoryId (SureKeep F2)
194
194
  import { IKycVerificationsBusinessApi, KycVerificationsBusinessApi } from "./kyc/index.js";
195
+ // Webhook business — publisher de la cola compartida de entrega de eventos a suscriptores externos
196
+ import { IWebhookBusinessPublisher, WebhookBusinessPublisher } from "./webhook-business/index.js";
195
197
  import { ICentralPaymentsWebhookApi, CentralPaymentsWebhookApi } from "./central-payments-webhook/index.js";
196
198
  // Collection engine — cliente del motor de cobro central (collect/registerCollectible/deregister)
197
199
  import { ICollectionEngineApi } from "./collection-engine/index.js";
@@ -366,6 +368,10 @@ export const apiInvokerBindings = new ContainerModule(({ bind }) => {
366
368
  // KYC verifications business — lectura confirmatoria server-side del KYC por directoryId.
367
369
  // La consume el wizard de SureKeep F2 tras el aviso async del stream de `KycVerifications`.
368
370
  bind<IKycVerificationsBusinessApi>("IKycVerificationsBusinessApi").to(KycVerificationsBusinessApi);
371
+
372
+ // Webhook business — lo produce cualquier lambda que tenga algo que avisarle a un externo.
373
+ // El productor necesita WEBHOOK_BUSINESS_QUEUE_URL y sqs:SendMessage sobre la cola.
374
+ bind<IWebhookBusinessPublisher>("IWebhookBusinessPublisher").to(WebhookBusinessPublisher);
369
375
  bind<ICentralPaymentsWebhookApi>("ICentralPaymentsWebhookApi").to(CentralPaymentsWebhookApi);
370
376
 
371
377
  // Collection engine — motor de cobro central multitenant (collect/registerCollectible/deregister)
package/src/index.ts CHANGED
@@ -85,6 +85,7 @@ export * from "./datalakeQuery/index.js";
85
85
  export * from "./retailCustomer/index.js";
86
86
  export * from "./retailCatalog/index.js";
87
87
  export * from "./retailCards/index.js";
88
+ export * from "./retailNotifications/index.js";
88
89
  export * from "./shortlink/index.js";
89
90
  export * from "./loanConfig/index.js";
90
91
  export * from "./loanOfferings/index.js";
@@ -111,3 +112,4 @@ export * from "./central-payments-webhook/index.js";
111
112
  export * from "./collection-engine/index.js";
112
113
  export * from "./domain-callback/index.js";
113
114
  export * from "./datacultrConnector/index.js";
115
+ export * from "./webhook-business/index.js";
@@ -1,4 +1,5 @@
1
1
  import { StandardResponse } from "@fiado/gateway-adapter";
2
+ import type { MdmLockModeEnum, MdmProviderEnum } from "@fiado/type-kit/bin/retailCatalog/index.js";
2
3
 
3
4
  /**
4
5
  * Contrato del publisher HTTP del lambda `retail-catalog-business` (SureKeep Fase 1, pista Retail)
@@ -50,6 +51,15 @@ export interface StoreCatalogItem {
50
51
  tier: string;
51
52
  /** Financieras alternativas habilitadas (derivación externa, paso 04 del wizard). */
52
53
  eligibleFinanciers: string[];
54
+ // Política MDM del producto (2026-08-25, MINOR aditivo). El detalle de la venta la muestra y el
55
+ // destino ya trae el producto cargado. Es la POLÍTICA del producto, no el estado del candado del
56
+ // dispositivo (ese vive en `loan-credit`).
57
+ /** `true` si el producto se vende con MDM. */
58
+ mdmEnabled: boolean;
59
+ /** Proveedor MDM; `null` si el producto no lleva MDM. */
60
+ mdmProvider: MdmProviderEnum | null;
61
+ /** Modo de bloqueo pactado; `null` si el producto no lleva MDM. */
62
+ mdmLockMode: MdmLockModeEnum | null;
53
63
  }
54
64
 
55
65
  /** Resultado de la reserva (espeja `ReserveInventoryResponse` del destino). */
@@ -0,0 +1,24 @@
1
+ import { inject, injectable } from "inversify";
2
+ import type { IHttpRequest } from "@fiado/http-client";
3
+ import { StandardResponse } from "@fiado/gateway-adapter";
4
+ import { IRetailNotificationsWebsocketApi } from "./interfaces/IRetailNotificationsWebsocketApi.js";
5
+ import { NotifySubjectRequest } from "./dtos/NotifySubjectRequest.js";
6
+ import { NotifySubjectResult } from "./dtos/NotifySubjectResult.js";
7
+
8
+ /**
9
+ * Publisher HTTP del lambda `retail-notifications-websocket` (SureKeep Fase 2) para su privado M2M de
10
+ * avisos al vendedor. Contrato y semántica completos → `IRetailNotificationsWebsocketApi`.
11
+ *
12
+ * Env var requerida en el consumer: `RETAIL_NOTIFICATIONS_WEBSOCKET_URL`.
13
+ */
14
+ @injectable()
15
+ export default class RetailNotificationsWebsocketApi implements IRetailNotificationsWebsocketApi {
16
+ // El buildspec publica la URL en SSM con "/" final — se normaliza para no armar "//private".
17
+ private readonly baseUrl = (process.env.RETAIL_NOTIFICATIONS_WEBSOCKET_URL || "").replace(/\/+$/, "");
18
+
19
+ constructor(@inject("IHttpRequest") private httpRequest: IHttpRequest) {}
20
+
21
+ async notifySubject(payload: NotifySubjectRequest): Promise<StandardResponse<NotifySubjectResult>> {
22
+ return await this.httpRequest.post(`${this.baseUrl}/private/notifications`, payload);
23
+ }
24
+ }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Catálogo de avisos que el canal WebSocket de SureKeep sabe empujar. Espeja el
3
+ * `NotificationTypeEnum` de `retail-notifications-websocket`, que valida el cuerpo con `@IsEnum`: un tipo
4
+ * fuera del catálogo se rechaza con 400 `INVALID_PAYLOAD`.
5
+ */
6
+ export type RetailNotificationType = 'SALE_UPDATED';
7
+
8
+ /**
9
+ * Lo que viaja por el privado M2M: a qué vendedor avisarle, de qué y sobre cuál de sus ventas.
10
+ * NO lleva ningún dato del comprador — el navegador recibe solo el tipo y el id de la venta, y el
11
+ * detalle se lo pide después al wizard.
12
+ */
13
+ export interface NotifySubjectRequest {
14
+ /** `sub` de Cognito del vendedor destinatario. Máx. 128 caracteres. */
15
+ subjectId: string;
16
+ type: RetailNotificationType;
17
+ /** Id de la venta, para que el vendedor sepa de cuál de las suyas se trata. Máx. 128 caracteres. */
18
+ sessionId: string;
19
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Resultado de una entrega. `delivered: 0` significa que el vendedor NO tiene ningún socket abierto
3
+ * —tiene la pestaña cerrada— y NO es un error: el llamador no debe reintentar ni alarmar.
4
+ */
5
+ export interface NotifySubjectResult {
6
+ /** Conexiones a las que llegó el aviso. */
7
+ delivered: number;
8
+ /** Filas de conexiones muertas que el destino limpió de paso. */
9
+ staleRemoved: number;
10
+ /** Conexiones vivas a las que el envío falló. */
11
+ failed: number;
12
+ }
@@ -0,0 +1,44 @@
1
+ import { StandardResponse } from "@fiado/gateway-adapter";
2
+ import { NotifySubjectRequest } from "../dtos/NotifySubjectRequest.js";
3
+ import { NotifySubjectResult } from "../dtos/NotifySubjectResult.js";
4
+
5
+ /**
6
+ * Contrato del publisher HTTP del lambda `retail-notifications-websocket` (SureKeep Fase 2): el canal por
7
+ * el que el portal le avisa al VENDEDOR que su venta cambió de estado.
8
+ *
9
+ * Consumidor previsto:
10
+ * - `retail-wizard-business` → al asentar el desenlace de una venta (stream de la cuenta PCF).
11
+ *
12
+ * ─────────────────────────────────────────────────────────────────────────────
13
+ * SIN TENANT — a diferencia de los clients retail, este NO manda `?tenantId=`.
14
+ * ─────────────────────────────────────────────────────────────────────────────
15
+ * El destino resuelve el silo de SU PROPIO entorno (`resolveTenantId`), nunca del request: el
16
+ * `tenantId` es el hash del GSI de ruteo, así que el aislamiento no puede depender de quien llama.
17
+ *
18
+ * ⚠️ Retorno `StandardResponse<T>` — el consumer lee `result.data`, NO `result.body`.
19
+ *
20
+ * Los DTOs viven acá y no en `@fiado/type-kit` porque el destino tampoco depende de type-kit: sus
21
+ * DTOs son locales. Si algún día el contrato se comparte con un tercer repo, mudarlos a type-kit.
22
+ *
23
+ * Env var requerida en el consumer: `RETAIL_NOTIFICATIONS_WEBSOCKET_URL`.
24
+ * El template.yml del consumer la setea con:
25
+ *
26
+ * RETAIL_NOTIFICATIONS_WEBSOCKET_URL: '{{resolve:ssm:retail-notifications-websocket}}'
27
+ *
28
+ * Convención CLAUDE.md global: SSM key = nombre del lambda owner de la URL.
29
+ */
30
+ export interface IRetailNotificationsWebsocketApi {
31
+ /**
32
+ * POST `/private/notifications` — empuja un aviso al vendedor por su WebSocket abierto.
33
+ *
34
+ * Semántica de la respuesta, que decide qué hacer ante cada desenlace:
35
+ * · 200 con `delivered: 0` → el vendedor tiene la pestaña cerrada. NO es error, NO reintentar.
36
+ * · 400 `INVALID_PAYLOAD` → el cuerpo está mal (falta un campo, tipo fuera del catálogo).
37
+ * NO reintentar: reintentarlo igual da lo mismo.
38
+ * · 500 `INTERNAL_ERROR` → falló el canal. El request estaba bien y se PUEDE reintentar.
39
+ *
40
+ * Avisar es un efecto de borde: si falla, el aviso se pierde y lo cubre el barrido del wizard.
41
+ * Nunca debe bloquear ni deshacer la operación que lo disparó.
42
+ */
43
+ notifySubject(payload: NotifySubjectRequest): Promise<StandardResponse<NotifySubjectResult>>;
44
+ }
@@ -0,0 +1,4 @@
1
+ export * from "./api/dtos/NotifySubjectRequest.js";
2
+ export * from "./api/dtos/NotifySubjectResult.js";
3
+ export * from "./api/interfaces/IRetailNotificationsWebsocketApi.js";
4
+ export { default as RetailNotificationsWebsocketApi } from "./api/RetailNotificationsWebsocketApi.js";
@@ -9,7 +9,7 @@ import {
9
9
  AuthorizeSpeiMexBankTransferInRequest,
10
10
  AuthorizeRenewalPreAuthRequest,
11
11
  AuthorizeRenewalPreAuthResponse,
12
- AuthorizeWelcomeBonusRequest,
12
+ AuthorizeBonusRequest,
13
13
  TransactionProcessorResponse
14
14
  } from "@fiado/type-kit/bin/transactionProcessor/index.js";
15
15
  import { ApiGatewayResponse } from "@fiado/gateway-adapter";
@@ -117,7 +117,7 @@ export default class TransactionProcessorApi implements ITransactionProcessorApi
117
117
  }
118
118
 
119
119
  async authorizeWelcomeBonus(
120
- request: AuthorizeWelcomeBonusRequest,
120
+ request: AuthorizeBonusRequest,
121
121
  ): Promise<FiadoApiResponse<TransactionProcessorResponse>> {
122
122
  const url = `${this.baseUrl}bonus/authorize`;
123
123
  return await this.httpRequest.post(url, request);
@@ -8,7 +8,7 @@ import {
8
8
  AuthorizeSpeiMexBankTransferInRequest,
9
9
  AuthorizeRenewalPreAuthRequest,
10
10
  AuthorizeRenewalPreAuthResponse,
11
- AuthorizeWelcomeBonusRequest,
11
+ AuthorizeBonusRequest,
12
12
  TransactionProcessorResponse
13
13
  } from "@fiado/type-kit/bin/transactionProcessor/index.js";
14
14
  import { ApiGatewayResponse } from "@fiado/gateway-adapter";
@@ -110,6 +110,6 @@ export interface ITransactionProcessorApi {
110
110
  * Idempotente por `idempotencyKey` (el `bonusId` del asiento).
111
111
  */
112
112
  authorizeWelcomeBonus(
113
- request: AuthorizeWelcomeBonusRequest,
113
+ request: AuthorizeBonusRequest,
114
114
  ): Promise<FiadoApiResponse<TransactionProcessorResponse>>;
115
115
  }
@@ -0,0 +1,2 @@
1
+ export * from './queue/interfaces/IWebhookBusinessPublisher.js';
2
+ export * from './queue/WebhookBusinessPublisher.js';
@@ -0,0 +1,26 @@
1
+ import { SendMessageCommand, SendMessageRequest, SQSClient } from "@aws-sdk/client-sqs";
2
+ import { injectable } from "inversify";
3
+ import { WebhookOutboundMessage } from "@fiado/type-kit/bin/webhookBusiness/index.js";
4
+ import { IWebhookBusinessPublisher } from "./interfaces/IWebhookBusinessPublisher.js";
5
+
6
+ @injectable()
7
+ export class WebhookBusinessPublisher implements IWebhookBusinessPublisher {
8
+ private readonly WEBHOOK_BUSINESS_QUEUE_URL = process.env.WEBHOOK_BUSINESS_QUEUE_URL;
9
+
10
+ async publish(message: WebhookOutboundMessage): Promise<void> {
11
+ try {
12
+ const client: SQSClient = new SQSClient();
13
+ // Cola ESTÁNDAR (no FIFO): sin MessageGroupId/MessageDeduplicationId ni orden garantizado.
14
+ // El consumidor deduplica por `eventId` del sobre y reordena por `occurredAt`.
15
+ const sendMessageRequest: SendMessageRequest = {
16
+ QueueUrl: this.WEBHOOK_BUSINESS_QUEUE_URL,
17
+ MessageBody: JSON.stringify(message),
18
+ };
19
+ const command: SendMessageCommand = new SendMessageCommand(sendMessageRequest);
20
+ await client.send(command);
21
+ } catch (error) {
22
+ const errorMessage = error instanceof Error ? error.message : String(error);
23
+ throw new Error(`Error publishing message to WebhookBusinessQueue: ${errorMessage}`);
24
+ }
25
+ }
26
+ }
@@ -0,0 +1,21 @@
1
+ import { WebhookOutboundMessage } from "@fiado/type-kit/bin/webhookBusiness/index.js";
2
+
3
+ /**
4
+ * Publisher de Fiado hacia `WebhookBusinessQueue`, que consume `webhook-business` para entregarle
5
+ * el evento a los suscriptores externos que tengan el tag `wh:*` del `directoryId`.
6
+ *
7
+ * Lo produce cualquier lambda de Fiado que tenga algo que avisarle a un externo (la emisión de la
8
+ * cuenta PCF, el dictamen de cumplimiento). Vive aquí —y no como SDK crudo en cada lambda— porque la
9
+ * cola es COMPARTIDA cross-lambda: el estándar Fiado publica a una cola compartida vía Publisher de
10
+ * `@fiado/api-invoker` (mismo patrón que `IRetailKycInboundPublisher`).
11
+ *
12
+ * El productor necesita la env var `WEBHOOK_BUSINESS_QUEUE_URL` en su `template.yml` (la misma que
13
+ * ya declara `webhook-business` del lado consumidor) y permiso `sqs:SendMessage` sobre la cola.
14
+ *
15
+ * El tipo del mensaje es la unión cerrada `WebhookOutboundMessage` del `@fiado/type-kit`: agregar un
16
+ * evento obliga a tocar también el router del consumidor, que es la conversación que tiene que
17
+ * pasar antes de meter un mensaje nuevo en una cola compartida.
18
+ */
19
+ export interface IWebhookBusinessPublisher {
20
+ publish(message: WebhookOutboundMessage): Promise<void>;
21
+ }