Implement Phortune charge updates
Summary: Ref T2787. These don't necessarily do a ton yet, but we can get PayPal out of hold, at least. Test Plan: Updated charges from all providers. Cleared a PayPal hold. Reviewers: btrahan Reviewed By: btrahan Subscribers: epriestley Maniphest Tasks: T2787 Differential Revision: https://secure.phabricator.com/D10670
This commit is contained in:
		| @@ -79,6 +79,8 @@ final class FundBackerProduct extends PhortuneProductImplementation { | ||||
|   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()) | ||||
|   | ||||
| @@ -169,6 +169,8 @@ final class PhortuneAccountViewController extends PhortuneController { | ||||
|       ->withStatuses( | ||||
|         array( | ||||
|           PhortuneCart::STATUS_PURCHASING, | ||||
|           PhortuneCart::STATUS_CHARGED, | ||||
|           PhortuneCart::STATUS_HOLD, | ||||
|           PhortuneCart::STATUS_PURCHASED, | ||||
|         )) | ||||
|       ->execute(); | ||||
| @@ -197,6 +199,7 @@ final class PhortuneAccountViewController extends PhortuneController { | ||||
|  | ||||
|       $rowc[] = ''; | ||||
|       $rows[] = array( | ||||
|         $cart->getID(), | ||||
|         phutil_tag( | ||||
|           'strong', | ||||
|           array(), | ||||
| @@ -206,6 +209,7 @@ final class PhortuneAccountViewController extends PhortuneController { | ||||
|           'strong', | ||||
|           array(), | ||||
|           $cart->getTotalPriceAsCurrency()->formatForDisplay()), | ||||
|         PhortuneCart::getNameForStatus($cart->getStatus()), | ||||
|         phabricator_datetime($cart->getDateModified(), $viewer), | ||||
|       ); | ||||
|       foreach ($purchases as $purchase) { | ||||
| @@ -219,6 +223,7 @@ final class PhortuneAccountViewController extends PhortuneController { | ||||
|           $handles[$purchase->getPHID()]->renderLink(), | ||||
|           $price, | ||||
|           '', | ||||
|           '', | ||||
|         ); | ||||
|       } | ||||
|     } | ||||
| @@ -227,21 +232,25 @@ final class PhortuneAccountViewController extends PhortuneController { | ||||
|       ->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) | ||||
|   | ||||
| @@ -22,7 +22,41 @@ final class PhortuneCartUpdateController | ||||
|       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()); | ||||
|   | ||||
| @@ -83,8 +83,7 @@ final class PhortuneCartViewController | ||||
|  | ||||
|     $header = id(new PHUIHeaderView()) | ||||
|       ->setUser($viewer) | ||||
|       ->setHeader(pht('Order Detail')) | ||||
|       ->setPolicyObject($cart); | ||||
|       ->setHeader(pht('Order Detail')); | ||||
|  | ||||
|     $cart_box = id(new PHUIObjectBoxView()) | ||||
|       ->setHeader($header) | ||||
|   | ||||
| @@ -102,10 +102,7 @@ final class PhortuneBalancedPaymentProvider extends PhortunePaymentProvider { | ||||
|   } | ||||
|  | ||||
|   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 @@ final class PhortuneBalancedPaymentProvider extends PhortunePaymentProvider { | ||||
|   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 @@ final class PhortuneBalancedPaymentProvider extends PhortunePaymentProvider { | ||||
|   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 @@ final class PhortuneBalancedPaymentProvider extends PhortunePaymentProvider { | ||||
|     $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 @@ final class PhortuneBalancedPaymentProvider extends PhortunePaymentProvider { | ||||
|     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 @@ final class PhortuneBalancedPaymentProvider extends PhortunePaymentProvider { | ||||
|     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'; | ||||
|   } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -192,6 +192,62 @@ final class PhortunePayPalPaymentProvider extends PhortunePaymentProvider { | ||||
|       $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 @@ final class PhortunePayPalPaymentProvider extends PhortunePaymentProvider { | ||||
|           '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, | ||||
|   | ||||
| @@ -149,7 +149,9 @@ abstract class PhortunePaymentProvider { | ||||
|  | ||||
|   abstract protected function executeRefund( | ||||
|     PhortuneCharge $charge, | ||||
|     PhortuneCharge $charge); | ||||
|     PhortuneCharge $refund); | ||||
|  | ||||
|   abstract public function updateCharge(PhortuneCharge $charge); | ||||
|  | ||||
|  | ||||
| /* -(  Adding Payment Methods  )--------------------------------------------- */ | ||||
|   | ||||
| @@ -116,8 +116,7 @@ final class PhortuneStripePaymentProvider extends PhortunePaymentProvider { | ||||
|   } | ||||
|  | ||||
|   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 @@ final class PhortuneStripePaymentProvider extends PhortunePaymentProvider { | ||||
|   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 @@ final class PhortuneStripePaymentProvider extends PhortunePaymentProvider { | ||||
|   protected function executeRefund( | ||||
|     PhortuneCharge $charge, | ||||
|     PhortuneCharge $refund) { | ||||
|     $this->loadStripeAPILibraries(); | ||||
|  | ||||
|     $charge_id = $charge->getMetadataValue('stripe.chargeID'); | ||||
|     if (!$charge_id) { | ||||
| @@ -167,9 +165,6 @@ final class PhortuneStripePaymentProvider extends PhortunePaymentProvider { | ||||
|         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 @@ final class PhortuneStripePaymentProvider extends PhortunePaymentProvider { | ||||
|     $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 @@ final class PhortuneStripePaymentProvider extends PhortunePaymentProvider { | ||||
|     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 @@ final class PhortuneStripePaymentProvider extends PhortunePaymentProvider { | ||||
|     return null; | ||||
|   } | ||||
|  | ||||
|   private function loadStripeAPILibraries() { | ||||
|     $root = dirname(phutil_get_library_root('phabricator')); | ||||
|     require_once $root.'/externals/stripe-php/lib/Stripe.php'; | ||||
|   } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -62,6 +62,10 @@ final class PhortuneTestPaymentProvider extends PhortunePaymentProvider { | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   public function updateCharge(PhortuneCharge $charge) { | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   public function getAllConfigurableProperties() { | ||||
|     return array(); | ||||
|   } | ||||
|   | ||||
| @@ -49,8 +49,7 @@ final class PhortuneWePayPaymentProvider extends PhortunePaymentProvider { | ||||
|   } | ||||
|  | ||||
|   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 @@ final class PhortuneWePayPaymentProvider extends PhortunePaymentProvider { | ||||
|   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 @@ final class PhortuneWePayPaymentProvider extends PhortunePaymentProvider { | ||||
|     $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 @@ final class PhortuneWePayPaymentProvider extends PhortunePaymentProvider { | ||||
|   public function processControllerRequest( | ||||
|     PhortuneProviderActionController $controller, | ||||
|     AphrontRequest $request) { | ||||
|     $wepay = $this->loadWePayAPILibraries(); | ||||
|  | ||||
|     $viewer = $request->getUser(); | ||||
|  | ||||
| @@ -244,15 +248,6 @@ final class PhortuneWePayPaymentProvider extends PhortunePaymentProvider { | ||||
|       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 @@ final class PhortuneWePayPaymentProvider extends PhortunePaymentProvider { | ||||
|       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; | ||||
|   } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -149,13 +149,12 @@ final class PhortuneCart extends PhortuneDAO | ||||
|         $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 @@ final class PhortuneCart extends PhortuneDAO | ||||
|         $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(); | ||||
|   | ||||
| @@ -84,6 +84,10 @@ final class PhortuneCharge extends PhortuneDAO | ||||
|     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()) { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 epriestley
					epriestley