<commit_before>import models from '../../models';
import roles from '../../constants/roles';
import * as libpayments from '../../lib/payments';
import * as libtransactions from '../../lib/transactions';
import { TransactionTypes, OC_FEE_PERCENT } from '../../constants/transactions';

/** Get the balance of a prepaid credit card
 *
 * When a card is created by a host (by adding funds to an
 * organization for example) the card is created with an initial
 * balance. This function subtracts the amount from transactions made
 * with this card from the initial balance.
 *
 * @param {models.PaymentMethod} paymentMethod is the instance of the
 *  prepaid credit card payment method.
 * @return {Object} with amount & currency from the payment method.
 */
async function getBalance(paymentMethod) {
  if (!libpayments.isProvider('opencollective.prepaid', paymentMethod)) {
    throw new Error(`Expected opencollective.prepaid but got ${paymentMethod.service}.${paymentMethod.type}`);
  }
  /* Result will be negative (We're looking for DEBIT transactions) */
  const spent = await libtransactions.sum({
    PaymentMethodId: paymentMethod.id,
    currency: paymentMethod.currency,
    type: 'DEBIT',
  });
  return {
    amount: paymentMethod.initialBalance + spent,
    currency: paymentMethod.currency,
  };
}

/** Process a pre paid card order
 *
 * @param {models.Order} order The order instance to be processed.
 * @return {models.Transaction} As any other payment method, after
 *  processing Giftcard orders, the transaction generated from it is
 *  returned.
 */
async function processOrder(order) {
  const user = order.createdByUser;
  const { paymentMethod: { data } } = order;

  // Making sure the paymentMethod has the information we need to
  // process a prepaid card
  if (!order.paymentMethod.customerId)
    throw new Error('Prepaid method must have a value for `customerId`');
  if (!data || !data.HostCollectiveId)
    throw new Error('Prepaid method must have a value for `data.HostCollectiveId`');

  // Check if the prepaid card was created for the collective making
  // the donation
  const fromCollective = await models.Collective.findById(order.FromCollectiveId);
  if (order.paymentMethod.customerId !== fromCollective.slug)
    throw new Error('Prepaid method can only be used by the organization that received it');

  // Check that target Collective's Host is same as gift card issuer
  const hostCollective = await order.collective.getHostCollective();
  if (hostCollective.id !== data.HostCollectiveId)
    throw new Error('Prepaid method can only be used in collectives from the same host');

  // Use the above payment method to donate to Collective
  const hostFeeInHostCurrency = libpayments.calcFee(
    order.totalAmount,
    order.collective.hostFeePercent);
  const platformFeeInHostCurrency = libpayments.calcFee(
    order.totalAmount, OC_FEE_PERCENT);
  const transactions = await models.Transaction.createFromPayload({
    CreatedByUserId: user.id,
    FromCollectiveId: order.FromCollectiveId,
    CollectiveId: order.CollectiveId,
    PaymentMethodId: order.paymentMethod.id,
    transaction: {
      type: TransactionTypes.CREDIT,
      OrderId: order.id,
      amount: order.totalAmount,
      amountInHostCurrency: order.totalAmount,
      currency: order.currency,
      hostCurrency: order.currency,
      hostCurrencyFxRate: 1,
      hostFeeInHostCurrency,
      platformFeeInHostCurrency,
      paymentProcessorFeeInHostCurrency: 0,
      description: order.description
    }
  });

  // add roles
  await order.collective.findOrAddUserWithRole({ id: user.id, CollectiveId: order.fromCollective.id}, roles.BACKER, {
    CreatedByUserId: user.id, TierId: order.TierId,
  });

  // Mark order row as processed
  await order.update({ processedAt: new Date() });

  // Mark paymentMethod as confirmed
  order.paymentMethod.update({ confirmedAt: new Date() });

  return transactions;
}

/* Expected API of a Payment Method Type */
export default {
  features: {
    recurring: true,
    waitToCharge: false
  },
  getBalance,
  processOrder,
};
<commit_msg>fix(PaymentMethods/prepaid): check not only the slug, but also if PM.ColletiveId matches order.FromCollective.id so it won't throw error<commit_after>import models from '../../models';
import roles from '../../constants/roles';
import * as libpayments from '../../lib/payments';
import * as libtransactions from '../../lib/transactions';
import { TransactionTypes, OC_FEE_PERCENT } from '../../constants/transactions';

/** Get the balance of a prepaid credit card
 *
 * When a card is created by a host (by adding funds to an
 * organization for example) the card is created with an initial
 * balance. This function subtracts the amount from transactions made
 * with this card from the initial balance.
 *
 * @param {models.PaymentMethod} paymentMethod is the instance of the
 *  prepaid credit card payment method.
 * @return {Object} with amount & currency from the payment method.
 */
async function getBalance(paymentMethod) {
  if (!libpayments.isProvider('opencollective.prepaid', paymentMethod)) {
    throw new Error(`Expected opencollective.prepaid but got ${paymentMethod.service}.${paymentMethod.type}`);
  }
  /* Result will be negative (We're looking for DEBIT transactions) */
  const spent = await libtransactions.sum({
    PaymentMethodId: paymentMethod.id,
    currency: paymentMethod.currency,
    type: 'DEBIT',
  });
  return {
    amount: paymentMethod.initialBalance + spent,
    currency: paymentMethod.currency,
  };
}

/** Process a pre paid card order
 *
 * @param {models.Order} order The order instance to be processed.
 * @return {models.Transaction} As any other payment method, after
 *  processing Giftcard orders, the transaction generated from it is
 *  returned.
 */
async function processOrder(order) {
  const user = order.createdByUser;
  const { paymentMethod: { data } } = order;
  console.log(0);
  // Making sure the paymentMethod has the information we need to
  // process a prepaid card
  if (!order.paymentMethod.customerId)
    throw new Error('Prepaid method must have a value for `customerId`');
  if (!data || !data.HostCollectiveId)
    throw new Error('Prepaid method must have a value for `data.HostCollectiveId`');

  // Check if the prepaid card was created for the collective making
  // the donation
  const fromCollective = await models.Collective.findById(order.FromCollectiveId);
  if (order.paymentMethod.customerId !== fromCollective.slug) {
    //if the PM has the CollectiveId setup, it's supposed to be the same as the FromCollectiveId
    if (!order.paymentMethod.CollectiveId || order.paymentMethod.CollectiveId !== order.FromCollectiveId)
      throw new Error('Prepaid method can only be used by the organization that received it');
  }
  // Check that target Collective's Host is same as gift card issuer
  const hostCollective = await order.collective.getHostCollective();
  if (hostCollective.id !== data.HostCollectiveId)
    throw new Error('Prepaid method can only be used in collectives from the same host');

  // Use the above payment method to donate to Collective
  const hostFeeInHostCurrency = libpayments.calcFee(
    order.totalAmount,
    order.collective.hostFeePercent);
  const platformFeeInHostCurrency = libpayments.calcFee(
    order.totalAmount, OC_FEE_PERCENT);
  const transactions = await models.Transaction.createFromPayload({
    CreatedByUserId: user.id,
    FromCollectiveId: order.FromCollectiveId,
    CollectiveId: order.CollectiveId,
    PaymentMethodId: order.paymentMethod.id,
    transaction: {
      type: TransactionTypes.CREDIT,
      OrderId: order.id,
      amount: order.totalAmount,
      amountInHostCurrency: order.totalAmount,
      currency: order.currency,
      hostCurrency: order.currency,
      hostCurrencyFxRate: 1,
      hostFeeInHostCurrency,
      platformFeeInHostCurrency,
      paymentProcessorFeeInHostCurrency: 0,
      description: order.description
    }
  });

  // add roles
  await order.collective.findOrAddUserWithRole({ id: user.id, CollectiveId: order.fromCollective.id}, roles.BACKER, {
    CreatedByUserId: user.id, TierId: order.TierId,
  });

  // Mark order row as processed
  await order.update({ processedAt: new Date() });

  // Mark paymentMethod as confirmed
  order.paymentMethod.update({ confirmedAt: new Date() });

  return transactions;
}

/* Expected API of a Payment Method Type */
export default {
  features: {
    recurring: true,
    waitToCharge: false
  },
  getBalance,
  processOrder,
};

{'fraction_non_alphanumeric': 0.09621672115833722, 'fraction_numerical': 0.0005838393274170948, 'mean_word_length': 3.9337557603686637, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5101126', 'n_tokens_mistral': 2385, 'n_tokens_neox': 2387, 'n_words': 795}