diff --git a/resources/sql/autopatches/20141007.fundmerchant.sql b/resources/sql/autopatches/20141007.fundmerchant.sql new file mode 100644 --- /dev/null +++ b/resources/sql/autopatches/20141007.fundmerchant.sql @@ -0,0 +1,2 @@ +ALTER TABLE {$NAMESPACE}_fund.fund_initiative + ADD merchantPHID VARBINARY(64); diff --git a/resources/sql/autopatches/20141007.phortunecartmerchant.sql b/resources/sql/autopatches/20141007.phortunecartmerchant.sql new file mode 100644 --- /dev/null +++ b/resources/sql/autopatches/20141007.phortunecartmerchant.sql @@ -0,0 +1,5 @@ +ALTER TABLE {$NAMESPACE}_phortune.phortune_cart + ADD merchantPHID VARBINARY(64) NOT NULL; + +ALTER TABLE {$NAMESPACE}_phortune.phortune_cart + ADD KEY `key_merchant` (merchantPHID); diff --git a/resources/sql/autopatches/20141007.phortunecharge.sql b/resources/sql/autopatches/20141007.phortunecharge.sql new file mode 100644 --- /dev/null +++ b/resources/sql/autopatches/20141007.phortunecharge.sql @@ -0,0 +1,16 @@ +TRUNCATE TABLE {$NAMESPACE}_phortune.phortune_charge; + +ALTER TABLE {$NAMESPACE}_phortune.phortune_charge + DROP paymentProviderKey; + +ALTER TABLE {$NAMESPACE}_phortune.phortune_charge + ADD merchantPHID VARBINARY(64) NOT NULL; + +ALTER TABLE {$NAMESPACE}_phortune.phortune_charge + ADD providerPHID VARBINARY(64) NOT NULL; + +ALTER TABLE {$NAMESPACE}_phortune.phortune_charge + ADD KEY `key_merchant` (merchantPHID); + +ALTER TABLE {$NAMESPACE}_phortune.phortune_charge + ADD KEY `key_provider` (providerPHID); diff --git a/resources/sql/autopatches/20141007.phortunepayment.sql b/resources/sql/autopatches/20141007.phortunepayment.sql new file mode 100644 --- /dev/null +++ b/resources/sql/autopatches/20141007.phortunepayment.sql @@ -0,0 +1,16 @@ +TRUNCATE TABLE {$NAMESPACE}_phortune.phortune_paymentmethod; + +ALTER TABLE {$NAMESPACE}_phortune.phortune_paymentmethod + DROP providerType; + +ALTER TABLE {$NAMESPACE}_phortune.phortune_paymentmethod + DROP providerDomain; + +ALTER TABLE {$NAMESPACE}_phortune.phortune_paymentmethod + ADD merchantPHID VARBINARY(64) NOT NULL; + +ALTER TABLE {$NAMESPACE}_phortune.phortune_paymentmethod + ADD providerPHID VARBINARY(64) NOT NULL; + +ALTER TABLE {$NAMESPACE}_phortune.phortune_paymentmethod + ADD KEY `key_merchant` (merchantPHID, accountPHID); diff --git a/src/applications/fund/controller/FundInitiativeBackController.php b/src/applications/fund/controller/FundInitiativeBackController.php --- a/src/applications/fund/controller/FundInitiativeBackController.php +++ b/src/applications/fund/controller/FundInitiativeBackController.php @@ -21,6 +21,14 @@ return new Aphront404Response(); } + $merchant = id(new PhortuneMerchantQuery()) + ->setViewer($viewer) + ->withPHIDs(array($initiative->getMerchantPHID())) + ->executeOne(); + if (!$merchant) { + return new Aphront404Response(); + } + $initiative_uri = '/'.$initiative->getMonogram(); if ($initiative->isClosed()) { @@ -73,7 +81,7 @@ $cart_implementation = id(new FundBackerCart()) ->setInitiative($initiative); - $cart = $account->newCart($viewer, $cart_implementation); + $cart = $account->newCart($viewer, $cart_implementation, $merchant); $purchase = $cart->newPurchase($viewer, $product); $purchase diff --git a/src/applications/fund/controller/FundInitiativeEditController.php b/src/applications/fund/controller/FundInitiativeEditController.php --- a/src/applications/fund/controller/FundInitiativeEditController.php +++ b/src/applications/fund/controller/FundInitiativeEditController.php @@ -48,6 +48,9 @@ $e_name = true; $v_name = $initiative->getName(); + $e_merchant = null; + $v_merchant = $initiative->getMerchantPHID(); + $v_desc = $initiative->getDescription(); if ($is_new) { @@ -65,10 +68,12 @@ $v_desc = $request->getStr('description'); $v_view = $request->getStr('viewPolicy'); $v_edit = $request->getStr('editPolicy'); + $v_merchant = $request->getStr('merchantPHID'); $v_projects = $request->getArr('projects'); $type_name = FundInitiativeTransaction::TYPE_NAME; $type_desc = FundInitiativeTransaction::TYPE_DESCRIPTION; + $type_merchant = FundInitiativeTransaction::TYPE_MERCHANT; $type_view = PhabricatorTransactions::TYPE_VIEW_POLICY; $type_edit = PhabricatorTransactions::TYPE_EDIT_POLICY; @@ -83,6 +88,10 @@ ->setNewValue($v_desc); $xactions[] = id(new FundInitiativeTransaction()) + ->setTransactionType($type_merchant) + ->setNewValue($v_merchant); + + $xactions[] = id(new FundInitiativeTransaction()) ->setTransactionType($type_view) ->setNewValue($v_view); @@ -110,6 +119,7 @@ $validation_exception = $ex; $e_name = $ex->getShortMessage($type_name); + $e_merchant = $ex->getShortMessage($type_merchant); $initiative->setViewPolicy($v_view); $initiative->setEditPolicy($v_edit); @@ -127,6 +137,45 @@ $project_handles = array(); } + $merchants = id(new PhortuneMerchantQuery()) + ->setViewer($viewer) + ->requireCapabilities( + array( + PhabricatorPolicyCapability::CAN_VIEW, + PhabricatorPolicyCapability::CAN_EDIT, + )) + ->execute(); + + $merchant_options = array(); + foreach ($merchants as $merchant) { + $merchant_options[$merchant->getPHID()] = pht( + 'Merchant %d %s', + $merchant->getID(), + $merchant->getName()); + } + + if ($v_merchant && empty($merchant_options[$v_merchant])) { + $merchant_options = array( + $v_merchant => pht('(Restricted Merchant)'), + ) + $merchant_options; + } + + if (!$merchant_options) { + return $this->newDialog() + ->setTitle(pht('No Valid Phortune Merchant Accounts')) + ->appendParagraph( + pht( + 'You do not control any merchant accounts which can receive '. + 'payments from this initiative. When you create an initiative, '. + 'you need to specify a merchant account where funds will be paid '. + 'to.')) + ->appendParagraph( + pht( + 'Create a merchant account in the Phortune application before '. + 'creating an initiative in Fund.')) + ->addCancelButton($this->getApplicationURI()); + } + $form = id(new AphrontFormView()) ->setUser($viewer) ->appendChild( @@ -136,6 +185,13 @@ ->setValue($v_name) ->setError($e_name)) ->appendChild( + id(new AphrontFormSelectControl()) + ->setName('merchantPHID') + ->setLabel(pht('Pay To Merchant')) + ->setValue($v_merchant) + ->setError($e_merchant) + ->setOptions($merchant_options)) + ->appendChild( id(new PhabricatorRemarkupControl()) ->setName('description') ->setLabel(pht('Description')) diff --git a/src/applications/fund/editor/FundInitiativeEditor.php b/src/applications/fund/editor/FundInitiativeEditor.php --- a/src/applications/fund/editor/FundInitiativeEditor.php +++ b/src/applications/fund/editor/FundInitiativeEditor.php @@ -18,6 +18,7 @@ $types[] = FundInitiativeTransaction::TYPE_DESCRIPTION; $types[] = FundInitiativeTransaction::TYPE_STATUS; $types[] = FundInitiativeTransaction::TYPE_BACKER; + $types[] = FundInitiativeTransaction::TYPE_MERCHANT; $types[] = PhabricatorTransactions::TYPE_VIEW_POLICY; $types[] = PhabricatorTransactions::TYPE_EDIT_POLICY; @@ -36,6 +37,8 @@ return $object->getStatus(); case FundInitiativeTransaction::TYPE_BACKER: return null; + case FundInitiativeTransaction::TYPE_MERCHANT: + return $object->getMerchantPHID(); } return parent::getCustomTransactionOldValue($object, $xaction); @@ -50,6 +53,7 @@ case FundInitiativeTransaction::TYPE_DESCRIPTION: case FundInitiativeTransaction::TYPE_STATUS: case FundInitiativeTransaction::TYPE_BACKER: + case FundInitiativeTransaction::TYPE_MERCHANT: return $xaction->getNewValue(); } @@ -67,6 +71,9 @@ case FundInitiativeTransaction::TYPE_DESCRIPTION: $object->setDescription($xaction->getNewValue()); return; + case FundInitiativeTransaction::TYPE_MERCHANT: + $object->setMerchantPHID($xaction->getNewValue()); + return; case FundInitiativeTransaction::TYPE_STATUS: $object->setStatus($xaction->getNewValue()); return; @@ -89,6 +96,7 @@ case FundInitiativeTransaction::TYPE_NAME: case FundInitiativeTransaction::TYPE_DESCRIPTION: case FundInitiativeTransaction::TYPE_STATUS: + case FundInitiativeTransaction::TYPE_MERCHANT: case FundInitiativeTransaction::TYPE_BACKER: // TODO: Maybe we should apply the backer transaction from here? return; @@ -124,6 +132,46 @@ $errors[] = $error; } break; + case FundInitiativeTransaction::TYPE_MERCHANT: + $missing = $this->validateIsEmptyTextField( + $object->getName(), + $xactions); + if ($missing) { + $error = new PhabricatorApplicationTransactionValidationError( + $type, + pht('Required'), + pht('Payable merchant is required.'), + nonempty(last($xactions), null)); + + $error->setIsMissingFieldError(true); + $errors[] = $error; + } else if ($xactions) { + $merchant_phid = last($xactions)->getNewValue(); + + // Make sure the actor has permission to edit the merchant they're + // selecting. You aren't allowed to send payments to an account you + // do not control. + $merchants = id(new PhortuneMerchantQuery()) + ->setViewer($this->requireActor()) + ->withPHIDs(array($merchant_phid)) + ->requireCapabilities( + array( + PhabricatorPolicyCapability::CAN_VIEW, + PhabricatorPolicyCapability::CAN_EDIT, + )) + ->execute(); + if (!$merchants) { + $error = new PhabricatorApplicationTransactionValidationError( + $type, + pht('Invalid'), + pht( + 'You must specify a merchant account you control as the '. + 'recipient of funds from this initiative.'), + last($xactions)); + $errors[] = $error; + } + } + break; } return $errors; diff --git a/src/applications/fund/storage/FundInitiative.php b/src/applications/fund/storage/FundInitiative.php --- a/src/applications/fund/storage/FundInitiative.php +++ b/src/applications/fund/storage/FundInitiative.php @@ -13,6 +13,7 @@ protected $name; protected $ownerPHID; + protected $merchantPHID; protected $description; protected $viewPolicy; protected $editPolicy; @@ -52,6 +53,7 @@ 'name' => 'text255', 'description' => 'text', 'status' => 'text32', + 'merchantPHID' => 'phid?', ), self::CONFIG_KEY_SCHEMA => array( 'key_status' => array( diff --git a/src/applications/fund/storage/FundInitiativeTransaction.php b/src/applications/fund/storage/FundInitiativeTransaction.php --- a/src/applications/fund/storage/FundInitiativeTransaction.php +++ b/src/applications/fund/storage/FundInitiativeTransaction.php @@ -7,6 +7,7 @@ const TYPE_DESCRIPTION = 'fund:description'; const TYPE_STATUS = 'fund:status'; const TYPE_BACKER = 'fund:backer'; + const TYPE_MERCHANT = 'fund:merchant'; public function getApplicationName() { return 'fund'; @@ -20,6 +21,27 @@ return null; } + public function getRequiredHandlePHIDs() { + $phids = parent::getRequiredHandlePHIDs(); + + $old = $this->getOldValue(); + $new = $this->getNewValue(); + + $type = $this->getTransactionType(); + switch ($type) { + case FundInitiativeTransaction::TYPE_MERCHANT: + if ($old) { + $phids[] = $old; + } + if ($new) { + $phids[] = $new; + } + break; + } + + return $phids; + } + public function getTitle() { $author_phid = $this->getAuthorPHID(); $object_phid = $this->getObjectPHID(); @@ -62,6 +84,20 @@ return pht( '%s backed this initiative.', $this->renderHandleLink($author_phid)); + case FundInitiativeTransaction::TYPE_MERCHANT: + if ($old === null) { + return pht( + '%s set this initiative to pay to %s.', + $this->renderHandleLink($author_phid), + $this->renderHandleLink($new)); + } else { + return pht( + '%s changed the merchant receiving funds from this '. + 'initiative from %s to %s.', + $this->renderHandleLink($author_phid), + $this->renderHandleLink($old), + $this->renderHandleLink($new)); + } } return parent::getTitle(); 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 @@ -89,13 +89,7 @@ $id = $account->getID(); $header = id(new PHUIHeaderView()) - ->setHeader(pht('Payment Methods')) - ->addActionLink( - id(new PHUIButtonView()) - ->setTag('a') - ->setHref($this->getApplicationURI($id.'/card/new/')) - ->setText(pht('Add Payment Method')) - ->setIcon(id(new PHUIIconView())->setIconFont('fa-plus'))); + ->setHeader(pht('Payment Methods')); $list = id(new PHUIObjectItemListView()) ->setUser($viewer) 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 @@ -23,6 +23,7 @@ } $cancel_uri = $cart->getCancelURI(); + $merchant = $cart->getMerchant(); switch ($cart->getStatus()) { case PhortuneCart::STATUS_BUILDING: @@ -83,6 +84,7 @@ $methods = id(new PhortunePaymentMethodQuery()) ->setViewer($viewer) ->withAccountPHIDs(array($account->getPHID())) + ->withMerchantPHIDs(array($merchant->getPHID())) ->withStatuses(array(PhortunePaymentMethod::STATUS_ACTIVE)) ->execute(); @@ -142,14 +144,22 @@ $method_control->setError($e_method); - $payment_method_uri = $this->getApplicationURI( - $account->getID().'/card/new/'); + $account_id = $account->getID(); + + $payment_method_uri = $this->getApplicationURI("{$account_id}/card/new/"); + $payment_method_uri = new PhutilURI($payment_method_uri); + $payment_method_uri->setQueryParams( + array( + 'merchantID' => $merchant->getID(), + 'cartID' => $cart->getID(), + )); $form = id(new AphrontFormView()) ->setUser($viewer) ->appendChild($method_control); - $add_providers = PhortunePaymentProvider::getProvidersForAddPaymentMethod(); + $add_providers = $this->loadCreatePaymentMethodProvidersForMerchant( + $merchant); if ($add_providers) { $new_method = phutil_tag( 'a', @@ -178,7 +188,7 @@ $provider_form = null; - $pay_providers = PhortunePaymentProvider::getProvidersForOneTimePayment(); + $pay_providers = $this->loadOneTimePaymentProvidersForMerchant($merchant); if ($pay_providers) { $one_time_options = array(); foreach ($pay_providers as $provider) { 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 @@ -86,4 +86,50 @@ } } + private function loadEnabledProvidersForMerchant(PhortuneMerchant $merchant) { + $viewer = $this->getRequest()->getUser(); + + $provider_configs = id(new PhortunePaymentProviderConfigQuery()) + ->setViewer($viewer) + ->withMerchantPHIDs(array($merchant->getPHID())) + ->execute(); + $providers = mpull($provider_configs, 'buildProvider', 'getID'); + + foreach ($providers as $key => $provider) { + if (!$provider->isEnabled()) { + unset($providers[$key]); + } + } + + return $providers; + } + + protected function loadCreatePaymentMethodProvidersForMerchant( + PhortuneMerchant $merchant) { + + $providers = $this->loadEnabledProvidersForMerchant($merchant); + foreach ($providers as $key => $provider) { + if (!$provider->canCreatePaymentMethods()) { + unset($providers[$key]); + continue; + } + } + + return $providers; + } + + protected function loadOneTimePaymentProvidersForMerchant( + PhortuneMerchant $merchant) { + + $providers = $this->loadEnabledProvidersForMerchant($merchant); + foreach ($providers as $key => $provider) { + if (!$provider->canProcessOneTimePayments()) { + unset($providers[$key]); + continue; + } + } + + return $providers; + } + } diff --git a/src/applications/phortune/controller/PhortunePaymentMethodCreateController.php b/src/applications/phortune/controller/PhortunePaymentMethodCreateController.php --- a/src/applications/phortune/controller/PhortunePaymentMethodCreateController.php +++ b/src/applications/phortune/controller/PhortunePaymentMethodCreateController.php @@ -11,28 +11,36 @@ public function processRequest() { $request = $this->getRequest(); - $user = $request->getUser(); + $viewer = $request->getUser(); $account = id(new PhortuneAccountQuery()) - ->setViewer($user) + ->setViewer($viewer) ->withIDs(array($this->accountID)) ->executeOne(); if (!$account) { return new Aphront404Response(); } + $merchant = id(new PhortuneMerchantQuery()) + ->setViewer($viewer) + ->withIDs(array($request->getInt('merchantID'))) + ->executeOne(); + if (!$merchant) { + return new Aphront404Response(); + } + $cancel_uri = $this->getApplicationURI($account->getID().'/'); $account_uri = $this->getApplicationURI($account->getID().'/'); - $providers = PhortunePaymentProvider::getProvidersForAddPaymentMethod(); + $providers = $this->loadCreatePaymentMethodProvidersForMerchant($merchant); if (!$providers) { throw new Exception( 'There are no payment providers enabled that can add payment '. 'methods.'); } - $provider_key = $request->getStr('providerKey'); - if (empty($providers[$provider_key])) { + $provider_id = $request->getInt('providerID'); + if (empty($providers[$provider_id])) { $choices = array(); foreach ($providers as $provider) { $choices[] = $this->renderSelectProvider($provider); @@ -53,16 +61,16 @@ ->addCancelButton($account_uri); } - $provider = $providers[$provider_key]; + $provider = $providers[$provider_id]; $errors = array(); if ($request->isFormPost() && $request->getBool('isProviderForm')) { $method = id(new PhortunePaymentMethod()) ->setAccountPHID($account->getPHID()) - ->setAuthorPHID($user->getPHID()) - ->setStatus(PhortunePaymentMethod::STATUS_ACTIVE) - ->setProviderType($provider->getProviderType()) - ->setProviderDomain($provider->getProviderDomain()); + ->setAuthorPHID($viewer->getPHID()) + ->setMerchantPHID($merchant->getPHID()) + ->setProviderPHID($provider->getProviderConfig()->getPHID()) + ->setStatus(PhortunePaymentMethod::STATUS_ACTIVE); if (!$errors) { $errors = $this->processClientErrors( @@ -97,12 +105,21 @@ if (!$errors) { $method->save(); - $save_uri = new PhutilURI($account_uri); - $save_uri->setFragment('payment'); - return id(new AphrontRedirectResponse())->setURI($save_uri); + // If we added this method on a cart flow, return to the cart to + // check out. + $cart_id = $request->getInt('cartID'); + if ($cart_id) { + $next_uri = $this->getApplicationURI( + "cart/{$cart_id}/checkout/?paymentMethodID=".$method->getID()); + } else { + $next_uri = new PhutilURI($account_uri); + $next_uri->setFragment('payment'); + } + + return id(new AphrontRedirectResponse())->setURI($next_uri); } else { $dialog = id(new AphrontDialogView()) - ->setUser($user) + ->setUser($viewer) ->setTitle(pht('Error Adding Payment Method')) ->appendChild(id(new AphrontErrorView())->setErrors($errors)) ->addCancelButton($request->getRequestURI()); @@ -114,10 +131,11 @@ $form = $provider->renderCreatePaymentMethodForm($request, $errors); $form - ->setUser($user) + ->setUser($viewer) ->setAction($request->getRequestURI()) ->setWorkflow(true) - ->addHiddenInput('providerKey', $provider_key) + ->addHiddenInput('providerID', $provider_id) + ->addHiddenInput('cartID', $request->getInt('cartID')) ->addHiddenInput('isProviderForm', true) ->appendChild( id(new AphrontFormSubmitControl()) @@ -145,7 +163,7 @@ PhortunePaymentProvider $provider) { $request = $this->getRequest(); - $user = $request->getUser(); + $viewer = $request->getUser(); $description = $provider->getPaymentMethodDescription(); $icon_uri = $provider->getPaymentMethodIcon(); @@ -165,8 +183,8 @@ ->setSubtext($details); $form = id(new AphrontFormView()) - ->setUser($user) - ->addHiddenInput('providerKey', $provider->getProviderKey()) + ->setUser($viewer) + ->addHiddenInput('providerID', $provider->getProviderConfig()->getID()) ->appendChild($button); return $form; diff --git a/src/applications/phortune/phid/PhortuneMerchantPHIDType.php b/src/applications/phortune/phid/PhortuneMerchantPHIDType.php --- a/src/applications/phortune/phid/PhortuneMerchantPHIDType.php +++ b/src/applications/phortune/phid/PhortuneMerchantPHIDType.php @@ -30,7 +30,7 @@ $id = $merchant->getID(); - $handle->setName(pht('Merchant %d', $id)); + $handle->setName(pht('Merchant %d %s', $id, $merchant->getName())); $handle->setURI("/phortune/merchant/{$id}/"); } } diff --git a/src/applications/phortune/provider/PhortunePaymentProvider.php b/src/applications/phortune/provider/PhortunePaymentProvider.php --- a/src/applications/phortune/provider/PhortunePaymentProvider.php +++ b/src/applications/phortune/provider/PhortunePaymentProvider.php @@ -120,36 +120,6 @@ ->loadObjects(); } - public static function getEnabledProviders() { - $providers = self::getAllProviders(); - foreach ($providers as $key => $provider) { - if (!$provider->isEnabled()) { - unset($providers[$key]); - } - } - return $providers; - } - - public static function getProvidersForAddPaymentMethod() { - $providers = self::getEnabledProviders(); - foreach ($providers as $key => $provider) { - if (!$provider->canCreatePaymentMethods()) { - unset($providers[$key]); - } - } - return $providers; - } - - public static function getProvidersForOneTimePayment() { - $providers = self::getEnabledProviders(); - foreach ($providers as $key => $provider) { - if (!$provider->canProcessOneTimePayments()) { - unset($providers[$key]); - } - } - return $providers; - } - abstract public function isEnabled(); abstract public function getPaymentMethodDescription(); 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 @@ -66,6 +66,21 @@ $cart->attachAccount($account); } + $merchants = id(new PhortuneMerchantQuery()) + ->setViewer($this->getViewer()) + ->withPHIDs(mpull($carts, 'getMerchantPHID')) + ->execute(); + $merchants = mpull($merchants, null, 'getPHID'); + + foreach ($carts as $key => $cart) { + $merchant = idx($merchants, $cart->getMerchantPHID()); + if (!$merchant) { + unset($carts[$key]); + continue; + } + $cart->attachMerchant($merchant); + } + $implementations = array(); $cart_map = mgroup($carts, 'getCartClass'); 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 @@ -6,6 +6,7 @@ private $ids; private $phids; private $accountPHIDs; + private $merchantPHIDs; private $statuses; public function withIDs(array $ids) { @@ -23,6 +24,11 @@ return $this; } + public function withMerchantPHIDs(array $phids) { + $this->merchantPHIDs = $phids; + return $this; + } + public function withStatuses(array $statuses) { $this->statuses = $statuses; return $this; @@ -44,15 +50,6 @@ } protected function willFilterPage(array $methods) { - foreach ($methods as $key => $method) { - try { - $method->buildPaymentProvider(); - } catch (Exception $ex) { - unset($methods[$key]); - continue; - } - } - $accounts = id(new PhortuneAccountQuery()) ->setViewer($this->getViewer()) ->withPHIDs(mpull($methods, 'getAccountPHID')) @@ -68,6 +65,36 @@ $method->attachAccount($account); } + $merchants = id(new PhortuneMerchantQuery()) + ->setViewer($this->getViewer()) + ->withPHIDs(mpull($methods, 'getMerchantPHID')) + ->execute(); + $merchants = mpull($merchants, null, 'getPHID'); + + foreach ($methods as $key => $method) { + $merchant = idx($merchants, $method->getMerchantPHID()); + if (!$merchant) { + unset($methods[$key]); + continue; + } + $method->attachMerchant($merchant); + } + + $provider_configs = id(new PhortunePaymentProviderConfigQuery()) + ->setViewer($this->getViewer()) + ->withPHIDs(mpull($methods, 'getProviderPHID')) + ->execute(); + $provider_configs = mpull($provider_configs, null, 'getPHID'); + + foreach ($methods as $key => $method) { + $provider_config = idx($provider_configs, $method->getProviderPHID()); + if (!$provider_config) { + unset($methods[$key]); + continue; + } + $method->attachProviderConfig($provider_config); + } + return $methods; } @@ -95,6 +122,13 @@ $this->accountPHIDs); } + if ($this->merchantPHIDs !== null) { + $where[] = qsprintf( + $conn, + 'merchantPHID IN (%Ls)', + $this->merchantPHIDs); + } + if ($this->statuses !== null) { $where[] = qsprintf( $conn, 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 @@ -58,9 +58,10 @@ public function newCart( PhabricatorUser $actor, - PhortuneCartImplementation $implementation) { + PhortuneCartImplementation $implementation, + PhortuneMerchant $merchant) { - $cart = PhortuneCart::initializeNewCart($actor, $this); + $cart = PhortuneCart::initializeNewCart($actor, $this, $merchant); $cart->setCartClass(get_class($implementation)); $cart->attachImplementation($implementation); 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 @@ -11,6 +11,7 @@ protected $accountPHID; protected $authorPHID; + protected $merchantPHID; protected $cartClass; protected $status; protected $metadata = array(); @@ -18,14 +19,17 @@ private $account = self::ATTACHABLE; private $purchases = self::ATTACHABLE; private $implementation = self::ATTACHABLE; + private $merchant = self::ATTACHABLE; public static function initializeNewCart( PhabricatorUser $actor, - PhortuneAccount $account) { + PhortuneAccount $account, + PhortuneMerchant $merchant) { $cart = id(new PhortuneCart()) ->setAuthorPHID($actor->getPHID()) ->setStatus(self::STATUS_BUILDING) - ->setAccountPHID($account->getPHID()); + ->setAccountPHID($account->getPHID()) + ->setMerchantPHID($merchant->getPHID()); $cart->account = $account; $cart->purchases = array(); @@ -63,7 +67,8 @@ ->setAccountPHID($account->getPHID()) ->setCartPHID($this->getPHID()) ->setAuthorPHID($actor->getPHID()) - ->setPaymentProviderKey($provider->getProviderKey()) + ->setMerchantPHID($this->getMerchant()->getPHID()) + ->setProviderPHID($provider->getProviderConfig()->getPHID()) ->setAmountAsCurrency($this->getTotalPriceAsCurrency()); if ($method) { @@ -154,6 +159,9 @@ 'key_account' => array( 'columns' => array('accountPHID'), ), + 'key_merchant' => array( + 'columns' => array('merchantPHID'), + ), ), ) + parent::getConfiguration(); } @@ -182,6 +190,15 @@ 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; diff --git a/src/applications/phortune/storage/PhortuneCharge.php b/src/applications/phortune/storage/PhortuneCharge.php --- a/src/applications/phortune/storage/PhortuneCharge.php +++ b/src/applications/phortune/storage/PhortuneCharge.php @@ -16,7 +16,8 @@ protected $accountPHID; protected $authorPHID; protected $cartPHID; - protected $paymentProviderKey; + protected $providerPHID; + protected $merchantPHID; protected $paymentMethodPHID; protected $amountAsCurrency; protected $status; @@ -52,6 +53,12 @@ 'key_account' => array( 'columns' => array('accountPHID'), ), + 'key_merchant' => array( + 'columns' => array('merchantPHID'), + ), + 'key_provider' => array( + 'columns' => array('providerPHID'), + ), ), ) + parent::getConfiguration(); } diff --git a/src/applications/phortune/storage/PhortunePaymentMethod.php b/src/applications/phortune/storage/PhortunePaymentMethod.php --- a/src/applications/phortune/storage/PhortunePaymentMethod.php +++ b/src/applications/phortune/storage/PhortunePaymentMethod.php @@ -14,6 +14,7 @@ protected $status; protected $accountPHID; protected $authorPHID; + protected $merchantPHID; protected $providerPHID; protected $expires; protected $metadata = array(); @@ -21,6 +22,8 @@ protected $lastFourDigits; private $account = self::ATTACHABLE; + private $merchant = self::ATTACHABLE; + private $providerConfig = self::ATTACHABLE; public function getConfiguration() { return array( @@ -39,6 +42,9 @@ 'key_account' => array( 'columns' => array('accountPHID', 'status'), ), + 'key_merchant' => array( + 'columns' => array('merchantPHID', 'accountPHID'), + ), ), ) + parent::getConfiguration(); } @@ -57,6 +63,24 @@ 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 attachProviderConfig(PhortunePaymentProviderConfig $config) { + $this->providerConfig = $config; + return $this; + } + + public function getProviderConfig() { + return $this->assertAttached($this->providerConfig); + } + public function getDescription() { $provider = $this->buildPaymentProvider(); return $provider->getPaymentMethodProviderDescription(); @@ -72,7 +96,7 @@ } public function buildPaymentProvider() { - throw new Exception(pht('TODO: Reimplement this junk.')); + return $this->getProviderConfig()->buildProvider(); } public function getDisplayName() {