payments/src/lib/actions/handle_event.ts

import {
  Payment,
  ExternalPaymentEvent,
  PaymentProcessingState,
} from '../types';
import { matchExternalStatus } from '../status_matching';
import { tracer } from '@capchase/tracer';
import { convertFromPrisma } from '../converters';
import { prisma } from '../prisma';
import { create } from './create';
import { callLog } from '../call_log';
import { incrementMetric } from '@capchase/metrics';

const internalHandleEvent = async (
  event: ExternalPaymentEvent,
  actionBy: string
): Promise<Payment> => {
  await callLog({
    callName: 'handlePayment',
    actionBy,
    args: [event, actionBy],
  });

  const span = tracer?.scope()?.active();

  const {
    id,
    status,
    service,
    event_id,
    external_url,
    metadata,
    created_at,
    payment_id,
  } = event;

  span?.addTags({
    status,
    service,
    event_data: event,
  });

  const payment = convertFromPrisma(
    (
      await prisma.payment.findMany({
        take: 1,
        orderBy: [
          {
            id: 'desc',
          },
        ],
        // If the `payment_id` is set, then we can use
        // this directly for handling the event.
        where: payment_id
          ? { key: payment_id }
          : {
              externalKey: id,
            },
      })
    )[0]
  );

  const matchedStatus = matchExternalStatus(payment.status, service, status);

  incrementMetric('payment_system.payment.external_event', {
    status: matchedStatus,
    external_status: status,
    service,
    action_by: actionBy,
  });

  return await create({
    ...payment,
    status: matchedStatus,
    processingState: PaymentProcessingState.UNPROCESSED,
    externalKey: id,
    externalStatus: status,
    externalService: service,
    eventData: {
      id: event_id,
      url: external_url,
      metadata: metadata,
      createdAt: new Date(created_at).toISOString(),
    },
  });
};

/**
 * Handles an external event and process it.
 *
 * @param {ExternalPaymentEvent} paymentKey Payment key
 * @param {string} actionBy Action executor
 * @return {Promise<Payment>} The handled payment
 *
 * @async
 * @function
 * @memberOf module:payments/actions
 * @access public
 */
const handleEvent =
  tracer?.wrap('payments.handleEvent', internalHandleEvent) ||
  internalHandleEvent;

export { handleEvent };