/**
  * Capture a previously authorized payment
  *
  * If the transaction-reference of the payment to capture is known, pass it via $data as
  * `transactionReference` parameter. Otherwise the service will try to look up the reference
  * from previous payment messages.
  *
  * If there's no transaction-reference to be found, this method will raise an exception.
  *
  * You can issue partial captures (if the gateway supports it) by passing an `amount` parameter in the $data
  * array. The amount can also exceed the authorized amount, if the configuration allows it (`max_capture` setting).
  * An amount that exceeds the authorized amount will always be considered as a full capture!
  * If the amount given is not a number, or if it exceeds the total possible capture amount, an exception
  * will be raised.
  *
  * @inheritdoc
  * @throws MissingParameterException if no transaction reference can be found from messages or parameters
  * @throws InvalidParameterException if the amount parameter was invalid
  */
 public function initiate($data = array())
 {
     if (!$this->payment->canCapture()) {
         throw new InvalidConfigurationException('Capture of this payment not allowed.');
     }
     if (!$this->payment->isInDB()) {
         $this->payment->write();
     }
     $reference = null;
     // If the gateway isn't manual, we need a transaction reference to refund a payment
     if (!GatewayInfo::isManual($this->payment->Gateway)) {
         if (!empty($data['transactionReference'])) {
             $reference = $data['transactionReference'];
         } elseif (!empty($data['receipt'])) {
             // legacy code?
             $reference = $data['receipt'];
         } else {
             $reference = $this->payment->TransactionReference;
         }
         if (empty($reference)) {
             throw new MissingParameterException('transactionReference not found and is not set as parameter');
         }
     }
     $gateway = $this->oGateway();
     if (!$gateway->supportsCapture()) {
         throw new InvalidConfigurationException(sprintf('The gateway "%s" doesn\'t support capture', $this->payment->Gateway));
     }
     $authorized = $amount = $this->payment->MoneyAmount;
     $diff = 0;
     if (!empty($data['amount'])) {
         $amount = $data['amount'];
         if (!is_numeric($amount)) {
             throw new InvalidParameterException('The "amount" parameter has to be numeric.');
         }
         if (!($amount > 0)) {
             throw new InvalidParameterException('The "amount" parameter has to be positive.');
         }
         // check if the amount exceeds the max. amount that can be captured
         if (PaymentMath::compare($this->payment->getMaxCaptureAmount(), $amount) === -1) {
             throw new InvalidParameterException('The "amount" given exceeds the amount that can be captured.');
         }
         $diff = PaymentMath::subtract($amount, $authorized);
     }
     if ($diff < 0 && !$this->payment->canCapture(null, true)) {
         throw new InvalidParameterException('This payment cannot be partially captured (unsupported by gateway).');
     }
     $gatewayData = array_merge($data, array('amount' => (double) $amount, 'currency' => $this->payment->MoneyCurrency, 'transactionReference' => $reference, 'notifyUrl' => $this->getEndpointUrl('notify')));
     $this->extend('onBeforeCapture', $gatewayData);
     $request = $this->oGateway()->capture($gatewayData);
     $this->extend('onAfterCapture', $request);
     $message = $this->createMessage($this->requestMessageType, $request);
     $message->write();
     try {
         $response = $this->response = $request->send();
     } catch (OmnipayException $e) {
         $this->createMessage($this->errorMessageType, $e);
         return $this->generateServiceResponse(ServiceResponse::SERVICE_ERROR);
     }
     Helper::safeExtend($this, 'onAfterSendCapture', $request, $response);
     $serviceResponse = $this->wrapOmnipayResponse($response);
     if ($serviceResponse->isAwaitingNotification()) {
         if ($diff < 0) {
             $this->createPartialPayment(PaymentMath::multiply($amount, '-1'), $this->pendingState);
         } elseif ($diff > 0) {
             $this->createPartialPayment($diff, $this->pendingState);
         }
         $this->payment->Status = $this->pendingState;
         $this->payment->write();
     } else {
         if ($serviceResponse->isError()) {
             $this->createMessage($this->errorMessageType, $response);
         } else {
             if ($diff < 0) {
                 $this->createPartialPayment(PaymentMath::multiply($amount, '-1'), $this->pendingState);
             } elseif ($diff > 0) {
                 $this->createPartialPayment($diff, $this->pendingState);
             }
             $this->markCompleted($this->endState, $serviceResponse, $response);
         }
     }
     return $serviceResponse;
 }
Esempio n. 2
0
 /**
  * Calculate the max amount that can be captured for this payment.
  * If the Status of the payment isn't 'Authorized', this will return 0
  * @return int|string the max amount that can be captured for this payment.
  */
 public function getMaxCaptureAmount()
 {
     if ($this->Status !== 'Authorized') {
         return 0;
     }
     $percent = GatewayInfo::maxExcessCapturePercent($this->Gateway);
     $fixedAmount = GatewayInfo::maxExcessCaptureAmount($this->Gateway, $this->getCurrency());
     // -1 will only be returned if there's a fixed amount, but no percentage.
     // We can safely return the fixed amount here
     if ($percent === -1) {
         return PaymentMath::add($this->MoneyAmount, $fixedAmount);
     }
     // calculate what amount the percentage will result in
     $percentAmount = PaymentMath::multiply(PaymentMath::multiply($percent, '0.01'), $this->MoneyAmount);
     // if there's no fixed amount and only the percentage is set, we can return the percentage amount right away.
     if ($fixedAmount === -1) {
         return PaymentMath::add($this->MoneyAmount, $percentAmount);
     }
     // If the amount from the percentage is smaller, use the percentage
     if (PaymentMath::compare($fixedAmount, $percentAmount) > 0) {
         return PaymentMath::add($this->MoneyAmount, $percentAmount);
     }
     // otherwise use the fixed amount
     return PaymentMath::add($this->MoneyAmount, $fixedAmount);
 }