1
0
Fork 0
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:
epriestley 2014-10-07 14:41:59 -07:00
parent 9aa5a8cb7b
commit 9c4b8a0fb2
21 changed files with 403 additions and 78 deletions

View file

@ -0,0 +1,2 @@
ALTER TABLE {$NAMESPACE}_fund.fund_initiative
ADD merchantPHID VARBINARY(64);

View file

@ -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);

View 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);

View 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);

View file

@ -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

View file

@ -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')

View file

@ -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;

View file

@ -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(

View file

@ -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();

View file

@ -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)

View file

@ -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) {

View file

@ -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;
}
}

View file

@ -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;

View file

@ -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}/");
}
}

View file

@ -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();

View file

@ -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');

View file

@ -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,

View file

@ -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);

View file

@ -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;

View file

@ -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();
}

View file

@ -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() {