Page MenuHomestyx hydra

No OneTemporary

diff --git a/src/applications/phortune/product/PhortuneSubscriptionProduct.php b/src/applications/phortune/product/PhortuneSubscriptionProduct.php
index dc1aed421b..9b83cbf6cb 100644
--- a/src/applications/phortune/product/PhortuneSubscriptionProduct.php
+++ b/src/applications/phortune/product/PhortuneSubscriptionProduct.php
@@ -1,90 +1,91 @@
<?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) {
+ // Prices are calculated by the SubscriptionImplementation.
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/storage/PhortuneSubscription.php b/src/applications/phortune/storage/PhortuneSubscription.php
index 51ac283668..dedc0010fc 100644
--- a/src/applications/phortune/storage/PhortuneSubscription.php
+++ b/src/applications/phortune/storage/PhortuneSubscription.php
@@ -1,233 +1,240 @@
<?php
/**
* A subscription bills users regularly.
*/
final class PhortuneSubscription extends PhortuneDAO
implements PhabricatorPolicyInterface {
const STATUS_ACTIVE = 'active';
const STATUS_CANCELLED = 'cancelled';
protected $accountPHID;
protected $merchantPHID;
protected $triggerPHID;
protected $authorPHID;
protected $subscriptionClassKey;
protected $subscriptionClass;
protected $subscriptionRefKey;
protected $subscriptionRef;
protected $status;
protected $metadata = array();
private $merchant = self::ATTACHABLE;
private $account = self::ATTACHABLE;
private $implementation = self::ATTACHABLE;
private $trigger = self::ATTACHABLE;
protected function getConfiguration() {
return array(
self::CONFIG_AUX_PHID => true,
self::CONFIG_SERIALIZATION => array(
'metadata' => self::SERIALIZATION_JSON,
),
self::CONFIG_COLUMN_SCHEMA => array(
'subscriptionClassKey' => 'bytes12',
'subscriptionClass' => 'text128',
'subscriptionRefKey' => 'bytes12',
'subscriptionRef' => 'text128',
'status' => 'text32',
),
self::CONFIG_KEY_SCHEMA => array(
'key_subscription' => array(
'columns' => array('subscriptionClassKey', 'subscriptionRefKey'),
'unique' => true,
),
'key_account' => array(
'columns' => array('accountPHID'),
),
'key_merchant' => array(
'columns' => array('merchantPHID'),
),
),
) + parent::getConfiguration();
}
public function generatePHID() {
return PhabricatorPHID::generateNewPHID(
PhortuneSubscriptionPHIDType::TYPECONST);
}
public static function initializeNewSubscription(
PhortuneAccount $account,
PhortuneMerchant $merchant,
PhabricatorUser $author,
PhortuneSubscriptionImplementation $implementation,
PhabricatorTriggerClock $clock) {
$trigger = id(new PhabricatorWorkerTrigger())
->setClock($clock);
return id(new PhortuneSubscription())
->setStatus(self::STATUS_ACTIVE)
->setAccountPHID($account->getPHID())
->attachAccount($account)
->setMerchantPHID($merchant->getPHID())
->attachMerchant($merchant)
->setAuthorPHID($author->getPHID())
->setSubscriptionClass(get_class($implementation))
->setSubscriptionRef($implementation->getRef())
->attachImplementation($implementation)
->attachTrigger($trigger);
}
public function attachImplementation(
PhortuneSubscriptionImplementation $impl) {
$this->implementation = $impl;
return $this;
}
public function getImplementation() {
return $this->assertAttached($this->implementation);
}
public function attachAccount(PhortuneAccount $account) {
$this->account = $account;
return $this;
}
public function getAccount() {
return $this->assertAttached($this->account);
}
public function attachMerchant(PhortuneMerchant $merchant) {
$this->merchant = $merchant;
return $this;
}
public function getMerchant() {
return $this->assertAttached($this->merchant);
}
public function attachTrigger(PhabricatorWorkerTrigger $trigger) {
$this->trigger = $trigger;
return $this;
}
public function getTrigger() {
return $this->assertAttached($this->trigger);
}
public function save() {
$this->subscriptionClassKey = PhabricatorHash::digestForIndex(
$this->subscriptionClass);
$this->subscriptionRefKey = PhabricatorHash::digestForIndex(
$this->subscriptionRef);
$trigger = $this->getTrigger();
$is_new = (!$this->getID());
$this->openTransaction();
// If we're saving this subscription for the first time, we're also
// going to set up the trigger for it.
if ($is_new) {
$trigger_phid = PhabricatorPHID::generateNewPHID(
PhabricatorWorkerTriggerPHIDType::TYPECONST);
$this->setTriggerPHID($trigger_phid);
}
$result = parent::save();
if ($is_new) {
$trigger_action = new PhabricatorScheduleTaskTriggerAction(
array(
'class' => 'PhortuneSubscriptionWorker',
'data' => array(
'subscriptionPHID' => $this->getPHID(),
),
'options' => array(
'objectPHID' => $this->getPHID(),
'priority' => PhabricatorWorker::PRIORITY_BULK,
),
));
$trigger->setPHID($trigger_phid);
$trigger->setAction($trigger_action);
$trigger->save();
}
$this->saveTransaction();
return $result;
}
public function getSubscriptionName() {
return $this->getImplementation()->getName($this);
}
public function getCartName(PhortuneCart $cart) {
return $this->getImplementation()->getCartName($this, $cart);
}
public function getURI() {
$account_id = $this->getAccount()->getID();
$id = $this->getID();
return "/phortune/{$account_id}/subscription/view/{$id}/";
}
public function getMerchantURI() {
$merchant_id = $this->getMerchant()->getID();
$id = $this->getID();
return "/phortune/merchant/{$merchant_id}/subscription/view/{$id}/";
}
+ public function getCostForBillingPeriodAsCurrency($start_epoch, $end_epoch) {
+ return $this->getImplementation()->getCostForBillingPeriodAsCurrency(
+ $this,
+ $start_epoch,
+ $end_epoch);
+ }
+
/* -( PhabricatorPolicyInterface )----------------------------------------- */
public function getCapabilities() {
return array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
);
}
public function getPolicy($capability) {
// NOTE: Both view and edit use the account's edit policy. We punch a hole
// through this for merchants, below.
return $this
->getAccount()
->getPolicy(PhabricatorPolicyCapability::CAN_EDIT);
}
public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
if ($this->getAccount()->hasAutomaticCapability($capability, $viewer)) {
return true;
}
// If the viewer controls the merchant this subscription bills to, they can
// view the subscription.
if ($capability == PhabricatorPolicyCapability::CAN_VIEW) {
$can_admin = PhabricatorPolicyFilter::hasCapability(
$viewer,
$this->getMerchant(),
PhabricatorPolicyCapability::CAN_EDIT);
if ($can_admin) {
return true;
}
}
return false;
}
public function describeAutomaticCapability($capability) {
return array(
pht('Subscriptions inherit the policies of the associated account.'),
pht(
'The merchant you are subscribed with can review and manage the '.
'subscription.'),
);
}
}
diff --git a/src/applications/phortune/subscription/PhortuneSubscriptionImplementation.php b/src/applications/phortune/subscription/PhortuneSubscriptionImplementation.php
index 8e7559c2e9..0abc346a85 100644
--- a/src/applications/phortune/subscription/PhortuneSubscriptionImplementation.php
+++ b/src/applications/phortune/subscription/PhortuneSubscriptionImplementation.php
@@ -1,24 +1,28 @@
<?php
abstract class PhortuneSubscriptionImplementation {
abstract public function loadImplementationsForRefs(
PhabricatorUser $viewer,
array $refs);
abstract public function getRef();
abstract public function getName(PhortuneSubscription $subscription);
+ abstract public function getCostForBillingPeriodAsCurrency(
+ PhortuneSubscription $subscription,
+ $start_epoch,
+ $end_epoch);
protected function getContentSource() {
return PhabricatorContentSource::newForSource(
PhabricatorContentSource::SOURCE_PHORTUNE,
array());
}
public function getCartName(
PhortuneSubscription $subscription,
PhortuneCart $cart) {
return pht('Subscription');
}
}
diff --git a/src/applications/phortune/worker/PhortuneSubscriptionWorker.php b/src/applications/phortune/worker/PhortuneSubscriptionWorker.php
index 8c9809042f..c59bef0422 100644
--- a/src/applications/phortune/worker/PhortuneSubscriptionWorker.php
+++ b/src/applications/phortune/worker/PhortuneSubscriptionWorker.php
@@ -1,129 +1,129 @@
<?php
final class PhortuneSubscriptionWorker extends PhabricatorWorker {
protected function doWork() {
$subscription = $this->loadSubscription();
$range = $this->getBillingPeriodRange($subscription);
list($last_epoch, $next_epoch) = $range;
$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');
+ $currency = $subscription->getCostForBillingPeriodAsCurrency(
+ $last_epoch,
+ $next_epoch);
$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";
}
/**
* Load the subscription to generate an invoice for.
*
* @return PhortuneSubscription The subscription to invoice.
*/
private function loadSubscription() {
$viewer = PhabricatorUser::getOmnipotentUser();
$data = $this->getTaskData();
$subscription_phid = idx($data, 'subscriptionPHID');
$subscription = id(new PhortuneSubscriptionQuery())
->setViewer($viewer)
->withPHIDs(array($subscription_phid))
->executeOne();
if (!$subscription) {
throw new PhabricatorWorkerPermanentFailureException(
pht(
'Failed to load subscription with PHID "%s".',
$subscription_phid));
}
return $subscription;
}
/**
* Get the start and end epoch timestamps for this billing period.
*
* @param PhortuneSubscription The subscription being billed.
* @return pair<int, int> Beginning and end of the billing range.
*/
private function getBillingPeriodRange(PhortuneSubscription $subscription) {
$data = $this->getTaskData();
$last_epoch = idx($data, 'trigger.last-epoch');
if (!$last_epoch) {
// If this is the first time the subscription is firing, use the
// creation date as the start of the billing period.
$last_epoch = $subscription->getDateCreated();
}
$this_epoch = idx($data, 'trigger.next-epoch');
if (!$last_epoch || !$this_epoch) {
throw new PhabricatorWorkerPermanentFailureException(
pht(
'Subscription is missing billing period information.'));
}
$period_length = ($this_epoch - $last_epoch);
if ($period_length <= 0) {
throw new PhabricatorWorkerPermanentFailureException(
pht(
'Subscription has invalid billing period.'));
}
if (PhabricatorTime::getNow() < $this_epoch) {
throw new Exception(
pht(
'Refusing to generate a subscription invoice for a billing period '.
'which ends in the future.'));
}
return array($last_epoch, $this_epoch);
}
}

File Metadata

Mime Type
text/x-diff
Expires
Sat, Nov 15, 3:57 AM (9 h, 13 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
337616
Default Alt Text
(15 KB)

Event Timeline