mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-26 00:32:42 +01:00
Adjust payment workflows to deal with merchants and configurable providers in Phortune
Summary: Ref T2787. Builds on D10649 by rebining existing objects (carts, charges, etc) to merchantPHIDs and providerPHIDs instead of an implicit global merchant and weird global artifacts (providerType / providerKey). Basically: - When you create something that users can pay for, you specify a merchant to control where the payment goes. - Accounts are install-wide, but payment methods are bound to merchants. This seems to do a reasonable job of balancing usability and technical concerns. - Replace a bunch of weird links between objects with standard PHIDs. - Improve "add payment method" flow. Test Plan: Went through the Fund flow with Stripe and WePay, funding an initiative. Reviewers: btrahan Reviewed By: btrahan Subscribers: epriestley Maniphest Tasks: T2787 Differential Revision: https://secure.phabricator.com/D10652
This commit is contained in:
parent
9aa5a8cb7b
commit
9c4b8a0fb2
21 changed files with 403 additions and 78 deletions
2
resources/sql/autopatches/20141007.fundmerchant.sql
Normal file
2
resources/sql/autopatches/20141007.fundmerchant.sql
Normal file
|
@ -0,0 +1,2 @@
|
|||
ALTER TABLE {$NAMESPACE}_fund.fund_initiative
|
||||
ADD merchantPHID VARBINARY(64);
|
|
@ -0,0 +1,5 @@
|
|||
ALTER TABLE {$NAMESPACE}_phortune.phortune_cart
|
||||
ADD merchantPHID VARBINARY(64) NOT NULL;
|
||||
|
||||
ALTER TABLE {$NAMESPACE}_phortune.phortune_cart
|
||||
ADD KEY `key_merchant` (merchantPHID);
|
16
resources/sql/autopatches/20141007.phortunecharge.sql
Normal file
16
resources/sql/autopatches/20141007.phortunecharge.sql
Normal file
|
@ -0,0 +1,16 @@
|
|||
TRUNCATE TABLE {$NAMESPACE}_phortune.phortune_charge;
|
||||
|
||||
ALTER TABLE {$NAMESPACE}_phortune.phortune_charge
|
||||
DROP paymentProviderKey;
|
||||
|
||||
ALTER TABLE {$NAMESPACE}_phortune.phortune_charge
|
||||
ADD merchantPHID VARBINARY(64) NOT NULL;
|
||||
|
||||
ALTER TABLE {$NAMESPACE}_phortune.phortune_charge
|
||||
ADD providerPHID VARBINARY(64) NOT NULL;
|
||||
|
||||
ALTER TABLE {$NAMESPACE}_phortune.phortune_charge
|
||||
ADD KEY `key_merchant` (merchantPHID);
|
||||
|
||||
ALTER TABLE {$NAMESPACE}_phortune.phortune_charge
|
||||
ADD KEY `key_provider` (providerPHID);
|
16
resources/sql/autopatches/20141007.phortunepayment.sql
Normal file
16
resources/sql/autopatches/20141007.phortunepayment.sql
Normal file
|
@ -0,0 +1,16 @@
|
|||
TRUNCATE TABLE {$NAMESPACE}_phortune.phortune_paymentmethod;
|
||||
|
||||
ALTER TABLE {$NAMESPACE}_phortune.phortune_paymentmethod
|
||||
DROP providerType;
|
||||
|
||||
ALTER TABLE {$NAMESPACE}_phortune.phortune_paymentmethod
|
||||
DROP providerDomain;
|
||||
|
||||
ALTER TABLE {$NAMESPACE}_phortune.phortune_paymentmethod
|
||||
ADD merchantPHID VARBINARY(64) NOT NULL;
|
||||
|
||||
ALTER TABLE {$NAMESPACE}_phortune.phortune_paymentmethod
|
||||
ADD providerPHID VARBINARY(64) NOT NULL;
|
||||
|
||||
ALTER TABLE {$NAMESPACE}_phortune.phortune_paymentmethod
|
||||
ADD KEY `key_merchant` (merchantPHID, accountPHID);
|
|
@ -21,6 +21,14 @@ final class FundInitiativeBackController
|
|||
return new Aphront404Response();
|
||||
}
|
||||
|
||||
$merchant = id(new PhortuneMerchantQuery())
|
||||
->setViewer($viewer)
|
||||
->withPHIDs(array($initiative->getMerchantPHID()))
|
||||
->executeOne();
|
||||
if (!$merchant) {
|
||||
return new Aphront404Response();
|
||||
}
|
||||
|
||||
$initiative_uri = '/'.$initiative->getMonogram();
|
||||
|
||||
if ($initiative->isClosed()) {
|
||||
|
@ -73,7 +81,7 @@ final class FundInitiativeBackController
|
|||
$cart_implementation = id(new FundBackerCart())
|
||||
->setInitiative($initiative);
|
||||
|
||||
$cart = $account->newCart($viewer, $cart_implementation);
|
||||
$cart = $account->newCart($viewer, $cart_implementation, $merchant);
|
||||
|
||||
$purchase = $cart->newPurchase($viewer, $product);
|
||||
$purchase
|
||||
|
|
|
@ -48,6 +48,9 @@ final class FundInitiativeEditController
|
|||
$e_name = true;
|
||||
$v_name = $initiative->getName();
|
||||
|
||||
$e_merchant = null;
|
||||
$v_merchant = $initiative->getMerchantPHID();
|
||||
|
||||
$v_desc = $initiative->getDescription();
|
||||
|
||||
if ($is_new) {
|
||||
|
@ -65,10 +68,12 @@ final class FundInitiativeEditController
|
|||
$v_desc = $request->getStr('description');
|
||||
$v_view = $request->getStr('viewPolicy');
|
||||
$v_edit = $request->getStr('editPolicy');
|
||||
$v_merchant = $request->getStr('merchantPHID');
|
||||
$v_projects = $request->getArr('projects');
|
||||
|
||||
$type_name = FundInitiativeTransaction::TYPE_NAME;
|
||||
$type_desc = FundInitiativeTransaction::TYPE_DESCRIPTION;
|
||||
$type_merchant = FundInitiativeTransaction::TYPE_MERCHANT;
|
||||
$type_view = PhabricatorTransactions::TYPE_VIEW_POLICY;
|
||||
$type_edit = PhabricatorTransactions::TYPE_EDIT_POLICY;
|
||||
|
||||
|
@ -82,6 +87,10 @@ final class FundInitiativeEditController
|
|||
->setTransactionType($type_desc)
|
||||
->setNewValue($v_desc);
|
||||
|
||||
$xactions[] = id(new FundInitiativeTransaction())
|
||||
->setTransactionType($type_merchant)
|
||||
->setNewValue($v_merchant);
|
||||
|
||||
$xactions[] = id(new FundInitiativeTransaction())
|
||||
->setTransactionType($type_view)
|
||||
->setNewValue($v_view);
|
||||
|
@ -110,6 +119,7 @@ final class FundInitiativeEditController
|
|||
$validation_exception = $ex;
|
||||
|
||||
$e_name = $ex->getShortMessage($type_name);
|
||||
$e_merchant = $ex->getShortMessage($type_merchant);
|
||||
|
||||
$initiative->setViewPolicy($v_view);
|
||||
$initiative->setEditPolicy($v_edit);
|
||||
|
@ -127,6 +137,45 @@ final class FundInitiativeEditController
|
|||
$project_handles = array();
|
||||
}
|
||||
|
||||
$merchants = id(new PhortuneMerchantQuery())
|
||||
->setViewer($viewer)
|
||||
->requireCapabilities(
|
||||
array(
|
||||
PhabricatorPolicyCapability::CAN_VIEW,
|
||||
PhabricatorPolicyCapability::CAN_EDIT,
|
||||
))
|
||||
->execute();
|
||||
|
||||
$merchant_options = array();
|
||||
foreach ($merchants as $merchant) {
|
||||
$merchant_options[$merchant->getPHID()] = pht(
|
||||
'Merchant %d %s',
|
||||
$merchant->getID(),
|
||||
$merchant->getName());
|
||||
}
|
||||
|
||||
if ($v_merchant && empty($merchant_options[$v_merchant])) {
|
||||
$merchant_options = array(
|
||||
$v_merchant => pht('(Restricted Merchant)'),
|
||||
) + $merchant_options;
|
||||
}
|
||||
|
||||
if (!$merchant_options) {
|
||||
return $this->newDialog()
|
||||
->setTitle(pht('No Valid Phortune Merchant Accounts'))
|
||||
->appendParagraph(
|
||||
pht(
|
||||
'You do not control any merchant accounts which can receive '.
|
||||
'payments from this initiative. When you create an initiative, '.
|
||||
'you need to specify a merchant account where funds will be paid '.
|
||||
'to.'))
|
||||
->appendParagraph(
|
||||
pht(
|
||||
'Create a merchant account in the Phortune application before '.
|
||||
'creating an initiative in Fund.'))
|
||||
->addCancelButton($this->getApplicationURI());
|
||||
}
|
||||
|
||||
$form = id(new AphrontFormView())
|
||||
->setUser($viewer)
|
||||
->appendChild(
|
||||
|
@ -135,6 +184,13 @@ final class FundInitiativeEditController
|
|||
->setLabel(pht('Name'))
|
||||
->setValue($v_name)
|
||||
->setError($e_name))
|
||||
->appendChild(
|
||||
id(new AphrontFormSelectControl())
|
||||
->setName('merchantPHID')
|
||||
->setLabel(pht('Pay To Merchant'))
|
||||
->setValue($v_merchant)
|
||||
->setError($e_merchant)
|
||||
->setOptions($merchant_options))
|
||||
->appendChild(
|
||||
id(new PhabricatorRemarkupControl())
|
||||
->setName('description')
|
||||
|
|
|
@ -18,6 +18,7 @@ final class FundInitiativeEditor
|
|||
$types[] = FundInitiativeTransaction::TYPE_DESCRIPTION;
|
||||
$types[] = FundInitiativeTransaction::TYPE_STATUS;
|
||||
$types[] = FundInitiativeTransaction::TYPE_BACKER;
|
||||
$types[] = FundInitiativeTransaction::TYPE_MERCHANT;
|
||||
$types[] = PhabricatorTransactions::TYPE_VIEW_POLICY;
|
||||
$types[] = PhabricatorTransactions::TYPE_EDIT_POLICY;
|
||||
|
||||
|
@ -36,6 +37,8 @@ final class FundInitiativeEditor
|
|||
return $object->getStatus();
|
||||
case FundInitiativeTransaction::TYPE_BACKER:
|
||||
return null;
|
||||
case FundInitiativeTransaction::TYPE_MERCHANT:
|
||||
return $object->getMerchantPHID();
|
||||
}
|
||||
|
||||
return parent::getCustomTransactionOldValue($object, $xaction);
|
||||
|
@ -50,6 +53,7 @@ final class FundInitiativeEditor
|
|||
case FundInitiativeTransaction::TYPE_DESCRIPTION:
|
||||
case FundInitiativeTransaction::TYPE_STATUS:
|
||||
case FundInitiativeTransaction::TYPE_BACKER:
|
||||
case FundInitiativeTransaction::TYPE_MERCHANT:
|
||||
return $xaction->getNewValue();
|
||||
}
|
||||
|
||||
|
@ -67,6 +71,9 @@ final class FundInitiativeEditor
|
|||
case FundInitiativeTransaction::TYPE_DESCRIPTION:
|
||||
$object->setDescription($xaction->getNewValue());
|
||||
return;
|
||||
case FundInitiativeTransaction::TYPE_MERCHANT:
|
||||
$object->setMerchantPHID($xaction->getNewValue());
|
||||
return;
|
||||
case FundInitiativeTransaction::TYPE_STATUS:
|
||||
$object->setStatus($xaction->getNewValue());
|
||||
return;
|
||||
|
@ -89,6 +96,7 @@ final class FundInitiativeEditor
|
|||
case FundInitiativeTransaction::TYPE_NAME:
|
||||
case FundInitiativeTransaction::TYPE_DESCRIPTION:
|
||||
case FundInitiativeTransaction::TYPE_STATUS:
|
||||
case FundInitiativeTransaction::TYPE_MERCHANT:
|
||||
case FundInitiativeTransaction::TYPE_BACKER:
|
||||
// TODO: Maybe we should apply the backer transaction from here?
|
||||
return;
|
||||
|
@ -124,6 +132,46 @@ final class FundInitiativeEditor
|
|||
$errors[] = $error;
|
||||
}
|
||||
break;
|
||||
case FundInitiativeTransaction::TYPE_MERCHANT:
|
||||
$missing = $this->validateIsEmptyTextField(
|
||||
$object->getName(),
|
||||
$xactions);
|
||||
if ($missing) {
|
||||
$error = new PhabricatorApplicationTransactionValidationError(
|
||||
$type,
|
||||
pht('Required'),
|
||||
pht('Payable merchant is required.'),
|
||||
nonempty(last($xactions), null));
|
||||
|
||||
$error->setIsMissingFieldError(true);
|
||||
$errors[] = $error;
|
||||
} else if ($xactions) {
|
||||
$merchant_phid = last($xactions)->getNewValue();
|
||||
|
||||
// Make sure the actor has permission to edit the merchant they're
|
||||
// selecting. You aren't allowed to send payments to an account you
|
||||
// do not control.
|
||||
$merchants = id(new PhortuneMerchantQuery())
|
||||
->setViewer($this->requireActor())
|
||||
->withPHIDs(array($merchant_phid))
|
||||
->requireCapabilities(
|
||||
array(
|
||||
PhabricatorPolicyCapability::CAN_VIEW,
|
||||
PhabricatorPolicyCapability::CAN_EDIT,
|
||||
))
|
||||
->execute();
|
||||
if (!$merchants) {
|
||||
$error = new PhabricatorApplicationTransactionValidationError(
|
||||
$type,
|
||||
pht('Invalid'),
|
||||
pht(
|
||||
'You must specify a merchant account you control as the '.
|
||||
'recipient of funds from this initiative.'),
|
||||
last($xactions));
|
||||
$errors[] = $error;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return $errors;
|
||||
|
|
|
@ -13,6 +13,7 @@ final class FundInitiative extends FundDAO
|
|||
|
||||
protected $name;
|
||||
protected $ownerPHID;
|
||||
protected $merchantPHID;
|
||||
protected $description;
|
||||
protected $viewPolicy;
|
||||
protected $editPolicy;
|
||||
|
@ -52,6 +53,7 @@ final class FundInitiative extends FundDAO
|
|||
'name' => 'text255',
|
||||
'description' => 'text',
|
||||
'status' => 'text32',
|
||||
'merchantPHID' => 'phid?',
|
||||
),
|
||||
self::CONFIG_KEY_SCHEMA => array(
|
||||
'key_status' => array(
|
||||
|
|
|
@ -7,6 +7,7 @@ final class FundInitiativeTransaction
|
|||
const TYPE_DESCRIPTION = 'fund:description';
|
||||
const TYPE_STATUS = 'fund:status';
|
||||
const TYPE_BACKER = 'fund:backer';
|
||||
const TYPE_MERCHANT = 'fund:merchant';
|
||||
|
||||
public function getApplicationName() {
|
||||
return 'fund';
|
||||
|
@ -20,6 +21,27 @@ final class FundInitiativeTransaction
|
|||
return null;
|
||||
}
|
||||
|
||||
public function getRequiredHandlePHIDs() {
|
||||
$phids = parent::getRequiredHandlePHIDs();
|
||||
|
||||
$old = $this->getOldValue();
|
||||
$new = $this->getNewValue();
|
||||
|
||||
$type = $this->getTransactionType();
|
||||
switch ($type) {
|
||||
case FundInitiativeTransaction::TYPE_MERCHANT:
|
||||
if ($old) {
|
||||
$phids[] = $old;
|
||||
}
|
||||
if ($new) {
|
||||
$phids[] = $new;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return $phids;
|
||||
}
|
||||
|
||||
public function getTitle() {
|
||||
$author_phid = $this->getAuthorPHID();
|
||||
$object_phid = $this->getObjectPHID();
|
||||
|
@ -62,6 +84,20 @@ final class FundInitiativeTransaction
|
|||
return pht(
|
||||
'%s backed this initiative.',
|
||||
$this->renderHandleLink($author_phid));
|
||||
case FundInitiativeTransaction::TYPE_MERCHANT:
|
||||
if ($old === null) {
|
||||
return pht(
|
||||
'%s set this initiative to pay to %s.',
|
||||
$this->renderHandleLink($author_phid),
|
||||
$this->renderHandleLink($new));
|
||||
} else {
|
||||
return pht(
|
||||
'%s changed the merchant receiving funds from this '.
|
||||
'initiative from %s to %s.',
|
||||
$this->renderHandleLink($author_phid),
|
||||
$this->renderHandleLink($old),
|
||||
$this->renderHandleLink($new));
|
||||
}
|
||||
}
|
||||
|
||||
return parent::getTitle();
|
||||
|
|
|
@ -89,13 +89,7 @@ final class PhortuneAccountViewController extends PhortuneController {
|
|||
$id = $account->getID();
|
||||
|
||||
$header = id(new PHUIHeaderView())
|
||||
->setHeader(pht('Payment Methods'))
|
||||
->addActionLink(
|
||||
id(new PHUIButtonView())
|
||||
->setTag('a')
|
||||
->setHref($this->getApplicationURI($id.'/card/new/'))
|
||||
->setText(pht('Add Payment Method'))
|
||||
->setIcon(id(new PHUIIconView())->setIconFont('fa-plus')));
|
||||
->setHeader(pht('Payment Methods'));
|
||||
|
||||
$list = id(new PHUIObjectItemListView())
|
||||
->setUser($viewer)
|
||||
|
|
|
@ -23,6 +23,7 @@ final class PhortuneCartCheckoutController
|
|||
}
|
||||
|
||||
$cancel_uri = $cart->getCancelURI();
|
||||
$merchant = $cart->getMerchant();
|
||||
|
||||
switch ($cart->getStatus()) {
|
||||
case PhortuneCart::STATUS_BUILDING:
|
||||
|
@ -83,6 +84,7 @@ final class PhortuneCartCheckoutController
|
|||
$methods = id(new PhortunePaymentMethodQuery())
|
||||
->setViewer($viewer)
|
||||
->withAccountPHIDs(array($account->getPHID()))
|
||||
->withMerchantPHIDs(array($merchant->getPHID()))
|
||||
->withStatuses(array(PhortunePaymentMethod::STATUS_ACTIVE))
|
||||
->execute();
|
||||
|
||||
|
@ -142,14 +144,22 @@ final class PhortuneCartCheckoutController
|
|||
|
||||
$method_control->setError($e_method);
|
||||
|
||||
$payment_method_uri = $this->getApplicationURI(
|
||||
$account->getID().'/card/new/');
|
||||
$account_id = $account->getID();
|
||||
|
||||
$payment_method_uri = $this->getApplicationURI("{$account_id}/card/new/");
|
||||
$payment_method_uri = new PhutilURI($payment_method_uri);
|
||||
$payment_method_uri->setQueryParams(
|
||||
array(
|
||||
'merchantID' => $merchant->getID(),
|
||||
'cartID' => $cart->getID(),
|
||||
));
|
||||
|
||||
$form = id(new AphrontFormView())
|
||||
->setUser($viewer)
|
||||
->appendChild($method_control);
|
||||
|
||||
$add_providers = PhortunePaymentProvider::getProvidersForAddPaymentMethod();
|
||||
$add_providers = $this->loadCreatePaymentMethodProvidersForMerchant(
|
||||
$merchant);
|
||||
if ($add_providers) {
|
||||
$new_method = phutil_tag(
|
||||
'a',
|
||||
|
@ -178,7 +188,7 @@ final class PhortuneCartCheckoutController
|
|||
|
||||
$provider_form = null;
|
||||
|
||||
$pay_providers = PhortunePaymentProvider::getProvidersForOneTimePayment();
|
||||
$pay_providers = $this->loadOneTimePaymentProvidersForMerchant($merchant);
|
||||
if ($pay_providers) {
|
||||
$one_time_options = array();
|
||||
foreach ($pay_providers as $provider) {
|
||||
|
|
|
@ -86,4 +86,50 @@ abstract class PhortuneController extends PhabricatorController {
|
|||
}
|
||||
}
|
||||
|
||||
private function loadEnabledProvidersForMerchant(PhortuneMerchant $merchant) {
|
||||
$viewer = $this->getRequest()->getUser();
|
||||
|
||||
$provider_configs = id(new PhortunePaymentProviderConfigQuery())
|
||||
->setViewer($viewer)
|
||||
->withMerchantPHIDs(array($merchant->getPHID()))
|
||||
->execute();
|
||||
$providers = mpull($provider_configs, 'buildProvider', 'getID');
|
||||
|
||||
foreach ($providers as $key => $provider) {
|
||||
if (!$provider->isEnabled()) {
|
||||
unset($providers[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
return $providers;
|
||||
}
|
||||
|
||||
protected function loadCreatePaymentMethodProvidersForMerchant(
|
||||
PhortuneMerchant $merchant) {
|
||||
|
||||
$providers = $this->loadEnabledProvidersForMerchant($merchant);
|
||||
foreach ($providers as $key => $provider) {
|
||||
if (!$provider->canCreatePaymentMethods()) {
|
||||
unset($providers[$key]);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return $providers;
|
||||
}
|
||||
|
||||
protected function loadOneTimePaymentProvidersForMerchant(
|
||||
PhortuneMerchant $merchant) {
|
||||
|
||||
$providers = $this->loadEnabledProvidersForMerchant($merchant);
|
||||
foreach ($providers as $key => $provider) {
|
||||
if (!$provider->canProcessOneTimePayments()) {
|
||||
unset($providers[$key]);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return $providers;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -11,28 +11,36 @@ final class PhortunePaymentMethodCreateController
|
|||
|
||||
public function processRequest() {
|
||||
$request = $this->getRequest();
|
||||
$user = $request->getUser();
|
||||
$viewer = $request->getUser();
|
||||
|
||||
$account = id(new PhortuneAccountQuery())
|
||||
->setViewer($user)
|
||||
->setViewer($viewer)
|
||||
->withIDs(array($this->accountID))
|
||||
->executeOne();
|
||||
if (!$account) {
|
||||
return new Aphront404Response();
|
||||
}
|
||||
|
||||
$merchant = id(new PhortuneMerchantQuery())
|
||||
->setViewer($viewer)
|
||||
->withIDs(array($request->getInt('merchantID')))
|
||||
->executeOne();
|
||||
if (!$merchant) {
|
||||
return new Aphront404Response();
|
||||
}
|
||||
|
||||
$cancel_uri = $this->getApplicationURI($account->getID().'/');
|
||||
$account_uri = $this->getApplicationURI($account->getID().'/');
|
||||
|
||||
$providers = PhortunePaymentProvider::getProvidersForAddPaymentMethod();
|
||||
$providers = $this->loadCreatePaymentMethodProvidersForMerchant($merchant);
|
||||
if (!$providers) {
|
||||
throw new Exception(
|
||||
'There are no payment providers enabled that can add payment '.
|
||||
'methods.');
|
||||
}
|
||||
|
||||
$provider_key = $request->getStr('providerKey');
|
||||
if (empty($providers[$provider_key])) {
|
||||
$provider_id = $request->getInt('providerID');
|
||||
if (empty($providers[$provider_id])) {
|
||||
$choices = array();
|
||||
foreach ($providers as $provider) {
|
||||
$choices[] = $this->renderSelectProvider($provider);
|
||||
|
@ -53,16 +61,16 @@ final class PhortunePaymentMethodCreateController
|
|||
->addCancelButton($account_uri);
|
||||
}
|
||||
|
||||
$provider = $providers[$provider_key];
|
||||
$provider = $providers[$provider_id];
|
||||
|
||||
$errors = array();
|
||||
if ($request->isFormPost() && $request->getBool('isProviderForm')) {
|
||||
$method = id(new PhortunePaymentMethod())
|
||||
->setAccountPHID($account->getPHID())
|
||||
->setAuthorPHID($user->getPHID())
|
||||
->setStatus(PhortunePaymentMethod::STATUS_ACTIVE)
|
||||
->setProviderType($provider->getProviderType())
|
||||
->setProviderDomain($provider->getProviderDomain());
|
||||
->setAuthorPHID($viewer->getPHID())
|
||||
->setMerchantPHID($merchant->getPHID())
|
||||
->setProviderPHID($provider->getProviderConfig()->getPHID())
|
||||
->setStatus(PhortunePaymentMethod::STATUS_ACTIVE);
|
||||
|
||||
if (!$errors) {
|
||||
$errors = $this->processClientErrors(
|
||||
|
@ -97,12 +105,21 @@ final class PhortunePaymentMethodCreateController
|
|||
if (!$errors) {
|
||||
$method->save();
|
||||
|
||||
$save_uri = new PhutilURI($account_uri);
|
||||
$save_uri->setFragment('payment');
|
||||
return id(new AphrontRedirectResponse())->setURI($save_uri);
|
||||
// If we added this method on a cart flow, return to the cart to
|
||||
// check out.
|
||||
$cart_id = $request->getInt('cartID');
|
||||
if ($cart_id) {
|
||||
$next_uri = $this->getApplicationURI(
|
||||
"cart/{$cart_id}/checkout/?paymentMethodID=".$method->getID());
|
||||
} else {
|
||||
$next_uri = new PhutilURI($account_uri);
|
||||
$next_uri->setFragment('payment');
|
||||
}
|
||||
|
||||
return id(new AphrontRedirectResponse())->setURI($next_uri);
|
||||
} else {
|
||||
$dialog = id(new AphrontDialogView())
|
||||
->setUser($user)
|
||||
->setUser($viewer)
|
||||
->setTitle(pht('Error Adding Payment Method'))
|
||||
->appendChild(id(new AphrontErrorView())->setErrors($errors))
|
||||
->addCancelButton($request->getRequestURI());
|
||||
|
@ -114,10 +131,11 @@ final class PhortunePaymentMethodCreateController
|
|||
$form = $provider->renderCreatePaymentMethodForm($request, $errors);
|
||||
|
||||
$form
|
||||
->setUser($user)
|
||||
->setUser($viewer)
|
||||
->setAction($request->getRequestURI())
|
||||
->setWorkflow(true)
|
||||
->addHiddenInput('providerKey', $provider_key)
|
||||
->addHiddenInput('providerID', $provider_id)
|
||||
->addHiddenInput('cartID', $request->getInt('cartID'))
|
||||
->addHiddenInput('isProviderForm', true)
|
||||
->appendChild(
|
||||
id(new AphrontFormSubmitControl())
|
||||
|
@ -145,7 +163,7 @@ final class PhortunePaymentMethodCreateController
|
|||
PhortunePaymentProvider $provider) {
|
||||
|
||||
$request = $this->getRequest();
|
||||
$user = $request->getUser();
|
||||
$viewer = $request->getUser();
|
||||
|
||||
$description = $provider->getPaymentMethodDescription();
|
||||
$icon_uri = $provider->getPaymentMethodIcon();
|
||||
|
@ -165,8 +183,8 @@ final class PhortunePaymentMethodCreateController
|
|||
->setSubtext($details);
|
||||
|
||||
$form = id(new AphrontFormView())
|
||||
->setUser($user)
|
||||
->addHiddenInput('providerKey', $provider->getProviderKey())
|
||||
->setUser($viewer)
|
||||
->addHiddenInput('providerID', $provider->getProviderConfig()->getID())
|
||||
->appendChild($button);
|
||||
|
||||
return $form;
|
||||
|
|
|
@ -30,7 +30,7 @@ final class PhortuneMerchantPHIDType extends PhabricatorPHIDType {
|
|||
|
||||
$id = $merchant->getID();
|
||||
|
||||
$handle->setName(pht('Merchant %d', $id));
|
||||
$handle->setName(pht('Merchant %d %s', $id, $merchant->getName()));
|
||||
$handle->setURI("/phortune/merchant/{$id}/");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -120,36 +120,6 @@ abstract class PhortunePaymentProvider {
|
|||
->loadObjects();
|
||||
}
|
||||
|
||||
public static function getEnabledProviders() {
|
||||
$providers = self::getAllProviders();
|
||||
foreach ($providers as $key => $provider) {
|
||||
if (!$provider->isEnabled()) {
|
||||
unset($providers[$key]);
|
||||
}
|
||||
}
|
||||
return $providers;
|
||||
}
|
||||
|
||||
public static function getProvidersForAddPaymentMethod() {
|
||||
$providers = self::getEnabledProviders();
|
||||
foreach ($providers as $key => $provider) {
|
||||
if (!$provider->canCreatePaymentMethods()) {
|
||||
unset($providers[$key]);
|
||||
}
|
||||
}
|
||||
return $providers;
|
||||
}
|
||||
|
||||
public static function getProvidersForOneTimePayment() {
|
||||
$providers = self::getEnabledProviders();
|
||||
foreach ($providers as $key => $provider) {
|
||||
if (!$provider->canProcessOneTimePayments()) {
|
||||
unset($providers[$key]);
|
||||
}
|
||||
}
|
||||
return $providers;
|
||||
}
|
||||
|
||||
abstract public function isEnabled();
|
||||
|
||||
abstract public function getPaymentMethodDescription();
|
||||
|
|
|
@ -66,6 +66,21 @@ final class PhortuneCartQuery
|
|||
$cart->attachAccount($account);
|
||||
}
|
||||
|
||||
$merchants = id(new PhortuneMerchantQuery())
|
||||
->setViewer($this->getViewer())
|
||||
->withPHIDs(mpull($carts, 'getMerchantPHID'))
|
||||
->execute();
|
||||
$merchants = mpull($merchants, null, 'getPHID');
|
||||
|
||||
foreach ($carts as $key => $cart) {
|
||||
$merchant = idx($merchants, $cart->getMerchantPHID());
|
||||
if (!$merchant) {
|
||||
unset($carts[$key]);
|
||||
continue;
|
||||
}
|
||||
$cart->attachMerchant($merchant);
|
||||
}
|
||||
|
||||
$implementations = array();
|
||||
|
||||
$cart_map = mgroup($carts, 'getCartClass');
|
||||
|
|
|
@ -6,6 +6,7 @@ final class PhortunePaymentMethodQuery
|
|||
private $ids;
|
||||
private $phids;
|
||||
private $accountPHIDs;
|
||||
private $merchantPHIDs;
|
||||
private $statuses;
|
||||
|
||||
public function withIDs(array $ids) {
|
||||
|
@ -23,6 +24,11 @@ final class PhortunePaymentMethodQuery
|
|||
return $this;
|
||||
}
|
||||
|
||||
public function withMerchantPHIDs(array $phids) {
|
||||
$this->merchantPHIDs = $phids;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withStatuses(array $statuses) {
|
||||
$this->statuses = $statuses;
|
||||
return $this;
|
||||
|
@ -44,15 +50,6 @@ final class PhortunePaymentMethodQuery
|
|||
}
|
||||
|
||||
protected function willFilterPage(array $methods) {
|
||||
foreach ($methods as $key => $method) {
|
||||
try {
|
||||
$method->buildPaymentProvider();
|
||||
} catch (Exception $ex) {
|
||||
unset($methods[$key]);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
$accounts = id(new PhortuneAccountQuery())
|
||||
->setViewer($this->getViewer())
|
||||
->withPHIDs(mpull($methods, 'getAccountPHID'))
|
||||
|
@ -68,6 +65,36 @@ final class PhortunePaymentMethodQuery
|
|||
$method->attachAccount($account);
|
||||
}
|
||||
|
||||
$merchants = id(new PhortuneMerchantQuery())
|
||||
->setViewer($this->getViewer())
|
||||
->withPHIDs(mpull($methods, 'getMerchantPHID'))
|
||||
->execute();
|
||||
$merchants = mpull($merchants, null, 'getPHID');
|
||||
|
||||
foreach ($methods as $key => $method) {
|
||||
$merchant = idx($merchants, $method->getMerchantPHID());
|
||||
if (!$merchant) {
|
||||
unset($methods[$key]);
|
||||
continue;
|
||||
}
|
||||
$method->attachMerchant($merchant);
|
||||
}
|
||||
|
||||
$provider_configs = id(new PhortunePaymentProviderConfigQuery())
|
||||
->setViewer($this->getViewer())
|
||||
->withPHIDs(mpull($methods, 'getProviderPHID'))
|
||||
->execute();
|
||||
$provider_configs = mpull($provider_configs, null, 'getPHID');
|
||||
|
||||
foreach ($methods as $key => $method) {
|
||||
$provider_config = idx($provider_configs, $method->getProviderPHID());
|
||||
if (!$provider_config) {
|
||||
unset($methods[$key]);
|
||||
continue;
|
||||
}
|
||||
$method->attachProviderConfig($provider_config);
|
||||
}
|
||||
|
||||
return $methods;
|
||||
}
|
||||
|
||||
|
@ -95,6 +122,13 @@ final class PhortunePaymentMethodQuery
|
|||
$this->accountPHIDs);
|
||||
}
|
||||
|
||||
if ($this->merchantPHIDs !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn,
|
||||
'merchantPHID IN (%Ls)',
|
||||
$this->merchantPHIDs);
|
||||
}
|
||||
|
||||
if ($this->statuses !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn,
|
||||
|
|
|
@ -58,9 +58,10 @@ final class PhortuneAccount extends PhortuneDAO
|
|||
|
||||
public function newCart(
|
||||
PhabricatorUser $actor,
|
||||
PhortuneCartImplementation $implementation) {
|
||||
PhortuneCartImplementation $implementation,
|
||||
PhortuneMerchant $merchant) {
|
||||
|
||||
$cart = PhortuneCart::initializeNewCart($actor, $this);
|
||||
$cart = PhortuneCart::initializeNewCart($actor, $this, $merchant);
|
||||
|
||||
$cart->setCartClass(get_class($implementation));
|
||||
$cart->attachImplementation($implementation);
|
||||
|
|
|
@ -11,6 +11,7 @@ final class PhortuneCart extends PhortuneDAO
|
|||
|
||||
protected $accountPHID;
|
||||
protected $authorPHID;
|
||||
protected $merchantPHID;
|
||||
protected $cartClass;
|
||||
protected $status;
|
||||
protected $metadata = array();
|
||||
|
@ -18,14 +19,17 @@ final class PhortuneCart extends PhortuneDAO
|
|||
private $account = self::ATTACHABLE;
|
||||
private $purchases = self::ATTACHABLE;
|
||||
private $implementation = self::ATTACHABLE;
|
||||
private $merchant = self::ATTACHABLE;
|
||||
|
||||
public static function initializeNewCart(
|
||||
PhabricatorUser $actor,
|
||||
PhortuneAccount $account) {
|
||||
PhortuneAccount $account,
|
||||
PhortuneMerchant $merchant) {
|
||||
$cart = id(new PhortuneCart())
|
||||
->setAuthorPHID($actor->getPHID())
|
||||
->setStatus(self::STATUS_BUILDING)
|
||||
->setAccountPHID($account->getPHID());
|
||||
->setAccountPHID($account->getPHID())
|
||||
->setMerchantPHID($merchant->getPHID());
|
||||
|
||||
$cart->account = $account;
|
||||
$cart->purchases = array();
|
||||
|
@ -63,7 +67,8 @@ final class PhortuneCart extends PhortuneDAO
|
|||
->setAccountPHID($account->getPHID())
|
||||
->setCartPHID($this->getPHID())
|
||||
->setAuthorPHID($actor->getPHID())
|
||||
->setPaymentProviderKey($provider->getProviderKey())
|
||||
->setMerchantPHID($this->getMerchant()->getPHID())
|
||||
->setProviderPHID($provider->getProviderConfig()->getPHID())
|
||||
->setAmountAsCurrency($this->getTotalPriceAsCurrency());
|
||||
|
||||
if ($method) {
|
||||
|
@ -154,6 +159,9 @@ final class PhortuneCart extends PhortuneDAO
|
|||
'key_account' => array(
|
||||
'columns' => array('accountPHID'),
|
||||
),
|
||||
'key_merchant' => array(
|
||||
'columns' => array('merchantPHID'),
|
||||
),
|
||||
),
|
||||
) + parent::getConfiguration();
|
||||
}
|
||||
|
@ -182,6 +190,15 @@ final class PhortuneCart extends PhortuneDAO
|
|||
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 attachImplementation(
|
||||
PhortuneCartImplementation $implementation) {
|
||||
$this->implementation = $implementation;
|
||||
|
|
|
@ -16,7 +16,8 @@ final class PhortuneCharge extends PhortuneDAO
|
|||
protected $accountPHID;
|
||||
protected $authorPHID;
|
||||
protected $cartPHID;
|
||||
protected $paymentProviderKey;
|
||||
protected $providerPHID;
|
||||
protected $merchantPHID;
|
||||
protected $paymentMethodPHID;
|
||||
protected $amountAsCurrency;
|
||||
protected $status;
|
||||
|
@ -52,6 +53,12 @@ final class PhortuneCharge extends PhortuneDAO
|
|||
'key_account' => array(
|
||||
'columns' => array('accountPHID'),
|
||||
),
|
||||
'key_merchant' => array(
|
||||
'columns' => array('merchantPHID'),
|
||||
),
|
||||
'key_provider' => array(
|
||||
'columns' => array('providerPHID'),
|
||||
),
|
||||
),
|
||||
) + parent::getConfiguration();
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ final class PhortunePaymentMethod extends PhortuneDAO
|
|||
protected $status;
|
||||
protected $accountPHID;
|
||||
protected $authorPHID;
|
||||
protected $merchantPHID;
|
||||
protected $providerPHID;
|
||||
protected $expires;
|
||||
protected $metadata = array();
|
||||
|
@ -21,6 +22,8 @@ final class PhortunePaymentMethod extends PhortuneDAO
|
|||
protected $lastFourDigits;
|
||||
|
||||
private $account = self::ATTACHABLE;
|
||||
private $merchant = self::ATTACHABLE;
|
||||
private $providerConfig = self::ATTACHABLE;
|
||||
|
||||
public function getConfiguration() {
|
||||
return array(
|
||||
|
@ -39,6 +42,9 @@ final class PhortunePaymentMethod extends PhortuneDAO
|
|||
'key_account' => array(
|
||||
'columns' => array('accountPHID', 'status'),
|
||||
),
|
||||
'key_merchant' => array(
|
||||
'columns' => array('merchantPHID', 'accountPHID'),
|
||||
),
|
||||
),
|
||||
) + parent::getConfiguration();
|
||||
}
|
||||
|
@ -57,6 +63,24 @@ final class PhortunePaymentMethod extends PhortuneDAO
|
|||
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 attachProviderConfig(PhortunePaymentProviderConfig $config) {
|
||||
$this->providerConfig = $config;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getProviderConfig() {
|
||||
return $this->assertAttached($this->providerConfig);
|
||||
}
|
||||
|
||||
public function getDescription() {
|
||||
$provider = $this->buildPaymentProvider();
|
||||
return $provider->getPaymentMethodProviderDescription();
|
||||
|
@ -72,7 +96,7 @@ final class PhortunePaymentMethod extends PhortuneDAO
|
|||
}
|
||||
|
||||
public function buildPaymentProvider() {
|
||||
throw new Exception(pht('TODO: Reimplement this junk.'));
|
||||
return $this->getProviderConfig()->buildProvider();
|
||||
}
|
||||
|
||||
public function getDisplayName() {
|
||||
|
|
Loading…
Reference in a new issue