/**
  * Contact the PayPal servers in order to verify the payment
  * @param jmsPaymentMethodData $data
  */
 private function verifyApproval(jmsPaymentMethodData $data)
 {
     $expressCheckoutDetailsRequest = PayPal::getType('GetExpressCheckoutDetailsRequestType');
     $expressCheckoutDetailsRequest->setToken($data->getValue('express_token'));
     $response = $this->getCallerServices()->GetExpressCheckoutDetails($expressCheckoutDetailsRequest);
     if (Pear::isError($response)) {
         throw new jmsPaymentCommunicationException('Error while fetching express checkout details: ' . $response->getMessage());
     }
     if ($response->Ack !== 'Success') {
         throw new jmsPaymentCommunicationException('Express checkout details could not be retrieved: ' . $response->Ack);
     }
     $details = $response->getGetExpressCheckoutDetailsResponseDetails();
     $buyerInfo = $details->getPayerInfo();
     if ($this->verifiedAccountRequired() && $buyerInfo->PayerStatus !== 'verified') {
         throw new jmsPaymentUnverifiedBuyerException('This PayPal account is not verified.');
     }
     $data->setValue('payer_id', $buyerInfo->PayerID);
     $basicAmount = PayPal::getType('BasicAmountType');
     $basicAmount->setattr('currencyID', $data->getCurrency());
     $basicAmount->setval(number_format($data->getAmount(), 2));
     $paymentDetails = PayPal::getType('PaymentDetailsType');
     $paymentDetails->setOrderTotal($basicAmount);
     $paymentDetails->setOrderDescription($data->subject);
     $request = PayPal::getType('DoExpressCheckoutPaymentRequestDetailsType');
     $request->setToken($data->getValue('express_token'));
     $request->setPayerID($buyerInfo->PayerID);
     $request->setPaymentAction('Order');
     $request->setPaymentDetails($paymentDetails);
     $doExpressCheckoutPaymentRequest = PayPal::getType('DoExpressCheckoutPaymentRequestType');
     $doExpressCheckoutPaymentRequest->setVersion(self::API_VERSION);
     $doExpressCheckoutPaymentRequest->setDoExpressCheckoutPaymentRequestDetails($request);
     $response = $this->getCallerServices()->DoExpressCheckoutPayment($doExpressCheckoutPaymentRequest);
     if (Pear::isError($response)) {
         $reasonCode = $response->getMessage();
         $data->setReasonCode($reasonCode);
         $e = new jmsPaymentCommunicationException('Error while authorizing express checkout payment: ' . $reasonCode);
         $e->setPaymentMethodData($data);
         throw $e;
     }
     if ($response->Ack !== 'Success') {
         $reasonCode = $this->extractErrors($response->Errors);
         $data->setReasonCode($reasonCode);
         $data->setResponseCode($response->Ack);
         $e = new jmsPaymentException('Payment could not be authorized: ' . $reasonCode);
         $e->setPaymentMethodData($data);
         throw $e;
     }
     $details = $response->getDoExpressCheckoutPaymentResponseDetails();
     $paymentInfo = $details->getPaymentInfo();
     $data->setValue('external_reference_number', $paymentInfo->TransactionID);
     $data->setProcessedAmount($data->getAmount());
 }
 /**
  * Deposits a transaction. This method is not intended to call any
  * api belonging to 4b system as this api does not exist. The first
  * action for depositing has to be a POST submission to an URL outside
  * of the online shop pages where the purchaser has to fill payment info.
  * When this is done 4b server calls in return online shop url.
  * You have to connect that url with this method.
  * You can inform 4b about the url that leads to this method in the
  * field labeled: "URL que graba el resultado en la BD del comercio"
  * https://tpv.4b.es/config
  * 
  * @see plugins/jmsPaymentPlugin/lib/method/jmsPaymentMethod#deposit($data, $retry)
  */
 public function deposit(jmsPaymentMethodData $data, $retry = false)
 {
     //If this is not called from 4b systems we want to launch a user interaction exception
     //Check call originated from 4b simulation or production servers.
     $caller_address = sfContext::getInstance()->getRequest()->getRemoteAddress();
     //This call is far from the paymentDemo sfAction
     //to not taint the 'general' logic implemented there.
     $production_ip = $this->getProductionIp();
     $simulation_ip = $this->getSimulationIp();
     //This block is going to be executed when call is from 'shop' pages and
     //4b pages to be filled by the user are going to be shown as result.
     if ($caller_address != $production_ip && $caller_address != $simulation_ip) {
         //call to deposit not from 4b servers
         $production_url = $this->getProductionUrl();
         $simulation_url = $this->getSimulationUrl();
         $_4b_conection_url = !$this->isDebug() ? $production_url : $simulation_url;
         // throw
         $exception = new jmsPaymentUserActionRequiredException(new jmsPaymentUserActionVisitURL($_4b_conection_url));
         $data->setValue('transaction_from_ip', $caller_address);
         $exception->setPaymentMethodData($data);
         throw $exception;
     }
     //This block is going to be executed when call is from 4b server
     $amount_value = $data->getAmount();
     if ($data->getValue('transaction_result') == 1) {
         //Success
         $data->setResponseCode($data->getValue('transaction_id'));
         $data->setReasonCode('Approval with 4b approval code: ' . $data->getValue('transaction_approval_code'));
         $data->setProcessedAmount($amount_value);
         //Processed amount equals requested.
     } else {
         //Failed
         $data->setResponseCode($data->getValue('transaction_error_code'));
         $data->setReasonCode($data->getValue('transaction_error_description'));
         $e = new jmsPaymentException('Payment could not be completed. Reason: ' . $data->getReasonCode());
         $e->setPaymentMethodData($data);
         throw $e;
     }
 }