mirror of
https://we.phorge.it/source/phorge.git
synced 2024-12-03 12:12:43 +01:00
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:
parent
2a2fb62229
commit
f41ae2228a
12 changed files with 214 additions and 68 deletions
|
@ -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()) {
|
||||
|
|
Loading…
Reference in a new issue