public function willRefundCharge(PhabricatorUser $actor, PhortunePaymentProvider $provider, PhortuneCharge $charge, PhortuneCurrency $amount) { if (!$amount->isPositive()) { throw new Exception(pht('Trying to refund non-positive amount of money!')); } if ($amount->isGreaterThan($charge->getAmountRefundableAsCurrency())) { throw new Exception(pht('Trying to refund more money than remaining on charge!')); } if ($charge->getRefundedChargePHID()) { throw new Exception(pht('Trying to refund a refund!')); } if ($charge->getStatus() !== PhortuneCharge::STATUS_CHARGED && $charge->getStatus() !== PhortuneCharge::STATUS_HOLD) { throw new Exception(pht('Trying to refund an uncharged charge!')); } $refund_charge = PhortuneCharge::initializeNewCharge()->setAccountPHID($this->getAccount()->getPHID())->setCartPHID($this->getPHID())->setAuthorPHID($actor->getPHID())->setMerchantPHID($this->getMerchant()->getPHID())->setProviderPHID($provider->getProviderConfig()->getPHID())->setPaymentMethodPHID($charge->getPaymentMethodPHID())->setRefundedChargePHID($charge->getPHID())->setAmountAsCurrency($amount->negate()); $charge->openTransaction(); $charge->beginReadLocking(); $copy = clone $charge; $copy->reload(); if ($copy->getRefundingPHID() !== null) { throw new Exception(pht('Trying to refund a charge which is already refunding!')); } $refund_charge->save(); $charge->setRefundingPHID($refund_charge->getPHID()); $charge->save(); $charge->endReadLocking(); $charge->saveTransaction(); return $refund_charge; }
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); } }