Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F15415573
D11580.id27869.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
20 KB
Referenced Files
None
Subscribers
None
D11580.id27869.diff
View Options
diff --git a/resources/sql/autopatches/20150130.phortune.1.subphid.sql b/resources/sql/autopatches/20150130.phortune.1.subphid.sql
new file mode 100644
--- /dev/null
+++ b/resources/sql/autopatches/20150130.phortune.1.subphid.sql
@@ -0,0 +1,2 @@
+ALTER TABLE {$NAMESPACE}_phortune.phortune_cart
+ ADD subscriptionPHID VARBINARY(64);
diff --git a/resources/sql/autopatches/20150130.phortune.2.subkey.sql b/resources/sql/autopatches/20150130.phortune.2.subkey.sql
new file mode 100644
--- /dev/null
+++ b/resources/sql/autopatches/20150130.phortune.2.subkey.sql
@@ -0,0 +1,2 @@
+ALTER TABLE {$NAMESPACE}_phortune.phortune_cart
+ ADD KEY `key_subscription` (subscriptionPHID);
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
@@ -2811,9 +2811,11 @@
'PhortuneSchemaSpec' => 'applications/phortune/storage/PhortuneSchemaSpec.php',
'PhortuneStripePaymentProvider' => 'applications/phortune/provider/PhortuneStripePaymentProvider.php',
'PhortuneSubscription' => 'applications/phortune/storage/PhortuneSubscription.php',
+ 'PhortuneSubscriptionCart' => 'applications/phortune/cart/PhortuneSubscriptionCart.php',
'PhortuneSubscriptionImplementation' => 'applications/phortune/subscription/PhortuneSubscriptionImplementation.php',
'PhortuneSubscriptionListController' => 'applications/phortune/controller/PhortuneSubscriptionListController.php',
'PhortuneSubscriptionPHIDType' => 'applications/phortune/phid/PhortuneSubscriptionPHIDType.php',
+ 'PhortuneSubscriptionProduct' => 'applications/phortune/product/PhortuneSubscriptionProduct.php',
'PhortuneSubscriptionQuery' => 'applications/phortune/query/PhortuneSubscriptionQuery.php',
'PhortuneSubscriptionSearchEngine' => 'applications/phortune/query/PhortuneSubscriptionSearchEngine.php',
'PhortuneSubscriptionTableView' => 'applications/phortune/view/PhortuneSubscriptionTableView.php',
@@ -6169,8 +6171,10 @@
'PhortuneDAO',
'PhabricatorPolicyInterface',
),
+ 'PhortuneSubscriptionCart' => 'PhortuneCartImplementation',
'PhortuneSubscriptionListController' => 'PhortuneController',
'PhortuneSubscriptionPHIDType' => 'PhabricatorPHIDType',
+ 'PhortuneSubscriptionProduct' => 'PhortuneProductImplementation',
'PhortuneSubscriptionQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'PhortuneSubscriptionSearchEngine' => 'PhabricatorApplicationSearchEngine',
'PhortuneSubscriptionTableView' => 'AphrontView',
diff --git a/src/applications/phortune/application/PhabricatorPhortuneApplication.php b/src/applications/phortune/application/PhabricatorPhortuneApplication.php
--- a/src/applications/phortune/application/PhabricatorPhortuneApplication.php
+++ b/src/applications/phortune/application/PhabricatorPhortuneApplication.php
@@ -50,6 +50,8 @@
=> 'PhortuneSubscriptionListController',
'view/(?P<id>\d+)/'
=> 'PhortuneSubscriptionViewController',
+ 'order/(?P<subscriptionID>\d+)/'
+ => 'PhortuneCartListController',
),
'charge/(?:query/(?P<queryKey>[^/]+)/)?'
=> 'PhortuneChargeListController',
@@ -90,6 +92,8 @@
=> 'PhortuneSubscriptionListController',
'view/(?P<id>\d+)/'
=> 'PhortuneSubscriptionViewController',
+ 'order/(?P<subscriptionID>\d+)/'
+ => 'PhortuneCartListController',
),
'(?P<id>\d+)/' => 'PhortuneMerchantViewController',
),
diff --git a/src/applications/phortune/cart/PhortuneSubscriptionCart.php b/src/applications/phortune/cart/PhortuneSubscriptionCart.php
new file mode 100644
--- /dev/null
+++ b/src/applications/phortune/cart/PhortuneSubscriptionCart.php
@@ -0,0 +1,89 @@
+<?php
+
+final class PhortuneSubscriptionCart
+ extends PhortuneCartImplementation {
+
+ private $subscriptionPHID;
+ private $subscription;
+
+ public function setSubscriptionPHID($subscription_phid) {
+ $this->subscriptionPHID = $subscription_phid;
+ return $this;
+ }
+
+ public function getSubscriptionPHID() {
+ return $this->subscriptionPHID;
+ }
+
+ public function setSubscription(PhortuneSubscription $subscription) {
+ $this->subscription = $subscription;
+ return $this;
+ }
+
+ public function getSubscription() {
+ return $this->subscription;
+ }
+
+ public function getName(PhortuneCart $cart) {
+ return pht('Subscription');
+ }
+
+ public function willCreateCart(
+ PhabricatorUser $viewer,
+ PhortuneCart $cart) {
+
+ $subscription = $this->getSubscription();
+ if (!$subscription) {
+ throw new Exception(
+ pht('Call setSubscription() before building a cart!'));
+ }
+
+ $cart->setMetadataValue('subscriptionPHID', $subscription->getPHID());
+ }
+
+ public function loadImplementationsForCarts(
+ PhabricatorUser $viewer,
+ array $carts) {
+
+ $phids = array();
+ foreach ($carts as $cart) {
+ $phids[] = $cart->getMetadataValue('subscriptionPHID');
+ }
+
+ $subscriptions = id(new PhortuneSubscriptionQuery())
+ ->setViewer($viewer)
+ ->withPHIDs($phids)
+ ->execute();
+ $subscriptions = mpull($subscriptions, null, 'getPHID');
+
+ $objects = array();
+ foreach ($carts as $key => $cart) {
+ $subscription_phid = $cart->getMetadataValue('subscriptionPHID');
+ $subscription = idx($subscriptions, $subscription_phid);
+ if (!$subscription) {
+ continue;
+ }
+
+ $object = id(new PhortuneSubscriptionCart())
+ ->setSubscriptionPHID($subscription_phid)
+ ->setSubscription($subscription);
+
+ $objects[$key] = $object;
+ }
+
+ return $objects;
+ }
+
+ public function getCancelURI(PhortuneCart $cart) {
+ return $this->getSubscription()->getURI();
+ }
+
+ public function getDoneURI(PhortuneCart $cart) {
+ return $this->getSubscription()->getURI();
+ }
+
+ public function getDoneActionName(PhortuneCart $cart) {
+ return pht('Return to Subscription');
+ }
+
+}
diff --git a/src/applications/phortune/controller/PhortuneCartListController.php b/src/applications/phortune/controller/PhortuneCartListController.php
--- a/src/applications/phortune/controller/PhortuneCartListController.php
+++ b/src/applications/phortune/controller/PhortuneCartListController.php
@@ -3,29 +3,35 @@
final class PhortuneCartListController
extends PhortuneController {
- private $accountID;
- private $merchantID;
- private $queryKey;
-
private $merchant;
private $account;
+ private $subscription;
- public function willProcessRequest(array $data) {
- $this->merchantID = idx($data, 'merchantID');
- $this->accountID = idx($data, 'accountID');
- $this->queryKey = idx($data, 'queryKey');
- }
+ public function handleRequest(AphrontRequest $request) {
+ $viewer = $this->getViewer();
- public function processRequest() {
- $request = $this->getRequest();
- $viewer = $request->getUser();
+ $merchant_id = $request->getURIData('merchantID');
+ $account_id = $request->getURIData('accountID');
+ $subscription_id = $request->getURIData('subscriptionID');
$engine = new PhortuneCartSearchEngine();
- if ($this->merchantID) {
+ if ($subscription_id) {
+ $subscription = id(new PhortuneSubscriptionQuery())
+ ->setViewer($viewer)
+ ->withIDs(array($subscription_id))
+ ->executeOne();
+ if (!$subscription) {
+ return new Aphront404Response();
+ }
+ $this->subscription = $subscription;
+ $engine->setSubscription($subscription);
+ }
+
+ if ($merchant_id) {
$merchant = id(new PhortuneMerchantQuery())
->setViewer($viewer)
- ->withIDs(array($this->merchantID))
+ ->withIDs(array($merchant_id))
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
@@ -37,10 +43,10 @@
}
$this->merchant = $merchant;
$engine->setMerchant($merchant);
- } else if ($this->accountID) {
+ } else if ($account_id) {
$account = id(new PhortuneAccountQuery())
->setViewer($viewer)
- ->withIDs(array($this->accountID))
+ ->withIDs(array($account_id))
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
@@ -57,7 +63,7 @@
}
$controller = id(new PhabricatorApplicationSearchController())
- ->setQueryKey($this->queryKey)
+ ->setQueryKey($request->getURIData('queryKey'))
->setSearchEngine($engine)
->setNavigation($this->buildSideNavView());
@@ -82,26 +88,39 @@
protected function buildApplicationCrumbs() {
$crumbs = parent::buildApplicationCrumbs();
+ $subscription = $this->subscription;
+
$merchant = $this->merchant;
if ($merchant) {
$id = $merchant->getID();
- $crumbs->addTextCrumb(
- $merchant->getName(),
- $this->getApplicationURI("merchant/{$id}/"));
- $crumbs->addTextCrumb(
- pht('Orders'),
- $this->getApplicationURI("merchant/orders/{$id}/"));
+ $this->addMerchantCrumb($crumbs, $merchant);
+ if (!$subscription) {
+ $crumbs->addTextCrumb(
+ pht('Orders'),
+ $this->getApplicationURI("merchant/orders/{$id}/"));
+ }
}
$account = $this->account;
if ($account) {
$id = $account->getID();
+ $this->addAccountCrumb($crumbs, $account);
+ if (!$subscription) {
+ $crumbs->addTextCrumb(
+ pht('Orders'),
+ $this->getApplicationURI("{$id}/order/"));
+ }
+ }
+
+ if ($subscription) {
+ if ($merchant) {
+ $subscription_uri = $subscription->getMerchantURI();
+ } else {
+ $subscription_uri = $subscription->getURI();
+ }
$crumbs->addTextCrumb(
- $account->getName(),
- $this->getApplicationURI("{$id}/"));
- $crumbs->addTextCrumb(
- pht('Orders'),
- $this->getApplicationURI("{$id}/order/"));
+ $subscription->getSubscriptionName(),
+ $subscription_uri);
}
return $crumbs;
diff --git a/src/applications/phortune/controller/PhortuneSubscriptionViewController.php b/src/applications/phortune/controller/PhortuneSubscriptionViewController.php
--- a/src/applications/phortune/controller/PhortuneSubscriptionViewController.php
+++ b/src/applications/phortune/controller/PhortuneSubscriptionViewController.php
@@ -15,6 +15,8 @@
}
$is_merchant = (bool)$request->getURIData('merchantID');
+ $merchant = $subscription->getMerchant();
+ $account = $subscription->getAccount();
$title = pht('Subscription: %s', $subscription->getSubscriptionName());
@@ -27,9 +29,9 @@
$crumbs = $this->buildApplicationCrumbs();
if ($is_merchant) {
- $this->addMerchantCrumb($crumbs, $subscription->getMerchant());
+ $this->addMerchantCrumb($crumbs, $merchant);
} else {
- $this->addAccountCrumb($crumbs, $subscription->getAccount());
+ $this->addAccountCrumb($crumbs, $account);
}
$crumbs->addTextCrumb(pht('Subscription %d', $subscription->getID()));
@@ -46,10 +48,66 @@
->setHeader($header)
->addPropertyList($properties);
+ $carts = id(new PhortuneCartQuery())
+ ->setViewer($viewer)
+ ->withSubscriptionPHIDs(array($subscription->getPHID()))
+ ->needPurchases(true)
+ ->withStatuses(
+ array(
+ PhortuneCart::STATUS_PURCHASING,
+ PhortuneCart::STATUS_CHARGED,
+ PhortuneCart::STATUS_HOLD,
+ PhortuneCart::STATUS_REVIEW,
+ PhortuneCart::STATUS_PURCHASED,
+ ))
+ ->execute();
+
+ $phids = array();
+ foreach ($carts as $cart) {
+ $phids[] = $cart->getPHID();
+ foreach ($cart->getPurchases() as $purchase) {
+ $phids[] = $purchase->getPHID();
+ }
+ }
+ $handles = $this->loadViewerHandles($phids);
+
+ $invoice_table = id(new PhortuneOrderTableView())
+ ->setUser($viewer)
+ ->setCarts($carts)
+ ->setHandles($handles);
+
+ $account_id = $account->getID();
+ $merchant_id = $merchant->getID();
+ $subscription_id = $subscription->getID();
+
+ if ($is_merchant) {
+ $invoices_uri = $this->getApplicationURI(
+ "merchant/{$merchant_id}/subscription/order/{$subscription_id}/");
+ } else {
+ $invoices_uri = $this->getApplicationURI(
+ "{$account_id}/subscription/order/{$subscription_id}/");
+ }
+
+ $invoice_header = id(new PHUIHeaderView())
+ ->setHeader(pht('Recent Invoices'))
+ ->addActionLink(
+ id(new PHUIButtonView())
+ ->setTag('a')
+ ->setIcon(
+ id(new PHUIIconView())
+ ->setIconFont('fa-list'))
+ ->setHref($invoices_uri)
+ ->setText(pht('View All Invoices')));
+
+ $invoice_box = id(new PHUIObjectBoxView())
+ ->setHeader($invoice_header)
+ ->appendChild($invoice_table);
+
return $this->buildApplicationPage(
array(
$crumbs,
$object_box,
+ $invoice_box,
),
array(
'title' => $title,
diff --git a/src/applications/phortune/product/PhortuneSubscriptionProduct.php b/src/applications/phortune/product/PhortuneSubscriptionProduct.php
new file mode 100644
--- /dev/null
+++ b/src/applications/phortune/product/PhortuneSubscriptionProduct.php
@@ -0,0 +1,90 @@
+<?php
+
+final class PhortuneSubscriptionProduct
+ extends PhortuneProductImplementation {
+
+ private $viewer;
+ private $subscriptionPHID;
+ private $subscription;
+
+ public function setSubscriptionPHID($subscription_phid) {
+ $this->subscriptionPHID = $subscription_phid;
+ return $this;
+ }
+
+ public function getSubscriptionPHID() {
+ return $this->subscriptionPHID;
+ }
+
+ public function setSubscription(PhortuneSubscription $subscription) {
+ $this->subscription = $subscription;
+ return $this;
+ }
+
+ public function getSubscription() {
+ return $this->subscription;
+ }
+
+ public function setViewer(PhabricatorUser $viewer) {
+ $this->viewer = $viewer;
+ return $this;
+ }
+
+ public function getViewer() {
+ return $this->viewer;
+ }
+
+ public function getRef() {
+ return $this->getSubscriptionPHID();
+ }
+
+ public function getName(PhortuneProduct $product) {
+ return $this->getSubscription()->getSubscriptionName();
+ }
+
+ public function getPriceAsCurrency(PhortuneProduct $product) {
+ return PhortuneCurrency::newEmptyCurrency();
+ }
+
+ public function didPurchaseProduct(
+ PhortuneProduct $product,
+ PhortunePurchase $purchase) {
+ // TODO: Callback the subscription.
+ return;
+ }
+
+ public function didRefundProduct(
+ PhortuneProduct $product,
+ PhortunePurchase $purchase,
+ PhortuneCurrency $amount) {
+ // TODO: Callback the subscription.
+ return;
+ }
+
+ public function loadImplementationsForRefs(
+ PhabricatorUser $viewer,
+ array $refs) {
+
+ $subscriptions = id(new PhortuneSubscriptionQuery())
+ ->setViewer($viewer)
+ ->withPHIDs($refs)
+ ->execute();
+ $subscriptions = mpull($subscriptions, null, 'getPHID');
+
+ $objects = array();
+ foreach ($refs as $ref) {
+ $subscription = idx($subscriptions, $ref);
+ if (!$subscription) {
+ continue;
+ }
+
+ $objects[] = id(new PhortuneSubscriptionProduct())
+ ->setViewer($viewer)
+ ->setSubscriptionPHID($ref)
+ ->setSubscription($subscription);
+ }
+
+ return $objects;
+ }
+
+}
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
@@ -7,6 +7,7 @@
private $phids;
private $accountPHIDs;
private $merchantPHIDs;
+ private $subscriptionPHIDs;
private $statuses;
private $needPurchases;
@@ -31,6 +32,11 @@
return $this;
}
+ public function withSubscriptionPHIDs(array $subscription_phids) {
+ $this->subscriptionPHIDs = $subscription_phids;
+ return $this;
+ }
+
public function withStatuses(array $statuses) {
$this->statuses = $statuses;
return $this;
@@ -158,6 +164,13 @@
$this->merchantPHIDs);
}
+ if ($this->subscriptionPHIDs !== null) {
+ $where[] = qsprintf(
+ $conn,
+ 'cart.subscriptionPHID IN (%Ls)',
+ $this->subscriptionPHIDs);
+ }
+
if ($this->statuses !== null) {
$where[] = qsprintf(
$conn,
diff --git a/src/applications/phortune/query/PhortuneCartSearchEngine.php b/src/applications/phortune/query/PhortuneCartSearchEngine.php
--- a/src/applications/phortune/query/PhortuneCartSearchEngine.php
+++ b/src/applications/phortune/query/PhortuneCartSearchEngine.php
@@ -5,6 +5,7 @@
private $merchant;
private $account;
+ private $subscription;
public function setAccount(PhortuneAccount $account) {
$this->account = $account;
@@ -24,6 +25,15 @@
return $this->merchant;
}
+ public function setSubscription(PhortuneSubscription $subscription) {
+ $this->subscription = $subscription;
+ return $this;
+ }
+
+ public function getSubscription() {
+ return $this->subscription;
+ }
+
public function getResultTypeDescription() {
return pht('Phortune Orders');
}
@@ -83,6 +93,11 @@
}
}
+ $subscription = $this->getSubscription();
+ if ($subscription) {
+ $query->withSubscriptionPHIDs(array($subscription->getPHID()));
+ }
+
return $query;
}
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
@@ -16,6 +16,7 @@
protected $accountPHID;
protected $authorPHID;
protected $merchantPHID;
+ protected $subscriptionPHID;
protected $cartClass;
protected $status;
protected $metadata = array();
@@ -518,6 +519,7 @@
'status' => 'text32',
'cartClass' => 'text128',
'mailKey' => 'bytes20',
+ 'subscriptionPHID' => 'phid?',
),
self::CONFIG_KEY_SCHEMA => array(
'key_account' => array(
@@ -526,6 +528,9 @@
'key_merchant' => array(
'columns' => array('merchantPHID'),
),
+ 'key_subscription' => array(
+ 'columns' => array('subscriptionPHID'),
+ ),
),
) + parent::getConfiguration();
}
diff --git a/src/applications/phortune/worker/PhortuneSubscriptionWorker.php b/src/applications/phortune/worker/PhortuneSubscriptionWorker.php
--- a/src/applications/phortune/worker/PhortuneSubscriptionWorker.php
+++ b/src/applications/phortune/worker/PhortuneSubscriptionWorker.php
@@ -8,8 +8,55 @@
$range = $this->getBillingPeriodRange($subscription);
list($last_epoch, $next_epoch) = $range;
- // TODO: Actual billing.
- echo "Bill from {$last_epoch} to {$next_epoch}.\n";
+ $account = $subscription->getAccount();
+ $merchant = $subscription->getMerchant();
+
+ $viewer = PhabricatorUser::getOmnipotentUser();
+
+ $product = id(new PhortuneProductQuery())
+ ->setViewer($viewer)
+ ->withClassAndRef('PhortuneSubscriptionProduct', $subscription->getPHID())
+ ->executeOne();
+
+ $cart_implementation = id(new PhortuneSubscriptionCart())
+ ->setSubscription($subscription);
+
+
+ // TODO: This isn't really ideal. It would be better to use an application
+ // actor than the original author of the subscription. In particular, if
+ // someone initiates a subscription, adds some other account managers, and
+ // later leaves the company, they'll continue "acting" here indefinitely.
+ // However, for now, some of the stuff later in the pipeline requires a
+ // valid actor with a real PHID. The subscription should eventually be
+ // able to create these invoices "as" the application it is acting on
+ // behalf of.
+ $actor = id(new PhabricatorPeopleQuery())
+ ->setViewer($viewer)
+ ->withPHIDs(array($subscription->getAuthorPHID()))
+ ->executeOne();
+ if (!$actor) {
+ throw new Exception(pht('Failed to load actor to bill subscription!'));
+ }
+
+ $cart = $account->newCart($actor, $cart_implementation, $merchant);
+
+ $purchase = $cart->newPurchase($actor, $product);
+
+ // TODO: Consider allowing subscriptions to cost an amount other than one
+ // dollar and twenty-three cents.
+ $currency = PhortuneCurrency::newFromUserInput($actor, '1.23 USD');
+
+ $purchase
+ ->setBasePriceAsCurrency($currency)
+ ->setMetadataValue('subscriptionPHID', $subscription->getPHID())
+ ->save();
+
+ $cart->setSubscriptionPHID($subscription->getPHID());
+ $cart->activateCart();
+
+ // TODO: Autocharge this, etc.; this is still mostly faked up.
+ echo 'Okay, made a cart here: ';
+ echo $cart->getCheckoutURI()."\n\n";
}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Fri, Mar 21, 6:44 AM (35 m, 59 s)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
7709490
Default Alt Text
D11580.id27869.diff (20 KB)
Attached To
Mode
D11580: Kind of generate a bill for users
Attached
Detach File
Event Timeline
Log In to Comment