mirror of
https://we.phorge.it/source/phorge.git
synced 2024-12-18 19:40:55 +01:00
Make payment providers a configurable property of Merchants in Phortune
Summary: Ref T2787. Instead of making providers global configuration, make them a thing on merchants with web configuration. Payment methods and some of the pyament workflow needs to be retooled a bit after this, but this seemed like a reasonable cutoff point for this diff. Test Plan: See screenshots. Reviewers: btrahan Reviewed By: btrahan Subscribers: epriestley Maniphest Tasks: T2787 Differential Revision: https://secure.phabricator.com/D10649
This commit is contained in:
parent
3cf9a5820f
commit
9aa5a8cb7b
23 changed files with 1447 additions and 329 deletions
12
resources/sql/autopatches/20141007.phortuneprovider.sql
Normal file
12
resources/sql/autopatches/20141007.phortuneprovider.sql
Normal file
|
@ -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;
|
19
resources/sql/autopatches/20141007.phortuneproviderx.sql
Normal file
19
resources/sql/autopatches/20141007.phortuneproviderx.sql
Normal file
|
@ -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;
|
|
@ -1966,7 +1966,6 @@ phutil_register_library_map(array(
|
|||
'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 @@ phutil_register_library_map(array(
|
|||
'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 @@ phutil_register_library_map(array(
|
|||
'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 @@ phutil_register_library_map(array(
|
|||
'PhabricatorPholioConfigOptions' => 'PhabricatorApplicationConfigOptions',
|
||||
'PhabricatorPholioMockTestDataGenerator' => 'PhabricatorTestDataGenerator',
|
||||
'PhabricatorPhortuneApplication' => 'PhabricatorApplication',
|
||||
'PhabricatorPhortuneConfigOptions' => 'PhabricatorApplicationConfigOptions',
|
||||
'PhabricatorPhragmentApplication' => 'PhabricatorApplication',
|
||||
'PhabricatorPhrequentApplication' => 'PhabricatorApplication',
|
||||
'PhabricatorPhrequentConfigOptions' => 'PhabricatorApplicationConfigOptions',
|
||||
|
@ -5639,6 +5642,7 @@ phutil_register_library_map(array(
|
|||
'PhortuneMultiplePaymentProvidersException' => 'Exception',
|
||||
'PhortuneNoPaymentProviderException' => 'Exception',
|
||||
'PhortuneNotImplementedException' => 'Exception',
|
||||
'PhortunePayPalPaymentProvider' => 'PhortunePaymentProvider',
|
||||
'PhortunePaymentMethod' => array(
|
||||
'PhortuneDAO',
|
||||
'PhabricatorPolicyInterface',
|
||||
|
@ -5648,8 +5652,15 @@ phutil_register_library_map(array(
|
|||
'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 @@ phutil_register_library_map(array(
|
|||
'PhortuneProductPHIDType' => 'PhabricatorPHIDType',
|
||||
'PhortuneProductQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
||||
'PhortuneProductViewController' => 'PhortuneController',
|
||||
'PhortuneProviderController' => 'PhortuneController',
|
||||
'PhortuneProviderActionController' => 'PhortuneController',
|
||||
'PhortuneProviderEditController' => 'PhortuneMerchantController',
|
||||
'PhortunePurchase' => array(
|
||||
'PhortuneDAO',
|
||||
'PhabricatorPolicyInterface',
|
||||
|
@ -5668,7 +5680,6 @@ phutil_register_library_map(array(
|
|||
'PhortunePurchaseViewController' => 'PhortuneController',
|
||||
'PhortuneSchemaSpec' => 'PhabricatorConfigSchemaSpec',
|
||||
'PhortuneStripePaymentProvider' => 'PhortunePaymentProvider',
|
||||
'PhortuneTestExtraPaymentProvider' => 'PhortunePaymentProvider',
|
||||
'PhortuneTestPaymentProvider' => 'PhortunePaymentProvider',
|
||||
'PhortuneWePayPaymentProvider' => 'PhortunePaymentProvider',
|
||||
'PhragmentBrowseController' => 'PhragmentController',
|
||||
|
|
|
@ -58,8 +58,11 @@ final class PhabricatorPhortuneApplication extends PhabricatorApplication {
|
|||
'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',
|
||||
|
|
|
@ -22,7 +22,7 @@ final class PhortuneMerchantViewController
|
|||
}
|
||||
|
||||
$crumbs = $this->buildApplicationCrumbs();
|
||||
$crumbs->addTextCrumb(pht('Merchant %d', $merchant->getID()));
|
||||
$crumbs->addTextCrumb($merchant->getName());
|
||||
|
||||
$title = pht(
|
||||
'Merchant %d %s',
|
||||
|
@ -39,6 +39,8 @@ final class PhortuneMerchantViewController
|
|||
$actions = $this->buildActionListView($merchant);
|
||||
$properties->setActionList($actions);
|
||||
|
||||
$providers = $this->buildProviderList($merchant);
|
||||
|
||||
$box = id(new PHUIObjectBoxView())
|
||||
->setHeader($header)
|
||||
->appendChild($properties);
|
||||
|
@ -57,6 +59,7 @@ final class PhortuneMerchantViewController
|
|||
array(
|
||||
$crumbs,
|
||||
$box,
|
||||
$providers,
|
||||
$timeline,
|
||||
),
|
||||
array(
|
||||
|
@ -98,4 +101,57 @@ final class PhortuneMerchantViewController
|
|||
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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -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 @@ final class PhortuneProviderController extends PhortuneController {
|
|||
|
||||
public function processRequest() {
|
||||
$request = $this->getRequest();
|
||||
$user = $request->getUser();
|
||||
$viewer = $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.
|
||||
|
||||
$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) {
|
|
@ -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,
|
||||
));
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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.')),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
|
@ -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}/");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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 getProviderDomain() {
|
||||
return 'balancedpayments.com';
|
||||
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 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 @@ final class PhortuneBalancedPaymentProvider extends PhortunePaymentProvider {
|
|||
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 @@ final class PhortuneBalancedPaymentProvider extends PhortunePaymentProvider {
|
|||
$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 @@ final class PhortuneBalancedPaymentProvider extends PhortunePaymentProvider {
|
|||
* @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(
|
||||
|
|
|
@ -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 @@ final class PhortunePaypalPaymentProvider extends PhortunePaymentProvider {
|
|||
$this->getPaypalAPISignature();
|
||||
}
|
||||
|
||||
public function getProviderType() {
|
||||
return 'paypal';
|
||||
public function getName() {
|
||||
return pht('PayPal');
|
||||
}
|
||||
|
||||
public function getProviderDomain() {
|
||||
return 'paypal.com';
|
||||
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 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 @@ final class PhortunePaypalPaymentProvider extends PhortunePaymentProvider {
|
|||
}
|
||||
|
||||
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 @@ final class PhortunePaypalPaymentProvider extends PhortunePaymentProvider {
|
|||
}
|
||||
|
||||
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 @@ final class PhortunePaypalPaymentProvider extends PhortunePaymentProvider {
|
|||
}
|
||||
|
||||
public function processControllerRequest(
|
||||
PhortuneProviderController $controller,
|
||||
PhortuneProviderActionController $controller,
|
||||
AphrontRequest $request) {
|
||||
|
||||
$viewer = $request->getUser();
|
||||
|
@ -214,8 +339,15 @@ final class PhortunePaypalPaymentProvider extends PhortunePaymentProvider {
|
|||
}
|
||||
|
||||
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());
|
|
@ -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 @@ abstract class PhortunePaymentProvider {
|
|||
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 @@ abstract class PhortunePaymentProvider {
|
|||
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 @@ abstract class PhortunePaymentProvider {
|
|||
}
|
||||
|
||||
public function processControllerRequest(
|
||||
PhortuneProviderController $controller,
|
||||
PhortuneProviderActionController $controller,
|
||||
AphrontRequest $request) {
|
||||
throw new PhortuneNotImplementedException($this);
|
||||
}
|
||||
|
|
|
@ -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 getProviderDomain() {
|
||||
return 'stripe.com';
|
||||
public function getConfigureName() {
|
||||
return pht('Add Stripe Payments Account');
|
||||
}
|
||||
|
||||
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 @@ final class PhortuneStripePaymentProvider extends PhortunePaymentProvider {
|
|||
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 @@ final class PhortuneStripePaymentProvider extends PhortunePaymentProvider {
|
|||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -6,12 +6,27 @@ final class PhortuneTestPaymentProvider extends PhortunePaymentProvider {
|
|||
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 @@ final class PhortuneTestPaymentProvider extends PhortunePaymentProvider {
|
|||
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 )--------------------------------------------- */
|
||||
|
||||
|
|
|
@ -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 @@ final class PhortuneWePayPaymentProvider extends PhortunePaymentProvider {
|
|||
$this->getWePayAccountID();
|
||||
}
|
||||
|
||||
public function getProviderType() {
|
||||
return 'wepay';
|
||||
public function getName() {
|
||||
return pht('WePay');
|
||||
}
|
||||
|
||||
public function getProviderDomain() {
|
||||
return 'wepay.com';
|
||||
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 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 @@ final class PhortuneWePayPaymentProvider extends PhortunePaymentProvider {
|
|||
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 @@ final class PhortuneWePayPaymentProvider extends PhortunePaymentProvider {
|
|||
}
|
||||
|
||||
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 @@ final class PhortuneWePayPaymentProvider extends PhortunePaymentProvider {
|
|||
* @phutil-external-symbol class WePay
|
||||
*/
|
||||
public function processControllerRequest(
|
||||
PhortuneProviderController $controller,
|
||||
PhortuneProviderActionController $controller,
|
||||
AphrontRequest $request) {
|
||||
|
||||
$viewer = $request->getUser();
|
||||
|
|
|
@ -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.');
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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';
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
|
||||
final class PhortunePaymentProviderConfigTransactionQuery
|
||||
extends PhabricatorApplicationTransactionQuery {
|
||||
|
||||
public function getTemplateApplicationTransaction() {
|
||||
return new PhortunePaymentProviderConfigTransaction();
|
||||
}
|
||||
|
||||
}
|
|
@ -14,12 +14,11 @@ final class PhortunePaymentMethod extends PhortuneDAO
|
|||
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 @@ final class PhortunePaymentMethod extends PhortuneDAO
|
|||
'status' => 'text64',
|
||||
'brand' => 'text64',
|
||||
'expires' => 'text16',
|
||||
'providerType' => 'text16',
|
||||
'providerDomain' => 'text64',
|
||||
'lastFourDigits' => 'text16',
|
||||
),
|
||||
self::CONFIG_KEY_SCHEMA => array(
|
||||
|
@ -75,27 +72,9 @@ final class PhortunePaymentMethod extends PhortuneDAO
|
|||
}
|
||||
|
||||
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;
|
||||
|
|
|
@ -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.');
|
||||
}
|
||||
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in a new issue