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;
   }