<commit_before><?php

final class PhortuneCart extends PhortuneDAO
  implements
    PhabricatorApplicationTransactionInterface,
    PhabricatorPolicyInterface {

  const STATUS_BUILDING = 'cart:building';
  const STATUS_READY = 'cart:ready';
  const STATUS_PURCHASING = 'cart:purchasing';
  const STATUS_CHARGED = 'cart:charged';
  const STATUS_HOLD = 'cart:hold';
  const STATUS_REVIEW = 'cart:review';
  const STATUS_PURCHASED = 'cart:purchased';

  protected $accountPHID;
  protected $authorPHID;
  protected $merchantPHID;
  protected $subscriptionPHID;
  protected $cartClass;
  protected $status;
  protected $metadata = array();
  protected $mailKey;

  private $account = self::ATTACHABLE;
  private $purchases = self::ATTACHABLE;
  private $implementation = self::ATTACHABLE;
  private $merchant = self::ATTACHABLE;

  public static function initializeNewCart(
    PhabricatorUser $actor,
    PhortuneAccount $account,
    PhortuneMerchant $merchant) {
    $cart = id(new PhortuneCart())
      ->setAuthorPHID($actor->getPHID())
      ->setStatus(self::STATUS_BUILDING)
      ->setAccountPHID($account->getPHID())
      ->attachAccount($account)
      ->setMerchantPHID($merchant->getPHID())
      ->attachMerchant($merchant);

    $cart->account = $account;
    $cart->purchases = array();

    return $cart;
  }

  public function newPurchase(
    PhabricatorUser $actor,
    PhortuneProduct $product) {

    $purchase = PhortunePurchase::initializeNewPurchase($actor, $product)
      ->setAccountPHID($this->getAccount()->getPHID())
      ->setCartPHID($this->getPHID())
      ->save();

    $this->purchases[] = $purchase;

    return $purchase;
  }

  public static function getStatusNameMap() {
    return array(
      self::STATUS_BUILDING => pht('Building'),
      self::STATUS_READY => pht('Ready'),
      self::STATUS_PURCHASING => pht('Purchasing'),
      self::STATUS_CHARGED => pht('Charged'),
      self::STATUS_HOLD => pht('Hold'),
      self::STATUS_REVIEW => pht('Review'),
      self::STATUS_PURCHASED => pht('Purchased'),
    );
  }

  public static function getNameForStatus($status) {
    return idx(self::getStatusNameMap(), $status, $status);
  }

  public function activateCart() {
    $this->openTransaction();
      $this->beginReadLocking();

        $copy = clone $this;
        $copy->reload();

        if ($copy->getStatus() !== self::STATUS_BUILDING) {
          throw new Exception(
            pht(
              'Cart has wrong status ("%s") to call willApplyCharge().',
              $copy->getStatus()));
        }

        $this->setStatus(self::STATUS_READY)->save();

      $this->endReadLocking();
    $this->saveTransaction();

    $this->recordCartTransaction(PhortuneCartTransaction::TYPE_CREATED);

    return $this;
  }

  public function willApplyCharge(
    PhabricatorUser $actor,
    PhortunePaymentProvider $provider,
    PhortunePaymentMethod $method = null) {

    $account = $this->getAccount();

    $charge = PhortuneCharge::initializeNewCharge()
      ->setAccountPHID($account->getPHID())
      ->setCartPHID($this->getPHID())
      ->setAuthorPHID($actor->getPHID())
      ->setMerchantPHID($this->getMerchant()->getPHID())
      ->setProviderPHID($provider->getProviderConfig()->getPHID())
      ->setAmountAsCurrency($this->getTotalPriceAsCurrency());

    if ($method) {
      $charge->setPaymentMethodPHID($method->getPHID());
    }

    $this->openTransaction();
      $this->beginReadLocking();

        $copy = clone $this;
        $copy->reload();

        if ($copy->getStatus() !== self::STATUS_READY) {
          throw new Exception(
            pht(
              'Cart has wrong status ("%s") to call willApplyCharge(), '.
              'expected "%s".',
              $copy->getStatus(),
              self::STATUS_READY));
        }

        $charge->save();
        $this->setStatus(PhortuneCart::STATUS_PURCHASING)->save();

      $this->endReadLocking();
    $this->saveTransaction();

    return $charge;
  }

  public function didHoldCharge(PhortuneCharge $charge) {
    $charge->setStatus(PhortuneCharge::STATUS_HOLD);

    $this->openTransaction();
      $this->beginReadLocking();

        $copy = clone $this;
        $copy->reload();

        if ($copy->getStatus() !== self::STATUS_PURCHASING) {
          throw new Exception(
            pht(
              'Cart has wrong status ("%s") to call didHoldCharge(), '.
              'expected "%s".',
              $copy->getStatus(),
              self::STATUS_PURCHASING));
        }

        $charge->save();
        $this->setStatus(self::STATUS_HOLD)->save();

      $this->endReadLocking();
    $this->saveTransaction();

    $this->recordCartTransaction(PhortuneCartTransaction::TYPE_HOLD);
  }

  public function didApplyCharge(PhortuneCharge $charge) {
    $charge->setStatus(PhortuneCharge::STATUS_CHARGED);

    $this->openTransaction();
      $this->beginReadLocking();

        $copy = clone $this;
        $copy->reload();

        if (($copy->getStatus() !== self::STATUS_PURCHASING) &&
            ($copy->getStatus() !== self::STATUS_HOLD)) {
          throw new Exception(
            pht(
              'Cart has wrong status ("%s") to call didApplyCharge().',
              $copy->getStatus()));
        }

        $charge->save();
        $this->setStatus(self::STATUS_CHARGED)->save();

      $this->endReadLocking();
    $this->saveTransaction();

    // TODO: Perform purchase review. Here, we would apply rules to determine
    // whether the charge needs manual review (maybe making the decision via
    // Herald, configuration, or by examining provider fraud data). For now,
    // always require review.
    $needs_review = true;

    if ($needs_review) {
      $this->willReviewCart();
    } else {
      $this->didReviewCart();
    }

    return $this;
  }

  public function willReviewCart() {
    $this->openTransaction();
      $this->beginReadLocking();

        $copy = clone $this;
        $copy->reload();

        if (($copy->getStatus() !== self::STATUS_CHARGED)) {
          throw new Exception(
            pht(
              'Cart has wrong status ("%s") to call willReviewCart()!',
              $copy->getStatus()));
        }

        $this->setStatus(self::STATUS_REVIEW)->save();

      $this->endReadLocking();
    $this->saveTransaction();

    $this->recordCartTransaction(PhortuneCartTransaction::TYPE_REVIEW);
<commit_msg>Don't require payment review by default in Phortune<commit_after><?php

final class PhortuneCart extends PhortuneDAO
  implements
    PhabricatorApplicationTransactionInterface,
    PhabricatorPolicyInterface {

  const STATUS_BUILDING = 'cart:building';
  const STATUS_READY = 'cart:ready';
  const STATUS_PURCHASING = 'cart:purchasing';
  const STATUS_CHARGED = 'cart:charged';
  const STATUS_HOLD = 'cart:hold';
  const STATUS_REVIEW = 'cart:review';
  const STATUS_PURCHASED = 'cart:purchased';

  protected $accountPHID;
  protected $authorPHID;
  protected $merchantPHID;
  protected $subscriptionPHID;
  protected $cartClass;
  protected $status;
  protected $metadata = array();
  protected $mailKey;

  private $account = self::ATTACHABLE;
  private $purchases = self::ATTACHABLE;
  private $implementation = self::ATTACHABLE;
  private $merchant = self::ATTACHABLE;

  public static function initializeNewCart(
    PhabricatorUser $actor,
    PhortuneAccount $account,
    PhortuneMerchant $merchant) {
    $cart = id(new PhortuneCart())
      ->setAuthorPHID($actor->getPHID())
      ->setStatus(self::STATUS_BUILDING)
      ->setAccountPHID($account->getPHID())
      ->attachAccount($account)
      ->setMerchantPHID($merchant->getPHID())
      ->attachMerchant($merchant);

    $cart->account = $account;
    $cart->purchases = array();

    return $cart;
  }

  public function newPurchase(
    PhabricatorUser $actor,
    PhortuneProduct $product) {

    $purchase = PhortunePurchase::initializeNewPurchase($actor, $product)
      ->setAccountPHID($this->getAccount()->getPHID())
      ->setCartPHID($this->getPHID())
      ->save();

    $this->purchases[] = $purchase;

    return $purchase;
  }

  public static function getStatusNameMap() {
    return array(
      self::STATUS_BUILDING => pht('Building'),
      self::STATUS_READY => pht('Ready'),
      self::STATUS_PURCHASING => pht('Purchasing'),
      self::STATUS_CHARGED => pht('Charged'),
      self::STATUS_HOLD => pht('Hold'),
      self::STATUS_REVIEW => pht('Review'),
      self::STATUS_PURCHASED => pht('Purchased'),
    );
  }

  public static function getNameForStatus($status) {
    return idx(self::getStatusNameMap(), $status, $status);
  }

  public function activateCart() {
    $this->openTransaction();
      $this->beginReadLocking();

        $copy = clone $this;
        $copy->reload();

        if ($copy->getStatus() !== self::STATUS_BUILDING) {
          throw new Exception(
            pht(
              'Cart has wrong status ("%s") to call willApplyCharge().',
              $copy->getStatus()));
        }

        $this->setStatus(self::STATUS_READY)->save();

      $this->endReadLocking();
    $this->saveTransaction();

    $this->recordCartTransaction(PhortuneCartTransaction::TYPE_CREATED);

    return $this;
  }

  public function willApplyCharge(
    PhabricatorUser $actor,
    PhortunePaymentProvider $provider,
    PhortunePaymentMethod $method = null) {

    $account = $this->getAccount();

    $charge = PhortuneCharge::initializeNewCharge()
      ->setAccountPHID($account->getPHID())
      ->setCartPHID($this->getPHID())
      ->setAuthorPHID($actor->getPHID())
      ->setMerchantPHID($this->getMerchant()->getPHID())
      ->setProviderPHID($provider->getProviderConfig()->getPHID())
      ->setAmountAsCurrency($this->getTotalPriceAsCurrency());

    if ($method) {
      $charge->setPaymentMethodPHID($method->getPHID());
    }

    $this->openTransaction();
      $this->beginReadLocking();

        $copy = clone $this;
        $copy->reload();

        if ($copy->getStatus() !== self::STATUS_READY) {
          throw new Exception(
            pht(
              'Cart has wrong status ("%s") to call willApplyCharge(), '.
              'expected "%s".',
              $copy->getStatus(),
              self::STATUS_READY));
        }

        $charge->save();
        $this->setStatus(PhortuneCart::STATUS_PURCHASING)->save();

      $this->endReadLocking();
    $this->saveTransaction();

    return $charge;
  }

  public function didHoldCharge(PhortuneCharge $charge) {
    $charge->setStatus(PhortuneCharge::STATUS_HOLD);

    $this->openTransaction();
      $this->beginReadLocking();

        $copy = clone $this;
        $copy->reload();

        if ($copy->getStatus() !== self::STATUS_PURCHASING) {
          throw new Exception(
            pht(
              'Cart has wrong status ("%s") to call didHoldCharge(), '.
              'expected "%s".',
              $copy->getStatus(),
              self::STATUS_PURCHASING));
        }

        $charge->save();
        $this->setStatus(self::STATUS_HOLD)->save();

      $this->endReadLocking();
    $this->saveTransaction();

    $this->recordCartTransaction(PhortuneCartTransaction::TYPE_HOLD);
  }

  public function didApplyCharge(PhortuneCharge $charge) {
    $charge->setStatus(PhortuneCharge::STATUS_CHARGED);

    $this->openTransaction();
      $this->beginReadLocking();

        $copy = clone $this;
        $copy->reload();

        if (($copy->getStatus() !== self::STATUS_PURCHASING) &&
            ($copy->getStatus() !== self::STATUS_HOLD)) {
          throw new Exception(
            pht(
              'Cart has wrong status ("%s") to call didApplyCharge().',
              $copy->getStatus()));
        }

        $charge->save();
        $this->setStatus(self::STATUS_CHARGED)->save();

      $this->endReadLocking();
    $this->saveTransaction();

    // TODO: Perform purchase review. Here, we would apply rules to determine
    // whether the charge needs manual review (maybe making the decision via
    // Herald, configuration, or by examining provider fraud data). For now,
    // don't require review.
    $needs_review = false;

    if ($needs_review) {
      $this->willReviewCart();
    } else {
      $this->didReviewCart();
    }

    return $this;
  }

  public function willReviewCart() {
    $this->openTransaction();
      $this->beginReadLocking();

        $copy = clone $this;
        $copy->reload();

        if (($copy->getStatus() !== self::STATUS_CHARGED)) {
          throw new Exception(
            pht(
              'Cart has wrong status ("%s") to call willReviewCart()!',
              $copy->getStatus()));
        }

        $this->setStatus(self::STATUS_REVIEW)->save();

      $this->endReadLocking();
    $this->saveTransaction();

    $this->recordCartTransaction(PhortuneCartTransaction::TYPE_REVIEW);

{'fraction_non_alphanumeric': 0.14947172156619018, 'fraction_numerical': 0.0, 'mean_word_length': 2.958487084870849, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 199, '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': '5006167', 'n_tokens_mistral': 3950, 'n_tokens_neox': 3798, 'n_words': 717}