mirror of
https://we.phorge.it/source/phorge.git
synced 2024-12-23 05:50:55 +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',
|
||||
'PhortuneLandingController' => 'applications/phortune/controller/PhortuneLandingController.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',
|
||||
'PhortunePaymentMethodEditController' => 'applications/phortune/controller/PhortunePaymentMethodEditController.php',
|
||||
'PhortunePaymentMethodListController' => 'applications/phortune/controller/PhortunePaymentMethodListController.php',
|
||||
'PhortunePaymentMethodQuery' => 'applications/phortune/query/PhortunePaymentMethodQuery.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',
|
||||
'PhortuneProductEditController' => 'applications/phortune/controller/PhortuneProductEditController.php',
|
||||
'PhortuneProductEditor' => 'applications/phortune/editor/PhortuneProductEditor.php',
|
||||
|
@ -1601,6 +1605,9 @@ phutil_register_library_map(array(
|
|||
'PhortuneProductViewController' => 'applications/phortune/controller/PhortuneProductViewController.php',
|
||||
'PhortunePurchase' => 'applications/phortune/storage/PhortunePurchase.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',
|
||||
'PhrequentController' => 'applications/phrequent/controller/PhrequentController.php',
|
||||
'PhrequentDAO' => 'applications/phrequent/storage/PhrequentDAO.php',
|
||||
|
@ -3300,6 +3307,8 @@ phutil_register_library_map(array(
|
|||
'PhortuneDAO' => 'PhabricatorLiskDAO',
|
||||
'PhortuneLandingController' => 'PhortuneController',
|
||||
'PhortuneMonthYearExpiryControl' => 'AphrontFormControl',
|
||||
'PhortuneMultiplePaymentProvidersException' => 'Exception',
|
||||
'PhortuneNoPaymentProviderException' => 'Exception',
|
||||
'PhortunePaymentMethod' =>
|
||||
array(
|
||||
0 => 'PhortuneDAO',
|
||||
|
@ -3309,6 +3318,7 @@ phutil_register_library_map(array(
|
|||
'PhortunePaymentMethodListController' => 'PhabricatorController',
|
||||
'PhortunePaymentMethodQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
||||
'PhortunePaymentMethodViewController' => 'PhabricatorController',
|
||||
'PhortunePaymentProviderTestCase' => 'PhabricatorTestCase',
|
||||
'PhortuneProduct' =>
|
||||
array(
|
||||
0 => 'PhortuneDAO',
|
||||
|
@ -3323,6 +3333,9 @@ phutil_register_library_map(array(
|
|||
'PhortuneProductViewController' => 'PhortuneController',
|
||||
'PhortunePurchase' => 'PhortuneDAO',
|
||||
'PhortuneStripePaymentFormView' => 'AphrontView',
|
||||
'PhortuneStripePaymentProvider' => 'PhortunePaymentProvider',
|
||||
'PhortuneTestExtraPaymentProvider' => 'PhortunePaymentProvider',
|
||||
'PhortuneTestPaymentProvider' => 'PhortunePaymentProvider',
|
||||
'PhrequentController' => 'PhabricatorController',
|
||||
'PhrequentDAO' => 'PhabricatorLiskDAO',
|
||||
'PhrequentListController' => 'PhrequentController',
|
||||
|
|
|
@ -96,8 +96,8 @@ final class PhortunePaymentMethodEditController
|
|||
->setMetadata(
|
||||
array(
|
||||
'type' => 'stripe.customer',
|
||||
'stripeCustomerID' => $customer->id,
|
||||
'stripeTokenID' => $stripe_token,
|
||||
'stripe.customerID' => $customer->id,
|
||||
'stripe.tokenID' => $stripe_token,
|
||||
))
|
||||
->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_AUTHORIZED = 'charge:authorized';
|
||||
const STATUS_CHARGING = 'charge:charging';
|
||||
const STATUS_CHARGED = 'charge:charged';
|
||||
const STATUS_FAILED = 'charge:failed';
|
||||
|
||||
|
@ -19,7 +20,7 @@ final class PhortuneCharge extends PhortuneDAO {
|
|||
protected $paymentMethodPHID;
|
||||
protected $amountInCents;
|
||||
protected $status;
|
||||
protected $metadata;
|
||||
protected $metadata = array();
|
||||
|
||||
public function getConfiguration() {
|
||||
return array(
|
||||
|
|
|
@ -16,7 +16,7 @@ final class PhortunePaymentMethod extends PhortuneDAO
|
|||
protected $accountPHID;
|
||||
protected $authorPHID;
|
||||
protected $expiresEpoch;
|
||||
protected $metadata;
|
||||
protected $metadata = array();
|
||||
|
||||
private $account;
|
||||
|
||||
|
@ -50,6 +50,40 @@ final class PhortunePaymentMethod extends PhortuneDAO
|
|||
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 )----------------------------------------- */
|
||||
|
||||
|
|
Loading…
Reference in a new issue