mirror of
https://we.phorge.it/source/phorge.git
synced 2024-12-01 19:22:42 +01:00
Kind of generate a bill for users
Summary: Ref T6881. This generates a product, purchase and invoice for users, and there's sort of some UI for them. Stuff it doesn't do yet: - Try to autobill when we have a CC; - actually tell the user they should pay it; - ask the application for anything like "how much should we charge", or tell the application anything like "the user paid". However, these work: - You can //technically// pay the invoices. - You can see the invoices you paid in the past. Test Plan: Used `bin/phriction invoice` to double-bill myself over and over again. Paid one of the invoices. Reviewers: btrahan Reviewed By: btrahan Subscribers: epriestley Maniphest Tasks: T6881 Differential Revision: https://secure.phabricator.com/D11580
This commit is contained in:
parent
bdb3adeee4
commit
d1e793a292
12 changed files with 380 additions and 32 deletions
|
@ -0,0 +1,2 @@
|
||||||
|
ALTER TABLE {$NAMESPACE}_phortune.phortune_cart
|
||||||
|
ADD subscriptionPHID VARBINARY(64);
|
2
resources/sql/autopatches/20150130.phortune.2.subkey.sql
Normal file
2
resources/sql/autopatches/20150130.phortune.2.subkey.sql
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
ALTER TABLE {$NAMESPACE}_phortune.phortune_cart
|
||||||
|
ADD KEY `key_subscription` (subscriptionPHID);
|
|
@ -2811,9 +2811,11 @@ phutil_register_library_map(array(
|
||||||
'PhortuneSchemaSpec' => 'applications/phortune/storage/PhortuneSchemaSpec.php',
|
'PhortuneSchemaSpec' => 'applications/phortune/storage/PhortuneSchemaSpec.php',
|
||||||
'PhortuneStripePaymentProvider' => 'applications/phortune/provider/PhortuneStripePaymentProvider.php',
|
'PhortuneStripePaymentProvider' => 'applications/phortune/provider/PhortuneStripePaymentProvider.php',
|
||||||
'PhortuneSubscription' => 'applications/phortune/storage/PhortuneSubscription.php',
|
'PhortuneSubscription' => 'applications/phortune/storage/PhortuneSubscription.php',
|
||||||
|
'PhortuneSubscriptionCart' => 'applications/phortune/cart/PhortuneSubscriptionCart.php',
|
||||||
'PhortuneSubscriptionImplementation' => 'applications/phortune/subscription/PhortuneSubscriptionImplementation.php',
|
'PhortuneSubscriptionImplementation' => 'applications/phortune/subscription/PhortuneSubscriptionImplementation.php',
|
||||||
'PhortuneSubscriptionListController' => 'applications/phortune/controller/PhortuneSubscriptionListController.php',
|
'PhortuneSubscriptionListController' => 'applications/phortune/controller/PhortuneSubscriptionListController.php',
|
||||||
'PhortuneSubscriptionPHIDType' => 'applications/phortune/phid/PhortuneSubscriptionPHIDType.php',
|
'PhortuneSubscriptionPHIDType' => 'applications/phortune/phid/PhortuneSubscriptionPHIDType.php',
|
||||||
|
'PhortuneSubscriptionProduct' => 'applications/phortune/product/PhortuneSubscriptionProduct.php',
|
||||||
'PhortuneSubscriptionQuery' => 'applications/phortune/query/PhortuneSubscriptionQuery.php',
|
'PhortuneSubscriptionQuery' => 'applications/phortune/query/PhortuneSubscriptionQuery.php',
|
||||||
'PhortuneSubscriptionSearchEngine' => 'applications/phortune/query/PhortuneSubscriptionSearchEngine.php',
|
'PhortuneSubscriptionSearchEngine' => 'applications/phortune/query/PhortuneSubscriptionSearchEngine.php',
|
||||||
'PhortuneSubscriptionTableView' => 'applications/phortune/view/PhortuneSubscriptionTableView.php',
|
'PhortuneSubscriptionTableView' => 'applications/phortune/view/PhortuneSubscriptionTableView.php',
|
||||||
|
@ -6169,8 +6171,10 @@ phutil_register_library_map(array(
|
||||||
'PhortuneDAO',
|
'PhortuneDAO',
|
||||||
'PhabricatorPolicyInterface',
|
'PhabricatorPolicyInterface',
|
||||||
),
|
),
|
||||||
|
'PhortuneSubscriptionCart' => 'PhortuneCartImplementation',
|
||||||
'PhortuneSubscriptionListController' => 'PhortuneController',
|
'PhortuneSubscriptionListController' => 'PhortuneController',
|
||||||
'PhortuneSubscriptionPHIDType' => 'PhabricatorPHIDType',
|
'PhortuneSubscriptionPHIDType' => 'PhabricatorPHIDType',
|
||||||
|
'PhortuneSubscriptionProduct' => 'PhortuneProductImplementation',
|
||||||
'PhortuneSubscriptionQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
'PhortuneSubscriptionQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
||||||
'PhortuneSubscriptionSearchEngine' => 'PhabricatorApplicationSearchEngine',
|
'PhortuneSubscriptionSearchEngine' => 'PhabricatorApplicationSearchEngine',
|
||||||
'PhortuneSubscriptionTableView' => 'AphrontView',
|
'PhortuneSubscriptionTableView' => 'AphrontView',
|
||||||
|
|
|
@ -50,6 +50,8 @@ final class PhabricatorPhortuneApplication extends PhabricatorApplication {
|
||||||
=> 'PhortuneSubscriptionListController',
|
=> 'PhortuneSubscriptionListController',
|
||||||
'view/(?P<id>\d+)/'
|
'view/(?P<id>\d+)/'
|
||||||
=> 'PhortuneSubscriptionViewController',
|
=> 'PhortuneSubscriptionViewController',
|
||||||
|
'order/(?P<subscriptionID>\d+)/'
|
||||||
|
=> 'PhortuneCartListController',
|
||||||
),
|
),
|
||||||
'charge/(?:query/(?P<queryKey>[^/]+)/)?'
|
'charge/(?:query/(?P<queryKey>[^/]+)/)?'
|
||||||
=> 'PhortuneChargeListController',
|
=> 'PhortuneChargeListController',
|
||||||
|
@ -90,6 +92,8 @@ final class PhabricatorPhortuneApplication extends PhabricatorApplication {
|
||||||
=> 'PhortuneSubscriptionListController',
|
=> 'PhortuneSubscriptionListController',
|
||||||
'view/(?P<id>\d+)/'
|
'view/(?P<id>\d+)/'
|
||||||
=> 'PhortuneSubscriptionViewController',
|
=> 'PhortuneSubscriptionViewController',
|
||||||
|
'order/(?P<subscriptionID>\d+)/'
|
||||||
|
=> 'PhortuneCartListController',
|
||||||
),
|
),
|
||||||
'(?P<id>\d+)/' => 'PhortuneMerchantViewController',
|
'(?P<id>\d+)/' => 'PhortuneMerchantViewController',
|
||||||
),
|
),
|
||||||
|
|
89
src/applications/phortune/cart/PhortuneSubscriptionCart.php
Normal file
89
src/applications/phortune/cart/PhortuneSubscriptionCart.php
Normal file
|
@ -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');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -3,29 +3,35 @@
|
||||||
final class PhortuneCartListController
|
final class PhortuneCartListController
|
||||||
extends PhortuneController {
|
extends PhortuneController {
|
||||||
|
|
||||||
private $accountID;
|
|
||||||
private $merchantID;
|
|
||||||
private $queryKey;
|
|
||||||
|
|
||||||
private $merchant;
|
private $merchant;
|
||||||
private $account;
|
private $account;
|
||||||
|
private $subscription;
|
||||||
|
|
||||||
public function willProcessRequest(array $data) {
|
public function handleRequest(AphrontRequest $request) {
|
||||||
$this->merchantID = idx($data, 'merchantID');
|
$viewer = $this->getViewer();
|
||||||
$this->accountID = idx($data, 'accountID');
|
|
||||||
$this->queryKey = idx($data, 'queryKey');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function processRequest() {
|
$merchant_id = $request->getURIData('merchantID');
|
||||||
$request = $this->getRequest();
|
$account_id = $request->getURIData('accountID');
|
||||||
$viewer = $request->getUser();
|
$subscription_id = $request->getURIData('subscriptionID');
|
||||||
|
|
||||||
$engine = new PhortuneCartSearchEngine();
|
$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())
|
$merchant = id(new PhortuneMerchantQuery())
|
||||||
->setViewer($viewer)
|
->setViewer($viewer)
|
||||||
->withIDs(array($this->merchantID))
|
->withIDs(array($merchant_id))
|
||||||
->requireCapabilities(
|
->requireCapabilities(
|
||||||
array(
|
array(
|
||||||
PhabricatorPolicyCapability::CAN_VIEW,
|
PhabricatorPolicyCapability::CAN_VIEW,
|
||||||
|
@ -37,10 +43,10 @@ final class PhortuneCartListController
|
||||||
}
|
}
|
||||||
$this->merchant = $merchant;
|
$this->merchant = $merchant;
|
||||||
$engine->setMerchant($merchant);
|
$engine->setMerchant($merchant);
|
||||||
} else if ($this->accountID) {
|
} else if ($account_id) {
|
||||||
$account = id(new PhortuneAccountQuery())
|
$account = id(new PhortuneAccountQuery())
|
||||||
->setViewer($viewer)
|
->setViewer($viewer)
|
||||||
->withIDs(array($this->accountID))
|
->withIDs(array($account_id))
|
||||||
->requireCapabilities(
|
->requireCapabilities(
|
||||||
array(
|
array(
|
||||||
PhabricatorPolicyCapability::CAN_VIEW,
|
PhabricatorPolicyCapability::CAN_VIEW,
|
||||||
|
@ -57,7 +63,7 @@ final class PhortuneCartListController
|
||||||
}
|
}
|
||||||
|
|
||||||
$controller = id(new PhabricatorApplicationSearchController())
|
$controller = id(new PhabricatorApplicationSearchController())
|
||||||
->setQueryKey($this->queryKey)
|
->setQueryKey($request->getURIData('queryKey'))
|
||||||
->setSearchEngine($engine)
|
->setSearchEngine($engine)
|
||||||
->setNavigation($this->buildSideNavView());
|
->setNavigation($this->buildSideNavView());
|
||||||
|
|
||||||
|
@ -82,27 +88,40 @@ final class PhortuneCartListController
|
||||||
protected function buildApplicationCrumbs() {
|
protected function buildApplicationCrumbs() {
|
||||||
$crumbs = parent::buildApplicationCrumbs();
|
$crumbs = parent::buildApplicationCrumbs();
|
||||||
|
|
||||||
|
$subscription = $this->subscription;
|
||||||
|
|
||||||
$merchant = $this->merchant;
|
$merchant = $this->merchant;
|
||||||
if ($merchant) {
|
if ($merchant) {
|
||||||
$id = $merchant->getID();
|
$id = $merchant->getID();
|
||||||
$crumbs->addTextCrumb(
|
$this->addMerchantCrumb($crumbs, $merchant);
|
||||||
$merchant->getName(),
|
if (!$subscription) {
|
||||||
$this->getApplicationURI("merchant/{$id}/"));
|
|
||||||
$crumbs->addTextCrumb(
|
$crumbs->addTextCrumb(
|
||||||
pht('Orders'),
|
pht('Orders'),
|
||||||
$this->getApplicationURI("merchant/orders/{$id}/"));
|
$this->getApplicationURI("merchant/orders/{$id}/"));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$account = $this->account;
|
$account = $this->account;
|
||||||
if ($account) {
|
if ($account) {
|
||||||
$id = $account->getID();
|
$id = $account->getID();
|
||||||
$crumbs->addTextCrumb(
|
$this->addAccountCrumb($crumbs, $account);
|
||||||
$account->getName(),
|
if (!$subscription) {
|
||||||
$this->getApplicationURI("{$id}/"));
|
|
||||||
$crumbs->addTextCrumb(
|
$crumbs->addTextCrumb(
|
||||||
pht('Orders'),
|
pht('Orders'),
|
||||||
$this->getApplicationURI("{$id}/order/"));
|
$this->getApplicationURI("{$id}/order/"));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($subscription) {
|
||||||
|
if ($merchant) {
|
||||||
|
$subscription_uri = $subscription->getMerchantURI();
|
||||||
|
} else {
|
||||||
|
$subscription_uri = $subscription->getURI();
|
||||||
|
}
|
||||||
|
$crumbs->addTextCrumb(
|
||||||
|
$subscription->getSubscriptionName(),
|
||||||
|
$subscription_uri);
|
||||||
|
}
|
||||||
|
|
||||||
return $crumbs;
|
return $crumbs;
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,8 @@ final class PhortuneSubscriptionViewController extends PhortuneController {
|
||||||
}
|
}
|
||||||
|
|
||||||
$is_merchant = (bool)$request->getURIData('merchantID');
|
$is_merchant = (bool)$request->getURIData('merchantID');
|
||||||
|
$merchant = $subscription->getMerchant();
|
||||||
|
$account = $subscription->getAccount();
|
||||||
|
|
||||||
$title = pht('Subscription: %s', $subscription->getSubscriptionName());
|
$title = pht('Subscription: %s', $subscription->getSubscriptionName());
|
||||||
|
|
||||||
|
@ -27,9 +29,9 @@ final class PhortuneSubscriptionViewController extends PhortuneController {
|
||||||
|
|
||||||
$crumbs = $this->buildApplicationCrumbs();
|
$crumbs = $this->buildApplicationCrumbs();
|
||||||
if ($is_merchant) {
|
if ($is_merchant) {
|
||||||
$this->addMerchantCrumb($crumbs, $subscription->getMerchant());
|
$this->addMerchantCrumb($crumbs, $merchant);
|
||||||
} else {
|
} else {
|
||||||
$this->addAccountCrumb($crumbs, $subscription->getAccount());
|
$this->addAccountCrumb($crumbs, $account);
|
||||||
}
|
}
|
||||||
$crumbs->addTextCrumb(pht('Subscription %d', $subscription->getID()));
|
$crumbs->addTextCrumb(pht('Subscription %d', $subscription->getID()));
|
||||||
|
|
||||||
|
@ -46,10 +48,66 @@ final class PhortuneSubscriptionViewController extends PhortuneController {
|
||||||
->setHeader($header)
|
->setHeader($header)
|
||||||
->addPropertyList($properties);
|
->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(
|
return $this->buildApplicationPage(
|
||||||
array(
|
array(
|
||||||
$crumbs,
|
$crumbs,
|
||||||
$object_box,
|
$object_box,
|
||||||
|
$invoice_box,
|
||||||
),
|
),
|
||||||
array(
|
array(
|
||||||
'title' => $title,
|
'title' => $title,
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -7,6 +7,7 @@ final class PhortuneCartQuery
|
||||||
private $phids;
|
private $phids;
|
||||||
private $accountPHIDs;
|
private $accountPHIDs;
|
||||||
private $merchantPHIDs;
|
private $merchantPHIDs;
|
||||||
|
private $subscriptionPHIDs;
|
||||||
private $statuses;
|
private $statuses;
|
||||||
|
|
||||||
private $needPurchases;
|
private $needPurchases;
|
||||||
|
@ -31,6 +32,11 @@ final class PhortuneCartQuery
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function withSubscriptionPHIDs(array $subscription_phids) {
|
||||||
|
$this->subscriptionPHIDs = $subscription_phids;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
public function withStatuses(array $statuses) {
|
public function withStatuses(array $statuses) {
|
||||||
$this->statuses = $statuses;
|
$this->statuses = $statuses;
|
||||||
return $this;
|
return $this;
|
||||||
|
@ -158,6 +164,13 @@ final class PhortuneCartQuery
|
||||||
$this->merchantPHIDs);
|
$this->merchantPHIDs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($this->subscriptionPHIDs !== null) {
|
||||||
|
$where[] = qsprintf(
|
||||||
|
$conn,
|
||||||
|
'cart.subscriptionPHID IN (%Ls)',
|
||||||
|
$this->subscriptionPHIDs);
|
||||||
|
}
|
||||||
|
|
||||||
if ($this->statuses !== null) {
|
if ($this->statuses !== null) {
|
||||||
$where[] = qsprintf(
|
$where[] = qsprintf(
|
||||||
$conn,
|
$conn,
|
||||||
|
|
|
@ -5,6 +5,7 @@ final class PhortuneCartSearchEngine
|
||||||
|
|
||||||
private $merchant;
|
private $merchant;
|
||||||
private $account;
|
private $account;
|
||||||
|
private $subscription;
|
||||||
|
|
||||||
public function setAccount(PhortuneAccount $account) {
|
public function setAccount(PhortuneAccount $account) {
|
||||||
$this->account = $account;
|
$this->account = $account;
|
||||||
|
@ -24,6 +25,15 @@ final class PhortuneCartSearchEngine
|
||||||
return $this->merchant;
|
return $this->merchant;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function setSubscription(PhortuneSubscription $subscription) {
|
||||||
|
$this->subscription = $subscription;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSubscription() {
|
||||||
|
return $this->subscription;
|
||||||
|
}
|
||||||
|
|
||||||
public function getResultTypeDescription() {
|
public function getResultTypeDescription() {
|
||||||
return pht('Phortune Orders');
|
return pht('Phortune Orders');
|
||||||
}
|
}
|
||||||
|
@ -83,6 +93,11 @@ final class PhortuneCartSearchEngine
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$subscription = $this->getSubscription();
|
||||||
|
if ($subscription) {
|
||||||
|
$query->withSubscriptionPHIDs(array($subscription->getPHID()));
|
||||||
|
}
|
||||||
|
|
||||||
return $query;
|
return $query;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,7 @@ final class PhortuneCart extends PhortuneDAO
|
||||||
protected $accountPHID;
|
protected $accountPHID;
|
||||||
protected $authorPHID;
|
protected $authorPHID;
|
||||||
protected $merchantPHID;
|
protected $merchantPHID;
|
||||||
|
protected $subscriptionPHID;
|
||||||
protected $cartClass;
|
protected $cartClass;
|
||||||
protected $status;
|
protected $status;
|
||||||
protected $metadata = array();
|
protected $metadata = array();
|
||||||
|
@ -518,6 +519,7 @@ final class PhortuneCart extends PhortuneDAO
|
||||||
'status' => 'text32',
|
'status' => 'text32',
|
||||||
'cartClass' => 'text128',
|
'cartClass' => 'text128',
|
||||||
'mailKey' => 'bytes20',
|
'mailKey' => 'bytes20',
|
||||||
|
'subscriptionPHID' => 'phid?',
|
||||||
),
|
),
|
||||||
self::CONFIG_KEY_SCHEMA => array(
|
self::CONFIG_KEY_SCHEMA => array(
|
||||||
'key_account' => array(
|
'key_account' => array(
|
||||||
|
@ -526,6 +528,9 @@ final class PhortuneCart extends PhortuneDAO
|
||||||
'key_merchant' => array(
|
'key_merchant' => array(
|
||||||
'columns' => array('merchantPHID'),
|
'columns' => array('merchantPHID'),
|
||||||
),
|
),
|
||||||
|
'key_subscription' => array(
|
||||||
|
'columns' => array('subscriptionPHID'),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
) + parent::getConfiguration();
|
) + parent::getConfiguration();
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,8 +8,55 @@ final class PhortuneSubscriptionWorker extends PhabricatorWorker {
|
||||||
$range = $this->getBillingPeriodRange($subscription);
|
$range = $this->getBillingPeriodRange($subscription);
|
||||||
list($last_epoch, $next_epoch) = $range;
|
list($last_epoch, $next_epoch) = $range;
|
||||||
|
|
||||||
// TODO: Actual billing.
|
$account = $subscription->getAccount();
|
||||||
echo "Bill from {$last_epoch} to {$next_epoch}.\n";
|
$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";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue