diff --git a/src/applications/people/storage/PhabricatorUser.php b/src/applications/people/storage/PhabricatorUser.php
--- a/src/applications/people/storage/PhabricatorUser.php
+++ b/src/applications/people/storage/PhabricatorUser.php
@@ -50,6 +50,8 @@
   private $alternateCSRFString = self::ATTACHABLE;
   private $session = self::ATTACHABLE;
 
+  private $authorities = array();
+
   protected function readField($field) {
     switch ($field) {
       case 'timezoneIdentifier':
@@ -694,6 +696,25 @@
       $email->getUserPHID());
   }
 
+
+  /**
+   * Grant a user a source of authority, to let them bypass policy checks they
+   * could not otherwise.
+   */
+  public function grantAuthority($authority) {
+    $this->authorities[] = $authority;
+    return $this;
+  }
+
+
+  /**
+   * Get authorities granted to the user.
+   */
+  public function getAuthorities() {
+    return $this->authorities;
+  }
+
+
 /* -(  Multi-Factor Authentication  )---------------------------------------- */
 
 
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
@@ -85,6 +85,12 @@
           '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',
diff --git a/src/applications/phortune/controller/PhortuneCartListController.php b/src/applications/phortune/controller/PhortuneCartListController.php
--- a/src/applications/phortune/controller/PhortuneCartListController.php
+++ b/src/applications/phortune/controller/PhortuneCartListController.php
@@ -42,6 +42,7 @@
         return new Aphront404Response();
       }
       $this->merchant = $merchant;
+      $viewer->grantAuthority($merchant);
       $engine->setMerchant($merchant);
     } else if ($account_id) {
       $account = id(new PhortuneAccountQuery())
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
@@ -13,6 +13,11 @@
     $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))
@@ -157,7 +162,11 @@
     $account = $cart->getAccount();
 
     $crumbs = $this->buildApplicationCrumbs();
-    $this->addAccountCrumb($crumbs, $cart->getAccount());
+    if ($authority) {
+      $this->addMerchantCrumb($crumbs, $authority);
+    } else {
+      $this->addAccountCrumb($crumbs, $cart->getAccount());
+    }
     $crumbs->addTextCrumb(pht('Cart %d', $cart->getID()));
 
     $timeline = $this->buildTransactionTimeline(
diff --git a/src/applications/phortune/controller/PhortuneController.php b/src/applications/phortune/controller/PhortuneController.php
--- a/src/applications/phortune/controller/PhortuneController.php
+++ b/src/applications/phortune/controller/PhortuneController.php
@@ -84,4 +84,30 @@
     return $providers;
   }
 
+  protected function loadMerchantAuthority() {
+    $request = $this->getRequest();
+    $viewer = $this->getViewer();
+
+    $is_merchant = (bool)$request->getURIData('merchantID');
+    if (!$is_merchant) {
+      return null;
+    }
+
+    $merchant = id(new PhortuneMerchantQuery())
+      ->setViewer($viewer)
+      ->withIDs(array($request->getURIData('merchantID')))
+      ->requireCapabilities(
+        array(
+          PhabricatorPolicyCapability::CAN_VIEW,
+          PhabricatorPolicyCapability::CAN_EDIT,
+        ))
+      ->executeOne();
+    if (!$merchant) {
+      return null;
+    }
+
+    $viewer->grantAuthority($merchant);
+    return $merchant;
+  }
+
 }
diff --git a/src/applications/phortune/controller/PhortuneSubscriptionListController.php b/src/applications/phortune/controller/PhortuneSubscriptionListController.php
--- a/src/applications/phortune/controller/PhortuneSubscriptionListController.php
+++ b/src/applications/phortune/controller/PhortuneSubscriptionListController.php
@@ -36,6 +36,7 @@
         return new Aphront404Response();
       }
       $this->merchant = $merchant;
