mirror of
https://we.phorge.it/source/phorge.git
synced 2024-12-29 00:40:57 +01:00
Automatically bill subscriptions when a payment method is available
Summary: Ref T6881. - Allow users to set a default payment method for a subscription, which we'll try to autobill (not all payment methods are autobillable, so we can't require this in the general case, and a charge might fail anyway). - If a subscription has an autopay method, try to automatically bill it. - Otherwise, we'll send them an email like "hey here's a bill, it couldn't autopay for some reasons, go pay it and fix those if you want". - (That email doesn't exist yet but there's a comment about it.) - Also some UI cleanup. Test Plan: - Used `bin/phortune invoice` to autobill myself some fake test money. {F279416} Reviewers: btrahan Reviewed By: btrahan Subscribers: epriestley Maniphest Tasks: T6881 Differential Revision: https://secure.phabricator.com/D11596
This commit is contained in:
parent
87deb72cdb
commit
77db15c47b
10 changed files with 327 additions and 24 deletions
|
@ -0,0 +1,2 @@
|
||||||
|
ALTER TABLE {$NAMESPACE}_phortune.phortune_subscription
|
||||||
|
ADD defaultPaymentMethodPHID VARBINARY(64);
|
|
@ -2812,6 +2812,7 @@ phutil_register_library_map(array(
|
||||||
'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',
|
'PhortuneSubscriptionCart' => 'applications/phortune/cart/PhortuneSubscriptionCart.php',
|
||||||
|
'PhortuneSubscriptionEditController' => 'applications/phortune/controller/PhortuneSubscriptionEditController.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',
|
||||||
|
@ -6172,6 +6173,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorPolicyInterface',
|
'PhabricatorPolicyInterface',
|
||||||
),
|
),
|
||||||
'PhortuneSubscriptionCart' => 'PhortuneCartImplementation',
|
'PhortuneSubscriptionCart' => 'PhortuneCartImplementation',
|
||||||
|
'PhortuneSubscriptionEditController' => 'PhortuneController',
|
||||||
'PhortuneSubscriptionListController' => 'PhortuneController',
|
'PhortuneSubscriptionListController' => 'PhortuneController',
|
||||||
'PhortuneSubscriptionPHIDType' => 'PhabricatorPHIDType',
|
'PhortuneSubscriptionPHIDType' => 'PhabricatorPHIDType',
|
||||||
'PhortuneSubscriptionProduct' => 'PhortuneProductImplementation',
|
'PhortuneSubscriptionProduct' => 'PhortuneProductImplementation',
|
||||||
|
|
|
@ -46,6 +46,8 @@ final class PhabricatorPhortuneApplication extends PhabricatorApplication {
|
||||||
=> 'PhortuneSubscriptionListController',
|
=> 'PhortuneSubscriptionListController',
|
||||||
'view/(?P<id>\d+)/'
|
'view/(?P<id>\d+)/'
|
||||||
=> 'PhortuneSubscriptionViewController',
|
=> 'PhortuneSubscriptionViewController',
|
||||||
|
'edit/(?P<id>\d+)/'
|
||||||
|
=> 'PhortuneSubscriptionEditController',
|
||||||
'order/(?P<subscriptionID>\d+)/'
|
'order/(?P<subscriptionID>\d+)/'
|
||||||
=> 'PhortuneCartListController',
|
=> 'PhortuneCartListController',
|
||||||
),
|
),
|
||||||
|
|
|
@ -0,0 +1,132 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class PhortuneSubscriptionEditController extends PhortuneController {
|
||||||
|
|
||||||
|
public function handleRequest(AphrontRequest $request) {
|
||||||
|
$viewer = $this->getViewer();
|
||||||
|
|
||||||
|
$subscription = id(new PhortuneSubscriptionQuery())
|
||||||
|
->setViewer($viewer)
|
||||||
|
->withIDs(array($request->getURIData('id')))
|
||||||
|
->requireCapabilities(
|
||||||
|
array(
|
||||||
|
PhabricatorPolicyCapability::CAN_VIEW,
|
||||||
|
PhabricatorPolicyCapability::CAN_EDIT,
|
||||||
|
))
|
||||||
|
->executeOne();
|
||||||
|
if (!$subscription) {
|
||||||
|
return new Aphront404Response();
|
||||||
|
}
|
||||||
|
|
||||||
|
$merchant = $subscription->getMerchant();
|
||||||
|
$account = $subscription->getAccount();
|
||||||
|
|
||||||
|
$title = pht('Subscription: %s', $subscription->getSubscriptionName());
|
||||||
|
|
||||||
|
$header = id(new PHUIHeaderView())
|
||||||
|
->setHeader($subscription->getSubscriptionName());
|
||||||
|
|
||||||
|
$view_uri = $subscription->getURI();
|
||||||
|
|
||||||
|
$valid_methods = id(new PhortunePaymentMethodQuery())
|
||||||
|
->setViewer($viewer)
|
||||||
|
->withAccountPHIDs(array($account->getPHID()))
|
||||||
|
->requireCapabilities(
|
||||||
|
array(
|
||||||
|
PhabricatorPolicyCapability::CAN_VIEW,
|
||||||
|
PhabricatorPolicyCapability::CAN_EDIT,
|
||||||
|
))
|
||||||
|
->execute();
|
||||||
|
$valid_methods = mpull($valid_methods, null, 'getPHID');
|
||||||
|
|
||||||
|
$current_phid = $subscription->getDefaultPaymentMethodPHID();
|
||||||
|
|
||||||
|
$errors = array();
|
||||||
|
if ($request->isFormPost()) {
|
||||||
|
|
||||||
|
$default_method_phid = $request->getStr('defaultPaymentMethodPHID');
|
||||||
|
if (!$default_method_phid) {
|
||||||
|
$default_method_phid = null;
|
||||||
|
$e_method = null;
|
||||||
|
} else if ($default_method_phid == $current_phid) {
|
||||||
|
// If you have an invalid setting already, it's OK to retain it.
|
||||||
|
$e_method = null;
|
||||||
|
} else {
|
||||||
|
if (empty($valid_methods[$default_method_phid])) {
|
||||||
|
$e_method = pht('Invalid');
|
||||||
|
$errors[] = pht('You must select a valid default payment method.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: We should use transactions here, and move the validation logic
|
||||||
|
// inside the Editor.
|
||||||
|
|
||||||
|
if (!$errors) {
|
||||||
|
$subscription->setDefaultPaymentMethodPHID($default_method_phid);
|
||||||
|
$subscription->save();
|
||||||
|
|
||||||
|
return id(new AphrontRedirectResponse())
|
||||||
|
->setURI($view_uri);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the option to disable autopay.
|
||||||
|
$disable_options = array(
|
||||||
|
'' => pht('(Disable Autopay)'),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Don't require the user to make a valid selection if the current method
|
||||||
|
// has become invalid.
|
||||||
|
// TODO: This should probably have a note about why this is bogus.
|
||||||
|
if ($current_phid && empty($valid_methods[$current_phid])) {
|
||||||
|
$handles = $this->loadViewerHandles(array($current_phid));
|
||||||
|
$current_options = array(
|
||||||
|
$current_phid => $handles[$current_phid]->getName(),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$current_options = array();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add any available options.
|
||||||
|
$valid_options = mpull($valid_methods, 'getFullDisplayName', 'getPHID');
|
||||||
|
|
||||||
|
$options = $disable_options + $current_options + $valid_options;
|
||||||
|
|
||||||
|
$crumbs = $this->buildApplicationCrumbs();
|
||||||
|
$this->addAccountCrumb($crumbs, $account);
|
||||||
|
$crumbs->addTextCrumb(
|
||||||
|
pht('Subscription %d', $subscription->getID()),
|
||||||
|
$view_uri);
|
||||||
|
$crumbs->addTextCrumb(pht('Edit'));
|
||||||
|
|
||||||
|
$form = id(new AphrontFormView())
|
||||||
|
->setUser($viewer)
|
||||||
|
->appendChild(
|
||||||
|
id(new AphrontFormSelectControl())
|
||||||
|
->setName('defaultPaymentMethodPHID')
|
||||||
|
->setLabel(pht('Autopay With'))
|
||||||
|
->setValue($current_phid)
|
||||||
|
->setOptions($options))
|
||||||
|
->appendChild(
|
||||||
|
id(new AphrontFormSubmitControl())
|
||||||
|
->setValue(pht('Save Changes'))
|
||||||
|
->addCancelButton($view_uri));
|
||||||
|
|
||||||
|
$box = id(new PHUIObjectBoxView())
|
||||||
|
->setUser($viewer)
|
||||||
|
->setHeaderText(pht('Edit %s', $subscription->getSubscriptionName()))
|
||||||
|
->setFormErrors($errors)
|
||||||
|
->appendChild($form);
|
||||||
|
|
||||||
|
return $this->buildApplicationPage(
|
||||||
|
array(
|
||||||
|
$crumbs,
|
||||||
|
$box,
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'title' => $title,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -14,10 +14,18 @@ final class PhortuneSubscriptionViewController extends PhortuneController {
|
||||||
return new Aphront404Response();
|
return new Aphront404Response();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$can_edit = PhabricatorPolicyFilter::hasCapability(
|
||||||
|
$viewer,
|
||||||
|
$subscription,
|
||||||
|
PhabricatorPolicyCapability::CAN_EDIT);
|
||||||
|
|
||||||
$is_merchant = (bool)$request->getURIData('merchantID');
|
$is_merchant = (bool)$request->getURIData('merchantID');
|
||||||
$merchant = $subscription->getMerchant();
|
$merchant = $subscription->getMerchant();
|
||||||
$account = $subscription->getAccount();
|
$account = $subscription->getAccount();
|
||||||
|
|
||||||
|
$account_id = $account->getID();
|
||||||
|
$subscription_id = $subscription->getID();
|
||||||
|
|
||||||
$title = pht('Subscription: %s', $subscription->getSubscriptionName());
|
$title = pht('Subscription: %s', $subscription->getSubscriptionName());
|
||||||
|
|
||||||
$header = id(new PHUIHeaderView())
|
$header = id(new PHUIHeaderView())
|
||||||
|
@ -27,6 +35,18 @@ final class PhortuneSubscriptionViewController extends PhortuneController {
|
||||||
->setUser($viewer)
|
->setUser($viewer)
|
||||||
->setObjectURI($request->getRequestURI());
|
->setObjectURI($request->getRequestURI());
|
||||||
|
|
||||||
|
$edit_uri = $this->getApplicationURI(
|
||||||
|
"{$account_id}/subscription/edit/{$subscription_id}/");
|
||||||
|
|
||||||
|
$actions->addAction(
|
||||||
|
id(new PhabricatorActionView())
|
||||||
|
->setIcon('fa-pencil')
|
||||||
|
->setName(pht('Edit Subscription'))
|
||||||
|
->setHref($edit_uri)
|
||||||
|
->setDisabled(!$can_edit)
|
||||||
|
->setWorkflow(!$can_edit));
|
||||||
|
|
||||||
|
|
||||||
$crumbs = $this->buildApplicationCrumbs();
|
$crumbs = $this->buildApplicationCrumbs();
|
||||||
if ($is_merchant) {
|
if ($is_merchant) {
|
||||||
$this->addMerchantCrumb($crumbs, $merchant);
|
$this->addMerchantCrumb($crumbs, $merchant);
|
||||||
|
@ -44,11 +64,83 @@ final class PhortuneSubscriptionViewController extends PhortuneController {
|
||||||
pht('Next Invoice'),
|
pht('Next Invoice'),
|
||||||
phabricator_datetime($next_invoice, $viewer));
|
phabricator_datetime($next_invoice, $viewer));
|
||||||
|
|
||||||
|
$default_method = $subscription->getDefaultPaymentMethodPHID();
|
||||||
|
if ($default_method) {
|
||||||
|
$handles = $this->loadViewerHandles(array($default_method));
|
||||||
|
$autopay_method = $handles[$default_method]->renderLink();
|
||||||
|
} else {
|
||||||
|
$autopay_method = phutil_tag(
|
||||||
|
'em',
|
||||||
|
array(),
|
||||||
|
pht('No Autopay Method Configured'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$properties->addProperty(
|
||||||
|
pht('Autopay With'),
|
||||||
|
$autopay_method);
|
||||||
|
|
||||||
$object_box = id(new PHUIObjectBoxView())
|
$object_box = id(new PHUIObjectBoxView())
|
||||||
->setHeader($header)
|
->setHeader($header)
|
||||||
->addPropertyList($properties);
|
->addPropertyList($properties);
|
||||||
|
|
||||||
$carts = id(new PhortuneCartQuery())
|
$due_box = $this->buildDueInvoices($subscription, $is_merchant);
|
||||||
|
$invoice_box = $this->buildPastInvoices($subscription, $is_merchant);
|
||||||
|
|
||||||
|
return $this->buildApplicationPage(
|
||||||
|
array(
|
||||||
|
$crumbs,
|
||||||
|
$object_box,
|
||||||
|
$due_box,
|
||||||
|
$invoice_box,
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'title' => $title,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
private function buildDueInvoices(
|
||||||
|
PhortuneSubscription $subscription,
|
||||||
|
$is_merchant) {
|
||||||
|
$viewer = $this->getViewer();
|
||||||
|
|
||||||
|
$invoices = id(new PhortuneCartQuery())
|
||||||
|
->setViewer($viewer)
|
||||||
|
->withSubscriptionPHIDs(array($subscription->getPHID()))
|
||||||
|
->needPurchases(true)
|
||||||
|
->withInvoices(true)
|
||||||
|
->execute();
|
||||||
|
|
||||||
|
$phids = array();
|
||||||
|
foreach ($invoices as $invoice) {
|
||||||
|
$phids[] = $invoice->getPHID();
|
||||||
|
$phids[] = $invoice->getMerchantPHID();
|
||||||
|
foreach ($invoice->getPurchases() as $purchase) {
|
||||||
|
$phids[] = $purchase->getPHID();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$handles = $this->loadViewerHandles($phids);
|
||||||
|
|
||||||
|
$invoice_table = id(new PhortuneOrderTableView())
|
||||||
|
->setUser($viewer)
|
||||||
|
->setCarts($invoices)
|
||||||
|
->setIsInvoices(true)
|
||||||
|
->setIsMerchantView($is_merchant)
|
||||||
|
->setHandles($handles);
|
||||||
|
|
||||||
|
$invoice_header = id(new PHUIHeaderView())
|
||||||
|
->setHeader(pht('Invoices Due'));
|
||||||
|
|
||||||
|
return id(new PHUIObjectBoxView())
|
||||||
|
->setHeader($invoice_header)
|
||||||
|
->appendChild($invoice_table);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function buildPastInvoices(
|
||||||
|
PhortuneSubscription $subscription,
|
||||||
|
$is_merchant) {
|
||||||
|
$viewer = $this->getViewer();
|
||||||
|
|
||||||
|
$invoices = id(new PhortuneCartQuery())
|
||||||
->setViewer($viewer)
|
->setViewer($viewer)
|
||||||
->withSubscriptionPHIDs(array($subscription->getPHID()))
|
->withSubscriptionPHIDs(array($subscription->getPHID()))
|
||||||
->needPurchases(true)
|
->needPurchases(true)
|
||||||
|
@ -60,12 +152,13 @@ final class PhortuneSubscriptionViewController extends PhortuneController {
|
||||||
PhortuneCart::STATUS_REVIEW,
|
PhortuneCart::STATUS_REVIEW,
|
||||||
PhortuneCart::STATUS_PURCHASED,
|
PhortuneCart::STATUS_PURCHASED,
|
||||||
))
|
))
|
||||||
|
->setLimit(50)
|
||||||
->execute();
|
->execute();
|
||||||
|
|
||||||
$phids = array();
|
$phids = array();
|
||||||
foreach ($carts as $cart) {
|
foreach ($invoices as $invoice) {
|
||||||
$phids[] = $cart->getPHID();
|
$phids[] = $invoice->getPHID();
|
||||||
foreach ($cart->getPurchases() as $purchase) {
|
foreach ($invoice->getPurchases() as $purchase) {
|
||||||
$phids[] = $purchase->getPHID();
|
$phids[] = $purchase->getPHID();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -73,9 +166,12 @@ final class PhortuneSubscriptionViewController extends PhortuneController {
|
||||||
|
|
||||||
$invoice_table = id(new PhortuneOrderTableView())
|
$invoice_table = id(new PhortuneOrderTableView())
|
||||||
->setUser($viewer)
|
->setUser($viewer)
|
||||||
->setCarts($carts)
|
->setCarts($invoices)
|
||||||
->setHandles($handles);
|
->setHandles($handles);
|
||||||
|
|
||||||
|
$account = $subscription->getAccount();
|
||||||
|
$merchant = $subscription->getMerchant();
|
||||||
|
|
||||||
$account_id = $account->getID();
|
$account_id = $account->getID();
|
||||||
$merchant_id = $merchant->getID();
|
$merchant_id = $merchant->getID();
|
||||||
$subscription_id = $subscription->getID();
|
$subscription_id = $subscription->getID();
|
||||||
|
@ -89,7 +185,7 @@ final class PhortuneSubscriptionViewController extends PhortuneController {
|
||||||
}
|
}
|
||||||
|
|
||||||
$invoice_header = id(new PHUIHeaderView())
|
$invoice_header = id(new PHUIHeaderView())
|
||||||
->setHeader(pht('Recent Invoices'))
|
->setHeader(pht('Past Invoices'))
|
||||||
->addActionLink(
|
->addActionLink(
|
||||||
id(new PHUIButtonView())
|
id(new PHUIButtonView())
|
||||||
->setTag('a')
|
->setTag('a')
|
||||||
|
@ -99,19 +195,9 @@ final class PhortuneSubscriptionViewController extends PhortuneController {
|
||||||
->setHref($invoices_uri)
|
->setHref($invoices_uri)
|
||||||
->setText(pht('View All Invoices')));
|
->setText(pht('View All Invoices')));
|
||||||
|
|
||||||
$invoice_box = id(new PHUIObjectBoxView())
|
return id(new PHUIObjectBoxView())
|
||||||
->setHeader($invoice_header)
|
->setHeader($invoice_header)
|
||||||
->appendChild($invoice_table);
|
->appendChild($invoice_table);
|
||||||
|
|
||||||
return $this->buildApplicationPage(
|
|
||||||
array(
|
|
||||||
$crumbs,
|
|
||||||
$object_box,
|
|
||||||
$invoice_box,
|
|
||||||
),
|
|
||||||
array(
|
|
||||||
'title' => $title,
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,7 +35,9 @@ final class PhortuneCart extends PhortuneDAO
|
||||||
->setAuthorPHID($actor->getPHID())
|
->setAuthorPHID($actor->getPHID())
|
||||||
->setStatus(self::STATUS_BUILDING)
|
->setStatus(self::STATUS_BUILDING)
|
||||||
->setAccountPHID($account->getPHID())
|
->setAccountPHID($account->getPHID())
|
||||||
->setMerchantPHID($merchant->getPHID());
|
->attachAccount($account)
|
||||||
|
->setMerchantPHID($merchant->getPHID())
|
||||||
|
->attachMerchant($merchant);
|
||||||
|
|
||||||
$cart->account = $account;
|
$cart->account = $account;
|
||||||
$cart->purchases = array();
|
$cart->purchases = array();
|
||||||
|
|
|
@ -31,6 +31,7 @@ final class PhortunePurchase extends PhortuneDAO
|
||||||
return id(new PhortunePurchase())
|
return id(new PhortunePurchase())
|
||||||
->setAuthorPHID($actor->getPHID())
|
->setAuthorPHID($actor->getPHID())
|
||||||
->setProductPHID($product->getPHID())
|
->setProductPHID($product->getPHID())
|
||||||
|
->attachProduct($product)
|
||||||
->setQuantity(1)
|
->setQuantity(1)
|
||||||
->setStatus(self::STATUS_PENDING)
|
->setStatus(self::STATUS_PENDING)
|
||||||
->setBasePriceAsCurrency($product->getPriceAsCurrency());
|
->setBasePriceAsCurrency($product->getPriceAsCurrency());
|
||||||
|
|
|
@ -13,6 +13,7 @@ final class PhortuneSubscription extends PhortuneDAO
|
||||||
protected $merchantPHID;
|
protected $merchantPHID;
|
||||||
protected $triggerPHID;
|
protected $triggerPHID;
|
||||||
protected $authorPHID;
|
protected $authorPHID;
|
||||||
|
protected $defaultPaymentMethodPHID;
|
||||||
protected $subscriptionClassKey;
|
protected $subscriptionClassKey;
|
||||||
protected $subscriptionClass;
|
protected $subscriptionClass;
|
||||||
protected $subscriptionRefKey;
|
protected $subscriptionRefKey;
|
||||||
|
@ -32,6 +33,7 @@ final class PhortuneSubscription extends PhortuneDAO
|
||||||
'metadata' => self::SERIALIZATION_JSON,
|
'metadata' => self::SERIALIZATION_JSON,
|
||||||
),
|
),
|
||||||
self::CONFIG_COLUMN_SCHEMA => array(
|
self::CONFIG_COLUMN_SCHEMA => array(
|
||||||
|
'defaultPaymentMethodPHID' => 'phid?',
|
||||||
'subscriptionClassKey' => 'bytes12',
|
'subscriptionClassKey' => 'bytes12',
|
||||||
'subscriptionClass' => 'text128',
|
'subscriptionClass' => 'text128',
|
||||||
'subscriptionRefKey' => 'bytes12',
|
'subscriptionRefKey' => 'bytes12',
|
||||||
|
@ -125,7 +127,6 @@ final class PhortuneSubscription extends PhortuneDAO
|
||||||
$this->subscriptionRefKey = PhabricatorHash::digestForIndex(
|
$this->subscriptionRefKey = PhabricatorHash::digestForIndex(
|
||||||
$this->subscriptionRef);
|
$this->subscriptionRef);
|
||||||
|
|
||||||
$trigger = $this->getTrigger();
|
|
||||||
$is_new = (!$this->getID());
|
$is_new = (!$this->getID());
|
||||||
|
|
||||||
$this->openTransaction();
|
$this->openTransaction();
|
||||||
|
@ -153,6 +154,7 @@ final class PhortuneSubscription extends PhortuneDAO
|
||||||
),
|
),
|
||||||
));
|
));
|
||||||
|
|
||||||
|
$trigger = $this->getTrigger();
|
||||||
$trigger->setPHID($trigger_phid);
|
$trigger->setPHID($trigger_phid);
|
||||||
$trigger->setAction($trigger_action);
|
$trigger->setAction($trigger_action);
|
||||||
$trigger->save();
|
$trigger->save();
|
||||||
|
|
|
@ -6,6 +6,7 @@ final class PhortuneOrderTableView extends AphrontView {
|
||||||
private $handles;
|
private $handles;
|
||||||
private $noDataString;
|
private $noDataString;
|
||||||
private $isInvoices;
|
private $isInvoices;
|
||||||
|
private $isMerchantView;
|
||||||
|
|
||||||
public function setHandles(array $handles) {
|
public function setHandles(array $handles) {
|
||||||
$this->handles = $handles;
|
$this->handles = $handles;
|
||||||
|
@ -43,12 +44,22 @@ final class PhortuneOrderTableView extends AphrontView {
|
||||||
return $this->noDataString;
|
return $this->noDataString;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function setIsMerchantView($is_merchant_view) {
|
||||||
|
$this->isMerchantView = $is_merchant_view;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getIsMerchantView() {
|
||||||
|
return $this->isMerchantView;
|
||||||
|
}
|
||||||
|
|
||||||
public function render() {
|
public function render() {
|
||||||
$carts = $this->getCarts();
|
$carts = $this->getCarts();
|
||||||
$handles = $this->getHandles();
|
$handles = $this->getHandles();
|
||||||
$viewer = $this->getUser();
|
$viewer = $this->getUser();
|
||||||
|
|
||||||
$is_invoices = $this->getIsInvoices();
|
$is_invoices = $this->getIsInvoices();
|
||||||
|
$is_merchant = $this->getIsMerchantView();
|
||||||
|
|
||||||
$rows = array();
|
$rows = array();
|
||||||
$rowc = array();
|
$rowc = array();
|
||||||
|
@ -138,7 +149,7 @@ final class PhortuneOrderTableView extends AphrontView {
|
||||||
'',
|
'',
|
||||||
'right',
|
'right',
|
||||||
'right',
|
'right',
|
||||||
'',
|
'action',
|
||||||
))
|
))
|
||||||
->setColumnVisibility(
|
->setColumnVisibility(
|
||||||
array(
|
array(
|
||||||
|
@ -150,7 +161,10 @@ final class PhortuneOrderTableView extends AphrontView {
|
||||||
!$is_invoices,
|
!$is_invoices,
|
||||||
!$is_invoices,
|
!$is_invoices,
|
||||||
$is_invoices,
|
$is_invoices,
|
||||||
$is_invoices,
|
|
||||||
|
// We show "Pay Now" for due invoices, but not if the viewer is the
|
||||||
|
// merchant, since it doesn't make sense for them to pay.
|
||||||
|
($is_invoices && !$is_merchant),
|
||||||
));
|
));
|
||||||
|
|
||||||
return $table;
|
return $table;
|
||||||
|
|
|
@ -54,9 +54,69 @@ final class PhortuneSubscriptionWorker extends PhabricatorWorker {
|
||||||
$cart->setSubscriptionPHID($subscription->getPHID());
|
$cart->setSubscriptionPHID($subscription->getPHID());
|
||||||
$cart->activateCart();
|
$cart->activateCart();
|
||||||
|
|
||||||
// TODO: Autocharge this, etc.; this is still mostly faked up.
|
try {
|
||||||
echo 'Okay, made a cart here: ';
|
$issues = $this->chargeSubscription($actor, $subscription, $cart);
|
||||||
echo $cart->getCheckoutURI()."\n\n";
|
} catch (Exception $ex) {
|
||||||
|
$issues = array(
|
||||||
|
pht(
|
||||||
|
'There was a technical error while trying to automatically bill '.
|
||||||
|
'this subscription: %s',
|
||||||
|
$ex),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$issues) {
|
||||||
|
// We're all done; charging the cart sends a billing email as a side
|
||||||
|
// effect.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Send an email telling the user that we weren't able to autopay
|
||||||
|
// so they need to pay this manually.
|
||||||
|
throw new Exception(implode("\n", $issues));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private function chargeSubscription(
|
||||||
|
PhabricatorUser $viewer,
|
||||||
|
PhortuneSubscription $subscription,
|
||||||
|
PhortuneCart $cart) {
|
||||||
|
|
||||||
|
$issues = array();
|
||||||
|
if (!$subscription->getDefaultPaymentMethodPHID()) {
|
||||||
|
$issues[] = pht(
|
||||||
|
'There is no payment method associated with this subscription, so '.
|
||||||
|
'it could not be billed automatically. Add a default payment method '.
|
||||||
|
'to enable automatic billing.');
|
||||||
|
return $issues;
|
||||||
|
}
|
||||||
|
|
||||||
|
$method = id(new PhortunePaymentMethodQuery())
|
||||||
|
->setViewer($viewer)
|
||||||
|
->withPHIDs(array($subscription->getDefaultPaymentMethodPHID()))
|
||||||
|
->executeOne();
|
||||||
|
if (!$method) {
|
||||||
|
$issues[] = pht(
|
||||||
|
'The payment method associated with this subscription is invalid '.
|
||||||
|
'or out of date, so it could not be automatically billed. Update '.
|
||||||
|
'the default payment method to enable automatic billing.');
|
||||||
|
return $issues;
|
||||||
|
}
|
||||||
|
|
||||||
|
$provider = $method->buildPaymentProvider();
|
||||||
|
$charge = $cart->willApplyCharge($viewer, $provider, $method);
|
||||||
|
|
||||||
|
try {
|
||||||
|
$provider->applyCharge($method, $charge);
|
||||||
|
} catch (Exception $ex) {
|
||||||
|
$cart->didFailCharge($charge);
|
||||||
|
$issues[] = pht(
|
||||||
|
'Automatic billing failed: %s',
|
||||||
|
$ex->getMessage());
|
||||||
|
return $issues;
|
||||||
|
}
|
||||||
|
|
||||||
|
$cart->didApplyCharge($charge);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue