commerce_funds-8.x-1.7/src/TransactionManager.php

src/TransactionManager.php
<?php

namespace Drupal\commerce_funds;

use Drupal\Core\Cache\Cache;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Database\Connection;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Mail\MailManagerInterface;
use Drupal\Core\Messenger\MessengerInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Session\AccountProxyInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\Utility\Token;
use Drupal\commerce_funds\Entity\Transaction;
use Drupal\commerce_funds\Entity\TransactionInterface;
use Drupal\commerce_funds\Exception\TransactionException;
use Drupal\commerce_order\Entity\OrderInterface;
use Drupal\commerce_price\Calculator;

/**
 * Transaction manager class.
 */
class TransactionManager implements TransactionManagerInterface {

  use StringTranslationTrait;

  /**
   * The entity type manager.
   *
   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
   */
  protected $entityTypeManager;

  /**
   * The db connection.
   *
   * @var \Drupal\Core\Database\Connection
   */
  protected $connection;

  /**
   * AccountProxy definition.
   *
   * @var \Drupal\Core\Session\AccountProxyInterface
   */
  protected $currentUser;

  /**
   * The config factory.
   *
   * @var \Drupal\Core\Config\ConfigFactoryInterface
   */
  protected $config;

  /**
   * The messenger.
   *
   * @var \Drupal\Core\Messenger\MessengerInterface
   */
  protected $messenger;

  /**
   * The mail manager.
   *
   * @var \Drupal\Core\Mail\MailManagerInterface
   */
  protected $mailManager;

  /**
   * The token utility.
   *
   * @var \Drupal\Core\Utility\Token
   */
  protected $token;

  /**
   * Class constructor.
   */
  public function __construct(EntityTypeManagerInterface $entity_type_manager, Connection $connection, AccountProxyInterface $current_user, ConfigFactoryInterface $config_factory, MessengerInterface $messenger, MailManagerInterface $mail_manager, Token $token) {
    $this->entityTypeManager = $entity_type_manager;
    $this->connection = $connection;
    $this->currentUser = $current_user;
    $this->config = $config_factory;
    $this->messenger = $messenger;
    $this->mailManager = $mail_manager;
    $this->token = $token;
  }

  /**
   * {@inheritdoc}
   */
  public function addDepositToBalance(OrderInterface $order) {
    $deposit_amount = $order->getItems()[0]->getTotalPrice()->getNumber();
    $deposit_currency_code = $order->getItems()[0]->getTotalPrice()->getCurrencyCode();
    $total_paid = $order->getTotalPrice()->getNumber();
    $fee_applied = Calculator::subtract($total_paid, $deposit_amount, 2);
    $payment_method = $order->get('payment_gateway')->getValue()[0]['target_id'];
    $notes = $this->t('Deposit of @amount @currency (order <a href="/user/@user/orders/@order">#@order</a>)', [
      '@amount' => number_format($deposit_amount, 2, '.', ','),
      '@currency' => $deposit_currency_code,
      '@user' => $order->getCustomerId(),
      '@order' => $order->id(),
    ]);

    // Transaction has been created at field level.
    /** @var \Drupal\commerce_funds\Entity\Transaction $transaction */
    if ($order->get('field_transaction')->getString()) {
      $field_values = $order->get('field_transaction')->referencedEntities();
      $transaction = reset($field_values);
      $transaction->setNotes([
        'value' => $notes,
        'format' => 'basic_html',
      ]);
      $transaction->save();
      // Update account balance.
      $this->performTransaction($transaction);
    }
    else {
      // Defines transaction and save it to db.
      $transaction = Transaction::create([
        'issuer' => $order->getCustomerId(),
        'recipient' => $order->getCustomerId(),
        'type' => 'deposit',
        'method' => $payment_method,
        'brut_amount' => $deposit_amount,
        'net_amount' => $total_paid,
        'fee' => $fee_applied,
        'currency' => $deposit_currency_code,
        'status' => Transaction::TRANSACTION_STATUS['canceled'],
        'notes' => [
          'value' => $notes,
          'format' => 'basic_html',
        ],
      ]);
      $transaction->save();
      // Update account balance.
      $this->performTransaction($transaction);
      // Add transaction to order.
      $order->set('field_transaction', $transaction->id());
    }
  }

