payments/src/lib/actions/process.ts

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

const internalProcessPayment = async (
  paymentKey: string,
  processingKey: string,
  actionBy: string
): Promise<Payment> => {
  await callLog({
    callName: 'processPayment',
    actionBy,
    args: [paymentKey, processingKey, actionBy],
  });

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

  span?.addTags({
    payment_key: paymentKey,
    processing_key: processingKey,
    action_by: actionBy,
  });

  // TODO(SHI-1454): Validate actual user permissions
  if (actionBy.length === 0) {
    throw new Error('Action by should be set');
  }

  const payment = convertFromPrisma(
    (
      await prisma.payment.findMany({
        where: {
          key: paymentKey,
          processingKey,
        },
      })
    )[0]
  );

  // If the last payment has already been processed
  // return it directly, i.e. noop
  if (payment.processingState === PaymentProcessingState.PROCESSED) {
    return payment;
  }

  // Append a new copy of the current payment marking it as processed
  const processedPayment = await create({
    ...payment,
    processingState: PaymentProcessingState.PROCESSED,
    actionBy: actionBy,
  });

  incrementMetric('payment_system.payment.process', {
    status: processedPayment.status,
    processing_state: processedPayment.processingState,
    contract_name: processedPayment.contractName,
    action_by: actionBy,
  });

  return processedPayment;
};

/**
 * Marks a payment for a given (`paymentKey`, `processingKey`) pair as processed.
 *
 * @param {string} paymentKey Payment key
 * @param {string} processingKey Processing key
 * @param {string} actionBy Action executor
 * @return {Promise<Payment>} The processed payment
 *
 * @throws {Error} If no unprocessed payment with the requested params is not found.
 *
 * @async
 * @function
 * @memberOf module:payments/actions
 * @access public
 */
const processPayment =
  tracer?.wrap('payments.processPayment', internalProcessPayment) ||
  internalProcessPayment;

export { processPayment };