Untitled

mail@pastecode.io avatar
unknown
plain_text
2 months ago
9.1 kB
2
Indexable
Never
import { APIGatewayEvent } from "aws-lambda";
import * as B from "fp-ts/boolean";
import * as C from "fp-ts/Console";
import { pipe } from "fp-ts/function";
import * as TE from "fp-ts/TaskEither";

import { readUrl } from "@lambdas/shared/configuration";
import { catchAndLogTE } from "@lambdas/shared/errors/CatchAndLog";
import * as Json from "@lambdas/shared/json";
import {
  LAMBDA_FUNCTION_TIMEOUT_DURATION,
  calculateSentryTimeoutWarningLimit,
} from "@lambdas/shared/monitoring/lambda";
import { decryptDbsNotificationMessage } from "@lambdas/shared/pgp";
import { EDDAMandateRepository } from "@lambdas/shared/repositories/EDDAMandateRepository";
import {
  getBankDetailsRepository,
  getEddaCollectFundsRequestRepo,
  getEddaMandateRepo,
} from "@lambdas/shared/repositoryFactory";
import { sendEddaAccountLinkFailEvent } from "@lambdas/shared/services/blueshift/sendEddaAccountLinkFailEvent";
import {
  initializeSentry,
  sentryWrapHandler,
} from "@lambdas/shared/services/sentry";
import { genericApiHandler } from "@lambdas/shared/sharedApiHandler";
import { sendMessagesToQueue } from "@lambdas/shared/sqs/sendMessagesToQueue";
import { HandlerContext, noPayload } from "@lambdas/shared/types";

import * as creationCallbackPayloadParser from "./creationCallbackParser";
import { getRequestIdFromEvent } from "./getRequestIdFromEvent";
import { encodeError, encodeSuccess } from "./httpResponseEncoder";
import {
  isLinkedAccountExistError,
  linkedAccountExistError,
} from "./linkedAccountExistError";
import { logError, logSuccess } from "./logger";
import { EDDAMandateWithBank } from "@lambdas/shared/models/EDDAMandate";
import { EDDACollectFundsRequest } from "@lambdas/shared/models/EDDACollectFundsRequest";
import { sendEgiroLinkAbortedDuplicateAccountPendingCollection } from "@lambdas/shared/services/blueshift/sendEgiroLinkAbortedDuplicateAccountPendingCollection";

initializeSentry();
export const handler = sentryWrapHandler(
  (event: APIGatewayEvent, context: HandlerContext) =>
    genericApiHandler(
      {
        event,
        name: "dbs-egiro-creation-callback",
        context,
        isEventBodyRequired: true,
      },
      (event: APIGatewayEvent) =>
        pipe(
          TE.Do,
          TE.bindW("requestId", () =>
            TE.fromEither(getRequestIdFromEvent(event))
          ),
          TE.bindW("payload", ({ requestId }) =>
            getRequestPayload(event, requestId)
          ),
          TE.bindW("eddaMandateRepo", getEddaMandateRepo),
          TE.bindW("eddaMandate", ({ eddaMandateRepo, payload }) =>
            eddaMandateRepo.findByTransactionIdWithBankAndUser(
              payload.boTransactionRefNo
            )
          ),
          TE.bindW("validBankAccount", ({ eddaMandate, payload }) =>
            validateBankAccount(
              eddaMandate.userId,
              payload.applicantBankCode!,
              payload.applicantBankAccNo!
            )
          ),
          TE.chainFirstW(({ eddaMandateRepo, payload, validBankAccount }) =>
            completeTokenReceipt(validBankAccount, eddaMandateRepo, payload)
          ),
          TE.chainFirstW(({ payload }) =>
            !!payload.errorMessage
              ? catchAndLogTE(C)(
                "Send edda linking fail event error with reason"
              )(publishRejectEventToBlueshift(payload.boTransactionRefNo))
              : handleSendMessageToQueue(payload)
          ),
          TE.orElseFirstIOK(logError),
          TE.chainFirstIOK(logSuccess),
          TE.match(encodeError, ({ requestId }) => encodeSuccess(requestId))
        )()
    ),
  {
    timeoutWarningLimit: calculateSentryTimeoutWarningLimit(
      LAMBDA_FUNCTION_TIMEOUT_DURATION.dbsEgiroCreationToken
    ),
  }
);

const getRequestPayload = (event: APIGatewayEvent, requestId: string) =>
  pipe(
    event.body,
    TE.fromNullable(noPayload()),
    TE.chainW(decryptDbsNotificationMessage),
    TE.chainEitherKW(Json.decode),
    TE.chainEitherKW((payload) =>
      creationCallbackPayloadParser.parse(payload, requestId)
    )
  );

const composeMessage = (
  payload: creationCallbackPayloadParser.CreationCallbackPayload
) => ({
  Id: payload.boTransactionRefNo,
  MessageGroupId: payload.boTransactionRefNo,
  MessageBody: JSON.stringify({
    applicantBankAccNo: payload.applicantBankAccNo,
    applicantBankCode: payload.applicantBankCode,
    boDDARefNo: payload.boDDARefNo,
    boTransactionRefNo: payload.boTransactionRefNo,
    bankToken: payload.bankToken,
    maxAmount: payload.maxAmount,
    fromDate: payload.fromDate?.toFormat("dd-MM-yyyy"),
    toDate: payload.toDate?.toFormat("dd-MM-yyyy"),
  }),
});

