mirror of
https://we.phorge.it/source/phorge.git
synced 2025-01-10 23:01:04 +01:00
Add basic payment providers to Phortune
Summary: Abstract out the Stripiness of payment providers so we can add a Test provider (and maybe MtGox / Balanced / Paypal / etc). Ref T2787. Test Plan: Ran unit tests. Reviewers: btrahan, chad Reviewed By: btrahan CC: aran Maniphest Tasks: T2787 Differential Revision: https://secure.phabricator.com/D5752
This commit is contained in:
parent
2b5c0c4b3b
commit
f790a2aeec
11 changed files with 225 additions and 4 deletions
|
@ -1586,11 +1586,15 @@ phutil_register_library_map(array(
|
||||||
'PhortuneDAO' => 'applications/phortune/storage/PhortuneDAO.php',
|
'PhortuneDAO' => 'applications/phortune/storage/PhortuneDAO.php',
|
||||||
'PhortuneLandingController' => 'applications/phortune/controller/PhortuneLandingController.php',
|
'PhortuneLandingController' => 'applications/phortune/controller/PhortuneLandingController.php',
|
||||||
'PhortuneMonthYearExpiryControl' => 'applications/phortune/control/PhortuneMonthYearExpiryControl.php',
|
'PhortuneMonthYearExpiryControl' => 'applications/phortune/control/PhortuneMonthYearExpiryControl.php',
|
||||||
|
'PhortuneMultiplePaymentProvidersException' => 'applications/phortune/exception/PhortuneMultiplePaymentProvidersException.php',
|
||||||
|
'PhortuneNoPaymentProviderException' => 'applications/phortune/exception/PhortuneNoPaymentProviderException.php',
|
||||||
'PhortunePaymentMethod' => 'applications/phortune/storage/PhortunePaymentMethod.php',
|
'PhortunePaymentMethod' => 'applications/phortune/storage/PhortunePaymentMethod.php',
|
||||||
'PhortunePaymentMethodEditController' => 'applications/phortune/controller/PhortunePaymentMethodEditController.php',
|
'PhortunePaymentMethodEditController' => 'applications/phortune/controller/PhortunePaymentMethodEditController.php',
|
||||||
'PhortunePaymentMethodListController' => 'applications/phortune/controller/PhortunePaymentMethodListController.php',
|
'PhortunePaymentMethodListController' => 'applications/phortune/controller/PhortunePaymentMethodListController.php',
|
||||||
'PhortunePaymentMethodQuery' => 'applications/phortune/query/PhortunePaymentMethodQuery.php',
|
'PhortunePaymentMethodQuery' => 'applications/phortune/query/PhortunePaymentMethodQuery.php',
|
||||||
'PhortunePaymentMethodViewController' => 'applications/phortune/controller/PhortunePaymentMethodViewController.php',
|
'PhortunePaymentMethodViewController' => 'applications/phortune/controller/PhortunePaymentMethodViewController.php',
|
||||||
|
'PhortunePaymentProvider' => 'applications/phortune/provider/PhortunePaymentProvider.php',
|
||||||
|
'PhortunePaymentProviderTestCase' => 'applications/phortune/provider/__tests__/PhortunePaymentProviderTestCase.php',
|
||||||
'PhortuneProduct' => 'applications/phortune/storage/PhortuneProduct.php',
|
'PhortuneProduct' => 'applications/phortune/storage/PhortuneProduct.php',
|
||||||
'PhortuneProductEditController' => 'applications/phortune/controller/PhortuneProductEditController.php',
|
'PhortuneProductEditController' => 'applications/phortune/controller/PhortuneProductEditController.php',
|
||||||
'PhortuneProductEditor' => 'applications/phortune/editor/PhortuneProductEditor.php',
|
'PhortuneProductEditor' => 'applications/phortune/editor/PhortuneProductEditor.php',
|
||||||
|
@ -1601,6 +1605,9 @@ phutil_register_library_map(array(
|
||||||
'PhortuneProductViewController' => 'applications/phortune/controller/PhortuneProductViewController.php',
|
'PhortuneProductViewController' => 'applications/phortune/controller/PhortuneProductViewController.php',
|
||||||
'PhortunePurchase' => 'applications/phortune/storage/PhortunePurchase.php',
|
'PhortunePurchase' => 'applications/phortune/storage/PhortunePurchase.php',
|
||||||
'PhortuneStripePaymentFormView' => 'applications/phortune/view/PhortuneStripePaymentFormView.php',
|
'PhortuneStripePaymentFormView' => 'applications/phortune/view/PhortuneStripePaymentFormView.php',
|
||||||
|
'PhortuneStripePaymentProvider' => 'applications/phortune/provider/PhortuneStripePaymentProvider.php',
|
||||||
|
'PhortuneTestExtraPaymentProvider' => 'applications/phortune/provider/__tests__/PhortuneTestExtraPaymentProvider.php',
|
||||||
|
'PhortuneTestPaymentProvider' => 'applications/phortune/provider/PhortuneTestPaymentProvider.php',
|
||||||
'PhortuneUtil' => 'applications/phortune/util/PhortuneUtil.php',
|
'PhortuneUtil' => 'applications/phortune/util/PhortuneUtil.php',
|
||||||
'PhrequentController' => 'applications/phrequent/controller/PhrequentController.php',
|
'PhrequentController' => 'applications/phrequent/controller/PhrequentController.php',
|
||||||
'PhrequentDAO' => 'applications/phrequent/storage/PhrequentDAO.php',
|
'PhrequentDAO' => 'applications/phrequent/storage/PhrequentDAO.php',
|
||||||
|
@ -3300,6 +3307,8 @@ phutil_register_library_map(array(
|
||||||
'PhortuneDAO' => 'PhabricatorLiskDAO',
|
'PhortuneDAO' => 'PhabricatorLiskDAO',
|
||||||
'PhortuneLandingController' => 'PhortuneController',
|
'PhortuneLandingController' => 'PhortuneController',
|
||||||
'PhortuneMonthYearExpiryControl' => 'AphrontFormControl',
|
'PhortuneMonthYearExpiryControl' => 'AphrontFormControl',
|
||||||
|
'PhortuneMultiplePaymentProvidersException' => 'Exception',
|
||||||
|
'PhortuneNoPaymentProviderException' => 'Exception',
|
||||||
'PhortunePaymentMethod' =>
|
'PhortunePaymentMethod' =>
|
||||||
array(
|
array(
|
||||||
0 => 'PhortuneDAO',
|
0 => 'PhortuneDAO',
|
||||||
|
@ -3309,6 +3318,7 @@ phutil_register_library_map(array(
|
||||||
'PhortunePaymentMethodListController' => 'PhabricatorController',
|
'PhortunePaymentMethodListController' => 'PhabricatorController',
|
||||||
'PhortunePaymentMethodQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
'PhortunePaymentMethodQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
||||||
'PhortunePaymentMethodViewController' => 'PhabricatorController',
|
'PhortunePaymentMethodViewController' => 'PhabricatorController',
|
||||||
|
'PhortunePaymentProviderTestCase' => 'PhabricatorTestCase',
|
||||||
'PhortuneProduct' =>
|
'PhortuneProduct' =>
|
||||||
array(
|
array(
|
||||||
0 => 'PhortuneDAO',
|
0 => 'PhortuneDAO',
|
||||||
|
@ -3323,6 +3333,9 @@ phutil_register_library_map(array(
|
||||||
'PhortuneProductViewController' => 'PhortuneController',
|
'PhortuneProductViewController' => 'PhortuneController',
|
||||||
'PhortunePurchase' => 'PhortuneDAO',
|
'PhortunePurchase' => 'PhortuneDAO',
|
||||||
'PhortuneStripePaymentFormView' => 'AphrontView',
|
'PhortuneStripePaymentFormView' => 'AphrontView',
|
||||||
|
'PhortuneStripePaymentProvider' => 'PhortunePaymentProvider',
|
||||||
|
'PhortuneTestExtraPaymentProvider' => 'PhortunePaymentProvider',
|
||||||
|
'PhortuneTestPaymentProvider' => 'PhortunePaymentProvider',
|
||||||
'PhrequentController' => 'PhabricatorController',
|
'PhrequentController' => 'PhabricatorController',
|
||||||
'PhrequentDAO' => 'PhabricatorLiskDAO',
|
'PhrequentDAO' => 'PhabricatorLiskDAO',
|
||||||
'PhrequentListController' => 'PhrequentController',
|
'PhrequentListController' => 'PhrequentController',
|
||||||
|
|
|
@ -96,8 +96,8 @@ final class PhortunePaymentMethodEditController
|
||||||
->setMetadata(
|
->setMetadata(
|
||||||
array(
|
array(
|
||||||
'type' => 'stripe.customer',
|
'type' => 'stripe.customer',
|
||||||
'stripeCustomerID' => $customer->id,
|
'stripe.customerID' => $customer->id,
|
||||||
'stripeTokenID' => $stripe_token,
|
'stripe.tokenID' => $stripe_token,
|
||||||
))
|
))
|
||||||
->save();
|
->save();
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class PhortuneMultiplePaymentProvidersException extends Exception {
|
||||||
|
|
||||||
|
public function __construct(PhortunePaymentMethod $method, array $providers) {
|
||||||
|
assert_instances_of($providers, 'PhortunePaymentProvider');
|
||||||
|
$type = $method->getMetadataValue('type');
|
||||||
|
|
||||||
|
$provider_names = array();
|
||||||
|
foreach ($providers as $provider) {
|
||||||
|
$provider_names[] = get_class($provider);
|
||||||
|
}
|
||||||
|
|
||||||
|
return parent::__construct(
|
||||||
|
"More than one payment provider can handle charging payments for this ".
|
||||||
|
"payment method. This is ambiguous and likely indicates that a payment ".
|
||||||
|
"provider is not properly implemented. You may be able to use a ".
|
||||||
|
"different payment method to complete this transaction. The payment ".
|
||||||
|
"method type is '{$type}'. The providers claiming to handle it are: ".
|
||||||
|
implode(', ', $provider_names).'.');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class PhortuneNoPaymentProviderException extends Exception {
|
||||||
|
|
||||||
|
public function __construct(PhortunePaymentMethod $method) {
|
||||||
|
$type = $method->getMetadataValue('type');
|
||||||
|
|
||||||
|
return parent::__construct(
|
||||||
|
"No available payment provider can handle charging payments for this ".
|
||||||
|
"payment method. You may be able to use a different payment method to ".
|
||||||
|
"complete this transaction. The payment method type is '{$type}'.");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
abstract class PhortunePaymentProvider {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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);
|
||||||
|
|
||||||
|
abstract protected function executeCharge(
|
||||||
|
PhortunePaymentMethod $payment_method,
|
||||||
|
PhortuneCharge $charge);
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class PhortuneStripePaymentProvider extends PhortunePaymentProvider {
|
||||||
|
|
||||||
|
public function canHandlePaymentMethod(PhortunePaymentMethod $method) {
|
||||||
|
$type = $method->getMetadataValue('type');
|
||||||
|
return ($type === 'stripe.customer');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @phutil-external-symbol class Stripe_Charge
|
||||||
|
*/
|
||||||
|
protected function executeCharge(
|
||||||
|
PhortunePaymentMethod $method,
|
||||||
|
PhortuneCharge $charge) {
|
||||||
|
|
||||||
|
$secret_key = $this->getSecretKey();
|
||||||
|
$params = array(
|
||||||
|
'amount' => $charge->getAmountInCents(),
|
||||||
|
'currency' => 'usd',
|
||||||
|
'customer' => $method->getMetadataValue('stripe.customerID'),
|
||||||
|
'description' => $charge->getPHID(),
|
||||||
|
'capture' => true,
|
||||||
|
);
|
||||||
|
|
||||||
|
$stripe_charge = Stripe_Charge::create($params, $secret_key);
|
||||||
|
$id = $stripe_charge->id;
|
||||||
|
if (!$id) {
|
||||||
|
throw new Exception("Stripe charge call did not return an ID!");
|
||||||
|
}
|
||||||
|
|
||||||
|
$charge->setMetadataValue('stripe.chargeID', $id);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getSecretKey() {
|
||||||
|
return PhabricatorEnv::getEnvConfig('stripe.secret-key');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class PhortuneTestPaymentProvider extends PhortunePaymentProvider {
|
||||||
|
|
||||||
|
public function canHandlePaymentMethod(PhortunePaymentMethod $method) {
|
||||||
|
$type = $method->getMetadataValue('type');
|
||||||
|
return ($type === 'test.cash' || $type === 'test.multiple');
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function executeCharge(
|
||||||
|
PhortunePaymentMethod $payment_method,
|
||||||
|
PhortuneCharge $charge) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class PhortunePaymentProviderTestCase extends PhabricatorTestCase {
|
||||||
|
|
||||||
|
public function getPhabricatorTestCaseConfiguration() {
|
||||||
|
return array(
|
||||||
|
self::PHABRICATOR_TESTCONFIG_BUILD_STORAGE_FIXTURES => true,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testNoPaymentProvider() {
|
||||||
|
$method = id(new PhortunePaymentMethod())
|
||||||
|
->setMetadataValue('type', 'hugs');
|
||||||
|
|
||||||
|
$caught = null;
|
||||||
|
try {
|
||||||
|
$provider = $method->buildPaymentProvider();
|
||||||
|
} catch (Exception $ex) {
|
||||||
|
$caught = $ex;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->assertEqual(
|
||||||
|
true,
|
||||||
|
($caught instanceof PhortuneNoPaymentProviderException),
|
||||||
|
'No provider should accept hugs; they are not a currency.');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testMultiplePaymentProviders() {
|
||||||
|
$method = id(new PhortunePaymentMethod())
|
||||||
|
->setMetadataValue('type', 'test.multiple');
|
||||||
|
|
||||||
|
$caught = null;
|
||||||
|
try {
|
||||||
|
$provider = $method->buildPaymentProvider();
|
||||||
|
} catch (Exception $ex) {
|
||||||
|
$caught = $ex;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->assertEqual(
|
||||||
|
true,
|
||||||
|
($caught instanceof PhortuneMultiplePaymentProvidersException),
|
||||||
|
'Expect exception when more than one provider handles a payment method.');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class PhortuneTestExtraPaymentProvider extends PhortunePaymentProvider {
|
||||||
|
|
||||||
|
public function canHandlePaymentMethod(PhortunePaymentMethod $method) {
|
||||||
|
$type = $method->getMetadataValue('type');
|
||||||
|
return ($type === 'test.multiple');
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function executeCharge(
|
||||||
|
PhortunePaymentMethod $payment_method,
|
||||||
|
PhortuneCharge $charge) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -11,6 +11,7 @@ final class PhortuneCharge extends PhortuneDAO {
|
||||||
|
|
||||||
const STATUS_PENDING = 'charge:pending';
|
const STATUS_PENDING = 'charge:pending';
|
||||||
const STATUS_AUTHORIZED = 'charge:authorized';
|
const STATUS_AUTHORIZED = 'charge:authorized';
|
||||||
|
const STATUS_CHARGING = 'charge:charging';
|
||||||
const STATUS_CHARGED = 'charge:charged';
|
const STATUS_CHARGED = 'charge:charged';
|
||||||
const STATUS_FAILED = 'charge:failed';
|
const STATUS_FAILED = 'charge:failed';
|
||||||
|
|
||||||
|
@ -19,7 +20,7 @@ final class PhortuneCharge extends PhortuneDAO {
|
||||||
protected $paymentMethodPHID;
|
protected $paymentMethodPHID;
|
||||||
protected $amountInCents;
|
protected $amountInCents;
|
||||||
protected $status;
|
protected $status;
|
||||||
protected $metadata;
|
protected $metadata = array();
|
||||||
|
|
||||||
public function getConfiguration() {
|
public function getConfiguration() {
|
||||||
return array(
|
return array(
|
||||||
|
|
|
@ -16,7 +16,7 @@ final class PhortunePaymentMethod extends PhortuneDAO
|
||||||
protected $accountPHID;
|
protected $accountPHID;
|
||||||
protected $authorPHID;
|
protected $authorPHID;
|
||||||
protected $expiresEpoch;
|
protected $expiresEpoch;
|
||||||
protected $metadata;
|
protected $metadata = array();
|
||||||
|
|
||||||
private $account;
|
private $account;
|
||||||
|
|
||||||
|
@ -50,6 +50,40 @@ final class PhortunePaymentMethod extends PhortuneDAO
|
||||||
return pht('Expires %s', date('m/y'), $this->getExpiresEpoch());
|
return pht('Expires %s', date('m/y'), $this->getExpiresEpoch());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getMetadataValue($key, $default = null) {
|
||||||
|
return idx($this->getMetadata(), $key, $default);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setMetadataValue($key, $value) {
|
||||||
|
$this->metadata[$key] = $value;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function buildPaymentProvider() {
|
||||||
|
$providers = id(new PhutilSymbolLoader())
|
||||||
|
->setAncestorClass('PhortunePaymentProvider')
|
||||||
|
->setConcreteOnly(true)
|
||||||
|
->selectAndLoadSymbols();
|
||||||
|
|
||||||
|
$accept = array();
|
||||||
|
foreach ($providers as $provider) {
|
||||||
|
$obj = newv($provider['name'], array());
|
||||||
|
if ($obj->canHandlePaymentMethod($this)) {
|
||||||
|
$accept[] = $obj;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$accept) {
|
||||||
|
throw new PhortuneNoPaymentProviderException($this);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count($accept) > 1) {
|
||||||
|
throw new PhortuneMultiplePaymentProvidersException($this, $accept);
|
||||||
|
}
|
||||||
|
|
||||||
|
return head($accept);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* -( PhabricatorPolicyInterface )----------------------------------------- */
|
/* -( PhabricatorPolicyInterface )----------------------------------------- */
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue