Page MenuHomestyx hydra

No OneTemporary

diff --git a/src/applications/phortune/application/PhabricatorPhortuneApplication.php b/src/applications/phortune/application/PhabricatorPhortuneApplication.php
index db3f52d61d..2fab046648 100644
--- a/src/applications/phortune/application/PhabricatorPhortuneApplication.php
+++ b/src/applications/phortune/application/PhabricatorPhortuneApplication.php
@@ -1,117 +1,116 @@
<?php
final class PhabricatorPhortuneApplication extends PhabricatorApplication {
public function getName() {
return pht('Phortune');
}
public function getBaseURI() {
return '/phortune/';
}
public function getShortDescription() {
return pht('Accounts and Billing');
}
public function getFontIcon() {
return 'fa-diamond';
}
public function getTitleGlyph() {
return "\xE2\x97\x87";
}
public function getApplicationGroup() {
return self::GROUP_UTILITIES;
}
public function isPrototype() {
return true;
}
public function getRoutes() {
return array(
'/phortune/' => array(
'' => 'PhortuneLandingController',
'(?P<accountID>\d+)/' => array(
'' => 'PhortuneAccountViewController',
'card/' => array(
'new/' => 'PhortunePaymentMethodCreateController',
),
'order/(?:query/(?P<queryKey>[^/]+)/)?'
=> 'PhortuneCartListController',
'subscription/' => array(
'(?:query/(?P<queryKey>[^/]+)/)?'
=> 'PhortuneSubscriptionListController',
'view/(?P<id>\d+)/'
=> 'PhortuneSubscriptionViewController',
'edit/(?P<id>\d+)/'
=> 'PhortuneSubscriptionEditController',
'order/(?P<subscriptionID>\d+)/'
=> 'PhortuneCartListController',
),
'charge/(?:query/(?P<queryKey>[^/]+)/)?'
=> 'PhortuneChargeListController',
),
'card/(?P<id>\d+)/' => array(
'edit/' => 'PhortunePaymentMethodEditController',
'disable/' => 'PhortunePaymentMethodDisableController',
),
'cart/(?P<id>\d+)/' => array(
'' => 'PhortuneCartViewController',
'checkout/' => 'PhortuneCartCheckoutController',
'(?P<action>cancel|refund)/' => 'PhortuneCartCancelController',
'update/' => 'PhortuneCartUpdateController',
- 'accept/' => 'PhortuneCartAcceptController',
),
'account/' => array(
'' => 'PhortuneAccountListController',
'edit/(?:(?P<id>\d+)/)?' => 'PhortuneAccountEditController',
),
'product/' => array(
'' => 'PhortuneProductListController',
'view/(?P<id>\d+)/' => 'PhortuneProductViewController',
'edit/(?:(?P<id>\d+)/)?' => 'PhortuneProductEditController',
),
'provider/' => array(
'edit/(?:(?P<id>\d+)/)?' => 'PhortuneProviderEditController',
'disable/(?P<id>\d+)/' => 'PhortuneProviderDisableController',
'(?P<id>\d+)/(?P<action>[^/]+)/'
=> 'PhortuneProviderActionController',
),
'merchant/' => array(
'(?:query/(?P<queryKey>[^/]+)/)?' => 'PhortuneMerchantListController',
'edit/(?:(?P<id>\d+)/)?' => 'PhortuneMerchantEditController',
'orders/(?P<merchantID>\d+)/(?:query/(?P<queryKey>[^/]+)/)?'
=> 'PhortuneCartListController',
'(?P<merchantID>\d+)/cart/(?P<id>\d+)/' => array(
'' => 'PhortuneCartViewController',
'(?P<action>cancel|refund)/' => 'PhortuneCartCancelController',
'update/' => 'PhortuneCartUpdateController',
'accept/' => 'PhortuneCartAcceptController',
),
'(?P<merchantID>\d+)/subscription/' => array(
'(?:query/(?P<queryKey>[^/]+)/)?'
=> 'PhortuneSubscriptionListController',
'view/(?P<id>\d+)/'
=> 'PhortuneSubscriptionViewController',
'order/(?P<subscriptionID>\d+)/'
=> 'PhortuneCartListController',
),
'(?P<id>\d+)/' => 'PhortuneMerchantViewController',
),
),
);
}
protected function getCustomCapabilities() {
return array(
PhortuneMerchantCapability::CAPABILITY => array(
'caption' => pht('Merchant accounts can receive payments.'),
'default' => PhabricatorPolicies::POLICY_ADMIN,
),
);
}
}
diff --git a/src/applications/phortune/controller/PhortuneCartAcceptController.php b/src/applications/phortune/controller/PhortuneCartAcceptController.php
index 35bb7b7cc8..3db7467290 100644
--- a/src/applications/phortune/controller/PhortuneCartAcceptController.php
+++ b/src/applications/phortune/controller/PhortuneCartAcceptController.php
@@ -1,57 +1,58 @@
<?php
final class PhortuneCartAcceptController
extends PhortuneCartController {
private $id;
public function willProcessRequest(array $data) {
$this->id = $data['id'];
}
public function processRequest() {
$request = $this->getRequest();
$viewer = $request->getUser();
+ // You must control the merchant to accept orders.
+ $authority = $this->loadMerchantAuthority();
+ if (!$authority) {
+ return new Aphront404Response();
+ }
+
$cart = id(new PhortuneCartQuery())
->setViewer($viewer)
->withIDs(array($this->id))
+ ->withMerchantPHIDs(array($authority->getPHID()))
->needPurchases(true)
->executeOne();
if (!$cart) {
return new Aphront404Response();
}
- // You must control the merchant to accept orders.
- PhabricatorPolicyFilter::requireCapability(
- $viewer,
- $cart->getMerchant(),
- PhabricatorPolicyCapability::CAN_EDIT);
-
- $cancel_uri = $cart->getDetailURI();
+ $cancel_uri = $cart->getDetailURI($authority);
if ($cart->getStatus() !== PhortuneCart::STATUS_REVIEW) {
return $this->newDialog()
->setTitle(pht('Order Not in Review'))
->appendParagraph(
pht(
'This order does not need manual review, so you can not '.
'accept it.'))
->addCancelButton($cancel_uri);
}
if ($request->isFormPost()) {
$cart->didReviewCart();
return id(new AphrontRedirectResponse())->setURI($cancel_uri);
}
return $this->newDialog()
->setTitle(pht('Accept Order?'))
->appendParagraph(
pht(
'This order has been flagged for manual review. You should review '.
'it carefully before accepting it.'))
->addCancelButton($cancel_uri)
->addSubmitButton(pht('Accept Order'));
}
}
diff --git a/src/applications/phortune/controller/PhortuneCartCancelController.php b/src/applications/phortune/controller/PhortuneCartCancelController.php
index 49561ba254..b7415c5211 100644
--- a/src/applications/phortune/controller/PhortuneCartCancelController.php
+++ b/src/applications/phortune/controller/PhortuneCartCancelController.php
@@ -1,208 +1,215 @@
<?php
final class PhortuneCartCancelController
extends PhortuneCartController {
private $id;
private $action;
public function willProcessRequest(array $data) {
$this->id = $data['id'];
$this->action = $data['action'];
}
public function processRequest() {
$request = $this->getRequest();
$viewer = $request->getUser();
- $cart = id(new PhortuneCartQuery())
+ $authority = $this->loadMerchantAuthority();
+
+ $cart_query = id(new PhortuneCartQuery())
->setViewer($viewer)
->withIDs(array($this->id))
- ->needPurchases(true)
- ->executeOne();
+ ->needPurchases(true);
+
+ if ($authority) {
+ $cart_query->withMerchantPHIDs(array($authority->getPHID()));
+ }
+
+ $cart = $cart_query->executeOne();
if (!$cart) {
return new Aphront404Response();
}
switch ($this->action) {
case 'cancel':
// You must be able to edit the account to cancel an order.
PhabricatorPolicyFilter::requireCapability(
$viewer,
$cart->getAccount(),
PhabricatorPolicyCapability::CAN_EDIT);
$is_refund = false;
break;
case 'refund':
// You must be able to control the merchant to refund an order.
PhabricatorPolicyFilter::requireCapability(
$viewer,
$cart->getMerchant(),
PhabricatorPolicyCapability::CAN_EDIT);
$is_refund = true;
break;
default:
return new Aphront404Response();
}
- $cancel_uri = $cart->getDetailURI();
+ $cancel_uri = $cart->getDetailURI($authority);
$merchant = $cart->getMerchant();
try {
if ($is_refund) {
$title = pht('Unable to Refund Order');
$cart->assertCanRefundOrder();
} else {
$title = pht('Unable to Cancel Order');
$cart->assertCanCancelOrder();
}
} catch (Exception $ex) {
return $this->newDialog()
->setTitle($title)
->appendChild($ex->getMessage())
->addCancelButton($cancel_uri);
}
$charges = id(new PhortuneChargeQuery())
->setViewer($viewer)
->withCartPHIDs(array($cart->getPHID()))
->withStatuses(
array(
PhortuneCharge::STATUS_HOLD,
PhortuneCharge::STATUS_CHARGED,
))
->execute();
$amounts = mpull($charges, 'getAmountAsCurrency');
$maximum = PhortuneCurrency::newFromList($amounts);
$v_refund = $maximum->formatForDisplay();
$errors = array();
$e_refund = true;
if ($request->isFormPost()) {
if ($is_refund) {
try {
$refund = PhortuneCurrency::newFromUserInput(
$viewer,
$request->getStr('refund'));
$refund->assertInRange('0.00 USD', $maximum->formatForDisplay());
} catch (Exception $ex) {
$errors[] = $ex->getMessage();
$e_refund = pht('Invalid');
}
} else {
$refund = $maximum;
}
if (!$errors) {
$charges = msort($charges, 'getID');
$charges = array_reverse($charges);
if ($charges) {
$providers = id(new PhortunePaymentProviderConfigQuery())
->setViewer($viewer)
->withPHIDs(mpull($charges, 'getProviderPHID'))
->execute();
$providers = mpull($providers, null, 'getPHID');
} else {
$providers = array();
}
foreach ($charges as $charge) {
$refundable = $charge->getAmountRefundableAsCurrency();
if (!$refundable->isPositive()) {
// This charge is a refund, or has already been fully refunded.
continue;
}
if ($refund->isGreaterThan($refundable)) {
$refund_amount = $refundable;
} else {
$refund_amount = $refund;
}
$provider_config = idx($providers, $charge->getProviderPHID());
if (!$provider_config) {
throw new Exception(pht('Unable to load provider for charge!'));
}
$provider = $provider_config->buildProvider();
$refund_charge = $cart->willRefundCharge(
$viewer,
$provider,
$charge,
$refund_amount);
$refunded = false;
try {
$provider->refundCharge($charge, $refund_charge);
$refunded = true;
} catch (Exception $ex) {
phlog($ex);
$cart->didFailRefund($charge, $refund_charge);
}
if ($refunded) {
$cart->didRefundCharge($charge, $refund_charge);
$refund = $refund->subtract($refund_amount);
}
if (!$refund->isPositive()) {
break;
}
}
if ($refund->isPositive()) {
throw new Exception(pht('Unable to refund some charges!'));
}
// TODO: If every HOLD and CHARGING transaction has been fully refunded
// and we're in a HOLD, REVIEW, PURCHASING or CHARGED cart state we
// probably need to kick the cart back to READY here (or maybe kill
// it if it was in REVIEW)?
return id(new AphrontRedirectResponse())->setURI($cancel_uri);
}
}
if ($is_refund) {
$title = pht('Refund Order?');
$body = pht(
'Really refund this order?');
$button = pht('Refund Order');
$cancel_text = pht('Cancel');
$form = id(new AphrontFormView())
->setUser($viewer)
->appendChild(
id(new AphrontFormTextControl())
->setName('refund')
->setLabel(pht('Amount'))
->setError($e_refund)
->setValue($v_refund));
$form = $form->buildLayoutView();
} else {
$title = pht('Cancel Order?');
$body = pht(
'Really cancel this order? Any payment will be refunded.');
$button = pht('Cancel Order');
// Don't give the user a "Cancel" button in response to a "Cancel?"
// prompt, as it's confusing.
$cancel_text = pht('Do Not Cancel Order');
$form = null;
}
return $this->newDialog()
->setTitle($title)
->setErrors($errors)
->appendChild($body)
->appendChild($form)
->addSubmitButton($button)
->addCancelButton($cancel_uri, $cancel_text);
}
}
diff --git a/src/applications/phortune/controller/PhortuneCartListController.php b/src/applications/phortune/controller/PhortuneCartListController.php
index f07fad3819..faf1431312 100644
--- a/src/applications/phortune/controller/PhortuneCartListController.php
+++ b/src/applications/phortune/controller/PhortuneCartListController.php
@@ -1,130 +1,132 @@
<?php
final class PhortuneCartListController
extends PhortuneController {
private $merchant;
private $account;
private $subscription;
public function handleRequest(AphrontRequest $request) {
$viewer = $this->getViewer();
$merchant_id = $request->getURIData('merchantID');
$account_id = $request->getURIData('accountID');
$subscription_id = $request->getURIData('subscriptionID');
$engine = new PhortuneCartSearchEngine();
- if ($subscription_id) {
- $subscription = id(new PhortuneSubscriptionQuery())
- ->setViewer($viewer)
- ->withIDs(array($subscription_id))
- ->executeOne();
- if (!$subscription) {
- return new Aphront404Response();
- }
- $this->subscription = $subscription;
- $engine->setSubscription($subscription);
- }
-
if ($merchant_id) {
$merchant = id(new PhortuneMerchantQuery())
->setViewer($viewer)
->withIDs(array($merchant_id))
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->executeOne();
if (!$merchant) {
return new Aphront404Response();
}
$this->merchant = $merchant;
$viewer->grantAuthority($merchant);
$engine->setMerchant($merchant);
} else if ($account_id) {
$account = id(new PhortuneAccountQuery())
->setViewer($viewer)
->withIDs(array($account_id))
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->executeOne();
if (!$account) {
return new Aphront404Response();
}
$this->account = $account;
$engine->setAccount($account);
} else {
return new Aphront404Response();
}
+ // NOTE: We must process this after processing the merchant authority, so
+ // it becomes visible in merchant contexts.
+ if ($subscription_id) {
+ $subscription = id(new PhortuneSubscriptionQuery())
+ ->setViewer($viewer)
+ ->withIDs(array($subscription_id))
+ ->executeOne();
+ if (!$subscription) {
+ return new Aphront404Response();
+ }
+ $this->subscription = $subscription;
+ $engine->setSubscription($subscription);
+ }
+
$controller = id(new PhabricatorApplicationSearchController())
->setQueryKey($request->getURIData('queryKey'))
->setSearchEngine($engine)
->setNavigation($this->buildSideNavView());
return $this->delegateToController($controller);
}
public function buildSideNavView() {
$viewer = $this->getRequest()->getUser();
$nav = new AphrontSideNavFilterView();
$nav->setBaseURI(new PhutilURI($this->getApplicationURI()));
id(new PhortuneCartSearchEngine())
->setViewer($viewer)
->addNavigationItems($nav->getMenu());
$nav->selectFilter(null);
return $nav;
}
protected function buildApplicationCrumbs() {
$crumbs = parent::buildApplicationCrumbs();
$subscription = $this->subscription;
$merchant = $this->merchant;
if ($merchant) {
$id = $merchant->getID();
$this->addMerchantCrumb($crumbs, $merchant);
if (!$subscription) {
$crumbs->addTextCrumb(
pht('Orders'),
$this->getApplicationURI("merchant/orders/{$id}/"));
}
}
$account = $this->account;
if ($account) {
$id = $account->getID();
$this->addAccountCrumb($crumbs, $account);
if (!$subscription) {
$crumbs->addTextCrumb(
pht('Orders'),
$this->getApplicationURI("{$id}/order/"));
}
}
if ($subscription) {
if ($merchant) {
$subscription_uri = $subscription->getMerchantURI();
} else {
$subscription_uri = $subscription->getURI();
}
$crumbs->addTextCrumb(
$subscription->getSubscriptionName(),
$subscription_uri);
}
return $crumbs;
}
}
diff --git a/src/applications/phortune/controller/PhortuneCartUpdateController.php b/src/applications/phortune/controller/PhortuneCartUpdateController.php
index b9477fc68b..ea571ccf8a 100644
--- a/src/applications/phortune/controller/PhortuneCartUpdateController.php
+++ b/src/applications/phortune/controller/PhortuneCartUpdateController.php
@@ -1,65 +1,72 @@
<?php
final class PhortuneCartUpdateController
extends PhortuneCartController {
private $id;
public function willProcessRequest(array $data) {
$this->id = $data['id'];
}
public function processRequest() {
$request = $this->getRequest();
$viewer = $request->getUser();
- $cart = id(new PhortuneCartQuery())
+ $authority = $this->loadMerchantAuthority();
+
+ $cart_query = id(new PhortuneCartQuery())
->setViewer($viewer)
->withIDs(array($this->id))
- ->needPurchases(true)
- ->executeOne();
+ ->needPurchases(true);
+
+ if ($authority) {
+ $cart_query->withMerchantPHIDs(array($authority->getPHID()));
+ }
+
+ $cart = $cart_query->executeOne();
if (!$cart) {
return new Aphront404Response();
}
$charges = id(new PhortuneChargeQuery())
->setViewer($viewer)
->withCartPHIDs(array($cart->getPHID()))
->needCarts(true)
->withStatuses(
array(
PhortuneCharge::STATUS_HOLD,
PhortuneCharge::STATUS_CHARGED,
))
->execute();
if ($charges) {
$providers = id(new PhortunePaymentProviderConfigQuery())
->setViewer($viewer)
->withPHIDs(mpull($charges, 'getProviderPHID'))
->execute();
$providers = mpull($providers, null, 'getPHID');
} else {
$providers = array();
}
foreach ($charges as $charge) {
if ($charge->isRefund()) {
// Don't update refunds.
continue;
}
$provider_config = idx($providers, $charge->getProviderPHID());
if (!$provider_config) {
throw new Exception(pht('Unable to load provider for charge!'));
}
$provider = $provider_config->buildProvider();
$provider->updateCharge($charge);
}
return id(new AphrontRedirectResponse())
- ->setURI($cart->getDetailURI());
+ ->setURI($cart->getDetailURI($authority));
}
}
diff --git a/src/applications/phortune/controller/PhortuneCartViewController.php b/src/applications/phortune/controller/PhortuneCartViewController.php
index b9f3d19b44..45d90a3a62 100644
--- a/src/applications/phortune/controller/PhortuneCartViewController.php
+++ b/src/applications/phortune/controller/PhortuneCartViewController.php
@@ -1,291 +1,289 @@
<?php
final class PhortuneCartViewController
extends PhortuneCartController {
private $id;
public function willProcessRequest(array $data) {
$this->id = $data['id'];
}
public function processRequest() {
$request = $this->getRequest();
$viewer = $request->getUser();
$authority = $this->loadMerchantAuthority();
- // TODO: This (and the rest of the Cart controllers) need to be updated
- // to use merchant URIs and merchant authority.
-
$cart = id(new PhortuneCartQuery())
->setViewer($viewer)
->withIDs(array($this->id))
->needPurchases(true)
->executeOne();
if (!$cart) {
return new Aphront404Response();
}
- $can_admin = PhabricatorPolicyFilter::hasCapability(
- $viewer,
- $cart->getMerchant(),
- PhabricatorPolicyCapability::CAN_EDIT);
-
$cart_table = $this->buildCartContentTable($cart);
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$cart,
PhabricatorPolicyCapability::CAN_EDIT);
$errors = array();
$error_view = null;
$resume_uri = null;
switch ($cart->getStatus()) {
case PhortuneCart::STATUS_PURCHASING:
if ($can_edit) {
$resume_uri = $cart->getMetadataValue('provider.checkoutURI');
if ($resume_uri) {
$errors[] = pht(
'The checkout process has been started, but not yet completed. '.
'You can continue checking out by clicking %s, or cancel the '.
'order, or contact the merchant for assistance.',
phutil_tag('strong', array(), pht('Continue Checkout')));
} else {
$errors[] = pht(
'The checkout process has been started, but an error occurred. '.
'You can cancel the order or contact the merchant for '.
'assistance.');
}
}
break;
case PhortuneCart::STATUS_CHARGED:
if ($can_edit) {
$errors[] = pht(
'You have been charged, but processing could not be completed. '.
'You can cancel your order, or contact the merchant for '.
'assistance.');
}
break;
case PhortuneCart::STATUS_HOLD:
if ($can_edit) {
$errors[] = pht(
'Payment for this order is on hold. You can click %s to check '.
'for updates, cancel the order, or contact the merchant for '.
'assistance.',
phutil_tag('strong', array(), pht('Update Status')));
}
break;
case PhortuneCart::STATUS_REVIEW:
- if ($can_admin) {
+ if ($authority) {
$errors[] = pht(
'This order has been flagged for manual review. Review the order '.
'and choose %s to accept it or %s to reject it.',
phutil_tag('strong', array(), pht('Accept Order')),
phutil_tag('strong', array(), pht('Refund Order')));
} else if ($can_edit) {
$errors[] = pht(
'This order requires manual processing and will complete once '.
'the merchant accepts it.');
}
break;
case PhortuneCart::STATUS_PURCHASED:
$error_view = id(new PHUIInfoView())
->setSeverity(PHUIInfoView::SEVERITY_NOTICE)
->appendChild(pht('This purchase has been completed.'));
break;
}
$properties = $this->buildPropertyListView($cart);
$actions = $this->buildActionListView(
$cart,
$can_edit,
- $can_admin,
+ $authority,
$resume_uri);
$properties->setActionList($actions);
$header = id(new PHUIHeaderView())
->setUser($viewer)
->setHeader(pht('Order Detail'));
if ($cart->getStatus() == PhortuneCart::STATUS_PURCHASED) {
$done_uri = $cart->getDoneURI();
if ($done_uri) {
$header->addActionLink(
id(new PHUIButtonView())
->setTag('a')
->setHref($done_uri)
->setIcon(id(new PHUIIconView())
->setIconFont('fa-check-square green'))
->setText($cart->getDoneActionName()));
}
}
$cart_box = id(new PHUIObjectBoxView())
->setHeader($header)
->appendChild($properties)
->appendChild($cart_table);
if ($errors) {
$cart_box->setFormErrors($errors);
} else if ($error_view) {
$cart_box->setErrorView($error_view);
}
$charges = id(new PhortuneChargeQuery())
->setViewer($viewer)
->withCartPHIDs(array($cart->getPHID()))
->needCarts(true)
->execute();
$phids = array();
foreach ($charges as $charge) {
$phids[] = $charge->getProviderPHID();
$phids[] = $charge->getCartPHID();
$phids[] = $charge->getMerchantPHID();
$phids[] = $charge->getPaymentMethodPHID();
}
$handles = $this->loadViewerHandles($phids);
$charges_table = id(new PhortuneChargeTableView())
->setUser($viewer)
->setHandles($handles)
->setCharges($charges)
->setShowOrder(false);
$charges = id(new PHUIObjectBoxView())
->setHeaderText(pht('Charges'))
->appendChild($charges_table);
$account = $cart->getAccount();
$crumbs = $this->buildApplicationCrumbs();
if ($authority) {
$this->addMerchantCrumb($crumbs, $authority);
} else {
$this->addAccountCrumb($crumbs, $cart->getAccount());
}
$crumbs->addTextCrumb(pht('Cart %d', $cart->getID()));
$timeline = $this->buildTransactionTimeline(
$cart,
new PhortuneCartTransactionQuery());
$timeline
->setShouldTerminate(true);
return $this->buildApplicationPage(
array(
$crumbs,
$cart_box,
$charges,
$timeline,
),
array(
'title' => pht('Cart'),
));
}
private function buildPropertyListView(PhortuneCart $cart) {
$viewer = $this->getRequest()->getUser();
$view = id(new PHUIPropertyListView())
->setUser($viewer)
->setObject($cart);
$handles = $this->loadViewerHandles(
array(
$cart->getAccountPHID(),
$cart->getAuthorPHID(),
$cart->getMerchantPHID(),
));
$view->addProperty(
pht('Order Name'),
$cart->getName());
$view->addProperty(
pht('Account'),
$handles[$cart->getAccountPHID()]->renderLink());
$view->addProperty(
pht('Authorized By'),
$handles[$cart->getAuthorPHID()]->renderLink());
$view->addProperty(
pht('Merchant'),
$handles[$cart->getMerchantPHID()]->renderLink());
$view->addProperty(
pht('Status'),
PhortuneCart::getNameForStatus($cart->getStatus()));
$view->addProperty(
pht('Updated'),
phabricator_datetime($cart->getDateModified(), $viewer));
return $view;
}
private function buildActionListView(
PhortuneCart $cart,
$can_edit,
- $can_admin,
+ $authority,
$resume_uri) {
$viewer = $this->getRequest()->getUser();
$id = $cart->getID();
$view = id(new PhabricatorActionListView())
->setUser($viewer)
->setObject($cart);
$can_cancel = ($can_edit && $cart->canCancelOrder());
- $cancel_uri = $this->getApplicationURI("cart/{$id}/cancel/");
- $refund_uri = $this->getApplicationURI("cart/{$id}/refund/");
- $update_uri = $this->getApplicationURI("cart/{$id}/update/");
- $accept_uri = $this->getApplicationURI("cart/{$id}/accept/");
+ if ($authority) {
+ $prefix = 'merchant/'.$authority->getID().'/';
+ } else {
+ $prefix = '';
+ }
+
+ $cancel_uri = $this->getApplicationURI("{$prefix}cart/{$id}/cancel/");
+ $refund_uri = $this->getApplicationURI("{$prefix}cart/{$id}/refund/");
+ $update_uri = $this->getApplicationURI("{$prefix}cart/{$id}/update/");
+ $accept_uri = $this->getApplicationURI("{$prefix}cart/{$id}/accept/");
$view->addAction(
id(new PhabricatorActionView())
->setName(pht('Cancel Order'))
->setIcon('fa-times')
->setDisabled(!$can_cancel)
->setWorkflow(true)
->setHref($cancel_uri));
- if ($can_admin) {
+ if ($authority) {
if ($cart->getStatus() == PhortuneCart::STATUS_REVIEW) {
$view->addAction(
id(new PhabricatorActionView())
->setName(pht('Accept Order'))
->setIcon('fa-check')
->setWorkflow(true)
->setHref($accept_uri));
}
$view->addAction(
id(new PhabricatorActionView())
->setName(pht('Refund Order'))
->setIcon('fa-reply')
->setWorkflow(true)
->setHref($refund_uri));
}
$view->addAction(
id(new PhabricatorActionView())
->setName(pht('Update Status'))
->setIcon('fa-refresh')
->setHref($update_uri));
if ($can_edit && $resume_uri) {
$view->addAction(
id(new PhabricatorActionView())
->setName(pht('Continue Checkout'))
->setIcon('fa-shopping-cart')
->setHref($resume_uri));
}
return $view;
}
}
diff --git a/src/applications/phortune/controller/PhortuneSubscriptionViewController.php b/src/applications/phortune/controller/PhortuneSubscriptionViewController.php
index e3c5d3e759..ad4d366d15 100644
--- a/src/applications/phortune/controller/PhortuneSubscriptionViewController.php
+++ b/src/applications/phortune/controller/PhortuneSubscriptionViewController.php
@@ -1,203 +1,208 @@
<?php
final class PhortuneSubscriptionViewController extends PhortuneController {
public function handleRequest(AphrontRequest $request) {
$viewer = $this->getViewer();
- $is_merchant = (bool)$this->loadMerchantAuthority();
+ $authority = $this->loadMerchantAuthority();
- $subscription = id(new PhortuneSubscriptionQuery())
+ $subscription_query = id(new PhortuneSubscriptionQuery())
->setViewer($viewer)
->withIDs(array($request->getURIData('id')))
- ->needTriggers(true)
- ->executeOne();
+ ->needTriggers(true);
+
+ if ($authority) {
+ $subscription_query->withMerchantPHIDs(array($authority->getPHID()));
+ }
+
+ $subscription = $subscription_query->executeOne();
if (!$subscription) {
return new Aphront404Response();
}
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$subscription,
PhabricatorPolicyCapability::CAN_EDIT);
$merchant = $subscription->getMerchant();
$account = $subscription->getAccount();
$account_id = $account->getID();
$subscription_id = $subscription->getID();
$title = $subscription->getSubscriptionFullName();
$header = id(new PHUIHeaderView())
->setHeader($title);
$actions = id(new PhabricatorActionListView())
->setUser($viewer)
->setObjectURI($request->getRequestURI());
$edit_uri = $subscription->getEditURI();
$actions->addAction(
id(new PhabricatorActionView())
->setIcon('fa-pencil')
->setName(pht('Edit Subscription'))
->setHref($edit_uri)
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit));
$crumbs = $this->buildApplicationCrumbs();
- if ($is_merchant) {
+ if ($authority) {
$this->addMerchantCrumb($crumbs, $merchant);
} else {
$this->addAccountCrumb($crumbs, $account);
}
$crumbs->addTextCrumb($subscription->getSubscriptionCrumbName());
$properties = id(new PHUIPropertyListView())
->setUser($viewer)
->setActionList($actions);
$next_invoice = $subscription->getTrigger()->getNextEventPrediction();
$properties->addProperty(
pht('Next Invoice'),
phabricator_datetime($next_invoice, $viewer));
$default_method = $subscription->getDefaultPaymentMethodPHID();
if ($default_method) {
$handles = $this->loadViewerHandles(array($default_method));
$autopay_method = $handles[$default_method]->renderLink();
} else {
$autopay_method = phutil_tag(
'em',
array(),
pht('No Autopay Method Configured'));
}
$properties->addProperty(
pht('Autopay With'),
$autopay_method);
$object_box = id(new PHUIObjectBoxView())
->setHeader($header)
->addPropertyList($properties);
- $due_box = $this->buildDueInvoices($subscription, $is_merchant);
- $invoice_box = $this->buildPastInvoices($subscription, $is_merchant);
+ $due_box = $this->buildDueInvoices($subscription, $authority);
+ $invoice_box = $this->buildPastInvoices($subscription, $authority);
return $this->buildApplicationPage(
array(
$crumbs,
$object_box,
$due_box,
$invoice_box,
),
array(
'title' => $title,
));
}
private function buildDueInvoices(
PhortuneSubscription $subscription,
- $is_merchant) {
+ $authority) {
$viewer = $this->getViewer();
$invoices = id(new PhortuneCartQuery())
->setViewer($viewer)
->withSubscriptionPHIDs(array($subscription->getPHID()))
->needPurchases(true)
->withInvoices(true)
->execute();
$phids = array();
foreach ($invoices as $invoice) {
$phids[] = $invoice->getPHID();
$phids[] = $invoice->getMerchantPHID();
foreach ($invoice->getPurchases() as $purchase) {
$phids[] = $purchase->getPHID();
}
}
$handles = $this->loadViewerHandles($phids);
$invoice_table = id(new PhortuneOrderTableView())
->setUser($viewer)
->setCarts($invoices)
->setIsInvoices(true)
- ->setIsMerchantView($is_merchant)
+ ->setIsMerchantView((bool)$authority)
->setHandles($handles);
$invoice_header = id(new PHUIHeaderView())
->setHeader(pht('Invoices Due'));
return id(new PHUIObjectBoxView())
->setHeader($invoice_header)
->appendChild($invoice_table);
}
private function buildPastInvoices(
PhortuneSubscription $subscription,
- $is_merchant) {
+ $authority) {
$viewer = $this->getViewer();
$invoices = id(new PhortuneCartQuery())
->setViewer($viewer)
->withSubscriptionPHIDs(array($subscription->getPHID()))
->needPurchases(true)
->withStatuses(
array(
PhortuneCart::STATUS_PURCHASING,
PhortuneCart::STATUS_CHARGED,
PhortuneCart::STATUS_HOLD,
PhortuneCart::STATUS_REVIEW,
PhortuneCart::STATUS_PURCHASED,
))
->setLimit(50)
->execute();
$phids = array();
foreach ($invoices as $invoice) {
$phids[] = $invoice->getPHID();
foreach ($invoice->getPurchases() as $purchase) {
$phids[] = $purchase->getPHID();
}
}
$handles = $this->loadViewerHandles($phids);
$invoice_table = id(new PhortuneOrderTableView())
->setUser($viewer)
->setCarts($invoices)
->setHandles($handles);
$account = $subscription->getAccount();
$merchant = $subscription->getMerchant();
$account_id = $account->getID();
$merchant_id = $merchant->getID();
$subscription_id = $subscription->getID();
- if ($is_merchant) {
+ if ($authority) {
$invoices_uri = $this->getApplicationURI(
"merchant/{$merchant_id}/subscription/order/{$subscription_id}/");
} else {
$invoices_uri = $this->getApplicationURI(
"{$account_id}/subscription/order/{$subscription_id}/");
}
$invoice_header = id(new PHUIHeaderView())
->setHeader(pht('Past Invoices'))
->addActionLink(
id(new PHUIButtonView())
->setTag('a')
->setIcon(
id(new PHUIIconView())
->setIconFont('fa-list'))
->setHref($invoices_uri)
->setText(pht('View All Invoices')));
return id(new PHUIObjectBoxView())
->setHeader($invoice_header)
->appendChild($invoice_table);
}
}
diff --git a/src/applications/phortune/storage/PhortuneCart.php b/src/applications/phortune/storage/PhortuneCart.php
index dc24c3f132..af1da3e3bf 100644
--- a/src/applications/phortune/storage/PhortuneCart.php
+++ b/src/applications/phortune/storage/PhortuneCart.php
@@ -1,677 +1,682 @@
<?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);
return $this;
}
public function didReviewCart() {
$this->openTransaction();
$this->beginReadLocking();
$copy = clone $this;
$copy->reload();
if (($copy->getStatus() !== self::STATUS_CHARGED) &&
($copy->getStatus() !== self::STATUS_REVIEW)) {
throw new Exception(
pht(
'Cart has wrong status ("%s") to call didReviewCart()!',
$copy->getStatus()));
}
foreach ($this->purchases as $purchase) {
$purchase->getProduct()->didPurchaseProduct($purchase);
}
$this->setStatus(self::STATUS_PURCHASED)->save();
$this->endReadLocking();
$this->saveTransaction();
$this->recordCartTransaction(PhortuneCartTransaction::TYPE_PURCHASED);
return $this;
}
public function didFailCharge(PhortuneCharge $charge) {
$charge->setStatus(PhortuneCharge::STATUS_FAILED);
$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 didFailCharge().',
$copy->getStatus()));
}
$charge->save();
// Move the cart back into STATUS_READY so the user can try
// making the purchase again.
$this->setStatus(self::STATUS_READY)->save();
$this->endReadLocking();
$this->saveTransaction();
return $this;
}
public function willRefundCharge(
PhabricatorUser $actor,
PhortunePaymentProvider $provider,
PhortuneCharge $charge,
PhortuneCurrency $amount) {
if (!$amount->isPositive()) {
throw new Exception(
pht('Trying to refund nonpositive amount of money!'));
}
if ($amount->isGreaterThan($charge->getAmountRefundableAsCurrency())) {
throw new Exception(
pht('Trying to refund more money than remaining on charge!'));
}
if ($charge->getRefundedChargePHID()) {
throw new Exception(
pht('Trying to refund a refund!'));
}
if (($charge->getStatus() !== PhortuneCharge::STATUS_CHARGED) &&
($charge->getStatus() !== PhortuneCharge::STATUS_HOLD)) {
throw new Exception(
pht('Trying to refund an uncharged charge!'));
}
$refund_charge = PhortuneCharge::initializeNewCharge()
->setAccountPHID($this->getAccount()->getPHID())
->setCartPHID($this->getPHID())
->setAuthorPHID($actor->getPHID())
->setMerchantPHID($this->getMerchant()->getPHID())
->setProviderPHID($provider->getProviderConfig()->getPHID())
->setPaymentMethodPHID($charge->getPaymentMethodPHID())
->setRefundedChargePHID($charge->getPHID())
->setAmountAsCurrency($amount->negate());
$charge->openTransaction();
$charge->beginReadLocking();
$copy = clone $charge;
$copy->reload();
if ($copy->getRefundingPHID() !== null) {
throw new Exception(
pht('Trying to refund a charge which is already refunding!'));
}
$refund_charge->save();
$charge->setRefundingPHID($refund_charge->getPHID());
$charge->save();
$charge->endReadLocking();
$charge->saveTransaction();
return $refund_charge;
}
public function didRefundCharge(
PhortuneCharge $charge,
PhortuneCharge $refund) {
$refund->setStatus(PhortuneCharge::STATUS_CHARGED);
$this->openTransaction();
$this->beginReadLocking();
$copy = clone $charge;
$copy->reload();
if ($charge->getRefundingPHID() !== $refund->getPHID()) {
throw new Exception(
pht('Charge is in the wrong refunding state!'));
}
$charge->setRefundingPHID(null);
// NOTE: There's some trickiness here to get the signs right. Both
// these values are positive but the refund has a negative value.
$total_refunded = $charge
->getAmountRefundedAsCurrency()
->add($refund->getAmountAsCurrency()->negate());
$charge->setAmountRefundedAsCurrency($total_refunded);
$charge->save();
$refund->save();
$this->endReadLocking();
$this->saveTransaction();
$amount = $refund->getAmountAsCurrency()->negate();
foreach ($this->purchases as $purchase) {
$purchase->getProduct()->didRefundProduct($purchase, $amount);
}
return $this;
}
public function didFailRefund(
PhortuneCharge $charge,
PhortuneCharge $refund) {
$refund->setStatus(PhortuneCharge::STATUS_FAILED);
$this->openTransaction();
$this->beginReadLocking();
$copy = clone $charge;
$copy->reload();
if ($charge->getRefundingPHID() !== $refund->getPHID()) {
throw new Exception(
pht('Charge is in the wrong refunding state!'));
}
$charge->setRefundingPHID(null);
$charge->save();
$refund->save();
$this->endReadLocking();
$this->saveTransaction();
}
private function recordCartTransaction($type) {
$omnipotent_user = PhabricatorUser::getOmnipotentUser();
$phortune_phid = id(new PhabricatorPhortuneApplication())->getPHID();
$xactions = array();
$xactions[] = id(new PhortuneCartTransaction())
->setTransactionType($type)
->setNewValue(true);
$content_source = PhabricatorContentSource::newForSource(
PhabricatorContentSource::SOURCE_PHORTUNE,
array());
$editor = id(new PhortuneCartEditor())
->setActor($omnipotent_user)
->setActingAsPHID($phortune_phid)
->setContentSource($content_source)
->setContinueOnMissingFields(true)
->setContinueOnNoEffect(true);
$editor->applyTransactions($this, $xactions);
}
public function getName() {
return $this->getImplementation()->getName($this);
}
public function getDoneURI() {
return $this->getImplementation()->getDoneURI($this);
}
public function getDoneActionName() {
return $this->getImplementation()->getDoneActionName($this);
}
public function getCancelURI() {
return $this->getImplementation()->getCancelURI($this);
}
- public function getDetailURI() {
- return '/phortune/cart/'.$this->getID().'/';
+ public function getDetailURI(PhortuneMerchant $authority = null) {
+ if ($authority) {
+ $prefix = 'merchant/'.$authority->getID().'/';
+ } else {
+ $prefix = '';
+ }
+ return '/phortune/'.$prefix.'cart/'.$this->getID().'/';
}
public function getCheckoutURI() {
return '/phortune/cart/'.$this->getID().'/checkout/';
}
public function canCancelOrder() {
try {
$this->assertCanCancelOrder();
return true;
} catch (Exception $ex) {
return false;
}
}
public function canRefundOrder() {
try {
$this->assertCanRefundOrder();
return true;
} catch (Exception $ex) {
return false;
}
}
public function assertCanCancelOrder() {
switch ($this->getStatus()) {
case self::STATUS_BUILDING:
throw new Exception(
pht(
'This order can not be cancelled because the application has not '.
'finished building it yet.'));
case self::STATUS_READY:
throw new Exception(
pht(
'This order can not be cancelled because it has not been placed.'));
}
return $this->getImplementation()->assertCanCancelOrder($this);
}
public function assertCanRefundOrder() {
switch ($this->getStatus()) {
case self::STATUS_BUILDING:
throw new Exception(
pht(
'This order can not be refunded because the application has not '.
'finished building it yet.'));
case self::STATUS_READY:
throw new Exception(
pht(
'This order can not be refunded because it has not been placed.'));
}
return $this->getImplementation()->assertCanRefundOrder($this);
}
protected function getConfiguration() {
return array(
self::CONFIG_AUX_PHID => true,
self::CONFIG_SERIALIZATION => array(
'metadata' => self::SERIALIZATION_JSON,
),
self::CONFIG_COLUMN_SCHEMA => array(
'status' => 'text32',
'cartClass' => 'text128',
'mailKey' => 'bytes20',
'subscriptionPHID' => 'phid?',
),
self::CONFIG_KEY_SCHEMA => array(
'key_account' => array(
'columns' => array('accountPHID'),
),
'key_merchant' => array(
'columns' => array('merchantPHID'),
),
'key_subscription' => array(
'columns' => array('subscriptionPHID'),
),
),
) + parent::getConfiguration();
}
public function generatePHID() {
return PhabricatorPHID::generateNewPHID(
PhortuneCartPHIDType::TYPECONST);
}
public function save() {
if (!$this->getMailKey()) {
$this->setMailKey(Filesystem::readRandomCharacters(20));
}
return parent::save();
}
public function attachPurchases(array $purchases) {
assert_instances_of($purchases, 'PhortunePurchase');
$this->purchases = $purchases;
return $this;
}
public function getPurchases() {
return $this->assertAttached($this->purchases);
}
public function attachAccount(PhortuneAccount $account) {
$this->account = $account;
return $this;
}
public function getAccount() {
return $this->assertAttached($this->account);
}
public function attachMerchant(PhortuneMerchant $merchant) {
$this->merchant = $merchant;
return $this;
}
public function getMerchant() {
return $this->assertAttached($this->merchant);
}
public function attachImplementation(
PhortuneCartImplementation $implementation) {
$this->implementation = $implementation;
return $this;
}
public function getImplementation() {
return $this->assertAttached($this->implementation);
}
public function getTotalPriceAsCurrency() {
$prices = array();
foreach ($this->getPurchases() as $purchase) {
$prices[] = $purchase->getTotalPriceAsCurrency();
}
return PhortuneCurrency::newFromList($prices);
}
public function setMetadataValue($key, $value) {
$this->metadata[$key] = $value;
return $this;
}
public function getMetadataValue($key, $default = null) {
return idx($this->metadata, $key, $default);
}
/* -( PhabricatorApplicationTransactionInterface )------------------------- */
public function getApplicationTransactionEditor() {
return new PhortuneCartEditor();
}
public function getApplicationTransactionObject() {
return $this;
}
public function getApplicationTransactionTemplate() {
return new PhortuneCartTransaction();
}
public function willRenderTimeline(
PhabricatorApplicationTransactionView $timeline,
AphrontRequest $request) {
return $timeline;
}
/* -( PhabricatorPolicyInterface )----------------------------------------- */
public function getCapabilities() {
return array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
);
}
public function getPolicy($capability) {
// NOTE: Both view and edit use the account's edit policy. We punch a hole
// through this for merchants, below.
return $this
->getAccount()
->getPolicy(PhabricatorPolicyCapability::CAN_EDIT);
}
public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
if ($this->getAccount()->hasAutomaticCapability($capability, $viewer)) {
return true;
}
// If the viewer controls the merchant this order was placed with, they
// can view the order.
if ($capability == PhabricatorPolicyCapability::CAN_VIEW) {
$can_admin = PhabricatorPolicyFilter::hasCapability(
$viewer,
$this->getMerchant(),
PhabricatorPolicyCapability::CAN_EDIT);
if ($can_admin) {
return true;
}
}
return false;
}
public function describeAutomaticCapability($capability) {
return array(
pht('Orders inherit the policies of the associated account.'),
pht('The merchant you placed an order with can review and manage it.'),
);
}
}

File Metadata

Mime Type
text/x-diff
Expires
Tue, Mar 17, 2:09 AM (5 h, 2 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
964231
Default Alt Text
(55 KB)

Event Timeline