@fiado/type-kit 3.251.0 → 3.253.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.
- package/_test_/unit/biometrics/BiometricVerificationChangedV1.test.ts +96 -0
- package/_test_/unit/biometrics/CreateBiometricVerificationRequest.test.ts +109 -0
- package/bin/biometrics/dtos/BiometricDelivery.d.ts +65 -0
- package/bin/biometrics/dtos/BiometricDelivery.js +89 -0
- package/bin/biometrics/dtos/BiometricSubject.d.ts +43 -0
- package/bin/biometrics/dtos/BiometricSubject.js +63 -0
- package/bin/biometrics/dtos/requests/CreateBiometricVerificationRequest.d.ts +73 -0
- package/bin/biometrics/dtos/requests/CreateBiometricVerificationRequest.js +81 -0
- package/bin/biometrics/dtos/responses/BiometricVerificationResponse.d.ts +87 -0
- package/bin/biometrics/dtos/responses/BiometricVerificationResponse.js +131 -0
- package/bin/biometrics/enums/BiometricDeliveryModeEnum.d.ts +29 -0
- package/bin/biometrics/enums/BiometricDeliveryModeEnum.js +33 -0
- package/bin/biometrics/enums/BiometricEventTypeEnum.d.ts +28 -0
- package/bin/biometrics/enums/BiometricEventTypeEnum.js +32 -0
- package/bin/biometrics/enums/BiometricProviderEnum.d.ts +25 -0
- package/bin/biometrics/enums/BiometricProviderEnum.js +29 -0
- package/bin/biometrics/enums/BiometricResultEnum.d.ts +25 -0
- package/bin/biometrics/enums/BiometricResultEnum.js +29 -0
- package/bin/biometrics/enums/BiometricTypeEnum.d.ts +20 -0
- package/bin/biometrics/enums/BiometricTypeEnum.js +24 -0
- package/bin/biometrics/enums/BiometricVerificationStatusEnum.d.ts +33 -0
- package/bin/biometrics/enums/BiometricVerificationStatusEnum.js +37 -0
- package/bin/biometrics/events/BiometricVerificationChangedV1.d.ts +80 -0
- package/bin/biometrics/events/BiometricVerificationChangedV1.js +98 -0
- package/bin/biometrics/index.d.ts +11 -0
- package/bin/biometrics/index.js +35 -0
- package/bin/index.d.ts +1 -0
- package/bin/index.js +4 -3
- package/bin/zendeskMessaging/dtos/SendProactiveMessageRequest.d.ts +2 -0
- package/bin/zendeskMessaging/dtos/SendProactiveMessageRequest.js +5 -0
- package/package.json +1 -1
- package/src/biometrics/dtos/BiometricDelivery.ts +81 -0
- package/src/biometrics/dtos/BiometricSubject.ts +54 -0
- package/src/biometrics/dtos/requests/CreateBiometricVerificationRequest.ts +101 -0
- package/src/biometrics/dtos/responses/BiometricVerificationResponse.ts +133 -0
- package/src/biometrics/enums/BiometricDeliveryModeEnum.ts +31 -0
- package/src/biometrics/enums/BiometricEventTypeEnum.ts +31 -0
- package/src/biometrics/enums/BiometricProviderEnum.ts +28 -0
- package/src/biometrics/enums/BiometricResultEnum.ts +28 -0
- package/src/biometrics/enums/BiometricTypeEnum.ts +23 -0
- package/src/biometrics/enums/BiometricVerificationStatusEnum.ts +37 -0
- package/src/biometrics/events/BiometricVerificationChangedV1.ts +114 -0
- package/src/biometrics/index.ts +24 -0
- package/src/index.ts +1 -0
- package/src/zendeskMessaging/dtos/SendProactiveMessageRequest.ts +5 -0
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { BiometricEventTypeEnum } from '../enums/BiometricEventTypeEnum';
|
|
2
|
+
import { BiometricProviderEnum } from '../enums/BiometricProviderEnum';
|
|
3
|
+
import { BiometricResultEnum } from '../enums/BiometricResultEnum';
|
|
4
|
+
/**
|
|
5
|
+
* Lo que le pasó a una verificación biométrica, según el proveedor.
|
|
6
|
+
*
|
|
7
|
+
* **El recorrido:** Metamap → `kyc-metamap-webhook` (que valida la firma HMAC y traduce) →
|
|
8
|
+
* `BiometricsInboundQueue` → el consumer de `biometrics-business`, que aplica la transición.
|
|
9
|
+
*
|
|
10
|
+
* **Por qué el webhook no llama directo:** ese lambda tiene una disciplina explícita — *un fallo de
|
|
11
|
+
* publish NUNCA puede tumbar el webhook*, porque si lanza, Metamap reintenta el webhook completo y
|
|
12
|
+
* se duplican las actualizaciones de identidad. Con esa regla, una llamada síncrona que falle se
|
|
13
|
+
* traga el evento en silencio y la verificación queda `IN_PROGRESS` para siempre, con el caller
|
|
14
|
+
* poleando al vacío y sin alarma. La cola da durabilidad, DLQ y alarma sin tocar esa disciplina.
|
|
15
|
+
*
|
|
16
|
+
* 🔴 **Cola propia, NO `RetailKycInboundQueue`.** SQS no hace broadcast: dos lambdas sobre la misma
|
|
17
|
+
* cola son consumidores en competencia y cada mensaje le llega a UNO solo. Reusar la de SureKeep
|
|
18
|
+
* partiría sus KYC a la mitad, en producción, al azar y sin un solo error en los logs.
|
|
19
|
+
*
|
|
20
|
+
* 🔴 **Sin PII.** Solo identificadores operacionales y el veredicto.
|
|
21
|
+
*
|
|
22
|
+
* `biometrics-business` — Entrega 1.
|
|
23
|
+
*/
|
|
24
|
+
export declare class BiometricVerificationChangedV1 {
|
|
25
|
+
/**
|
|
26
|
+
* Discriminador del tipo de mensaje. Fijo, para que el consumer rutee por él y no por adivinar
|
|
27
|
+
* la forma del payload.
|
|
28
|
+
*/
|
|
29
|
+
schema?: string;
|
|
30
|
+
/**
|
|
31
|
+
* 🔴 **Identificador ÚNICO de este evento. La clave de deduplicación.**
|
|
32
|
+
*
|
|
33
|
+
* SQS es *at-least-once*: los duplicados son la norma, no la excepción. Y **el candado temporal
|
|
34
|
+
* por `occurredAt` NO alcanza para esto**: dos entregas del mismo evento traen exactamente el
|
|
35
|
+
* mismo `occurredAt`, así que una regla "descartar si es más viejo" las deja pasar a las dos.
|
|
36
|
+
*
|
|
37
|
+
* El consumer guarda los `eventId` ya aplicados y descarta los repetidos.
|
|
38
|
+
*
|
|
39
|
+
* ⚠️ **No deduplicar por el `MessageId` de SQS**: cambia en cada reintento de entrega, así que
|
|
40
|
+
* no deduplica nada. Es el error clásico y por eso queda escrito acá y no en la cabeza de cada
|
|
41
|
+
* consumidor.
|
|
42
|
+
*/
|
|
43
|
+
eventId: string;
|
|
44
|
+
/**
|
|
45
|
+
* A qué verificación se refiere. Sale del `metadata` que el proveedor ecoa tal cual → el
|
|
46
|
+
* consumer hace un `get` por PK, sin búsqueda.
|
|
47
|
+
*/
|
|
48
|
+
biometricVerificationId: string;
|
|
49
|
+
/** Quién ejecutó el biométrico. */
|
|
50
|
+
provider: BiometricProviderEnum;
|
|
51
|
+
/** El id del lado del proveedor (el `verificationId` de Metamap). Para soporte y reconciliación. */
|
|
52
|
+
providerRef?: string;
|
|
53
|
+
/** Qué pasó. Es lo que decide la transición de estado. */
|
|
54
|
+
eventType: BiometricEventTypeEnum;
|
|
55
|
+
/**
|
|
56
|
+
* El veredicto, cuando el evento lo trae. Llega en el `STEP_COMPLETED`, **antes** del cierre —
|
|
57
|
+
* por eso el consumer puede poblar `result` con `status` todavía en `IN_PROGRESS`.
|
|
58
|
+
*
|
|
59
|
+
* Ausente en `VERIFICATION_STARTED` y `VERIFICATION_EXPIRED`.
|
|
60
|
+
*/
|
|
61
|
+
result?: BiometricResultEnum;
|
|
62
|
+
/** Similitud reportada, 0–100, cuando el proveedor la expone. */
|
|
63
|
+
score?: number;
|
|
64
|
+
/**
|
|
65
|
+
* Cuándo ocurrió, según el proveedor. Obligatorio: es el candado de orden.
|
|
66
|
+
*
|
|
67
|
+
* ⚠️ **UTC estricto, con `Z` y sin offset** — por eso el `@Matches` además del `@IsISO8601()`.
|
|
68
|
+
* `@IsISO8601()` solo acepta `2026-07-26` (sin hora), `2026-W30-1` y, lo que hace daño de
|
|
69
|
+
* verdad, `2026-07-26T15:05:00+05:00`.
|
|
70
|
+
*
|
|
71
|
+
* 🔴 **El candado del consumer es LÉXICO.** Ese `+05:00` son las 10:05 UTC, pero comparado como
|
|
72
|
+
* texto es MAYOR que un `T10:06:00Z` posterior: el consumer lo leería como más nuevo, descartaría
|
|
73
|
+
* el evento real, y la transición no se aplicaría nunca — sin un solo error.
|
|
74
|
+
*
|
|
75
|
+
* Normalizar a UTC es trabajo del traductor del proveedor (`kyc-metamap-webhook`). Este candado
|
|
76
|
+
* existe para que si algún día deja de hacerlo, el mensaje caiga al DLQ en el boundary en vez de
|
|
77
|
+
* corromper el orden en silencio.
|
|
78
|
+
*/
|
|
79
|
+
occurredAt: string;
|
|
80
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
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;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.BiometricVerificationChangedV1 = void 0;
|
|
13
|
+
const class_transformer_1 = require("class-transformer");
|
|
14
|
+
const class_validator_1 = require("class-validator");
|
|
15
|
+
const BiometricEventTypeEnum_1 = require("../enums/BiometricEventTypeEnum");
|
|
16
|
+
const BiometricProviderEnum_1 = require("../enums/BiometricProviderEnum");
|
|
17
|
+
const BiometricResultEnum_1 = require("../enums/BiometricResultEnum");
|
|
18
|
+
/**
|
|
19
|
+
* Lo que le pasó a una verificación biométrica, según el proveedor.
|
|
20
|
+
*
|
|
21
|
+
* **El recorrido:** Metamap → `kyc-metamap-webhook` (que valida la firma HMAC y traduce) →
|
|
22
|
+
* `BiometricsInboundQueue` → el consumer de `biometrics-business`, que aplica la transición.
|
|
23
|
+
*
|
|
24
|
+
* **Por qué el webhook no llama directo:** ese lambda tiene una disciplina explícita — *un fallo de
|
|
25
|
+
* publish NUNCA puede tumbar el webhook*, porque si lanza, Metamap reintenta el webhook completo y
|
|
26
|
+
* se duplican las actualizaciones de identidad. Con esa regla, una llamada síncrona que falle se
|
|
27
|
+
* traga el evento en silencio y la verificación queda `IN_PROGRESS` para siempre, con el caller
|
|
28
|
+
* poleando al vacío y sin alarma. La cola da durabilidad, DLQ y alarma sin tocar esa disciplina.
|
|
29
|
+
*
|
|
30
|
+
* 🔴 **Cola propia, NO `RetailKycInboundQueue`.** SQS no hace broadcast: dos lambdas sobre la misma
|
|
31
|
+
* cola son consumidores en competencia y cada mensaje le llega a UNO solo. Reusar la de SureKeep
|
|
32
|
+
* partiría sus KYC a la mitad, en producción, al azar y sin un solo error en los logs.
|
|
33
|
+
*
|
|
34
|
+
* 🔴 **Sin PII.** Solo identificadores operacionales y el veredicto.
|
|
35
|
+
*
|
|
36
|
+
* `biometrics-business` — Entrega 1.
|
|
37
|
+
*/
|
|
38
|
+
class BiometricVerificationChangedV1 {
|
|
39
|
+
}
|
|
40
|
+
exports.BiometricVerificationChangedV1 = BiometricVerificationChangedV1;
|
|
41
|
+
__decorate([
|
|
42
|
+
(0, class_transformer_1.Expose)(),
|
|
43
|
+
(0, class_validator_1.IsOptional)(),
|
|
44
|
+
(0, class_validator_1.IsIn)(['BiometricVerificationChangedV1']),
|
|
45
|
+
__metadata("design:type", String)
|
|
46
|
+
], BiometricVerificationChangedV1.prototype, "schema", void 0);
|
|
47
|
+
__decorate([
|
|
48
|
+
(0, class_transformer_1.Expose)(),
|
|
49
|
+
(0, class_validator_1.IsString)(),
|
|
50
|
+
(0, class_validator_1.IsNotEmpty)(),
|
|
51
|
+
__metadata("design:type", String)
|
|
52
|
+
], BiometricVerificationChangedV1.prototype, "eventId", void 0);
|
|
53
|
+
__decorate([
|
|
54
|
+
(0, class_transformer_1.Expose)(),
|
|
55
|
+
(0, class_validator_1.IsString)(),
|
|
56
|
+
(0, class_validator_1.IsNotEmpty)(),
|
|
57
|
+
__metadata("design:type", String)
|
|
58
|
+
], BiometricVerificationChangedV1.prototype, "biometricVerificationId", void 0);
|
|
59
|
+
__decorate([
|
|
60
|
+
(0, class_transformer_1.Expose)(),
|
|
61
|
+
(0, class_validator_1.IsEnum)(BiometricProviderEnum_1.BiometricProviderEnum),
|
|
62
|
+
__metadata("design:type", String)
|
|
63
|
+
], BiometricVerificationChangedV1.prototype, "provider", void 0);
|
|
64
|
+
__decorate([
|
|
65
|
+
(0, class_transformer_1.Expose)(),
|
|
66
|
+
(0, class_validator_1.IsOptional)(),
|
|
67
|
+
(0, class_validator_1.IsString)(),
|
|
68
|
+
(0, class_validator_1.IsNotEmpty)(),
|
|
69
|
+
__metadata("design:type", String)
|
|
70
|
+
], BiometricVerificationChangedV1.prototype, "providerRef", void 0);
|
|
71
|
+
__decorate([
|
|
72
|
+
(0, class_transformer_1.Expose)(),
|
|
73
|
+
(0, class_validator_1.IsEnum)(BiometricEventTypeEnum_1.BiometricEventTypeEnum),
|
|
74
|
+
__metadata("design:type", String)
|
|
75
|
+
], BiometricVerificationChangedV1.prototype, "eventType", void 0);
|
|
76
|
+
__decorate([
|
|
77
|
+
(0, class_transformer_1.Expose)(),
|
|
78
|
+
(0, class_validator_1.IsOptional)(),
|
|
79
|
+
(0, class_validator_1.IsEnum)(BiometricResultEnum_1.BiometricResultEnum),
|
|
80
|
+
__metadata("design:type", String)
|
|
81
|
+
], BiometricVerificationChangedV1.prototype, "result", void 0);
|
|
82
|
+
__decorate([
|
|
83
|
+
(0, class_transformer_1.Expose)(),
|
|
84
|
+
(0, class_validator_1.IsOptional)(),
|
|
85
|
+
(0, class_validator_1.IsNumber)(),
|
|
86
|
+
(0, class_validator_1.Min)(0),
|
|
87
|
+
(0, class_validator_1.Max)(100),
|
|
88
|
+
__metadata("design:type", Number)
|
|
89
|
+
], BiometricVerificationChangedV1.prototype, "score", void 0);
|
|
90
|
+
__decorate([
|
|
91
|
+
(0, class_transformer_1.Expose)(),
|
|
92
|
+
(0, class_validator_1.IsString)(),
|
|
93
|
+
(0, class_validator_1.IsISO8601)({ strict: true }),
|
|
94
|
+
(0, class_validator_1.Matches)(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?Z$/, {
|
|
95
|
+
message: 'occurredAt debe ser ISO-8601 en UTC con Z (ej. 2026-07-26T10:05:00.000Z): un offset rompe el orden lexico',
|
|
96
|
+
}),
|
|
97
|
+
__metadata("design:type", String)
|
|
98
|
+
], BiometricVerificationChangedV1.prototype, "occurredAt", void 0);
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export * from './enums/BiometricTypeEnum';
|
|
2
|
+
export * from './enums/BiometricProviderEnum';
|
|
3
|
+
export * from './enums/BiometricDeliveryModeEnum';
|
|
4
|
+
export * from './enums/BiometricVerificationStatusEnum';
|
|
5
|
+
export * from './enums/BiometricResultEnum';
|
|
6
|
+
export * from './enums/BiometricEventTypeEnum';
|
|
7
|
+
export * from './dtos/BiometricSubject';
|
|
8
|
+
export * from './dtos/BiometricDelivery';
|
|
9
|
+
export * from './dtos/requests/CreateBiometricVerificationRequest';
|
|
10
|
+
export * from './dtos/responses/BiometricVerificationResponse';
|
|
11
|
+
export * from './events/BiometricVerificationChangedV1';
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Dominio `biometrics` — el contrato de `biometrics-business`, el master de la lógica biométrica
|
|
3
|
+
// de Fiado. El caller dice QUÉ necesita y PARA QUIÉN; el proveedor (Metamap hoy; Apple, Google y
|
|
4
|
+
// Amazon después) queda detrás de un port y no aparece en el contrato más que como un enum.
|
|
5
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
6
|
+
if (k2 === undefined) k2 = k;
|
|
7
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
8
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
9
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
10
|
+
}
|
|
11
|
+
Object.defineProperty(o, k2, desc);
|
|
12
|
+
}) : (function(o, m, k, k2) {
|
|
13
|
+
if (k2 === undefined) k2 = k;
|
|
14
|
+
o[k2] = m[k];
|
|
15
|
+
}));
|
|
16
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
17
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
18
|
+
};
|
|
19
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
|
+
// Enums
|
|
21
|
+
__exportStar(require("./enums/BiometricTypeEnum"), exports);
|
|
22
|
+
__exportStar(require("./enums/BiometricProviderEnum"), exports);
|
|
23
|
+
__exportStar(require("./enums/BiometricDeliveryModeEnum"), exports);
|
|
24
|
+
__exportStar(require("./enums/BiometricVerificationStatusEnum"), exports);
|
|
25
|
+
__exportStar(require("./enums/BiometricResultEnum"), exports);
|
|
26
|
+
__exportStar(require("./enums/BiometricEventTypeEnum"), exports);
|
|
27
|
+
// DTOs compartidos
|
|
28
|
+
__exportStar(require("./dtos/BiometricSubject"), exports);
|
|
29
|
+
__exportStar(require("./dtos/BiometricDelivery"), exports);
|
|
30
|
+
// Request DTOs
|
|
31
|
+
__exportStar(require("./dtos/requests/CreateBiometricVerificationRequest"), exports);
|
|
32
|
+
// Response DTOs
|
|
33
|
+
__exportStar(require("./dtos/responses/BiometricVerificationResponse"), exports);
|
|
34
|
+
// Events (BiometricsInboundQueue)
|
|
35
|
+
__exportStar(require("./events/BiometricVerificationChangedV1"), exports);
|
package/bin/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export * as Crypto from './crypto';
|
|
2
2
|
export * as Account from './account';
|
|
3
3
|
export * as MetamapConnector from './metamapConnector';
|
|
4
|
+
export * as Biometrics from './biometrics';
|
|
4
5
|
export * as Activity from './activity';
|
|
5
6
|
export * as Beneficiary from './beneficiary';
|
|
6
7
|
export * as Address from './address';
|
package/bin/index.js
CHANGED
|
@@ -36,12 +36,13 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
36
36
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
37
37
|
};
|
|
38
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
-
exports.
|
|
40
|
-
exports.
|
|
41
|
-
exports.Shipping = exports.PhoneSales = exports.Onboarding = exports.Kyc = exports.LoanScoring = exports.LoanCredit = void 0;
|
|
39
|
+
exports.IssuanceBusiness = exports.Blacklist = exports.CentralPayments = exports.Helpdesk = exports.FiadoApiResponse = exports.Auth = exports.LegalDocumentsBusiness = exports.Role = exports.STPAccount = exports.RiskProfile = exports.FraudPreventionEngine = exports.BBVARst = exports.Stp = exports.BenefitCenter = exports.BankAccount = exports.P2pContact = exports.CreditContract = exports.Contract = exports.ProductCatalog = exports.ContactInfo = exports.TransactionAnalytics = exports.Transaction = exports.TransactionProcessor = exports.GenericMessage = exports.EventBridgeMessage = exports.SessionActivity = exports.NotificationMessages = exports.ServicePayment = exports.Header = exports.Identity = exports.UserTags = exports.Group = exports.File = exports.ExchangeRate = exports.Directory = exports.Currency = exports.Country = exports.Card = exports.Authentication = exports.AppContent = exports.App = exports.Offices = exports.Places = exports.Address = exports.Beneficiary = exports.Activity = exports.Biometrics = exports.MetamapConnector = exports.Account = exports.Crypto = void 0;
|
|
40
|
+
exports.LoanOfferings = exports.Shortlink = exports.LoanConfig = exports.RetailWizard = exports.RetailCustomer = exports.RetailCards = exports.RetailCatalog = exports.RetailOrg = exports.EmailVerification = exports.NetworkConnector = exports.Modelias = exports.TotpSecurity = exports.Passport = exports.WalletFunding = exports.Remittance = exports.PlatformRbac = exports.CognitoBackofficeConnector = exports.TwilioConnector = exports.MessagesConnector = exports.Mdm = exports.MilestoneBusiness = exports.CirculoCredito = exports.CreditStatements = exports.Sentry = exports.AiEngine = exports.Funnel = exports.TeamsConnector = exports.PlatformErrorEvents = exports.CustomerFile = exports.CreditBackoffice = exports.CreditDashboard = exports.CreditEngine = exports.Credit = exports.ComissionBusiness = exports.ReferralBusiness = exports.ZendeskMessaging = exports.NotificationWS = exports.Event = exports.PayrollBusiness = exports.Cnbv = exports.DirectorySetting = exports.InvoiceCollector = exports.Collector = exports.Pricelist = exports.Company = exports.Services = exports.AccountIssuanceBusiness = exports.AppSelectionData = exports.Device = exports.Observations = void 0;
|
|
41
|
+
exports.Shipping = exports.PhoneSales = exports.Onboarding = exports.Kyc = exports.LoanScoring = exports.LoanCredit = exports.LoanCollectorAssignment = void 0;
|
|
42
42
|
exports.Crypto = __importStar(require("./crypto"));
|
|
43
43
|
exports.Account = __importStar(require("./account"));
|
|
44
44
|
exports.MetamapConnector = __importStar(require("./metamapConnector"));
|
|
45
|
+
exports.Biometrics = __importStar(require("./biometrics"));
|
|
45
46
|
exports.Activity = __importStar(require("./activity"));
|
|
46
47
|
exports.Beneficiary = __importStar(require("./beneficiary"));
|
|
47
48
|
exports.Address = __importStar(require("./address"));
|
|
@@ -29,3 +29,8 @@ __decorate([
|
|
|
29
29
|
(0, class_validator_1.IsOptional)(),
|
|
30
30
|
__metadata("design:type", String)
|
|
31
31
|
], SendProactiveMessageRequest.prototype, "displayName", void 0);
|
|
32
|
+
__decorate([
|
|
33
|
+
(0, class_validator_1.IsString)(),
|
|
34
|
+
(0, class_validator_1.IsOptional)(),
|
|
35
|
+
__metadata("design:type", String)
|
|
36
|
+
], SendProactiveMessageRequest.prototype, "agentNote", void 0);
|
package/package.json
CHANGED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { Expose } from 'class-transformer';
|
|
2
|
+
import { IsISO8601, IsNotEmpty, IsOptional, IsString } from 'class-validator';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Lo que el caller necesita para que el biométrico se ejecute. **Su forma la determina
|
|
6
|
+
* `BiometricVerificationResponse.deliveryMode`, que es el discriminador.**
|
|
7
|
+
*
|
|
8
|
+
* Se modela como TRES clases + una unión de tipos en vez de un objeto con todo opcional. Con la
|
|
9
|
+
* unión, TypeScript obliga al caller a hacer el `switch (deliveryMode)` y sabe exactamente qué
|
|
10
|
+
* campos tiene en cada rama. Con un objeto plano de campos opcionales, el compilador no ayuda y el
|
|
11
|
+
* caller termina haciendo `link!` — que es el bug esperando a que entre el segundo proveedor.
|
|
12
|
+
*
|
|
13
|
+
* `biometrics-business` — Entrega 1 (solo `RedirectDelivery` produce valores reales).
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* `deliveryMode = REDIRECT`. El usuario abre el link y ejecuta el biométrico ahí.
|
|
18
|
+
* Es lo que produce Metamap: una URL hospedada, acortada, con TTL.
|
|
19
|
+
*/
|
|
20
|
+
export class RedirectDelivery {
|
|
21
|
+
/**
|
|
22
|
+
* La URL a abrir. Ya viene acortada — si el acortador no respondió, viene la larga.
|
|
23
|
+
* Que el acortador falle NO detiene la operación: un QR más denso se escanea igual.
|
|
24
|
+
*/
|
|
25
|
+
@Expose() @IsString() @IsNotEmpty()
|
|
26
|
+
link!: string;
|
|
27
|
+
|
|
28
|
+
/** Hasta cuándo sirve el link. ISO-8601 UTC con `Z`. */
|
|
29
|
+
@Expose() @IsISO8601({ strict: true })
|
|
30
|
+
expiresAt!: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* `deliveryMode = CHALLENGE`. El biométrico corre en el dispositivo (Apple Face ID, biometría de
|
|
35
|
+
* Android) y el cliente devuelve una attestation firmada.
|
|
36
|
+
*
|
|
37
|
+
* ⚠️ **Declarado, sin implementar.** El endpoint que recibe la attestation
|
|
38
|
+
* (`POST /.../{id}/assertion`) queda fuera de la Entrega 1 a propósito — el contrato ya lo admite
|
|
39
|
+
* sin romperse, que es todo lo que hacía falta decidir ahora.
|
|
40
|
+
*/
|
|
41
|
+
export class ChallengeDelivery {
|
|
42
|
+
/** El desafío que el dispositivo debe firmar. */
|
|
43
|
+
@Expose() @IsString() @IsNotEmpty()
|
|
44
|
+
challenge!: string;
|
|
45
|
+
|
|
46
|
+
/** Nonce anti-replay. Un solo uso. */
|
|
47
|
+
@Expose() @IsString() @IsNotEmpty()
|
|
48
|
+
nonce!: string;
|
|
49
|
+
|
|
50
|
+
/** Hasta cuándo sirve el challenge. ISO-8601 UTC con `Z`. */
|
|
51
|
+
@Expose() @IsISO8601({ strict: true })
|
|
52
|
+
expiresAt!: string;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* `deliveryMode = IMMEDIATE`. No hay nada que entregar: el biométrico ya corrió en el servidor
|
|
57
|
+
* (Amazon Rekognition) y el veredicto viene en `result` de la misma respuesta.
|
|
58
|
+
*
|
|
59
|
+
* Vacío a propósito. Existe para que la unión sea exhaustiva y el `switch` del caller no necesite
|
|
60
|
+
* un `default` que trague casos nuevos en silencio.
|
|
61
|
+
*
|
|
62
|
+
* ⚠️ Declarado, sin implementar.
|
|
63
|
+
*/
|
|
64
|
+
export class ImmediateDelivery {
|
|
65
|
+
/** Reservado. Nunca se llena hoy — existe para que la clase no sea estructuralmente vacía. */
|
|
66
|
+
@Expose() @IsOptional() @IsString()
|
|
67
|
+
note?: string;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* La unión. El caller discrimina con `deliveryMode`:
|
|
72
|
+
*
|
|
73
|
+
* ```ts
|
|
74
|
+
* switch (res.deliveryMode) {
|
|
75
|
+
* case BiometricDeliveryModeEnum.REDIRECT: abrir((res.delivery as RedirectDelivery).link); break;
|
|
76
|
+
* case BiometricDeliveryModeEnum.CHALLENGE: firmar(res.delivery as ChallengeDelivery); break;
|
|
77
|
+
* case BiometricDeliveryModeEnum.IMMEDIATE: leer(res.result); break;
|
|
78
|
+
* }
|
|
79
|
+
* ```
|
|
80
|
+
*/
|
|
81
|
+
export type BiometricDelivery = RedirectDelivery | ChallengeDelivery | ImmediateDelivery;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { Expose } from 'class-transformer';
|
|
2
|
+
import { IsNotEmpty, IsOptional, IsString, Matches } from 'class-validator';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Datos del SUJETO que el proveedor necesita, además del `directoryId`.
|
|
6
|
+
*
|
|
7
|
+
* 🔴 **Tipado y CERRADO, jamás un `Record<string, unknown>`.** Un mapa abierto en un boundary es
|
|
8
|
+
* antipatrón Fiado, pero el problema real es peor: sería un canal por el que cualquier caller le
|
|
9
|
+
* puede filtrar PII a un tercero sin que nadie lo revise. Lo que viaja a un proveedor externo se
|
|
10
|
+
* declara campo por campo.
|
|
11
|
+
*
|
|
12
|
+
* **No confundir con `callerContext`**, que es correlación opaca del caller y NUNCA sale de Fiado.
|
|
13
|
+
* Son dos cosas que se mezclan seguido: "datos que el proveedor necesita" vs "mi correlación".
|
|
14
|
+
*
|
|
15
|
+
* **Todos opcionales, con resolución del lado del servicio (DEC-005b).** Lo que venga acá gana; lo
|
|
16
|
+
* que falte, `biometrics-business` lo resuelve contra `fiado-identity-lambda` a partir del
|
|
17
|
+
* `directoryId`. Si tras eso sigue faltando alguno, responde `SUBJECT_DATA_INCOMPLETE` (422)
|
|
18
|
+
* nombrando los CAMPOS, jamás sus valores.
|
|
19
|
+
*
|
|
20
|
+
* 🔴 **Ese error existe para prevenir una falla muda ya vivida.** Con la metadata de identidad
|
|
21
|
+
* vacía, el proveedor genera una verificación HUÉRFANA: el usuario hace la selfie completa, el
|
|
22
|
+
* webhook no la puede asociar a nadie, y el flujo queda colgado para siempre con la persona ya
|
|
23
|
+
* fuera de la tienda. Fallar temprano y ruidoso es la única respuesta correcta.
|
|
24
|
+
*
|
|
25
|
+
* `biometrics-business` — Entrega 1.
|
|
26
|
+
*/
|
|
27
|
+
export class BiometricSubject {
|
|
28
|
+
/** Identificador de la persona en el directorio de Fiado. */
|
|
29
|
+
@Expose() @IsOptional() @IsString() @IsNotEmpty()
|
|
30
|
+
peopleId?: string;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* País del sujeto en el catálogo de Fiado (`CountryId`). MX es `'484'`.
|
|
34
|
+
*
|
|
35
|
+
* ⚠️ Numérico-como-string a propósito: es el mismo valor que usa el resto de la plataforma.
|
|
36
|
+
* Un caller que mande `'MX'` acá rompería la metadata del proveedor.
|
|
37
|
+
*/
|
|
38
|
+
@Expose() @IsOptional() @IsString() @IsNotEmpty()
|
|
39
|
+
countryId?: string;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Teléfono **ya en E.164**, con `+` y código de país (`+521234567890`).
|
|
43
|
+
*
|
|
44
|
+
* ⚠️ **La normalización es del caller, no de este servicio.** El wizard, por ejemplo, guarda 10
|
|
45
|
+
* dígitos crudos en su sesión y los convierte con `toE164Mx()` solo al cruzar hacia el
|
|
46
|
+
* proveedor. Mandar 10 dígitos pelados acá produce una metadata que el proveedor acepta y que
|
|
47
|
+
* después nadie puede reconciliar.
|
|
48
|
+
*/
|
|
49
|
+
@Expose() @IsOptional() @IsString()
|
|
50
|
+
@Matches(/^\+[1-9]\d{7,14}$/, {
|
|
51
|
+
message: 'phoneNumber debe venir en E.164 con + y codigo de pais (ej. +521234567890)',
|
|
52
|
+
})
|
|
53
|
+
phoneNumber?: string;
|
|
54
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { Expose, Type } from 'class-transformer';
|
|
2
|
+
import {
|
|
3
|
+
IsEnum,
|
|
4
|
+
IsInt,
|
|
5
|
+
IsNotEmpty,
|
|
6
|
+
IsOptional,
|
|
7
|
+
IsString,
|
|
8
|
+
Max,
|
|
9
|
+
MaxLength,
|
|
10
|
+
Min,
|
|
11
|
+
ValidateNested,
|
|
12
|
+
} from 'class-validator';
|
|
13
|
+
import { BiometricProviderEnum } from '../../enums/BiometricProviderEnum';
|
|
14
|
+
import { BiometricTypeEnum } from '../../enums/BiometricTypeEnum';
|
|
15
|
+
import { BiometricSubject } from '../BiometricSubject';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* `POST /private/biometric-verifications` — pedir una verificación biométrica de una persona.
|
|
19
|
+
*
|
|
20
|
+
* El caller dice QUÉ necesita (`type`), QUIÉN lo ejecuta (`provider`) y PARA QUIÉN es
|
|
21
|
+
* (`directoryId`). Todo lo demás — resolver la foto de referencia, publicarla, firmarla, armar la
|
|
22
|
+
* metadata del proveedor, acortar el link — lo hace `biometrics-business`. Ese es el punto: que el
|
|
23
|
+
* segundo producto que necesite biometría haga un `POST` en vez de un proyecto.
|
|
24
|
+
*
|
|
25
|
+
* `biometrics-business` — Entrega 1.
|
|
26
|
+
*/
|
|
27
|
+
export class CreateBiometricVerificationRequest {
|
|
28
|
+
/**
|
|
29
|
+
* QUÉ biométrico. Hoy solo `FACEMATCH` está implementado; el resto devuelve
|
|
30
|
+
* `BIOMETRIC_NOT_SUPPORTED` (422).
|
|
31
|
+
*/
|
|
32
|
+
@Expose() @IsEnum(BiometricTypeEnum)
|
|
33
|
+
type!: BiometricTypeEnum;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* QUIÉN lo ejecuta. **Obligatorio** (DEC-004): el caller lo nombra, el servicio no resuelve un
|
|
37
|
+
* default. Hoy solo `METAMAP`.
|
|
38
|
+
*
|
|
39
|
+
* ⚠️ **TD-001**: esto acopla a los callers. Cambiar de proveedor obliga a tocar y redesplegar a
|
|
40
|
+
* todos. Se aceptó a cambio de que la elección sea explícita y auditable en cada request.
|
|
41
|
+
*/
|
|
42
|
+
@Expose() @IsEnum(BiometricProviderEnum)
|
|
43
|
+
provider!: BiometricProviderEnum;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* PARA QUIÉN es. Es la llave con la que se resuelve la foto de referencia y los datos que falten
|
|
47
|
+
* de `subject`.
|
|
48
|
+
*
|
|
49
|
+
* `@IsNotEmpty()` y no solo `@IsString()`: un `''` colado pasaría el boundary y fallaría tres
|
|
50
|
+
* pasos río abajo, contra identity, con un error que no apunta a nada.
|
|
51
|
+
*/
|
|
52
|
+
@Expose() @IsString() @IsNotEmpty()
|
|
53
|
+
directoryId!: string;
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* QUIÉN pide (el producto o lambda: `surekeep-wizard`, `user-wallet-app`…). Para auditoría y
|
|
57
|
+
* trazabilidad.
|
|
58
|
+
*
|
|
59
|
+
* ⚠️ **No autoriza nada** (TD-009). El `GET` no lo valida: hoy el aislamiento es que los dos
|
|
60
|
+
* endpoints son VPC-only, así que solo lambdas de Fiado llegan. Está anotado como deuda
|
|
61
|
+
* consciente, no como olvido.
|
|
62
|
+
*/
|
|
63
|
+
@Expose() @IsString() @IsNotEmpty()
|
|
64
|
+
requestedBy!: string;
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Datos del sujeto que el proveedor necesita. Lo que mandes gana; lo que falte se resuelve
|
|
68
|
+
* contra identity. Ver `BiometricSubject` — sobre todo la nota de por qué `phoneNumber` va en
|
|
69
|
+
* E.164 y la normalización es tuya.
|
|
70
|
+
*/
|
|
71
|
+
@Expose() @IsOptional() @ValidateNested() @Type(() => BiometricSubject)
|
|
72
|
+
subject?: BiometricSubject;
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Override de la foto de referencia. Si no viene, el servicio la resuelve solo: pide la **selfie
|
|
76
|
+
* del KYC** a identity (no el avatar `PROFILE_PICTURE`, que el KYC nunca genera) y la publica.
|
|
77
|
+
*
|
|
78
|
+
* Úsalo solo si ya tienes la foto correcta a mano y quieres ahorrarte el round-trip.
|
|
79
|
+
*/
|
|
80
|
+
@Expose() @IsOptional() @IsString() @IsNotEmpty()
|
|
81
|
+
referenceImageUrl?: string;
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Vigencia de la entrega, en segundos. Default 3600.
|
|
85
|
+
*
|
|
86
|
+
* El techo de 24 h no es arbitrario: lo que se entrega es una identidad abierta. Un link de
|
|
87
|
+
* verificación que sobreviva a la operación que lo pidió es una puerta sin nadie mirándola.
|
|
88
|
+
*/
|
|
89
|
+
@Expose() @IsOptional() @IsInt() @Min(60) @Max(86400)
|
|
90
|
+
ttlSeconds?: number;
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Correlación tuya, opaca. Te la devuelvo intacta en el `GET`.
|
|
94
|
+
*
|
|
95
|
+
* 🔴 **NUNCA viaja al proveedor.** Es lo que la separa de `subject`: uno son datos que el
|
|
96
|
+
* tercero necesita, esto es un dato que solo te sirve a ti. No metas PII acá — mete tu id de
|
|
97
|
+
* sesión, de orden, de lo que sea que estés correlacionando.
|
|
98
|
+
*/
|
|
99
|
+
@Expose() @IsOptional() @IsString() @MaxLength(256)
|
|
100
|
+
callerContext?: string;
|
|
101
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { Expose } from 'class-transformer';
|
|
2
|
+
import {
|
|
3
|
+
IsEnum,
|
|
4
|
+
IsISO8601,
|
|
5
|
+
IsNotEmpty,
|
|
6
|
+
IsNumber,
|
|
7
|
+
IsOptional,
|
|
8
|
+
IsString,
|
|
9
|
+
Max,
|
|
10
|
+
MaxLength,
|
|
11
|
+
Min,
|
|
12
|
+
} from 'class-validator';
|
|
13
|
+
import { BiometricDeliveryModeEnum } from '../../enums/BiometricDeliveryModeEnum';
|
|
14
|
+
import { BiometricProviderEnum } from '../../enums/BiometricProviderEnum';
|
|
15
|
+
import { BiometricResultEnum } from '../../enums/BiometricResultEnum';
|
|
16
|
+
import { BiometricTypeEnum } from '../../enums/BiometricTypeEnum';
|
|
17
|
+
import { BiometricVerificationStatusEnum } from '../../enums/BiometricVerificationStatusEnum';
|
|
18
|
+
import type { BiometricDelivery } from '../BiometricDelivery';
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* El estado completo de una verificación biométrica.
|
|
22
|
+
*
|
|
23
|
+
* 🔴 **El MISMO tipo lo devuelven el `POST` y el `GET`** (DEC-012). Es una excepción consciente a
|
|
24
|
+
* "1 DTO por endpoint": el `POST` devuelve el estado inicial del MISMO recurso que el `GET` expone
|
|
25
|
+
* después. Duplicar el tipo garantizaría drift entre los dos — la clase de bug donde el `GET` gana
|
|
26
|
+
* un campo y el `POST` no, y el caller descubre la diferencia en producción. Queda registrado acá
|
|
27
|
+
* para que no haya que re-discutirlo en cada PR.
|
|
28
|
+
*
|
|
29
|
+
* `biometrics-business` — Entrega 1.
|
|
30
|
+
*/
|
|
31
|
+
export class BiometricVerificationResponse {
|
|
32
|
+
/**
|
|
33
|
+
* La llave de todo. Se genera al crear, viaja dentro de la metadata del proveedor, y vuelve
|
|
34
|
+
* ECOADA en cada webhook — **por eso la correlación es un `get` por PK y no una búsqueda**.
|
|
35
|
+
*/
|
|
36
|
+
@Expose() @IsString() @IsNotEmpty()
|
|
37
|
+
biometricVerificationId!: string;
|
|
38
|
+
|
|
39
|
+
/** Eco de lo que pediste. */
|
|
40
|
+
@Expose() @IsEnum(BiometricTypeEnum)
|
|
41
|
+
type!: BiometricTypeEnum;
|
|
42
|
+
|
|
43
|
+
/** Eco de lo que pediste. */
|
|
44
|
+
@Expose() @IsEnum(BiometricProviderEnum)
|
|
45
|
+
provider!: BiometricProviderEnum;
|
|
46
|
+
|
|
47
|
+
/** Eco de lo que pediste. */
|
|
48
|
+
@Expose() @IsString() @IsNotEmpty()
|
|
49
|
+
directoryId!: string;
|
|
50
|
+
|
|
51
|
+
/** Dónde va el PROCESO. Ver `BiometricVerificationStatusEnum`. */
|
|
52
|
+
@Expose() @IsEnum(BiometricVerificationStatusEnum)
|
|
53
|
+
status!: BiometricVerificationStatusEnum;
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Qué dijo el BIOMÉTRICO. Ver `BiometricResultEnum`.
|
|
57
|
+
*
|
|
58
|
+
* ⚠️ Puede venir poblado con `status` todavía en `IN_PROGRESS`: el veredicto del facematch llega
|
|
59
|
+
* en el `step_completed`, antes del cierre de la verificación. Eso no es una inconsistencia, es
|
|
60
|
+
* exactamente la razón de que `status` y `result` sean campos distintos.
|
|
61
|
+
*/
|
|
62
|
+
@Expose() @IsEnum(BiometricResultEnum)
|
|
63
|
+
result!: BiometricResultEnum;
|
|
64
|
+
|
|
65
|
+
/** Discriminador de `delivery`. Ver `BiometricDeliveryModeEnum`. */
|
|
66
|
+
@Expose() @IsEnum(BiometricDeliveryModeEnum)
|
|
67
|
+
deliveryMode!: BiometricDeliveryModeEnum;
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Lo que necesitas para ejecutar el biométrico. Su forma depende de `deliveryMode` — haz el
|
|
71
|
+
* `switch`, no leas `link` a ciegas.
|
|
72
|
+
*
|
|
73
|
+
* Sin decorador de validación a propósito: es una unión discriminada y `class-validator` no
|
|
74
|
+
* sabe validar uniones sin un `@Type()` con discriminador que acá agregaría más ceremonia que
|
|
75
|
+
* seguridad. El contrato lo garantiza el productor, que es un solo lambda.
|
|
76
|
+
*/
|
|
77
|
+
@Expose()
|
|
78
|
+
delivery!: BiometricDelivery;
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Similitud reportada por el proveedor, 0–100, cuando la expone. Rekognition da score; Metamap
|
|
82
|
+
* responde pass/fail y esto viene ausente.
|
|
83
|
+
*
|
|
84
|
+
* ⚠️ **No lo uses para decidir tú el umbral.** El proveedor ya aplicó el suyo y eso es lo que
|
|
85
|
+
* dice `result`. Esto es para diagnóstico y para calibrar, no para re-juzgar.
|
|
86
|
+
*/
|
|
87
|
+
@Expose() @IsOptional() @IsNumber() @Min(0) @Max(100)
|
|
88
|
+
score?: number;
|
|
89
|
+
|
|
90
|
+
/** Umbral que aplicó el proveedor, cuando lo expone. Contexto para leer el `score`. */
|
|
91
|
+
@Expose() @IsOptional() @IsNumber() @Min(0) @Max(100)
|
|
92
|
+
threshold?: number;
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* El id de la verificación del lado del proveedor (en Metamap, su `verificationId`). Para
|
|
96
|
+
* soporte y reconciliación: es lo que se busca en el panel del proveedor.
|
|
97
|
+
*/
|
|
98
|
+
@Expose() @IsOptional() @IsString() @IsNotEmpty()
|
|
99
|
+
providerRef?: string;
|
|
100
|
+
|
|
101
|
+
/** Tu correlación, tal cual la mandaste. */
|
|
102
|
+
@Expose() @IsOptional() @IsString() @MaxLength(256)
|
|
103
|
+
callerContext?: string;
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Por qué falló, solo con `status = FAILED`. Texto OPERACIONAL, sin PII y sin nada del
|
|
107
|
+
* proveedor: describe qué se rompió de nuestro lado (DEC-013).
|
|
108
|
+
*/
|
|
109
|
+
@Expose() @IsOptional() @IsString()
|
|
110
|
+
failureReason?: string;
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Todas las marcas de tiempo son **`string` ISO-8601 UTC con `Z`**.
|
|
114
|
+
*
|
|
115
|
+
* ⚠️ En la tabla se persisten como `Number` (dynamoose serializa `Date` a number, y un GSI
|
|
116
|
+
* declarado `S` sobre un timestamp se rompe en silencio). El mapeo Row → DTO es explícito. Si
|
|
117
|
+
* copias este tipo para el `Row`, rompes el índice.
|
|
118
|
+
*/
|
|
119
|
+
@Expose() @IsISO8601({ strict: true })
|
|
120
|
+
createdAt!: string;
|
|
121
|
+
|
|
122
|
+
/** Última vez que cambió algo. ISO-8601 UTC con `Z`. */
|
|
123
|
+
@Expose() @IsISO8601({ strict: true })
|
|
124
|
+
updatedAt!: string;
|
|
125
|
+
|
|
126
|
+
/** Cuándo vence la entrega. Es lo que el `GET` compara para persistir `EXPIRED` (DEC-015). */
|
|
127
|
+
@Expose() @IsISO8601({ strict: true })
|
|
128
|
+
expiresAt!: string;
|
|
129
|
+
|
|
130
|
+
/** Cuándo alcanzó un estado terminal. Ausente mientras siga viva. */
|
|
131
|
+
@Expose() @IsOptional() @IsISO8601({ strict: true })
|
|
132
|
+
completedAt?: string;
|
|
133
|
+
}
|