Page MenuHomePhabricator

D10639.id.diff
No OneTemporary

D10639.id.diff

diff --git a/src/applications/phortune/controller/PhortuneCartCheckoutController.php b/src/applications/phortune/controller/PhortuneCartCheckoutController.php
--- a/src/applications/phortune/controller/PhortuneCartCheckoutController.php
+++ b/src/applications/phortune/controller/PhortuneCartCheckoutController.php
@@ -38,6 +38,25 @@
// This is the expected, normal state for a cart that's ready for
// checkout.
break;
+ case PhortuneCart::STATUS_PURCHASING:
+ // We've started the purchase workflow for this cart, but were not able
+ // to complete it. If the workflow is on an external site, this could
+ // happen because the user abandoned the workflow. Just return them to
+ // the right place so they can resume where they left off.
+ $uri = $cart->getMetadataValue('provider.checkoutURI');
+ if ($uri !== null) {
+ return id(new AphrontRedirectResponse())
+ ->setIsExternal(true)
+ ->setURI($uri);
+ }
+
+ return $this->newDialog()
+ ->setTitle(pht('Charge Failed'))
+ ->appendParagraph(
+ pht(
+ 'Failed to charge this cart.'))
+ ->addCancelButton($cancel_uri);
+ break;
case PhortuneCart::STATUS_CHARGED:
// TODO: This is really bad (we took your money and at least partially
// failed to fulfill your order) and should have better steps forward.
@@ -89,24 +108,8 @@
if (!$errors) {
$provider = $method->buildPaymentProvider();
- $charge = id(new PhortuneCharge())
- ->setAccountPHID($account->getPHID())
- ->setCartPHID($cart->getPHID())
- ->setAuthorPHID($viewer->getPHID())
- ->setPaymentProviderKey($provider->getProviderKey())
- ->setPaymentMethodPHID($method->getPHID())
- ->setAmountAsCurrency($cart->getTotalPriceAsCurrency())
- ->setStatus(PhortuneCharge::STATUS_PENDING);
-
- $charge->openTransaction();
- $charge->save();
-
- $cart->setStatus(PhortuneCart::STATUS_PURCHASING);
- $cart->save();
- $charge->saveTransaction();
-
+ $charge = $cart->willApplyCharge($viewer, $provider, $method);
$provider->applyCharge($method, $charge);
-
$cart->didApplyCharge($charge);
$done_uri = $cart->getDoneURI();
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
@@ -204,11 +204,13 @@
->setText($description)
->setSubtext($details);
+ // NOTE: We generate a local URI to make sure the form picks up CSRF tokens.
$uri = $this->getControllerURI(
'checkout',
array(
'cartID' => $cart->getID(),
- ));
+ ),
+ $local = true);
return phabricator_form(
$user,
@@ -225,7 +227,8 @@
final public function getControllerURI(
$action,
- array $params = array()) {
+ array $params = array(),
+ $local = false) {
$digest = PhabricatorHash::digestForIndex($this->getProviderKey());
@@ -235,7 +238,11 @@
$uri = new PhutilURI($path);
$uri->setQueryParams($params);
- return PhabricatorEnv::getURI((string)$uri);
+ if ($local) {
+ return $uri;
+ } else {
+ return PhabricatorEnv::getURI((string)$uri);
+ }
}
public function canRespondToControllerAction($action) {
diff --git a/src/applications/phortune/provider/PhortuneWePayPaymentProvider.php b/src/applications/phortune/provider/PhortuneWePayPaymentProvider.php
--- a/src/applications/phortune/provider/PhortuneWePayPaymentProvider.php
+++ b/src/applications/phortune/provider/PhortuneWePayPaymentProvider.php
@@ -91,8 +91,6 @@
return new Aphront404Response();
}
- $cart_uri = '/phortune/cart/'.$cart->getID().'/';
-
$root = dirname(phutil_get_library_root('phabricator'));
require_once $root.'/externals/wepay/wepay.php';
@@ -102,6 +100,29 @@
$wepay = new WePay($this->getWePayAccessToken());
+ $charge = id(new PhortuneChargeQuery())
+ ->setViewer($viewer)
+ ->withCartPHIDs(array($cart->getPHID()))
+ ->withStatuses(
+ array(
+ PhortuneCharge::STATUS_CHARGING,
+ ))
+ ->executeOne();
+
+ switch ($controller->getAction()) {
+ case 'checkout':
+ if ($charge) {
+ throw new Exception(pht('Cart is already charging!'));
+ }
+ break;
+ case 'charge':
+ case 'cancel':
+ if (!$charge) {
+ throw new Exception(pht('Cart is not charging yet!'));
+ }
+ break;
+ }
+
switch ($controller->getAction()) {
case 'checkout':
$return_uri = $this->getControllerURI(
@@ -142,10 +163,13 @@
'funding_sources' => 'bank,cc'
);
+ $cart->willApplyCharge($viewer, $this);
+
$result = $wepay->request('checkout/create', $params);
- // TODO: We must store "$result->checkout_id" on the Cart since the
- // user might not end up back here. Really this needs a bunch of junk.
+ $cart->setMetadataValue('provider.checkoutURI', $result->checkout_uri);
+ $cart->setMetadataValue('wepay.checkoutID', $result->checkout_id);
+ $cart->save();
$uri = new PhutilURI($result->checkout_uri);
return id(new AphrontRedirectResponse())
@@ -175,38 +199,22 @@
$result->state));
}
- $currency = PhortuneCurrency::newFromString($checkout->gross, 'USD');
-
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
-
- $charge = id(new PhortuneCharge())
- ->setAmountAsCurrency($currency)
- ->setAccountPHID($cart->getAccount()->getPHID())
- ->setAuthorPHID($viewer->getPHID())
- ->setPaymentProviderKey($this->getProviderKey())
- ->setCartPHID($cart->getPHID())
- ->setStatus(PhortuneCharge::STATUS_CHARGING)
- ->save();
-
- $cart->openTransaction();
- $charge->setStatus(PhortuneCharge::STATUS_CHARGED);
- $charge->save();
-
- $cart->setStatus(PhortuneCart::STATUS_PURCHASED);
- $cart->save();
- $cart->saveTransaction();
-
+ $cart->didApplyCharge($charge);
unset($unguarded);
return id(new AphrontRedirectResponse())
- ->setIsExternal(true)
- ->setURI($cart_uri);
+ ->setURI($cart->getDoneURI());
case 'cancel':
- var_dump($_REQUEST);
+ // TODO: I don't know how it's possible to cancel out of a WePay
+ // charge workflow.
+ throw new Exception(
+ pht('How did you get here? WePay has no cancel flow in its UI...?'));
break;
}
- throw new Exception("The rest of this isn't implemented yet.");
+ throw new Exception(
+ pht('Unsupported action "%s".', $controller->getAction()));
}
diff --git a/src/applications/phortune/query/PhortuneChargeQuery.php b/src/applications/phortune/query/PhortuneChargeQuery.php
--- a/src/applications/phortune/query/PhortuneChargeQuery.php
+++ b/src/applications/phortune/query/PhortuneChargeQuery.php
@@ -7,6 +7,7 @@
private $phids;
private $accountPHIDs;
private $cartPHIDs;
+ private $statuses;
private $needCarts;
@@ -30,6 +31,11 @@
return $this;
}
+ public function withStatuses(array $statuses) {
+ $this->statuses = $statuses;
+ return $this;
+ }
+
public function needCarts($need_carts) {
$this->needCarts = $need_carts;
return $this;
@@ -121,6 +127,13 @@
$this->cartPHIDs);
}
+ if ($this->statuses !== null) {
+ $where[] = qsprintf(
+ $conn,
+ 'charge.status IN (%Ls)',
+ $this->statuses);
+ }
+
return $this->formatWhereClause($where);
}
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
@@ -52,17 +52,67 @@
return $this;
}
- public function didApplyCharge(PhortuneCharge $charge) {
- if ($this->getStatus() !== self::STATUS_PURCHASING) {
- throw new Exception(
- pht(
- 'Cart has wrong status ("%s") to call didApplyCharge(), expected '.
- '"%s".',
- $this->getStatus(),
- self::STATUS_PURCHASING));
+ public function willApplyCharge(
+ PhabricatorUser $actor,
+ PhortunePaymentProvider $provider,
+ PhortunePaymentMethod $method = null) {
+
+ $account = $this->getAccount();
+
+ $charge = PhortuneCharge::initializeNewCharge()
+ ->setAccountPHID($account->getPHID())
+ ->setCartPHID($this->getPHID())
+ ->setAuthorPHID($actor->getPHID())
+ ->setPaymentProviderKey($provider->getProviderKey())
+ ->setAmountAsCurrency($this->getTotalPriceAsCurrency());
+
+ if ($method) {
+ $charge->setPaymentMethodPHID($method->getPHID());
}
- $this->setStatus(self::STATUS_CHARGED)->save();
+ $this->openTransaction();
+ $this->beginReadLocking();
+
+ $copy = clone $this;
+ $copy->reload();
+
+ if ($copy->getStatus() !== self::STATUS_READY) {
+ throw new Exception(
+ pht(
+ 'Cart has wrong status ("%s") to call willApplyCharge(), expected '.
+ '"%s".',
+ $copy->getStatus(),
+ self::STATUS_READY));
+ }
+
+ $charge->save();
+ $this->setStatus(PhortuneCart::STATUS_PURCHASING)->save();
+ $this->saveTransaction();
+
+ return $charge;
+ }
+
+ public function didApplyCharge(PhortuneCharge $charge) {
+ $charge->setStatus(PhortuneCharge::STATUS_CHARGED);
+
+ $this->openTransaction();
+ $this->beginReadLocking();
+
+ $copy = clone $this;
+ $copy->reload();
+
+ if ($copy->getStatus() !== self::STATUS_PURCHASING) {
+ throw new Exception(
+ pht(
+ 'Cart has wrong status ("%s") to call didApplyCharge(), expected '.
+ '"%s".',
+ $copy->getStatus(),
+ self::STATUS_PURCHASING));
+ }
+
+ $charge->save();
+ $this->setStatus(self::STATUS_CHARGED)->save();
+ $this->saveTransaction();
foreach ($this->purchases as $purchase) {
$purchase->getProduct()->didPurchaseProduct($purchase);
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
@@ -9,8 +9,6 @@
final class PhortuneCharge extends PhortuneDAO
implements PhabricatorPolicyInterface {
- 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';
@@ -27,6 +25,11 @@
private $account = self::ATTACHABLE;
private $cart = self::ATTACHABLE;
+ public static function initializeNewCharge() {
+ return id(new PhortuneCharge())
+ ->setStatus(self::STATUS_CHARGING);
+ }
+
public function getConfiguration() {
return array(
self::CONFIG_AUX_PHID => true,

File Metadata

Mime Type
text/plain
Expires
Sat, Feb 1, 6:26 PM (15 h, 21 m)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
7082495
Default Alt Text
D10639.id.diff (11 KB)

Event Timeline