@fiado/type-kit 3.536.0 → 3.537.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 (45) hide show
  1. package/_test_/unit/loanCredit/creditLifecycleEnums.test.ts +2 -1
  2. package/_test_/unit/loanCredit/signLoanCreditRequest.test.ts +37 -0
  3. package/_test_/unit/messagesConnector/otpBinding.test.ts +43 -0
  4. package/_test_/unit/retailWizard/signatureSendOtpRequest.test.ts +25 -0
  5. package/bin/loanCredit/dtos/LoanContract.d.ts +5 -1
  6. package/bin/loanCredit/dtos/LoanContract.js +8 -1
  7. package/bin/loanCredit/dtos/LoanContractSignatureEvidence.d.ts +45 -0
  8. package/bin/loanCredit/dtos/LoanContractSignatureEvidence.js +2 -0
  9. package/bin/loanCredit/dtos/SignatureContext.d.ts +20 -0
  10. package/bin/loanCredit/dtos/SignatureContext.js +92 -0
  11. package/bin/loanCredit/dtos/requests/SignLoanCreditRequest.d.ts +6 -0
  12. package/bin/loanCredit/dtos/requests/SignLoanCreditRequest.js +8 -0
  13. package/bin/loanCredit/dtos/responses/LoanContractResponse.d.ts +3 -0
  14. package/bin/loanCredit/enums/ContractTypeEnum.d.ts +2 -0
  15. package/bin/loanCredit/enums/ContractTypeEnum.js +2 -0
  16. package/bin/loanCredit/index.d.ts +2 -0
  17. package/bin/loanCredit/index.js +2 -0
  18. package/bin/messagesConnector/constants/OtpBinding.d.ts +2 -0
  19. package/bin/messagesConnector/constants/OtpBinding.js +5 -0
  20. package/bin/messagesConnector/dtos/SendMessageRequest.d.ts +1 -0
  21. package/bin/messagesConnector/dtos/SendMessageRequest.js +7 -0
  22. package/bin/messagesConnector/dtos/VerifyOtpRequest.d.ts +2 -0
  23. package/bin/messagesConnector/dtos/VerifyOtpRequest.js +14 -0
  24. package/bin/messagesConnector/dtos/VerifyOtpResponse.d.ts +6 -0
  25. package/bin/messagesConnector/index.d.ts +1 -0
  26. package/bin/messagesConnector/index.js +1 -0
  27. package/bin/retailWizard/dtos/requests/SignatureSendOtpRequest.d.ts +7 -0
  28. package/bin/retailWizard/dtos/requests/SignatureSendOtpRequest.js +26 -0
  29. package/bin/retailWizard/index.d.ts +1 -0
  30. package/bin/retailWizard/index.js +1 -0
  31. package/package.json +1 -1
  32. package/src/loanCredit/dtos/LoanContract.ts +10 -2
  33. package/src/loanCredit/dtos/LoanContractSignatureEvidence.ts +50 -0
  34. package/src/loanCredit/dtos/SignatureContext.ts +75 -0
  35. package/src/loanCredit/dtos/requests/SignLoanCreditRequest.ts +11 -0
  36. package/src/loanCredit/dtos/responses/LoanContractResponse.ts +3 -0
  37. package/src/loanCredit/enums/ContractTypeEnum.ts +2 -0
  38. package/src/loanCredit/index.ts +2 -0
  39. package/src/messagesConnector/constants/OtpBinding.ts +2 -0
  40. package/src/messagesConnector/dtos/SendMessageRequest.ts +5 -1
  41. package/src/messagesConnector/dtos/VerifyOtpRequest.ts +5 -1
  42. package/src/messagesConnector/dtos/VerifyOtpResponse.ts +6 -0
  43. package/src/messagesConnector/index.ts +1 -0
  44. package/src/retailWizard/dtos/requests/SignatureSendOtpRequest.ts +12 -0
  45. package/src/retailWizard/index.ts +1 -0
