Page MenuHomePhabricator

D10675.id25634.diff
No OneTemporary

D10675.id25634.diff

diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -2557,6 +2557,7 @@
'PhortuneAccountViewController' => 'applications/phortune/controller/PhortuneAccountViewController.php',
'PhortuneBalancedPaymentProvider' => 'applications/phortune/provider/PhortuneBalancedPaymentProvider.php',
'PhortuneCart' => 'applications/phortune/storage/PhortuneCart.php',
+ 'PhortuneCartAcceptController' => 'applications/phortune/controller/PhortuneCartAcceptController.php',
'PhortuneCartCancelController' => 'applications/phortune/controller/PhortuneCartCancelController.php',
'PhortuneCartCheckoutController' => 'applications/phortune/controller/PhortuneCartCheckoutController.php',
'PhortuneCartController' => 'applications/phortune/controller/PhortuneCartController.php',
@@ -5616,6 +5617,7 @@
'PhortuneDAO',
'PhabricatorPolicyInterface',
),
+ 'PhortuneCartAcceptController' => 'PhortuneCartController',
'PhortuneCartCancelController' => 'PhortuneCartController',
'PhortuneCartCheckoutController' => 'PhortuneCartController',
'PhortuneCartController' => 'PhortuneController',
diff --git a/src/applications/phortune/application/PhabricatorPhortuneApplication.php b/src/applications/phortune/application/PhabricatorPhortuneApplication.php
--- a/src/applications/phortune/application/PhabricatorPhortuneApplication.php
+++ b/src/applications/phortune/application/PhabricatorPhortuneApplication.php
@@ -50,6 +50,7 @@
'checkout/' => 'PhortuneCartCheckoutController',
'(?P<action>cancel|refund)/' => 'PhortuneCartCancelController',
'update/' => 'PhortuneCartUpdateController',
+ 'accept/' => 'PhortuneCartAcceptController',
),
'account/' => array(
'' => 'PhortuneAccountListController',
diff --git a/src/applications/phortune/controller/PhortuneAccountViewController.php b/src/applications/phortune/controller/PhortuneAccountViewController.php
--- a/src/applications/phortune/controller/PhortuneAccountViewController.php
+++ b/src/applications/phortune/controller/PhortuneAccountViewController.php
@@ -176,6 +176,7 @@
PhortuneCart::STATUS_PURCHASING,
PhortuneCart::STATUS_CHARGED,
PhortuneCart::STATUS_HOLD,
+ PhortuneCart::STATUS_REVIEW,
PhortuneCart::STATUS_PURCHASED,
))
->execute();
diff --git a/src/applications/phortune/controller/PhortuneCartAcceptController.php b/src/applications/phortune/controller/PhortuneCartAcceptController.php
new file mode 100644
--- /dev/null
+++ b/src/applications/phortune/controller/PhortuneCartAcceptController.php
@@ -0,0 +1,57 @@
+<?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();
+
+ $cart = id(new PhortuneCartQuery())
+ ->setViewer($viewer)
+ ->withIDs(array($this->id))
+ ->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();
+
+ 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
--- a/src/applications/phortune/controller/PhortuneCartCancelController.php
+++ b/src/applications/phortune/controller/PhortuneCartCancelController.php
@@ -158,8 +158,9 @@
}
// TODO: If every HOLD and CHARGING transaction has been fully refunded
- // and we're in a HOLD, PURCHASING or CHARGED cart state we probably
- // need to kick the cart back to READY here?
+ // 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);
}
@@ -170,6 +171,7 @@
$body = pht(
'Really refund this order?');
$button = pht('Refund Order');
+ $cancel_text = pht('Cancel');
$form = id(new AphrontFormView())
->setUser($viewer)
@@ -181,6 +183,7 @@
->setValue($v_refund));
$form = $form->buildLayoutView();
+
} else {
$title = pht('Cancel Order?');
$body = pht(
diff --git a/src/applications/phortune/controller/PhortuneCartCheckoutController.php b/src/applications/phortune/controller/PhortuneCartCheckoutController.php
--- a/src/applications/phortune/controller/PhortuneCartCheckoutController.php
+++ b/src/applications/phortune/controller/PhortuneCartCheckoutController.php
@@ -42,6 +42,7 @@
case PhortuneCart::STATUS_CHARGED:
case PhortuneCart::STATUS_PURCHASING:
case PhortuneCart::STATUS_HOLD:
+ case PhortuneCart::STATUS_REVIEW:
case PhortuneCart::STATUS_PURCHASED:
// For these states, kick the user to the order page to give them
// information and options.
diff --git a/src/applications/phortune/controller/PhortuneCartViewController.php b/src/applications/phortune/controller/PhortuneCartViewController.php
--- a/src/applications/phortune/controller/PhortuneCartViewController.php
+++ b/src/applications/phortune/controller/PhortuneCartViewController.php
@@ -72,6 +72,19 @@
phutil_tag('strong', array(), pht('Update Status')));
}
break;
+ case PhortuneCart::STATUS_REVIEW:
+ if ($can_admin) {
+ $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 AphrontErrorView())
->setSeverity(AphrontErrorView::SEVERITY_NOTICE)
@@ -197,6 +210,7 @@
$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/");
$view->addAction(
id(new PhabricatorActionView())
@@ -207,6 +221,15 @@
->setHref($cancel_uri));
if ($can_admin) {
+ 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'))
diff --git a/src/applications/phortune/provider/PhortunePayPalPaymentProvider.php b/src/applications/phortune/provider/PhortunePayPalPaymentProvider.php
--- a/src/applications/phortune/provider/PhortunePayPalPaymentProvider.php
+++ b/src/applications/phortune/provider/PhortunePayPalPaymentProvider.php
@@ -472,7 +472,7 @@
return $response;
case 'cancel':
- if ($cart->getStatus() !== PhortuneCart::STATUS_PURCHASING) {
+ if ($cart->getStatus() === PhortuneCart::STATUS_PURCHASING) {
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
// TODO: Since the user cancelled this, we could conceivably just
// throw it away or make it more clear that it's a user cancel.
diff --git a/src/applications/phortune/query/PhortuneCartSearchEngine.php b/src/applications/phortune/query/PhortuneCartSearchEngine.php
--- a/src/applications/phortune/query/PhortuneCartSearchEngine.php
+++ b/src/applications/phortune/query/PhortuneCartSearchEngine.php
@@ -31,6 +31,8 @@
array(
PhortuneCart::STATUS_PURCHASING,
PhortuneCart::STATUS_CHARGED,
+ PhortuneCart::STATUS_HOLD,
+ PhortuneCart::STATUS_REVIEW,
PhortuneCart::STATUS_PURCHASED,
));
diff --git a/src/applications/phortune/storage/PhortuneCart.php b/src/applications/phortune/storage/PhortuneCart.php
--- a/src/applications/phortune/storage/PhortuneCart.php
+++ b/src/applications/phortune/storage/PhortuneCart.php
@@ -8,6 +8,7 @@
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;
@@ -59,6 +60,7 @@
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'),
);
}
@@ -163,11 +165,68 @@
$this->endReadLocking();
$this->saveTransaction();
- foreach ($this->purchases as $purchase) {
- $purchase->getProduct()->didPurchaseProduct($purchase);
+ // 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();
}
- $this->setStatus(self::STATUS_PURCHASED)->save();
+ 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();
+
+ // TODO: Notify merchant to review order.
+
+ 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();
return $this;
}

File Metadata

Mime Type
text/plain
Expires
Sat, May 11, 5:25 AM (1 w, 5 d ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
6284901
Default Alt Text
D10675.id25634.diff (11 KB)

Event Timeline