@fiado/type-kit 3.302.0 → 3.303.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/kyc/KycProofOfAddressCompletedV1.test.ts +195 -0
- package/_test_/unit/retailWizard/proofOfAddressPrepareRequest.test.ts +26 -0
- package/bin/kyc/enums/ProofAddressStatusEnum.d.ts +33 -0
- package/bin/kyc/enums/ProofAddressStatusEnum.js +37 -0
- package/bin/kyc/events/KycProofOfAddressCompletedV1.d.ts +49 -0
- package/bin/kyc/events/KycProofOfAddressCompletedV1.js +75 -0
- package/bin/kyc/index.d.ts +2 -0
- package/bin/kyc/index.js +2 -0
- package/bin/retailWizard/dtos/requests/ProofOfAddressPrepareRequest.d.ts +10 -0
- package/bin/retailWizard/dtos/requests/ProofOfAddressPrepareRequest.js +28 -0
- package/bin/retailWizard/dtos/responses/KycPrepareResponse.d.ts +15 -0
- package/bin/retailWizard/dtos/responses/KycPrepareResponse.js +12 -1
- package/bin/retailWizard/dtos/responses/ProofOfAddressPrepareResponse.d.ts +26 -0
- package/bin/retailWizard/dtos/responses/ProofOfAddressPrepareResponse.js +2 -0
- package/bin/retailWizard/index.d.ts +2 -0
- package/bin/retailWizard/index.js +2 -0
- package/package.json +1 -1
- package/src/kyc/enums/ProofAddressStatusEnum.ts +44 -0
- package/src/kyc/events/KycProofOfAddressCompletedV1.ts +68 -0
- package/src/kyc/index.ts +2 -0
- package/src/retailWizard/dtos/requests/ProofOfAddressPrepareRequest.ts +15 -0
- package/src/retailWizard/dtos/responses/KycPrepareResponse.ts +16 -0
- package/src/retailWizard/dtos/responses/ProofOfAddressPrepareResponse.ts +27 -0
- package/src/retailWizard/index.ts +2 -0
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import 'reflect-metadata';
|
|
2
|
+
import { plainToInstance } from 'class-transformer';
|
|
3
|
+
import { validate } from 'class-validator';
|
|
4
|
+
import {
|
|
5
|
+
KycProofOfAddressCompletedV1,
|
|
6
|
+
KycStepCompletedV1,
|
|
7
|
+
ProofAddressStatusEnum,
|
|
8
|
+
} from '../../../src/kyc/index';
|
|
9
|
+
|
|
10
|
+
/** Base válida del evento; cada caso pisa solo el campo que está probando. */
|
|
11
|
+
const base = {
|
|
12
|
+
eventType: 'KycProofOfAddressCompletedV1',
|
|
13
|
+
verificationId: 'ver-1',
|
|
14
|
+
directoryId: 'dir-1',
|
|
15
|
+
peopleId: 'ppl-1',
|
|
16
|
+
status: 'SUCCESS',
|
|
17
|
+
addressPersisted: true,
|
|
18
|
+
occurredAt: '2026-08-11T10:05:00.000Z',
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const instancia = (raw: Record<string, unknown>): KycProofOfAddressCompletedV1 =>
|
|
22
|
+
plainToInstance(KycProofOfAddressCompletedV1, raw, { excludeExtraneousValues: true });
|
|
23
|
+
|
|
24
|
+
describe('KycProofOfAddressCompletedV1', () => {
|
|
25
|
+
it('valida el shape que publica el webhook al cerrar el comprobante', async () => {
|
|
26
|
+
const dto = instancia(base);
|
|
27
|
+
|
|
28
|
+
expect(await validate(dto as object)).toHaveLength(0);
|
|
29
|
+
expect(dto.status).toBe(ProofAddressStatusEnum.SUCCESS);
|
|
30
|
+
expect(dto.addressPersisted).toBe(true);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('valida con peopleId vacío (Metamap no siempre lo manda)', async () => {
|
|
34
|
+
const dto = instancia({ ...base, peopleId: '' });
|
|
35
|
+
|
|
36
|
+
expect(await validate(dto as object)).toHaveLength(0);
|
|
37
|
+
expect(dto.peopleId).toBe('');
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('valida sin peopleId', async () => {
|
|
41
|
+
const { peopleId: _omitido, ...sinPeople } = base;
|
|
42
|
+
|
|
43
|
+
expect(await validate(instancia(sinPeople) as object)).toHaveLength(0);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
// ── eventType: acá SÍ es obligatorio ──────────────────────────────────────
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* A diferencia de `KycStepCompletedV1`, donde es opcional por los mensajes que ya estaban en
|
|
50
|
+
* vuelo. Este evento nace hoy: el discriminador puede exigirse desde el primer mensaje.
|
|
51
|
+
*/
|
|
52
|
+
it('rechaza un evento sin eventType (el discriminador es obligatorio)', async () => {
|
|
53
|
+
const { eventType: _omitido, ...sinTipo } = base;
|
|
54
|
+
const errors = await validate(instancia(sinTipo) as object);
|
|
55
|
+
|
|
56
|
+
expect(errors.map((e) => e.property)).toContain('eventType');
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('rechaza un eventType de otro evento', async () => {
|
|
60
|
+
const errors = await validate(instancia({ ...base, eventType: 'KycStepCompletedV1' }) as object);
|
|
61
|
+
|
|
62
|
+
expect(errors.map((e) => e.property)).toContain('eventType');
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
// ── Identificadores ───────────────────────────────────────────────────────
|
|
66
|
+
|
|
67
|
+
it.each(['verificationId', 'directoryId'])('rechaza %s vacío', async (campo) => {
|
|
68
|
+
const errors = await validate(instancia({ ...base, [campo]: '' }) as object);
|
|
69
|
+
|
|
70
|
+
expect(errors.map((e) => e.property)).toContain(campo);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('rechaza un evento sin directoryId (sin él no hay gate posible)', async () => {
|
|
74
|
+
const { directoryId: _omitido, ...sinDirectory } = base;
|
|
75
|
+
const errors = await validate(instancia(sinDirectory) as object);
|
|
76
|
+
|
|
77
|
+
expect(errors.map((e) => e.property)).toContain('directoryId');
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
// ── status y addressPersisted ─────────────────────────────────────────────
|
|
81
|
+
|
|
82
|
+
it.each(Object.values(ProofAddressStatusEnum))('acepta el status %s', async (status) => {
|
|
83
|
+
expect(await validate(instancia({ ...base, status }) as object)).toHaveLength(0);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it('rechaza un status fuera del enum', async () => {
|
|
87
|
+
const errors = await validate(instancia({ ...base, status: 'MAS_O_MENOS' }) as object);
|
|
88
|
+
|
|
89
|
+
expect(errors.map((e) => e.property)).toContain('status');
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Va explícito aunque sea derivable del `status`: sin él cada consumidor reimplementaría el mapa
|
|
94
|
+
* de 12 casos, y ese mapa lo conoce el productor.
|
|
95
|
+
*/
|
|
96
|
+
it('rechaza un evento sin addressPersisted', async () => {
|
|
97
|
+
const { addressPersisted: _omitido, ...sinFlag } = base;
|
|
98
|
+
const errors = await validate(instancia(sinFlag) as object);
|
|
99
|
+
|
|
100
|
+
expect(errors.map((e) => e.property)).toContain('addressPersisted');
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it('rechaza addressPersisted que no sea booleano', async () => {
|
|
104
|
+
const errors = await validate(instancia({ ...base, addressPersisted: 'true' }) as object);
|
|
105
|
+
|
|
106
|
+
expect(errors.map((e) => e.property)).toContain('addressPersisted');
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it('acepta el caso sin domicilio guardado', async () => {
|
|
110
|
+
const dto = instancia({
|
|
111
|
+
...base,
|
|
112
|
+
status: ProofAddressStatusEnum.NO_ADDRESS_DETECTED,
|
|
113
|
+
addressPersisted: false,
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
expect(await validate(dto as object)).toHaveLength(0);
|
|
117
|
+
expect(dto.addressPersisted).toBe(false);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
// ── Orden ─────────────────────────────────────────────────────────────────
|
|
121
|
+
|
|
122
|
+
it('rechaza un evento sin occurredAt (sin marca de tiempo no hay cómo ordenar)', async () => {
|
|
123
|
+
const { occurredAt: _omitido, ...sinFecha } = base;
|
|
124
|
+
const errors = await validate(instancia(sinFecha) as object);
|
|
125
|
+
|
|
126
|
+
expect(errors.map((e) => e.property)).toContain('occurredAt');
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* `@IsISO8601()` a secas acepta todos estos. El offset es el que hace daño: rompe la comparación
|
|
131
|
+
* léxica del consumidor, que descarta el evento real sin que nada truene.
|
|
132
|
+
*/
|
|
133
|
+
it.each([
|
|
134
|
+
['offset positivo en vez de Z', '2026-08-11T15:05:00+05:00'],
|
|
135
|
+
['offset negativo en vez de Z', '2026-08-11T05:05:00-05:00'],
|
|
136
|
+
['solo fecha, sin hora', '2026-08-11'],
|
|
137
|
+
['formato básico sin guiones', '20260811T100500Z'],
|
|
138
|
+
['fecha de semana ISO', '2026-W30-1'],
|
|
139
|
+
['fecha imposible', '2026-02-30T10:05:00.000Z'],
|
|
140
|
+
['sin zona horaria', '2026-08-11T10:05:00'],
|
|
141
|
+
])('rechaza occurredAt %s', async (_etiqueta, valor) => {
|
|
142
|
+
const errors = await validate(instancia({ ...base, occurredAt: valor }) as object);
|
|
143
|
+
|
|
144
|
+
expect(errors.map((e) => e.property)).toContain('occurredAt');
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
it.each([
|
|
148
|
+
['con milisegundos', '2026-08-11T10:05:00.000Z'],
|
|
149
|
+
['sin milisegundos', '2026-08-11T10:05:00Z'],
|
|
150
|
+
])('acepta occurredAt en UTC %s', async (_etiqueta, valor) => {
|
|
151
|
+
expect(await validate(instancia({ ...base, occurredAt: valor }) as object)).toHaveLength(0);
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
// ── Convivencia en RetailKycInboundQueue ──────────────────────────────────
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Los tres eventos comparten cola. Estos candados prueban que uno no se puede hacer pasar por
|
|
158
|
+
* otro: si se volvieran mutuamente válidos, el consumidor tomaría la rama equivocada.
|
|
159
|
+
*/
|
|
160
|
+
it('un cierre de estación NO valida como comprobante de domicilio', async () => {
|
|
161
|
+
const estacion = {
|
|
162
|
+
eventType: 'KycStepCompletedV1', verificationId: 'ver-1', directoryId: 'dir-1',
|
|
163
|
+
step: 'facematch', status: 'PASS', occurredAt: '2026-08-11T10:05:00.000Z',
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
const errors = await validate(instancia(estacion) as object);
|
|
167
|
+
|
|
168
|
+
expect(errors.map((e) => e.property)).toEqual(
|
|
169
|
+
expect.arrayContaining(['eventType', 'status', 'addressPersisted']),
|
|
170
|
+
);
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
it('un comprobante de domicilio NO valida como cierre de estación', async () => {
|
|
174
|
+
const dto = plainToInstance(KycStepCompletedV1, base, { excludeExtraneousValues: true });
|
|
175
|
+
const errors = await validate(dto as object);
|
|
176
|
+
|
|
177
|
+
expect(errors.map((e) => e.property)).toEqual(expect.arrayContaining(['eventType', 'step', 'status']));
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
// ── El espejo con fiado-abstractions ──────────────────────────────────────
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* El enum se espeja de `@fiado/fiado-abstractions` porque el wizard no depende de esa lib. Si allá
|
|
184
|
+
* agregan un caso y acá no, el evento con ese status caería al DLQ como corrupto.
|
|
185
|
+
*/
|
|
186
|
+
it('declara los 12 estados del comprobante', () => {
|
|
187
|
+
expect(Object.values(ProofAddressStatusEnum).sort()).toEqual(
|
|
188
|
+
[
|
|
189
|
+
'SUCCESS', 'MEX_HELP_NEEDED', 'USA_HELP_NEEDED', 'COUNTRY_MISMATCH',
|
|
190
|
+
'COUNTRY_NOT_SUPPORTED', 'PENDING_METAMAP_REVIEW', 'NO_ADDRESS_DETECTED',
|
|
191
|
+
'PLACES_NO_MATCH', 'METAMAP_ERROR', 'PLACES_ERROR', 'ADDRESS_SAVE_ERROR', 'CHECKBOX_ERROR',
|
|
192
|
+
].sort(),
|
|
193
|
+
);
|
|
194
|
+
});
|
|
195
|
+
});
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import 'reflect-metadata';
|
|
2
|
+
import { plainToInstance } from 'class-transformer';
|
|
3
|
+
import { validate } from 'class-validator';
|
|
4
|
+
import { ProofOfAddressPrepareRequest } from '../../../src/retailWizard/index';
|
|
5
|
+
|
|
6
|
+
const instancia = (raw: Record<string, unknown>): ProofOfAddressPrepareRequest =>
|
|
7
|
+
plainToInstance(ProofOfAddressPrepareRequest, raw, { excludeExtraneousValues: true });
|
|
8
|
+
|
|
9
|
+
describe('ProofOfAddressPrepareRequest', () => {
|
|
10
|
+
it.each(['CUSTOMER_LINK', 'STORE_DEVICE'])('acepta la modalidad %s', async (modality) => {
|
|
11
|
+
expect(await validate(instancia({ modality }) as object)).toHaveLength(0);
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it('rechaza un request sin modality', async () => {
|
|
15
|
+
const errors = await validate(instancia({}) as object);
|
|
16
|
+
|
|
17
|
+
expect(errors.map((e) => e.property)).toContain('modality');
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
// MANUAL no es una modalidad de prepare: la captura manual es su propio endpoint.
|
|
21
|
+
it('rechaza MANUAL', async () => {
|
|
22
|
+
const errors = await validate(instancia({ modality: 'MANUAL' }) as object);
|
|
23
|
+
|
|
24
|
+
expect(errors.map((e) => e.property)).toContain('modality');
|
|
25
|
+
});
|
|
26
|
+
});
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resultado del procesamiento del comprobante de domicilio de Metamap: "¿pudimos usar el
|
|
3
|
+
* comprobante?", que no es lo mismo que "¿Metamap aprobó el documento?".
|
|
4
|
+
*
|
|
5
|
+
* Espeja `@fiado/fiado-abstractions` (`Fiado/Identity/ProofAddressStatusEnum`) porque los
|
|
6
|
+
* consumidores de SureKeep no dependen de esa lib.
|
|
7
|
+
*/
|
|
8
|
+
export declare enum ProofAddressStatusEnum {
|
|
9
|
+
/** Domicilio de México o Estados Unidos resuelto a nivel calle y guardado con sus componentes. */
|
|
10
|
+
SUCCESS = "SUCCESS",
|
|
11
|
+
/** Domicilio mexicano que el geocoder no resolvió a nivel calle. Se guardó el texto del recibo para revisión manual. */
|
|
12
|
+
MEX_HELP_NEEDED = "MEX_HELP_NEEDED",
|
|
13
|
+
/** Igual que `MEX_HELP_NEEDED`, para un domicilio estadounidense. */
|
|
14
|
+
USA_HELP_NEEDED = "USA_HELP_NEEDED",
|
|
15
|
+
/** El usuario declaró un país en Metamap y el domicilio resolvió al otro. Requiere revisión. */
|
|
16
|
+
COUNTRY_MISMATCH = "COUNTRY_MISMATCH",
|
|
17
|
+
/** El domicilio es de un país fuera de México y Estados Unidos. Se guarda pero NO habilita el requisito. */
|
|
18
|
+
COUNTRY_NOT_SUPPORTED = "COUNTRY_NOT_SUPPORTED",
|
|
19
|
+
/** Metamap mandó el documento a revisión manual. No se procesa hasta tener su veredicto. */
|
|
20
|
+
PENDING_METAMAP_REVIEW = "PENDING_METAMAP_REVIEW",
|
|
21
|
+
/** El OCR de Metamap no devolvió domicilio: el comprobante es ilegible o no es un comprobante. */
|
|
22
|
+
NO_ADDRESS_DETECTED = "NO_ADDRESS_DETECTED",
|
|
23
|
+
/** El geocoder respondió sin candidatos. Texto ilegible o domicilio fuera de los países consultados. */
|
|
24
|
+
PLACES_NO_MATCH = "PLACES_NO_MATCH",
|
|
25
|
+
/** No se pudo traer la evidencia de Metamap (timeout, 5xx). Reintentable. */
|
|
26
|
+
METAMAP_ERROR = "METAMAP_ERROR",
|
|
27
|
+
/** Falló `places-business`. Reintentable. */
|
|
28
|
+
PLACES_ERROR = "PLACES_ERROR",
|
|
29
|
+
/** Falló `fiado-address-lambda` al guardar el domicilio. Reintentable. */
|
|
30
|
+
ADDRESS_SAVE_ERROR = "ADDRESS_SAVE_ERROR",
|
|
31
|
+
/** El domicilio se guardó pero falló prender la casilla. Reintentable — el dato está a salvo. */
|
|
32
|
+
CHECKBOX_ERROR = "CHECKBOX_ERROR"
|
|
33
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ProofAddressStatusEnum = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Resultado del procesamiento del comprobante de domicilio de Metamap: "¿pudimos usar el
|
|
6
|
+
* comprobante?", que no es lo mismo que "¿Metamap aprobó el documento?".
|
|
7
|
+
*
|
|
8
|
+
* Espeja `@fiado/fiado-abstractions` (`Fiado/Identity/ProofAddressStatusEnum`) porque los
|
|
9
|
+
* consumidores de SureKeep no dependen de esa lib.
|
|
10
|
+
*/
|
|
11
|
+
var ProofAddressStatusEnum;
|
|
12
|
+
(function (ProofAddressStatusEnum) {
|
|
13
|
+
/** Domicilio de México o Estados Unidos resuelto a nivel calle y guardado con sus componentes. */
|
|
14
|
+
ProofAddressStatusEnum["SUCCESS"] = "SUCCESS";
|
|
15
|
+
/** Domicilio mexicano que el geocoder no resolvió a nivel calle. Se guardó el texto del recibo para revisión manual. */
|
|
16
|
+
ProofAddressStatusEnum["MEX_HELP_NEEDED"] = "MEX_HELP_NEEDED";
|
|
17
|
+
/** Igual que `MEX_HELP_NEEDED`, para un domicilio estadounidense. */
|
|
18
|
+
ProofAddressStatusEnum["USA_HELP_NEEDED"] = "USA_HELP_NEEDED";
|
|
19
|
+
/** El usuario declaró un país en Metamap y el domicilio resolvió al otro. Requiere revisión. */
|
|
20
|
+
ProofAddressStatusEnum["COUNTRY_MISMATCH"] = "COUNTRY_MISMATCH";
|
|
21
|
+
/** El domicilio es de un país fuera de México y Estados Unidos. Se guarda pero NO habilita el requisito. */
|
|
22
|
+
ProofAddressStatusEnum["COUNTRY_NOT_SUPPORTED"] = "COUNTRY_NOT_SUPPORTED";
|
|
23
|
+
/** Metamap mandó el documento a revisión manual. No se procesa hasta tener su veredicto. */
|
|
24
|
+
ProofAddressStatusEnum["PENDING_METAMAP_REVIEW"] = "PENDING_METAMAP_REVIEW";
|
|
25
|
+
/** El OCR de Metamap no devolvió domicilio: el comprobante es ilegible o no es un comprobante. */
|
|
26
|
+
ProofAddressStatusEnum["NO_ADDRESS_DETECTED"] = "NO_ADDRESS_DETECTED";
|
|
27
|
+
/** El geocoder respondió sin candidatos. Texto ilegible o domicilio fuera de los países consultados. */
|
|
28
|
+
ProofAddressStatusEnum["PLACES_NO_MATCH"] = "PLACES_NO_MATCH";
|
|
29
|
+
/** No se pudo traer la evidencia de Metamap (timeout, 5xx). Reintentable. */
|
|
30
|
+
ProofAddressStatusEnum["METAMAP_ERROR"] = "METAMAP_ERROR";
|
|
31
|
+
/** Falló `places-business`. Reintentable. */
|
|
32
|
+
ProofAddressStatusEnum["PLACES_ERROR"] = "PLACES_ERROR";
|
|
33
|
+
/** Falló `fiado-address-lambda` al guardar el domicilio. Reintentable. */
|
|
34
|
+
ProofAddressStatusEnum["ADDRESS_SAVE_ERROR"] = "ADDRESS_SAVE_ERROR";
|
|
35
|
+
/** El domicilio se guardó pero falló prender la casilla. Reintentable — el dato está a salvo. */
|
|
36
|
+
ProofAddressStatusEnum["CHECKBOX_ERROR"] = "CHECKBOX_ERROR";
|
|
37
|
+
})(ProofAddressStatusEnum || (exports.ProofAddressStatusEnum = ProofAddressStatusEnum = {}));
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { ProofAddressStatusEnum } from '../enums/ProofAddressStatusEnum';
|
|
2
|
+
/**
|
|
3
|
+
* El resultado del COMPROBANTE DE DOMICILIO de Metamap, publicado por `kyc-metamap-webhook` cuando
|
|
4
|
+
* termina de procesarlo. Es un flow aparte del documento de identidad.
|
|
5
|
+
*
|
|
6
|
+
* **Convive con `KycVerificationChangedV1` y `KycStepCompletedV1` en la MISMA cola**
|
|
7
|
+
* (`RetailKycInboundQueue`). El discriminador es `eventType`.
|
|
8
|
+
*
|
|
9
|
+
* 🔴 **Sin PII, a propósito.** No lleva el domicilio ni el texto del recibo: solo identificadores
|
|
10
|
+
* operacionales y el veredicto. Quien necesite el domicilio lo lee del expediente del cliente.
|
|
11
|
+
*
|
|
12
|
+
* **Idempotencia:** la clave es `verificationId`. Un comprobante se procesa una sola vez por
|
|
13
|
+
* verificación; reprocesar el evento escribe el mismo valor.
|
|
14
|
+
*
|
|
15
|
+
* SureKeep Fase 2 — comprobante de domicilio del paso 02.
|
|
16
|
+
*/
|
|
17
|
+
export declare class KycProofOfAddressCompletedV1 {
|
|
18
|
+
/**
|
|
19
|
+
* Discriminador del tipo de evento. **Requerido**, a diferencia de `KycStepCompletedV1`: ese es
|
|
20
|
+
* opcional por los mensajes que ya estaban en vuelo, éste nace hoy y puede exigirlo desde el primero.
|
|
21
|
+
*/
|
|
22
|
+
eventType: string;
|
|
23
|
+
/** Correlaciona el comprobante con su verificación de Metamap. */
|
|
24
|
+
verificationId: string;
|
|
25
|
+
/**
|
|
26
|
+
* Con esto el consumidor encuentra la sesión (el gate de pertenencia). `@IsNotEmpty()` y no solo
|
|
27
|
+
* `@IsString()`: un `''` colado fallaría tres pasos río abajo en vez de en el boundary.
|
|
28
|
+
*/
|
|
29
|
+
directoryId: string;
|
|
30
|
+
/** Identidad Fiado del cliente. Opcional: el productor publica `''` cuando Metamap no lo manda. */
|
|
31
|
+
peopleId?: string;
|
|
32
|
+
/** Por qué el comprobante quedó como quedó — el motivo legible, no solo si sirvió. */
|
|
33
|
+
status: ProofAddressStatusEnum;
|
|
34
|
+
/**
|
|
35
|
+
* ¿Quedó un domicilio en el expediente del cliente? Va explícito aunque sea derivable del
|
|
36
|
+
* `status`: sin él cada consumidor reimplementaría el mapa de 12 casos que conoce el productor.
|
|
37
|
+
*/
|
|
38
|
+
addressPersisted: boolean;
|
|
39
|
+
/**
|
|
40
|
+
* Cuándo se cerró el comprobante. **Obligatorio:** la cola es SQS estándar, que no garantiza
|
|
41
|
+
* orden, y el consumidor descarta lo que llega más viejo que lo que ya escribió.
|
|
42
|
+
*
|
|
43
|
+
* ⚠️ **UTC estricto, con `Z` y sin offset** — por eso el `@Matches` además del `@IsISO8601()`.
|
|
44
|
+
* `@IsISO8601()` solo acepta `2026-08-11T15:05:00+05:00`, y ese es el que hace daño: léxicamente
|
|
45
|
+
* es "mayor" que un `T10:06:00Z` posterior, así que el consumidor descarta el evento real y nada
|
|
46
|
+
* truena. Normalizar a UTC es trabajo del webhook; este candado lo manda al DLQ si deja de hacerlo.
|
|
47
|
+
*/
|
|
48
|
+
occurredAt: string;
|
|
49
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
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.KycProofOfAddressCompletedV1 = void 0;
|
|
13
|
+
const class_transformer_1 = require("class-transformer");
|
|
14
|
+
const class_validator_1 = require("class-validator");
|
|
15
|
+
const ProofAddressStatusEnum_1 = require("../enums/ProofAddressStatusEnum");
|
|
16
|
+
/**
|
|
17
|
+
* El resultado del COMPROBANTE DE DOMICILIO de Metamap, publicado por `kyc-metamap-webhook` cuando
|
|
18
|
+
* termina de procesarlo. Es un flow aparte del documento de identidad.
|
|
19
|
+
*
|
|
20
|
+
* **Convive con `KycVerificationChangedV1` y `KycStepCompletedV1` en la MISMA cola**
|
|
21
|
+
* (`RetailKycInboundQueue`). El discriminador es `eventType`.
|
|
22
|
+
*
|
|
23
|
+
* 🔴 **Sin PII, a propósito.** No lleva el domicilio ni el texto del recibo: solo identificadores
|
|
24
|
+
* operacionales y el veredicto. Quien necesite el domicilio lo lee del expediente del cliente.
|
|
25
|
+
*
|
|
26
|
+
* **Idempotencia:** la clave es `verificationId`. Un comprobante se procesa una sola vez por
|
|
27
|
+
* verificación; reprocesar el evento escribe el mismo valor.
|
|
28
|
+
*
|
|
29
|
+
* SureKeep Fase 2 — comprobante de domicilio del paso 02.
|
|
30
|
+
*/
|
|
31
|
+
class KycProofOfAddressCompletedV1 {
|
|
32
|
+
}
|
|
33
|
+
exports.KycProofOfAddressCompletedV1 = KycProofOfAddressCompletedV1;
|
|
34
|
+
__decorate([
|
|
35
|
+
(0, class_transformer_1.Expose)(),
|
|
36
|
+
(0, class_validator_1.IsIn)(['KycProofOfAddressCompletedV1']),
|
|
37
|
+
__metadata("design:type", String)
|
|
38
|
+
], KycProofOfAddressCompletedV1.prototype, "eventType", void 0);
|
|
39
|
+
__decorate([
|
|
40
|
+
(0, class_transformer_1.Expose)(),
|
|
41
|
+
(0, class_validator_1.IsString)(),
|
|
42
|
+
(0, class_validator_1.IsNotEmpty)(),
|
|
43
|
+
__metadata("design:type", String)
|
|
44
|
+
], KycProofOfAddressCompletedV1.prototype, "verificationId", void 0);
|
|
45
|
+
__decorate([
|
|
46
|
+
(0, class_transformer_1.Expose)(),
|
|
47
|
+
(0, class_validator_1.IsString)(),
|
|
48
|
+
(0, class_validator_1.IsNotEmpty)(),
|
|
49
|
+
__metadata("design:type", String)
|
|
50
|
+
], KycProofOfAddressCompletedV1.prototype, "directoryId", void 0);
|
|
51
|
+
__decorate([
|
|
52
|
+
(0, class_transformer_1.Expose)(),
|
|
53
|
+
(0, class_validator_1.IsOptional)(),
|
|
54
|
+
(0, class_validator_1.IsString)(),
|
|
55
|
+
__metadata("design:type", String)
|
|
56
|
+
], KycProofOfAddressCompletedV1.prototype, "peopleId", void 0);
|
|
57
|
+
__decorate([
|
|
58
|
+
(0, class_transformer_1.Expose)(),
|
|
59
|
+
(0, class_validator_1.IsEnum)(ProofAddressStatusEnum_1.ProofAddressStatusEnum),
|
|
60
|
+
__metadata("design:type", String)
|
|
61
|
+
], KycProofOfAddressCompletedV1.prototype, "status", void 0);
|
|
62
|
+
__decorate([
|
|
63
|
+
(0, class_transformer_1.Expose)(),
|
|
64
|
+
(0, class_validator_1.IsBoolean)(),
|
|
65
|
+
__metadata("design:type", Boolean)
|
|
66
|
+
], KycProofOfAddressCompletedV1.prototype, "addressPersisted", void 0);
|
|
67
|
+
__decorate([
|
|
68
|
+
(0, class_transformer_1.Expose)(),
|
|
69
|
+
(0, class_validator_1.IsString)(),
|
|
70
|
+
(0, class_validator_1.IsISO8601)({ strict: true }),
|
|
71
|
+
(0, class_validator_1.Matches)(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?Z$/, {
|
|
72
|
+
message: 'occurredAt debe ser ISO-8601 en UTC con Z (ej. 2026-08-11T10:05:00.000Z): un offset rompe el orden léxico',
|
|
73
|
+
}),
|
|
74
|
+
__metadata("design:type", String)
|
|
75
|
+
], KycProofOfAddressCompletedV1.prototype, "occurredAt", void 0);
|
package/bin/kyc/index.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ export * from './enums/KycVerificationStatusEnum';
|
|
|
2
2
|
export * from './enums/KycSubCheckStatusEnum';
|
|
3
3
|
export * from './enums/KycWatchlistStatusEnum';
|
|
4
4
|
export * from './enums/KycSubCheckNameEnum';
|
|
5
|
+
export * from './enums/ProofAddressStatusEnum';
|
|
5
6
|
export * from './dtos/KycSubChecks';
|
|
6
7
|
export * from './validators/IsKycStepStatusByStep';
|
|
7
8
|
export * from './helpers/applyKycStep';
|
|
@@ -10,3 +11,4 @@ export * from './dtos/responses/KycPersonData';
|
|
|
10
11
|
export * from './dtos/responses/KycByDirectoryResponse';
|
|
11
12
|
export * from './events/KycVerificationChangedV1';
|
|
12
13
|
export * from './events/KycStepCompletedV1';
|
|
14
|
+
export * from './events/KycProofOfAddressCompletedV1';
|
package/bin/kyc/index.js
CHANGED
|
@@ -20,6 +20,7 @@ __exportStar(require("./enums/KycVerificationStatusEnum"), exports);
|
|
|
20
20
|
__exportStar(require("./enums/KycSubCheckStatusEnum"), exports);
|
|
21
21
|
__exportStar(require("./enums/KycWatchlistStatusEnum"), exports);
|
|
22
22
|
__exportStar(require("./enums/KycSubCheckNameEnum"), exports);
|
|
23
|
+
__exportStar(require("./enums/ProofAddressStatusEnum"), exports);
|
|
23
24
|
// Sub-pasos descompuestos (SureKeep F2)
|
|
24
25
|
__exportStar(require("./dtos/KycSubChecks"), exports);
|
|
25
26
|
// Validators
|
|
@@ -34,3 +35,4 @@ __exportStar(require("./dtos/responses/KycByDirectoryResponse"), exports);
|
|
|
34
35
|
// Events
|
|
35
36
|
__exportStar(require("./events/KycVerificationChangedV1"), exports);
|
|
36
37
|
__exportStar(require("./events/KycStepCompletedV1"), exports);
|
|
38
|
+
__exportStar(require("./events/KycProofOfAddressCompletedV1"), exports);
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { KycModalityInput } from './KycPrepareRequest';
|
|
2
|
+
/**
|
|
3
|
+
* POST /wizard/sessions/:id/proof-of-address/prepare — prepara el flujo del COMPROBANTE DE
|
|
4
|
+
* DOMICILIO de Metamap. Gemelo de `KycPrepareRequest` para el otro flow.
|
|
5
|
+
* `modality` = link al cliente o captura en tablet.
|
|
6
|
+
* SureKeep Fase 2 — pista Retail.
|
|
7
|
+
*/
|
|
8
|
+
export declare class ProofOfAddressPrepareRequest {
|
|
9
|
+
modality: KycModalityInput;
|
|
10
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
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.ProofOfAddressPrepareRequest = void 0;
|
|
13
|
+
const class_transformer_1 = require("class-transformer");
|
|
14
|
+
const class_validator_1 = require("class-validator");
|
|
15
|
+
/**
|
|
16
|
+
* POST /wizard/sessions/:id/proof-of-address/prepare — prepara el flujo del COMPROBANTE DE
|
|
17
|
+
* DOMICILIO de Metamap. Gemelo de `KycPrepareRequest` para el otro flow.
|
|
18
|
+
* `modality` = link al cliente o captura en tablet.
|
|
19
|
+
* SureKeep Fase 2 — pista Retail.
|
|
20
|
+
*/
|
|
21
|
+
class ProofOfAddressPrepareRequest {
|
|
22
|
+
}
|
|
23
|
+
exports.ProofOfAddressPrepareRequest = ProofOfAddressPrepareRequest;
|
|
24
|
+
__decorate([
|
|
25
|
+
(0, class_transformer_1.Expose)(),
|
|
26
|
+
(0, class_validator_1.IsIn)(['CUSTOMER_LINK', 'STORE_DEVICE']),
|
|
27
|
+
__metadata("design:type", String)
|
|
28
|
+
], ProofOfAddressPrepareRequest.prototype, "modality", void 0);
|
|
@@ -47,6 +47,21 @@ export interface KycSdkMetadata {
|
|
|
47
47
|
* no código.
|
|
48
48
|
*/
|
|
49
49
|
source?: KycMetadataSourceEnum;
|
|
50
|
+
/**
|
|
51
|
+
* Paso del onboarding que originó la verificación. Es lo único que distingue al comprobante de
|
|
52
|
+
* domicilio del documento de identidad: `kyc-metamap-webhook` rutea por este campo.
|
|
53
|
+
*/
|
|
54
|
+
kycStep?: KycMetadataStepEnum;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Paso del onboarding que originó una verificación, tal como viaja en `KycSdkMetadata.kycStep`.
|
|
58
|
+
*
|
|
59
|
+
* Enum y no string libre por el mismo motivo que `KycMetadataSourceEnum`: el valor lo escribe un
|
|
60
|
+
* repo (`retail-wizard-business`) y lo compara otro (`kyc-metamap-webhook`). Un typo dejaría al
|
|
61
|
+
* webhook procesando el comprobante como si fuera un documento de identidad, sin un solo error.
|
|
62
|
+
*/
|
|
63
|
+
export declare enum KycMetadataStepEnum {
|
|
64
|
+
PROOF_OF_ADDRESS = "PROOF_OF_ADDRESS"
|
|
50
65
|
}
|
|
51
66
|
/**
|
|
52
67
|
* Producto que originó una verificación KYC, tal como viaja en `KycSdkMetadata.source`.
|
|
@@ -1,6 +1,17 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.KycMetadataSourceEnum = void 0;
|
|
3
|
+
exports.KycMetadataSourceEnum = exports.KycMetadataStepEnum = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Paso del onboarding que originó una verificación, tal como viaja en `KycSdkMetadata.kycStep`.
|
|
6
|
+
*
|
|
7
|
+
* Enum y no string libre por el mismo motivo que `KycMetadataSourceEnum`: el valor lo escribe un
|
|
8
|
+
* repo (`retail-wizard-business`) y lo compara otro (`kyc-metamap-webhook`). Un typo dejaría al
|
|
9
|
+
* webhook procesando el comprobante como si fuera un documento de identidad, sin un solo error.
|
|
10
|
+
*/
|
|
11
|
+
var KycMetadataStepEnum;
|
|
12
|
+
(function (KycMetadataStepEnum) {
|
|
13
|
+
KycMetadataStepEnum["PROOF_OF_ADDRESS"] = "PROOF_OF_ADDRESS";
|
|
14
|
+
})(KycMetadataStepEnum || (exports.KycMetadataStepEnum = KycMetadataStepEnum = {}));
|
|
4
15
|
/**
|
|
5
16
|
* Producto que originó una verificación KYC, tal como viaja en `KycSdkMetadata.source`.
|
|
6
17
|
*
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { KycModalityEnum } from '../../../retailCustomer/enums/KycModalityEnum';
|
|
2
|
+
/**
|
|
3
|
+
* POST /wizard/sessions/:id/proof-of-address/prepare — respuesta. Gemelo de `KycPrepareResponse`
|
|
4
|
+
* para el flow del comprobante de domicilio; la metadata del SDK es la misma `KycSdkMetadata` y la
|
|
5
|
+
* marca de que es comprobante viaja en su `kycStep`.
|
|
6
|
+
*
|
|
7
|
+
* **Bifurca por modalidad**, por eso todo es opcional salvo `modality`:
|
|
8
|
+
* - `STORE_DEVICE` → `clientId` + `flowId` + `metadata` para montar el `<metamap-button>` en la tablet.
|
|
9
|
+
* - `CUSTOMER_LINK` → `shortUrl` para el QR / SMS al teléfono del cliente.
|
|
10
|
+
*
|
|
11
|
+
* SureKeep Fase 2 — pista Retail.
|
|
12
|
+
*/
|
|
13
|
+
export interface ProofOfAddressPrepareResponse {
|
|
14
|
+
/** `MANUAL` no es una modalidad de `prepare`: la captura manual es su propio endpoint. */
|
|
15
|
+
modality: KycModalityEnum.CUSTOMER_LINK | KycModalityEnum.STORE_DEVICE;
|
|
16
|
+
/** STORE_DEVICE — clientId de la cuenta Metamap (mismo valor en prod y dev). */
|
|
17
|
+
clientId?: string;
|
|
18
|
+
/** STORE_DEVICE — flow de Metamap del comprobante de domicilio. */
|
|
19
|
+
flowId?: string;
|
|
20
|
+
/** STORE_DEVICE — `KycSdkMetadata` serializada en base64. */
|
|
21
|
+
metadata?: string;
|
|
22
|
+
/** CUSTOMER_LINK — link corto para el QR o el SMS. */
|
|
23
|
+
shortUrl?: string;
|
|
24
|
+
/** El expediente ya tiene domicilio principal: no hay comprobante que pedir. */
|
|
25
|
+
alreadyHasAddress?: boolean;
|
|
26
|
+
}
|
|
@@ -40,6 +40,7 @@ export * from './dtos/requests/ConsentsRequest';
|
|
|
40
40
|
export * from './dtos/requests/KycPrepareRequest';
|
|
41
41
|
export * from './dtos/requests/KycConfirmRequest';
|
|
42
42
|
export * from './dtos/requests/KycManualRequest';
|
|
43
|
+
export * from './dtos/requests/ProofOfAddressPrepareRequest';
|
|
43
44
|
export * from './dtos/requests/OccupationRequest';
|
|
44
45
|
export * from './dtos/requests/AddProductRequest';
|
|
45
46
|
export * from './dtos/requests/DeleteProductRequest';
|
|
@@ -63,6 +64,7 @@ export * from './dtos/requests/DownPaymentRequest';
|
|
|
63
64
|
export * from './dtos/requests/SignatureSignRequest';
|
|
64
65
|
export * from './dtos/requests/RegisterDeviceRequest';
|
|
65
66
|
export * from './dtos/responses/KycPrepareResponse';
|
|
67
|
+
export * from './dtos/responses/ProofOfAddressPrepareResponse';
|
|
66
68
|
export * from './dtos/responses/SessionFinanciersResponse';
|
|
67
69
|
export * from './dtos/responses/ScoreConsultResponse';
|
|
68
70
|
export * from './dtos/responses/ScoreResultResponse';
|
|
@@ -62,6 +62,7 @@ __exportStar(require("./dtos/requests/ConsentsRequest"), exports);
|
|
|
62
62
|
__exportStar(require("./dtos/requests/KycPrepareRequest"), exports);
|
|
63
63
|
__exportStar(require("./dtos/requests/KycConfirmRequest"), exports);
|
|
64
64
|
__exportStar(require("./dtos/requests/KycManualRequest"), exports);
|
|
65
|
+
__exportStar(require("./dtos/requests/ProofOfAddressPrepareRequest"), exports);
|
|
65
66
|
__exportStar(require("./dtos/requests/OccupationRequest"), exports);
|
|
66
67
|
__exportStar(require("./dtos/requests/AddProductRequest"), exports);
|
|
67
68
|
__exportStar(require("./dtos/requests/DeleteProductRequest"), exports);
|
|
@@ -86,6 +87,7 @@ __exportStar(require("./dtos/requests/SignatureSignRequest"), exports);
|
|
|
86
87
|
__exportStar(require("./dtos/requests/RegisterDeviceRequest"), exports);
|
|
87
88
|
// Response DTOs (output tipado por endpoint)
|
|
88
89
|
__exportStar(require("./dtos/responses/KycPrepareResponse"), exports);
|
|
90
|
+
__exportStar(require("./dtos/responses/ProofOfAddressPrepareResponse"), exports);
|
|
89
91
|
__exportStar(require("./dtos/responses/SessionFinanciersResponse"), exports);
|
|
90
92
|
__exportStar(require("./dtos/responses/ScoreConsultResponse"), exports);
|
|
91
93
|
__exportStar(require("./dtos/responses/ScoreResultResponse"), exports);
|
package/package.json
CHANGED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resultado del procesamiento del comprobante de domicilio de Metamap: "¿pudimos usar el
|
|
3
|
+
* comprobante?", que no es lo mismo que "¿Metamap aprobó el documento?".
|
|
4
|
+
*
|
|
5
|
+
* Espeja `@fiado/fiado-abstractions` (`Fiado/Identity/ProofAddressStatusEnum`) porque los
|
|
6
|
+
* consumidores de SureKeep no dependen de esa lib.
|
|
7
|
+
*/
|
|
8
|
+
export enum ProofAddressStatusEnum {
|
|
9
|
+
/** Domicilio de México o Estados Unidos resuelto a nivel calle y guardado con sus componentes. */
|
|
10
|
+
SUCCESS = "SUCCESS",
|
|
11
|
+
|
|
12
|
+
/** Domicilio mexicano que el geocoder no resolvió a nivel calle. Se guardó el texto del recibo para revisión manual. */
|
|
13
|
+
MEX_HELP_NEEDED = "MEX_HELP_NEEDED",
|
|
14
|
+
|
|
15
|
+
/** Igual que `MEX_HELP_NEEDED`, para un domicilio estadounidense. */
|
|
16
|
+
USA_HELP_NEEDED = "USA_HELP_NEEDED",
|
|
17
|
+
|
|
18
|
+
/** El usuario declaró un país en Metamap y el domicilio resolvió al otro. Requiere revisión. */
|
|
19
|
+
COUNTRY_MISMATCH = "COUNTRY_MISMATCH",
|
|
20
|
+
|
|
21
|
+
/** El domicilio es de un país fuera de México y Estados Unidos. Se guarda pero NO habilita el requisito. */
|
|
22
|
+
COUNTRY_NOT_SUPPORTED = "COUNTRY_NOT_SUPPORTED",
|
|
23
|
+
|
|
24
|
+
/** Metamap mandó el documento a revisión manual. No se procesa hasta tener su veredicto. */
|
|
25
|
+
PENDING_METAMAP_REVIEW = "PENDING_METAMAP_REVIEW",
|
|
26
|
+
|
|
27
|
+
/** El OCR de Metamap no devolvió domicilio: el comprobante es ilegible o no es un comprobante. */
|
|
28
|
+
NO_ADDRESS_DETECTED = "NO_ADDRESS_DETECTED",
|
|
29
|
+
|
|
30
|
+
/** El geocoder respondió sin candidatos. Texto ilegible o domicilio fuera de los países consultados. */
|
|
31
|
+
PLACES_NO_MATCH = "PLACES_NO_MATCH",
|
|
32
|
+
|
|
33
|
+
/** No se pudo traer la evidencia de Metamap (timeout, 5xx). Reintentable. */
|
|
34
|
+
METAMAP_ERROR = "METAMAP_ERROR",
|
|
35
|
+
|
|
36
|
+
/** Falló `places-business`. Reintentable. */
|
|
37
|
+
PLACES_ERROR = "PLACES_ERROR",
|
|
38
|
+
|
|
39
|
+
/** Falló `fiado-address-lambda` al guardar el domicilio. Reintentable. */
|
|
40
|
+
ADDRESS_SAVE_ERROR = "ADDRESS_SAVE_ERROR",
|
|
41
|
+
|
|
42
|
+
/** El domicilio se guardó pero falló prender la casilla. Reintentable — el dato está a salvo. */
|
|
43
|
+
CHECKBOX_ERROR = "CHECKBOX_ERROR",
|
|
44
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { Expose } from 'class-transformer';
|
|
2
|
+
import { IsBoolean, IsEnum, IsIn, IsISO8601, IsNotEmpty, IsOptional, IsString, Matches } from 'class-validator';
|
|
3
|
+
import { ProofAddressStatusEnum } from '../enums/ProofAddressStatusEnum';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* El resultado del COMPROBANTE DE DOMICILIO de Metamap, publicado por `kyc-metamap-webhook` cuando
|
|
7
|
+
* termina de procesarlo. Es un flow aparte del documento de identidad.
|
|
8
|
+
*
|
|
9
|
+
* **Convive con `KycVerificationChangedV1` y `KycStepCompletedV1` en la MISMA cola**
|
|
10
|
+
* (`RetailKycInboundQueue`). El discriminador es `eventType`.
|
|
11
|
+
*
|
|
12
|
+
* 🔴 **Sin PII, a propósito.** No lleva el domicilio ni el texto del recibo: solo identificadores
|
|
13
|
+
* operacionales y el veredicto. Quien necesite el domicilio lo lee del expediente del cliente.
|
|
14
|
+
*
|
|
15
|
+
* **Idempotencia:** la clave es `verificationId`. Un comprobante se procesa una sola vez por
|
|
16
|
+
* verificación; reprocesar el evento escribe el mismo valor.
|
|
17
|
+
*
|
|
18
|
+
* SureKeep Fase 2 — comprobante de domicilio del paso 02.
|
|
19
|
+
*/
|
|
20
|
+
export class KycProofOfAddressCompletedV1 {
|
|
21
|
+
/**
|
|
22
|
+
* Discriminador del tipo de evento. **Requerido**, a diferencia de `KycStepCompletedV1`: ese es
|
|
23
|
+
* opcional por los mensajes que ya estaban en vuelo, éste nace hoy y puede exigirlo desde el primero.
|
|
24
|
+
*/
|
|
25
|
+
@Expose() @IsIn(['KycProofOfAddressCompletedV1'])
|
|
26
|
+
eventType!: string;
|
|
27
|
+
|
|
28
|
+
/** Correlaciona el comprobante con su verificación de Metamap. */
|
|
29
|
+
@Expose() @IsString() @IsNotEmpty()
|
|
30
|
+
verificationId!: string;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Con esto el consumidor encuentra la sesión (el gate de pertenencia). `@IsNotEmpty()` y no solo
|
|
34
|
+
* `@IsString()`: un `''` colado fallaría tres pasos río abajo en vez de en el boundary.
|
|
35
|
+
*/
|
|
36
|
+
@Expose() @IsString() @IsNotEmpty()
|
|
37
|
+
directoryId!: string;
|
|
38
|
+
|
|
39
|
+
/** Identidad Fiado del cliente. Opcional: el productor publica `''` cuando Metamap no lo manda. */
|
|
40
|
+
@Expose() @IsOptional() @IsString()
|
|
41
|
+
peopleId?: string;
|
|
42
|
+
|
|
43
|
+
/** Por qué el comprobante quedó como quedó — el motivo legible, no solo si sirvió. */
|
|
44
|
+
@Expose() @IsEnum(ProofAddressStatusEnum)
|
|
45
|
+
status!: ProofAddressStatusEnum;
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* ¿Quedó un domicilio en el expediente del cliente? Va explícito aunque sea derivable del
|
|
49
|
+
* `status`: sin él cada consumidor reimplementaría el mapa de 12 casos que conoce el productor.
|
|
50
|
+
*/
|
|
51
|
+
@Expose() @IsBoolean()
|
|
52
|
+
addressPersisted!: boolean;
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Cuándo se cerró el comprobante. **Obligatorio:** la cola es SQS estándar, que no garantiza
|
|
56
|
+
* orden, y el consumidor descarta lo que llega más viejo que lo que ya escribió.
|
|
57
|
+
*
|
|
58
|
+
* ⚠️ **UTC estricto, con `Z` y sin offset** — por eso el `@Matches` además del `@IsISO8601()`.
|
|
59
|
+
* `@IsISO8601()` solo acepta `2026-08-11T15:05:00+05:00`, y ese es el que hace daño: léxicamente
|
|
60
|
+
* es "mayor" que un `T10:06:00Z` posterior, así que el consumidor descarta el evento real y nada
|
|
61
|
+
* truena. Normalizar a UTC es trabajo del webhook; este candado lo manda al DLQ si deja de hacerlo.
|
|
62
|
+
*/
|
|
63
|
+
@Expose() @IsString() @IsISO8601({ strict: true })
|
|
64
|
+
@Matches(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?Z$/, {
|
|
65
|
+
message: 'occurredAt debe ser ISO-8601 en UTC con Z (ej. 2026-08-11T10:05:00.000Z): un offset rompe el orden léxico',
|
|
66
|
+
})
|
|
67
|
+
occurredAt!: string;
|
|
68
|
+
}
|
package/src/kyc/index.ts
CHANGED
|
@@ -4,6 +4,7 @@ export * from './enums/KycVerificationStatusEnum';
|
|
|
4
4
|
export * from './enums/KycSubCheckStatusEnum';
|
|
5
5
|
export * from './enums/KycWatchlistStatusEnum';
|
|
6
6
|
export * from './enums/KycSubCheckNameEnum';
|
|
7
|
+
export * from './enums/ProofAddressStatusEnum';
|
|
7
8
|
|
|
8
9
|
// Sub-pasos descompuestos (SureKeep F2)
|
|
9
10
|
export * from './dtos/KycSubChecks';
|
|
@@ -24,3 +25,4 @@ export * from './dtos/responses/KycByDirectoryResponse';
|
|
|
24
25
|
// Events
|
|
25
26
|
export * from './events/KycVerificationChangedV1';
|
|
26
27
|
export * from './events/KycStepCompletedV1';
|
|
28
|
+
export * from './events/KycProofOfAddressCompletedV1';
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Expose } from 'class-transformer';
|
|
2
|
+
import { IsIn } from 'class-validator';
|
|
3
|
+
import { KycModalityInput } from './KycPrepareRequest';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* POST /wizard/sessions/:id/proof-of-address/prepare — prepara el flujo del COMPROBANTE DE
|
|
7
|
+
* DOMICILIO de Metamap. Gemelo de `KycPrepareRequest` para el otro flow.
|
|
8
|
+
* `modality` = link al cliente o captura en tablet.
|
|
9
|
+
* SureKeep Fase 2 — pista Retail.
|
|
10
|
+
*/
|
|
11
|
+
export class ProofOfAddressPrepareRequest {
|
|
12
|
+
@Expose()
|
|
13
|
+
@IsIn(['CUSTOMER_LINK', 'STORE_DEVICE'])
|
|
14
|
+
modality!: KycModalityInput;
|
|
15
|
+
}
|
|
@@ -48,6 +48,22 @@ export interface KycSdkMetadata {
|
|
|
48
48
|
* no código.
|
|
49
49
|
*/
|
|
50
50
|
source?: KycMetadataSourceEnum;
|
|
51
|
+
/**
|
|
52
|
+
* Paso del onboarding que originó la verificación. Es lo único que distingue al comprobante de
|
|
53
|
+
* domicilio del documento de identidad: `kyc-metamap-webhook` rutea por este campo.
|
|
54
|
+
*/
|
|
55
|
+
kycStep?: KycMetadataStepEnum;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Paso del onboarding que originó una verificación, tal como viaja en `KycSdkMetadata.kycStep`.
|
|
60
|
+
*
|
|
61
|
+
* Enum y no string libre por el mismo motivo que `KycMetadataSourceEnum`: el valor lo escribe un
|
|
62
|
+
* repo (`retail-wizard-business`) y lo compara otro (`kyc-metamap-webhook`). Un typo dejaría al
|
|
63
|
+
* webhook procesando el comprobante como si fuera un documento de identidad, sin un solo error.
|
|
64
|
+
*/
|
|
65
|
+
export enum KycMetadataStepEnum {
|
|
66
|
+
PROOF_OF_ADDRESS = 'PROOF_OF_ADDRESS',
|
|
51
67
|
}
|
|
52
68
|
|
|
53
69
|
/**
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { KycModalityEnum } from '../../../retailCustomer/enums/KycModalityEnum';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* POST /wizard/sessions/:id/proof-of-address/prepare — respuesta. Gemelo de `KycPrepareResponse`
|
|
5
|
+
* para el flow del comprobante de domicilio; la metadata del SDK es la misma `KycSdkMetadata` y la
|
|
6
|
+
* marca de que es comprobante viaja en su `kycStep`.
|
|
7
|
+
*
|
|
8
|
+
* **Bifurca por modalidad**, por eso todo es opcional salvo `modality`:
|
|
9
|
+
* - `STORE_DEVICE` → `clientId` + `flowId` + `metadata` para montar el `<metamap-button>` en la tablet.
|
|
10
|
+
* - `CUSTOMER_LINK` → `shortUrl` para el QR / SMS al teléfono del cliente.
|
|
11
|
+
*
|
|
12
|
+
* SureKeep Fase 2 — pista Retail.
|
|
13
|
+
*/
|
|
14
|
+
export interface ProofOfAddressPrepareResponse {
|
|
15
|
+
/** `MANUAL` no es una modalidad de `prepare`: la captura manual es su propio endpoint. */
|
|
16
|
+
modality: KycModalityEnum.CUSTOMER_LINK | KycModalityEnum.STORE_DEVICE;
|
|
17
|
+
/** STORE_DEVICE — clientId de la cuenta Metamap (mismo valor en prod y dev). */
|
|
18
|
+
clientId?: string;
|
|
19
|
+
/** STORE_DEVICE — flow de Metamap del comprobante de domicilio. */
|
|
20
|
+
flowId?: string;
|
|
21
|
+
/** STORE_DEVICE — `KycSdkMetadata` serializada en base64. */
|
|
22
|
+
metadata?: string;
|
|
23
|
+
/** CUSTOMER_LINK — link corto para el QR o el SMS. */
|
|
24
|
+
shortUrl?: string;
|
|
25
|
+
/** El expediente ya tiene domicilio principal: no hay comprobante que pedir. */
|
|
26
|
+
alreadyHasAddress?: boolean;
|
|
27
|
+
}
|
|
@@ -51,6 +51,7 @@ export * from './dtos/requests/ConsentsRequest';
|
|
|
51
51
|
export * from './dtos/requests/KycPrepareRequest';
|
|
52
52
|
export * from './dtos/requests/KycConfirmRequest';
|
|
53
53
|
export * from './dtos/requests/KycManualRequest';
|
|
54
|
+
export * from './dtos/requests/ProofOfAddressPrepareRequest';
|
|
54
55
|
export * from './dtos/requests/OccupationRequest';
|
|
55
56
|
export * from './dtos/requests/AddProductRequest';
|
|
56
57
|
export * from './dtos/requests/DeleteProductRequest';
|
|
@@ -76,6 +77,7 @@ export * from './dtos/requests/RegisterDeviceRequest';
|
|
|
76
77
|
|
|
77
78
|
// Response DTOs (output tipado por endpoint)
|
|
78
79
|
export * from './dtos/responses/KycPrepareResponse';
|
|
80
|
+
export * from './dtos/responses/ProofOfAddressPrepareResponse';
|
|
79
81
|
export * from './dtos/responses/SessionFinanciersResponse';
|
|
80
82
|
export * from './dtos/responses/ScoreConsultResponse';
|
|
81
83
|
export * from './dtos/responses/ScoreResultResponse';
|