@@ -16,12 +16,13 @@ import {
16
16
  const sortedValues = (enumObject: Record<string, string>) => Object.values(enumObject).sort();
17
17
 
18
18
  describe('ContractTypeEnum', () => {
19
- it('suma los documentos del ciclo de vida a los tres de la originación', () => {
19
+ it('suma la constancia de firma y los documentos del ciclo de vida a los tres de la originación', () => {
20
20
  expect(sortedValues(ContractTypeEnum)).toEqual(
21
21
  [
22
22
  'CREDIT',
23
23
  'PROMISSORY_NOTE',
24
24
  'SIC_AUTHORIZATION',
25
+ 'SIGNATURE_EVIDENCE',
25
26
  'RESTRUCTURE_ADDENDUM',
26
27
  'ADVANCE_AUTHORIZATION',
27
28
  'SETTLEMENT_CERTIFICATE',
@@ -86,3 +86,40 @@ describe('SignLoanCreditRequest', () => {
86
86
  expect(await errorsOf({ contractDetails: details })).toEqual(['contractDetails.provider.legalName']);
87
87
  });
88
88
  });
89
+
90
+ const CONTEXT = {
91
+ ip: '201.141.10.20',
92
+ userAgent: 'Mozilla/5.0',
93
+ termsAccepted: true,
94
+ sellerId: 'ru-1',
95
+ sellerActorId: 'sub-1',
96
+ sellerName: 'Ana López',
97
+ storeId: 'st-1',
98
+ storeName: 'Tienda Centro',
99
+ wizardSessionId: 'WZ-1',
100
+ };
101
+
102
+ describe('SignLoanCreditRequest.signatureContext', () => {
103
+ it('acepta el contexto completo', async () => {
104
+ expect(await errorsOf({ signatureContext: CONTEXT })).toEqual([]);
105
+ });
106
+
107
+ it('acepta el contexto sin nombres ni navegador', async () => {
108
+ const minimal = { ip: CONTEXT.ip, sellerId: 'ru-1', sellerActorId: 'sub-1', storeId: 'st-1', wizardSessionId: 'WZ-1' };
109
+ expect(await errorsOf({ signatureContext: minimal })).toEqual([]);
110
+ });
111
+
112
+ it('exige ip, vendedor, tienda y sesión', async () => {
113
+ expect(await errorsOf({ signatureContext: {} })).toEqual([
114
+ 'signatureContext.ip',
115
+ 'signatureContext.sellerActorId',
116
+ 'signatureContext.sellerId',
117
+ 'signatureContext.storeId',
118
+ 'signatureContext.wizardSessionId',
119
+ ]);
120
+ });
121
+
122
+ it('rechaza una aceptación que no es booleana', async () => {
123
+ expect(await errorsOf({ signatureContext: { ...CONTEXT, termsAccepted: 'si' } })).toEqual(['signatureContext.termsAccepted']);
124
+ });
125
+ });
@@ -0,0 +1,43 @@
1
+ import 'reflect-metadata';
2
+ import { plainToInstance } from 'class-transformer';
3
+ import { validate } from 'class-validator';
4
+ import { SendMessageRequest, VerifyOtpRequest } from '../../../src/messagesConnector/index';
5
+ import { DeliveryChannelEnum } from '../../../src/messaging';
6
+
7
+ const HASH = 'a'.repeat(64);
8
+ const SEND = {
9
+ channelType: DeliveryChannelEnum.SMS,
10
+ messageType: 'CONTRACT_SIGNATURE_OTP',
11
+ directoryId: 'd1',
12
+ typeOfDirectory: 'USER',
13
+ language: 'es',
14
+ country: 'MX',
15
+ destination: '+525512345678',
16
+ transactionNumber: 'c-1',
17
+ };
18
+ const VERIFY = { directoryId: 'd1', typeOfDirectoryId: 'USER', message: '1234' };
19
+
20
+ const failing = async (cls: new () => object, body: Record<string, unknown>): Promise<string[]> =>
21
+ (await validate(plainToInstance(cls, body))).map((error) => error.property).sort();
22
+
23
+ describe('amarre del código de firma', () => {
24
+ it('SendMessageRequest acepta la huella sha256 del documento', async () => {
25
+ expect(await failing(SendMessageRequest, { ...SEND, documentHash: HASH })).toEqual([]);
26
+ });
27
+
28
+ it('SendMessageRequest rechaza una huella que no es sha256 en hexadecimal', async () => {
29
+ expect(await failing(SendMessageRequest, { ...SEND, documentHash: 'ABC' })).toEqual(['documentHash']);
30
+ });
31
+
32
+ it('VerifyOtpRequest acepta el crédito y la huella', async () => {
33
+ expect(await failing(VerifyOtpRequest, { ...VERIFY, transactionNumber: 'c-1', documentHash: HASH })).toEqual([]);
34
+ });
35
+
36
+ it('VerifyOtpRequest sigue aceptando la verificación sin amarre', async () => {
37
+ expect(await failing(VerifyOtpRequest, VERIFY)).toEqual([]);
38
+ });
39
+
40
+ it('VerifyOtpRequest rechaza una huella inválida', async () => {
41
+ expect(await failing(VerifyOtpRequest, { ...VERIFY, documentHash: 'x' })).toEqual(['documentHash']);
42
+ });
43
+ });
@@ -0,0 +1,25 @@
1
+ import 'reflect-metadata';
2
+ import { plainToInstance } from 'class-transformer';
3
+ import { validate } from 'class-validator';
4
+ import { SignatureSendOtpRequest } from '../../../src/retailWizard/index';
5
+
6
+ const failing = async (body: Record<string, unknown>): Promise<string[]> =>
7
+ (await validate(plainToInstance(SignatureSendOtpRequest, body))).map((error) => error.property);
8
+
9
+ describe('SignatureSendOtpRequest', () => {
10
+ it('acepta la aceptación de los términos', async () => {
11
+ expect(await failing({ termsAccepted: true })).toEqual([]);
12
+ });
13
+
14
+ it('rechaza el envío sin aceptación', async () => {
15
+ expect(await failing({})).toEqual(['termsAccepted']);
16
+ });
17
+
18
+ it('rechaza termsAccepted en false', async () => {
19
+ expect(await failing({ termsAccepted: false })).toEqual(['termsAccepted']);
20
+ });
21
+
22
+ it('rechaza "true" como texto', async () => {
23
+ expect(await failing({ termsAccepted: 'true' })).toEqual(['termsAccepted']);
24
+ });
25
+ });
@@ -1,7 +1,9 @@
1
1
  import { ContractTypeEnum } from '../enums/ContractTypeEnum';
2
+ import type { LoanContractSignatureEvidence } from './LoanContractSignatureEvidence';
2
3
  /**
3
4
  * LoanContract — documento del contrato de crédito con firma FES (modelo-datos §5b ·
4
- * `<T>LoanContract_GT` · M2 §4.3-§4.4). Un registro por documento (3 por crédito — C4).
5
+ * `<T>LoanContract_GT` · M2 §4.3-§4.4). Un registro por documento: los 3 de la originación (C4), más la
6
+ * constancia de firma al firmar.
5
7
  *
6
8
  * Nace DRAFT en la originación (sin PDF); la firma FES genera el PDF, calcula su `sha256`,
7
9
  * verifica el OTP y sella con NOM-151. Conservación documental: 10 años (NOM-151).
@@ -22,6 +24,8 @@ export declare class LoanContract {
22
24
  /** Sello de tiempo NOM-151 (TSA). Ausente hasta firmar (AS del TSA connector). */
23
25
  nom151Timestamp?: string;
24
26
  signedAt?: string;
27
+ /** Evidencia de la firma con código (solo en documentos firmados). */
28
+ signatureEvidence?: LoanContractSignatureEvidence;
25
29
  createdBy: string;
26
30
  updatedBy: string;
27
31
  createdAt: string;
@@ -16,7 +16,8 @@ const regex_1 = require("../../helpers/constans/regex");
16
16
  const ContractTypeEnum_1 = require("../enums/ContractTypeEnum");
17
17
  /**
18
18
  * LoanContract — documento del contrato de crédito con firma FES (modelo-datos §5b ·
19
- * `<T>LoanContract_GT` · M2 §4.3-§4.4). Un registro por documento (3 por crédito — C4).
19
+ * `<T>LoanContract_GT` · M2 §4.3-§4.4). Un registro por documento: los 3 de la originación (C4), más la
20
+ * constancia de firma al firmar.
20
21
  *
21
22
  * Nace DRAFT en la originación (sin PDF); la firma FES genera el PDF, calcula su `sha256`,
22
23
  * verifica el OTP y sella con NOM-151. Conservación documental: 10 años (NOM-151).
@@ -75,6 +76,12 @@ __decorate([
75
76
  (0, class_validator_1.Matches)(regex_1.regexIso8601),
76
77
  __metadata("design:type", String)
77
78
  ], LoanContract.prototype, "signedAt", void 0);
79
+ __decorate([
80
+ (0, class_transformer_1.Expose)(),
81
+ (0, class_validator_1.IsOptional)(),
82
+ (0, class_validator_1.IsObject)(),
83
+ __metadata("design:type", Object)
84
+ ], LoanContract.prototype, "signatureEvidence", void 0);
78
85
  __decorate([
79
86
  (0, class_transformer_1.Expose)(),
80
87
  (0, class_validator_1.IsString)(),
@@ -0,0 +1,45 @@
1
+ /** El código de firma tal como lo validó messages-business. */
2
+ export interface SignatureEvidenceOtp {
3
+ /** Id del envío en messages-business (fila del log del código). */
4
+ messageLogId: string;
5
+ sentAt: string;
6
+ verifiedAt: string;
7
+ channel: 'SMS';
8
+ /** Teléfono destino enmascarado (`****1234`). */
9
+ destinationMasked?: string;
10
+ }
11
+ /** Envíos del código (con reenvíos) y validaciones fallidas antes de firmar. */
12
+ export interface SignatureEvidenceAttempts {
13
+ sends: number;
14
+ failedVerifications: number;
15
+ }
16
+ /** Aceptación de términos del tiempo 1, amarrada a la huella del contrato generado en esa llamada. */
17
+ export interface SignatureEvidenceAcceptance {
18
+ acceptedAt: string;
19
+ contractSha256: string;
20
+ ip: string;
21
+ userAgent?: string;
22
+ }
23
+ /** Desde dónde y quién envió el código al firmar (tiempo 2). */
24
+ export interface SignatureEvidenceSigning {
25
+ ip: string;
26
+ userAgent?: string;
27
+ sellerActorId: string;
28
+ }
29
+ /** Quién facilitó la firma en la tienda. */
30
+ export interface SignatureEvidenceFacilitator {
31
+ sellerId: string;
32
+ sellerActorId: string;
33
+ sellerName?: string;
34
+ storeId: string;
35
+ storeName?: string;
36
+ wizardSessionId: string;
37
+ }
38
+ /** Evidencia de la firma con código que queda en cada documento firmado. */
39
+ export interface LoanContractSignatureEvidence {
40
+ otp: SignatureEvidenceOtp;
41
+ attempts: SignatureEvidenceAttempts;
42
+ acceptance: SignatureEvidenceAcceptance;
43
+ signing: SignatureEvidenceSigning;
44
+ facilitator: SignatureEvidenceFacilitator;
45
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Contexto de la firma que aporta el wizard en cada tiempo de `POST /private/credits/:creditId/sign`:
3
+ * quién facilita la firma (vendedor, tienda, sesión) y desde qué navegador. Es evidencia de la
4
+ * ceremonia: la IP y el navegador son del equipo de la tienda. Nunca se loguean.
5
+ */
6
+ export declare class SignatureContext {
7
+ /** IP de origen de la llamada, como la ve el API Gateway del wizard. */
8
+ ip: string;
9
+ userAgent?: string;
10
+ /** Tiempo 1: el cliente marcó "Acepto los términos". El motor exige `true` para mandar el código. */
11
+ termsAccepted?: boolean;
12
+ /** Usuario retail del vendedor. */
13
+ sellerId: string;
14
+ /** Cognito sub del vendedor: queda como actor de la firma. */
15
+ sellerActorId: string;
16
+ sellerName?: string;
17
+ storeId: string;
18
+ storeName?: string;
19
+ wizardSessionId: string;
20
+ }
@@ -0,0 +1,92 @@
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.SignatureContext = void 0;
13
+ const class_transformer_1 = require("class-transformer");
14
+ const class_validator_1 = require("class-validator");
15
+ /** Largo máximo de un id operacional (vendedor, tienda, sesión). */
16
+ const ID_TEXT = 128;
17
+ /** Largo máximo de un nombre para mostrar. */
18
+ const NAME_TEXT = 300;
19
+ /** Una IPv6 mide 39 caracteres; se deja margen para el formato mapeado. */
20
+ const IP_TEXT = 64;
21
+ /** Los navegadores mandan User-Agent de 150 a 300 caracteres. */
22
+ const USER_AGENT_TEXT = 512;
23
+ /**
24
+ * Contexto de la firma que aporta el wizard en cada tiempo de `POST /private/credits/:creditId/sign`:
25
+ * quién facilita la firma (vendedor, tienda, sesión) y desde qué navegador. Es evidencia de la
26
+ * ceremonia: la IP y el navegador son del equipo de la tienda. Nunca se loguean.
27
+ */
28
+ class SignatureContext {
29
+ }
30
+ exports.SignatureContext = SignatureContext;
31
+ __decorate([
32
+ (0, class_transformer_1.Expose)(),
33
+ (0, class_validator_1.IsString)(),
34
+ (0, class_validator_1.IsNotEmpty)(),
35
+ (0, class_validator_1.MaxLength)(IP_TEXT),
36
+ __metadata("design:type", String)
37
+ ], SignatureContext.prototype, "ip", void 0);
38
+ __decorate([
39
+ (0, class_transformer_1.Expose)(),
40
+ (0, class_validator_1.IsOptional)(),
41
+ (0, class_validator_1.IsString)(),
42
+ (0, class_validator_1.MaxLength)(USER_AGENT_TEXT),
43
+ __metadata("design:type", String)
44
+ ], SignatureContext.prototype, "userAgent", void 0);
45
+ __decorate([
46
+ (0, class_transformer_1.Expose)(),
47
+ (0, class_validator_1.IsOptional)(),
48
+ (0, class_validator_1.IsBoolean)(),
49
+ __metadata("design:type", Boolean)
50
+ ], SignatureContext.prototype, "termsAccepted", void 0);
51
+ __decorate([
52
+ (0, class_transformer_1.Expose)(),
53
+ (0, class_validator_1.IsString)(),
54
+ (0, class_validator_1.IsNotEmpty)(),
55
+ (0, class_validator_1.MaxLength)(ID_TEXT),
56
+ __metadata("design:type", String)
57
+ ], SignatureContext.prototype, "sellerId", void 0);
58
+ __decorate([
59
+ (0, class_transformer_1.Expose)(),
60
+ (0, class_validator_1.IsString)(),
61
+ (0, class_validator_1.IsNotEmpty)(),
62
+ (0, class_validator_1.MaxLength)(ID_TEXT),
63
+ __metadata("design:type", String)
64
+ ], SignatureContext.prototype, "sellerActorId", void 0);
65
+ __decorate([
66
+ (0, class_transformer_1.Expose)(),
67
+ (0, class_validator_1.IsOptional)(),
68
+ (0, class_validator_1.IsString)(),
69
+ (0, class_validator_1.MaxLength)(NAME_TEXT),
70
+ __metadata("design:type", String)
71
+ ], SignatureContext.prototype, "sellerName", void 0);
72
+ __decorate([
73
+ (0, class_transformer_1.Expose)(),
74
+ (0, class_validator_1.IsString)(),
75
+ (0, class_validator_1.IsNotEmpty)(),
76
+ (0, class_validator_1.MaxLength)(ID_TEXT),
77
+ __metadata("design:type", String)
78
+ ], SignatureContext.prototype, "storeId", void 0);
79
+ __decorate([
80
+ (0, class_transformer_1.Expose)(),
81
+ (0, class_validator_1.IsOptional)(),
82
+ (0, class_validator_1.IsString)(),
83
+ (0, class_validator_1.MaxLength)(NAME_TEXT),
84
+ __metadata("design:type", String)
85
+ ], SignatureContext.prototype, "storeName", void 0);
86
+ __decorate([
87
+ (0, class_transformer_1.Expose)(),
88
+ (0, class_validator_1.IsString)(),
89
+ (0, class_validator_1.IsNotEmpty)(),
90
+ (0, class_validator_1.MaxLength)(ID_TEXT),
91
+ __metadata("design:type", String)
92
+ ], SignatureContext.prototype, "wizardSessionId", void 0);
@@ -1,4 +1,5 @@
1
1
  import { LoanContractDetails } from '../LoanContractDetails';
2
+ import { SignatureContext } from '../SignatureContext';
2
3
  /**
3
4
  * Body de `POST /private/credits/:creditId/sign` (loan-credit-business — wizard paso 08, firma FES
4
5
  * M2 §4.4). Endpoint de DOS tiempos sobre el mismo contrato:
@@ -40,4 +41,9 @@ export declare class SignLoanCreditRequest {
40
41
  * PII-zero — el teléfono vive en RetailCustomer).
41
42
  */
42
43
  otpDestination?: string;
44
+ /**
45
+ * Ambos tiempos: quién facilita la firma y desde qué navegador (evidencia de la ceremonia). En el
46
+ * tiempo 1 trae `termsAccepted: true`. El motor lo exige; la IP y el navegador nunca se loguean.
47
+ */
48
+ signatureContext?: SignatureContext;
43
49
  }
@@ -13,6 +13,7 @@ exports.SignLoanCreditRequest = void 0;
13
13
  const class_transformer_1 = require("class-transformer");
14
14
  const class_validator_1 = require("class-validator");
15
15
  const LoanContractDetails_1 = require("../LoanContractDetails");
16
+ const SignatureContext_1 = require("../SignatureContext");
16
17
  /**
17
18
  * Body de `POST /private/credits/:creditId/sign` (loan-credit-business — wizard paso 08, firma FES
18
19
  * M2 §4.4). Endpoint de DOS tiempos sobre el mismo contrato:
@@ -61,3 +62,10 @@ __decorate([
61
62
  (0, class_validator_1.IsString)(),
62
63
  __metadata("design:type", String)
63
64
  ], SignLoanCreditRequest.prototype, "otpDestination", void 0);
65
+ __decorate([
66
+ (0, class_transformer_1.Expose)(),
67
+ (0, class_validator_1.IsOptional)(),
68
+ (0, class_validator_1.ValidateNested)(),
69
+ (0, class_transformer_1.Type)(() => SignatureContext_1.SignatureContext),
70
+ __metadata("design:type", SignatureContext_1.SignatureContext)
71
+ ], SignLoanCreditRequest.prototype, "signatureContext", void 0);
@@ -1,4 +1,5 @@
1
1
  import { ContractTypeEnum } from '../../enums/ContractTypeEnum';
2
+ import type { LoanContractSignatureEvidence } from '../LoanContractSignatureEvidence';
2
3
  /**
3
4
  * Response de un documento del contrato (loan-credit-business). `signedAt` ausente = draft.
4
5
  */
@@ -12,6 +13,8 @@ export interface LoanContractResponse {
12
13
  otpVerified: boolean;
13
14
  nom151Timestamp?: string;
14
15
  signedAt?: string;
16
+ /** Evidencia de la firma con código (solo en documentos firmados). */
17
+ signatureEvidence?: LoanContractSignatureEvidence;
15
18
  createdAt: string;
16
19
  updatedAt: string;
17
20
  }
@@ -6,6 +6,8 @@ export declare enum ContractTypeEnum {
6
6
  CREDIT = "CREDIT",
7
7
  PROMISSORY_NOTE = "PROMISSORY_NOTE",
8
8
  SIC_AUTHORIZATION = "SIC_AUTHORIZATION",
9
+ /** Constancia de firma electrónica: la evidencia de la firma con código, en PDF. */
10
+ SIGNATURE_EVIDENCE = "SIGNATURE_EVIDENCE",
9
11
  RESTRUCTURE_ADDENDUM = "RESTRUCTURE_ADDENDUM",
10
12
  ADVANCE_AUTHORIZATION = "ADVANCE_AUTHORIZATION",
11
13
  SETTLEMENT_CERTIFICATE = "SETTLEMENT_CERTIFICATE",
@@ -10,6 +10,8 @@ var ContractTypeEnum;
10
10
  ContractTypeEnum["CREDIT"] = "CREDIT";
11
11
  ContractTypeEnum["PROMISSORY_NOTE"] = "PROMISSORY_NOTE";
12
12
  ContractTypeEnum["SIC_AUTHORIZATION"] = "SIC_AUTHORIZATION";
13
+ /** Constancia de firma electrónica: la evidencia de la firma con código, en PDF. */
14
+ ContractTypeEnum["SIGNATURE_EVIDENCE"] = "SIGNATURE_EVIDENCE";
13
15
  ContractTypeEnum["RESTRUCTURE_ADDENDUM"] = "RESTRUCTURE_ADDENDUM";
14
16
  ContractTypeEnum["ADVANCE_AUTHORIZATION"] = "ADVANCE_AUTHORIZATION";
15
17
  ContractTypeEnum["SETTLEMENT_CERTIFICATE"] = "SETTLEMENT_CERTIFICATE";
@@ -9,6 +9,8 @@ export * from './dtos/LoanInstallment';
9
9
  export * from './dtos/InstallmentPaidEvent';
10
10
  export * from './dtos/LoanContract';
11
11
  export * from './dtos/LoanContractDetails';
12
+ export * from './dtos/SignatureContext';
13
+ export * from './dtos/LoanContractSignatureEvidence';
12
14
  export * from './dtos/ActivationChecklist';
13
15
  export * from './dtos/AppliedPromotionSnapshot';
14
16
  export * from './dtos/AppliedStorePolicySnapshot';
@@ -30,6 +30,8 @@ __exportStar(require("./dtos/LoanInstallment"), exports);
30
30
  __exportStar(require("./dtos/InstallmentPaidEvent"), exports);
31
31
  __exportStar(require("./dtos/LoanContract"), exports);
32
32
  __exportStar(require("./dtos/LoanContractDetails"), exports);
33
+ __exportStar(require("./dtos/SignatureContext"), exports);
34
+ __exportStar(require("./dtos/LoanContractSignatureEvidence"), exports);
33
35
  __exportStar(require("./dtos/ActivationChecklist"), exports);
34
36
  __exportStar(require("./dtos/AppliedPromotionSnapshot"), exports);
35
37
  __exportStar(require("./dtos/AppliedStorePolicySnapshot"), exports);
@@ -0,0 +1,2 @@
1
+ /** Huella SHA-256 en hexadecimal, tal como la calcula pdf-render sobre el PDF. */
2
+ export declare const OTP_DOCUMENT_HASH_PATTERN: RegExp;
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.OTP_DOCUMENT_HASH_PATTERN = void 0;
4
+ /** Huella SHA-256 en hexadecimal, tal como la calcula pdf-render sobre el PDF. */
5
+ exports.OTP_DOCUMENT_HASH_PATTERN = /^[a-f0-9]{64}$/;
@@ -18,6 +18,7 @@ export declare class SendMessageRequest {
18
18
  options?: Record<string, unknown>;
19
19
  referenceMessageId?: string;
20
20
  transactionNumber?: string;
21
+ documentHash?: string;
21
22
  purpose?: OtpMessagePurposeEnum;
22
23
  tenantId?: string;
23
24
  }
@@ -13,6 +13,7 @@ exports.SendMessageRequest = void 0;
13
13
  const class_transformer_1 = require("class-transformer");
14
14
  const class_validator_1 = require("class-validator");
15
15
  const messaging_1 = require("../../messaging");
16
+ const OtpBinding_1 = require("../constants/OtpBinding");
16
17
  const OtpMessagePurposeEnum_1 = require("../enums/OtpMessagePurposeEnum");
17
18
  class SendMessageRequest {
18
19
  }
@@ -118,6 +119,12 @@ __decorate([
118
119
  (0, class_validator_1.IsString)(),
119
120
  __metadata("design:type", String)
120
121
  ], SendMessageRequest.prototype, "transactionNumber", void 0);
122
+ __decorate([
123
+ (0, class_transformer_1.Expose)(),
124
+ (0, class_validator_1.IsOptional)(),
125
+ (0, class_validator_1.Matches)(OtpBinding_1.OTP_DOCUMENT_HASH_PATTERN),
126
+ __metadata("design:type", String)
127
+ ], SendMessageRequest.prototype, "documentHash", void 0);
121
128
  __decorate([
122
129
  (0, class_transformer_1.Expose)(),
123
130
  (0, class_validator_1.IsOptional)(),
@@ -4,4 +4,6 @@ export declare class VerifyOtpRequest {
4
4
  typeOfDirectoryId: string;
5
5
  message: string;
6
6
  purpose?: OtpMessagePurposeEnum;
7
+ transactionNumber?: string;
8
+ documentHash?: string;
7
9
  }
@@ -12,6 +12,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
12
12
  exports.VerifyOtpRequest = void 0;
13
13
  const class_transformer_1 = require("class-transformer");
14
14
  const class_validator_1 = require("class-validator");
15
+ const OtpBinding_1 = require("../constants/OtpBinding");
15
16
  const OtpMessagePurposeEnum_1 = require("../enums/OtpMessagePurposeEnum");
16
17
  class VerifyOtpRequest {
17
18
  }
@@ -40,3 +41,16 @@ __decorate([
40
41
  (0, class_validator_1.IsEnum)(OtpMessagePurposeEnum_1.OtpMessagePurposeEnum),
41
42
  __metadata("design:type", String)
42
43
  ], VerifyOtpRequest.prototype, "purpose", void 0);
44
+ __decorate([
45
+ (0, class_transformer_1.Expose)(),
46
+ (0, class_validator_1.IsOptional)(),
47
+ (0, class_validator_1.IsString)(),
48
+ (0, class_validator_1.IsNotEmpty)(),
49
+ __metadata("design:type", String)
50
+ ], VerifyOtpRequest.prototype, "transactionNumber", void 0);
51
+ __decorate([
52
+ (0, class_transformer_1.Expose)(),
53
+ (0, class_validator_1.IsOptional)(),
54
+ (0, class_validator_1.Matches)(OtpBinding_1.OTP_DOCUMENT_HASH_PATTERN),
55
+ __metadata("design:type", String)
56
+ ], VerifyOtpRequest.prototype, "documentHash", void 0);
@@ -2,4 +2,10 @@ export declare class VerifyOtpResponse {
2
2
  verified: boolean;
3
3
  directoryId: string;
4
4
  typeOfDirectoryId: string;
5
+ /** Id del envío (fila del log) que se validó. */
6
+ logId?: string;
7
+ /** Cuándo se mandó el código (ISO 8601). */
8
+ sentAt?: string;
9
+ /** Cuándo se validó (ISO 8601). */
10
+ verifiedAt?: string;
5
11
  }
@@ -2,6 +2,7 @@ export * from './enums/DeliveryChannelExtendedEnum';
2
2
  export * from './enums/NotificationOriginEnum';
3
3
  export * from './enums/MessagingSlotEnum';
4
4
  export * from './enums/OtpMessagePurposeEnum';
5
+ export * from './constants/OtpBinding';
5
6
  export * from './validators/IsStringValueRecord';
6
7
  export * from './dtos/NotificationQueueMessageRequestV2';
7
8
  export * from './dtos/SendMessageRequest';
@@ -18,6 +18,7 @@ __exportStar(require("./enums/DeliveryChannelExtendedEnum"), exports);
18
18
  __exportStar(require("./enums/NotificationOriginEnum"), exports);
19
19
  __exportStar(require("./enums/MessagingSlotEnum"), exports);
20
20
  __exportStar(require("./enums/OtpMessagePurposeEnum"), exports);
21
+ __exportStar(require("./constants/OtpBinding"), exports);
21
22
  __exportStar(require("./validators/IsStringValueRecord"), exports);
22
23
  __exportStar(require("./dtos/NotificationQueueMessageRequestV2"), exports);
23
24
  __exportStar(require("./dtos/SendMessageRequest"), exports);
@@ -0,0 +1,7 @@
1
+ /**
2
+ * POST /wizard/sessions/:id/signature/send-otp — firma FES tiempo 1 (paso 08). El cliente marcó
3
+ * "Acepto los términos del contrato de crédito": sin esa aceptación el wizard no manda el código.
4
+ */
5
+ export declare class SignatureSendOtpRequest {
6
+ termsAccepted: true;
7
+ }
@@ -0,0 +1,26 @@
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.SignatureSendOtpRequest = void 0;
13
+ const class_transformer_1 = require("class-transformer");
14
+ const class_validator_1 = require("class-validator");
15
+ /**
16
+ * POST /wizard/sessions/:id/signature/send-otp — firma FES tiempo 1 (paso 08). El cliente marcó
17
+ * "Acepto los términos del contrato de crédito": sin esa aceptación el wizard no manda el código.
18
+ */
19
+ class SignatureSendOtpRequest {
20
+ }
21
+ exports.SignatureSendOtpRequest = SignatureSendOtpRequest;
22
+ __decorate([
23
+ (0, class_transformer_1.Expose)(),
24
+ (0, class_validator_1.Equals)(true, { message: 'termsAccepted debe ser true: el cliente tiene que aceptar los términos' }),
25
+ __metadata("design:type", Boolean)
26
+ ], SignatureSendOtpRequest.prototype, "termsAccepted", void 0);
@@ -91,6 +91,7 @@ export * from './dtos/requests/CancelSaleRequest';
91
91
  export * from './dtos/requests/QuoteRequest';
92
92
  export * from './dtos/requests/DownPaymentRequest';
93
93
  export * from './dtos/requests/SignatureSignRequest';
94
+ export * from './dtos/requests/SignatureSendOtpRequest';
94
95
  export * from './dtos/requests/RegisterDeviceRequest';
95
96
  export * from './dtos/requests/CompleteSaleRequest';
96
97
  export * from './dtos/requests/SuggestIdentifiersQuery';
@@ -116,6 +116,7 @@ __exportStar(require("./dtos/requests/CancelSaleRequest"), exports);
116
116
  __exportStar(require("./dtos/requests/QuoteRequest"), exports);
117
117
  __exportStar(require("./dtos/requests/DownPaymentRequest"), exports);
118
118
  __exportStar(require("./dtos/requests/SignatureSignRequest"), exports);
119
+ __exportStar(require("./dtos/requests/SignatureSendOtpRequest"), exports);
119
120
  __exportStar(require("./dtos/requests/RegisterDeviceRequest"), exports);
120
121
  __exportStar(require("./dtos/requests/CompleteSaleRequest"), exports);
121
122
  __exportStar(require("./dtos/requests/SuggestIdentifiersQuery"), exports);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fiado/type-kit",
3
- "version": "3.536.0",
3
+ "version": "3.537.0",
4
4
  "description": "",
5
5
  "main": "bin/index.js",
6
6
  "types": "bin/index.d.ts",
@@ -1,11 +1,13 @@
1
1
  import { Expose } from 'class-transformer';
2
- import { IsBoolean, IsEnum, IsOptional, IsString, Matches } from 'class-validator';
2
+ import { IsBoolean, IsEnum, IsObject, IsOptional, IsString, Matches } from 'class-validator';
3
3
  import { regexIso8601 } from '../../helpers/constans/regex';
4
4
  import { ContractTypeEnum } from '../enums/ContractTypeEnum';
5
+ import type { LoanContractSignatureEvidence } from './LoanContractSignatureEvidence';
5
6
 
6
7
  /**
7
8
  * LoanContract — documento del contrato de crédito con firma FES (modelo-datos §5b ·
8
- * `<T>LoanContract_GT` · M2 §4.3-§4.4). Un registro por documento (3 por crédito — C4).
9
+ * `<T>LoanContract_GT` · M2 §4.3-§4.4). Un registro por documento: los 3 de la originación (C4), más la
10
+ * constancia de firma al firmar.
9
11
  *
10
12
  * Nace DRAFT en la originación (sin PDF); la firma FES genera el PDF, calcula su `sha256`,
11
13
  * verifica el OTP y sella con NOM-151. Conservación documental: 10 años (NOM-151).
@@ -58,6 +60,12 @@ export class LoanContract {
58
60
  @Matches(regexIso8601)
59
61
  signedAt?: string;
60
62
 
63
+ /** Evidencia de la firma con código (solo en documentos firmados). */
64
+ @Expose()
65
+ @IsOptional()
66
+ @IsObject()
67
+ signatureEvidence?: LoanContractSignatureEvidence;
68
+
61
69
  @Expose()
62
70
  @IsString()
63
71
  createdBy!: string;
@@ -0,0 +1,50 @@
1
+ /** El código de firma tal como lo validó messages-business. */
2
+ export interface SignatureEvidenceOtp {
3
+ /** Id del envío en messages-business (fila del log del código). */
4
+ messageLogId: string;
5
+ sentAt: string;
6
+ verifiedAt: string;
7
+ channel: 'SMS';
8
+ /** Teléfono destino enmascarado (`****1234`). */
9
+ destinationMasked?: string;
10
+ }
11
+
12
+ /** Envíos del código (con reenvíos) y validaciones fallidas antes de firmar. */
13
+ export interface SignatureEvidenceAttempts {
14
+ sends: number;
15
+ failedVerifications: number;
16
+ }
17
+
18
+ /** Aceptación de términos del tiempo 1, amarrada a la huella del contrato generado en esa llamada. */
19
+ export interface SignatureEvidenceAcceptance {
20
+ acceptedAt: string;
21
+ contractSha256: string;
22
+ ip: string;
23
+ userAgent?: string;
24
+ }
25
+
26
+ /** Desde dónde y quién envió el código al firmar (tiempo 2). */
27
+ export interface SignatureEvidenceSigning {
28
+ ip: string;
29
+ userAgent?: string;
30
+ sellerActorId: string;
31
+ }
32
+
33
+ /** Quién facilitó la firma en la tienda. */
34
+ export interface SignatureEvidenceFacilitator {
35
+ sellerId: string;
36
+ sellerActorId: string;
37
+ sellerName?: string;
38
+ storeId: string;
39
+ storeName?: string;
40
+ wizardSessionId: string;
41
+ }
42
+
43
+ /** Evidencia de la firma con código que queda en cada documento firmado. */
44
+ export interface LoanContractSignatureEvidence {
45
+ otp: SignatureEvidenceOtp;
46
+ attempts: SignatureEvidenceAttempts;
47
+ acceptance: SignatureEvidenceAcceptance;
48
+ signing: SignatureEvidenceSigning;
49
+ facilitator: SignatureEvidenceFacilitator;
50
+ }
@@ -0,0 +1,75 @@
1
+ import { Expose } from 'class-transformer';
2
+ import { IsBoolean, IsNotEmpty, IsOptional, IsString, MaxLength } from 'class-validator';
3
+
4
+ /** Largo máximo de un id operacional (vendedor, tienda, sesión). */
5
+ const ID_TEXT = 128;
6
+ /** Largo máximo de un nombre para mostrar. */
7
+ const NAME_TEXT = 300;
8
+ /** Una IPv6 mide 39 caracteres; se deja margen para el formato mapeado. */
9
+ const IP_TEXT = 64;
10
+ /** Los navegadores mandan User-Agent de 150 a 300 caracteres. */
11
+ const USER_AGENT_TEXT = 512;
12
+
13
+ /**
14
+ * Contexto de la firma que aporta el wizard en cada tiempo de `POST /private/credits/:creditId/sign`:
15
+ * quién facilita la firma (vendedor, tienda, sesión) y desde qué navegador. Es evidencia de la
16
+ * ceremonia: la IP y el navegador son del equipo de la tienda. Nunca se loguean.
17
+ */
18
+ export class SignatureContext {
19
+ /** IP de origen de la llamada, como la ve el API Gateway del wizard. */
20
+ @Expose()
21
+ @IsString()
22
+ @IsNotEmpty()
23
+ @MaxLength(IP_TEXT)
24
+ ip!: string;
25
+
26
+ @Expose()
27
+ @IsOptional()
28
+ @IsString()
29
+ @MaxLength(USER_AGENT_TEXT)
30
+ userAgent?: string;
31
+
32
+ /** Tiempo 1: el cliente marcó "Acepto los términos". El motor exige `true` para mandar el código. */
33
+ @Expose()
34
+ @IsOptional()
35
+ @IsBoolean()
36
+ termsAccepted?: boolean;
37
+
38
+ /** Usuario retail del vendedor. */
39
+ @Expose()
40
+ @IsString()
41
+ @IsNotEmpty()
42
+ @MaxLength(ID_TEXT)
43
+ sellerId!: string;
44
+
45
+ /** Cognito sub del vendedor: queda como actor de la firma. */
46
+ @Expose()
47
+ @IsString()
48
+ @IsNotEmpty()
49
+ @MaxLength(ID_TEXT)
50
+ sellerActorId!: string;
51
+
52
+ @Expose()
53
+ @IsOptional()
54
+ @IsString()
55
+ @MaxLength(NAME_TEXT)
56
+ sellerName?: string;
57
+
58
+ @Expose()
59
+ @IsString()
60
+ @IsNotEmpty()
61
+ @MaxLength(ID_TEXT)
62
+ storeId!: string;
63
+
64
+ @Expose()
65
+ @IsOptional()
66
+ @IsString()
67
+ @MaxLength(NAME_TEXT)
68
+ storeName?: string;
69
+
70
+ @Expose()
71
+ @IsString()
72
+ @IsNotEmpty()
73
+ @MaxLength(ID_TEXT)
74
+ wizardSessionId!: string;
75
+ }
@@ -1,6 +1,7 @@
1
1
  import { Expose, Type } from 'class-transformer';
2
2
  import { IsObject, IsOptional, IsString, Length, ValidateNested } from 'class-validator';
3
3
  import { LoanContractDetails } from '../LoanContractDetails';
4
+ import { SignatureContext } from '../SignatureContext';
4
5
 
5
6
  /**
6
7
  * Body de `POST /private/credits/:creditId/sign` (loan-credit-business — wizard paso 08, firma FES
@@ -64,4 +65,14 @@ export class SignLoanCreditRequest {
64
65
  @IsOptional()
65
66
  @IsString()
66
67
  otpDestination?: string;
68
+
69
+ /**
70
+ * Ambos tiempos: quién facilita la firma y desde qué navegador (evidencia de la ceremonia). En el
71
+ * tiempo 1 trae `termsAccepted: true`. El motor lo exige; la IP y el navegador nunca se loguean.
72
+ */
73
+ @Expose()
74
+ @IsOptional()
75
+ @ValidateNested()
76
+ @Type(() => SignatureContext)
77
+ signatureContext?: SignatureContext;
67
78
  }
@@ -1,4 +1,5 @@
1
1
  import { ContractTypeEnum } from '../../enums/ContractTypeEnum';
2
+ import type { LoanContractSignatureEvidence } from '../LoanContractSignatureEvidence';
2
3
 
3
4
  /**
4
5
  * Response de un documento del contrato (loan-credit-business). `signedAt` ausente = draft.
@@ -13,6 +14,8 @@ export interface LoanContractResponse {
13
14
  otpVerified: boolean;
14
15
  nom151Timestamp?: string;
15
16
  signedAt?: string;
17
+ /** Evidencia de la firma con código (solo en documentos firmados). */
18
+ signatureEvidence?: LoanContractSignatureEvidence;
16
19
  createdAt: string;
17
20
  updatedAt: string;
18
21
  }
@@ -6,6 +6,8 @@ export enum ContractTypeEnum {
6
6
  CREDIT = 'CREDIT',
7
7
  PROMISSORY_NOTE = 'PROMISSORY_NOTE',
8
8
  SIC_AUTHORIZATION = 'SIC_AUTHORIZATION',
9
+ /** Constancia de firma electrónica: la evidencia de la firma con código, en PDF. */
10
+ SIGNATURE_EVIDENCE = 'SIGNATURE_EVIDENCE',
9
11
  RESTRUCTURE_ADDENDUM = 'RESTRUCTURE_ADDENDUM',
10
12
  ADVANCE_AUTHORIZATION = 'ADVANCE_AUTHORIZATION',
11
13
  SETTLEMENT_CERTIFICATE = 'SETTLEMENT_CERTIFICATE',
@@ -13,6 +13,8 @@ export * from './dtos/LoanInstallment';
13
13
  export * from './dtos/InstallmentPaidEvent';
14
14
  export * from './dtos/LoanContract';
15
15
  export * from './dtos/LoanContractDetails';
16
+ export * from './dtos/SignatureContext';
17
+ export * from './dtos/LoanContractSignatureEvidence';
16
18
  export * from './dtos/ActivationChecklist';
17
19
  export * from './dtos/AppliedPromotionSnapshot';
18
20
  export * from './dtos/AppliedStorePolicySnapshot';
@@ -0,0 +1,2 @@
1
+ /** Huella SHA-256 en hexadecimal, tal como la calcula pdf-render sobre el PDF. */
2
+ export const OTP_DOCUMENT_HASH_PATTERN = /^[a-f0-9]{64}$/;
@@ -1,6 +1,7 @@
1
1
  import { Expose } from 'class-transformer';
2
- import { IsEnum, IsNotEmpty, IsObject, IsOptional, IsString, IsUUID } from 'class-validator';
2
+ import { IsEnum, IsNotEmpty, IsObject, IsOptional, IsString, IsUUID, Matches } from 'class-validator';
3
3
  import { DeliveryChannelEnum } from '../../messaging';
4
+ import { OTP_DOCUMENT_HASH_PATTERN } from '../constants/OtpBinding';
4
5
  import { OtpMessagePurposeEnum } from '../enums/OtpMessagePurposeEnum';
5
6
 
6
7
  export class SendMessageRequest {
@@ -28,6 +29,9 @@ export class SendMessageRequest {
28
29
  @Expose() @IsOptional() @IsObject() options?: Record<string, unknown>;
29
30
  @Expose() @IsOptional() @IsUUID() referenceMessageId?: string;
30
31
  @Expose() @IsOptional() @IsString() transactionNumber?: string;
32
+ // Huella del documento que firma el código (CONTRACT_SIGNATURE_OTP). Junto con `transactionNumber`
33
+ // amarra el código a ese documento: messages solo lo valida contra la misma pareja.
34
+ @Expose() @IsOptional() @Matches(OTP_DOCUMENT_HASH_PATTERN) documentHash?: string;
31
35
  @Expose() @IsOptional() @IsEnum(OtpMessagePurposeEnum) purpose?: OtpMessagePurposeEnum;
32
36
  // Tenant emisor: decide plantilla e identidad de envío. Sin él messages-business cae a FIADO.
33
37
  @Expose() @IsOptional() @IsString() @IsNotEmpty() tenantId?: string;
@@ -1,5 +1,6 @@
1
1
  import { Expose } from 'class-transformer';
2
- import { IsEnum, IsNotEmpty, IsOptional, IsString } from 'class-validator';
2
+ import { IsEnum, IsNotEmpty, IsOptional, IsString, Matches } from 'class-validator';
3
+ import { OTP_DOCUMENT_HASH_PATTERN } from '../constants/OtpBinding';
3
4
  import { OtpMessagePurposeEnum } from '../enums/OtpMessagePurposeEnum';
4
5
 
5
6
  export class VerifyOtpRequest {
@@ -11,4 +12,7 @@ export class VerifyOtpRequest {
11
12
  @Expose() @IsString() @IsNotEmpty() typeOfDirectoryId!: string;
12
13
  @Expose() @IsString() @IsNotEmpty() message!: string;
13
14
  @Expose() @IsOptional() @IsEnum(OtpMessagePurposeEnum) purpose?: OtpMessagePurposeEnum;
15
+ // Amarre del código de firma: si vienen, la fila tiene que traer los mismos o el código no sirve.
16
+ @Expose() @IsOptional() @IsString() @IsNotEmpty() transactionNumber?: string;
17
+ @Expose() @IsOptional() @Matches(OTP_DOCUMENT_HASH_PATTERN) documentHash?: string;
14
18
  }
@@ -2,4 +2,10 @@ export class VerifyOtpResponse {
2
2
  verified!: boolean;
3
3
  directoryId!: string;
4
4
  typeOfDirectoryId!: string;
5
+ /** Id del envío (fila del log) que se validó. */
6
+ logId?: string;
7
+ /** Cuándo se mandó el código (ISO 8601). */
8
+ sentAt?: string;
9
+ /** Cuándo se validó (ISO 8601). */
10
+ verifiedAt?: string;
5
11
  }
@@ -2,6 +2,7 @@ export * from './enums/DeliveryChannelExtendedEnum';
2
2
  export * from './enums/NotificationOriginEnum';
3
3
  export * from './enums/MessagingSlotEnum';
4
4
  export * from './enums/OtpMessagePurposeEnum';
5
+ export * from './constants/OtpBinding';
5
6
  export * from './validators/IsStringValueRecord';
6
7
  export * from './dtos/NotificationQueueMessageRequestV2';
7
8
  export * from './dtos/SendMessageRequest';
@@ -0,0 +1,12 @@
1
+ import { Expose } from 'class-transformer';
2
+ import { Equals } from 'class-validator';
3
+
4
+ /**
5
+ * POST /wizard/sessions/:id/signature/send-otp — firma FES tiempo 1 (paso 08). El cliente marcó
6
+ * "Acepto los términos del contrato de crédito": sin esa aceptación el wizard no manda el código.
7
+ */
8
+ export class SignatureSendOtpRequest {
9
+ @Expose()
10
+ @Equals(true, { message: 'termsAccepted debe ser true: el cliente tiene que aceptar los términos' })
11
+ termsAccepted!: true;
12
+ }
@@ -102,6 +102,7 @@ export * from './dtos/requests/CancelSaleRequest';
102
102
  export * from './dtos/requests/QuoteRequest';
103
103
  export * from './dtos/requests/DownPaymentRequest';
104
104
  export * from './dtos/requests/SignatureSignRequest';
105
+ export * from './dtos/requests/SignatureSendOtpRequest';
105
106
  export * from './dtos/requests/RegisterDeviceRequest';
106
107
  export * from './dtos/requests/CompleteSaleRequest';
107
108
  export * from './dtos/requests/SuggestIdentifiersQuery';