Page MenuHomePhabricator

D10649.id25584.diff
No OneTemporary

D10649.id25584.diff

diff --git a/resources/sql/autopatches/20141007.phortuneprovider.sql b/resources/sql/autopatches/20141007.phortuneprovider.sql
new file mode 100644
--- /dev/null
+++ b/resources/sql/autopatches/20141007.phortuneprovider.sql
@@ -0,0 +1,12 @@
+CREATE TABLE {$NAMESPACE}_phortune.phortune_paymentproviderconfig (
+ id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
+ phid VARCHAR(64) NOT NULL COLLATE utf8_bin,
+ merchantPHID VARCHAR(64) NOT NULL COLLATE utf8_bin,
+ providerClassKey BINARY(12) NOT NULL,
+ providerClass VARCHAR(128) NOT NULL COLLATE utf8_bin,
+ metadata LONGTEXT NOT NULL COLLATE utf8_bin,
+ dateCreated INT UNSIGNED NOT NULL,
+ dateModified INT UNSIGNED NOT NULL,
+ UNIQUE KEY `key_phid` (phid),
+ UNIQUE KEY `key_merchant` (merchantPHID, providerClassKey)
+) ENGINE=InnoDB, COLLATE=utf8_bin;
diff --git a/resources/sql/autopatches/20141007.phortuneproviderx.sql b/resources/sql/autopatches/20141007.phortuneproviderx.sql
new file mode 100644
--- /dev/null
+++ b/resources/sql/autopatches/20141007.phortuneproviderx.sql
@@ -0,0 +1,19 @@
+CREATE TABLE {$NAMESPACE}_phortune.phortune_paymentproviderconfigtransaction (
+ id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
+ phid VARCHAR(64) COLLATE utf8_bin NOT NULL,
+ authorPHID VARCHAR(64) COLLATE utf8_bin NOT NULL,
+ objectPHID VARCHAR(64) COLLATE utf8_bin NOT NULL,
+ viewPolicy VARCHAR(64) COLLATE utf8_bin NOT NULL,
+ editPolicy VARCHAR(64) COLLATE utf8_bin NOT NULL,
+ commentPHID VARCHAR(64) COLLATE utf8_bin DEFAULT NULL,
+ commentVersion INT UNSIGNED NOT NULL,
+ transactionType VARCHAR(32) COLLATE utf8_bin NOT NULL,
+ oldValue LONGTEXT COLLATE utf8_bin NOT NULL,
+ newValue LONGTEXT COLLATE utf8_bin NOT NULL,
+ contentSource LONGTEXT COLLATE utf8_bin NOT NULL,
+ metadata LONGTEXT COLLATE utf8_bin NOT NULL,
+ dateCreated INT UNSIGNED NOT NULL,
+ dateModified INT UNSIGNED NOT NULL,
+ UNIQUE KEY `key_phid` (`phid`),
+ KEY `key_object` (`objectPHID`)
+) ENGINE=InnoDB, COLLATE utf8_general_ci;
diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -1966,7 +1966,6 @@
'PhabricatorPholioConfigOptions' => 'applications/pholio/config/PhabricatorPholioConfigOptions.php',
'PhabricatorPholioMockTestDataGenerator' => 'applications/pholio/lipsum/PhabricatorPholioMockTestDataGenerator.php',
'PhabricatorPhortuneApplication' => 'applications/phortune/application/PhabricatorPhortuneApplication.php',
- 'PhabricatorPhortuneConfigOptions' => 'applications/phortune/option/PhabricatorPhortuneConfigOptions.php',
'PhabricatorPhragmentApplication' => 'applications/phragment/application/PhabricatorPhragmentApplication.php',
'PhabricatorPhrequentApplication' => 'applications/phrequent/application/PhabricatorPhrequentApplication.php',
'PhabricatorPhrequentConfigOptions' => 'applications/phrequent/config/PhabricatorPhrequentConfigOptions.php',
@@ -2589,6 +2588,7 @@
'PhortuneMultiplePaymentProvidersException' => 'applications/phortune/exception/PhortuneMultiplePaymentProvidersException.php',
'PhortuneNoPaymentProviderException' => 'applications/phortune/exception/PhortuneNoPaymentProviderException.php',
'PhortuneNotImplementedException' => 'applications/phortune/exception/PhortuneNotImplementedException.php',
+ 'PhortunePayPalPaymentProvider' => 'applications/phortune/provider/PhortunePayPalPaymentProvider.php',
'PhortunePaymentMethod' => 'applications/phortune/storage/PhortunePaymentMethod.php',
'PhortunePaymentMethodCreateController' => 'applications/phortune/controller/PhortunePaymentMethodCreateController.php',
'PhortunePaymentMethodDisableController' => 'applications/phortune/controller/PhortunePaymentMethodDisableController.php',
@@ -2596,22 +2596,26 @@
'PhortunePaymentMethodPHIDType' => 'applications/phortune/phid/PhortunePaymentMethodPHIDType.php',
'PhortunePaymentMethodQuery' => 'applications/phortune/query/PhortunePaymentMethodQuery.php',
'PhortunePaymentProvider' => 'applications/phortune/provider/PhortunePaymentProvider.php',
- 'PhortunePaymentProviderTestCase' => 'applications/phortune/provider/__tests__/PhortunePaymentProviderTestCase.php',
- 'PhortunePaypalPaymentProvider' => 'applications/phortune/provider/PhortunePaypalPaymentProvider.php',
+ 'PhortunePaymentProviderConfig' => 'applications/phortune/storage/PhortunePaymentProviderConfig.php',
+ 'PhortunePaymentProviderConfigEditor' => 'applications/phortune/editor/PhortunePaymentProviderConfigEditor.php',
+ 'PhortunePaymentProviderConfigQuery' => 'applications/phortune/query/PhortunePaymentProviderConfigQuery.php',
+ 'PhortunePaymentProviderConfigTransaction' => 'applications/phortune/storage/PhortunePaymentProviderConfigTransaction.php',
+ 'PhortunePaymentProviderConfigTransactionQuery' => 'applications/phortune/query/PhortunePaymentProviderConfigTransactionQuery.php',
+ 'PhortunePaymentProviderPHIDType' => 'applications/phortune/phid/PhortunePaymentProviderPHIDType.php',
'PhortuneProduct' => 'applications/phortune/storage/PhortuneProduct.php',
'PhortuneProductImplementation' => 'applications/phortune/product/PhortuneProductImplementation.php',
'PhortuneProductListController' => 'applications/phortune/controller/PhortuneProductListController.php',
'PhortuneProductPHIDType' => 'applications/phortune/phid/PhortuneProductPHIDType.php',
'PhortuneProductQuery' => 'applications/phortune/query/PhortuneProductQuery.php',
'PhortuneProductViewController' => 'applications/phortune/controller/PhortuneProductViewController.php',
- 'PhortuneProviderController' => 'applications/phortune/controller/PhortuneProviderController.php',
+ 'PhortuneProviderActionController' => 'applications/phortune/controller/PhortuneProviderActionController.php',
+ 'PhortuneProviderEditController' => 'applications/phortune/controller/PhortuneProviderEditController.php',
'PhortunePurchase' => 'applications/phortune/storage/PhortunePurchase.php',
'PhortunePurchasePHIDType' => 'applications/phortune/phid/PhortunePurchasePHIDType.php',
'PhortunePurchaseQuery' => 'applications/phortune/query/PhortunePurchaseQuery.php',
'PhortunePurchaseViewController' => 'applications/phortune/controller/PhortunePurchaseViewController.php',
'PhortuneSchemaSpec' => 'applications/phortune/storage/PhortuneSchemaSpec.php',
'PhortuneStripePaymentProvider' => 'applications/phortune/provider/PhortuneStripePaymentProvider.php',
- 'PhortuneTestExtraPaymentProvider' => 'applications/phortune/provider/__tests__/PhortuneTestExtraPaymentProvider.php',
'PhortuneTestPaymentProvider' => 'applications/phortune/provider/PhortuneTestPaymentProvider.php',
'PhortuneWePayPaymentProvider' => 'applications/phortune/provider/PhortuneWePayPaymentProvider.php',
'PhragmentBrowseController' => 'applications/phragment/controller/PhragmentBrowseController.php',
@@ -4935,7 +4939,6 @@
'PhabricatorPholioConfigOptions' => 'PhabricatorApplicationConfigOptions',
'PhabricatorPholioMockTestDataGenerator' => 'PhabricatorTestDataGenerator',
'PhabricatorPhortuneApplication' => 'PhabricatorApplication',
- 'PhabricatorPhortuneConfigOptions' => 'PhabricatorApplicationConfigOptions',
'PhabricatorPhragmentApplication' => 'PhabricatorApplication',
'PhabricatorPhrequentApplication' => 'PhabricatorApplication',
'PhabricatorPhrequentConfigOptions' => 'PhabricatorApplicationConfigOptions',
@@ -5639,6 +5642,7 @@
'PhortuneMultiplePaymentProvidersException' => 'Exception',
'PhortuneNoPaymentProviderException' => 'Exception',
'PhortuneNotImplementedException' => 'Exception',
+ 'PhortunePayPalPaymentProvider' => 'PhortunePaymentProvider',
'PhortunePaymentMethod' => array(
'PhortuneDAO',
'PhabricatorPolicyInterface',
@@ -5648,8 +5652,15 @@
'PhortunePaymentMethodEditController' => 'PhortuneController',
'PhortunePaymentMethodPHIDType' => 'PhabricatorPHIDType',
'PhortunePaymentMethodQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
- 'PhortunePaymentProviderTestCase' => 'PhabricatorTestCase',
- 'PhortunePaypalPaymentProvider' => 'PhortunePaymentProvider',
+ 'PhortunePaymentProviderConfig' => array(
+ 'PhortuneDAO',
+ 'PhabricatorPolicyInterface',
+ ),
+ 'PhortunePaymentProviderConfigEditor' => 'PhabricatorApplicationTransactionEditor',
+ 'PhortunePaymentProviderConfigQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
+ 'PhortunePaymentProviderConfigTransaction' => 'PhabricatorApplicationTransaction',
+ 'PhortunePaymentProviderConfigTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
+ 'PhortunePaymentProviderPHIDType' => 'PhabricatorPHIDType',
'PhortuneProduct' => array(
'PhortuneDAO',
'PhabricatorPolicyInterface',
@@ -5658,7 +5669,8 @@
'PhortuneProductPHIDType' => 'PhabricatorPHIDType',
'PhortuneProductQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'PhortuneProductViewController' => 'PhortuneController',
- 'PhortuneProviderController' => 'PhortuneController',
+ 'PhortuneProviderActionController' => 'PhortuneController',
+ 'PhortuneProviderEditController' => 'PhortuneMerchantController',
'PhortunePurchase' => array(
'PhortuneDAO',
'PhabricatorPolicyInterface',
@@ -5668,7 +5680,6 @@
'PhortunePurchaseViewController' => 'PhortuneController',
'PhortuneSchemaSpec' => 'PhabricatorConfigSchemaSpec',
'PhortuneStripePaymentProvider' => 'PhortunePaymentProvider',
- 'PhortuneTestExtraPaymentProvider' => 'PhortunePaymentProvider',
'PhortuneTestPaymentProvider' => 'PhortunePaymentProvider',
'PhortuneWePayPaymentProvider' => 'PhortunePaymentProvider',
'PhragmentBrowseController' => 'PhragmentController',
diff --git a/src/applications/phortune/application/PhabricatorPhortuneApplication.php b/src/applications/phortune/application/PhabricatorPhortuneApplication.php
--- a/src/applications/phortune/application/PhabricatorPhortuneApplication.php
+++ b/src/applications/phortune/application/PhabricatorPhortuneApplication.php
@@ -58,8 +58,11 @@
'view/(?P<id>\d+)/' => 'PhortuneProductViewController',
'edit/(?:(?P<id>\d+)/)?' => 'PhortuneProductEditController',
),
- 'provider/(?P<digest>[^/]+)/(?P<action>[^/]+)/'
- => 'PhortuneProviderController',
+ 'provider/' => array(
+ 'edit/(?:(?P<id>\d+)/)?' => 'PhortuneProviderEditController',
+ '(?P<id>\d+)/(?P<action>[^/]+)/'
+ => 'PhortuneProviderActionController',
+ ),
'merchant/' => array(
'(?:query/(?P<queryKey>[^/]+)/)?' => 'PhortuneMerchantListController',
'edit/(?:(?P<id>\d+)/)?' => 'PhortuneMerchantEditController',
diff --git a/src/applications/phortune/controller/PhortuneMerchantViewController.php b/src/applications/phortune/controller/PhortuneMerchantViewController.php
--- a/src/applications/phortune/controller/PhortuneMerchantViewController.php
+++ b/src/applications/phortune/controller/PhortuneMerchantViewController.php
@@ -22,7 +22,7 @@
}
$crumbs = $this->buildApplicationCrumbs();
- $crumbs->addTextCrumb(pht('Merchant %d', $merchant->getID()));
+ $crumbs->addTextCrumb($merchant->getName());
$title = pht(
'Merchant %d %s',
@@ -39,6 +39,8 @@
$actions = $this->buildActionListView($merchant);
$properties->setActionList($actions);
+ $providers = $this->buildProviderList($merchant);
+
$box = id(new PHUIObjectBoxView())
->setHeader($header)
->appendChild($properties);
@@ -57,6 +59,7 @@
array(
$crumbs,
$box,
+ $providers,
$timeline,
),
array(
@@ -98,4 +101,57 @@
return $view;
}
+ private function buildProviderList(PhortuneMerchant $merchant) {
+ $viewer = $this->getRequest()->getUser();
+ $id = $merchant->getID();
+
+ $can_edit = PhabricatorPolicyFilter::hasCapability(
+ $viewer,
+ $merchant,
+ PhabricatorPolicyCapability::CAN_EDIT);
+
+ $provider_list = id(new PHUIObjectItemListView())
+ ->setNoDataString(pht('This merchant has no payment providers.'));
+
+ $providers = id(new PhortunePaymentProviderConfigQuery())
+ ->setViewer($viewer)
+ ->withMerchantPHIDs(array($merchant->getPHID()))
+ ->execute();
+ foreach ($providers as $provider_config) {
+ $provider = $provider_config->buildProvider();
+ $provider_id = $provider_config->getID();
+
+ $item = id(new PHUIObjectItemView())
+ ->setObjectName(pht('Provider %d', $provider_id))
+ ->setHeader($provider->getName());
+
+ $item->addAction(
+ id(new PHUIListItemView())
+ ->setIcon('fa-pencil')
+ ->setHref($this->getApplicationURI("/provider/edit/{$provider_id}"))
+ ->setWorkflow(!$can_edit)
+ ->setDisabled(!$can_edit));
+
+ $provider_list->addItem($item);
+ }
+
+ $add_action = id(new PHUIButtonView())
+ ->setTag('a')
+ ->setHref($this->getApplicationURI('provider/edit/?merchantID='.$id))
+ ->setText(pht('Add Payment Provider'))
+ ->setDisabled(!$can_edit)
+ ->setWorkflow(!$can_edit)
+ ->setIcon(id(new PHUIIconView())->setIconFont('fa-plus'));
+
+ $header = id(new PHUIHeaderView())
+ ->setHeader(pht('Payment Providers'))
+ ->addActionLink($add_action);
+
+ return id(new PHUIObjectBoxView())
+ ->setHeader($header)
+ ->appendChild($provider_list);
+ }
+
+
+
}
diff --git a/src/applications/phortune/controller/PhortuneProviderController.php b/src/applications/phortune/controller/PhortuneProviderActionController.php
rename from src/applications/phortune/controller/PhortuneProviderController.php
rename to src/applications/phortune/controller/PhortuneProviderActionController.php
--- a/src/applications/phortune/controller/PhortuneProviderController.php
+++ b/src/applications/phortune/controller/PhortuneProviderActionController.php
@@ -1,12 +1,12 @@
<?php
-final class PhortuneProviderController extends PhortuneController {
+final class PhortuneProviderActionController extends PhortuneController {
- private $digest;
+ private $id;
private $action;
public function willProcessRequest(array $data) {
- $this->digest = $data['digest'];
+ $this->id = $data['id'];
$this->setAction($data['action']);
}
@@ -21,24 +21,22 @@
public function processRequest() {
$request = $this->getRequest();
- $user = $request->getUser();
-
-
- // NOTE: This use of digests to identify payment providers is because
- // payment provider keys don't necessarily have restrictions on what they
- // contain (so they might have stuff that's not safe to put in URIs), and
- // using digests prevents errors with URI encoding.
+ $viewer = $request->getUser();
- $provider = PhortunePaymentProvider::getProviderByDigest($this->digest);
- if (!$provider) {
- throw new Exception('Invalid payment provider digest!');
+ $provider_config = id(new PhortunePaymentProviderConfigQuery())
+ ->setViewer($viewer)
+ ->withIDs(array($this->id))
+ ->executeOne();
+ if (!$provider_config) {
+ return new Aphront404Response();
}
+ $provider = $provider_config->buildProvider();
+
if (!$provider->canRespondToControllerAction($this->getAction())) {
return new Aphront404Response();
}
-
$response = $provider->processControllerRequest($this, $request);
if ($response instanceof AphrontResponse) {
diff --git a/src/applications/phortune/controller/PhortuneProviderEditController.php b/src/applications/phortune/controller/PhortuneProviderEditController.php
new file mode 100644
--- /dev/null
+++ b/src/applications/phortune/controller/PhortuneProviderEditController.php
@@ -0,0 +1,292 @@
+<?php
+
+final class PhortuneProviderEditController
+ extends PhortuneMerchantController {
+
+ private $id;
+
+ public function willProcessRequest(array $data) {
+ $this->id = idx($data, 'id');
+ }
+
+ public function processRequest() {
+ $request = $this->getRequest();
+ $viewer = $request->getUser();
+
+ if ($this->id) {
+ $provider_config = id(new PhortunePaymentProviderConfigQuery())
+ ->setViewer($viewer)
+ ->withIDs(array($this->id))
+ ->requireCapabilities(
+ array(
+ PhabricatorPolicyCapability::CAN_VIEW,
+ PhabricatorPolicyCapability::CAN_EDIT,
+ ))
+ ->executeOne();
+ if (!$provider_config) {
+ return new Aphront404Response();
+ }
+ $is_new = false;
+ $is_choose_type = false;
+
+ $merchant = $provider_config->getMerchant();
+ $merchant_id = $merchant->getID();
+ $cancel_uri = $this->getApplicationURI("merchant/{$merchant_id}/");
+ } else {
+ $merchant = id(new PhortuneMerchantQuery())
+ ->setViewer($viewer)
+ ->withIDs(array($request->getStr('merchantID')))
+ ->requireCapabilities(
+ array(
+ PhabricatorPolicyCapability::CAN_VIEW,
+ PhabricatorPolicyCapability::CAN_EDIT,
+ ))
+ ->executeOne();
+ if (!$merchant) {
+ return new Aphront404Response();
+ }
+ $merchant_id = $merchant->getID();
+
+ $current_providers = id(new PhortunePaymentProviderConfigQuery())
+ ->setViewer($viewer)
+ ->withMerchantPHIDs(array($merchant->getPHID()))
+ ->execute();
+ $current_map = mgroup($current_providers, 'getProviderClass');
+
+ $provider_config = PhortunePaymentProviderConfig::initializeNewProvider(
+ $merchant);
+
+ $is_new = true;
+
+ $classes = PhortunePaymentProvider::getAllProviders();
+ $class = $request->getStr('class');
+ if (empty($classes[$class]) || isset($current_map[$class])) {
+ return $this->processChooseClassRequest(
+ $request,
+ $merchant,
+ $current_map);
+ }
+
+ $provider_config->setProviderClass($class);
+
+ $cancel_uri = $this->getApplicationURI(
+ 'provider/edit/?merchantID='.$merchant_id);
+ }
+
+ $provider = $provider_config->buildProvider();
+
+ if ($is_new) {
+ $title = pht('Create Payment Provider');
+ $button_text = pht('Create Provider');
+ } else {
+ $title = pht(
+ 'Edit Payment Provider %d %s',
+ $provider_config->getID(),
+ $provider->getName());
+ $button_text = pht('Save Changes');
+ }
+
+ $errors = array();
+ if ($request->isFormPost() && $request->getStr('edit')) {
+ $form_values = $provider->readEditFormValuesFromRequest($request);
+
+ list($errors, $issues, $xaction_values) = $provider->processEditForm(
+ $request,
+ $form_values);
+
+ if (!$errors) {
+ // Find any secret fields which we're about to set to "*******"
+ // (indicating that the user did not edit the value) and remove them
+ // from the list of properties to update (so we don't write "******"
+ // to permanent configuration.
+ $secrets = $provider->getAllConfigurableSecretProperties();
+ $secrets = array_fuse($secrets);
+ foreach ($xaction_values as $key => $value) {
+ if ($provider->isConfigurationSecret($value)) {
+ unset($xaction_values[$key]);
+ }
+ }
+
+ if ($provider->canRunConfigurationTest()) {
+ $proxy = clone $provider;
+ $proxy_config = clone $provider_config;
+ $proxy_config->setMetadata(
+ $xaction_values + $provider_config->getMetadata());
+ $proxy->setProviderConfig($proxy_config);
+
+ try {
+ $proxy->runConfigurationTest();
+ } catch (Exception $ex) {
+ $errors[] = pht('Unable to connect to payment provider:');
+ $errors[] = $ex->getMessage();
+ }
+ }
+
+ if (!$errors) {
+ $template = id(new PhortunePaymentProviderConfigTransaction())
+ ->setTransactionType(
+ PhortunePaymentProviderConfigTransaction::TYPE_PROPERTY);
+
+ $xactions = array();
+
+ $xactions[] = id(new PhortunePaymentProviderConfigTransaction())
+ ->setTransactionType(
+ PhortunePaymentProviderConfigTransaction::TYPE_CREATE)
+ ->setNewValue(true);
+
+ foreach ($xaction_values as $key => $value) {
+ $xactions[] = id(clone $template)
+ ->setMetadataValue(
+ PhortunePaymentProviderConfigTransaction::PROPERTY_KEY,
+ $key)
+ ->setNewValue($value);
+ }
+
+ $editor = id(new PhortunePaymentProviderConfigEditor())
+ ->setActor($viewer)
+ ->setContentSourceFromRequest($request)
+ ->setContinueOnNoEffect(true);
+
+ $editor->applyTransactions($provider_config, $xactions);
+
+ $merchant_uri = $this->getApplicationURI(
+ 'merchant/'.$merchant->getID().'/');
+ return id(new AphrontRedirectResponse())->setURI($merchant_uri);
+ }
+ }
+ } else {
+ $form_values = $provider->readEditFormValuesFromProviderConfig();
+ $issues = array();
+ }
+
+ $form = id(new AphrontFormView())
+ ->setUser($viewer)
+ ->addHiddenInput('merchantID', $merchant->getID())
+ ->addHiddenInput('class', $provider_config->getProviderClass())
+ ->addHiddenInput('edit', true)
+ ->appendChild(
+ id(new AphrontFormMarkupControl())
+ ->setLabel(pht('Provider Type'))
+ ->setValue($provider->getName()));
+
+ $provider->extendEditForm($request, $form, $form_values, $issues);
+
+ $form
+ ->appendChild(
+ id(new AphrontFormSubmitControl())
+ ->setValue($button_text)
+ ->addCancelButton($cancel_uri))
+ ->appendChild(
+ id(new AphrontFormDividerControl()))
+ ->appendRemarkupInstructions(
+ $provider->getConfigureInstructions());
+
+ $crumbs = $this->buildApplicationCrumbs();
+ $crumbs->addTextCrumb($merchant->getName(), $cancel_uri);
+
+ if ($is_new) {
+ $crumbs->addTextCrumb(pht('Add Provider'));
+ } else {
+ $crumbs->addTextCrumb(
+ pht('Edit Provider %d', $provider_config->getID()));
+ }
+
+ $box = id(new PHUIObjectBoxView())
+ ->setFormErrors($errors)
+ ->setHeaderText($title)
+ ->appendChild($form);
+
+ return $this->buildApplicationPage(
+ array(
+ $crumbs,
+ $box,
+ ),
+ array(
+ 'title' => $title,
+ ));
+ }
+
+ private function processChooseClassRequest(
+ AphrontRequest $request,
+ PhortuneMerchant $merchant,
+ array $current_map) {
+
+ $viewer = $request->getUser();
+
+ $providers = PhortunePaymentProvider::getAllProviders();
+ $v_class = null;
+ $errors = array();
+ if ($request->isFormPost()) {
+ $v_class = $request->getStr('class');
+ if (!isset($providers[$v_class])) {
+ $errors[] = pht('You must select a valid provider type.');
+ }
+ }
+
+ $merchant_id = $merchant->getID();
+ $cancel_uri = $this->getApplicationURI("merchant/{$merchant_id}/");
+
+ if (!$v_class) {
+ $v_class = key($providers);
+ }
+
+ $panel_classes = id(new AphrontFormRadioButtonControl())
+ ->setName('class')
+ ->setValue($v_class);
+
+ $providers = msort($providers, 'getConfigureName');
+ foreach ($providers as $class => $provider) {
+ $disabled = isset($current_map[$class]);
+ if ($disabled) {
+ $description = phutil_tag(
+ 'em',
+ array(),
+ pht(
+ 'This merchant already has a payment account configured '.
+ 'with this provider.'));
+ } else {
+ $description = $provider->getConfigureDescription();
+ }
+
+ $panel_classes->addButton(
+ $class,
+ $provider->getConfigureName(),
+ $description,
+ null,
+ $disabled);
+ }
+
+ $form = id(new AphrontFormView())
+ ->setUser($viewer)
+ ->addHiddenInput('merchantID', $merchant->getID())
+ ->appendRemarkupInstructions(
+ pht(
+ 'Choose the type of payment provider to add:'))
+ ->appendChild($panel_classes)
+ ->appendChild(
+ id(new AphrontFormSubmitControl())
+ ->setValue(pht('Continue'))
+ ->addCancelButton($cancel_uri));
+
+ $title = pht('Add Payment Provider');
+
+ $crumbs = $this->buildApplicationCrumbs();
+ $crumbs->addTextCrumb($merchant->getName(), $cancel_uri);
+ $crumbs->addTextCrumb($title);
+
+ $box = id(new PHUIObjectBoxView())
+ ->setHeaderText($title)
+ ->setFormErrors($errors)
+ ->setForm($form);
+
+ return $this->buildApplicationPage(
+ array(
+ $crumbs,
+ $box,
+ ),
+ array(
+ 'title' => $title,
+ ));
+ }
+
+}
diff --git a/src/applications/phortune/editor/PhortunePaymentProviderConfigEditor.php b/src/applications/phortune/editor/PhortunePaymentProviderConfigEditor.php
new file mode 100644
--- /dev/null
+++ b/src/applications/phortune/editor/PhortunePaymentProviderConfigEditor.php
@@ -0,0 +1,81 @@
+<?php
+
+final class PhortunePaymentProviderConfigEditor
+ extends PhabricatorApplicationTransactionEditor {
+
+ public function getEditorApplicationClass() {
+ return 'PhabricatorPhortuneApplication';
+ }
+
+ public function getEditorObjectsDescription() {
+ return pht('Phortune Payment Providers');
+ }
+
+ public function getTransactionTypes() {
+ $types = parent::getTransactionTypes();
+
+ $types[] = PhortunePaymentProviderConfigTransaction::TYPE_CREATE;
+ $types[] = PhortunePaymentProviderConfigTransaction::TYPE_PROPERTY;
+
+ return $types;
+ }
+
+ protected function getCustomTransactionOldValue(
+ PhabricatorLiskDAO $object,
+ PhabricatorApplicationTransaction $xaction) {
+ switch ($xaction->getTransactionType()) {
+ case PhortunePaymentProviderConfigTransaction::TYPE_CREATE:
+ return null;
+ case PhortunePaymentProviderConfigTransaction::TYPE_PROPERTY:
+ $property_key = $xaction->getMetadataValue(
+ PhortunePaymentProviderConfigTransaction::PROPERTY_KEY);
+ return $object->getMetadataValue($property_key);
+ }
+
+ return parent::getCustomTransactionOldValue($object, $xaction);
+ }
+
+ protected function getCustomTransactionNewValue(
+ PhabricatorLiskDAO $object,
+ PhabricatorApplicationTransaction $xaction) {
+
+ switch ($xaction->getTransactionType()) {
+ case PhortunePaymentProviderConfigTransaction::TYPE_CREATE:
+ case PhortunePaymentProviderConfigTransaction::TYPE_PROPERTY:
+ return $xaction->getNewValue();
+ }
+
+ return parent::getCustomTransactionNewValue($object, $xaction);
+ }
+
+ protected function applyCustomInternalTransaction(
+ PhabricatorLiskDAO $object,
+ PhabricatorApplicationTransaction $xaction) {
+
+ switch ($xaction->getTransactionType()) {
+ case PhortunePaymentProviderConfigTransaction::TYPE_CREATE:
+ return;
+ case PhortunePaymentProviderConfigTransaction::TYPE_PROPERTY:
+ $property_key = $xaction->getMetadataValue(
+ PhortunePaymentProviderConfigTransaction::PROPERTY_KEY);
+ $object->setMetadataValue($property_key, $xaction->getNewValue());
+ return;
+ }
+
+ return parent::applyCustomInternalTransaction($object, $xaction);
+ }
+
+ protected function applyCustomExternalTransaction(
+ PhabricatorLiskDAO $object,
+ PhabricatorApplicationTransaction $xaction) {
+
+ switch ($xaction->getTransactionType()) {
+ case PhortunePaymentProviderConfigTransaction::TYPE_CREATE:
+ case PhortunePaymentProviderConfigTransaction::TYPE_PROPERTY:
+ return;
+ }
+
+ return parent::applyCustomExternalTransaction($object, $xaction);
+ }
+
+}
diff --git a/src/applications/phortune/option/PhabricatorPhortuneConfigOptions.php b/src/applications/phortune/option/PhabricatorPhortuneConfigOptions.php
deleted file mode 100644
--- a/src/applications/phortune/option/PhabricatorPhortuneConfigOptions.php
+++ /dev/null
@@ -1,70 +0,0 @@
-<?php
-
-final class PhabricatorPhortuneConfigOptions
- extends PhabricatorApplicationConfigOptions {
-
- public function getName() {
- return pht('Phortune');
- }
-
- public function getDescription() {
- return pht('Configure payments and billing.');
- }
-
- public function getOptions() {
- return array(
- $this->newOption('phortune.stripe.publishable-key', 'string', null)
- ->setLocked(true)
- ->setDescription(pht('Stripe publishable key.')),
- $this->newOption('phortune.stripe.secret-key', 'string', null)
- ->setHidden(true)
- ->setDescription(pht('Stripe secret key.')),
- $this->newOption('phortune.balanced.marketplace-uri', 'string', null)
- ->setLocked(true)
- ->setDescription(pht('Balanced Marketplace URI.')),
- $this->newOption('phortune.balanced.secret-key', 'string', null)
- ->setHidden(true)
- ->setDescription(pht('Balanced secret key.')),
- $this->newOption('phortune.test.enabled', 'bool', false)
- ->setBoolOptions(
- array(
- pht('Enable Test Provider'),
- pht('Disable Test Provider'),
- ))
- ->setSummary(pht('Enable test payment provider.'))
- ->setDescription(
- pht(
- "Enable the test payment provider.\n\n".
- "NOTE: Enabling this provider gives all users infinite free ".
- "money! You should enable it **ONLY** for testing and ".
- "development."))
- ->setLocked(true),
- $this->newOption('phortune.paypal.api-username', 'string', null)
- ->setLocked(true)
- ->setDescription(
- pht('PayPal API username.')),
- $this->newOption('phortune.paypal.api-password', 'string', null)
- ->setHidden(true)
- ->setDescription(
- pht('PayPal API password.')),
- $this->newOption('phortune.paypal.api-signature', 'string', null)
- ->setHidden(true)
- ->setDescription(
- pht('PayPal API signature.')),
- $this->newOption('phortune.wepay.client-id', 'string', null)
- ->setLocked(true)
- ->setDescription(pht('WePay application ID.')),
- $this->newOption('phortune.wepay.client-secret', 'string', null)
- ->setHidden(true)
- ->setDescription(pht('WePay application secret.')),
- $this->newOption('phortune.wepay.access-token', 'string', null)
- ->setHidden(true)
- ->setDescription(pht('WePay access token.')),
- $this->newOption('phortune.wepay.account-id', 'string', null)
- ->setLocked(true)
- ->setHidden(true)
- ->setDescription(pht('WePay account ID.')),
- );
- }
-
-}
diff --git a/src/applications/phortune/phid/PhortunePaymentProviderPHIDType.php b/src/applications/phortune/phid/PhortunePaymentProviderPHIDType.php
new file mode 100644
--- /dev/null
+++ b/src/applications/phortune/phid/PhortunePaymentProviderPHIDType.php
@@ -0,0 +1,38 @@
+<?php
+
+final class PhortunePaymentProviderPHIDType extends PhabricatorPHIDType {
+
+ const TYPECONST = 'PHPR';
+
+ public function getTypeName() {
+ return pht('Phortune Payment Provider');
+ }
+
+ public function newObject() {
+ return new PhortunePaymentProviderConfig();
+ }
+
+ protected function buildQueryForObjects(
+ PhabricatorObjectQuery $query,
+ array $phids) {
+
+ return id(new PhortunePaymentProviderConfigQuery())
+ ->withPHIDs($phids);
+ }
+
+ public function loadHandles(
+ PhabricatorHandleQuery $query,
+ array $handles,
+ array $objects) {
+
+ foreach ($handles as $phid => $handle) {
+ $provider_config = $objects[$phid];
+
+ $id = $provider_config->getID();
+
+ $handle->setName(pht('Payment Provider %d', $id));
+ $handle->setURI("/phortune/provider/{$id}/");
+ }
+ }
+
+}
diff --git a/src/applications/phortune/provider/PhortuneBalancedPaymentProvider.php b/src/applications/phortune/provider/PhortuneBalancedPaymentProvider.php
--- a/src/applications/phortune/provider/PhortuneBalancedPaymentProvider.php
+++ b/src/applications/phortune/provider/PhortuneBalancedPaymentProvider.php
@@ -2,17 +2,118 @@
final class PhortuneBalancedPaymentProvider extends PhortunePaymentProvider {
+ const BALANCED_MARKETPLACE_ID = 'balanced.marketplace-id';
+ const BALANCED_SECRET_KEY = 'balanced.secret-key';
+
public function isEnabled() {
return $this->getMarketplaceURI() &&
$this->getSecretKey();
}
- public function getProviderType() {
- return 'balanced';
+ public function getName() {
+ return pht('Balanced Payments');
+ }
+
+ public function getConfigureName() {
+ return pht('Add Balanced Payments Account');
+ }
+
+ public function getConfigureDescription() {
+ return pht(
+ 'Allows you to accept credit or debit card payments with a '.
+ 'balancedpayments.com account.');
+ }
+
+ public function getConfigureInstructions() {
+ return pht(
+ "To configure Balacned, register or log in to an existing account on ".
+ "[[https://balancedpayments.com | balancedpayments.com]]. Once logged ".
+ "in:\n\n".
+ " - Choose a marketplace.\n".
+ " - Find the **Marketplace ID** in {nav My Marketplace > Settings} and ".
+ " copy it into the field above.\n".
+ " - On the same screen, under **API keys**, choose **Add a key**, then ".
+ " **Show key secret**. Copy the value into the field above.\n\n".
+ "You can either use a test marketplace to add this provider in test ".
+ "mode, or use a live marketplace to accept live payments.");
+ }
+
+ public function getAllConfigurableProperties() {
+ return array(
+ self::BALANCED_MARKETPLACE_ID,
+ self::BALANCED_SECRET_KEY,
+ );
+ }
+
+ public function getAllConfigurableSecretProperties() {
+ return array(
+ self::BALANCED_SECRET_KEY,
+ );
+ }
+
+ public function processEditForm(
+ AphrontRequest $request,
+ array $values) {
+
+ $errors = array();
+ $issues = array();
+
+ if (!strlen($values[self::BALANCED_MARKETPLACE_ID])) {
+ $errors[] = pht('Balanced Marketplace ID is required.');
+ $issues[self::BALANCED_MARKETPLACE_ID] = pht('Required');
+ }
+
+ if (!strlen($values[self::BALANCED_SECRET_KEY])) {
+ $errors[] = pht('Balanced Secret Key is required.');
+ $issues[self::BALANCED_SECRET_KEY] = pht('Required');
+ }
+
+ return array($errors, $issues, $values);
}
- public function getProviderDomain() {
- return 'balancedpayments.com';
+ public function extendEditForm(
+ AphrontRequest $request,
+ AphrontFormView $form,
+ array $values,
+ array $issues) {
+
+ $form
+ ->appendChild(
+ id(new AphrontFormTextControl())
+ ->setName(self::BALANCED_MARKETPLACE_ID)
+ ->setValue($values[self::BALANCED_MARKETPLACE_ID])
+ ->setError(idx($issues, self::BALANCED_MARKETPLACE_ID, true))
+ ->setLabel(pht('Balanced Marketplace ID')))
+ ->appendChild(
+ id(new AphrontFormTextControl())
+ ->setName(self::BALANCED_SECRET_KEY)
+ ->setValue($values[self::BALANCED_SECRET_KEY])
+ ->setError(idx($issues, self::BALANCED_SECRET_KEY, true))
+ ->setLabel(pht('Balanced Secret Key')));
+
+ }
+
+ public function canRunConfigurationTest() {
+ return true;
+ }
+
+ public function runConfigurationTest() {
+ $root = dirname(phutil_get_library_root('phabricator'));
+ require_once $root.'/externals/httpful/bootstrap.php';
+ require_once $root.'/externals/restful/bootstrap.php';
+ require_once $root.'/externals/balanced-php/bootstrap.php';
+
+ // TODO: This only tests that the secret key is correct. It's not clear
+ // how to test that the marketplace is correct.
+
+ try {
+ Balanced\Settings::$api_key = $this->getSecretKey();
+ Balanced\APIKey::query()->first();
+ } catch (RESTful\Exceptions\HTTPError $error) {
+ // NOTE: This exception doesn't print anything meaningful if it escapes
+ // to top level. Replace it with something slightly readable.
+ throw new Exception($error->response->body->description);
+ }
}
public function getPaymentMethodDescription() {
@@ -32,11 +133,6 @@
return pht('Credit/Debit Card');
}
- public function canHandlePaymentMethod(PhortunePaymentMethod $method) {
- $type = $method->getMetadataValue('type');
- return ($type === 'balanced.account');
- }
-
protected function executeCharge(
PhortunePaymentMethod $method,
PhortuneCharge $charge) {
@@ -79,12 +175,20 @@
$charge->save();
}
- private function getMarketplaceURI() {
- return PhabricatorEnv::getEnvConfig('phortune.balanced.marketplace-uri');
+ private function getMarketplaceID() {
+ return $this
+ ->getProviderConfig()
+ ->getMetadataValue(self::BALANCED_MARKETPLACE_ID);
}
private function getSecretKey() {
- return PhabricatorEnv::getEnvConfig('phortune.balanced.secret-key');
+ return $this
+ ->getProviderConfig()
+ ->getMetadataValue(self::BALANCED_SECRET_KEY);
+ }
+
+ private function getMarketplaceURI() {
+ return '/v1/marketplace/'.$this->getMarketplaceID();
}
@@ -104,6 +208,7 @@
* @phutil-external-symbol class Balanced\Card
* @phutil-external-symbol class Balanced\Settings
* @phutil-external-symbol class Balanced\Marketplace
+ * @phutil-external-symbol class Balanced\APIKey
* @phutil-external-symbol class RESTful\Exceptions\HTTPError
*/
public function createPaymentMethodFromRequest(
diff --git a/src/applications/phortune/provider/PhortunePaypalPaymentProvider.php b/src/applications/phortune/provider/PhortunePayPalPaymentProvider.php
rename from src/applications/phortune/provider/PhortunePaypalPaymentProvider.php
rename to src/applications/phortune/provider/PhortunePayPalPaymentProvider.php
--- a/src/applications/phortune/provider/PhortunePaypalPaymentProvider.php
+++ b/src/applications/phortune/provider/PhortunePayPalPaymentProvider.php
@@ -1,6 +1,11 @@
<?php
-final class PhortunePaypalPaymentProvider extends PhortunePaymentProvider {
+final class PhortunePayPalPaymentProvider extends PhortunePaymentProvider {
+
+ const PAYPAL_API_USERNAME = 'paypal.api-username';
+ const PAYPAL_API_PASSWORD = 'paypal.api-password';
+ const PAYPAL_API_SIGNATURE = 'paypal.api-signature';
+ const PAYPAL_MODE = 'paypal.mode';
public function isEnabled() {
// TODO: See note in processControllerRequest().
@@ -11,16 +16,135 @@
$this->getPaypalAPISignature();
}
- public function getProviderType() {
- return 'paypal';
+ public function getName() {
+ return pht('PayPal');
+ }
+
+ public function getConfigureName() {
+ return pht('Add PayPal Payments Account');
+ }
+
+ public function getConfigureDescription() {
+ return pht(
+ 'Allows you to accept various payment instruments with a paypal.com '.
+ 'account.');
+ }
+
+ public function getConfigureInstructions() {
+ return pht(
+ "To configure PayPal, register or log into an existing account on ".
+ "[[https://paypal.com | paypal.com]] (for live payments) or ".
+ "[[https://sandbox.paypal.com | sandbox.paypal.com]] (for test ".
+ "payments). Once logged in:\n\n".
+ " - Navigate to {nav Tools > API Access}.\n".
+ " - Choose **View API Signature**.\n".
+ " - Copy the **API Username**, **API Password** and **Signature** ".
+ " into the fields above.\n\n".
+ "You can select whether the provider operates in test mode or ".
+ "accepts live payments using the **Mode** dropdown above.\n\n".
+ "You can either use `sandbox.paypal.com` to retrieve live credentials, ".
+ "or `paypal.com` to retrieve live credentials.");
+ }
+
+ public function getAllConfigurableProperties() {
+ return array(
+ self::PAYPAL_API_USERNAME,
+ self::PAYPAL_API_PASSWORD,
+ self::PAYPAL_API_SIGNATURE,
+ self::PAYPAL_MODE,
+ );
+ }
+
+ public function getAllConfigurableSecretProperties() {
+ return array(
+ self::PAYPAL_API_PASSWORD,
+ self::PAYPAL_API_SIGNATURE,
+ );
+ }
+
+ public function processEditForm(
+ AphrontRequest $request,
+ array $values) {
+
+ $errors = array();
+ $issues = array();
+
+ if (!strlen($values[self::PAYPAL_API_USERNAME])) {
+ $errors[] = pht('PayPal API Username is required.');
+ $issues[self::PAYPAL_API_USERNAME] = pht('Required');
+ }
+
+ if (!strlen($values[self::PAYPAL_API_PASSWORD])) {
+ $errors[] = pht('PayPal API Password is required.');
+ $issues[self::PAYPAL_API_PASSWORD] = pht('Required');
+ }
+
+ if (!strlen($values[self::PAYPAL_API_SIGNATURE])) {
+ $errors[] = pht('PayPal API Signature is required.');
+ $issues[self::PAYPAL_API_SIGNATURE] = pht('Required');
+ }
+
+ if (!strlen($values[self::PAYPAL_MODE])) {
+ $errors[] = pht('Mode is required.');
+ $issues[self::PAYPAL_MODE] = pht('Required');
+ }
+
+ return array($errors, $issues, $values);
+ }
+
+ public function extendEditForm(
+ AphrontRequest $request,
+ AphrontFormView $form,
+ array $values,
+ array $issues) {
+
+ $form
+ ->appendChild(
+ id(new AphrontFormTextControl())
+ ->setName(self::PAYPAL_API_USERNAME)
+ ->setValue($values[self::PAYPAL_API_USERNAME])
+ ->setError(idx($issues, self::PAYPAL_API_USERNAME, true))
+ ->setLabel(pht('Paypal API Username')))
+ ->appendChild(
+ id(new AphrontFormTextControl())
+ ->setName(self::PAYPAL_API_PASSWORD)
+ ->setValue($values[self::PAYPAL_API_PASSWORD])
+ ->setError(idx($issues, self::PAYPAL_API_PASSWORD, true))
+ ->setLabel(pht('Paypal API Password')))
+ ->appendChild(
+ id(new AphrontFormTextControl())
+ ->setName(self::PAYPAL_API_SIGNATURE)
+ ->setValue($values[self::PAYPAL_API_SIGNATURE])
+ ->setError(idx($issues, self::PAYPAL_API_SIGNATURE, true))
+ ->setLabel(pht('Paypal API Signature')))
+ ->appendChild(
+ id(new AphrontFormSelectControl())
+ ->setName(self::PAYPAL_MODE)
+ ->setValue($values[self::PAYPAL_MODE])
+ ->setError(idx($issues, self::PAYPAL_MODE))
+ ->setLabel(pht('Mode'))
+ ->setOptions(
+ array(
+ 'test' => pht('Test Mode'),
+ 'live' => pht('Live Mode'),
+ )));
+
+ return;
+ }
+
+ public function canRunConfigurationTest() {
+ return true;
}
- public function getProviderDomain() {
- return 'paypal.com';
+ public function runConfigurationTest() {
+ $result = $this
+ ->newPaypalAPICall()
+ ->setRawPayPalQuery('GetBalance', array())
+ ->resolve();
}
public function getPaymentMethodDescription() {
- return pht('Credit Card or Paypal Account');
+ return pht('Credit Card or PayPal Account');
}
public function getPaymentMethodIcon() {
@@ -28,12 +152,7 @@
}
public function getPaymentMethodProviderDescription() {
- return 'Paypal';
- }
-
- public function canHandlePaymentMethod(PhortunePaymentMethod $method) {
- $type = $method->getMetadataValue('type');
- return ($type == 'paypal');
+ return 'PayPal';
}
protected function executeCharge(
@@ -43,15 +162,21 @@
}
private function getPaypalAPIUsername() {
- return PhabricatorEnv::getEnvConfig('phortune.paypal.api-username');
+ return $this
+ ->getProviderConfig()
+ ->getMetadataValue(self::PAYPAL_API_USERNAME);
}
private function getPaypalAPIPassword() {
- return PhabricatorEnv::getEnvConfig('phortune.paypal.api-password');
+ return $this
+ ->getProviderConfig()
+ ->getMetadataValue(self::PAYPAL_API_PASSWORD);
}
private function getPaypalAPISignature() {
- return PhabricatorEnv::getEnvConfig('phortune.paypal.api-signature');
+ return $this
+ ->getProviderConfig()
+ ->getMetadataValue(self::PAYPAL_API_SIGNATURE);
}
/* -( One-Time Payments )-------------------------------------------------- */
@@ -74,7 +199,7 @@
}
public function processControllerRequest(
- PhortuneProviderController $controller,
+ PhortuneProviderActionController $controller,
AphrontRequest $request) {
$viewer = $request->getUser();
@@ -214,8 +339,15 @@
}
private function newPaypalAPICall() {
+ $mode = $this->getProviderConfig()->getMetadataValue(self::PAYPAL_MODE);
+ if ($mode == 'live') {
+ $host = 'https://api-3t.paypal.com/nvp';
+ } else {
+ $host = 'https://api-3t.sandbox.paypal.com/nvp';
+ }
+
return id(new PhutilPayPalAPIFuture())
- ->setHost('https://api-3t.sandbox.paypal.com/nvp')
+ ->setHost($host)
->setAPIUsername($this->getPaypalAPIUsername())
->setAPIPassword($this->getPaypalAPIPassword())
->setAPISignature($this->getPaypalAPISignature());
diff --git a/src/applications/phortune/provider/PhortunePaymentProvider.php b/src/applications/phortune/provider/PhortunePaymentProvider.php
--- a/src/applications/phortune/provider/PhortunePaymentProvider.php
+++ b/src/applications/phortune/provider/PhortunePaymentProvider.php
@@ -5,16 +5,119 @@
*/
abstract class PhortunePaymentProvider {
+ private $providerConfig;
+
+ public function setProviderConfig(
+ PhortunePaymentProviderConfig $provider_config) {
+ $this->providerConfig = $provider_config;
+ return $this;
+ }
+
+ public function getProviderConfig() {
+ return $this->providerConfig;
+ }
+
+ /**
+ * Return a short name which identifies this provider.
+ */
+ abstract public function getName();
+
+
+/* -( Configuring Providers )---------------------------------------------- */
+
+
+ /**
+ * Return a human-readable provider name for use on the merchant workflow
+ * where a merchant owner adds providers.
+ */
+ abstract public function getConfigureName();
+
+
+ /**
+ * Return a human-readable provider description for use on the merchant
+ * workflow where a merchant owner adds providers.
+ */
+ abstract public function getConfigureDescription();
+
+ abstract public function getConfigureInstructions();
+
+ abstract public function getAllConfigurableProperties();
+
+ abstract public function getAllConfigurableSecretProperties();
+ /**
+ * Read a dictionary of properties from the provider's configuration for
+ * use when editing the provider.
+ */
+ public function readEditFormValuesFromProviderConfig() {
+ $properties = $this->getAllConfigurableProperties();
+ $config = $this->getProviderConfig();
+
+ $secrets = $this->getAllConfigurableSecretProperties();
+ $secrets = array_fuse($secrets);
+
+ $map = array();
+ foreach ($properties as $property) {
+ $map[$property] = $config->getMetadataValue($property);
+ if (isset($secrets[$property])) {
+ $map[$property] = $this->renderConfigurationSecret($map[$property]);
+ }
+ }
+
+ return $map;
+ }
+
+
+ /**
+ * Read a dictionary of properties from a request for use when editing the
+ * provider.
+ */
+ public function readEditFormValuesFromRequest(AphrontRequest $request) {
+ $properties = $this->getAllConfigurableProperties();
+
+ $map = array();
+ foreach ($properties as $property) {
+ $map[$property] = $request->getStr($property);
+ }
+
+ return $map;
+ }
+
+
+ abstract public function processEditForm(
+ AphrontRequest $request,
+ array $values);
+
+ abstract public function extendEditForm(
+ AphrontRequest $request,
+ AphrontFormView $form,
+ array $values,
+ array $issues);
+
+ protected function renderConfigurationSecret($value) {
+ if (strlen($value)) {
+ return str_repeat('*', strlen($value));
+ }
+ return '';
+ }
+
+ public function isConfigurationSecret($value) {
+ return preg_match('/^\*+\z/', trim($value));
+ }
+
+ abstract public function canRunConfigurationTest();
+
+ public function runConfigurationTest() {
+ throw new PhortuneNotImplementedException($this);
+ }
+
/* -( Selecting Providers )------------------------------------------------ */
public static function getAllProviders() {
- $objects = id(new PhutilSymbolLoader())
+ return id(new PhutilSymbolLoader())
->setAncestorClass('PhortunePaymentProvider')
->loadObjects();
-
- return mpull($objects, null, 'getProviderKey');
}
public static function getEnabledProviders() {
@@ -47,66 +150,16 @@
return $providers;
}
- public static function getProviderByDigest($digest) {
- $providers = self::getEnabledProviders();
- foreach ($providers as $key => $provider) {
- $provider_digest = PhabricatorHash::digestForIndex($key);
- if ($provider_digest == $digest) {
- return $provider;
- }
- }
- return null;
- }
-
abstract public function isEnabled();
- final public function getProviderKey() {
- return $this->getProviderType().'@'.$this->getProviderDomain();
- }
-
-
- /**
- * Return a short string which uniquely identifies this provider's protocol
- * type, like "stripe", "paypal", or "balanced".
- */
- abstract public function getProviderType();
-
-
- /**
- * Return a short string which uniquely identifies the domain for this
- * provider, like "stripe.com" or "google.com".
- *
- * This is distinct from the provider type so that protocols are not bound
- * to a single domain. This is probably not relevant for payments, but this
- * assumption burned us pretty hard with authentication and it's easy enough
- * to avoid.
- */
- abstract public function getProviderDomain();
-
abstract public function getPaymentMethodDescription();
abstract public function getPaymentMethodIcon();
abstract public function getPaymentMethodProviderDescription();
-
- /**
- * Determine of a provider can handle a payment method.
- *
- * @return bool True if this provider can apply charges to the payment method.
- */
- abstract public function canHandlePaymentMethod(
- PhortunePaymentMethod $method);
-
final public function applyCharge(
PhortunePaymentMethod $payment_method,
PhortuneCharge $charge) {
-
- $charge->setStatus(PhortuneCharge::STATUS_CHARGING);
- $charge->save();
-
$this->executeCharge($payment_method, $charge);
-
- $charge->setStatus(PhortuneCharge::STATUS_CHARGED);
- $charge->save();
}
abstract protected function executeCharge(
@@ -230,10 +283,9 @@
array $params = array(),
$local = false) {
- $digest = PhabricatorHash::digestForIndex($this->getProviderKey());
-
+ $id = $this->getProviderConfig()->getID();
$app = PhabricatorApplication::getByClass('PhabricatorPhortuneApplication');
- $path = $app->getBaseURI().'provider/'.$digest.'/'.$action.'/';
+ $path = $app->getBaseURI().'provider/'.$id.'/'.$action.'/';
$uri = new PhutilURI($path);
$uri->setQueryParams($params);
@@ -250,7 +302,7 @@
}
public function processControllerRequest(
- PhortuneProviderController $controller,
+ PhortuneProviderActionController $controller,
AphrontRequest $request) {
throw new PhortuneNotImplementedException($this);
}
diff --git a/src/applications/phortune/provider/PhortuneStripePaymentProvider.php b/src/applications/phortune/provider/PhortuneStripePaymentProvider.php
--- a/src/applications/phortune/provider/PhortuneStripePaymentProvider.php
+++ b/src/applications/phortune/provider/PhortuneStripePaymentProvider.php
@@ -2,17 +2,26 @@
final class PhortuneStripePaymentProvider extends PhortunePaymentProvider {
+ const STRIPE_PUBLISHABLE_KEY = 'stripe.publishable-key';
+ const STRIPE_SECRET_KEY = 'stripe.secret-key';
+
public function isEnabled() {
return $this->getPublishableKey() &&
$this->getSecretKey();
}
- public function getProviderType() {
- return 'stripe';
+ public function getName() {
+ return pht('Stripe');
+ }
+
+ public function getConfigureName() {
+ return pht('Add Stripe Payments Account');
}
- public function getProviderDomain() {
- return 'stripe.com';
+ public function getConfigureDescription() {
+ return pht(
+ 'Allows you to accept credit or debit card payments with a '.
+ 'stripe.com account.');
}
public function getPaymentMethodDescription() {
@@ -32,14 +41,88 @@
return pht('Credit/Debit Card');
}
- public function canHandlePaymentMethod(PhortunePaymentMethod $method) {
- $type = $method->getMetadataValue('type');
- return ($type === 'stripe.customer');
+ public function getAllConfigurableProperties() {
+ return array(
+ self::STRIPE_PUBLISHABLE_KEY,
+ self::STRIPE_SECRET_KEY,
+ );
+ }
+
+ public function getAllConfigurableSecretProperties() {
+ return array(
+ self::STRIPE_SECRET_KEY,
+ );
+ }
+
+ public function processEditForm(
+ AphrontRequest $request,
+ array $values) {
+
+ $errors = array();
+ $issues = array();
+
+ if (!strlen($values[self::STRIPE_SECRET_KEY])) {
+ $errors[] = pht('Stripe Secret Key is required.');
+ $issues[self::STRIPE_SECRET_KEY] = pht('Required');
+ }
+
+ if (!strlen($values[self::STRIPE_PUBLISHABLE_KEY])) {
+ $errors[] = pht('Stripe Publishable Key is required.');
+ $issues[self::STRIPE_PUBLISHABLE_KEY] = pht('Required');
+ }
+
+ return array($errors, $issues, $values);
+ }
+
+ public function extendEditForm(
+ AphrontRequest $request,
+ AphrontFormView $form,
+ array $values,
+ array $issues) {
+
+ $form
+ ->appendChild(
+ id(new AphrontFormTextControl())
+ ->setName(self::STRIPE_SECRET_KEY)
+ ->setValue($values[self::STRIPE_SECRET_KEY])
+ ->setError(idx($issues, self::STRIPE_SECRET_KEY, true))
+ ->setLabel(pht('Stripe Secret Key')))
+ ->appendChild(
+ id(new AphrontFormTextControl())
+ ->setName(self::STRIPE_PUBLISHABLE_KEY)
+ ->setValue($values[self::STRIPE_PUBLISHABLE_KEY])
+ ->setError(idx($issues, self::STRIPE_PUBLISHABLE_KEY, true))
+ ->setLabel(pht('Stripe Publishable Key')));
+ }
+
+ public function getConfigureInstructions() {
+ return pht(
+ "To configure Stripe, register or log in to an existing account on ".
+ "[[https://stripe.com | stripe.com]]. Once logged in:\n\n".
+ " - Go to {nav icon=user, name=Your Account > Account Settings ".
+ "> API Keys}\n".
+ " - Copy the **Secret Key** and **Publishable Key** into the fields ".
+ "above.\n\n".
+ "You can either use the test keys to add this provider in test mode, ".
+ "or the live keys to accept live payments.");
+ }
+
+ public function canRunConfigurationTest() {
+ return true;
+ }
+
+ public function runConfigurationTest() {
+ $root = dirname(phutil_get_library_root('phabricator'));
+ require_once $root.'/externals/stripe-php/lib/Stripe.php';
+
+ $secret_key = $this->getSecretKey();
+ $account = Stripe_Account::retrieve($secret_key);
}
/**
* @phutil-external-symbol class Stripe_Charge
* @phutil-external-symbol class Stripe_CardError
+ * @phutil-external-symbol class Stripe_Account
*/
protected function executeCharge(
PhortunePaymentMethod $method,
@@ -76,11 +159,15 @@
}
private function getPublishableKey() {
- return PhabricatorEnv::getEnvConfig('phortune.stripe.publishable-key');
+ return $this
+ ->getProviderConfig()
+ ->getMetadataValue(self::STRIPE_PUBLISHABLE_KEY);
}
private function getSecretKey() {
- return PhabricatorEnv::getEnvConfig('phortune.stripe.secret-key');
+ return $this
+ ->getProviderConfig()
+ ->getMetadataValue(self::STRIPE_SECRET_KEY);
}
diff --git a/src/applications/phortune/provider/PhortuneTestPaymentProvider.php b/src/applications/phortune/provider/PhortuneTestPaymentProvider.php
--- a/src/applications/phortune/provider/PhortuneTestPaymentProvider.php
+++ b/src/applications/phortune/provider/PhortuneTestPaymentProvider.php
@@ -6,12 +6,27 @@
return PhabricatorEnv::getEnvConfig('phortune.test.enabled');
}
- public function getProviderType() {
- return 'test';
+ public function getName() {
+ return pht('Test Payments');
}
- public function getProviderDomain() {
- return 'example.com';
+ public function getConfigureName() {
+ return pht('Test Payments');
+ }
+
+ public function getConfigureDescription() {
+ return pht(
+ 'Adds a test provider to allow you to test payments. This allows '.
+ 'users to make purchases by clicking a button without actually paying '.
+ 'any money.');
+ }
+
+ public function getConfigureInstructions() {
+ return pht('This providers does not require any special configuration.');
+ }
+
+ public function canRunConfigurationTest() {
+ return false;
}
public function getPaymentMethodDescription() {
@@ -31,17 +46,40 @@
return pht('Vast Wealth');
}
- public function canHandlePaymentMethod(PhortunePaymentMethod $method) {
- $type = $method->getMetadataValue('type');
- return ($type === 'test.wealth' || $type == 'test.multiple');
- }
-
protected function executeCharge(
PhortunePaymentMethod $payment_method,
PhortuneCharge $charge) {
return;
}
+ public function getAllConfigurableProperties() {
+ return array();
+ }
+
+ public function getAllConfigurableSecretProperties() {
+ return array();
+ }
+
+ public function processEditForm(
+ AphrontRequest $request,
+ array $values) {
+
+ $errors = array();
+ $issues = array();
+ $values = array();
+
+ return array($errors, $issues, $values);
+ }
+
+ public function extendEditForm(
+ AphrontRequest $request,
+ AphrontFormView $form,
+ array $values,
+ array $issues) {
+ return;
+ }
+
+
/* -( Adding Payment Methods )--------------------------------------------- */
diff --git a/src/applications/phortune/provider/PhortuneWePayPaymentProvider.php b/src/applications/phortune/provider/PhortuneWePayPaymentProvider.php
--- a/src/applications/phortune/provider/PhortuneWePayPaymentProvider.php
+++ b/src/applications/phortune/provider/PhortuneWePayPaymentProvider.php
@@ -2,6 +2,11 @@
final class PhortuneWePayPaymentProvider extends PhortunePaymentProvider {
+ const WEPAY_CLIENT_ID = 'wepay.client-id';
+ const WEPAY_CLIENT_SECRET = 'wepay.client-secret';
+ const WEPAY_ACCESS_TOKEN = 'wepay.access-token';
+ const WEPAY_ACCOUNT_ID = 'wepay.account-id';
+
public function isEnabled() {
return $this->getWePayClientID() &&
$this->getWePayClientSecret() &&
@@ -9,12 +14,133 @@
$this->getWePayAccountID();
}
- public function getProviderType() {
- return 'wepay';
+ public function getName() {
+ return pht('WePay');
+ }
+
+ public function getConfigureName() {
+ return pht('Add WePay Payments Account');
+ }
+
+ public function getConfigureDescription() {
+ return pht(
+ 'Allows you to accept credit or debit card payments with a '.
+ 'wepay.com account.');
+ }
+
+ public function getConfigureInstructions() {
+ return pht(
+ "To configure WePay, register or log in to an existing account on ".
+ "[[https://wepay.com | wepay.com]] (for live payments) or ".
+ "[[https://stage.wepay.com | stage.wepay.com]] (for testing). ".
+ "Once logged in:\n\n".
+ " - Create an API application if you don't already have one.\n".
+ " - Click the API application name to go to the detail page.\n".
+ " - Copy **Client ID**, **Client Secret**, **Access Token** and ".
+ " **AccountID** from that page to the fields above.\n\n".
+ "You can either use `stage.wepay.com` to retrieve test credentials, ".
+ "or `wepay.com` to retrieve live credentials for accepting live ".
+ "payments.");
+ }
+
+ public function canRunConfigurationTest() {
+ return true;
+ }
+
+ public function runConfigurationTest() {
+ $root = dirname(phutil_get_library_root('phabricator'));
+ require_once $root.'/externals/wepay/wepay.php';
+
+ WePay::useStaging(
+ $this->getWePayClientID(),
+ $this->getWePayClientSecret());
+
+ $wepay = new WePay($this->getWePayAccessToken());
+ $params = array(
+ 'client_id' => $this->getWePayClientID(),
+ 'client_secret' => $this->getWePayClientSecret(),
+ );
+
+ $wepay->request('app', $params);
+ }
+
+ public function getAllConfigurableProperties() {
+ return array(
+ self::WEPAY_CLIENT_ID,
+ self::WEPAY_CLIENT_SECRET,
+ self::WEPAY_ACCESS_TOKEN,
+ self::WEPAY_ACCOUNT_ID,
+ );
+ }
+
+ public function getAllConfigurableSecretProperties() {
+ return array(
+ self::WEPAY_CLIENT_SECRET,
+ );
+ }
+
+ public function processEditForm(
+ AphrontRequest $request,
+ array $values) {
+
+ $errors = array();
+ $issues = array();
+
+ if (!strlen($values[self::WEPAY_CLIENT_ID])) {
+ $errors[] = pht('WePay Client ID is required.');
+ $issues[self::WEPAY_CLIENT_ID] = pht('Required');
+ }
+
+ if (!strlen($values[self::WEPAY_CLIENT_SECRET])) {
+ $errors[] = pht('WePay Client Secret is required.');
+ $issues[self::WEPAY_CLIENT_SECRET] = pht('Required');
+ }
+
+ if (!strlen($values[self::WEPAY_ACCESS_TOKEN])) {
+ $errors[] = pht('WePay Access Token is required.');
+ $issues[self::WEPAY_ACCESS_TOKEN] = pht('Required');
+ }
+
+ if (!strlen($values[self::WEPAY_ACCOUNT_ID])) {
+ $errors[] = pht('WePay Account ID is required.');
+ $issues[self::WEPAY_ACCOUNT_ID] = pht('Required');
+ }
+
+ return array($errors, $issues, $values);
}
- public function getProviderDomain() {
- return 'wepay.com';
+ public function extendEditForm(
+ AphrontRequest $request,
+ AphrontFormView $form,
+ array $values,
+ array $issues) {
+
+ $form
+ ->appendChild(
+ id(new AphrontFormTextControl())
+ ->setName(self::WEPAY_CLIENT_ID)
+ ->setValue($values[self::WEPAY_CLIENT_ID])
+ ->setError(idx($issues, self::WEPAY_CLIENT_ID, true))
+ ->setLabel(pht('WePay Client ID')))
+ ->appendChild(
+ id(new AphrontFormTextControl())
+ ->setName(self::WEPAY_CLIENT_SECRET)
+ ->setValue($values[self::WEPAY_CLIENT_SECRET])
+ ->setError(idx($issues, self::WEPAY_CLIENT_SECRET, true))
+ ->setLabel(pht('WePay Client Secret')))
+ ->appendChild(
+ id(new AphrontFormTextControl())
+ ->setName(self::WEPAY_ACCESS_TOKEN)
+ ->setValue($values[self::WEPAY_ACCESS_TOKEN])
+ ->setError(idx($issues, self::WEPAY_ACCESS_TOKEN, true))
+ ->setLabel(pht('WePay Access Token')))
+ ->appendChild(
+ id(new AphrontFormTextControl())
+ ->setName(self::WEPAY_ACCOUNT_ID)
+ ->setValue($values[self::WEPAY_ACCOUNT_ID])
+ ->setError(idx($issues, self::WEPAY_ACCOUNT_ID, true))
+ ->setLabel(pht('WePay Account ID')));
+
}
public function getPaymentMethodDescription() {
@@ -29,11 +155,6 @@
return 'WePay';
}
- public function canHandlePaymentMethod(PhortunePaymentMethod $method) {
- $type = $method->getMetadataValue('type');
- return ($type == 'wepay');
- }
-
protected function executeCharge(
PhortunePaymentMethod $payment_method,
PhortuneCharge $charge) {
@@ -41,19 +162,27 @@
}
private function getWePayClientID() {
- return PhabricatorEnv::getEnvConfig('phortune.wepay.client-id');
+ return $this
+ ->getProviderConfig()
+ ->getMetadataValue(self::WEPAY_CLIENT_ID);
}
private function getWePayClientSecret() {
- return PhabricatorEnv::getEnvConfig('phortune.wepay.client-secret');
+ return $this
+ ->getProviderConfig()
+ ->getMetadataValue(self::WEPAY_CLIENT_SECRET);
}
private function getWePayAccessToken() {
- return PhabricatorEnv::getEnvConfig('phortune.wepay.access-token');
+ return $this
+ ->getProviderConfig()
+ ->getMetadataValue(self::WEPAY_ACCESS_TOKEN);
}
private function getWePayAccountID() {
- return PhabricatorEnv::getEnvConfig('phortune.wepay.account-id');
+ return $this
+ ->getProviderConfig()
+ ->getMetadataValue(self::WEPAY_ACCOUNT_ID);
}
@@ -81,7 +210,7 @@
* @phutil-external-symbol class WePay
*/
public function processControllerRequest(
- PhortuneProviderController $controller,
+ PhortuneProviderActionController $controller,
AphrontRequest $request) {
$viewer = $request->getUser();
diff --git a/src/applications/phortune/provider/__tests__/PhortunePaymentProviderTestCase.php b/src/applications/phortune/provider/__tests__/PhortunePaymentProviderTestCase.php
deleted file mode 100644
--- a/src/applications/phortune/provider/__tests__/PhortunePaymentProviderTestCase.php
+++ /dev/null
@@ -1,51 +0,0 @@
-<?php
-
-final class PhortunePaymentProviderTestCase extends PhabricatorTestCase {
-
- public function getPhabricatorTestCaseConfiguration() {
- return array(
- self::PHABRICATOR_TESTCONFIG_BUILD_STORAGE_FIXTURES => true,
- );
- }
-
- public function testNoPaymentProvider() {
- $env = PhabricatorEnv::beginScopedEnv();
- $env->overrideEnvConfig('phortune.test.enabled', true);
-
- $method = id(new PhortunePaymentMethod())
- ->setMetadataValue('type', 'hugs');
-
- $caught = null;
- try {
- $provider = $method->buildPaymentProvider();
- } catch (Exception $ex) {
- $caught = $ex;
- }
-
- $this->assertTrue(
- ($caught instanceof PhortuneNoPaymentProviderException),
- 'No provider should accept hugs; they are not a currency.');
- }
-
- public function testMultiplePaymentProviders() {
- $env = PhabricatorEnv::beginScopedEnv();
- $env->overrideEnvConfig('phortune.test.enabled', true);
-
- $method = id(new PhortunePaymentMethod())
- ->setMetadataValue('type', 'test.multiple');
-
- $caught = null;
- try {
- $provider = $method->buildPaymentProvider();
- } catch (Exception $ex) {
- $caught = $ex;
- }
-
- $this->assertTrue(
- ($caught instanceof PhortuneMultiplePaymentProvidersException),
- 'Expect exception when more than one provider handles a payment method.');
- }
-
-
-
-}
diff --git a/src/applications/phortune/provider/__tests__/PhortuneTestExtraPaymentProvider.php b/src/applications/phortune/provider/__tests__/PhortuneTestExtraPaymentProvider.php
deleted file mode 100644
--- a/src/applications/phortune/provider/__tests__/PhortuneTestExtraPaymentProvider.php
+++ /dev/null
@@ -1,40 +0,0 @@
-<?php
-
-final class PhortuneTestExtraPaymentProvider extends PhortunePaymentProvider {
-
- public function isEnabled() {
- return false;
- }
-
- public function getProviderType() {
- return 'test2';
- }
-
- public function getProviderDomain() {
- return 'example.com';
- }
-
- public function getPaymentMethodDescription() {
- return pht('You Should Not Be Able to See This');
- }
-
- public function getPaymentMethodIcon() {
- return celerity_get_resource_uri('/rsrc/image/phortune/test.png');
- }
-
- public function getPaymentMethodProviderDescription() {
- return pht('Just for Unit Tests');
- }
-
- public function canHandlePaymentMethod(PhortunePaymentMethod $method) {
- $type = $method->getMetadataValue('type');
- return ($type === 'test.multiple');
- }
-
- protected function executeCharge(
- PhortunePaymentMethod $payment_method,
- PhortuneCharge $charge) {
- return;
- }
-
-}
diff --git a/src/applications/phortune/query/PhortunePaymentProviderConfigQuery.php b/src/applications/phortune/query/PhortunePaymentProviderConfigQuery.php
new file mode 100644
--- /dev/null
+++ b/src/applications/phortune/query/PhortunePaymentProviderConfigQuery.php
@@ -0,0 +1,95 @@
+<?php
+
+final class PhortunePaymentProviderConfigQuery
+ extends PhabricatorCursorPagedPolicyAwareQuery {
+
+ private $ids;
+ private $phids;
+ private $merchantPHIDs;
+
+ public function withIDs(array $ids) {
+ $this->ids = $ids;
+ return $this;
+ }
+
+ public function withPHIDs(array $phids) {
+ $this->phids = $phids;
+ return $this;
+ }
+
+ public function withMerchantPHIDs(array $phids) {
+ $this->merchantPHIDs = $phids;
+ return $this;
+ }
+
+ protected function loadPage() {
+ $table = new PhortunePaymentProviderConfig();
+ $conn = $table->establishConnection('r');
+
+ $rows = queryfx_all(
+ $conn,
+ 'SELECT * FROM %T %Q %Q %Q',
+ $table->getTableName(),
+ $this->buildWhereClause($conn),
+ $this->buildOrderClause($conn),
+ $this->buildLimitClause($conn));
+
+ return $table->loadAllFromArray($rows);
+ }
+
+ protected function willFilterPage(array $provider_configs) {
+ $merchant_phids = mpull($provider_configs, 'getMerchantPHID');
+ $merchants = id(new PhortuneMerchantQuery())
+ ->setViewer($this->getViewer())
+ ->setParentQuery($this)
+ ->withPHIDs($merchant_phids)
+ ->execute();
+ $merchants = mpull($merchants, null, 'getPHID');
+
+ foreach ($provider_configs as $key => $config) {
+ $merchant = idx($merchants, $config->getMerchantPHID());
+ if (!$merchant) {
+ $this->didRejectResult($config);
+ unset($provider_configs[$key]);
+ continue;
+ }
+ $config->attachMerchant($merchant);
+ }
+
+ return $provider_configs;
+ }
+
+ private function buildWhereClause(AphrontDatabaseConnection $conn) {
+ $where = array();
+
+ if ($this->ids !== null) {
+ $where[] = qsprintf(
+ $conn,
+ 'id IN (%Ld)',
+ $this->ids);
+ }
+
+ if ($this->phids !== null) {
+ $where[] = qsprintf(
+ $conn,
+ 'phid IN (%Ls)',
+ $this->phids);
+ }
+
+ if ($this->merchantPHIDs !== null) {
+ $where[] = qsprintf(
+ $conn,
+ 'merchantPHID IN (%Ls)',
+ $this->merchantPHIDs);
+ }
+
+ $where[] = $this->buildPagingClause($conn);
+
+ return $this->formatWhereClause($where);
+ }
+
+ public function getQueryApplicationClass() {
+ return 'PhabricatorPhortuneApplication';
+ }
+
+}
diff --git a/src/applications/phortune/query/PhortunePaymentProviderConfigTransactionQuery.php b/src/applications/phortune/query/PhortunePaymentProviderConfigTransactionQuery.php
new file mode 100644
--- /dev/null
+++ b/src/applications/phortune/query/PhortunePaymentProviderConfigTransactionQuery.php
@@ -0,0 +1,10 @@
+<?php
+
+final class PhortunePaymentProviderConfigTransactionQuery
+ extends PhabricatorApplicationTransactionQuery {
+
+ public function getTemplateApplicationTransaction() {
+ return new PhortunePaymentProviderConfigTransaction();
+ }
+
+}
diff --git a/src/applications/phortune/storage/PhortunePaymentMethod.php b/src/applications/phortune/storage/PhortunePaymentMethod.php
--- a/src/applications/phortune/storage/PhortunePaymentMethod.php
+++ b/src/applications/phortune/storage/PhortunePaymentMethod.php
@@ -14,12 +14,11 @@
protected $status;
protected $accountPHID;
protected $authorPHID;
+ protected $providerPHID;
protected $expires;
protected $metadata = array();
protected $brand;
protected $lastFourDigits;
- protected $providerType;
- protected $providerDomain;
private $account = self::ATTACHABLE;
@@ -34,8 +33,6 @@
'status' => 'text64',
'brand' => 'text64',
'expires' => 'text16',
- 'providerType' => 'text16',
- 'providerDomain' => 'text64',
'lastFourDigits' => 'text16',
),
self::CONFIG_KEY_SCHEMA => array(
@@ -75,27 +72,9 @@
}
public function buildPaymentProvider() {
- $providers = PhortunePaymentProvider::getAllProviders();
-
- $accept = array();
- foreach ($providers as $provider) {
- if ($provider->canHandlePaymentMethod($this)) {
- $accept[] = $provider;
- }
- }
-
- if (!$accept) {
- throw new PhortuneNoPaymentProviderException($this);
- }
-
- if (count($accept) > 1) {
- throw new PhortuneMultiplePaymentProvidersException($this, $accept);
- }
-
- return head($accept);
+ throw new Exception(pht('TODO: Reimplement this junk.'));
}
-
public function getDisplayName() {
if (strlen($this->name)) {
return $this->name;
diff --git a/src/applications/phortune/storage/PhortunePaymentProviderConfig.php b/src/applications/phortune/storage/PhortunePaymentProviderConfig.php
new file mode 100644
--- /dev/null
+++ b/src/applications/phortune/storage/PhortunePaymentProviderConfig.php
@@ -0,0 +1,96 @@
+<?php
+
+final class PhortunePaymentProviderConfig extends PhortuneDAO
+ implements PhabricatorPolicyInterface {
+
+ protected $merchantPHID;
+ protected $providerClassKey;
+ protected $providerClass;
+ protected $metadata = array();
+
+ private $merchant = self::ATTACHABLE;
+
+ public static function initializeNewProvider(
+ PhortuneMerchant $merchant) {
+ return id(new PhortunePaymentProviderConfig())
+ ->setMerchantPHID($merchant->getPHID());
+ }
+
+ public function getConfiguration() {
+ return array(
+ self::CONFIG_AUX_PHID => true,
+ self::CONFIG_SERIALIZATION => array(
+ 'metadata' => self::SERIALIZATION_JSON,
+ ),
+ self::CONFIG_COLUMN_SCHEMA => array(
+ 'providerClassKey' => 'bytes12',
+ 'providerClass' => 'text128',
+ ),
+ self::CONFIG_KEY_SCHEMA => array(
+ 'key_merchant' => array(
+ 'columns' => array('merchantPHID', 'providerClassKey'),
+ 'unique' => true,
+ ),
+ ),
+ ) + parent::getConfiguration();
+ }
+
+ public function save() {
+ $this->providerClassKey = PhabricatorHash::digestForIndex(
+ $this->providerClass);
+
+ return parent::save();
+ }
+
+ public function generatePHID() {
+ return PhabricatorPHID::generateNewPHID(
+ PhortunePaymentProviderPHIDType::TYPECONST);
+ }
+
+ public function attachMerchant(PhortuneMerchant $merchant) {
+ $this->merchant = $merchant;
+ return $this;
+ }
+
+ public function getMerchant() {
+ return $this->assertAttached($this->merchant);
+ }
+
+ public function getMetadataValue($key, $default = null) {
+ return idx($this->metadata, $key, $default);
+ }
+
+ public function setMetadataValue($key, $value) {
+ $this->metadata[$key] = $value;
+ return $this;
+ }
+
+ public function buildProvider() {
+ return newv($this->getProviderClass(), array())
+ ->setProviderConfig($this);
+ }
+
+
+/* -( PhabricatorPolicyInterface )----------------------------------------- */
+
+
+ public function getCapabilities() {
+ return array(
+ PhabricatorPolicyCapability::CAN_VIEW,
+ PhabricatorPolicyCapability::CAN_EDIT,
+ );
+ }
+
+ public function getPolicy($capability) {
+ return $this->getMerchant()->getPolicy($capability);
+ }
+
+ public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
+ return $this->getMerchant()->hasAutomaticCapability($capability, $viewer);
+ }
+
+ public function describeAutomaticCapability($capability) {
+ return pht('Providers have the policies of their merchant.');
+ }
+
+}
diff --git a/src/applications/phortune/storage/PhortunePaymentProviderConfigTransaction.php b/src/applications/phortune/storage/PhortunePaymentProviderConfigTransaction.php
new file mode 100644
--- /dev/null
+++ b/src/applications/phortune/storage/PhortunePaymentProviderConfigTransaction.php
@@ -0,0 +1,46 @@
+<?php
+
+final class PhortunePaymentProviderConfigTransaction
+ extends PhabricatorApplicationTransaction {
+
+ const TYPE_CREATE = 'paymentprovider:create';
+ const TYPE_PROPERTY = 'paymentprovider:property';
+
+ const PROPERTY_KEY = 'provider-property';
+
+ public function getApplicationName() {
+ return 'phortune';
+ }
+
+ public function getApplicationTransactionType() {
+ return PhortunePaymentProviderPHIDType::TYPECONST;
+ }
+
+ public function getApplicationTransactionCommentObject() {
+ return null;
+ }
+
+ public function getTitle() {
+ $author_phid = $this->getAuthorPHID();
+
+ $old = $this->getOldValue();
+ $new = $this->getNewValue();
+
+ switch ($this->getTransactionType()) {
+ case self::TYPE_CREATE:
+ return pht(
+ '%s created this payment provider.',
+ $this->renderHandleLink($author_phid));
+ case self::TYPE_PROPERTY:
+ // TODO: Allow providers to improve this.
+
+ return pht(
+ '%s edited a property of this payment provider.',
+ $this->renderHandleLink($author_phid));
+ break;
+ }
+
+ return parent::getTitle();
+ }
+
+}

File Metadata

Mime Type
text/plain
Expires
Fri, Apr 4, 11:20 PM (1 w, 2 d ago)
Storage Engine
amazon-s3
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
phabricator/secure/cm/qf/qglgdna4mo46qhaf
Default Alt Text
D10649.id25584.diff (75 KB)

Event Timeline