redsys-easy 5.2.7 → 5.2.8
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/lib/errors.d.ts +8 -8
- package/lib/index.esm.js +90 -99
- package/lib/index.js +90 -99
- package/package.json +6 -13
package/lib/errors.d.ts
CHANGED
|
@@ -23,14 +23,14 @@ export declare class ValidationError extends RedsysError {
|
|
|
23
23
|
*/
|
|
24
24
|
export declare class ParseError extends RedsysError {
|
|
25
25
|
value: unknown;
|
|
26
|
-
location: unknown
|
|
26
|
+
location: unknown;
|
|
27
27
|
description: string | undefined;
|
|
28
|
-
constructor(message: string, value?: unknown
|
|
28
|
+
constructor(message: string, value?: unknown, location?: unknown);
|
|
29
29
|
}
|
|
30
30
|
interface HTTPErrorOptions {
|
|
31
31
|
message?: string | undefined;
|
|
32
32
|
code: number;
|
|
33
|
-
response?: unknown
|
|
33
|
+
response?: unknown;
|
|
34
34
|
}
|
|
35
35
|
/**
|
|
36
36
|
* HTTP Error
|
|
@@ -39,13 +39,13 @@ interface HTTPErrorOptions {
|
|
|
39
39
|
*/
|
|
40
40
|
export declare class HTTPError extends RedsysError {
|
|
41
41
|
code: number | undefined;
|
|
42
|
-
response: unknown
|
|
42
|
+
response: unknown;
|
|
43
43
|
constructor({ message, code, response }: HTTPErrorOptions);
|
|
44
44
|
}
|
|
45
45
|
interface GatewayErrorOptions {
|
|
46
46
|
message?: string | undefined;
|
|
47
47
|
code: string;
|
|
48
|
-
response?: unknown
|
|
48
|
+
response?: unknown;
|
|
49
49
|
}
|
|
50
50
|
/**
|
|
51
51
|
* Request could not be processed by Redsys
|
|
@@ -54,13 +54,13 @@ interface GatewayErrorOptions {
|
|
|
54
54
|
*/
|
|
55
55
|
export declare class GatewayError extends RedsysError {
|
|
56
56
|
code: string | undefined;
|
|
57
|
-
response: unknown
|
|
57
|
+
response: unknown;
|
|
58
58
|
constructor({ message, code, response }: GatewayErrorOptions);
|
|
59
59
|
}
|
|
60
60
|
interface ResponseErrorOptions {
|
|
61
61
|
message?: string | undefined;
|
|
62
62
|
code: number;
|
|
63
|
-
response?: unknown
|
|
63
|
+
response?: unknown;
|
|
64
64
|
}
|
|
65
65
|
/**
|
|
66
66
|
* Response contained an error code
|
|
@@ -69,7 +69,7 @@ interface ResponseErrorOptions {
|
|
|
69
69
|
*/
|
|
70
70
|
export declare class ResponseError extends RedsysError {
|
|
71
71
|
code: number | undefined;
|
|
72
|
-
response: unknown
|
|
72
|
+
response: unknown;
|
|
73
73
|
constructor({ message, code, response }: ResponseErrorOptions);
|
|
74
74
|
}
|
|
75
75
|
export {};
|
package/lib/index.esm.js
CHANGED
|
@@ -1756,9 +1756,7 @@ const getSISErrorCodeMessage = (code) => {
|
|
|
1756
1756
|
}
|
|
1757
1757
|
return SIS_ERROR_CODES[code.trim()];
|
|
1758
1758
|
};
|
|
1759
|
-
const getHTTPErrorCodeMessage = (code) =>
|
|
1760
|
-
return STATUS_CODES[code];
|
|
1761
|
-
};
|
|
1759
|
+
const getHTTPErrorCodeMessage = (code) => STATUS_CODES[code];
|
|
1762
1760
|
|
|
1763
1761
|
/**
|
|
1764
1762
|
* Error superclass for all redsys-easy specific errors
|
|
@@ -1834,7 +1832,9 @@ class ParseError extends RedsysError {
|
|
|
1834
1832
|
class HTTPError extends RedsysError {
|
|
1835
1833
|
constructor({ message, code, response }) {
|
|
1836
1834
|
const codeDescription = getHTTPErrorCodeMessage(code);
|
|
1837
|
-
const codeInfo =
|
|
1835
|
+
const codeInfo =
|
|
1836
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- robustness
|
|
1837
|
+
code !== undefined
|
|
1838
1838
|
? ` ${code}${codeDescription != null ? `: ${codeDescription}` : ''}`
|
|
1839
1839
|
: '';
|
|
1840
1840
|
const augmentationLine = message != null && message ? `\n${message}` : '';
|
|
@@ -1865,7 +1865,9 @@ class HTTPError extends RedsysError {
|
|
|
1865
1865
|
class GatewayError extends RedsysError {
|
|
1866
1866
|
constructor({ message, code, response }) {
|
|
1867
1867
|
const codeDescription = getSISErrorCodeMessage(code);
|
|
1868
|
-
const codeInfo =
|
|
1868
|
+
const codeInfo =
|
|
1869
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- robustness
|
|
1870
|
+
code !== undefined
|
|
1869
1871
|
? ` ${code}${codeDescription != null ? `: ${codeDescription}` : ''}`
|
|
1870
1872
|
: '';
|
|
1871
1873
|
const augmentationLine = message != null && message ? `\n${message}` : '';
|
|
@@ -1896,7 +1898,9 @@ class GatewayError extends RedsysError {
|
|
|
1896
1898
|
class ResponseError extends RedsysError {
|
|
1897
1899
|
constructor({ message, code, response }) {
|
|
1898
1900
|
const codeDescription = getResponseCodeMessage(code);
|
|
1899
|
-
const codeInfo =
|
|
1901
|
+
const codeInfo =
|
|
1902
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- robustness
|
|
1903
|
+
code !== undefined
|
|
1900
1904
|
? ` ${code}${codeDescription != null ? `: ${codeDescription}` : ''}`
|
|
1901
1905
|
: '';
|
|
1902
1906
|
const augmentationLine = message != null && message ? `\n${message}` : '';
|
|
@@ -1927,17 +1931,17 @@ const deserializeJSONMerchantParams = (strPayload) => {
|
|
|
1927
1931
|
if (typeof strPayload !== 'string') {
|
|
1928
1932
|
throw new ParseError('Payload must be a base-64 encoded string');
|
|
1929
1933
|
}
|
|
1934
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- assume it satisfies format
|
|
1930
1935
|
const payload = JSON.parse(Buffer.from(strPayload, 'base64').toString('utf8'));
|
|
1931
1936
|
if (typeof payload !== 'object' || payload == null) {
|
|
1932
1937
|
throw new ParseError('Cannot parse notification payload');
|
|
1933
1938
|
}
|
|
1934
1939
|
return payload;
|
|
1935
1940
|
};
|
|
1936
|
-
const serializeJSONMerchantParams = (requestParams) =>
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
};
|
|
1941
|
+
const serializeJSONMerchantParams = (requestParams) =>
|
|
1942
|
+
// Official redsys docs escape "/" because they use php json_encode. I think
|
|
1943
|
+
// this is wrong and it also works without escaping.
|
|
1944
|
+
Buffer.from(JSON.stringify(requestParams), 'utf8').toString('base64');
|
|
1941
1945
|
|
|
1942
1946
|
/**
|
|
1943
1947
|
* Adds padding to a buffer.
|
|
@@ -1971,7 +1975,7 @@ const encrypt3DES = (key, message) => {
|
|
|
1971
1975
|
]);
|
|
1972
1976
|
// Make sure that encrypted buffer is not longer than the padded message
|
|
1973
1977
|
const maxLength = Math.ceil(messageBuf.length / 8) * 8;
|
|
1974
|
-
return encryptedBuf.
|
|
1978
|
+
return encryptedBuf.subarray(0, maxLength);
|
|
1975
1979
|
};
|
|
1976
1980
|
/**
|
|
1977
1981
|
* Compute HMAC_SHA256_V1 signature
|
|
@@ -1991,9 +1995,7 @@ const sha256Sign = (merchantKey, order, params) => {
|
|
|
1991
1995
|
/**
|
|
1992
1996
|
* Discrete uniform distribution with domain (0 to *max*)
|
|
1993
1997
|
*/
|
|
1994
|
-
const drawPositiveDiscreteUniform = (max) =>
|
|
1995
|
-
return randomInt(max + 1);
|
|
1996
|
-
};
|
|
1998
|
+
const drawPositiveDiscreteUniform = (max) => randomInt(max + 1);
|
|
1997
1999
|
const alphanumeric = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
|
|
1998
2000
|
/**
|
|
1999
2001
|
* Generates a random order ID following redsys requirements
|
|
@@ -2014,7 +2016,6 @@ const randomTransactionId = () => {
|
|
|
2014
2016
|
return `${numStr}${alphanumStr}`;
|
|
2015
2017
|
};
|
|
2016
2018
|
const extractAndAssertOrderFromRequestParams = (requestParams) => {
|
|
2017
|
-
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions, @typescript-eslint/prefer-nullish-coalescing
|
|
2018
2019
|
const order = requestParams['DS_MERCHANT_ORDER'] ||
|
|
2019
2020
|
requestParams['Ds_Merchant_Order'];
|
|
2020
2021
|
if (typeof order !== 'string' || !order) {
|
|
@@ -2023,7 +2024,6 @@ const extractAndAssertOrderFromRequestParams = (requestParams) => {
|
|
|
2023
2024
|
return order;
|
|
2024
2025
|
};
|
|
2025
2026
|
const extractAndAssertOrderFromResponseParams = (responseParams) => {
|
|
2026
|
-
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions, @typescript-eslint/prefer-nullish-coalescing
|
|
2027
2027
|
const order = responseParams['Ds_Order'] ||
|
|
2028
2028
|
responseParams['DS_ORDER'];
|
|
2029
2029
|
if (typeof order !== 'string' || !order) {
|
|
@@ -2086,11 +2086,7 @@ const isStringNotEmpty = (str) => str != null && str.length > 0;
|
|
|
2086
2086
|
*
|
|
2087
2087
|
* If value is defined apply function and return output, otherwise return undefined.
|
|
2088
2088
|
*/
|
|
2089
|
-
const mapMaybeMonad = (value, fn) =>
|
|
2090
|
-
return value !== undefined
|
|
2091
|
-
? fn(value)
|
|
2092
|
-
: undefined;
|
|
2093
|
-
};
|
|
2089
|
+
const mapMaybeMonad = (value, fn) => (value !== undefined ? fn(value) : undefined);
|
|
2094
2090
|
|
|
2095
2091
|
const sha256VerifyJSONResponse = (merchantKey, response, responseParams) => {
|
|
2096
2092
|
if (response.Ds_SignatureVersion !== 'HMAC_SHA256_V1') {
|
|
@@ -2129,27 +2125,27 @@ const serializeAndSignJSONRequest = (merchantKey, requestParams) => {
|
|
|
2129
2125
|
/**
|
|
2130
2126
|
* Tries to decode x-www-form-urlencoded values of an object
|
|
2131
2127
|
*/
|
|
2132
|
-
const decodeUrlEntries = (params) =>
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
catch (_) { }
|
|
2128
|
+
const decodeUrlEntries = (params) =>
|
|
2129
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- type is unchanged
|
|
2130
|
+
Object.fromEntries(Object.entries(params).map((entry) => {
|
|
2131
|
+
let processedValue = entry[1];
|
|
2132
|
+
if (typeof entry[1] === 'string') {
|
|
2133
|
+
try {
|
|
2134
|
+
processedValue = decodeURIComponent(entry[1].replaceAll('+', ' '));
|
|
2140
2135
|
}
|
|
2141
|
-
|
|
2142
|
-
}
|
|
2143
|
-
|
|
2136
|
+
catch (_) { }
|
|
2137
|
+
}
|
|
2138
|
+
return [entry[0], processedValue];
|
|
2139
|
+
}));
|
|
2144
2140
|
|
|
2145
2141
|
const serializeSoapNotificationResponse = ({ allow: allowOperation }) => {
|
|
2146
2142
|
const answer = allowOperation ? 'OK' : 'KO';
|
|
2147
2143
|
return `<Response Ds_Version="0.0"><Ds_Response_Merchant>${answer}</Ds_Response_Merchant></Response>`;
|
|
2148
2144
|
};
|
|
2149
2145
|
const xmlParser = new XMLParser({ parseTagValue: false });
|
|
2150
|
-
const deserializeSoapNotification = (xml) =>
|
|
2151
|
-
|
|
2152
|
-
|
|
2146
|
+
const deserializeSoapNotification = (xml) =>
|
|
2147
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- assume xml matches schema
|
|
2148
|
+
xmlParser.parse(xml).Message;
|
|
2153
2149
|
|
|
2154
2150
|
const verifySoapNotification = (merchantKey, xml, msg) => {
|
|
2155
2151
|
const startToken = '<Request';
|
|
@@ -2162,6 +2158,7 @@ const verifySoapNotification = (merchantKey, xml, msg) => {
|
|
|
2162
2158
|
const signedStr = xml.slice(startPos, endPos + endToken.length);
|
|
2163
2159
|
const signature = msg.Signature;
|
|
2164
2160
|
const order = msg.Request.Ds_Order;
|
|
2161
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- robustness check
|
|
2165
2162
|
if (order == null || !order) {
|
|
2166
2163
|
throw new ParseError('Invalid notification response');
|
|
2167
2164
|
}
|
|
@@ -2195,6 +2192,7 @@ const jsonRequest = async ({ fetch, url, merchantKey, rawRequestParams }) => {
|
|
|
2195
2192
|
},
|
|
2196
2193
|
body: JSON.stringify(payload)
|
|
2197
2194
|
});
|
|
2195
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- assume correct format
|
|
2198
2196
|
const responseData = await response.json();
|
|
2199
2197
|
if (!response.ok) {
|
|
2200
2198
|
throw new HTTPError({
|
|
@@ -2246,6 +2244,7 @@ const createRedsysAPI = (config) => {
|
|
|
2246
2244
|
throw new RedsysError('A secretKey key must be provided');
|
|
2247
2245
|
}
|
|
2248
2246
|
if (typeof config.urls !== 'object' ||
|
|
2247
|
+
// eslint-disable-next-line @typescript-eslint/prefer-optional-chain, @typescript-eslint/no-unnecessary-condition -- robustness
|
|
2249
2248
|
config.urls == null ||
|
|
2250
2249
|
!config.urls.restIniciaPeticion ||
|
|
2251
2250
|
!config.urls.restTrataPeticion ||
|
|
@@ -2255,14 +2254,12 @@ const createRedsysAPI = (config) => {
|
|
|
2255
2254
|
/**
|
|
2256
2255
|
* Sends a iniciaPeticion request using REST interface
|
|
2257
2256
|
*/
|
|
2258
|
-
const restIniciaPeticion = async (paramsInput) => {
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
});
|
|
2265
|
-
};
|
|
2257
|
+
const restIniciaPeticion = async (paramsInput) => await jsonRequest({
|
|
2258
|
+
fetch: fetch$1,
|
|
2259
|
+
url: config.urls.restIniciaPeticion,
|
|
2260
|
+
merchantKey: config.secretKey,
|
|
2261
|
+
rawRequestParams: paramsInput
|
|
2262
|
+
});
|
|
2266
2263
|
/**
|
|
2267
2264
|
* Sends a trataPeticion request using REST interface
|
|
2268
2265
|
*/
|
|
@@ -2300,10 +2297,9 @@ const createRedsysAPI = (config) => {
|
|
|
2300
2297
|
*/
|
|
2301
2298
|
const processDirectRestNotification = (
|
|
2302
2299
|
/** Body of JSON notification, as a POJO (Plain Old Javascript Object) */
|
|
2303
|
-
body) =>
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
};
|
|
2300
|
+
body) =>
|
|
2301
|
+
// A notification can't contain a gateway error, it didn't initiate the request
|
|
2302
|
+
deserializeAndVerifyJSONResponse(config.secretKey, body);
|
|
2307
2303
|
/**
|
|
2308
2304
|
* Processes either a URLOK/URLKO query or a JSON REST notification
|
|
2309
2305
|
*
|
|
@@ -2329,9 +2325,7 @@ const createRedsysAPI = (config) => {
|
|
|
2329
2325
|
*/
|
|
2330
2326
|
const processSoapNotification = (
|
|
2331
2327
|
/** SOAP notification as a XML string */
|
|
2332
|
-
xml) =>
|
|
2333
|
-
return deserializeAndVerifySoapNotification(config.secretKey, xml);
|
|
2334
|
-
};
|
|
2328
|
+
xml) => deserializeAndVerifySoapNotification(config.secretKey, xml);
|
|
2335
2329
|
/**
|
|
2336
2330
|
* Creates an answer for a SOAP notification, serializes and signs it
|
|
2337
2331
|
*/
|
|
@@ -2339,12 +2333,10 @@ const createRedsysAPI = (config) => {
|
|
|
2339
2333
|
/** Order identifier */
|
|
2340
2334
|
order,
|
|
2341
2335
|
/** Indicates if the payment is allowed to proceed */
|
|
2342
|
-
allow) => {
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
});
|
|
2347
|
-
};
|
|
2336
|
+
allow) => serializeAndSignSoapNotificationResponse(config.secretKey, {
|
|
2337
|
+
order,
|
|
2338
|
+
allow
|
|
2339
|
+
});
|
|
2348
2340
|
return {
|
|
2349
2341
|
restIniciaPeticion,
|
|
2350
2342
|
restTrataPeticion,
|
|
@@ -2382,45 +2374,41 @@ const create3DSMethodForm = (emv3dsParams, notificationURL) => {
|
|
|
2382
2374
|
*
|
|
2383
2375
|
* @public
|
|
2384
2376
|
*/
|
|
2385
|
-
const create3DSv1ChallengeForm = (emv3dsV1Challenge, challengeNotificationUrl) => {
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
};
|
|
2394
|
-
};
|
|
2377
|
+
const create3DSv1ChallengeForm = (emv3dsV1Challenge, challengeNotificationUrl) => ({
|
|
2378
|
+
url: emv3dsV1Challenge.acsURL,
|
|
2379
|
+
body: {
|
|
2380
|
+
PaReq: emv3dsV1Challenge.PAReq,
|
|
2381
|
+
MD: emv3dsV1Challenge.MD,
|
|
2382
|
+
TermUrl: challengeNotificationUrl
|
|
2383
|
+
}
|
|
2384
|
+
});
|
|
2395
2385
|
/**
|
|
2396
2386
|
* Creates parameters for a 3DS v2 challenge form
|
|
2397
2387
|
*
|
|
2398
2388
|
* @public
|
|
2399
2389
|
*/
|
|
2400
|
-
const create3DSv2ChallengeForm = (emv3dsV2Challenge) => {
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
};
|
|
2407
|
-
};
|
|
2390
|
+
const create3DSv2ChallengeForm = (emv3dsV2Challenge) => ({
|
|
2391
|
+
url: emv3dsV2Challenge.acsURL,
|
|
2392
|
+
body: {
|
|
2393
|
+
creq: emv3dsV2Challenge.creq
|
|
2394
|
+
}
|
|
2395
|
+
});
|
|
2408
2396
|
/**
|
|
2409
2397
|
* Deserialize threeDSMethodData
|
|
2410
2398
|
*
|
|
2411
2399
|
* @public
|
|
2412
2400
|
*/
|
|
2413
|
-
const deserializeThreeDSMethodData = (threeDSMethodData) =>
|
|
2414
|
-
|
|
2415
|
-
|
|
2401
|
+
const deserializeThreeDSMethodData = (threeDSMethodData) =>
|
|
2402
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- assume it satisfies format
|
|
2403
|
+
JSON.parse(base64url.decode(threeDSMethodData));
|
|
2416
2404
|
/**
|
|
2417
2405
|
* Deserialize `cres` field of a 3DS v2 challenge
|
|
2418
2406
|
*
|
|
2419
2407
|
* @public
|
|
2420
2408
|
*/
|
|
2421
|
-
const deserializeCres = (cres) =>
|
|
2422
|
-
|
|
2423
|
-
|
|
2409
|
+
const deserializeCres = (cres) =>
|
|
2410
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- assume it satisfies format
|
|
2411
|
+
JSON.parse(Buffer.from(cres, 'base64').toString('utf8'));
|
|
2424
2412
|
|
|
2425
2413
|
const unescapeXML = (str) => {
|
|
2426
2414
|
const xml = str.replace(/&(lt|#60|gt|#62|quot|#34|amp|#38|apos|#39);/g, (_match, p1) => {
|
|
@@ -2761,10 +2749,11 @@ const CURRENCIES = {
|
|
|
2761
2749
|
*
|
|
2762
2750
|
* @public
|
|
2763
2751
|
*/
|
|
2764
|
-
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
2752
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions, @typescript-eslint/no-unsafe-type-assertion -- known deterministically
|
|
2765
2753
|
const REV_CURRENCIES = {};
|
|
2766
2754
|
for (const [key, value] of Object.entries(CURRENCIES)) {
|
|
2767
2755
|
REV_CURRENCIES[value.num] = {
|
|
2756
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- known deterministically
|
|
2768
2757
|
code: key,
|
|
2769
2758
|
decimals: value.decimals
|
|
2770
2759
|
};
|
|
@@ -2820,8 +2809,9 @@ const LANGUAGES = {
|
|
|
2820
2809
|
*
|
|
2821
2810
|
* @public
|
|
2822
2811
|
*/
|
|
2823
|
-
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
2812
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions, @typescript-eslint/no-unsafe-type-assertion -- known deterministically
|
|
2824
2813
|
const REV_LANGUAGES = {};
|
|
2814
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- known deterministically
|
|
2825
2815
|
for (const [key, value] of Object.entries(LANGUAGES)) {
|
|
2826
2816
|
REV_LANGUAGES[value] = key;
|
|
2827
2817
|
}
|
|
@@ -3095,8 +3085,9 @@ const COUNTRIES = {
|
|
|
3095
3085
|
*
|
|
3096
3086
|
* @public
|
|
3097
3087
|
*/
|
|
3098
|
-
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
3088
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions, @typescript-eslint/no-unsafe-type-assertion -- known deterministically
|
|
3099
3089
|
const REV_COUNTRIES = {};
|
|
3090
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- known deterministically
|
|
3100
3091
|
for (const [key, value] of Object.entries(COUNTRIES)) {
|
|
3101
3092
|
REV_COUNTRIES[value] = key;
|
|
3102
3093
|
}
|
|
@@ -3124,8 +3115,9 @@ const CARDBRANDS = {
|
|
|
3124
3115
|
*
|
|
3125
3116
|
* @public
|
|
3126
3117
|
*/
|
|
3127
|
-
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
3118
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions, @typescript-eslint/no-unsafe-type-assertion -- known deterministically
|
|
3128
3119
|
const REV_CARDBRANDS = {};
|
|
3120
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- known deterministically
|
|
3129
3121
|
for (const [key, value] of Object.entries(CARDBRANDS)) {
|
|
3130
3122
|
REV_CARDBRANDS[value] = key;
|
|
3131
3123
|
}
|
|
@@ -3191,29 +3183,23 @@ const TRANSACTION_TYPES = {
|
|
|
3191
3183
|
*
|
|
3192
3184
|
* @public
|
|
3193
3185
|
*/
|
|
3194
|
-
const useSingleInputFormatter = (fn, inputFormatter) =>
|
|
3195
|
-
return (input, ...otherArgs) => fn(inputFormatter(input), ...otherArgs);
|
|
3196
|
-
};
|
|
3186
|
+
const useSingleInputFormatter = (fn, inputFormatter) => (input, ...otherArgs) => fn(inputFormatter(input), ...otherArgs);
|
|
3197
3187
|
/**
|
|
3198
3188
|
* Applies an output formatter to a function
|
|
3199
3189
|
*
|
|
3200
3190
|
* @public
|
|
3201
3191
|
*/
|
|
3202
|
-
const useOutputFormatter = (fn, outputFormatter) =>
|
|
3203
|
-
return (a) => outputFormatter(fn(a));
|
|
3204
|
-
};
|
|
3192
|
+
const useOutputFormatter = (fn, outputFormatter) => (a) => outputFormatter(fn(a));
|
|
3205
3193
|
/**
|
|
3206
3194
|
* Applies an output formatter to the resolved ouput promise of a function
|
|
3207
3195
|
*
|
|
3208
3196
|
* @public
|
|
3209
3197
|
*/
|
|
3210
|
-
const usePromiseOutputFormatter = (fn, outputFormatter) =>
|
|
3211
|
-
return async (a) => outputFormatter(await fn(a));
|
|
3212
|
-
};
|
|
3198
|
+
const usePromiseOutputFormatter = (fn, outputFormatter) => async (a) => outputFormatter(await fn(a));
|
|
3213
3199
|
|
|
3200
|
+
/* eslint-disable @typescript-eslint/no-unnecessary-condition -- robustness handling inputs */
|
|
3214
3201
|
const formatInputCurrency = (input) => {
|
|
3215
3202
|
const currencyData = CURRENCIES[input];
|
|
3216
|
-
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
|
|
3217
3203
|
if (!(currencyData === null || currencyData === void 0 ? void 0 : currencyData.num)) {
|
|
3218
3204
|
throw new ValidationError('Unsupported currency', { currency: input });
|
|
3219
3205
|
}
|
|
@@ -3237,8 +3223,7 @@ const formatAmount = (value, context) => {
|
|
|
3237
3223
|
throw new ValidationError('Missing currency', { currency });
|
|
3238
3224
|
}
|
|
3239
3225
|
const currencyData = CURRENCIES[currency];
|
|
3240
|
-
|
|
3241
|
-
if (!(currencyData === null || currencyData === void 0 ? void 0 : currencyData.decimals)) {
|
|
3226
|
+
if (!Number.isFinite(currencyData === null || currencyData === void 0 ? void 0 : currencyData.decimals)) {
|
|
3242
3227
|
throw new ValidationError('Unsupported currency', { currency });
|
|
3243
3228
|
}
|
|
3244
3229
|
const rawValue = typeof value === 'number'
|
|
@@ -3396,10 +3381,12 @@ const restTrataPeticionInputFormatter = (raw) => {
|
|
|
3396
3381
|
dayjs.extend(timezone);
|
|
3397
3382
|
dayjs.extend(utc);
|
|
3398
3383
|
const formatCountry = (rawCountry) => {
|
|
3384
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- dealth within scope
|
|
3399
3385
|
const countryInt = Number.parseInt(rawCountry).toString();
|
|
3400
3386
|
return REV_COUNTRIES[countryInt];
|
|
3401
3387
|
};
|
|
3402
3388
|
const formatCardBrand = (rawCardBrand) => {
|
|
3389
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- dealth within scope
|
|
3403
3390
|
const cardBrandInt = Number.parseInt(rawCardBrand).toString();
|
|
3404
3391
|
return REV_CARDBRANDS[cardBrandInt];
|
|
3405
3392
|
};
|
|
@@ -3431,9 +3418,10 @@ const baseOutputFormatter = (raw) => {
|
|
|
3431
3418
|
};
|
|
3432
3419
|
const formatPrice = (params) => {
|
|
3433
3420
|
const { Ds_Amount: rawAmount, Ds_Currency: rawCurrency } = params;
|
|
3421
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- dealth within scope
|
|
3434
3422
|
const currencyInt = Number.parseInt(rawCurrency).toString();
|
|
3435
3423
|
const currencyInfo = REV_CURRENCIES[currencyInt];
|
|
3436
|
-
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
|
|
3424
|
+
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions, @typescript-eslint/no-unnecessary-condition -- proper object
|
|
3437
3425
|
if (!currencyInfo) {
|
|
3438
3426
|
throw new ParseError('Unknown currency', rawCurrency);
|
|
3439
3427
|
}
|
|
@@ -3447,6 +3435,7 @@ const formatPrice = (params) => {
|
|
|
3447
3435
|
};
|
|
3448
3436
|
};
|
|
3449
3437
|
const formatLang = (rawLang) => {
|
|
3438
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- dealth within scope
|
|
3450
3439
|
const langInt = Number.parseInt(rawLang).toString();
|
|
3451
3440
|
return REV_LANGUAGES[langInt];
|
|
3452
3441
|
};
|
|
@@ -3496,7 +3485,9 @@ const soapNotificationOutputFormatter = (raw) => {
|
|
|
3496
3485
|
};
|
|
3497
3486
|
};
|
|
3498
3487
|
const requestOutputFormatter = (raw) => {
|
|
3499
|
-
const {
|
|
3488
|
+
const {
|
|
3489
|
+
// eslint-disable-next-line @typescript-eslint/no-deprecated -- robust to old format
|
|
3490
|
+
Ds_CardNumber: deprecatedCardNumber, Ds_Card_Number: newCardNumber, Ds_Merchant_Identifier: identifier, Ds_ExpiryDate: rawExpiryDate, Ds_UrlPago2Fases: payURL, Ds_Language: rawLang } = raw;
|
|
3500
3491
|
const cardNumber = newCardNumber !== null && newCardNumber !== void 0 ? newCardNumber : deprecatedCardNumber;
|
|
3501
3492
|
return {
|
|
3502
3493
|
...baseOutputFormatter(raw),
|
package/lib/index.js
CHANGED
|
@@ -1758,9 +1758,7 @@ const getSISErrorCodeMessage = (code) => {
|
|
|
1758
1758
|
}
|
|
1759
1759
|
return SIS_ERROR_CODES[code.trim()];
|
|
1760
1760
|
};
|
|
1761
|
-
const getHTTPErrorCodeMessage = (code) =>
|
|
1762
|
-
return http.STATUS_CODES[code];
|
|
1763
|
-
};
|
|
1761
|
+
const getHTTPErrorCodeMessage = (code) => http.STATUS_CODES[code];
|
|
1764
1762
|
|
|
1765
1763
|
/**
|
|
1766
1764
|
* Error superclass for all redsys-easy specific errors
|
|
@@ -1836,7 +1834,9 @@ class ParseError extends RedsysError {
|
|
|
1836
1834
|
class HTTPError extends RedsysError {
|
|
1837
1835
|
constructor({ message, code, response }) {
|
|
1838
1836
|
const codeDescription = getHTTPErrorCodeMessage(code);
|
|
1839
|
-
const codeInfo =
|
|
1837
|
+
const codeInfo =
|
|
1838
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- robustness
|
|
1839
|
+
code !== undefined
|
|
1840
1840
|
? ` ${code}${codeDescription != null ? `: ${codeDescription}` : ''}`
|
|
1841
1841
|
: '';
|
|
1842
1842
|
const augmentationLine = message != null && message ? `\n${message}` : '';
|
|
@@ -1867,7 +1867,9 @@ class HTTPError extends RedsysError {
|
|
|
1867
1867
|
class GatewayError extends RedsysError {
|
|
1868
1868
|
constructor({ message, code, response }) {
|
|
1869
1869
|
const codeDescription = getSISErrorCodeMessage(code);
|
|
1870
|
-
const codeInfo =
|
|
1870
|
+
const codeInfo =
|
|
1871
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- robustness
|
|
1872
|
+
code !== undefined
|
|
1871
1873
|
? ` ${code}${codeDescription != null ? `: ${codeDescription}` : ''}`
|
|
1872
1874
|
: '';
|
|
1873
1875
|
const augmentationLine = message != null && message ? `\n${message}` : '';
|
|
@@ -1898,7 +1900,9 @@ class GatewayError extends RedsysError {
|
|
|
1898
1900
|
class ResponseError extends RedsysError {
|
|
1899
1901
|
constructor({ message, code, response }) {
|
|
1900
1902
|
const codeDescription = getResponseCodeMessage(code);
|
|
1901
|
-
const codeInfo =
|
|
1903
|
+
const codeInfo =
|
|
1904
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- robustness
|
|
1905
|
+
code !== undefined
|
|
1902
1906
|
? ` ${code}${codeDescription != null ? `: ${codeDescription}` : ''}`
|
|
1903
1907
|
: '';
|
|
1904
1908
|
const augmentationLine = message != null && message ? `\n${message}` : '';
|
|
@@ -1929,17 +1933,17 @@ const deserializeJSONMerchantParams = (strPayload) => {
|
|
|
1929
1933
|
if (typeof strPayload !== 'string') {
|
|
1930
1934
|
throw new ParseError('Payload must be a base-64 encoded string');
|
|
1931
1935
|
}
|
|
1936
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- assume it satisfies format
|
|
1932
1937
|
const payload = JSON.parse(Buffer.from(strPayload, 'base64').toString('utf8'));
|
|
1933
1938
|
if (typeof payload !== 'object' || payload == null) {
|
|
1934
1939
|
throw new ParseError('Cannot parse notification payload');
|
|
1935
1940
|
}
|
|
1936
1941
|
return payload;
|
|
1937
1942
|
};
|
|
1938
|
-
const serializeJSONMerchantParams = (requestParams) =>
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
};
|
|
1943
|
+
const serializeJSONMerchantParams = (requestParams) =>
|
|
1944
|
+
// Official redsys docs escape "/" because they use php json_encode. I think
|
|
1945
|
+
// this is wrong and it also works without escaping.
|
|
1946
|
+
Buffer.from(JSON.stringify(requestParams), 'utf8').toString('base64');
|
|
1943
1947
|
|
|
1944
1948
|
/**
|
|
1945
1949
|
* Adds padding to a buffer.
|
|
@@ -1973,7 +1977,7 @@ const encrypt3DES = (key, message) => {
|
|
|
1973
1977
|
]);
|
|
1974
1978
|
// Make sure that encrypted buffer is not longer than the padded message
|
|
1975
1979
|
const maxLength = Math.ceil(messageBuf.length / 8) * 8;
|
|
1976
|
-
return encryptedBuf.
|
|
1980
|
+
return encryptedBuf.subarray(0, maxLength);
|
|
1977
1981
|
};
|
|
1978
1982
|
/**
|
|
1979
1983
|
* Compute HMAC_SHA256_V1 signature
|
|
@@ -1993,9 +1997,7 @@ const sha256Sign = (merchantKey, order, params) => {
|
|
|
1993
1997
|
/**
|
|
1994
1998
|
* Discrete uniform distribution with domain (0 to *max*)
|
|
1995
1999
|
*/
|
|
1996
|
-
const drawPositiveDiscreteUniform = (max) =>
|
|
1997
|
-
return node_crypto.randomInt(max + 1);
|
|
1998
|
-
};
|
|
2000
|
+
const drawPositiveDiscreteUniform = (max) => node_crypto.randomInt(max + 1);
|
|
1999
2001
|
const alphanumeric = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
|
|
2000
2002
|
/**
|
|
2001
2003
|
* Generates a random order ID following redsys requirements
|
|
@@ -2016,7 +2018,6 @@ const randomTransactionId = () => {
|
|
|
2016
2018
|
return `${numStr}${alphanumStr}`;
|
|
2017
2019
|
};
|
|
2018
2020
|
const extractAndAssertOrderFromRequestParams = (requestParams) => {
|
|
2019
|
-
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions, @typescript-eslint/prefer-nullish-coalescing
|
|
2020
2021
|
const order = requestParams['DS_MERCHANT_ORDER'] ||
|
|
2021
2022
|
requestParams['Ds_Merchant_Order'];
|
|
2022
2023
|
if (typeof order !== 'string' || !order) {
|
|
@@ -2025,7 +2026,6 @@ const extractAndAssertOrderFromRequestParams = (requestParams) => {
|
|
|
2025
2026
|
return order;
|
|
2026
2027
|
};
|
|
2027
2028
|
const extractAndAssertOrderFromResponseParams = (responseParams) => {
|
|
2028
|
-
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions, @typescript-eslint/prefer-nullish-coalescing
|
|
2029
2029
|
const order = responseParams['Ds_Order'] ||
|
|
2030
2030
|
responseParams['DS_ORDER'];
|
|
2031
2031
|
if (typeof order !== 'string' || !order) {
|
|
@@ -2088,11 +2088,7 @@ const isStringNotEmpty = (str) => str != null && str.length > 0;
|
|
|
2088
2088
|
*
|
|
2089
2089
|
* If value is defined apply function and return output, otherwise return undefined.
|
|
2090
2090
|
*/
|
|
2091
|
-
const mapMaybeMonad = (value, fn) =>
|
|
2092
|
-
return value !== undefined
|
|
2093
|
-
? fn(value)
|
|
2094
|
-
: undefined;
|
|
2095
|
-
};
|
|
2091
|
+
const mapMaybeMonad = (value, fn) => (value !== undefined ? fn(value) : undefined);
|
|
2096
2092
|
|
|
2097
2093
|
const sha256VerifyJSONResponse = (merchantKey, response, responseParams) => {
|
|
2098
2094
|
if (response.Ds_SignatureVersion !== 'HMAC_SHA256_V1') {
|
|
@@ -2131,27 +2127,27 @@ const serializeAndSignJSONRequest = (merchantKey, requestParams) => {
|
|
|
2131
2127
|
/**
|
|
2132
2128
|
* Tries to decode x-www-form-urlencoded values of an object
|
|
2133
2129
|
*/
|
|
2134
|
-
const decodeUrlEntries = (params) =>
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
catch (_) { }
|
|
2130
|
+
const decodeUrlEntries = (params) =>
|
|
2131
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- type is unchanged
|
|
2132
|
+
Object.fromEntries(Object.entries(params).map((entry) => {
|
|
2133
|
+
let processedValue = entry[1];
|
|
2134
|
+
if (typeof entry[1] === 'string') {
|
|
2135
|
+
try {
|
|
2136
|
+
processedValue = decodeURIComponent(entry[1].replaceAll('+', ' '));
|
|
2142
2137
|
}
|
|
2143
|
-
|
|
2144
|
-
}
|
|
2145
|
-
|
|
2138
|
+
catch (_) { }
|
|
2139
|
+
}
|
|
2140
|
+
return [entry[0], processedValue];
|
|
2141
|
+
}));
|
|
2146
2142
|
|
|
2147
2143
|
const serializeSoapNotificationResponse = ({ allow: allowOperation }) => {
|
|
2148
2144
|
const answer = allowOperation ? 'OK' : 'KO';
|
|
2149
2145
|
return `<Response Ds_Version="0.0"><Ds_Response_Merchant>${answer}</Ds_Response_Merchant></Response>`;
|
|
2150
2146
|
};
|
|
2151
2147
|
const xmlParser = new fastXmlParser.XMLParser({ parseTagValue: false });
|
|
2152
|
-
const deserializeSoapNotification = (xml) =>
|
|
2153
|
-
|
|
2154
|
-
|
|
2148
|
+
const deserializeSoapNotification = (xml) =>
|
|
2149
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- assume xml matches schema
|
|
2150
|
+
xmlParser.parse(xml).Message;
|
|
2155
2151
|
|
|
2156
2152
|
const verifySoapNotification = (merchantKey, xml, msg) => {
|
|
2157
2153
|
const startToken = '<Request';
|
|
@@ -2164,6 +2160,7 @@ const verifySoapNotification = (merchantKey, xml, msg) => {
|
|
|
2164
2160
|
const signedStr = xml.slice(startPos, endPos + endToken.length);
|
|
2165
2161
|
const signature = msg.Signature;
|
|
2166
2162
|
const order = msg.Request.Ds_Order;
|
|
2163
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- robustness check
|
|
2167
2164
|
if (order == null || !order) {
|
|
2168
2165
|
throw new ParseError('Invalid notification response');
|
|
2169
2166
|
}
|
|
@@ -2197,6 +2194,7 @@ const jsonRequest = async ({ fetch, url, merchantKey, rawRequestParams }) => {
|
|
|
2197
2194
|
},
|
|
2198
2195
|
body: JSON.stringify(payload)
|
|
2199
2196
|
});
|
|
2197
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- assume correct format
|
|
2200
2198
|
const responseData = await response.json();
|
|
2201
2199
|
if (!response.ok) {
|
|
2202
2200
|
throw new HTTPError({
|
|
@@ -2248,6 +2246,7 @@ const createRedsysAPI = (config) => {
|
|
|
2248
2246
|
throw new RedsysError('A secretKey key must be provided');
|
|
2249
2247
|
}
|
|
2250
2248
|
if (typeof config.urls !== 'object' ||
|
|
2249
|
+
// eslint-disable-next-line @typescript-eslint/prefer-optional-chain, @typescript-eslint/no-unnecessary-condition -- robustness
|
|
2251
2250
|
config.urls == null ||
|
|
2252
2251
|
!config.urls.restIniciaPeticion ||
|
|
2253
2252
|
!config.urls.restTrataPeticion ||
|
|
@@ -2257,14 +2256,12 @@ const createRedsysAPI = (config) => {
|
|
|
2257
2256
|
/**
|
|
2258
2257
|
* Sends a iniciaPeticion request using REST interface
|
|
2259
2258
|
*/
|
|
2260
|
-
const restIniciaPeticion = async (paramsInput) => {
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
});
|
|
2267
|
-
};
|
|
2259
|
+
const restIniciaPeticion = async (paramsInput) => await jsonRequest({
|
|
2260
|
+
fetch,
|
|
2261
|
+
url: config.urls.restIniciaPeticion,
|
|
2262
|
+
merchantKey: config.secretKey,
|
|
2263
|
+
rawRequestParams: paramsInput
|
|
2264
|
+
});
|
|
2268
2265
|
/**
|
|
2269
2266
|
* Sends a trataPeticion request using REST interface
|
|
2270
2267
|
*/
|
|
@@ -2302,10 +2299,9 @@ const createRedsysAPI = (config) => {
|
|
|
2302
2299
|
*/
|
|
2303
2300
|
const processDirectRestNotification = (
|
|
2304
2301
|
/** Body of JSON notification, as a POJO (Plain Old Javascript Object) */
|
|
2305
|
-
body) =>
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
};
|
|
2302
|
+
body) =>
|
|
2303
|
+
// A notification can't contain a gateway error, it didn't initiate the request
|
|
2304
|
+
deserializeAndVerifyJSONResponse(config.secretKey, body);
|
|
2309
2305
|
/**
|
|
2310
2306
|
* Processes either a URLOK/URLKO query or a JSON REST notification
|
|
2311
2307
|
*
|
|
@@ -2331,9 +2327,7 @@ const createRedsysAPI = (config) => {
|
|
|
2331
2327
|
*/
|
|
2332
2328
|
const processSoapNotification = (
|
|
2333
2329
|
/** SOAP notification as a XML string */
|
|
2334
|
-
xml) =>
|
|
2335
|
-
return deserializeAndVerifySoapNotification(config.secretKey, xml);
|
|
2336
|
-
};
|
|
2330
|
+
xml) => deserializeAndVerifySoapNotification(config.secretKey, xml);
|
|
2337
2331
|
/**
|
|
2338
2332
|
* Creates an answer for a SOAP notification, serializes and signs it
|
|
2339
2333
|
*/
|
|
@@ -2341,12 +2335,10 @@ const createRedsysAPI = (config) => {
|
|
|
2341
2335
|
/** Order identifier */
|
|
2342
2336
|
order,
|
|
2343
2337
|
/** Indicates if the payment is allowed to proceed */
|
|
2344
|
-
allow) => {
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
});
|
|
2349
|
-
};
|
|
2338
|
+
allow) => serializeAndSignSoapNotificationResponse(config.secretKey, {
|
|
2339
|
+
order,
|
|
2340
|
+
allow
|
|
2341
|
+
});
|
|
2350
2342
|
return {
|
|
2351
2343
|
restIniciaPeticion,
|
|
2352
2344
|
restTrataPeticion,
|
|
@@ -2384,45 +2376,41 @@ const create3DSMethodForm = (emv3dsParams, notificationURL) => {
|
|
|
2384
2376
|
*
|
|
2385
2377
|
* @public
|
|
2386
2378
|
*/
|
|
2387
|
-
const create3DSv1ChallengeForm = (emv3dsV1Challenge, challengeNotificationUrl) => {
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
};
|
|
2396
|
-
};
|
|
2379
|
+
const create3DSv1ChallengeForm = (emv3dsV1Challenge, challengeNotificationUrl) => ({
|
|
2380
|
+
url: emv3dsV1Challenge.acsURL,
|
|
2381
|
+
body: {
|
|
2382
|
+
PaReq: emv3dsV1Challenge.PAReq,
|
|
2383
|
+
MD: emv3dsV1Challenge.MD,
|
|
2384
|
+
TermUrl: challengeNotificationUrl
|
|
2385
|
+
}
|
|
2386
|
+
});
|
|
2397
2387
|
/**
|
|
2398
2388
|
* Creates parameters for a 3DS v2 challenge form
|
|
2399
2389
|
*
|
|
2400
2390
|
* @public
|
|
2401
2391
|
*/
|
|
2402
|
-
const create3DSv2ChallengeForm = (emv3dsV2Challenge) => {
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
};
|
|
2409
|
-
};
|
|
2392
|
+
const create3DSv2ChallengeForm = (emv3dsV2Challenge) => ({
|
|
2393
|
+
url: emv3dsV2Challenge.acsURL,
|
|
2394
|
+
body: {
|
|
2395
|
+
creq: emv3dsV2Challenge.creq
|
|
2396
|
+
}
|
|
2397
|
+
});
|
|
2410
2398
|
/**
|
|
2411
2399
|
* Deserialize threeDSMethodData
|
|
2412
2400
|
*
|
|
2413
2401
|
* @public
|
|
2414
2402
|
*/
|
|
2415
|
-
const deserializeThreeDSMethodData = (threeDSMethodData) =>
|
|
2416
|
-
|
|
2417
|
-
|
|
2403
|
+
const deserializeThreeDSMethodData = (threeDSMethodData) =>
|
|
2404
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- assume it satisfies format
|
|
2405
|
+
JSON.parse(base64url.decode(threeDSMethodData));
|
|
2418
2406
|
/**
|
|
2419
2407
|
* Deserialize `cres` field of a 3DS v2 challenge
|
|
2420
2408
|
*
|
|
2421
2409
|
* @public
|
|
2422
2410
|
*/
|
|
2423
|
-
const deserializeCres = (cres) =>
|
|
2424
|
-
|
|
2425
|
-
|
|
2411
|
+
const deserializeCres = (cres) =>
|
|
2412
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- assume it satisfies format
|
|
2413
|
+
JSON.parse(Buffer.from(cres, 'base64').toString('utf8'));
|
|
2426
2414
|
|
|
2427
2415
|
const unescapeXML = (str) => {
|
|
2428
2416
|
const xml = str.replace(/&(lt|#60|gt|#62|quot|#34|amp|#38|apos|#39);/g, (_match, p1) => {
|
|
@@ -2763,10 +2751,11 @@ const CURRENCIES = {
|
|
|
2763
2751
|
*
|
|
2764
2752
|
* @public
|
|
2765
2753
|
*/
|
|
2766
|
-
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
2754
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions, @typescript-eslint/no-unsafe-type-assertion -- known deterministically
|
|
2767
2755
|
const REV_CURRENCIES = {};
|
|
2768
2756
|
for (const [key, value] of Object.entries(CURRENCIES)) {
|
|
2769
2757
|
REV_CURRENCIES[value.num] = {
|
|
2758
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- known deterministically
|
|
2770
2759
|
code: key,
|
|
2771
2760
|
decimals: value.decimals
|
|
2772
2761
|
};
|
|
@@ -2822,8 +2811,9 @@ const LANGUAGES = {
|
|
|
2822
2811
|
*
|
|
2823
2812
|
* @public
|
|
2824
2813
|
*/
|
|
2825
|
-
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
2814
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions, @typescript-eslint/no-unsafe-type-assertion -- known deterministically
|
|
2826
2815
|
const REV_LANGUAGES = {};
|
|
2816
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- known deterministically
|
|
2827
2817
|
for (const [key, value] of Object.entries(LANGUAGES)) {
|
|
2828
2818
|
REV_LANGUAGES[value] = key;
|
|
2829
2819
|
}
|
|
@@ -3097,8 +3087,9 @@ const COUNTRIES = {
|
|
|
3097
3087
|
*
|
|
3098
3088
|
* @public
|
|
3099
3089
|
*/
|
|
3100
|
-
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
3090
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions, @typescript-eslint/no-unsafe-type-assertion -- known deterministically
|
|
3101
3091
|
const REV_COUNTRIES = {};
|
|
3092
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- known deterministically
|
|
3102
3093
|
for (const [key, value] of Object.entries(COUNTRIES)) {
|
|
3103
3094
|
REV_COUNTRIES[value] = key;
|
|
3104
3095
|
}
|
|
@@ -3126,8 +3117,9 @@ const CARDBRANDS = {
|
|
|
3126
3117
|
*
|
|
3127
3118
|
* @public
|
|
3128
3119
|
*/
|
|
3129
|
-
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
3120
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions, @typescript-eslint/no-unsafe-type-assertion -- known deterministically
|
|
3130
3121
|
const REV_CARDBRANDS = {};
|
|
3122
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- known deterministically
|
|
3131
3123
|
for (const [key, value] of Object.entries(CARDBRANDS)) {
|
|
3132
3124
|
REV_CARDBRANDS[value] = key;
|
|
3133
3125
|
}
|
|
@@ -3193,29 +3185,23 @@ const TRANSACTION_TYPES = {
|
|
|
3193
3185
|
*
|
|
3194
3186
|
* @public
|
|
3195
3187
|
*/
|
|
3196
|
-
const useSingleInputFormatter = (fn, inputFormatter) =>
|
|
3197
|
-
return (input, ...otherArgs) => fn(inputFormatter(input), ...otherArgs);
|
|
3198
|
-
};
|
|
3188
|
+
const useSingleInputFormatter = (fn, inputFormatter) => (input, ...otherArgs) => fn(inputFormatter(input), ...otherArgs);
|
|
3199
3189
|
/**
|
|
3200
3190
|
* Applies an output formatter to a function
|
|
3201
3191
|
*
|
|
3202
3192
|
* @public
|
|
3203
3193
|
*/
|
|
3204
|
-
const useOutputFormatter = (fn, outputFormatter) =>
|
|
3205
|
-
return (a) => outputFormatter(fn(a));
|
|
3206
|
-
};
|
|
3194
|
+
const useOutputFormatter = (fn, outputFormatter) => (a) => outputFormatter(fn(a));
|
|
3207
3195
|
/**
|
|
3208
3196
|
* Applies an output formatter to the resolved ouput promise of a function
|
|
3209
3197
|
*
|
|
3210
3198
|
* @public
|
|
3211
3199
|
*/
|
|
3212
|
-
const usePromiseOutputFormatter = (fn, outputFormatter) =>
|
|
3213
|
-
return async (a) => outputFormatter(await fn(a));
|
|
3214
|
-
};
|
|
3200
|
+
const usePromiseOutputFormatter = (fn, outputFormatter) => async (a) => outputFormatter(await fn(a));
|
|
3215
3201
|
|
|
3202
|
+
/* eslint-disable @typescript-eslint/no-unnecessary-condition -- robustness handling inputs */
|
|
3216
3203
|
const formatInputCurrency = (input) => {
|
|
3217
3204
|
const currencyData = CURRENCIES[input];
|
|
3218
|
-
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
|
|
3219
3205
|
if (!(currencyData === null || currencyData === void 0 ? void 0 : currencyData.num)) {
|
|
3220
3206
|
throw new ValidationError('Unsupported currency', { currency: input });
|
|
3221
3207
|
}
|
|
@@ -3239,8 +3225,7 @@ const formatAmount = (value, context) => {
|
|
|
3239
3225
|
throw new ValidationError('Missing currency', { currency });
|
|
3240
3226
|
}
|
|
3241
3227
|
const currencyData = CURRENCIES[currency];
|
|
3242
|
-
|
|
3243
|
-
if (!(currencyData === null || currencyData === void 0 ? void 0 : currencyData.decimals)) {
|
|
3228
|
+
if (!Number.isFinite(currencyData === null || currencyData === void 0 ? void 0 : currencyData.decimals)) {
|
|
3244
3229
|
throw new ValidationError('Unsupported currency', { currency });
|
|
3245
3230
|
}
|
|
3246
3231
|
const rawValue = typeof value === 'number'
|
|
@@ -3398,10 +3383,12 @@ const restTrataPeticionInputFormatter = (raw) => {
|
|
|
3398
3383
|
dayjs.extend(timezone);
|
|
3399
3384
|
dayjs.extend(utc);
|
|
3400
3385
|
const formatCountry = (rawCountry) => {
|
|
3386
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- dealth within scope
|
|
3401
3387
|
const countryInt = Number.parseInt(rawCountry).toString();
|
|
3402
3388
|
return REV_COUNTRIES[countryInt];
|
|
3403
3389
|
};
|
|
3404
3390
|
const formatCardBrand = (rawCardBrand) => {
|
|
3391
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- dealth within scope
|
|
3405
3392
|
const cardBrandInt = Number.parseInt(rawCardBrand).toString();
|
|
3406
3393
|
return REV_CARDBRANDS[cardBrandInt];
|
|
3407
3394
|
};
|
|
@@ -3433,9 +3420,10 @@ const baseOutputFormatter = (raw) => {
|
|
|
3433
3420
|
};
|
|
3434
3421
|
const formatPrice = (params) => {
|
|
3435
3422
|
const { Ds_Amount: rawAmount, Ds_Currency: rawCurrency } = params;
|
|
3423
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- dealth within scope
|
|
3436
3424
|
const currencyInt = Number.parseInt(rawCurrency).toString();
|
|
3437
3425
|
const currencyInfo = REV_CURRENCIES[currencyInt];
|
|
3438
|
-
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
|
|
3426
|
+
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions, @typescript-eslint/no-unnecessary-condition -- proper object
|
|
3439
3427
|
if (!currencyInfo) {
|
|
3440
3428
|
throw new ParseError('Unknown currency', rawCurrency);
|
|
3441
3429
|
}
|
|
@@ -3449,6 +3437,7 @@ const formatPrice = (params) => {
|
|
|
3449
3437
|
};
|
|
3450
3438
|
};
|
|
3451
3439
|
const formatLang = (rawLang) => {
|
|
3440
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- dealth within scope
|
|
3452
3441
|
const langInt = Number.parseInt(rawLang).toString();
|
|
3453
3442
|
return REV_LANGUAGES[langInt];
|
|
3454
3443
|
};
|
|
@@ -3498,7 +3487,9 @@ const soapNotificationOutputFormatter = (raw) => {
|
|
|
3498
3487
|
};
|
|
3499
3488
|
};
|
|
3500
3489
|
const requestOutputFormatter = (raw) => {
|
|
3501
|
-
const {
|
|
3490
|
+
const {
|
|
3491
|
+
// eslint-disable-next-line @typescript-eslint/no-deprecated -- robust to old format
|
|
3492
|
+
Ds_CardNumber: deprecatedCardNumber, Ds_Card_Number: newCardNumber, Ds_Merchant_Identifier: identifier, Ds_ExpiryDate: rawExpiryDate, Ds_UrlPago2Fases: payURL, Ds_Language: rawLang } = raw;
|
|
3502
3493
|
const cardNumber = newCardNumber !== null && newCardNumber !== void 0 ? newCardNumber : deprecatedCardNumber;
|
|
3503
3494
|
return {
|
|
3504
3495
|
...baseOutputFormatter(raw),
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "redsys-easy",
|
|
3
3
|
"description": "Node JS interface for Redsys payment gateway",
|
|
4
|
-
"version": "5.2.
|
|
4
|
+
"version": "5.2.8",
|
|
5
5
|
"author": "Javier Garcia <javiertury@gmail.com>",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"keywords": [
|
|
@@ -59,22 +59,15 @@
|
|
|
59
59
|
"@types/koa": "^2.13.8",
|
|
60
60
|
"@types/koa-bodyparser": "^4.3.10",
|
|
61
61
|
"@types/koa__router": "^12.0.0",
|
|
62
|
-
"@typescript-eslint/eslint-plugin": "^5.62.0",
|
|
63
|
-
"@typescript-eslint/parser": "^5.62.0",
|
|
64
62
|
"cheerio": "1.0.0-rc.12",
|
|
65
63
|
"commit-and-tag-version": "^12.5.0",
|
|
66
|
-
"eslint": "^
|
|
67
|
-
"eslint-config-
|
|
68
|
-
"eslint-
|
|
69
|
-
"eslint-plugin-html": "^7.1.0",
|
|
70
|
-
"eslint-plugin-import": "^2.28.0",
|
|
71
|
-
"eslint-plugin-jest": "^27.2.3",
|
|
72
|
-
"eslint-plugin-node": "^11.1.0",
|
|
73
|
-
"eslint-plugin-promise": "^6.1.1",
|
|
64
|
+
"eslint": "^9.17.0",
|
|
65
|
+
"eslint-config-love": "^114.0.0",
|
|
66
|
+
"eslint-plugin-jest": "^28.10.0",
|
|
74
67
|
"jest": "^29.6.2",
|
|
75
68
|
"koa": "^2.14.2",
|
|
76
69
|
"koa-bodyparser": "^4.4.1",
|
|
77
|
-
"lint-staged": "^
|
|
70
|
+
"lint-staged": "^15.3.0",
|
|
78
71
|
"markdown-magic": "^2.6.1",
|
|
79
72
|
"rimraf": "^5.0.1",
|
|
80
73
|
"rollup": "^3.29.5",
|
|
@@ -105,7 +98,7 @@
|
|
|
105
98
|
}
|
|
106
99
|
},
|
|
107
100
|
"scripts": {
|
|
108
|
-
"lint": "eslint
|
|
101
|
+
"lint": "eslint .",
|
|
109
102
|
"build:clean": "rimraf lib",
|
|
110
103
|
"build:code": "rollup -c rollup.config.mjs",
|
|
111
104
|
"build:api": "api-extractor run --local",
|