+      $viewer->grantAuthority($merchant);
       $engine->setMerchant($merchant);
     } else if ($this->accountID) {
       $account = id(new PhortuneAccountQuery())
diff --git a/src/applications/phortune/controller/PhortuneSubscriptionViewController.php b/src/applications/phortune/controller/PhortuneSubscriptionViewController.php
--- a/src/applications/phortune/controller/PhortuneSubscriptionViewController.php
+++ b/src/applications/phortune/controller/PhortuneSubscriptionViewController.php
@@ -5,6 +5,8 @@
   public function handleRequest(AphrontRequest $request) {
     $viewer = $this->getViewer();
 
+    $is_merchant = (bool)$this->loadMerchantAuthority();
+
     $subscription = id(new PhortuneSubscriptionQuery())
       ->setViewer($viewer)
       ->withIDs(array($request->getURIData('id')))
@@ -19,7 +21,6 @@
       $subscription,
       PhabricatorPolicyCapability::CAN_EDIT);
 
-    $is_merchant = (bool)$request->getURIData('merchantID');
     $merchant = $subscription->getMerchant();
     $account = $subscription->getAccount();
 
diff --git a/src/applications/phortune/query/PhortuneCartQuery.php b/src/applications/phortune/query/PhortuneCartQuery.php
--- a/src/applications/phortune/query/PhortuneCartQuery.php
+++ b/src/applications/phortune/query/PhortuneCartQuery.php
@@ -91,6 +91,10 @@
       $cart->attachAccount($account);
     }
 
+    if (!$carts) {
+      return array();
+    }
+
     $merchants = id(new PhortuneMerchantQuery())
       ->setViewer($this->getViewer())
       ->withPHIDs(mpull($carts, 'getMerchantPHID'))
@@ -106,6 +110,10 @@
       $cart->attachMerchant($merchant);
     }
 
+    if (!$carts) {
+      return array();
+    }
+
     $implementations = array();
 
     $cart_map = mgroup($carts, 'getCartClass');
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
@@ -166,8 +166,21 @@
     foreach ($carts as $cart) {
       $merchant = $cart->getMerchant();
 
+      if ($this->getMerchant()) {
+        $href = $this->getApplicationURI(
+          'merchant/'.$merchant->getID().'/cart/'.$cart->getID().'/');
+      } else {
+        $href = $cart->getDetailURI();
+      }
+
       $rows[] = array(
         $cart->getID(),
+        phutil_tag(
+          'a',
+          array(
+            'href' => $href,
+          ),
+          $cart->getName()),
         $handles[$cart->getPHID()]->renderLink(),
         $handles[$merchant->getPHID()]->renderLink(),
         $handles[$cart->getAuthorPHID()]->renderLink(),
diff --git a/src/applications/phortune/query/PhortunePaymentMethodQuery.php b/src/applications/phortune/query/PhortunePaymentMethodQuery.php
--- a/src/applications/phortune/query/PhortunePaymentMethodQuery.php
+++ b/src/applications/phortune/query/PhortunePaymentMethodQuery.php
@@ -65,6 +65,10 @@
       $method->attachAccount($account);
     }
 
+    if (!$methods) {
+      return $methods;
+    }
+
     $merchants = id(new PhortuneMerchantQuery())
       ->setViewer($this->getViewer())
       ->withPHIDs(mpull($methods, 'getMerchantPHID'))
@@ -80,6 +84,10 @@
       $method->attachMerchant($merchant);
     }
 
+    if (!$methods) {
+      return $methods;
+    }
+
     $provider_configs = id(new PhortunePaymentProviderConfigQuery())
       ->setViewer($this->getViewer())
       ->withPHIDs(mpull($methods, 'getProviderPHID'))
diff --git a/src/applications/phortune/storage/PhortuneAccount.php b/src/applications/phortune/storage/PhortuneAccount.php
--- a/src/applications/phortune/storage/PhortuneAccount.php
+++ b/src/applications/phortune/storage/PhortuneAccount.php
@@ -133,10 +133,6 @@
   public function getPolicy($capability) {
     switch ($capability) {
       case PhabricatorPolicyCapability::CAN_VIEW:
-        // Accounts are technically visible to all users, because merchant
-        // controllers need to be able to see accounts in order to process
-        // orders. We lock things down more tightly at the application level.
-        return PhabricatorPolicies::POLICY_USER;
       case PhabricatorPolicyCapability::CAN_EDIT:
         if ($this->getPHID() === null) {
           // Allow a user to create an account for themselves.
@@ -149,7 +145,21 @@
 
   public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
     $members = array_fuse($this->getMemberPHIDs());
-    return isset($members[$viewer->getPHID()]);
+    if (isset($members[$viewer->getPHID()])) {
+      return true;
+    }
+
+    // If the viewer is acting on behalf of a merchant, they can see
+    // payment accounts.
+    if ($capability == PhabricatorPolicyCapability::CAN_VIEW) {
+      foreach ($viewer->getAuthorities() as $authority) {
+        if ($authority instanceof PhortuneMerchant) {
+          return true;
+        }
+      }
+    }
+
+    return false;
   }
 
   public function describeAutomaticCapability($capability) {