4 * A subscription bills users regularly.
6 final class PhortuneSubscription
9 PhabricatorPolicyInterface
,
10 PhabricatorExtendedPolicyInterface
,
11 PhabricatorPolicyCodexInterface
,
12 PhabricatorApplicationTransactionInterface
{
14 const STATUS_ACTIVE
= 'active';
15 const STATUS_CANCELLED
= 'cancelled';
17 protected $accountPHID;
18 protected $merchantPHID;
19 protected $triggerPHID;
20 protected $authorPHID;
21 protected $defaultPaymentMethodPHID;
22 protected $subscriptionClassKey;
23 protected $subscriptionClass;
24 protected $subscriptionRefKey;
25 protected $subscriptionRef;
27 protected $metadata = array();
29 private $merchant = self
::ATTACHABLE
;
30 private $account = self
::ATTACHABLE
;
31 private $implementation = self
::ATTACHABLE
;
32 private $trigger = self
::ATTACHABLE
;
34 protected function getConfiguration() {
36 self
::CONFIG_AUX_PHID
=> true,
37 self
::CONFIG_SERIALIZATION
=> array(
38 'metadata' => self
::SERIALIZATION_JSON
,
40 self
::CONFIG_COLUMN_SCHEMA
=> array(
41 'defaultPaymentMethodPHID' => 'phid?',
42 'subscriptionClassKey' => 'bytes12',
43 'subscriptionClass' => 'text128',
44 'subscriptionRefKey' => 'bytes12',
45 'subscriptionRef' => 'text128',
48 self
::CONFIG_KEY_SCHEMA
=> array(
49 'key_subscription' => array(
50 'columns' => array('subscriptionClassKey', 'subscriptionRefKey'),
53 'key_account' => array(
54 'columns' => array('accountPHID'),
56 'key_merchant' => array(
57 'columns' => array('merchantPHID'),
60 ) + parent
::getConfiguration();
63 public function getPHIDType() {
64 return PhortuneSubscriptionPHIDType
::TYPECONST
;
67 public static function initializeNewSubscription(
68 PhortuneAccount
$account,
69 PhortuneMerchant
$merchant,
70 PhabricatorUser
$author,
71 PhortuneSubscriptionImplementation
$implementation,
72 PhabricatorTriggerClock
$clock) {
74 $trigger = id(new PhabricatorWorkerTrigger())
77 return id(new PhortuneSubscription())
78 ->setStatus(self
::STATUS_ACTIVE
)
79 ->setAccountPHID($account->getPHID())
80 ->attachAccount($account)
81 ->setMerchantPHID($merchant->getPHID())
82 ->attachMerchant($merchant)
83 ->setAuthorPHID($author->getPHID())
84 ->setSubscriptionClass(get_class($implementation))
85 ->setSubscriptionRef($implementation->getRef())
86 ->attachImplementation($implementation)
87 ->attachTrigger($trigger);
90 public function attachImplementation(
91 PhortuneSubscriptionImplementation
$impl) {
92 $this->implementation
= $impl;
96 public function getImplementation() {
97 return $this->assertAttached($this->implementation
);
100 public function attachAccount(PhortuneAccount
$account) {
101 $this->account
= $account;
105 public function getAccount() {
106 return $this->assertAttached($this->account
);
109 public function attachMerchant(PhortuneMerchant
$merchant) {
110 $this->merchant
= $merchant;
114 public function getMerchant() {
115 return $this->assertAttached($this->merchant
);
118 public function attachTrigger(PhabricatorWorkerTrigger
$trigger) {
119 $this->trigger
= $trigger;
123 public function getTrigger() {
124 return $this->assertAttached($this->trigger
);
127 public function save() {
128 $this->subscriptionClassKey
= PhabricatorHash
::digestForIndex(
129 $this->subscriptionClass
);
131 $this->subscriptionRefKey
= PhabricatorHash
::digestForIndex(
132 $this->subscriptionRef
);
134 $is_new = (!$this->getID());
136 $this->openTransaction();
138 // If we're saving this subscription for the first time, we're also
139 // going to set up the trigger for it.
141 $trigger_phid = PhabricatorPHID
::generateNewPHID(
142 PhabricatorWorkerTriggerPHIDType
::TYPECONST
);
143 $this->setTriggerPHID($trigger_phid);
146 $result = parent
::save();
149 $trigger_action = new PhabricatorScheduleTaskTriggerAction(
151 'class' => 'PhortuneSubscriptionWorker',
153 'subscriptionPHID' => $this->getPHID(),
156 'objectPHID' => $this->getPHID(),
157 'priority' => PhabricatorWorker
::PRIORITY_BULK
,
161 $trigger = $this->getTrigger();
162 $trigger->setPHID($trigger_phid);
163 $trigger->setAction($trigger_action);
166 $this->saveTransaction();
168 $account = $this->getAccount();
169 $merchant = $this->getMerchant();
170 $account->writeMerchantEdge($merchant);
175 public function getSubscriptionName() {
176 return $this->getImplementation()->getName($this);
179 public function getSubscriptionFullName() {
180 return $this->getImplementation()->getFullName($this);
183 public function getSubscriptionCrumbName() {
184 return $this->getImplementation()->getCrumbName($this);
187 public function getCartName(PhortuneCart
$cart) {
188 return $this->getImplementation()->getCartName($this, $cart);
191 public function getURI() {
193 '/phortune/account/%d/subscriptions/%d/',
194 $this->getAccount()->getID(),
198 public function getEditURI() {
199 $account_id = $this->getAccount()->getID();
200 $id = $this->getID();
202 return "/phortune/{$account_id}/subscription/edit/{$id}/";
205 public function getMerchantURI() {
206 $merchant_id = $this->getMerchant()->getID();
207 $id = $this->getID();
208 return "/phortune/merchant/{$merchant_id}/subscription/view/{$id}/";
211 public function getCostForBillingPeriodAsCurrency($start_epoch, $end_epoch) {
212 return $this->getImplementation()->getCostForBillingPeriodAsCurrency(
218 public function shouldInvoiceForBillingPeriod($start_epoch, $end_epoch) {
219 return $this->getImplementation()->shouldInvoiceForBillingPeriod(
225 public function getPurchaseName(
226 PhortuneProduct
$product,
227 PhortunePurchase
$purchase) {
228 return $this->getImplementation()->getPurchaseName(
234 public function getPurchaseURI(
235 PhortuneProduct
$product,
236 PhortunePurchase
$purchase) {
237 return $this->getImplementation()->getPurchaseURI(
243 public function didPurchaseProduct(
244 PhortuneProduct
$product,
245 PhortunePurchase
$purchase) {
246 return $this->getImplementation()->didPurchaseProduct(
252 /* -( PhabricatorApplicationTransactionInterface )------------------------- */
255 public function getApplicationTransactionEditor() {
256 return new PhortuneSubscriptionEditor();
259 public function getApplicationTransactionTemplate() {
260 return new PhortuneSubscriptionTransaction();
263 /* -( PhabricatorPolicyInterface )----------------------------------------- */
266 public function getCapabilities() {
268 PhabricatorPolicyCapability
::CAN_VIEW
,
269 PhabricatorPolicyCapability
::CAN_EDIT
,
273 public function getPolicy($capability) {
274 return PhabricatorPolicies
::getMostOpenPolicy();
277 public function hasAutomaticCapability($capability, PhabricatorUser
$viewer) {
278 // See T13366. If you can edit the merchant associated with this
279 // subscription, you can view the subscription.
280 if ($capability === PhabricatorPolicyCapability
::CAN_VIEW
) {
281 $any_edit = PhortuneMerchantQuery
::canViewersEditMerchants(
282 array($viewer->getPHID()),
283 array($this->getMerchantPHID()));
293 /* -( PhabricatorExtendedPolicyInterface )--------------------------------- */
296 public function getExtendedPolicy($capability, PhabricatorUser
$viewer) {
297 if ($this->hasAutomaticCapability($capability, $viewer)) {
301 // See T13366. For blanket view and edit permissions on all subscriptions,
302 // you must be able to edit the associated account.
306 PhabricatorPolicyCapability
::CAN_EDIT
,
312 /* -( PhabricatorPolicyCodexInterface )------------------------------------ */
315 public function newPolicyCodex() {
316 return new PhortuneSubscriptionPolicyCodex();