  /**
   * {@inheritdoc}
   */
  public function performTransaction(TransactionInterface $transaction) {
    $type = $transaction->bundle();
    $issuer = $transaction->getIssuer();
    // Make sure not to perform already completed
    // transactions.
    if ($transaction->getStatus() === Transaction::TRANSACTION_STATUS['completed']) {
      throw new TransactionException(sprintf("Transaction already performed, id: %d", $transaction->id()));
    }
    // Executed on payment success.
    if ($type === 'deposit' && $transaction->access('create', $issuer)) {
      $this->addFundsToBalance($transaction, $issuer);
      $this->updateSiteBalance($transaction);
      $transaction->setStatus(Transaction::TRANSACTION_STATUS['completed']);
    }
    // Executed on transaction creation.
    elseif ($type === 'transfer' && $transaction->access('create', $issuer) || $type === 'payment' && $transaction->access('create', $issuer)) {
      $this->addFundsToBalance($transaction, $transaction->getRecipient());
      $this->removeFundsFromBalance($transaction, $issuer);
      $this->updateSiteBalance($transaction);
      $transaction->setStatus(Transaction::TRANSACTION_STATUS['completed']);
    }
    // Executed on escrow creation, released in confirm form.
    elseif ($type === 'escrow' && $transaction->access('create', $issuer)) {
      $this->removeFundsFromBalance($transaction, $issuer);
      $transaction->setStatus(Transaction::TRANSACTION_STATUS['pending']);
    }
    // Executed on withdrawal approval.
    elseif ($type === 'withdrawal_request' && $transaction->access('create', $this->currentUser)) {
      $this->removeFundsFromBalance($transaction, $issuer);
      $this->updateSiteBalance($transaction);
      $transaction->setStatus(Transaction::TRANSACTION_STATUS['completed']);
    }
    // Executed on transaction creation.
    elseif ($type === 'conversion' && $transaction->access('create', $issuer)) {
      $this->removeFundsFromBalance($transaction, $issuer);
      $this->addFundsToBalance($transaction, $transaction->getRecipient());
      $transaction->setStatus(Transaction::TRANSACTION_STATUS['completed']);
    }
    // Access denied.
    else {
      throw new TransactionException(sprintf("Transaction permission denied: %s", $transaction->bundle()));
    }

    // Save transaction status.
    $transaction->save();
  }

  /**
   * {@inheritdoc}
   */
  public function addFundsToBalance(TransactionInterface $transaction, AccountInterface $account) {
    $brut_amount = $transaction->getBrutAmount();
    $currency_code = $transaction->getCurrencyCode();

    // Cover case where it's an escrow canceled.
    if ($transaction->bundle() === "escrow" && $account->id() == $transaction->getIssuerId()) {
      $brut_amount = $transaction->getNetAmount();
    }

    // Cover conversions and payments.
    if ($transaction->bundle() === "conversion" || $transaction->bundle() === "payment") {
      $brut_amount = $transaction->getNetAmount();
    }

    $balance = $this->loadAccountBalance($account);
    $balance[$currency_code] = $balance[$currency_code] ?? 0;
    $balance[$currency_code] = Calculator::add((string) $brut_amount, (string) $balance[$currency_code], 2);

    // Update the user balance.
    $uid = $account->hasPermission('administer transactions') ? 1 : $account->id();
    $this->connection->merge('commerce_funds_user_funds')
      ->insertFields([
        'uid' => $uid,
        'balance' => serialize($balance),
      ])
      ->updateFields([
        'balance' => serialize($balance),
      ])
      ->key('uid', $uid)
      ->execute();

    // Invalidate balance block cache.
    Cache::invalidateTags(["funds_balance:$uid"]);
  }

  /**
   * {@inheritdoc}
   */
  public function removeFundsFromBalance(TransactionInterface $transaction, AccountInterface $account) {
    $net_amount = $transaction->getNetAmount();
    $currency_code = $transaction->getCurrencyCode();

    if ($transaction->bundle() == 'conversion') {
      $currency_code = $transaction->getFromCurrencyCode();
      $net_amount = $transaction->getBrutAmount();
    }

    if ($transaction->bundle() == 'payment') {
      $net_amount = $transaction->getBrutAmount();
    }

    $balance = $this->loadAccountBalance($account);
    $balance[$currency_code] = $balance[$currency_code] ?? 0;
    $balance[$currency_code] = Calculator::subtract((string) $balance[$currency_code], (string) $net_amount, 2);

    // Update the user balance.
    $uid = $account->hasPermission('administer transactions') ? 1 : $account->id();
    $this->connection->merge('commerce_funds_user_funds')
      ->insertFields([
        'uid' => $uid,
        'balance' => serialize($balance),
      ])
      ->updateFields([
        'balance' => serialize($balance),
      ])
      ->key('uid', $uid)
      ->execute();

    // Invalidate balance block cache.
    Cache::invalidateTags(["funds_balance:$uid"]);
  }

  /**
   * {@inheritdoc}
   */
  public function updateSiteBalance(TransactionInterface $transaction) {
    $currency_code = $transaction->getCurrencyCode();
    $site_balance = $this->loadSiteBalance();

    $site_balance[$currency_code] = $site_balance[$currency_code] ?? 0;
    $site_balance[$currency_code] = Calculator::add((string) $transaction->getFee(), (string) $site_balance[$currency_code], 2);

    // Update site balance.
    $this->connection->merge('commerce_funds_user_funds')
      ->key('uid', 1)
      ->updateFields([
        'balance' => serialize($site_balance),
      ])
      ->execute();

    // Invalidate balance block cache.
    Cache::invalidateTags(['funds_balance:1']);
  }

  /**
   * {@inheritdoc}
   */
  public function loadAccountBalance(AccountInterface $account) {
    // Check if issuer balance exists.
    $balance_exist = $this->connection->query("SELECT * FROM {commerce_funds_user_funds} WHERE uid = :uid", [
      ':uid' => $account->id(),
    ])->fetchObject();

    // Unserialize balance.
    $balance = $balance_exist ? unserialize($balance_exist->balance) : [];

    return $balance;
  }

  /**
   * {@inheritdoc}
   */
  public function loadSiteBalance() {
    // Check if issuer balance exists.
    $balance_exist = $this->connection->query("SELECT * FROM {commerce_funds_user_funds} WHERE uid = :uid", [
      ':uid' => 1,
    ])->fetchObject();

    // Unserialize balance.
    $balance = $balance_exist ? unserialize($balance_exist->balance) : [];

    return $balance;
  }

  /**
   * {@inheritdoc}
   */
  public function loadTransactionByHash($hash) {
    $transactions = $this->entityTypeManager->getStorage('commerce_funds_transaction')->loadByProperties(['hash' => $hash]);
    // @todo to be removed if unique in db.
    if (count($transactions) > 1) {
      $duplicate_ids = '';
      foreach ($transactions as $transaction) {
        $duplicate_ids .= $transaction->id() . ', ';
      }
      throw new TransactionException(sprintf("Transaction duplicate error: %s", $duplicate_ids));
    }

    return reset($transactions);
  }

  /**
   * {@inheritdoc}
   */
  public function getTransactionCurrency($transaction_id) {

    $currency = Transaction::load($transaction_id)->getCurrency();

    return $currency;
  }

  /**
   * {@inheritdoc}
   */
  public function getConversionFromCurrency($transaction_id) {

    $currency = Transaction::load($transaction_id)->getFromCurrency();

    return $currency;
  }

  /**
   * {@inheritdoc}
   */
  public function sendTransactionMails(TransactionInterface $transaction) {
    // Prepares variables for later.
    $langcode = $this->config->get('system.site')->get('langcode');
    $config = $this->config->get('commerce_funds.settings');
    $issuer = $transaction->getIssuer();
    $recipient = $transaction->getRecipient();
    $transaction_type = $transaction->bundle();
    $transaction_status = $transaction->getStatus();
    // Mapping of configuration.
    // [transaction_type => [mail config name => mail type]].
    $config_mapping = [
      'transfer' => ['transfer' => ['_recipient', '_issuer']],
      'escrow' => [
        'escrow_created' => ['_recipient', '_issuer'],
        'escrow_canceled_by_issuer' => ['_recipient', '_issuer'],
        'escrow_canceled_by_recipient' => ['_recipient', '_issuer'],
        'escrow_released' => ['_recipient', '_issuer'],
      ],
      'withdrawal_request' => [
        'withdrawal_declined' => ['_issuer'],
        'withdrawal_approved' => ['_issuer'],
      ],
    ];
    $mail_configs = $config_mapping[$transaction_type];

    // Map each transaction type to their specific config names.
    if ($transaction_type == 'transfer' && $transaction_status == $transaction::TRANSACTION_STATUS['completed']) {
      $config_name = 'transfer';
    }
    elseif ($transaction_type == 'escrow') {
      switch ($transaction_status) {
        default:
          $config_name = NULL;
          break;
        case $transaction::TRANSACTION_STATUS['pending']:
          $config_name = 'escrow_created';
          break;

        case $transaction::TRANSACTION_STATUS['canceled']:
          $config_name = $this->currentUser->id() == $issuer->id() ? 'escrow_canceled_by_issuer' : 'escrow_canceled_by_recipient';
          break;

        case $transaction::TRANSACTION_STATUS['completed']:
          $config_name = 'escrow_released';
      }
    }
    elseif ($transaction_type == 'withdrawal_request') {
      switch ($transaction_status) {
        default:
          $config_name = NULL;
          break;
        case $transaction::TRANSACTION_STATUS['canceled']:
          $config_name = 'withdrawal_declined';
          break;

        case $transaction::TRANSACTION_STATUS['completed']:
          $config_name = 'withdrawal_approved';
          break;
      }
    }
    else {
      $config_name = NULL;
    }

    // Stop here if no config.
    if (!$config_name) {
      return FALSE;
    }

    // Send mail for each mail type.
    foreach ($mail_configs[$config_name] as $type) {
      // Find recipient of email.
      $mail_recipient = strpos('issuer', $type) ? $issuer : $recipient;
      // Clean type for withdrawal requests.
      if ($transaction_type == 'withdrawal_request') {
        $type = '';
      }
      if ($config->get('mail_' . $config_name . $type)['activated']) {
        $balance = $this->loadAccountBalance($mail_recipient);
        $params = [
          'id' => $config_name . $type,
          'subject' => $this->token->replace($config->get('mail_' . $config_name . $type)['subject'], ['commerce_funds_transaction' => $transaction]),
          'body' => $this->token->replace($config->get('mail_' . $config_name . $type)['body']['value'], [
            'commerce_funds_transaction' => $transaction,
            'commerce_funds_balance' => $balance,
            'commerce_funds_balance_uid' => $mail_recipient->id(),
          ]),
        ];
        $email_sent[] = $this->mailManager->mail('commerce_funds', 'commerce_funds_transaction', $mail_recipient->getEmail(), $langcode, $params, NULL, TRUE);
      }
    }

    return !empty($email_sent) ? count($email_sent) : FALSE;
  }

