Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F15470812
D10002.id24041.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
18 KB
Referenced Files
None
Subscribers
None
D10002.id24041.diff
View Options
diff --git a/resources/sql/autopatches/20140721.phortune.3.charge.sql b/resources/sql/autopatches/20140721.phortune.3.charge.sql
new file mode 100644
--- /dev/null
+++ b/resources/sql/autopatches/20140721.phortune.3.charge.sql
@@ -0,0 +1,16 @@
+CREATE TABLE {$NAMESPACE}_phortune.phortune_charge (
+ id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
+ phid VARCHAR(64) NOT NULL COLLATE utf8_bin,
+ accountPHID VARCHAR(64) NOT NULL COLLATE utf8_bin,
+ authorPHID VARCHAR(64) NOT NULL COLLATE utf8_bin,
+ cartPHID VARCHAR(64) NOT NULL COLLATE utf8_bin,
+ paymentMethodPHID VARCHAR(64) NOT NULL COLLATE utf8_bin,
+ amountInCents INT NOT NULL,
+ status VARCHAR(32) 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),
+ KEY `key_cart` (cartPHID),
+ KEY `key_account` (accountPHID)
+) ENGINE=InnoDB, COLLATE utf8_general_ci;
diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -2498,6 +2498,7 @@
'PhortuneCart' => 'applications/phortune/storage/PhortuneCart.php',
'PhortuneCartQuery' => 'applications/phortune/query/PhortuneCartQuery.php',
'PhortuneCharge' => 'applications/phortune/storage/PhortuneCharge.php',
+ 'PhortuneChargeQuery' => 'applications/phortune/query/PhortuneChargeQuery.php',
'PhortuneConstants' => 'applications/phortune/constants/PhortuneConstants.php',
'PhortuneController' => 'applications/phortune/controller/PhortuneController.php',
'PhortuneCreditCardForm' => 'applications/phortune/view/PhortuneCreditCardForm.php',
@@ -5382,8 +5383,13 @@
'PhabricatorPolicyInterface',
),
'PhortuneCartQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
- 'PhortuneCharge' => 'PhortuneDAO',
+ 'PhortuneCharge' => array(
+ 'PhortuneDAO',
+ 'PhabricatorPolicyInterface',
+ ),
+ 'PhortuneChargeQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'PhortuneController' => 'PhabricatorController',
+ 'PhortuneCurrency' => 'Phobject',
'PhortuneCurrencyTestCase' => 'PhabricatorTestCase',
'PhortuneDAO' => 'PhabricatorLiskDAO',
'PhortuneErrCode' => 'PhortuneConstants',
diff --git a/src/applications/phortune/controller/PhortuneAccountBuyController.php b/src/applications/phortune/controller/PhortuneAccountBuyController.php
--- a/src/applications/phortune/controller/PhortuneAccountBuyController.php
+++ b/src/applications/phortune/controller/PhortuneAccountBuyController.php
@@ -11,10 +11,10 @@
public function processRequest() {
$request = $this->getRequest();
- $user = $request->getUser();
+ $viewer = $request->getUser();
$cart = id(new PhortuneCartQuery())
- ->setViewer($user)
+ ->setViewer($viewer)
->withIDs(array($this->id))
->needPurchases(true)
->executeOne();
@@ -25,6 +25,56 @@
$account = $cart->getAccount();
$account_uri = $this->getApplicationURI($account->getID().'/');
+ $methods = id(new PhortunePaymentMethodQuery())
+ ->setViewer($viewer)
+ ->withAccountPHIDs(array($account->getPHID()))
+ ->withStatus(PhortunePaymentMethodQuery::STATUS_OPEN)
+ ->execute();
+
+ $e_method = null;
+ $errors = array();
+
+ if ($request->isFormPost()) {
+
+ // Require CAN_EDIT on the cart to actually make purchases.
+
+ PhabricatorPolicyFilter::requireCapability(
+ $viewer,
+ $cart,
+ PhabricatorPolicyCapability::CAN_EDIT);
+
+ $method_id = $request->getInt('paymentMethodID');
+ $method = idx($methods, $method_id);
+ if (!$method) {
+ $e_method = pht('Required');
+ $errors[] = pht('You must choose a payment method.');
+ }
+
+ if (!$errors) {
+ $provider = $method->buildPaymentProvider();
+
+ $charge = id(new PhortuneCharge())
+ ->setAccountPHID($account->getPHID())
+ ->setCartPHID($cart->getPHID())
+ ->setAuthorPHID($viewer->getPHID())
+ ->setPaymentMethodPHID($method->getPHID())
+ ->setAmountInCents($cart->getTotalPriceInCents())
+ ->setStatus(PhortuneCharge::STATUS_PENDING);
+
+ $charge->openTransaction();
+ $charge->save();
+
+ // TODO: We should be setting some kind of status on the cart here.
+ $cart->save();
+ $charge->saveTransaction();
+
+ $provider->applyCharge($method, $charge);
+
+ throw new Exception('Executed a charge! Your money is gone forever!');
+ }
+ }
+
+
$rows = array();
$total = 0;
foreach ($cart->getPurchases() as $purchase) {
@@ -66,20 +116,11 @@
$cart_box = id(new PHUIObjectBoxView())
->setHeaderText(pht('Your Cart'))
+ ->setFormErrors($errors)
->appendChild($table);
$title = pht('Buy Stuff');
-
- $methods = id(new PhortunePaymentMethodQuery())
- ->setViewer($user)
- ->withAccountPHIDs(array($account->getPHID()))
- ->withStatus(PhortunePaymentMethodQuery::STATUS_OPEN)
- ->execute();
-
- $method_control = id(new AphrontFormRadioButtonControl())
- ->setLabel(pht('Payment Method'));
-
if (!$methods) {
$method_control = id(new AphrontFormStaticControl())
->setLabel(pht('Payment Method'))
@@ -98,11 +139,13 @@
}
}
+ $method_control->setError($e_method);
+
$payment_method_uri = $this->getApplicationURI(
$account->getID().'/paymentmethod/edit/');
$form = id(new AphrontFormView())
- ->setUser($user)
+ ->setUser($viewer)
->appendChild($method_control);
$add_providers = PhortunePaymentProvider::getProvidersForAddPaymentMethod();
@@ -137,7 +180,7 @@
$one_time_options[] = $provider->renderOneTimePaymentButton(
$account,
$cart,
- $user);
+ $viewer);
}
$provider_form = new PHUIFormLayoutView();
diff --git a/src/applications/phortune/controller/PhortuneAccountViewController.php b/src/applications/phortune/controller/PhortuneAccountViewController.php
--- a/src/applications/phortune/controller/PhortuneAccountViewController.php
+++ b/src/applications/phortune/controller/PhortuneAccountViewController.php
@@ -56,6 +56,7 @@
$payment_methods = $this->buildPaymentMethodsSection($account);
$purchase_history = $this->buildPurchaseHistorySection($account);
+ $charge_history = $this->buildChargeHistorySection($account);
$account_history = $this->buildAccountHistorySection($account);
$object_box = id(new PHUIObjectBoxView())
@@ -68,6 +69,7 @@
$object_box,
$payment_methods,
$purchase_history,
+ $charge_history,
$account_history,
),
array(
@@ -141,6 +143,56 @@
->setHeader($header);
}
+ private function buildChargeHistorySection(PhortuneAccount $account) {
+ $request = $this->getRequest();
+ $viewer = $request->getUser();
+
+ $charges = id(new PhortuneChargeQuery())
+ ->setViewer($viewer)
+ ->withAccountPHIDs(array($account->getPHID()))
+ ->execute();
+
+ $rows = array();
+ foreach ($charges as $charge) {
+ $rows[] = array(
+ $charge->getID(),
+ $charge->getCartPHID(),
+ $charge->getPaymentMethodPHID(),
+ PhortuneCurrency::newFromUSDCents($charge->getAmountInCents())
+ ->formatForDisplay(),
+ $charge->getStatus(),
+ phabricator_datetime($charge->getDateCreated(), $viewer),
+ );
+ }
+
+ $charge_table = id(new AphrontTableView($rows))
+ ->setHeaders(
+ array(
+ pht('Charge ID'),
+ pht('Cart'),
+ pht('Method'),
+ pht('Amount'),
+ pht('Status'),
+ pht('Created'),
+ ))
+ ->setColumnClasses(
+ array(
+ '',
+ '',
+ '',
+ 'wide right',
+ '',
+ '',
+ ));
+
+ $header = id(new PHUIHeaderView())
+ ->setHeader(pht('Charge History'));
+
+ return id(new PHUIObjectBoxView())
+ ->setHeader($header)
+ ->appendChild($charge_table);
+ }
+
private function buildAccountHistorySection(PhortuneAccount $account) {
$request = $this->getRequest();
$user = $request->getUser();
diff --git a/src/applications/phortune/currency/PhortuneCurrency.php b/src/applications/phortune/currency/PhortuneCurrency.php
--- a/src/applications/phortune/currency/PhortuneCurrency.php
+++ b/src/applications/phortune/currency/PhortuneCurrency.php
@@ -1,6 +1,6 @@
<?php
-final class PhortuneCurrency {
+final class PhortuneCurrency extends Phobject {
private $value;
private $currency;
@@ -52,6 +52,18 @@
return $obj;
}
+ public static function newFromList(array $list) {
+ assert_instances_of($list, 'PhortuneCurrency');
+
+ $total = 0;
+ foreach ($list as $item) {
+ // TODO: This should check for integer overflows, etc.
+ $total += $item->getValue();
+ }
+
+ return PhortuneCurrency::newFromUSDCents($total);
+ }
+
public static function newFromUSDCents($cents) {
if (!is_int($cents)) {
throw new Exception(
diff --git a/src/applications/phortune/provider/PhortunePaymentProvider.php b/src/applications/phortune/provider/PhortunePaymentProvider.php
--- a/src/applications/phortune/provider/PhortunePaymentProvider.php
+++ b/src/applications/phortune/provider/PhortunePaymentProvider.php
@@ -97,6 +97,19 @@
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(
PhortunePaymentMethod $payment_method,
PhortuneCharge $charge);
diff --git a/src/applications/phortune/provider/PhortuneStripePaymentProvider.php b/src/applications/phortune/provider/PhortuneStripePaymentProvider.php
--- a/src/applications/phortune/provider/PhortuneStripePaymentProvider.php
+++ b/src/applications/phortune/provider/PhortuneStripePaymentProvider.php
@@ -40,6 +40,9 @@
PhortunePaymentMethod $method,
PhortuneCharge $charge) {
+ $root = dirname(phutil_get_library_root('phabricator'));
+ require_once $root.'/externals/stripe-php/lib/Stripe.php';
+
$secret_key = $this->getSecretKey();
$params = array(
'amount' => $charge->getAmountInCents(),
diff --git a/src/applications/phortune/query/PhortuneCartQuery.php b/src/applications/phortune/query/PhortuneCartQuery.php
--- a/src/applications/phortune/query/PhortuneCartQuery.php
+++ b/src/applications/phortune/query/PhortuneCartQuery.php
@@ -49,6 +49,7 @@
$account = idx($accounts, $cart->getAccountPHID());
if (!$account) {
unset($carts[$key]);
+ continue;
}
$cart->attachAccount($account);
}
diff --git a/src/applications/phortune/query/PhortunePurchaseQuery.php b/src/applications/phortune/query/PhortuneChargeQuery.php
copy from src/applications/phortune/query/PhortunePurchaseQuery.php
copy to src/applications/phortune/query/PhortuneChargeQuery.php
--- a/src/applications/phortune/query/PhortunePurchaseQuery.php
+++ b/src/applications/phortune/query/PhortuneChargeQuery.php
@@ -1,11 +1,11 @@
<?php
-final class PhortunePurchaseQuery
+final class PhortuneChargeQuery
extends PhabricatorCursorPagedPolicyAwareQuery {
private $ids;
private $phids;
- private $cartPHIDs;
+ private $accountPHIDs;
public function withIDs(array $ids) {
$this->ids = $ids;
@@ -17,18 +17,18 @@
return $this;
}
- public function withCartPHIDs(array $cart_phids) {
- $this->cartPHIDs = $cart_phids;
+ public function withAccountPHIDs(array $account_phids) {
+ $this->accountPHIDs = $account_phids;
return $this;
}
protected function loadPage() {
- $table = new PhortunePurchase();
+ $table = new PhortuneCharge();
$conn = $table->establishConnection('r');
$rows = queryfx_all(
$conn,
- 'SELECT purchase.* FROM %T purchase %Q %Q %Q',
+ 'SELECT charge.* FROM %T charge %Q %Q %Q',
$table->getTableName(),
$this->buildWhereClause($conn),
$this->buildOrderClause($conn),
@@ -37,22 +37,24 @@
return $table->loadAllFromArray($rows);
}
- protected function willFilterPage(array $purchases) {
- $carts = id(new PhabricatorObjectQuery())
+ protected function willFilterPage(array $charges) {
+ $accounts = id(new PhortuneAccountQuery())
->setViewer($this->getViewer())
->setParentQuery($this)
- ->withPHIDs(mpull($purchases, 'getCartPHID'))
+ ->withPHIDs(mpull($charges, 'getAccountPHID'))
->execute();
+ $accounts = mpull($accounts, null, 'getPHID');
- foreach ($purchases as $key => $purchase) {
- $cart = idx($carts, $purchase->getCartPHID());
- if (!$cart) {
- unset($purchases[$key]);
+ foreach ($charges as $key => $charge) {
+ $account = idx($accounts, $charge->getAccountPHID());
+ if (!$account) {
+ unset($charges[$key]);
+ continue;
}
- $purchase->attachCart($cart);
+ $charge->attachAccount($account);
}
- return $purchases;
+ return $charges;
}
private function buildWhereClause(AphrontDatabaseConnection $conn) {
@@ -63,22 +65,22 @@
if ($this->ids !== null) {
$where[] = qsprintf(
$conn,
- 'purchase.id IN (%Ld)',
+ 'charge.id IN (%Ld)',
$this->ids);
}
if ($this->phids !== null) {
$where[] = qsprintf(
$conn,
- 'purchase.phid IN (%Ls)',
+ 'charge.phid IN (%Ls)',
$this->phids);
}
- if ($this->cartPHIDs !== null) {
+ if ($this->accountPHIDs !== null) {
$where[] = qsprintf(
$conn,
- 'purchase.cartPHID IN (%Ls)',
- $this->cartPHIDs);
+ 'charge.accountPHID IN (%Ls)',
+ $this->accountPHIDs);
}
return $this->formatWhereClause($where);
diff --git a/src/applications/phortune/query/PhortunePurchaseQuery.php b/src/applications/phortune/query/PhortunePurchaseQuery.php
--- a/src/applications/phortune/query/PhortunePurchaseQuery.php
+++ b/src/applications/phortune/query/PhortunePurchaseQuery.php
@@ -38,16 +38,18 @@
}
protected function willFilterPage(array $purchases) {
- $carts = id(new PhabricatorObjectQuery())
+ $carts = id(new PhortuneCartQuery())
->setViewer($this->getViewer())
->setParentQuery($this)
->withPHIDs(mpull($purchases, 'getCartPHID'))
->execute();
+ $carts = mpull($carts, null, 'getPHID');
foreach ($purchases as $key => $purchase) {
$cart = idx($carts, $purchase->getCartPHID());
if (!$cart) {
unset($purchases[$key]);
+ continue;
}
$purchase->attachCart($cart);
}
diff --git a/src/applications/phortune/storage/PhortuneCart.php b/src/applications/phortune/storage/PhortuneCart.php
--- a/src/applications/phortune/storage/PhortuneCart.php
+++ b/src/applications/phortune/storage/PhortuneCart.php
@@ -47,6 +47,16 @@
return $this->assertAttached($this->account);
}
+ public function getTotalPriceInCents() {
+ $prices = array();
+ foreach ($this->getPurchases() as $purchase) {
+ $prices[] = PhortuneCurrency::newFromUSDCents(
+ $purchase->getTotalPriceInCents());
+ }
+
+ return PhortuneCurrency::newFromList($prices)->getValue();
+ }
+
/* -( PhabricatorPolicyInterface )----------------------------------------- */
diff --git a/src/applications/phortune/storage/PhortuneCharge.php b/src/applications/phortune/storage/PhortuneCharge.php
--- a/src/applications/phortune/storage/PhortuneCharge.php
+++ b/src/applications/phortune/storage/PhortuneCharge.php
@@ -2,12 +2,12 @@
/**
* A charge is a charge (or credit) against an account and represents an actual
- * transfer of funds. Each charge is normally associated with a product, but a
- * product may have multiple charges. For example, a subscription may have
- * monthly charges, or a product may have a failed charge followed by a
- * successful charge.
+ * transfer of funds. Each charge is normally associated with a cart, but a
+ * cart may have multiple charges. For example, a product may have a failed
+ * charge followed by a successful charge.
*/
-final class PhortuneCharge extends PhortuneDAO {
+final class PhortuneCharge extends PhortuneDAO
+ implements PhabricatorPolicyInterface {
const STATUS_PENDING = 'charge:pending';
const STATUS_AUTHORIZED = 'charge:authorized';
@@ -16,12 +16,15 @@
const STATUS_FAILED = 'charge:failed';
protected $accountPHID;
- protected $purchasePHID;
+ protected $authorPHID;
+ protected $cartPHID;
protected $paymentMethodPHID;
protected $amountInCents;
protected $status;
protected $metadata = array();
+ private $account = self::ATTACHABLE;
+
public function getConfiguration() {
return array(
self::CONFIG_AUX_PHID => true,
@@ -36,4 +39,49 @@
PhabricatorPHIDConstants::PHID_TYPE_CHRG);
}
+ protected function didReadData() {
+ // The payment processing code is strict about types.
+ $this->amountInCents = (int)$this->amountInCents;
+ }
+
+ 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 getAccount() {
+ return $this->assertAttached($this->account);
+ }
+
+ public function attachAccount(PhortuneAccount $account) {
+ $this->account = $account;
+ return $this;
+ }
+
+
+/* -( PhabricatorPolicyInterface )----------------------------------------- */
+
+
+ public function getCapabilities() {
+ return array(
+ PhabricatorPolicyCapability::CAN_VIEW,
+ );
+ }
+
+ public function getPolicy($capability) {
+ return $this->getAccount()->getPolicy($capability);
+ }
+
+ public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
+ return $this->getAccount()->hasAutomaticCapability($capability, $viewer);
+ }
+
+ public function describeAutomaticCapability($capability) {
+ return pht('Charges inherit the policies of the associated account.');
+ }
+
}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sun, Apr 6, 1:42 AM (6 d, 2 h ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
7733834
Default Alt Text
D10002.id24041.diff (18 KB)
Attached To
Mode
D10002: Phortune Charges
Attached
Detach File
Event Timeline
Log In to Comment