1
0
Fork 0
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:
epriestley 2013-04-25 09:45:43 -07:00
parent 2b5c0c4b3b
commit f790a2aeec
11 changed files with 225 additions and 4 deletions

View file

@ -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',

View file

@ -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();

View file

@ -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).'.');
}
}

View file

@ -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}'.");
}
}

View file

@ -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);
}

View file

@ -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');
}
}

View file

@ -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;
}
}

View file

@ -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.');
}
}

View file

@ -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;
}
}

View file

@ -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(

View file

@ -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 )----------------------------------------- */