const handleSendMessageToQueue = (
  payload: creationCallbackPayloadParser.CreationCallbackPayload
) =>
  pipe(
    readUrl("QUEUE_URL"),
    TE.fromEither,
    TE.chainW((queueUrl) =>
      sendMessagesToQueue(queueUrl, [composeMessage(payload)])
    ),
    TE.map(() => undefined)
  );

const publishRejectEventToBlueshift = (transactionId: string) =>
  pipe(
    getEddaMandateRepo(),
    TE.chainW((repo) => repo.findByTransactionIdWithBankAndUser(transactionId)),
    TE.chainW(sendEddaAccountLinkFailEvent)
  );

const validateBankAccount = (
  userId: string,
  bic: string,
  accountNumber: string
) =>
  pipe(
    getBankDetailsRepository(),
    TE.chainW((repo) => repo.findByBicAndAccountNumber(bic, accountNumber)),
    TE.chainW(
      TE.fromPredicate(
        (bankDetail) => !bankDetail || bankDetail.userId === userId,
        linkedAccountExistError
      )
    ),
    TE.chainW(() => TE.of(true)),
    TE.orElseW((error) => {
      if (isLinkedAccountExistError(error)) {
        console.error("The requested bank account was already registered");

        return TE.right(false);
      }

      return TE.left(error);
    })
  );

const completeTokenReceipt = (
  validBankAccount: boolean,
  eddaMandateRepo: EDDAMandateRepository,
  payload: creationCallbackPayloadParser.CreationCallbackPayload
) =>
  pipe(
    validBankAccount,
    B.foldW(
      () =>
        eddaMandateRepo.completeTokenReceipt(
          payload.boTransactionRefNo,
          payload.boDDARefNo,
          false,
          undefined,
          `Complete token receipt: The requested bank account was already registered`
        ),
      () =>
        eddaMandateRepo.completeTokenReceipt(
          payload.boTransactionRefNo,
          payload.boDDARefNo,
          !payload.errorMessage,
          payload.applicantBankAccNo,
          payload.errorMessage &&
          `Complete token receipt: ${payload.errorMessage}`,
          payload.toDate
        )
    )
  );

const validateDuplidateEddaMandate = (
  eddaMandateRepo: EDDAMandateRepository,
  payload: creationCallbackPayloadParser.CreationCallbackPayload
) =>
  pipe(
    // !payload.errorMessage &&,
    // B.foldW(
    //   () =>
    //     ,
    //   () =>
    //     eddaMandateRepo.completeTokenReceipt(
    //       payload.boTransactionRefNo,
    //       payload.boDDARefNo,
    //       !payload.errorMessage,
    //       payload.applicantBankAccNo,
    //       payload.errorMessage &&
    //       `Complete token receipt: ${payload.errorMessage}`,
    //       payload.toDate
    //     )
    // )
    // getEddaCollectFundsRequestRepo(),
    // TE.chainW(
    //   TE.fromPredicate(
    //     (eddaMandate) => eddaMandate === null,
    //     () => pipe(
    //       getEddaCollectFundsRequestRepo(),
    //       TE.chainW((repo) => repo.findPendingRequestsByMandateTransactionId(payload.boTransactionRefNo)),
    //     )
    //   )
    // ),
    eddaMandateRepo.findActiveAccountsByAndBicAndBankAccountNumber(payload.applicantBankCode!, payload.applicantBankAccNo!),
    TE.bindTo("eddaMandate"),
    TE.bindW('collectFundsRequestRepo', getEddaCollectFundsRequestRepo),
    TE.bindW('pendingRequest', ({ collectFundsRequestRepo }) => collectFundsRequestRepo.findPendingRequestsByMandateTransactionId(payload.boTransactionRefNo)),
    TE.chainW(
      TE.fromPredicate(
        ({ eddaMandate }) => eddaMandate === null,
        ({ eddaMandate, pendingRequest }) => removeOldMandate(eddaMandate!, pendingRequest, eddaMandateRepo)
      )
    )
    // TE.chainW(({ eddaMandate, pendingRequest }) => pipe(
    //   eddaMandate === null || pendingRequest.length === 0,
    //   B.foldW(
    //     () => ,
    //     () =>
    //       eddaMandateRepo.completeTokenReceipt(
    //         payload.boTransactionRefNo,
    //         payload.boDDARefNo,
    //         !payload.errorMessage,
    //         payload.applicantBankAccNo,
    //         payload.errorMessage &&
    //         `Complete token receipt: ${payload.errorMessage}`,
    //         payload.toDate
    //       )
    //   )
    // ))
  );

const removeOldMandate = (
  eddaMandate: EDDAMandateWithBank,
  pendingRequest: EDDACollectFundsRequest[],
  eddaMandateRepo: EDDAMandateRepository,
) =>
  pipe(
    pendingRequest.length > 0,
    B.foldW(
      () => eddaMandateRepo.deleteByTransactionId(eddaMandate.transactionId),
      () => sendEgiroLinkAbortedDuplicateAccountPendingCollection(eddaMandate.userId)
    )
  )
Leave a Comment