diff --git a/resources/celerity/map.php b/resources/celerity/map.php --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -92,7 +92,7 @@ 'rsrc/css/application/pholio/pholio.css' => '88ef5ef1', 'rsrc/css/application/phortune/phortune-credit-card-form.css' => '3b9868a8', 'rsrc/css/application/phortune/phortune-invoice.css' => '4436b241', - 'rsrc/css/application/phortune/phortune.css' => '12e8251a', + 'rsrc/css/application/phortune/phortune.css' => '508a1a5e', 'rsrc/css/application/phrequent/phrequent.css' => 'bd79cc67', 'rsrc/css/application/phriction/phriction-document-css.css' => '03380da0', 'rsrc/css/application/policy/policy-edit.css' => '8794e2ed', @@ -810,7 +810,7 @@ 'pholio-inline-comments-css' => '722b48c2', 'phortune-credit-card-form' => 'd12d214f', 'phortune-credit-card-form-css' => '3b9868a8', - 'phortune-css' => '12e8251a', + 'phortune-css' => '508a1a5e', 'phortune-invoice-css' => '4436b241', 'phrequent-css' => 'bd79cc67', 'phriction-document-css' => '03380da0', diff --git a/resources/sql/autopatches/20190816.payment.01.xaction.sql b/resources/sql/autopatches/20190816.payment.01.xaction.sql new file mode 100644 --- /dev/null +++ b/resources/sql/autopatches/20190816.payment.01.xaction.sql @@ -0,0 +1,19 @@ +CREATE TABLE {$NAMESPACE}_phortune.phortune_paymentmethodtransaction ( + id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, + phid VARBINARY(64) NOT NULL, + authorPHID VARBINARY(64) NOT NULL, + objectPHID VARBINARY(64) NOT NULL, + viewPolicy VARBINARY(64) NOT NULL, + editPolicy VARBINARY(64) NOT NULL, + commentPHID VARBINARY(64) DEFAULT NULL, + commentVersion INT UNSIGNED NOT NULL, + transactionType VARCHAR(32) NOT NULL, + oldValue LONGTEXT NOT NULL, + newValue LONGTEXT NOT NULL, + contentSource LONGTEXT NOT NULL, + metadata LONGTEXT NOT NULL, + dateCreated INT UNSIGNED NOT NULL, + dateModified INT UNSIGNED NOT NULL, + UNIQUE KEY `key_phid` (`phid`), + KEY `key_object` (`objectPHID`) +) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE {$COLLATE_TEXT}; 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 @@ -5247,7 +5247,8 @@ 'PhortuneAccountOrdersController' => 'applications/phortune/controller/account/PhortuneAccountOrdersController.php', 'PhortuneAccountOverviewController' => 'applications/phortune/controller/account/PhortuneAccountOverviewController.php', 'PhortuneAccountPHIDType' => 'applications/phortune/phid/PhortuneAccountPHIDType.php', - 'PhortuneAccountPaymentMethodsController' => 'applications/phortune/controller/account/PhortuneAccountPaymentMethodsController.php', + 'PhortuneAccountPaymentMethodListController' => 'applications/phortune/controller/account/PhortuneAccountPaymentMethodListController.php', + 'PhortuneAccountPaymentMethodViewController' => 'applications/phortune/controller/account/PhortuneAccountPaymentMethodViewController.php', 'PhortuneAccountProfileController' => 'applications/phortune/controller/account/PhortuneAccountProfileController.php', 'PhortuneAccountQuery' => 'applications/phortune/query/PhortuneAccountQuery.php', 'PhortuneAccountSubscriptionController' => 'applications/phortune/controller/account/PhortuneAccountSubscriptionController.php', @@ -5323,12 +5324,18 @@ 'PhortuneOrderTableView' => 'applications/phortune/view/PhortuneOrderTableView.php', 'PhortunePayPalPaymentProvider' => 'applications/phortune/provider/PhortunePayPalPaymentProvider.php', 'PhortunePaymentMethod' => 'applications/phortune/storage/PhortunePaymentMethod.php', - 'PhortunePaymentMethodCreateController' => 'applications/phortune/controller/payment/PhortunePaymentMethodCreateController.php', - 'PhortunePaymentMethodDisableController' => 'applications/phortune/controller/payment/PhortunePaymentMethodDisableController.php', - 'PhortunePaymentMethodEditController' => 'applications/phortune/controller/payment/PhortunePaymentMethodEditController.php', + 'PhortunePaymentMethodCreateController' => 'applications/phortune/controller/paymentmethod/PhortunePaymentMethodCreateController.php', + 'PhortunePaymentMethodDisableController' => 'applications/phortune/controller/paymentmethod/PhortunePaymentMethodDisableController.php', + 'PhortunePaymentMethodEditController' => 'applications/phortune/controller/paymentmethod/PhortunePaymentMethodEditController.php', + 'PhortunePaymentMethodEditor' => 'applications/phortune/editor/PhortunePaymentMethodEditor.php', + 'PhortunePaymentMethodNameTransaction' => 'applications/phortune/xaction/paymentmethod/PhortunePaymentMethodNameTransaction.php', 'PhortunePaymentMethodPHIDType' => 'applications/phortune/phid/PhortunePaymentMethodPHIDType.php', 'PhortunePaymentMethodPolicyCodex' => 'applications/phortune/codex/PhortunePaymentMethodPolicyCodex.php', 'PhortunePaymentMethodQuery' => 'applications/phortune/query/PhortunePaymentMethodQuery.php', + 'PhortunePaymentMethodStatusTransaction' => 'applications/phortune/xaction/paymentmethod/PhortunePaymentMethodStatusTransaction.php', + 'PhortunePaymentMethodTransaction' => 'applications/phortune/storage/PhortunePaymentMethodTransaction.php', + 'PhortunePaymentMethodTransactionQuery' => 'applications/phortune/query/PhortunePaymentMethodTransactionQuery.php', + 'PhortunePaymentMethodTransactionType' => 'applications/phortune/xaction/paymentmethod/PhortunePaymentMethodTransactionType.php', 'PhortunePaymentProvider' => 'applications/phortune/provider/PhortunePaymentProvider.php', 'PhortunePaymentProviderConfig' => 'applications/phortune/storage/PhortunePaymentProviderConfig.php', 'PhortunePaymentProviderConfigEditor' => 'applications/phortune/editor/PhortunePaymentProviderConfigEditor.php', @@ -11800,7 +11807,8 @@ 'PhortuneAccountOrdersController' => 'PhortuneAccountProfileController', 'PhortuneAccountOverviewController' => 'PhortuneAccountProfileController', 'PhortuneAccountPHIDType' => 'PhabricatorPHIDType', - 'PhortuneAccountPaymentMethodsController' => 'PhortuneAccountProfileController', + 'PhortuneAccountPaymentMethodListController' => 'PhortuneAccountProfileController', + 'PhortuneAccountPaymentMethodViewController' => 'PhortuneAccountController', 'PhortuneAccountProfileController' => 'PhortuneAccountController', 'PhortuneAccountQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 'PhortuneAccountSubscriptionController' => 'PhortuneAccountProfileController', @@ -11891,13 +11899,20 @@ 'PhabricatorPolicyInterface', 'PhabricatorExtendedPolicyInterface', 'PhabricatorPolicyCodexInterface', + 'PhabricatorApplicationTransactionInterface', ), 'PhortunePaymentMethodCreateController' => 'PhortuneController', 'PhortunePaymentMethodDisableController' => 'PhortuneController', 'PhortunePaymentMethodEditController' => 'PhortuneController', + 'PhortunePaymentMethodEditor' => 'PhabricatorApplicationTransactionEditor', + 'PhortunePaymentMethodNameTransaction' => 'PhortunePaymentMethodTransactionType', 'PhortunePaymentMethodPHIDType' => 'PhabricatorPHIDType', 'PhortunePaymentMethodPolicyCodex' => 'PhabricatorPolicyCodex', 'PhortunePaymentMethodQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', + 'PhortunePaymentMethodStatusTransaction' => 'PhortunePaymentMethodTransactionType', + 'PhortunePaymentMethodTransaction' => 'PhabricatorModularTransaction', + 'PhortunePaymentMethodTransactionQuery' => 'PhabricatorApplicationTransactionQuery', + 'PhortunePaymentMethodTransactionType' => 'PhabricatorModularTransactionType', 'PhortunePaymentProvider' => 'Phobject', 'PhortunePaymentProviderConfig' => array( 'PhortuneDAO', 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 @@ -72,7 +72,10 @@ '(?P\d+)/' => array( 'details/' => 'PhortuneAccountDetailsController', - 'methods/' => 'PhortuneAccountPaymentMethodsController', + 'methods/' => array( + '' => 'PhortuneAccountPaymentMethodListController', + '(?P\d+)/' => 'PhortuneAccountPaymentMethodViewController', + ), 'orders/' => 'PhortuneAccountOrdersController', 'charges/' => 'PhortuneAccountChargesController', 'subscriptions/' => 'PhortuneAccountSubscriptionController', diff --git a/src/applications/phortune/controller/account/PhortuneAccountController.php b/src/applications/phortune/controller/account/PhortuneAccountController.php --- a/src/applications/phortune/controller/account/PhortuneAccountController.php +++ b/src/applications/phortune/controller/account/PhortuneAccountController.php @@ -23,6 +23,10 @@ abstract protected function shouldRequireAccountEditCapability(); abstract protected function handleAccountRequest(AphrontRequest $request); + private function hasAccount() { + return (bool)$this->account; + } + final protected function getAccount() { if ($this->account === null) { throw new Exception( @@ -37,8 +41,10 @@ protected function buildApplicationCrumbs() { $crumbs = parent::buildApplicationCrumbs(); - $account = $this->getAccount(); - if ($account) { + // If we hit a policy exception, we can make it here without finding + // an account. + if ($this->hasAccount()) { + $account = $this->getAccount(); $crumbs->addTextCrumb($account->getName(), $account->getURI()); } diff --git a/src/applications/phortune/controller/account/PhortuneAccountPaymentMethodsController.php b/src/applications/phortune/controller/account/PhortuneAccountPaymentMethodListController.php rename from src/applications/phortune/controller/account/PhortuneAccountPaymentMethodsController.php rename to src/applications/phortune/controller/account/PhortuneAccountPaymentMethodListController.php --- a/src/applications/phortune/controller/account/PhortuneAccountPaymentMethodsController.php +++ b/src/applications/phortune/controller/account/PhortuneAccountPaymentMethodListController.php @@ -1,6 +1,6 @@ getID(); - // TODO: Allow adding a card here directly $add = id(new PHUIButtonView()) ->setTag('a') - ->setText(pht('New Payment Method')) + ->setText(pht('Add Payment Method')) ->setIcon('fa-plus') - ->setHref($this->getApplicationURI("{$id}/card/new/")); + ->setHref($this->getApplicationURI("{$id}/card/new/")) + ->setDisabled(!$can_edit) + ->setWorkflow(!$can_edit); $header = id(new PHUIHeaderView()) - ->setHeader(pht('Payment Methods')); + ->setHeader(pht('Payment Methods')) + ->addActionLink($add); $list = id(new PHUIObjectItemListView()) ->setUser($viewer) @@ -74,39 +76,14 @@ foreach ($methods as $method) { $id = $method->getID(); - $item = new PHUIObjectItemView(); - $item->setHeader($method->getFullDisplayName()); - - switch ($method->getStatus()) { - case PhortunePaymentMethod::STATUS_ACTIVE: - $item->setStatusIcon('fa-check green'); - - $disable_uri = $this->getApplicationURI('card/'.$id.'/disable/'); - $item->addAction( - id(new PHUIListItemView()) - ->setIcon('fa-times') - ->setHref($disable_uri) - ->setDisabled(!$can_edit) - ->setWorkflow(true)); - break; - case PhortunePaymentMethod::STATUS_DISABLED: - $item->setStatusIcon('fa-ban lightbluetext'); - $item->setDisabled(true); - break; - } + $item = id(new PHUIObjectItemView()) + ->setObjectName($method->getObjectName()) + ->setHeader($method->getFullDisplayName()) + ->setHref($method->getURI()); $provider = $method->buildPaymentProvider(); $item->addAttribute($provider->getPaymentMethodProviderDescription()); - $edit_uri = $this->getApplicationURI('card/'.$id.'/edit/'); - - $item->addAction( - id(new PHUIListItemView()) - ->setIcon('fa-pencil') - ->setHref($edit_uri) - ->setDisabled(!$can_edit) - ->setWorkflow(!$can_edit)); - $list->addItem($item); } diff --git a/src/applications/phortune/controller/account/PhortuneAccountPaymentMethodViewController.php b/src/applications/phortune/controller/account/PhortuneAccountPaymentMethodViewController.php new file mode 100644 --- /dev/null +++ b/src/applications/phortune/controller/account/PhortuneAccountPaymentMethodViewController.php @@ -0,0 +1,154 @@ +getViewer(); + $account = $this->getAccount(); + + $method = id(new PhortunePaymentMethodQuery()) + ->setViewer($viewer) + ->withAccountPHIDs(array($account->getPHID())) + ->withIDs(array($request->getURIData('id'))) + ->withStatuses( + array( + PhortunePaymentMethod::STATUS_ACTIVE, + )) + ->executeOne(); + if (!$method) { + return new Aphront404Response(); + } + + $crumbs = $this->buildApplicationCrumbs() + ->addTextCrumb(pht('Payment Methods'), $account->getPaymentMethodsURI()) + ->addTextCrumb($method->getObjectName()) + ->setBorder(true); + + $header = id(new PHUIHeaderView()) + ->setHeader($method->getFullDisplayName()); + + $details = $this->newDetailsView($method); + + $timeline = $this->buildTransactionTimeline( + $method, + new PhortunePaymentMethodTransactionQuery()); + $timeline->setShouldTerminate(true); + + $autopay = $this->newAutopayView($method); + + $curtain = $this->buildCurtainView($method); + + $view = id(new PHUITwoColumnView()) + ->setHeader($header) + ->setCurtain($curtain) + ->setMainColumn( + array( + $details, + $autopay, + $timeline, + )); + + return $this->newPage() + ->setTitle($method->getObjectName()) + ->setCrumbs($crumbs) + ->appendChild($view); + } + + private function buildCurtainView(PhortunePaymentMethod $method) { + $viewer = $this->getViewer(); + + $can_edit = PhabricatorPolicyFilter::hasCapability( + $viewer, + $method, + PhabricatorPolicyCapability::CAN_EDIT); + + $edit_uri = $this->getApplicationURI( + urisprintf( + 'card/%d/edit/', + $method->getID())); + + $remove_uri = $this->getApplicationURI( + urisprintf( + 'card/%d/disable/', + $method->getID())); + + $curtain = $this->newCurtainView($method); + + $curtain->addAction( + id(new PhabricatorActionView()) + ->setName(pht('Edit Payment Method')) + ->setIcon('fa-pencil') + ->setHref($edit_uri) + ->setDisabled(!$can_edit) + ->setWorkflow(!$can_edit)); + + $curtain->addAction( + id(new PhabricatorActionView()) + ->setName(pht('Remove Payment Method')) + ->setIcon('fa-times') + ->setHref($remove_uri) + ->setDisabled(!$can_edit) + ->setWorkflow(true)); + + return $curtain; + } + + private function newDetailsView(PhortunePaymentMethod $method) { + $viewer = $this->getViewer(); + + $merchant_phid = $method->getMerchantPHID(); + $handles = $viewer->loadHandles( + array( + $merchant_phid, + )); + + $view = id(new PHUIPropertyListView()) + ->setUser($viewer); + + if (strlen($method->getName())) { + $view->addProperty(pht('Name'), $method->getDisplayName()); + } + + $view->addProperty(pht('Summary'), $method->getSummary()); + $view->addProperty(pht('Expires'), $method->getDisplayExpires()); + + $view->addProperty( + pht('Merchant'), + $handles[$merchant_phid]->renderLink()); + + return id(new PHUIObjectBoxView()) + ->setHeaderText(pht('Payment Method Details')) + ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) + ->addPropertyList($view); + } + + private function newAutopayView(PhortunePaymentMethod $method) { + $viewer = $this->getViewer(); + + $subscriptions = id(new PhortuneSubscriptionQuery()) + ->setViewer($viewer) + ->withPaymentMethodPHIDs(array($method->getPHID())) + ->execute(); + + $table = id(new PhortuneSubscriptionTableView()) + ->setViewer($viewer) + ->setSubscriptions($subscriptions) + ->newTableView(); + + $table->setNoDataString( + pht( + 'This payment method is not the default payment method for '. + 'any subscriptions.')); + + return id(new PHUIObjectBoxView()) + ->setHeaderText(pht('Autopay Subscriptions')) + ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) + ->setTable($table); + } + +} diff --git a/src/applications/phortune/controller/account/PhortuneAccountSubscriptionController.php b/src/applications/phortune/controller/account/PhortuneAccountSubscriptionController.php --- a/src/applications/phortune/controller/account/PhortuneAccountSubscriptionController.php +++ b/src/applications/phortune/controller/account/PhortuneAccountSubscriptionController.php @@ -47,11 +47,8 @@ ->setLimit(25) ->execute(); - $handles = $this->loadViewerHandles(mpull($subscriptions, 'getPHID')); - $table = id(new PhortuneSubscriptionTableView()) ->setUser($viewer) - ->setHandles($handles) ->setSubscriptions($subscriptions); $header = id(new PHUIHeaderView()) diff --git a/src/applications/phortune/controller/payment/PhortunePaymentMethodCreateController.php b/src/applications/phortune/controller/payment/PhortunePaymentMethodCreateController.php deleted file mode 100644 --- a/src/applications/phortune/controller/payment/PhortunePaymentMethodCreateController.php +++ /dev/null @@ -1,303 +0,0 @@ -getViewer(); - $account_id = $request->getURIData('accountID'); - - $account = id(new PhortuneAccountQuery()) - ->setViewer($viewer) - ->withIDs(array($account_id)) - ->executeOne(); - if (!$account) { - return new Aphront404Response(); - } - $account_id = $account->getID(); - - $merchant = id(new PhortuneMerchantQuery()) - ->setViewer($viewer) - ->withIDs(array($request->getInt('merchantID'))) - ->executeOne(); - if (!$merchant) { - return new Aphront404Response(); - } - - $cart_id = $request->getInt('cartID'); - $subscription_id = $request->getInt('subscriptionID'); - if ($cart_id) { - $cancel_uri = $this->getApplicationURI("cart/{$cart_id}/checkout/"); - } else if ($subscription_id) { - $cancel_uri = $this->getApplicationURI( - "{$account_id}/subscription/edit/{$subscription_id}/"); - } else { - $cancel_uri = $this->getApplicationURI($account->getID().'/'); - } - - $providers = $this->loadCreatePaymentMethodProvidersForMerchant($merchant); - if (!$providers) { - throw new Exception( - pht( - 'There are no payment providers enabled that can add payment '. - 'methods.')); - } - - if (count($providers) == 1) { - // If there's only one provider, always choose it. - $provider_id = head_key($providers); - } else { - $provider_id = $request->getInt('providerID'); - if (empty($providers[$provider_id])) { - $choices = array(); - foreach ($providers as $provider) { - $choices[] = $this->renderSelectProvider($provider); - } - - $content = phutil_tag( - 'div', - array( - 'class' => 'phortune-payment-method-list', - ), - $choices); - - return $this->newDialog() - ->setRenderDialogAsDiv(true) - ->setTitle(pht('Add Payment Method')) - ->appendParagraph(pht('Choose a payment method to add:')) - ->appendChild($content) - ->addCancelButton($cancel_uri); - } - } - - $provider = $providers[$provider_id]; - - $errors = array(); - $display_exception = null; - if ($request->isFormPost() && $request->getBool('isProviderForm')) { - $method = id(new PhortunePaymentMethod()) - ->setAccountPHID($account->getPHID()) - ->setAuthorPHID($viewer->getPHID()) - ->setMerchantPHID($merchant->getPHID()) - ->setProviderPHID($provider->getProviderConfig()->getPHID()) - ->setStatus(PhortunePaymentMethod::STATUS_ACTIVE); - - // Limit the rate at which you can attempt to add payment methods. This - // is intended as a line of defense against using Phortune to validate a - // large list of stolen credit card numbers. - - PhabricatorSystemActionEngine::willTakeAction( - array($viewer->getPHID()), - new PhortuneAddPaymentMethodAction(), - 1); - - if (!$errors) { - $errors = $this->processClientErrors( - $provider, - $request->getStr('errors')); - } - - if (!$errors) { - $client_token_raw = $request->getStr('token'); - $client_token = null; - try { - $client_token = phutil_json_decode($client_token_raw); - } catch (PhutilJSONParserException $ex) { - $errors[] = pht( - 'There was an error decoding token information submitted by the '. - 'client. Expected a JSON-encoded token dictionary, received: %s.', - nonempty($client_token_raw, pht('nothing'))); - } - - if (!$provider->validateCreatePaymentMethodToken($client_token)) { - $errors[] = pht( - 'There was an error with the payment token submitted by the '. - 'client. Expected a valid dictionary, received: %s.', - $client_token_raw); - } - - if (!$errors) { - try { - $provider->createPaymentMethodFromRequest( - $request, - $method, - $client_token); - } catch (PhortuneDisplayException $exception) { - $display_exception = $exception; - } catch (Exception $ex) { - $errors = array( - pht('There was an error adding this payment method:'), - $ex->getMessage(), - ); - } - } - } - - if (!$errors && !$display_exception) { - $method->save(); - - // If we added this method on a cart flow, return to the cart to - // check out. - if ($cart_id) { - $next_uri = $this->getApplicationURI( - "cart/{$cart_id}/checkout/?paymentMethodID=".$method->getID()); - } else if ($subscription_id) { - $next_uri = new PhutilURI($cancel_uri); - $next_uri->replaceQueryParam('added', true); - } else { - $account_uri = $this->getApplicationURI($account->getID().'/'); - $next_uri = new PhutilURI($account_uri); - $next_uri->setFragment('payment'); - } - - return id(new AphrontRedirectResponse())->setURI($next_uri); - } else { - if ($display_exception) { - $dialog_body = $display_exception->getView(); - } else { - $dialog_body = id(new PHUIInfoView()) - ->setErrors($errors); - } - - return $this->newDialog() - ->setTitle(pht('Error Adding Payment Method')) - ->appendChild($dialog_body) - ->addCancelButton($request->getRequestURI()); - } - } - - $form = $provider->renderCreatePaymentMethodForm($request, $errors); - - $form - ->setUser($viewer) - ->setAction($request->getRequestURI()) - ->setWorkflow(true) - ->addHiddenInput('providerID', $provider_id) - ->addHiddenInput('cartID', $request->getInt('cartID')) - ->addHiddenInput('subscriptionID', $request->getInt('subscriptionID')) - ->addHiddenInput('isProviderForm', true) - ->appendChild( - id(new AphrontFormSubmitControl()) - ->setValue(pht('Add Payment Method')) - ->addCancelButton($cancel_uri)); - - $box = id(new PHUIObjectBoxView()) - ->setHeaderText(pht('Method')) - ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) - ->setForm($form); - - $crumbs = $this->buildApplicationCrumbs(); - $crumbs->addTextCrumb(pht('Add Payment Method')); - $crumbs->setBorder(true); - - $header = id(new PHUIHeaderView()) - ->setHeader(pht('Add Payment Method')) - ->setHeaderIcon('fa-plus-square'); - - $view = id(new PHUITwoColumnView()) - ->setHeader($header) - ->setFooter(array( - $box, - )); - - return $this->newPage() - ->setTitle($provider->getPaymentMethodDescription()) - ->setCrumbs($crumbs) - ->appendChild($view); - - } - - private function renderSelectProvider( - PhortunePaymentProvider $provider) { - - $request = $this->getRequest(); - $viewer = $request->getUser(); - - $description = $provider->getPaymentMethodDescription(); - $icon_uri = $provider->getPaymentMethodIcon(); - $details = $provider->getPaymentMethodProviderDescription(); - - $this->requireResource('phortune-css'); - - $icon = id(new PHUIIconView()) - ->setSpriteSheet(PHUIIconView::SPRITE_LOGIN) - ->setSpriteIcon($provider->getPaymentMethodIcon()); - - $button = id(new PHUIButtonView()) - ->setSize(PHUIButtonView::BIG) - ->setColor(PHUIButtonView::GREY) - ->setIcon($icon) - ->setText($description) - ->setSubtext($details) - ->setMetadata(array('disableWorkflow' => true)); - - $form = id(new AphrontFormView()) - ->setUser($viewer) - ->setAction($request->getRequestURI()) - ->addHiddenInput('providerID', $provider->getProviderConfig()->getID()) - ->appendChild($button); - - return $form; - } - - private function processClientErrors( - PhortunePaymentProvider $provider, - $client_errors_raw) { - - $errors = array(); - - $client_errors = null; - try { - $client_errors = phutil_json_decode($client_errors_raw); - } catch (PhutilJSONParserException $ex) { - $errors[] = pht( - 'There was an error decoding error information submitted by the '. - 'client. Expected a JSON-encoded list of error codes, received: %s.', - nonempty($client_errors_raw, pht('nothing'))); - } - - foreach (array_unique($client_errors) as $key => $client_error) { - $client_errors[$key] = $provider->translateCreatePaymentMethodErrorCode( - $client_error); - } - - foreach (array_unique($client_errors) as $client_error) { - switch ($client_error) { - case PhortuneErrCode::ERR_CC_INVALID_NUMBER: - $message = pht( - 'The card number you entered is not a valid card number. Check '. - 'that you entered it correctly.'); - break; - case PhortuneErrCode::ERR_CC_INVALID_CVC: - $message = pht( - 'The CVC code you entered is not a valid CVC code. Check that '. - 'you entered it correctly. The CVC code is a 3-digit or 4-digit '. - 'numeric code which usually appears on the back of the card.'); - break; - case PhortuneErrCode::ERR_CC_INVALID_EXPIRY: - $message = pht( - 'The card expiration date is not a valid expiration date. Check '. - 'that you entered it correctly. You can not add an expired card '. - 'as a payment method.'); - break; - default: - $message = $provider->getCreatePaymentMethodErrorMessage( - $client_error); - if (!$message) { - $message = pht( - "There was an unexpected error ('%s') processing payment ". - "information.", - $client_error); - - phlog($message); - } - break; - } - - $errors[$client_error] = $message; - } - - return $errors; - } - -} diff --git a/src/applications/phortune/controller/paymentmethod/PhortunePaymentMethodCreateController.php b/src/applications/phortune/controller/paymentmethod/PhortunePaymentMethodCreateController.php new file mode 100644 --- /dev/null +++ b/src/applications/phortune/controller/paymentmethod/PhortunePaymentMethodCreateController.php @@ -0,0 +1,462 @@ +getViewer(); + + $account_id = $request->getURIData('accountID'); + $account = id(new PhortuneAccountQuery()) + ->setViewer($viewer) + ->withIDs(array($account_id)) + ->requireCapabilities( + array( + PhabricatorPolicyCapability::CAN_VIEW, + PhabricatorPolicyCapability::CAN_EDIT, + )) + ->executeOne(); + if (!$account) { + return new Aphront404Response(); + } + + $cart_id = $request->getInt('cartID'); + $subscription_id = $request->getInt('subscriptionID'); + $merchant_id = $request->getInt('merchantID'); + + if ($cart_id) { + $cart = id(new PhortuneCartQuery()) + ->setViewer($viewer) + ->withAccountPHIDs(array($account->getPHID())) + ->withIDs(array($cart_id)) + ->executeOne(); + if (!$cart) { + return new Aphront404Response(); + } + + $subscription_phid = $cart->getSubscriptionPHID(); + if ($subscription_phid) { + $subscription = id(new PhortuneSubscriptionQuery()) + ->setViewer($viewer) + ->withAccountPHIDs(array($account->getPHID())) + ->withPHIDs(array($subscription_phid)) + ->executeOne(); + if (!$subscription) { + return new Aphront404Response(); + } + } else { + $subscription = null; + } + + $merchant = $cart->getMerchant(); + + $cart_id = $cart->getID(); + $subscription_id = null; + $merchant_id = null; + + $next_uri = $cart->getCheckoutURI(); + } else if ($subscription_id) { + $subscription = id(new PhortuneSubscriptionQuery()) + ->setViewer($viewer) + ->withAccountPHIDs(array($account->getPHID())) + ->withIDs(array($subscription_id)) + ->executeOne(); + if (!$subscription) { + return new Aphront404Response(); + } + + $cart = null; + $merchant = $subscription->getMerchant(); + + $cart_id = null; + $subscription_id = $subscription->getID(); + $merchant_id = null; + + $next_uri = $subscription->getURI(); + } else if ($merchant_id) { + $merchant_phids = $account->getMerchantPHIDs(); + if ($merchant_phids) { + $merchant = id(new PhortuneMerchantQuery()) + ->setViewer($viewer) + ->withIDs(array($merchant_id)) + ->withPHIDs($merchant_phids) + ->executeOne(); + } else { + $merchant = null; + } + + if (!$merchant) { + return new Aphront404Response(); + } + + $cart = null; + $subscription = null; + + $cart_id = null; + $subscription_id = null; + $merchant_id = $merchant->getID(); + + $next_uri = $account->getPaymentMethodsURI(); + } else { + $next_uri = $account->getPaymentMethodsURI(); + + $merchant_phids = $account->getMerchantPHIDs(); + if ($merchant_phids) { + $merchants = id(new PhortuneMerchantQuery()) + ->setViewer($viewer) + ->withPHIDs($merchant_phids) + ->needProfileImage(true) + ->execute(); + } else { + $merchants = array(); + } + + if (!$merchants) { + return $this->newDialog() + ->setTitle(pht('No Merchants')) + ->appendParagraph( + pht( + 'You have not established a relationship with any merchants '. + 'yet. Create an order or subscription before adding payment '. + 'methods.')) + ->addCancelButton($next_uri); + } + + // If there's more than one merchant, ask the user to pick which one they + // want to pay. If there's only one, just pick it for them. + if (count($merchants) > 1) { + $menu = $this->newMerchantMenu($merchants); + + $form = id(new AphrontFormView()) + ->appendInstructions( + pht( + 'Choose the merchant you want to pay.')); + + return $this->newDialog() + ->setTitle(pht('Choose a Merchant')) + ->appendForm($form) + ->appendChild($menu) + ->addCancelButton($next_uri); + } + + $cart = null; + $subscription = null; + $merchant = head($merchants); + + $cart_id = null; + $subscription_id = null; + $merchant_id = $merchant->getID(); + } + + $providers = $this->loadCreatePaymentMethodProvidersForMerchant($merchant); + if (!$providers) { + throw new Exception( + pht( + 'There are no payment providers enabled that can add payment '. + 'methods.')); + } + + $state_params = array( + 'cartID' => $cart_id, + 'subscriptionID' => $subscription_id, + 'merchantID' => $merchant_id, + ); + $state_params = array_filter($state_params); + + $state_uri = new PhutilURI($request->getRequestURI()); + foreach ($state_params as $key => $value) { + $state_uri->replaceQueryParam($key, $value); + } + + $provider_id = $request->getInt('providerID'); + if (isset($providers[$provider_id])) { + $provider = $providers[$provider_id]; + } else { + // If there's more than one provider, ask the user to pick how they + // want to pay. If there's only one, just pick it. + if (count($providers) > 1) { + $menu = $this->newProviderMenu($providers, $state_uri); + + return $this->newDialog() + ->setTitle(pht('Choose a Payment Method')) + ->appendChild($menu) + ->addCancelButton($next_uri); + } + + $provider = head($providers); + } + + $provider_id = $provider->getProviderConfig()->getID(); + + $state_params['providerID'] = $provider_id; + + $errors = array(); + $display_exception = null; + if ($request->isFormPost() && $request->getBool('isProviderForm')) { + $method = id(new PhortunePaymentMethod()) + ->setAccountPHID($account->getPHID()) + ->setAuthorPHID($viewer->getPHID()) + ->setMerchantPHID($merchant->getPHID()) + ->setProviderPHID($provider->getProviderConfig()->getPHID()) + ->setStatus(PhortunePaymentMethod::STATUS_ACTIVE); + + // Limit the rate at which you can attempt to add payment methods. This + // is intended as a line of defense against using Phortune to validate a + // large list of stolen credit card numbers. + + PhabricatorSystemActionEngine::willTakeAction( + array($viewer->getPHID()), + new PhortuneAddPaymentMethodAction(), + 1); + + if (!$errors) { + $errors = $this->processClientErrors( + $provider, + $request->getStr('errors')); + } + + if (!$errors) { + $client_token_raw = $request->getStr('token'); + $client_token = null; + try { + $client_token = phutil_json_decode($client_token_raw); + } catch (PhutilJSONParserException $ex) { + $errors[] = pht( + 'There was an error decoding token information submitted by the '. + 'client. Expected a JSON-encoded token dictionary, received: %s.', + nonempty($client_token_raw, pht('nothing'))); + } + + if (!$provider->validateCreatePaymentMethodToken($client_token)) { + $errors[] = pht( + 'There was an error with the payment token submitted by the '. + 'client. Expected a valid dictionary, received: %s.', + $client_token_raw); + } + + if (!$errors) { + try { + $provider->createPaymentMethodFromRequest( + $request, + $method, + $client_token); + } catch (PhortuneDisplayException $exception) { + $display_exception = $exception; + } catch (Exception $ex) { + $errors = array( + pht('There was an error adding this payment method:'), + $ex->getMessage(), + ); + } + } + } + + if (!$errors && !$display_exception) { + $xactions = array(); + + $xactions[] = $method->getApplicationTransactionTemplate() + ->setTransactionType(PhabricatorTransactions::TYPE_CREATE) + ->setNewValue(true); + + $editor = id(new PhortunePaymentMethodEditor()) + ->setActor($viewer) + ->setContentSourceFromRequest($request) + ->setContinueOnNoEffect(true) + ->setContinueOnMissingFields(true); + + $editor->applyTransactions($method, $xactions); + + $next_uri = new PhutilURI($next_uri); + + // If we added this method on a cart flow, return to the cart to + // checkout with this payment method selected. + if ($cart_id) { + $next_uri->replaceQueryParam('paymentMethodID', $method->getID()); + } + + return id(new AphrontRedirectResponse())->setURI($next_uri); + } else { + if ($display_exception) { + $dialog_body = $display_exception->getView(); + } else { + $dialog_body = id(new PHUIInfoView()) + ->setErrors($errors); + } + + return $this->newDialog() + ->setTitle(pht('Error Adding Payment Method')) + ->appendChild($dialog_body) + ->addCancelButton($request->getRequestURI()); + } + } + + $form = $provider->renderCreatePaymentMethodForm($request, $errors); + + $form + ->setViewer($viewer) + ->setAction($request->getPath()) + ->setWorkflow(true) + ->addHiddenInput('isProviderForm', true) + ->appendChild( + id(new AphrontFormSubmitControl()) + ->setValue(pht('Add Payment Method')) + ->addCancelButton($next_uri)); + + foreach ($state_params as $key => $value) { + $form->addHiddenInput($key, $value); + } + + $box = id(new PHUIObjectBoxView()) + ->setHeaderText(pht('Method')) + ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) + ->setForm($form); + + $crumbs = $this->buildApplicationCrumbs() + ->addTextCrumb(pht('Add Payment Method')) + ->setBorder(true); + + $header = id(new PHUIHeaderView()) + ->setHeader(pht('Add Payment Method')) + ->setHeaderIcon('fa-plus-square'); + + $view = id(new PHUITwoColumnView()) + ->setHeader($header) + ->setFooter( + array( + $box, + )); + + return $this->newPage() + ->setTitle($provider->getPaymentMethodDescription()) + ->setCrumbs($crumbs) + ->appendChild($view); + + } + + private function processClientErrors( + PhortunePaymentProvider $provider, + $client_errors_raw) { + + $errors = array(); + + $client_errors = null; + try { + $client_errors = phutil_json_decode($client_errors_raw); + } catch (PhutilJSONParserException $ex) { + $errors[] = pht( + 'There was an error decoding error information submitted by the '. + 'client. Expected a JSON-encoded list of error codes, received: %s.', + nonempty($client_errors_raw, pht('nothing'))); + } + + foreach (array_unique($client_errors) as $key => $client_error) { + $client_errors[$key] = $provider->translateCreatePaymentMethodErrorCode( + $client_error); + } + + foreach (array_unique($client_errors) as $client_error) { + switch ($client_error) { + case PhortuneErrCode::ERR_CC_INVALID_NUMBER: + $message = pht( + 'The card number you entered is not a valid card number. Check '. + 'that you entered it correctly.'); + break; + case PhortuneErrCode::ERR_CC_INVALID_CVC: + $message = pht( + 'The CVC code you entered is not a valid CVC code. Check that '. + 'you entered it correctly. The CVC code is a 3-digit or 4-digit '. + 'numeric code which usually appears on the back of the card.'); + break; + case PhortuneErrCode::ERR_CC_INVALID_EXPIRY: + $message = pht( + 'The card expiration date is not a valid expiration date. Check '. + 'that you entered it correctly. You can not add an expired card '. + 'as a payment method.'); + break; + default: + $message = $provider->getCreatePaymentMethodErrorMessage( + $client_error); + if (!$message) { + $message = pht( + "There was an unexpected error ('%s') processing payment ". + "information.", + $client_error); + + phlog($message); + } + break; + } + + $errors[$client_error] = $message; + } + + return $errors; + } + + private function newMerchantMenu(array $merchants) { + assert_instances_of($merchants, 'PhortuneMerchant'); + + $request = $this->getRequest(); + $viewer = $this->getViewer(); + + $menu = id(new PHUIObjectItemListView()) + ->setUser($viewer) + ->setBig(true) + ->setFlush(true); + + foreach ($merchants as $merchant) { + $merchant_uri = id(new PhutilURI($request->getRequestURI())) + ->replaceQueryParam('merchantID', $merchant->getID()); + + $item = id(new PHUIObjectItemView()) + ->setObjectName($merchant->getObjectName()) + ->setHeader($merchant->getName()) + ->setHref($merchant_uri) + ->setClickable(true) + ->setImageURI($merchant->getProfileImageURI()); + + $menu->addItem($item); + } + + return $menu; + } + + private function newProviderMenu(array $providers, PhutilURI $state_uri) { + assert_instances_of($providers, 'PhortunePaymentProvider'); + + $request = $this->getRequest(); + $viewer = $this->getViewer(); + + $menu = id(new PHUIObjectItemListView()) + ->setUser($viewer) + ->setBig(true) + ->setFlush(true); + + foreach ($providers as $provider) { + $provider_id = $provider->getProviderConfig()->getID(); + + $provider_uri = id(clone $state_uri) + ->replaceQueryParam('providerID', $provider_id); + + $description = $provider->getPaymentMethodDescription(); + $icon_uri = $provider->getPaymentMethodIcon(); + $details = $provider->getPaymentMethodProviderDescription(); + + $icon = id(new PHUIIconView()) + ->setSpriteSheet(PHUIIconView::SPRITE_LOGIN) + ->setSpriteIcon($icon_uri); + + $item = id(new PHUIObjectItemView()) + ->setHeader($description) + ->setHref($provider_uri) + ->setClickable(true) + ->addAttribute($details) + ->setImageIcon($icon); + + $menu->addItem($item); + } + + return $menu; + } + +} diff --git a/src/applications/phortune/controller/payment/PhortunePaymentMethodDisableController.php b/src/applications/phortune/controller/paymentmethod/PhortunePaymentMethodDisableController.php rename from src/applications/phortune/controller/payment/PhortunePaymentMethodDisableController.php rename to src/applications/phortune/controller/paymentmethod/PhortunePaymentMethodDisableController.php --- a/src/applications/phortune/controller/payment/PhortunePaymentMethodDisableController.php +++ b/src/applications/phortune/controller/paymentmethod/PhortunePaymentMethodDisableController.php @@ -26,14 +26,23 @@ $account = $method->getAccount(); $account_id = $account->getID(); - $account_uri = $this->getApplicationURI("/account/billing/{$account_id}/"); + $account_uri = $account->getPaymentMethodsURI(); if ($request->isFormPost()) { + $xactions = array(); - // TODO: ApplicationTransactions!!!! - $method - ->setStatus(PhortunePaymentMethod::STATUS_DISABLED) - ->save(); + $xactions[] = $method->getApplicationTransactionTemplate() + ->setTransactionType( + PhortunePaymentMethodStatusTransaction::TRANSACTIONTYPE) + ->setNewValue(PhortunePaymentMethod::STATUS_DISABLED); + + $editor = id(new PhortunePaymentMethodEditor()) + ->setActor($viewer) + ->setContentSourceFromRequest($request) + ->setContinueOnNoEffect(true) + ->setContinueOnMissingFields(true); + + $editor->applyTransactions($method, $xactions); return id(new AphrontRedirectResponse())->setURI($account_uri); } diff --git a/src/applications/phortune/controller/payment/PhortunePaymentMethodEditController.php b/src/applications/phortune/controller/paymentmethod/PhortunePaymentMethodEditController.php rename from src/applications/phortune/controller/payment/PhortunePaymentMethodEditController.php rename to src/applications/phortune/controller/paymentmethod/PhortunePaymentMethodEditController.php --- a/src/applications/phortune/controller/payment/PhortunePaymentMethodEditController.php +++ b/src/applications/phortune/controller/paymentmethod/PhortunePaymentMethodEditController.php @@ -20,25 +20,36 @@ return new Aphront404Response(); } + $next_uri = $method->getURI(); + $account = $method->getAccount(); - $account_uri = $this->getApplicationURI($account->getID().'/'); + $v_name = $method->getName(); if ($request->isFormPost()) { + $v_name = $request->getStr('name'); + + $xactions = array(); - $name = $request->getStr('name'); + $xactions[] = $method->getApplicationTransactionTemplate() + ->setTransactionType( + PhortunePaymentMethodNameTransaction::TRANSACTIONTYPE) + ->setNewValue($v_name); - // TODO: Use ApplicationTransactions + $editor = id(new PhortunePaymentMethodEditor()) + ->setActor($viewer) + ->setContentSourceFromRequest($request) + ->setContinueOnNoEffect(true) + ->setContinueOnMissingFields(true); - $method->setName($name); - $method->save(); + $editor->applyTransactions($method, $xactions); - return id(new AphrontRedirectResponse())->setURI($account_uri); + return id(new AphrontRedirectResponse())->setURI($next_uri); } $provider = $method->buildPaymentProvider(); $form = id(new AphrontFormView()) - ->setUser($viewer) + ->setViewer($viewer) ->appendChild( id(new AphrontFormTextControl()) ->setLabel(pht('Name')) @@ -54,7 +65,7 @@ ->setValue($method->getDisplayExpires())) ->appendChild( id(new AphrontFormSubmitControl()) - ->addCancelButton($account_uri) + ->addCancelButton($next_uri) ->setValue(pht('Save Changes'))); $box = id(new PHUIObjectBoxView()) @@ -62,11 +73,12 @@ ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) ->setForm($form); - $crumbs = $this->buildApplicationCrumbs(); - $crumbs->addTextCrumb($account->getName(), $account_uri); - $crumbs->addTextCrumb($method->getDisplayName()); - $crumbs->addTextCrumb(pht('Edit')); - $crumbs->setBorder(true); + $crumbs = $this->buildApplicationCrumbs() + ->addTextCrumb($account->getName(), $account->getURI()) + ->addTextCrumb(pht('Payment Methods'), $account->getPaymentMethodsURI()) + ->addTextCrumb($method->getObjectName(), $method->getURI()) + ->addTextCrumb(pht('Edit')) + ->setBorder(true); $header = id(new PHUIHeaderView()) ->setHeader(pht('Edit Payment Method')) @@ -74,15 +86,15 @@ $view = id(new PHUITwoColumnView()) ->setHeader($header) - ->setFooter(array( - $box, - )); + ->setFooter( + array( + $box, + )); return $this->newPage() ->setTitle(pht('Edit Payment Method')) ->setCrumbs($crumbs) ->appendChild($view); - } } diff --git a/src/applications/phortune/editor/PhortunePaymentMethodEditor.php b/src/applications/phortune/editor/PhortunePaymentMethodEditor.php new file mode 100644 --- /dev/null +++ b/src/applications/phortune/editor/PhortunePaymentMethodEditor.php @@ -0,0 +1,18 @@ +paymentMethodPHIDs = $method_phids; + return $this; + } + public function needTriggers($need_triggers) { $this->needTriggers = $need_triggers; return $this; } + public function newResultObject() { + return new PhortuneSubscription(); + } + protected function loadPage() { - $table = new PhortuneSubscription(); - $conn = $table->establishConnection('r'); - - $rows = queryfx_all( - $conn, - 'SELECT subscription.* FROM %T subscription %Q %Q %Q', - $table->getTableName(), - $this->buildWhereClause($conn), - $this->buildOrderClause($conn), - $this->buildLimitClause($conn)); - - return $table->loadAllFromArray($rows); + return $this->loadStandardPage($this->newResultObject()); } protected function willFilterPage(array $subscriptions) { @@ -67,6 +66,7 @@ $account = idx($accounts, $subscription->getAccountPHID()); if (!$account) { unset($subscriptions[$key]); + $this->didRejectResult($subscription); continue; } $subscription->attachAccount($account); @@ -86,6 +86,7 @@ $merchant = idx($merchants, $subscription->getMerchantPHID()); if (!$merchant) { unset($subscriptions[$key]); + $this->didRejectResult($subscription); continue; } $subscription->attachMerchant($merchant); @@ -112,6 +113,7 @@ $implementation = idx($implementations, $ref); if (!$implementation) { unset($subscriptions[$key]); + $this->didRejectResult($subscription); continue; } $subscription->attachImplementation($implementation); @@ -133,6 +135,7 @@ $trigger = idx($triggers, $subscription->getTriggerPHID()); if (!$trigger) { unset($subscriptions[$key]); + $this->didRejectResult($subscription); continue; } $subscription->attachTrigger($trigger); @@ -142,10 +145,8 @@ return $subscriptions; } - protected function buildWhereClause(AphrontDatabaseConnection $conn) { - $where = array(); - - $where[] = $this->buildPagingClause($conn); + protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { + $where = parent::buildWhereClauseParts($conn); if ($this->ids !== null) { $where[] = qsprintf( @@ -182,7 +183,18 @@ $this->statuses); } - return $this->formatWhereClause($conn, $where); + if ($this->paymentMethodPHIDs !== null) { + $where[] = qsprintf( + $conn, + 'subscription.defaultPaymentMethodPHID IN (%Ls)', + $this->paymentMethodPHIDs); + } + + return $where; + } + + protected function getPrimaryTableAlias() { + return 'subscription'; } public function getQueryApplicationClass() { diff --git a/src/applications/phortune/query/PhortuneSubscriptionSearchEngine.php b/src/applications/phortune/query/PhortuneSubscriptionSearchEngine.php --- a/src/applications/phortune/query/PhortuneSubscriptionSearchEngine.php +++ b/src/applications/phortune/query/PhortuneSubscriptionSearchEngine.php @@ -125,18 +125,6 @@ return parent::buildSavedQueryFromBuiltin($query_key); } - protected function getRequiredHandlePHIDsForResultList( - array $subscriptions, - PhabricatorSavedQuery $query) { - $phids = array(); - foreach ($subscriptions as $subscription) { - $phids[] = $subscription->getPHID(); - $phids[] = $subscription->getMerchantPHID(); - $phids[] = $subscription->getAuthorPHID(); - } - return $phids; - } - protected function renderResultList( array $subscriptions, PhabricatorSavedQuery $query, @@ -147,7 +135,6 @@ $table = id(new PhortuneSubscriptionTableView()) ->setUser($viewer) - ->setHandles($handles) ->setSubscriptions($subscriptions); $merchant = $this->getMerchant(); 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 @@ -117,6 +117,12 @@ $this->getID()); } + public function getPaymentMethodsURI() { + return urisprintf( + '/phortune/account/%d/methods/', + $this->getID()); + } + public function attachMerchantPHIDs(array $merchant_phids) { $this->merchantPHIDs = $merchant_phids; return $this; diff --git a/src/applications/phortune/storage/PhortuneMerchant.php b/src/applications/phortune/storage/PhortuneMerchant.php --- a/src/applications/phortune/storage/PhortuneMerchant.php +++ b/src/applications/phortune/storage/PhortuneMerchant.php @@ -70,6 +70,10 @@ return $this->assertAttached($this->profileImageFile); } + public function getObjectName() { + return pht('Merchant %d', $this->getID()); + } + /* -( PhabricatorApplicationTransactionInterface )------------------------- */ 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 @@ -9,7 +9,8 @@ implements PhabricatorPolicyInterface, PhabricatorExtendedPolicyInterface, - PhabricatorPolicyCodexInterface { + PhabricatorPolicyCodexInterface, + PhabricatorApplicationTransactionInterface { const STATUS_ACTIVE = 'payment:active'; const STATUS_DISABLED = 'payment:disabled'; @@ -140,6 +141,29 @@ return ($this->getStatus() === self::STATUS_ACTIVE); } + public function getURI() { + return urisprintf( + '/phortune/account/%d/methods/%d/', + $this->getAccount()->getID(), + $this->getID()); + } + + public function getObjectName() { + return pht('Payment Method %d', $this->getID()); + } + + +/* -( PhabricatorApplicationTransactionInterface )------------------------- */ + + + public function getApplicationTransactionEditor() { + return new PhortunePaymentMethodEditor(); + } + + public function getApplicationTransactionTemplate() { + return new PhortunePaymentMethodTransaction(); + } + /* -( PhabricatorPolicyInterface )----------------------------------------- */ diff --git a/src/applications/phortune/storage/PhortunePaymentMethodTransaction.php b/src/applications/phortune/storage/PhortunePaymentMethodTransaction.php new file mode 100644 --- /dev/null +++ b/src/applications/phortune/storage/PhortunePaymentMethodTransaction.php @@ -0,0 +1,18 @@ +handles = $handles; - return $this; - } - - public function getHandles() { - return $this->handles; - } - public function setSubscriptions(array $subscriptions) { $this->subscriptions = $subscriptions; return $this; @@ -40,9 +30,15 @@ } public function render() { + return $this->newTableView(); + } + + public function newTableView() { $subscriptions = $this->getSubscriptions(); - $handles = $this->getHandles(); - $viewer = $this->getUser(); + $viewer = $this->getViewer(); + + $phids = mpull($subscriptions, 'getPHID'); + $handles = $viewer->loadHandles($phids); $rows = array(); $rowc = array(); diff --git a/src/applications/phortune/xaction/paymentmethod/PhortunePaymentMethodNameTransaction.php b/src/applications/phortune/xaction/paymentmethod/PhortunePaymentMethodNameTransaction.php new file mode 100644 --- /dev/null +++ b/src/applications/phortune/xaction/paymentmethod/PhortunePaymentMethodNameTransaction.php @@ -0,0 +1,39 @@ +getName(); + } + + public function applyInternalEffects($object, $value) { + $object->setName($value); + } + + public function getTitle() { + $old_value = $this->getOldValue(); + $new_value = $this->getNewValue(); + + if (strlen($old_value) && strlen($new_value)) { + return pht( + '%s renamed this payment method from %s to %s.', + $this->renderAuthor(), + $this->renderOldValue(), + $this->renderNewValue()); + } else if (strlen($new_value)) { + return pht( + '%s set the name of this payment method to %s.', + $this->renderAuthor(), + $this->renderNewValue()); + } else { + return pht( + '%s removed the name of this payment method (was: %s).', + $this->renderAuthor(), + $this->renderOldValue()); + } + } + +} diff --git a/src/applications/phortune/xaction/paymentmethod/PhortunePaymentMethodStatusTransaction.php b/src/applications/phortune/xaction/paymentmethod/PhortunePaymentMethodStatusTransaction.php new file mode 100644 --- /dev/null +++ b/src/applications/phortune/xaction/paymentmethod/PhortunePaymentMethodStatusTransaction.php @@ -0,0 +1,22 @@ +getStatus(); + } + + public function applyInternalEffects($object, $value) { + $object->setStatus($value); + } + + public function getTitle() { + return pht( + '%s changed the status of this payment method.', + $this->renderAuthor()); + } + +} diff --git a/src/applications/phortune/xaction/paymentmethod/PhortunePaymentMethodTransactionType.php b/src/applications/phortune/xaction/paymentmethod/PhortunePaymentMethodTransactionType.php new file mode 100644 --- /dev/null +++ b/src/applications/phortune/xaction/paymentmethod/PhortunePaymentMethodTransactionType.php @@ -0,0 +1,4 @@ +