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:
		| @@ -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', | ||||
|     '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 @@ phutil_register_library_map(array( | ||||
|       'PhortuneDAO', | ||||
|       'PhabricatorPolicyInterface', | ||||
|     ), | ||||
|     'PhortuneSubscriptionCart' => 'PhortuneCartImplementation', | ||||
|     'PhortuneSubscriptionListController' => 'PhortuneController', | ||||
|     'PhortuneSubscriptionPHIDType' => 'PhabricatorPHIDType', | ||||
|     'PhortuneSubscriptionProduct' => 'PhortuneProductImplementation', | ||||
|     'PhortuneSubscriptionQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', | ||||
|     'PhortuneSubscriptionSearchEngine' => 'PhabricatorApplicationSearchEngine', | ||||
|     'PhortuneSubscriptionTableView' => 'AphrontView', | ||||
|   | ||||
| @@ -50,6 +50,8 @@ final class PhabricatorPhortuneApplication extends PhabricatorApplication { | ||||
|               => 'PhortuneSubscriptionListController', | ||||
|             'view/(?P<id>\d+)/' | ||||
|               => 'PhortuneSubscriptionViewController', | ||||
|             'order/(?P<subscriptionID>\d+)/' | ||||
|               => 'PhortuneCartListController', | ||||
|           ), | ||||
|           'charge/(?:query/(?P<queryKey>[^/]+)/)?' | ||||
|             => 'PhortuneChargeListController', | ||||
| @@ -90,6 +92,8 @@ final class PhabricatorPhortuneApplication extends PhabricatorApplication { | ||||
|               => 'PhortuneSubscriptionListController', | ||||
|             'view/(?P<id>\d+)/' | ||||
|               => 'PhortuneSubscriptionViewController', | ||||
|             'order/(?P<subscriptionID>\d+)/' | ||||
|               => 'PhortuneCartListController', | ||||
|           ), | ||||
|           '(?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 | ||||
|   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 @@ final class PhortuneCartListController | ||||
|       } | ||||
|       $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 @@ final class PhortuneCartListController | ||||
|     } | ||||
|  | ||||
|     $controller = id(new PhabricatorApplicationSearchController()) | ||||
|       ->setQueryKey($this->queryKey) | ||||
|       ->setQueryKey($request->getURIData('queryKey')) | ||||
|       ->setSearchEngine($engine) | ||||
|       ->setNavigation($this->buildSideNavView()); | ||||
|  | ||||
| @@ -82,27 +88,40 @@ final class PhortuneCartListController | ||||
|   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}/")); | ||||
|       $this->addMerchantCrumb($crumbs, $merchant); | ||||
|       if (!$subscription) { | ||||
|         $crumbs->addTextCrumb( | ||||
|           pht('Orders'), | ||||
|           $this->getApplicationURI("merchant/orders/{$id}/")); | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     $account = $this->account; | ||||
|     if ($account) { | ||||
|       $id = $account->getID(); | ||||
|       $crumbs->addTextCrumb( | ||||
|         $account->getName(), | ||||
|         $this->getApplicationURI("{$id}/")); | ||||
|       $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( | ||||
|         $subscription->getSubscriptionName(), | ||||
|         $subscription_uri); | ||||
|     } | ||||
|  | ||||
|     return $crumbs; | ||||
|   } | ||||
|   | ||||
| @@ -15,6 +15,8 @@ final class PhortuneSubscriptionViewController extends PhortuneController { | ||||
|     } | ||||
|  | ||||
|     $is_merchant = (bool)$request->getURIData('merchantID'); | ||||
|     $merchant = $subscription->getMerchant(); | ||||
|     $account = $subscription->getAccount(); | ||||
|  | ||||
|     $title = pht('Subscription: %s', $subscription->getSubscriptionName()); | ||||
|  | ||||
| @@ -27,9 +29,9 @@ final class PhortuneSubscriptionViewController extends PhortuneController { | ||||
|  | ||||
|     $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 @@ final class PhortuneSubscriptionViewController extends PhortuneController { | ||||
|       ->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, | ||||
|   | ||||
| @@ -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 $accountPHIDs; | ||||
|   private $merchantPHIDs; | ||||
|   private $subscriptionPHIDs; | ||||
|   private $statuses; | ||||
|  | ||||
|   private $needPurchases; | ||||
| @@ -31,6 +32,11 @@ final class PhortuneCartQuery | ||||
|     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 @@ final class PhortuneCartQuery | ||||
|         $this->merchantPHIDs); | ||||
|     } | ||||
|  | ||||
|     if ($this->subscriptionPHIDs !== null) { | ||||
|       $where[] = qsprintf( | ||||
|         $conn, | ||||
|         'cart.subscriptionPHID IN (%Ls)', | ||||
|         $this->subscriptionPHIDs); | ||||
|     } | ||||
|  | ||||
|     if ($this->statuses !== null) { | ||||
|       $where[] = qsprintf( | ||||
|         $conn, | ||||
|   | ||||
| @@ -5,6 +5,7 @@ final class PhortuneCartSearchEngine | ||||
|  | ||||
|   private $merchant; | ||||
|   private $account; | ||||
|   private $subscription; | ||||
|  | ||||
|   public function setAccount(PhortuneAccount $account) { | ||||
|     $this->account = $account; | ||||
| @@ -24,6 +25,15 @@ final class PhortuneCartSearchEngine | ||||
|     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 @@ final class PhortuneCartSearchEngine | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     $subscription = $this->getSubscription(); | ||||
|     if ($subscription) { | ||||
|       $query->withSubscriptionPHIDs(array($subscription->getPHID())); | ||||
|     } | ||||
|  | ||||
|     return $query; | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -16,6 +16,7 @@ final class PhortuneCart extends PhortuneDAO | ||||
|   protected $accountPHID; | ||||
|   protected $authorPHID; | ||||
|   protected $merchantPHID; | ||||
|   protected $subscriptionPHID; | ||||
|   protected $cartClass; | ||||
|   protected $status; | ||||
|   protected $metadata = array(); | ||||
| @@ -518,6 +519,7 @@ final class PhortuneCart extends PhortuneDAO | ||||
|         'status' => 'text32', | ||||
|         'cartClass' => 'text128', | ||||
|         'mailKey' => 'bytes20', | ||||
|         'subscriptionPHID' => 'phid?', | ||||
|       ), | ||||
|       self::CONFIG_KEY_SCHEMA => array( | ||||
|         'key_account' => array( | ||||
| @@ -526,6 +528,9 @@ final class PhortuneCart extends PhortuneDAO | ||||
|         'key_merchant' => array( | ||||
|           'columns' => array('merchantPHID'), | ||||
|         ), | ||||
|         'key_subscription' => array( | ||||
|           'columns' => array('subscriptionPHID'), | ||||
|         ), | ||||
|       ), | ||||
|     ) + parent::getConfiguration(); | ||||
|   } | ||||
|   | ||||
| @@ -8,8 +8,55 @@ final class PhortuneSubscriptionWorker extends PhabricatorWorker { | ||||
|     $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"; | ||||
|   } | ||||
|  | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 epriestley
					epriestley