Permalink
Browse files

Phortune v0

Summary:
Ref T2787. This does very little so far, but makes inroads on accounts and billing. This is mostly just modeled on what Stripe looks like. The objects are:

  - **Account**: Has one or more authorized users, who can make manage the account. An example might be "Phacility", and the three of us would be able to manage it. A user may be associated with more than one account (e.g., a corporate account and a personal account) but the UI tries to simplify the common case of a single account.
  - **Payment Method**: Something we can get sweet sweet money from; for now, a credit card registered with Stripe. Payment methods are associated with an account.
  - **Product**: A good (one time charge) or service (recurring charge). This might be "t-shirt" or "enterprise plan" or "hourly support" or whatever else.
  - **Purchase**: Represents a user purchasing a Product for an Account, using a Payment Method. e.g., you bought a shirt, or started a plan, or purchased support.
  - **Charge**: Actual charges against payment methods. A Purchase can create more than one charge if it's a plan, or if the first charge fails and we re-bill.

This doesn't fully account for stuff like coupons/discounts yet but they should fit into the model without any issues.

This only implements `Account`, and that only partially.

Test Plan: {F37531}

Reviewers: chad, btrahan

Reviewed By: btrahan

CC: aran

Maniphest Tasks: T2787

Differential Revision: https://secure.phabricator.com/D5435
  • Loading branch information...
epriestley committed Mar 28, 2013
1 parent 6964989 commit 960ac3b2a6e1033e1ccc6a72ea1b4bca16647abe
Showing with 897 additions and 7 deletions.
  1. +46 −0 resources/sql/patches/20130322.phortune.sql
  2. +40 −0 src/__phutil_library_map__.php
  3. +0 −6 src/aphront/configuration/AphrontDefaultApplicationConfiguration.php
  4. +5 −0 src/applications/phid/PhabricatorPHIDConstants.php
  5. +53 −0 src/applications/phortune/application/PhabricatorApplicationPhortune.php
  6. +131 −0 src/applications/phortune/controller/PhortuneAccountViewController.php
  7. +45 −0 src/applications/phortune/controller/PhortuneController.php
  8. +29 −0 src/applications/phortune/controller/PhortuneLandingController.php
  9. +23 −0 src/applications/phortune/controller/PhortunePaymentMethodListController.php
  10. +21 −0 src/applications/phortune/controller/PhortunePaymentMethodViewController.php
  11. +62 −0 src/applications/phortune/editor/PhortuneAccountEditor.php
  12. +102 −0 src/applications/phortune/query/PhortuneAccountQuery.php
  13. +10 −0 src/applications/phortune/query/PhortuneAccountTransactionQuery.php
  14. +60 −0 src/applications/phortune/storage/PhortuneAccount.php
  15. +74 −0 src/applications/phortune/storage/PhortuneAccountTransaction.php
  16. +38 −0 src/applications/phortune/storage/PhortuneCharge.php
  17. +9 −0 src/applications/phortune/storage/PhortuneDAO.php
  18. +64 −0 src/applications/phortune/storage/PhortunePaymentMethod.php
  19. +34 −0 src/applications/phortune/storage/PhortuneProduct.php
  20. +35 −0 src/applications/phortune/storage/PhortunePurchase.php
  21. +8 −1 src/infrastructure/edges/constants/PhabricatorEdgeConfig.php
  22. +8 −0 src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php
@@ -0,0 +1,46 @@
+CREATE TABLE {$NAMESPACE}_phortune.phortune_account (
+ id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
+ phid VARCHAR(64) NOT NULL COLLATE utf8_bin,
+ name VARCHAR(255) NOT NULL,
+ balanceInCents BIGINT NOT NULL,
+ dateCreated INT UNSIGNED NOT NULL,
+ dateModified INT UNSIGNED NOT NULL,
+ UNIQUE KEY `key_phid` (phid)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+CREATE TABLE {$NAMESPACE}_phortune.phortune_accounttransaction (
+ id INT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
+ phid VARCHAR(64) NOT NULL COLLATE utf8_bin,
+ authorPHID VARCHAR(64) NOT NULL COLLATE utf8_bin,
+ objectPHID VARCHAR(64) NOT NULL COLLATE utf8_bin,
+ viewPolicy VARCHAR(64) NOT NULL COLLATE utf8_bin,
+ editPolicy VARCHAR(64) NOT NULL COLLATE utf8_bin,
+ commentPHID VARCHAR(64) COLLATE utf8_bin,
+ commentVersion INT UNSIGNED NOT NULL,
+ transactionType VARCHAR(32) NOT NULL COLLATE utf8_bin,
+ oldValue LONGTEXT NOT NULL COLLATE utf8_bin,
+ newValue LONGTEXT NOT NULL COLLATE utf8_bin,
+ contentSource LONGTEXT NOT NULL COLLATE utf8_bin,
+ metadata LONGTEXT NOT NULL COLLATE utf8_bin,
+ dateCreated INT UNSIGNED NOT NULL,
+ dateModified INT UNSIGNED NOT NULL,
+ UNIQUE KEY `key_phid` (phid),
+ KEY `key_object` (objectPHID)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+CREATE TABLE {$NAMESPACE}_phortune.edge (
+ src VARCHAR(64) NOT NULL COLLATE utf8_bin,
+ type INT UNSIGNED NOT NULL COLLATE utf8_bin,
+ dst VARCHAR(64) NOT NULL COLLATE utf8_bin,
+ dateCreated INT UNSIGNED NOT NULL,
+ seq INT UNSIGNED NOT NULL,
+ dataID INT UNSIGNED,
+ PRIMARY KEY (src, type, dst),
+ KEY (src, type, dateCreated, seq)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+CREATE TABLE {$NAMESPACE}_phortune.edgedata (
+ id INT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
+ data LONGTEXT NOT NULL COLLATE utf8_bin
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
@@ -702,6 +702,7 @@
'PhabricatorApplicationPhame' => 'applications/phame/application/PhabricatorApplicationPhame.php',
'PhabricatorApplicationPhlux' => 'applications/phlux/application/PhabricatorApplicationPhlux.php',
'PhabricatorApplicationPholio' => 'applications/pholio/application/PhabricatorApplicationPholio.php',
+ 'PhabricatorApplicationPhortune' => 'applications/phortune/application/PhabricatorApplicationPhortune.php',
'PhabricatorApplicationPhriction' => 'applications/phriction/application/PhabricatorApplicationPhriction.php',
'PhabricatorApplicationPonder' => 'applications/ponder/application/PhabricatorApplicationPonder.php',
'PhabricatorApplicationProject' => 'applications/project/application/PhabricatorApplicationProject.php',
@@ -1527,7 +1528,22 @@
'PholioTransactionQuery' => 'applications/pholio/query/PholioTransactionQuery.php',
'PholioTransactionType' => 'applications/pholio/constants/PholioTransactionType.php',
'PholioTransactionView' => 'applications/pholio/view/PholioTransactionView.php',
+ 'PhortuneAccount' => 'applications/phortune/storage/PhortuneAccount.php',
+ 'PhortuneAccountEditor' => 'applications/phortune/editor/PhortuneAccountEditor.php',
+ 'PhortuneAccountQuery' => 'applications/phortune/query/PhortuneAccountQuery.php',
+ 'PhortuneAccountTransaction' => 'applications/phortune/storage/PhortuneAccountTransaction.php',
+ 'PhortuneAccountTransactionQuery' => 'applications/phortune/query/PhortuneAccountTransactionQuery.php',
+ 'PhortuneAccountViewController' => 'applications/phortune/controller/PhortuneAccountViewController.php',
+ 'PhortuneCharge' => 'applications/phortune/storage/PhortuneCharge.php',
+ 'PhortuneController' => 'applications/phortune/controller/PhortuneController.php',
+ 'PhortuneDAO' => 'applications/phortune/storage/PhortuneDAO.php',
+ 'PhortuneLandingController' => 'applications/phortune/controller/PhortuneLandingController.php',
'PhortuneMonthYearExpiryControl' => 'applications/phortune/control/PhortuneMonthYearExpiryControl.php',
+ 'PhortunePaymentMethod' => 'applications/phortune/storage/PhortunePaymentMethod.php',
+ 'PhortunePaymentMethodListController' => 'applications/phortune/controller/PhortunePaymentMethodListController.php',
+ 'PhortunePaymentMethodViewController' => 'applications/phortune/controller/PhortunePaymentMethodViewController.php',
+ 'PhortuneProduct' => 'applications/phortune/storage/PhortuneProduct.php',
+ 'PhortunePurchase' => 'applications/phortune/storage/PhortunePurchase.php',
'PhortuneStripeBaseController' => 'applications/phortune/stripe/controller/PhortuneStripeBaseController.php',
'PhortuneStripePaymentFormView' => 'applications/phortune/stripe/view/PhortuneStripePaymentFormView.php',
'PhortuneStripeTestPaymentFormController' => 'applications/phortune/stripe/controller/PhortuneStripeTestPaymentFormController.php',
@@ -2328,6 +2344,7 @@
'PhabricatorApplicationPhame' => 'PhabricatorApplication',
'PhabricatorApplicationPhlux' => 'PhabricatorApplication',
'PhabricatorApplicationPholio' => 'PhabricatorApplication',
+ 'PhabricatorApplicationPhortune' => 'PhabricatorApplication',
'PhabricatorApplicationPhriction' => 'PhabricatorApplication',
'PhabricatorApplicationPonder' => 'PhabricatorApplication',
'PhabricatorApplicationProject' => 'PhabricatorApplication',
@@ -3157,7 +3174,30 @@
'PholioTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
'PholioTransactionType' => 'PholioConstants',
'PholioTransactionView' => 'PhabricatorApplicationTransactionView',
+ 'PhortuneAccount' =>
+ array(
+ 0 => 'PhortuneDAO',
+ 1 => 'PhabricatorPolicyInterface',
+ ),
+ 'PhortuneAccountEditor' => 'PhabricatorApplicationTransactionEditor',
+ 'PhortuneAccountQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
+ 'PhortuneAccountTransaction' => 'PhabricatorApplicationTransaction',
+ 'PhortuneAccountTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
+ 'PhortuneAccountViewController' => 'PhortuneController',
+ 'PhortuneCharge' => 'PhortuneDAO',
+ 'PhortuneController' => 'PhabricatorController',
+ 'PhortuneDAO' => 'PhabricatorLiskDAO',
+ 'PhortuneLandingController' => 'PhortuneController',
'PhortuneMonthYearExpiryControl' => 'AphrontFormControl',
+ 'PhortunePaymentMethod' =>
+ array(
+ 0 => 'PhortuneDAO',
+ 1 => 'PhabricatorPolicyInterface',
+ ),
+ 'PhortunePaymentMethodListController' => 'PhabricatorController',
+ 'PhortunePaymentMethodViewController' => 'PhabricatorController',
+ 'PhortuneProduct' => 'PhortuneDAO',
+ 'PhortunePurchase' => 'PhortuneDAO',
'PhortuneStripeBaseController' => 'PhabricatorController',
'PhortuneStripePaymentFormView' => 'AphrontView',
'PhortuneStripeTestPaymentFormController' => 'PhortuneStripeBaseController',
@@ -104,12 +104,6 @@ public function getURIMap() {
'clear/' => 'PhabricatorNotificationClearController',
),
- '/phortune/' => array(
- 'stripe/' => array(
- 'testpaymentform/' => 'PhortuneStripeTestPaymentFormController',
- ),
- ),
-
'/debug/' => 'PhabricatorDebugController',
);
}
@@ -33,6 +33,11 @@
const PHID_TYPE_CONF = 'CONF';
const PHID_TYPE_CONP = 'CONP';
const PHID_TYPE_PVAR = 'PVAR';
+ const PHID_TYPE_ACNT = 'ACNT';
+ const PHID_TYPE_PDCT = 'PDCT';
+ const PHID_TYPE_PRCH = 'PRCH';
+ const PHID_TYPE_PAYM = 'PAYM';
+ const PHID_TYPE_CHRG = 'CHRG';
const PHID_TYPE_XACT = 'XACT';
const PHID_TYPE_XCMT = 'XCMT';
@@ -0,0 +1,53 @@
+<?php
+
+final class PhabricatorApplicationPhortune extends PhabricatorApplication {
+
+ public function getBaseURI() {
+ return '/phortune/';
+ }
+
+ public function getShortDescription() {
+ return pht('Account and Billing');
+ }
+
+ public function getIconName() {
+ return 'phortune';
+ }
+
+ public function getTitleGlyph() {
+ return "\xE2\x9C\x98";
+ }
+
+ public function getApplicationGroup() {
+ return self::GROUP_UTILITIES;
+ }
+
+ public function isBeta() {
+ return true;
+ }
+
+ public function getRoutes() {
+ return array(
+ '/phortune/' => array(
+ '' => 'PhortuneLandingController',
+ '(?P<accountID>\d+)/' => array(
+ '' => 'PhortuneAccountViewController',
+ ),
+
+ 'account/' => array(
+ '' => 'PhortuneAccountListController',
+ 'edit/(?:(?P<id>\d+)/)?' => 'PhortuneAccountEditController',
+ ),
+ 'paymentmethod/' => array(
+ '' => 'PhortunePaymentMethodListController',
+ 'view/(?P<id>\d+)/' => 'PhortunePaymentMethodViewController',
+ 'edit/(?:(?P<id>\d+)/)?' => 'PhortunePaymentMethodEditController',
+ ),
+ 'stripe/' => array(
+ 'testpaymentform/' => 'PhortuneStripeTestPaymentFormController',
+ ),
+ ),
+ );
+ }
+
+}
@@ -0,0 +1,131 @@
+<?php
+
+final class PhortuneAccountViewController extends PhortuneController {
+
+ private $accountID;
+
+ public function willProcessRequest(array $data) {
+ $this->accountID = $data['accountID'];
+ }
+
+ public function processRequest() {
+ $request = $this->getRequest();
+ $user = $request->getUser();
+
+ $account = id(new PhortuneAccountQuery())
+ ->setViewer($user)
+ ->withIDs(array($this->accountID))
+ ->executeOne();
+
+ if (!$account) {
+ return new Aphront404Response();
+ }
+
+ $title = $account->getName();
+
+ $crumbs = $this->buildApplicationCrumbs();
+ $crumbs->addCrumb(
+ id(new PhabricatorCrumbView())
+ ->setName(pht('Account'))
+ ->setHref($request->getRequestURI()));
+
+ $header = id(new PhabricatorHeaderView())
+ ->setHeader($title);
+
+ $actions = id(new PhabricatorActionListView())
+ ->setUser($user)
+ ->addAction(
+ id(new PhabricatorActionView())
+ ->setName(pht('Edit Account'))
+ ->setIcon('edit')
+ ->setHref('#')
+ ->setDisabled(true))
+ ->addAction(
+ id(new PhabricatorActionView())
+ ->setName(pht('Edit Members'))
+ ->setIcon('transcript')
+ ->setHref('#')
+ ->setDisabled(true));
+
+ $properties = id(new PhabricatorPropertyListView())
+ ->setObject($account)
+ ->setUser($user);
+
+ $properties->addProperty(pht('Balance'), $account->getBalanceInCents());
+
+ $payment_methods = $this->buildPaymentMethodsSection($account);
+ $account_history = $this->buildAccountHistorySection($account);
+
+ return $this->buildApplicationPage(
+ array(
+ $crumbs,
+ $header,
+ $actions,
+ $properties,
+ $payment_methods,
+ $account_history,
+ ),
+ array(
+ 'title' => $title,
+ 'device' => true,
+ 'dust' => true,
+ ));
+ }
+
+ private function buildPaymentMethodsSection(PhortuneAccount $account) {
+ $request = $this->getRequest();
+ $user = $request->getUser();
+
+ $header = id(new PhabricatorHeaderView())
+ ->setHeader(pht('Payment Methods'));
+
+ $id = $account->getID();
+ $add_uri = $this->getApplicationURI($id.'/paymentmethod/edit/');
+
+ $actions = id(new PhabricatorActionListView())
+ ->setUser($user)
+ ->addAction(
+ id(new PhabricatorActionView())
+ ->setName(pht('Add Payment Method'))
+ ->setIcon('new')
+ ->setHref($add_uri));
+
+ $list = id(new PhabricatorObjectItemListView())
+ ->setUser($user)
+ ->setNoDataString(
+ pht('No payment methods associated with this account.'));
+
+ return array(
+ $header,
+ $actions,
+ $list,
+ );
+ }
+
+ private function buildAccountHistorySection(PhortuneAccount $account) {
+ $request = $this->getRequest();
+ $user = $request->getUser();
+
+ $header = id(new PhabricatorHeaderView())
+ ->setHeader(pht('Account History'));
+
+ $xactions = id(new PhortuneAccountTransactionQuery())
+ ->setViewer($user)
+ ->withObjectPHIDs(array($account->getPHID()))
+ ->execute();
+
+ $engine = id(new PhabricatorMarkupEngine())
+ ->setViewer($user);
+
+ $xaction_view = id(new PhabricatorApplicationTransactionView())
+ ->setUser($user)
+ ->setTransactions($xactions)
+ ->setMarkupEngine($engine);
+
+ return array(
+ $header,
+ $xaction_view,
+ );
+ }
+
+}
@@ -0,0 +1,45 @@
+<?php
+
+abstract class PhortuneController extends PhabricatorController {
+
+ protected function createUserAccount(PhabricatorUser $user) {
+ $request = $this->getRequest();
+
+ $xactions = array();
+ $xactions[] = id(new PhortuneAccountTransaction())
+ ->setTransactionType(PhortuneAccountTransaction::TYPE_NAME)
+ ->setNewValue(pht('Account (%s)', $user->getUserName()));
+
+ $xactions[] = id(new PhortuneAccountTransaction())
+ ->setTransactionType(PhabricatorTransactions::TYPE_EDGE)
+ ->setMetadataValue(
+ 'edge:type',
+ PhabricatorEdgeConfig::TYPE_ACCOUNT_HAS_MEMBER)
+ ->setNewValue(
+ array(
+ '=' => array($user->getPHID() => $user->getPHID()),
+ ));
+
+ $account = new PhortuneAccount();
+
+ $editor = id(new PhortuneAccountEditor())
+ ->setActor($user)
+ ->setContentSource(
+ PhabricatorContentSource::newForSource(
+ PhabricatorContentSource::SOURCE_WEB,
+ array(
+ 'ip' => $request->getRemoteAddr(),
+ )));
+
+ // We create an account for you the first time you visit Phortune.
+ $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
+
+ $editor->applyTransactions($account, $xactions);
+
+ unset($unguarded);
+
+ return $account;
+ }
+
+
+}
Oops, something went wrong.

0 comments on commit 960ac3b

Please sign in to comment.