  /**
   * {@inheritdoc}
   */
  public function generateConfirmationMessage(TransactionInterface $transaction) {
    $fee = $transaction->getFee();
    $status = $transaction->getStatus();
    $currency = $transaction->getCurrency();

    switch ($transaction->bundle()) {
      case 'transfer':
        if ((float) $fee) {
          $confirmation_message = 'You have transferred @amount @currency to @recipient (fees: %commission @currency).';
        }
        else {
          $confirmation_message = 'You have transferred @amount @currency to @recipient.';
        }
        break;

      case 'escrow':
        if ($status == $transaction::TRANSACTION_STATUS['pending']) {
          if ((float) $fee) {
            $confirmation_message = 'Escrow payment of @amount @currency successfully created to @recipient (fees: %commission @currency).';
          }
          else {
            $confirmation_message = 'Escrow payment of @amount @currency successfully created to @recipient.';
          }
        }
        if ($status == $transaction::TRANSACTION_STATUS['completed']) {
          if ((float) $fee) {
            $confirmation_message = 'You have transferred @amount @currency to @recipient (fees: %commission @currency).';
          }
          else {
            $confirmation_message = 'You have transferred @amount @currency to @recipient.';
          }
        }
        if ($status == $transaction::TRANSACTION_STATUS['canceled']) {
          if ($this->currentUser->id() == $transaction->getIssuerId()) {
            $confirmation_message = 'Escrow payment of @amount to @recipient has been canceled.';
          }
          if ($this->currentUser->id() == $transaction->getRecipientId()) {
            $confirmation_message = 'Escrow payment of @amount from @issuer has been canceled.';
          }

        }
        break;

      case 'withdrawal_request':
        if ($status == $transaction::TRANSACTION_STATUS['pending']) {
          if ((float) $fee) {
            $confirmation_message = 'Withdrawal request sent. An extra commission of %commission @currency will be applied to your withdrawal.';
          }
          else {
            $confirmation_message = 'Withdrawal request sent.';
          }
        }
        if ($status == $transaction::TRANSACTION_STATUS['declined']) {
          $confirmation_message = 'Request declined.';
        }
        if ($status == $transaction::TRANSACTION_STATUS['approved']) {
          $confirmation_message = 'Request approved.';
        }
        break;

      case 'conversion':
        if ($status == $transaction::TRANSACTION_STATUS['completed']) {
          $confirmation_message = '@amount_left @currency_left converted into @amount_right @currency_right.';
        }
    }

    if (!empty($confirmation_message)) {
      // phpcs:ignore.
      $this->messenger->addMessage($this->t($confirmation_message, [
        '@amount' => $currency->getSymbol() . $transaction->getBrutAmount(),
        '@amount_left' => $transaction->getFromCurrency() ? $transaction->getFromCurrency()->getSymbol() . $transaction->getBrutAmount() : '',
        '@amount_right' => $currency->getSymbol() . $transaction->getNetAmount(),
        '@currency_left' => $transaction->getFromCurrency() ? $transaction->getFromCurrency()->getCurrencyCode() : '',
        '@currency_right' => $currency->getCurrencyCode(),
        '@currency' => $currency->getCurrencyCode(),
        '@recipient' => $transaction->getRecipient()->getAccountName(),
        '@issuer' => $transaction->getIssuer()->getAccountName(),
        '%commission' => $currency->getSymbol() . number_format($fee, 2),
      ]), 'status');

      return TRUE;
    }

    return FALSE;
  }

}

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc