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

37.17% Statements 29/78
100% Branches 1/1
0% Functions 0/1
37.17% Lines 29/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  
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;
    };
});