Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F15476612
D10649.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
75 KB
Referenced Files
None
Subscribers
None
D10649.diff
View Options
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
Details
Attached
Mime Type
text/plain
Expires
Tue, Apr 8, 7:56 AM (6 d, 9 h ago)
Storage Engine
amazon-s3
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
phabricator/secure/cm/qf/qglgdna4mo46qhaf
Default Alt Text
D10649.diff (75 KB)
Attached To
Mode
D10649: Make payment providers a configurable property of Merchants in Phortune
Attached
Detach File
Event Timeline
Log In to Comment