All files / test/ctp/adapter/payshield decode.ts

67.94% Statements 53/78
20% Branches 2/10
100% Functions 1/1
67.94% Lines 53/78

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 791x 1x 1x 1x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x     2x 2x                               2x                 2x 2x 2x  
import {handler, type Errors, type IContext, type ILogger, type IMeta} from '@feasibleone/blong';
import bitsyntax from 'ut-bitsyntax';
 
export default handler(({config: {headerFormat, maskedKeys}, lib, lib: {mask}}) => {
    const errors = lib.errors as unknown as Errors<{
        [key: string]: unknown;
    }>;
    const commands = lib.commands as unknown as Record<
        string,
        {
            pattern: {name: string; size: number}[];
            code: string;
            warnings?: string[];
            method: string;
            mtid: 'request' | 'response';
            errorMatcher?: (...args: unknown[]) => {errorCode: string};
            matcher: (...args: unknown[]) => {errorCode: string};
        }
    >;
    const commandNames = lib.commandNames as unknown as Record<string, string>;
    const headerMatcher = bitsyntax.matcher(
        'headerNo:' + headerFormat + ', code:2/string, body/binary',
    ) as (...args: unknown[]) => {body: unknown; code: string; headerNo: string};
    const errorMatcher = bitsyntax.matcher('errorCode:2/string, rest/binary') as (
        ...args: unknown[]
    ) => {errorCode: string};
 
    return function decode(buff: Buffer, $meta: IMeta, context: IContext, log: ILogger) {
        const header: {body: unknown; code: string; headerNo: string} = headerMatcher(buff);
        if (!header) throw errors['payshield.unableMatchingHeaderPattern']({});
        const commandName = commandNames[header.code];
        if (!commandName)
            throw errors['payshield.unknownResponseCode']({params: {code: header.code}});
        const command = commands[commandName];
        if (!command) throw errors['payshield.notImplemented']({params: {opcode: commandName}});
 
        let result: {errorCode: string} = errorMatcher(header.body);
        if (!result) throw errors['payshield.unableMatchingResponseECode']({});
        // 00 = No error
        // 02 = Key inappropriate length for algorithm (in some cases is warning)
        const warning = (command.warnings && ['00'].concat(command.warnings)) || ['00'];
        $meta.trace = header.headerNo;
        $meta.method = command.method;
        if (warning.includes(result.errorCode)) {
            result = command.matcher(header.body);
            if (!result)
                throw errors['payshield.unableMatchingPattern']({
                    params: {opcode: commandName},
                });
            $meta.mtid = command.mtid;
        } else {
            let errorCode = 'generic';
            $meta.mtid = 'error';
            if (command.errorMatcher) {
                // try to match errorPattern if it exists
                errorCode = (command.errorMatcher(header.body) || result).errorCode || errorCode;
            } else if (result && result.errorCode) {
                errorCode = result.errorCode;
            }
            const error = (
                errors[`payshield.${command.method}.${errorCode}`] ||
                errors[`payshield.${errorCode}`]
            )({});
            log?.error?.(error);
            return error;
        }
        log?.trace?.({
            $meta: {mtid: 'frame', method: 'payshield.decode'},
            message: mask(buff.toString(), result, {
                pattern: command.pattern,
                maskedKeys,
                maskSymbol: '*',
            }),
            log: context?.session?.log,
        });
        return result;
    };
});