Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F15410349
D10670.id.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
19 KB
Referenced Files
None
Subscribers
None
D10670.id.diff
View Options
diff --git a/src/applications/fund/phortune/FundBackerProduct.php b/src/applications/fund/phortune/FundBackerProduct.php
--- a/src/applications/fund/phortune/FundBackerProduct.php
+++ b/src/applications/fund/phortune/FundBackerProduct.php
@@ -79,6 +79,8 @@
public function didPurchaseProduct(
PhortuneProduct $product,
PhortunePurchase $purchase) {
+ // TODO: This viewer may be wrong if the purchase completes after a hold
+ // we should load the backer explicitly.
$viewer = $this->getViewer();
$backer = id(new FundBackerQuery())
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
@@ -169,6 +169,8 @@
->withStatuses(
array(
PhortuneCart::STATUS_PURCHASING,
+ PhortuneCart::STATUS_CHARGED,
+ PhortuneCart::STATUS_HOLD,
PhortuneCart::STATUS_PURCHASED,
))
->execute();
@@ -197,6 +199,7 @@
$rowc[] = '';
$rows[] = array(
+ $cart->getID(),
phutil_tag(
'strong',
array(),
@@ -206,6 +209,7 @@
'strong',
array(),
$cart->getTotalPriceAsCurrency()->formatForDisplay()),
+ PhortuneCart::getNameForStatus($cart->getStatus()),
phabricator_datetime($cart->getDateModified(), $viewer),
);
foreach ($purchases as $purchase) {
@@ -219,6 +223,7 @@
$handles[$purchase->getPHID()]->renderLink(),
$price,
'',
+ '',
);
}
}
@@ -227,21 +232,25 @@
->setRowClasses($rowc)
->setHeaders(
array(
- pht('Cart'),
+ pht('ID'),
+ pht('Order'),
pht('Purchase'),
pht('Amount'),
+ pht('Status'),
pht('Updated'),
))
->setColumnClasses(
array(
'',
+ '',
'wide',
'right',
+ '',
'right',
));
$header = id(new PHUIHeaderView())
- ->setHeader(pht('Purchase History'));
+ ->setHeader(pht('Order History'));
return id(new PHUIObjectBoxView())
->setHeader($header)
diff --git a/src/applications/phortune/controller/PhortuneCartUpdateController.php b/src/applications/phortune/controller/PhortuneCartUpdateController.php
--- a/src/applications/phortune/controller/PhortuneCartUpdateController.php
+++ b/src/applications/phortune/controller/PhortuneCartUpdateController.php
@@ -22,7 +22,41 @@
return new Aphront404Response();
}
- // TODO: This obviously doesn't do anything for now.
+ $charges = id(new PhortuneChargeQuery())
+ ->setViewer($viewer)
+ ->withCartPHIDs(array($cart->getPHID()))
+ ->needCarts(true)
+ ->withStatuses(
+ array(
+ PhortuneCharge::STATUS_HOLD,
+ PhortuneCharge::STATUS_CHARGED,
+ ))
+ ->execute();
+
+ if ($charges) {
+ $providers = id(new PhortunePaymentProviderConfigQuery())
+ ->setViewer($viewer)
+ ->withPHIDs(mpull($charges, 'getProviderPHID'))
+ ->execute();
+ $providers = mpull($providers, null, 'getPHID');
+ } else {
+ $providers = array();
+ }
+
+ foreach ($charges as $charge) {
+ if ($charge->isRefund()) {
+ // Don't update refunds.
+ continue;
+ }
+
+ $provider_config = idx($providers, $charge->getProviderPHID());
+ if (!$provider_config) {
+ throw new Exception(pht('Unable to load provider for charge!'));
+ }
+
+ $provider = $provider_config->buildProvider();
+ $provider->updateCharge($charge);
+ }
return id(new AphrontRedirectResponse())
->setURI($cart->getDetailURI());
diff --git a/src/applications/phortune/controller/PhortuneCartViewController.php b/src/applications/phortune/controller/PhortuneCartViewController.php
--- a/src/applications/phortune/controller/PhortuneCartViewController.php
+++ b/src/applications/phortune/controller/PhortuneCartViewController.php
@@ -83,8 +83,7 @@
$header = id(new PHUIHeaderView())
->setUser($viewer)
- ->setHeader(pht('Order Detail'))
- ->setPolicyObject($cart);
+ ->setHeader(pht('Order Detail'));
$cart_box = id(new PHUIObjectBoxView())
->setHeader($header)
diff --git a/src/applications/phortune/provider/PhortuneBalancedPaymentProvider.php b/src/applications/phortune/provider/PhortuneBalancedPaymentProvider.php
--- a/src/applications/phortune/provider/PhortuneBalancedPaymentProvider.php
+++ b/src/applications/phortune/provider/PhortuneBalancedPaymentProvider.php
@@ -102,10 +102,7 @@
}
public function runConfigurationTest() {
- $root = dirname(phutil_get_library_root('phabricator'));
- require_once $root.'/externals/httpful/bootstrap.php';
- require_once $root.'/externals/restful/bootstrap.php';
- require_once $root.'/externals/balanced-php/bootstrap.php';
+ $this->loadBalancedAPILibraries();
// TODO: This only tests that the secret key is correct. It's not clear
// how to test that the marketplace is correct.
@@ -140,11 +137,7 @@
protected function executeCharge(
PhortunePaymentMethod $method,
PhortuneCharge $charge) {
-
- $root = dirname(phutil_get_library_root('phabricator'));
- require_once $root.'/externals/httpful/bootstrap.php';
- require_once $root.'/externals/restful/bootstrap.php';
- require_once $root.'/externals/balanced-php/bootstrap.php';
+ $this->loadBalancedAPILibraries();
$price = $charge->getAmountAsCurrency();
@@ -182,11 +175,7 @@
protected function executeRefund(
PhortuneCharge $charge,
PhortuneCharge $refund) {
-
- $root = dirname(phutil_get_library_root('phabricator'));
- require_once $root.'/externals/httpful/bootstrap.php';
- require_once $root.'/externals/restful/bootstrap.php';
- require_once $root.'/externals/balanced-php/bootstrap.php';
+ $this->loadBalancedAPILibraries();
$debit_uri = $charge->getMetadataValue('balanced.debitURI');
if (!$debit_uri) {
@@ -214,6 +203,24 @@
$refund->save();
}
+ public function updateCharge(PhortuneCharge $charge) {
+ $this->loadBalancedAPILibraries();
+
+ $debit_uri = $charge->getMetadataValue('balanced.debitURI');
+ if (!$debit_uri) {
+ throw new Exception(pht('No Balanced debit URI!'));
+ }
+
+ try {
+ Balanced\Settings::$api_key = $this->getSecretKey();
+ $balanced_debit = Balanced\Debit::get($debit_uri);
+ } catch (RESTful\Exceptions\HTTPError $error) {
+ throw new Exception($error->response->body->description);
+ }
+
+ // TODO: Deal with disputes / chargebacks / surprising refunds.
+ }
+
private function getMarketplaceID() {
return $this
->getProviderConfig()
@@ -255,14 +262,10 @@
AphrontRequest $request,
PhortunePaymentMethod $method,
array $token) {
+ $this->loadBalancedAPILibraries();
$errors = array();
- $root = dirname(phutil_get_library_root('phabricator'));
- require_once $root.'/externals/httpful/bootstrap.php';
- require_once $root.'/externals/restful/bootstrap.php';
- require_once $root.'/externals/balanced-php/bootstrap.php';
-
$account_phid = $method->getAccountPHID();
$author_phid = $method->getAuthorPHID();
$description = $account_phid.':'.$author_phid;
@@ -357,4 +360,11 @@
return null;
}
+ private function loadBalancedAPILibraries() {
+ $root = dirname(phutil_get_library_root('phabricator'));
+ require_once $root.'/externals/httpful/bootstrap.php';
+ require_once $root.'/externals/restful/bootstrap.php';
+ require_once $root.'/externals/balanced-php/bootstrap.php';
+ }
+
}
diff --git a/src/applications/phortune/provider/PhortunePayPalPaymentProvider.php b/src/applications/phortune/provider/PhortunePayPalPaymentProvider.php
--- a/src/applications/phortune/provider/PhortunePayPalPaymentProvider.php
+++ b/src/applications/phortune/provider/PhortunePayPalPaymentProvider.php
@@ -192,6 +192,62 @@
$result['REFUNDTRANSACTIONID']);
}
+ public function updateCharge(PhortuneCharge $charge) {
+ $transaction_id = $charge->getMetadataValue('paypal.transactionID');
+ if (!$transaction_id) {
+ throw new Exception(pht('Charge has no transaction ID!'));
+ }
+
+ $params = array(
+ 'TRANSACTIONID' => $transaction_id,
+ );
+
+ $result = $this
+ ->newPaypalAPICall()
+ ->setRawPayPalQuery('GetTransactionDetails', $params)
+ ->resolve();
+
+ $is_charge = false;
+ $is_fail = false;
+ switch ($result['PAYMENTSTATUS']) {
+ case 'Processed':
+ case 'Completed':
+ case 'Completed-Funds-Held':
+ $is_charge = true;
+ break;
+ case 'Partially-Refunded':
+ case 'Refunded':
+ case 'Reversed':
+ case 'Canceled-Reversal':
+ // TODO: Handle these.
+ return;
+ case 'In-Progress':
+ case 'Pending':
+ // TODO: Also handle these better?
+ return;
+ case 'Denied':
+ case 'Expired':
+ case 'Failed':
+ case 'None':
+ case 'Voided':
+ default:
+ $is_fail = true;
+ break;
+ }
+
+ if ($charge->getStatus() == PhortuneCharge::STATUS_HOLD) {
+ $cart = $charge->getCart();
+
+ $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
+ if ($is_charge) {
+ $cart->didApplyCharge($charge);
+ } else if ($is_fail) {
+ $cart->didFailCharge($charge);
+ }
+ unset($unguarded);
+ }
+ }
+
private function getPaypalAPIUsername() {
return $this
->getProviderConfig()
@@ -278,6 +334,7 @@
'PAYMENTREQUEST_0_CURRENCYCODE' => $price->getCurrency(),
'PAYMENTREQUEST_0_PAYMENTACTION' => 'Sale',
'PAYMENTREQUEST_0_CUSTOM' => $charge->getPHID(),
+ 'PAYMENTREQUEST_0_DESC' => $cart->getName(),
'RETURNURL' => $return_uri,
'CANCELURL' => $cancel_uri,
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
@@ -149,7 +149,9 @@
abstract protected function executeRefund(
PhortuneCharge $charge,
- PhortuneCharge $charge);
+ PhortuneCharge $refund);
+
+ abstract public function updateCharge(PhortuneCharge $charge);
/* -( Adding Payment Methods )--------------------------------------------- */
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
@@ -116,8 +116,7 @@
}
public function runConfigurationTest() {
- $root = dirname(phutil_get_library_root('phabricator'));
- require_once $root.'/externals/stripe-php/lib/Stripe.php';
+ $this->loadStripeAPILibraries();
$secret_key = $this->getSecretKey();
$account = Stripe_Account::retrieve($secret_key);
@@ -131,9 +130,7 @@
protected function executeCharge(
PhortunePaymentMethod $method,
PhortuneCharge $charge) {
-
- $root = dirname(phutil_get_library_root('phabricator'));
- require_once $root.'/externals/stripe-php/lib/Stripe.php';
+ $this->loadStripeAPILibraries();
$price = $charge->getAmountAsCurrency();
@@ -160,6 +157,7 @@
protected function executeRefund(
PhortuneCharge $charge,
PhortuneCharge $refund) {
+ $this->loadStripeAPILibraries();
$charge_id = $charge->getMetadataValue('stripe.chargeID');
if (!$charge_id) {
@@ -167,9 +165,6 @@
pht('Unable to refund charge; no Stripe chargeID!'));
}
- $root = dirname(phutil_get_library_root('phabricator'));
- require_once $root.'/externals/stripe-php/lib/Stripe.php';
-
$refund_cents = $refund
->getAmountAsCurrency()
->negate()
@@ -192,6 +187,22 @@
$charge->save();
}
+ public function updateCharge(PhortuneCharge $charge) {
+ $this->loadStripeAPILibraries();
+
+ $charge_id = $charge->getMetadataValue('stripe.chargeID');
+ if (!$charge_id) {
+ throw new Exception(
+ pht('Unable to update charge; no Stripe chargeID!'));
+ }
+
+ $secret_key = $this->getSecretKey();
+ $stripe_charge = Stripe_Charge::retrieve($charge_id, $secret_key);
+
+ // TODO: Deal with disputes / chargebacks / surprising refunds.
+
+ }
+
private function getPublishableKey() {
return $this
->getProviderConfig()
@@ -221,12 +232,10 @@
AphrontRequest $request,
PhortunePaymentMethod $method,
array $token) {
+ $this->loadStripeAPILibraries();
$errors = array();
- $root = dirname(phutil_get_library_root('phabricator'));
- require_once $root.'/externals/stripe-php/lib/Stripe.php';
-
$secret_key = $this->getSecretKey();
$stripe_token = $token['stripeCardToken'];
@@ -362,4 +371,9 @@
return null;
}
+ private function loadStripeAPILibraries() {
+ $root = dirname(phutil_get_library_root('phabricator'));
+ require_once $root.'/externals/stripe-php/lib/Stripe.php';
+ }
+
}
diff --git a/src/applications/phortune/provider/PhortuneTestPaymentProvider.php b/src/applications/phortune/provider/PhortuneTestPaymentProvider.php
--- a/src/applications/phortune/provider/PhortuneTestPaymentProvider.php
+++ b/src/applications/phortune/provider/PhortuneTestPaymentProvider.php
@@ -62,6 +62,10 @@
return;
}
+ public function updateCharge(PhortuneCharge $charge) {
+ return;
+ }
+
public function getAllConfigurableProperties() {
return array();
}
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
@@ -49,8 +49,7 @@
}
public function runConfigurationTest() {
- $root = dirname(phutil_get_library_root('phabricator'));
- require_once $root.'/externals/wepay/wepay.php';
+ $this->loadWePayAPILibraries();
WePay::useStaging(
$this->getWePayClientID(),
@@ -189,20 +188,12 @@
protected function executeRefund(
PhortuneCharge $charge,
PhortuneCharge $refund) {
+ $wepay = $this->loadWePayAPILibraries();
- $root = dirname(phutil_get_library_root('phabricator'));
- require_once $root.'/externals/wepay/wepay.php';
-
- WePay::useStaging(
- $this->getWePayClientID(),
- $this->getWePayClientSecret());
-
- $wepay = new WePay($this->getWePayAccessToken());
-
- $charge_id = $charge->getMetadataValue('wepay.checkoutID');
+ $checkout_id = $this->getWePayCheckoutID($charge);
$params = array(
- 'checkout_id' => $charge_id,
+ 'checkout_id' => $checkout_id,
'refund_reason' => pht('Refund'),
'amount' => $refund->getAmountAsCurrency()->negate()->formatBareValue(),
);
@@ -210,6 +201,18 @@
$wepay->request('checkout/refund', $params);
}
+ public function updateCharge(PhortuneCharge $charge) {
+ $wepay = $this->loadWePayAPILibraries();
+
+ $params = array(
+ 'checkout_id' => $this->getWePayCheckoutID($charge),
+ );
+ $wepay_checkout = $wepay->request('checkout', $params);
+
+ // TODO: Deal with disputes / chargebacks / surprising refunds.
+ }
+
+
/* -( One-Time Payments )-------------------------------------------------- */
public function canProcessOneTimePayments() {
@@ -236,6 +239,7 @@
public function processControllerRequest(
PhortuneProviderActionController $controller,
AphrontRequest $request) {
+ $wepay = $this->loadWePayAPILibraries();
$viewer = $request->getUser();
@@ -244,15 +248,6 @@
return new Aphront404Response();
}
- $root = dirname(phutil_get_library_root('phabricator'));
- require_once $root.'/externals/wepay/wepay.php';
-
- WePay::useStaging(
- $this->getWePayClientID(),
- $this->getWePayClientSecret());
-
- $wepay = new WePay($this->getWePayAccessToken());
-
$charge = $controller->loadActiveCharge($cart);
switch ($controller->getAction()) {
case 'checkout':
@@ -388,5 +383,23 @@
pht('Unsupported action "%s".', $controller->getAction()));
}
+ private function loadWePayAPILibraries() {
+ $root = dirname(phutil_get_library_root('phabricator'));
+ require_once $root.'/externals/wepay/wepay.php';
+
+ WePay::useStaging(
+ $this->getWePayClientID(),
+ $this->getWePayClientSecret());
+
+ return new WePay($this->getWePayAccessToken());
+ }
+
+ private function getWePayCheckoutID(PhortuneCharge $charge) {
+ $checkout_id = $charge->getMetadataValue('wepay.checkoutID');
+ if ($checkout_id === null) {
+ throw new Exception(pht('No WePay Checkout ID present on charge!'));
+ }
+ return $checkout_id;
+ }
}
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
@@ -149,13 +149,12 @@
$copy = clone $this;
$copy->reload();
- if ($copy->getStatus() !== self::STATUS_PURCHASING) {
+ if (($copy->getStatus() !== self::STATUS_PURCHASING) &&
+ ($copy->getStatus() !== self::STATUS_HOLD)) {
throw new Exception(
pht(
- 'Cart has wrong status ("%s") to call didApplyCharge(), '.
- 'expected "%s".',
- $copy->getStatus(),
- self::STATUS_PURCHASING));
+ 'Cart has wrong status ("%s") to call didApplyCharge().',
+ $copy->getStatus()));
}
$charge->save();
@@ -182,13 +181,12 @@
$copy = clone $this;
$copy->reload();
- if ($copy->getStatus() !== self::STATUS_PURCHASING) {
+ if (($copy->getStatus() !== self::STATUS_PURCHASING) &&
+ ($copy->getStatus() !== self::STATUS_HOLD)) {
throw new Exception(
pht(
- 'Cart has wrong status ("%s") to call didFailCharge(), '.
- 'expected "%s".',
- $copy->getStatus(),
- self::STATUS_PURCHASING));
+ 'Cart has wrong status ("%s") to call didFailCharge().',
+ $copy->getStatus()));
}
$charge->save();
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
@@ -84,6 +84,10 @@
return idx(self::getStatusNameMap(), $status, pht('Unknown'));
}
+ public function isRefund() {
+ return $this->getAmountAsCurrency()->negate()->isPositive();
+ }
+
public function getStatusForDisplay() {
if ($this->getStatus() == self::STATUS_CHARGED) {
if ($this->getRefundedChargePHID()) {
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Thu, Mar 20, 7:07 AM (1 w, 2 d ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
7666115
Default Alt Text
D10670.id.diff (19 KB)
Attached To
Mode
D10670: Implement Phortune charge updates
Attached
Detach File
Event Timeline
Log In to Comment