PayPal Direct Payment API Component
4 : The Paypal Component
Useful component that provides a wrapper for PayPal's Direct Payment API, allowing any cake based application to accept payments via Direct Payment (processing credit cards and payments without leaving your website) and Express Checkout (allowing users to use their PayPal account to pay)
The Paypal Component
Component Class:
<?php
/**
* Paypal Direct Payment API Component class file.
*
* @filesource
* @copyright Mariano Iglesias - mariano@cricava.com
* @link http://www.marianoiglesias.com.ar Mariano Iglesias
* @package cake
* @subpackage cake.controllers.components
*/
require_once('PayPal.php');
require_once('PayPal/Profile/API.php');
require_once('PayPal/Profile/Handler.php');
require_once('PayPal/Profile/Handler/Array.php');
require_once('PayPal/Type/AbstractResponseType.php');
require_once('PayPal/Type/AddressType.php');
require_once('PayPal/Type/BasicAmountType.php');
require_once('PayPal/Type/CreditCardDetailsType.php');
require_once('PayPal/Type/DoCaptureResponseDetailsType.php');
require_once('PayPal/Type/DoCaptureResponseType.php');
require_once('PayPal/Type/DoDirectPaymentRequestType.php');
require_once('PayPal/Type/DoDirectPaymentRequestDetailsType.php');
require_once('PayPal/Type/DoDirectPaymentResponseType.php');
require_once('PayPal/Type/DoExpressCheckoutPaymentRequestType.php');
require_once('PayPal/Type/DoExpressCheckoutPaymentRequestDetailsType.php');
require_once('PayPal/Type/DoExpressCheckoutPaymentResponseType.php');
require_once('PayPal/Type/DoVoidResponseType.php');
require_once('PayPal/Type/ErrorType.php');
require_once('PayPal/Type/GetExpressCheckoutDetailsRequestType.php');
require_once('PayPal/Type/GetExpressCheckoutDetailsResponseDetailsType.php');
require_once('PayPal/Type/GetExpressCheckoutDetailsResponseType.php');
require_once('PayPal/Type/GetTransactionDetailsResponseType.php');
require_once('PayPal/Type/PayerInfoType.php');
require_once('PayPal/Type/PaymentDetailsType.php');
require_once('PayPal/Type/PersonNameType.php');
require_once('PayPal/Type/RefundTransactionResponseType.php');
require_once('PayPal/Type/SetExpressCheckoutRequestType.php');
require_once('PayPal/Type/SetExpressCheckoutRequestDetailsType.php');
require_once('PayPal/Type/SetExpressCheckoutResponseType.php');
require_once('PayPal/Type/TransactionSearchResponseType.php');
define ('CAKE_COMPONENT_PAYPAL_ENVIRONMENT_LIVE', 'live');
define ('CAKE_COMPONENT_PAYPAL_ENVIRONMENT_SANDBOX', 'sandbox');
define ('CAKE_COMPONENT_PAYPAL_ENVIRONMENT_SANDBOX_BETA', 'beta-sandbox');
define ('CAKE_COMPONENT_PAYPAL_ORDER_TYPE_SALE', 'Sale');
define ('CAKE_COMPONENT_PAYPAL_CURRENCY', 'USD');
define ('CAKE_COMPONENT_PAYPAL_CHARSET_DEFAULT', 'iso-8859-1');
define ('CAKE_COMPONENT_PAYPAL_ACK_SUCCESS', 'Success');
define ('CAKE_COMPONENT_PAYPAL_ACK_SUCCESS_WITH_WARNING', 'SuccessWithWarning');
define ('CAKE_COMPONENT_PAYPAL_SESSION_SAVE_PATH', ROOT . DS . APP_DIR . DS . 'tmp' . DS . 'sessions'); // No trailing slash!
define ('CAKE_COMPONENT_PAYPAL_EXPRESS_CHECKOUT_URL', 'https://www.{$environment}.paypal.com/cgi-bin/webscr?cmd=_express-checkout&useraction=commit&token={$token}');
define ('CAKE_COMPONENT_PAYPAL_ERROR_CANT_CREATE_CALLER', 1);
define ('CAKE_COMPONENT_PAYPAL_ERROR_INVALID_ORDER', 2);
define ('CAKE_COMPONENT_PAYPAL_ERROR_INVALID_BUYER', 3);
define ('CAKE_COMPONENT_PAYPAL_ERROR_CANT_GET_AMOUNT_TYPE', 4);
define ('CAKE_COMPONENT_PAYPAL_ERROR_INVALID_CREDIT_CARD_EXPIRATION_DATE', 5);
define ('CAKE_COMPONENT_PAYPAL_ERROR_CREDIT_CARD_NOT_SET', 6);
define ('CAKE_COMPONENT_PAYPAL_ERROR_INVALID_REQUEST', 7);
define ('CAKE_COMPONENT_PAYPAL_ERROR_INVALID_CVV2', 10504);
define ('CAKE_COMPONENT_PAYPAL_ERROR_INVALID_CREDIT_CARD', 10527);
/**
* Provides a wrapper for Paypal Direct Payment API.
*
* @author Mariano Iglesias - mariano@cricava.com
* @package cake
* @subpackage cake.controllers.components
*/
class PaypalComponent extends Object
{
/**#@+
* @access protected
*/
/**
* Name of this component.
*
* @since 1.0
* @var string
*/
var $name = 'Paypal';
/**
* Components that will be used.
*
* @since 1.0
* @var array
*/
var $components = array('Session');
/**#@-*/
/**#@+
* @access private
*/
/**
* Settings.
*
* @since 1.0
* @var array
*/
var $settings = array(
'api.environment' => CAKE_COMPONENT_PAYPAL_ENVIRONMENT_SANDBOX,
'api.username' => null,
'api.password' => null,
'api.certificate' => null,
'api.signature' => null,
'api.charset' => CAKE_COMPONENT_PAYPAL_CHARSET_DEFAULT
);
/**
* Paypal API caller.
*
* @since 1.0
* @var CallerServices
*/
var $caller;
/**
* Last Paypal error code.
*
* @since 1.0
* @var int
*/
var $errorCode;
/**
* Last Paypal error.
*
* @since 1.0
* @var string
*/
var $error;
/**#@-*/
/**
* Startup the component.
*
* @param AppController $controller The controller using the component
*
* @access public
* @since 1.0
*/
function startup(&$controller)
{
}
/**
* Stores the current session to a temporary file for later retrieval.
*
* @return bool true if stored, false otherwise
*
* @access public
* @since 1.0
*/
function storeSession()
{
$sessionFileHandle = fopen(CAKE_COMPONENT_PAYPAL_SESSION_SAVE_PATH . DS . session_id() . '.ser.tmp', 'w');
if ($sessionFileHandle !== false)
{
fwrite($sessionFileHandle, serialize($_SESSION));
fclose($sessionFileHandle);
return true;
}
return false;
}
/**
* Restores the specified session.
*
* @param string Session ID
*
* @return bool true if able to restore, false otherwise
*
* @access public
* @since 1.0
*/
function restoreSession($session_id)
{
if (preg_match('/^[A-Za-z0-9]*$/', $session_id))
{
$sessionFile = CAKE_COMPONENT_PAYPAL_SESSION_SAVE_PATH . DS . $session_id . '.ser.tmp';
if (@file_exists($sessionFile) && @is_file($sessionFile) && @is_readable($sessionFile) && @filesize($sessionFile) > 0)
{
$contents = file_get_contents($sessionFile);
$oldSession = @unserialize($contents);
if (is_array($oldSession) && count($oldSession) > 0)
{
foreach($oldSession as $id => $value)
{
$this->Session->write($id, $value);
}
}
@unlink($sessionFile);
return true;
}
}
return false;
}
/**
* Sets the API environment.
*
* @param string $environment API environment.
*
* @access public
* @since 1.0
*/
function setEnvironment($environment)
{
if (in_array($environment, array(CAKE_COMPONENT_PAYPAL_ENVIRONMENT_LIVE, CAKE_COMPONENT_PAYPAL_ENVIRONMENT_SANDBOX, CAKE_COMPONENT_PAYPAL_ENVIRONMENT_SANDBOX_BETA)))
{
$this->settings['api.environment'] = $environment;
}
}
/**
* Sets the full path to the certificate file.
*
* @param string $certificate Path to Certificate file.
*
* @access public
* @since 1.0
*/
function setCertificate($certificate)
{
$this->settings['api.certificate'] = $certificate;
}
/**
* Sets the API signature.
*
* @param string $signature API signature.
*
* @access public
* @since 1.0
*/
function setSignature($signature)
{
$this->settings['api.signature'] = $signature;
}
/**
* Sets the API user.
*
* @param string $user API user.
*
* @access public
* @since 1.0
*/
function setUser($user)
{
$this->settings['api.username'] = $user;
}
/**
* Sets the API password.
*
* @param string $password API password.
*
* @access public
* @since 1.0
*/
function setPassword($password)
{
$this->settings['api.password'] = $password;
}
/**
* Sets the default charset.
*
* @param string $charset charset (example: iso-8859-1)
*
* @access public
* @since 1.0
*/
function setCharset($charset)
{
$this->settings['api.charset'] = $charset;
}
/**
* Sets the URL to which PayPal Express Checkout will redirect when setting the token.
*
* @param string $uri The URI (for example, $this->here from a controller)
*
* @access public
* @since 1.0
*/
function setTokenUrl($uri)
{
$this->settings['express.token_uri'] = $uri;
}
/**
* Sets the URL to which PayPal Express Checkout will redirect when order cancelled.
*
* @param string $uri The URI (for example, $this->here from a controller)
*
* @access public
* @since 1.0
*/
function setCancelUrl($uri)
{
$this->settings['express.cancel_uri'] = $uri;
}
/**
* Sets the order to be processed. The order is an indexed array.
* Example:
*
* Array
* (
* [action] => Sale
* [description] => ORDER_DESCRIPTION
* [id] => INVOICE_ID
* [total] => 200
* [buyer] => Array
* (
* [first] => FIRST_NAME
* [last] => LAST_NAME
* [address1] => ADDRESS_1
* [address2] => ADDRESS_2
* [city] => CITY
* [state] => STATE (two letter for US)
* [zip] => ZIP
* [country] => us
* )
* [cc] => Array
* (
* [type] => Visa/MasterCard/Discovery/Amex
* [number] => CC_NUMBER
* [expiration] => 1/2010
* [cvv2] => 123
* [owner] => Array
* (
* [first] => HOLDER_FIRST_NAME
* [last] => HOLDER_LAST_NAME
* )
* )
* )
*
* @param array $order Order to be processed.
*
* @access public
* @since 1.0
*/
function setOrder($order)
{
$this->settings['order'] = $order;
}
/**
* Gets the latest error message. If more than one error message was
* reported by PayPal, each message is separated by a new line.
*
* @return string Latest error message (null if none).
*
* @access public
* @since 1.0
*/
function getError()
{
return $this->error;
}
/**
* Gets the latest error code.
*
* @return int latest error code (0 if no error).
*
* @access public
* @since 1.0
*/
function getErrorCode()
{
return $this->errorCode;
}
/**
* Performs a direct payment with the specified order. If order was processed,
* returns an indexed array with:
*
* - transaction: Paypal Transaction id
* - ack: success code
* - avs: AVS response code for US credit cards (see https://www.paypal.com/IntegrationCenter/ic_direct-payment.html)
* - cvv2: CVV response code for US credit cards (see - avs: AVS response code for US credit cards (see https://www.paypal.com/IntegrationCenter/ic_direct-payment.html)
* - amount: processed amount
*
* In case of error it returns false and sets error code and error message.
*
* @return mixed array if success, false otherwise.
*
* @since 1.0
* @access public
*/
function directPayment()
{
$this->_initialize();
if (!isset($this->caller))
{
return false;
}
if (!isset($this->settings['order']) || !isset($this->settings['order']['total']) || !isset($this->settings['order']['action']))
{
$this->errorCode = CAKE_COMPONENT_PAYPAL_ERROR_INVALID_ORDER;
$this->error = 'Order was not set up properly';
return false;
}
$this->errorCode = 0;
$this->error = null;
// Set up buyer's information
$shipTo =& PayPal::getType('AddressType');
if (isset($this->settings['order']['buyer']))
{
$this->settings['order']['buyer']['country'] = strtoupper($this->settings['order']['buyer']['country']);
if ($this->settings['order']['buyer']['country'] == 'US')
{
$this->settings['order']['buyer']['state'] = strtoupper($this->settings['order']['buyer']['state']);
}
$shipTo->setName($this->settings['order']['buyer']['first'] . ' ' . $this->settings['order']['buyer']['last']);
$shipTo->setStreet1($this->settings['order']['buyer']['address1']);
if (isset($this->settings['order']['buyer']['address2']))
{
$shipTo->setStreet2($this->settings['order']['buyer']['address2']);
}
$shipTo->setCityName($this->settings['order']['buyer']['city']);
$shipTo->setStateOrProvince($this->settings['order']['buyer']['state']);
$shipTo->setPostalCode($this->settings['order']['buyer']['zip']);
$shipTo->setCountry($this->settings['order']['buyer']['country']);
}
else
{
$this->errorCode = CAKE_COMPONENT_PAYPAL_ERROR_INVALID_BUYER;
$this->error = 'Buyer was not set in order';
return false;
}
// Set up total $ for order
$orderTotal =& PayPal::getType('BasicAmountType');
if (PayPal::isError($orderTotal))
{
$this->errorCode = CAKE_COMPONENT_PAYPAL_ERROR_CANT_GET_AMOUNT_TYPE;
$this->error = $orderTotal->getMessage();
return false;
}
$orderTotal->setattr('currencyID', CAKE_COMPONENT_PAYPAL_CURRENCY);
$orderTotal->setval($this->settings['order']['total'], $this->settings['api.charset']);
// Set up payment details
$paymentDetails =& PayPal::getType('PaymentDetailsType');
$paymentDetails->setOrderTotal($orderTotal);
$paymentDetails->setShipToAddress($shipTo);
if (isset($this->settings['order']['description']))
{
$paymentDetails->setOrderDescription($this->settings['order']['description'], $this->settings['api.charset']);
}
if (isset($this->settings['order']['id']))
{
$paymentDetails->setInvoiceId($this->settings['order']['id'], $this->settings['api.charset']);
}
// Set up credit card information
$cardDetails =& PayPal::getType('CreditCardDetailsType');
if (isset($this->settings['order']['cc']))
{
$personDetails =& PayPal::getType('PersonNameType');
if (isset($this->settings['order']['cc']['owner']))
{
$personDetails->setFirstName($this->settings['order']['cc']['owner']['first']);
$personDetails->setLastName($this->settings['order']['cc']['owner']['last']);
}
else
{
$personDetails->setFirstName($this->settings['order']['buyer']['first']);
$personDetails->setLastName($this->settings['order']['buyer']['last']);
}
$payerDetails =& PayPal::getType('PayerInfoType');
$payerDetails->setPayerName($personDetails);
$payerDetails->setPayerCountry($this->settings['order']['buyer']['country']);
$payerDetails->setAddress($shipTo);
$cardDetailsExpiration = explode('/', $this->settings['order']['cc']['expiration']);
if (count($cardDetailsExpiration) != 2 || $cardDetailsExpiration[0] < 1 || $cardDetailsExpiration[0] > 12 || $cardDetailsExpiration[1] < date('Y'))
{
$this->errorCode = CAKE_COMPONENT_PAYPAL_ERROR_INVALID_CREDIT_CARD_EXPIRATION_DATE;
$this->error = 'Credit Card Expiration date seems to be wrong (format: month/year)';
return false;
}
$cardDetailsExpiration[0] = str_pad($cardDetailsExpiration[0], 2, '0', STR_PAD_LEFT);
$cardDetails->setCreditCardType($this->settings['order']['cc']['type']);
$cardDetails->setCreditCardNumber($this->settings['order']['cc']['number']);
$cardDetails->setCVV2($this->settings['order']['cc']['cvv2']);
$cardDetails->setExpMonth($cardDetailsExpiration[0]);
$cardDetails->setExpYear($cardDetailsExpiration[1]);
$cardDetails->setCardOwner($payerDetails);
}
else
{
$this->errorCode = CAKE_COMPONENT_PAYPAL_ERROR_CREDIT_CARD_NOT_SET;
$this->error = 'Credit Card was not set in order';
return false;
}
// Set up request details
$requestDetails =& PayPal::getType('DoDirectPaymentRequestDetailsType');
$requestDetails->setPaymentDetails($paymentDetails);
$requestDetails->setCreditCard($cardDetails);
$requestDetails->setPaymentAction($this->settings['order']['action']);
$requestDetails->setIPAddress($_SERVER['SERVER_ADDR']);
// Set up request
$request =& PayPal::getType('DoDirectPaymentRequestType');
$request->setDoDirectPaymentRequestDetails($requestDetails);
// Execute request
$response = $this->caller->DoDirectPayment($request);
if (PayPal::isError($response))
{
$this->errorCode = CAKE_COMPONENT_PAYPAL_ERROR_INVALID_REQUEST;
$this->error = $response->getMessage();
return false;
}
$response_ack = $response->getAck();
if ($response_ack == CAKE_COMPONENT_PAYPAL_ACK_SUCCESS || $response_ack == CAKE_COMPONENT_PAYPAL_ACK_SUCCESS_WITH_WARNING)
{
$response_amount = $response->getAmount();
$result = array (
'transaction' => $response->getTransactionID(),
'ack' => $response_ack,
'avs' => $response->getAVSCode(),
'cvv2' => $response->getCVV2Code(),
'amount' => $response_amount->_value
);
return $result;
}
else
{
$errorList = $response->getErrors();
if(!is_array($errorList))
{
$this->errorCode = $errorList->getErrorCode();
$this->error = '#' . $errorList->getErrorCode() . ': ' . $errorList->getShortMessage() . ' [' . $errorList->getLongMessage() . ']';
}
else
{
$this->error = '';
foreach($errorList as $error)
{
if (!empty($this->error))
{
$this->error .= "\n";
}
$this->errorCode = $error->getErrorCode();
$this->error .= '#' . $error->getErrorCode() . ': ' . $error->getShortMessage() . ' [' . $error->getLongMessage() . ']';
}
}
}
return false;
}
/**
* Performs an express checkout payment with the specified order. If order was processed,
* returns an indexed array with:
*
* - transaction: Paypal Transaction id
* - ack: success code
* - amount: processed amount
*
* In case of error it returns false and sets error code and error message.
*
* Please note that this function should be called TWICE to make an actual payment. First call
* sets environment and paypal token up and tells paypal to redirect back to the URL specified
* by the function setTokenUrl(). Once this URL is reached (meaning paypal has sent us the
* generated token) then this function should be called again to perform the actual payment.
*
* @return mixed array if success, false otherwise.
*
* @since 1.0
* @access public
*/
function expressCheckout()
{
$this->_initialize();
if (!isset($this->caller))
{
return false;
}
if (!isset($this->settings['order']) || !isset($this->settings['order']['total']) || !isset($this->settings['order']['action']))
{
$this->errorCode = CAKE_COMPONENT_PAYPAL_ERROR_INVALID_ORDER;
$this->error = 'Order was not set up properly';
return false;
}
$this->errorCode = 0;
$this->error = null;
if (!isset($_REQUEST['token']))
{
if (isset($this->settings['express.token_uri']))
{
$tokenUrl = $this->settings['express.token_uri'];
}
else
{
$serverName = $_SERVER['SERVER_NAME'];
$serverPort = $_SERVER['SERVER_PORT'];
$pathParts = pathinfo($_SERVER['SCRIPT_NAME']);
$pathInfo = $pathParts['dirname'];
$tokenUrl = 'http://';
if (isset($_SERVER['HTTPS']) && strcasecmp($_SERVER['HTTPS'], 'on') == 0)
{
$tokenUrl = 'https://';
}
$tokenUrl .= $serverName . ($serverPort != 80 ? ':' . $serverPort : '');
$tokenUrl .= $pathInfo;
$tokenUrl .= '/' . $_SERVER['SCRIPT_NAME'];
if (!empty($_SERVER['QUERY_STRING']))
{
$tokenUrl .= '?' . $_SERVER['QUERY_STRING'];
}
}
if (isset($this->settings['express.cancel_uri']))
{
$cancelUrl = $this->settings['express.cancel_uri'];
}
// Set up total $ for order
$orderTotal =& PayPal::getType('BasicAmountType');
if (PayPal::isError($orderTotal))
{
$this->errorCode = CAKE_COMPONENT_PAYPAL_ERROR_CANT_GET_AMOUNT_TYPE;
$this->error = $orderTotal->getMessage();
return false;
}
$orderTotal->setattr('currencyID', CAKE_COMPONENT_PAYPAL_CURRENCY);
$orderTotal->setval($this->settings['order']['total'], $this->settings['api.charset']);
// Set up request details
$requestDetails =& PayPal::getType('SetExpressCheckoutRequestDetailsType');
$requestDetails->setReturnURL($tokenUrl);
$requestDetails->setCancelURL($cancelUrl);
$requestDetails->setPaymentAction($this->settings['order']['action']);
$requestDetails->setOrderTotal($orderTotal);
// Set up request
$request =& PayPal::getType('SetExpressCheckoutRequestType');
$request->setSetExpressCheckoutRequestDetails($requestDetails);
// Execute request
$response = $this->caller->SetExpressCheckout($request);
if (PayPal::isError($response))
{
$this->errorCode = CAKE_COMPONENT_PAYPAL_ERROR_INVALID_REQUEST;
$this->error = $response->getMessage();
return false;
}
$response_ack = $response->getAck();
if ($response_ack == CAKE_COMPONENT_PAYPAL_ACK_SUCCESS || $response_ack == CAKE_COMPONENT_PAYPAL_ACK_SUCCESS_WITH_WARNING)
{
$token = $response->getToken();
$payPalUrl = CAKE_COMPONENT_PAYPAL_EXPRESS_CHECKOUT_URL;
$payPalUrl = str_replace('{$environment}', $this->settings['api.environment'], $payPalUrl);
$payPalUrl = str_replace('{$token}', $token, $payPalUrl);
header('Location: ' . $payPalUrl);
exit;
}
else
{
$errorList = $response->getErrors();
if(!is_array($errorList))
{
$this->errorCode = $errorList->getErrorCode();
$this->error = '#' . $errorList->getErrorCode() . ': ' . $errorList->getShortMessage() . ' [' . $errorList->getLongMessage() . ']';
}
else
{
$this->error = '';
foreach($errorList as $error)
{
if (!empty($this->error))
{
$this->error .= "\n";
}
$this->errorCode = $error->getErrorCode();
$this->error .= '#' . $error->getErrorCode() . ': ' . $error->getShortMessage() . ' [' . $error->getLongMessage() . ']';
}
return false;
}
}
}
else
{
// Set up request
$request =& PayPal::getType('GetExpressCheckoutDetailsRequestType');
$request->setToken($_REQUEST['token']);
// Execute request
$response = $this->caller->GetExpressCheckoutDetails($request);
if (PayPal::isError($response))
{
$this->errorCode = CAKE_COMPONENT_PAYPAL_ERROR_INVALID_REQUEST;
$this->error = $response->getMessage();
return false;
}
$response_ack = $response->getAck();
if ($response_ack == CAKE_COMPONENT_PAYPAL_ACK_SUCCESS || $response_ack == CAKE_COMPONENT_PAYPAL_ACK_SUCCESS_WITH_WARNING)
{
$responseDetails = $response->getGetExpressCheckoutDetailsResponseDetails();
$payerInfo = $responseDetails->getPayerInfo();
$payerId = $payerInfo->getPayerID();
}
else
{
$errorList = $response->getErrors();
if(!is_array($errorList))
{
$this->errorCode = $errorList->getErrorCode();
$this->error = '#' . $errorList->getErrorCode() . ': ' . $errorList->getShortMessage() . ' [' . $errorList->getLongMessage() . ']';
}
else
{
$this->error = '';
foreach($errorList as $error)
{
if (!empty($this->error))
{
$this->error .= "\n";
}
$this->errorCode = $error->getErrorCode();
$this->error .= '#' . $error->getErrorCode() . ': ' . $error->getShortMessage() . ' [' . $error->getLongMessage() . ']';
}
}
return false;
}
// Set up total $ for order
$orderTotal =& PayPal::getType('BasicAmountType');
if (PayPal::isError($orderTotal))
{
$this->errorCode = CAKE_COMPONENT_PAYPAL_ERROR_CANT_GET_AMOUNT_TYPE;
$this->error = $orderTotal->getMessage();
return false;
}
$orderTotal->setattr('currencyID', CAKE_COMPONENT_PAYPAL_CURRENCY);
$orderTotal->setval($this->settings['order']['total'], $this->settings['api.charset']);
// Set up payment details
$paymentDetails =& PayPal::getType('PaymentDetailsType');
$paymentDetails->setOrderTotal($orderTotal);
if (isset($this->settings['order']['description']))
{
$paymentDetails->setOrderDescription($this->settings['order']['description'], $this->settings['api.charset']);
}
if (isset($this->settings['order']['id']))
{
$paymentDetails->setInvoiceId($this->settings['order']['id'], $this->settings['api.charset']);
}
// Set up request details
$requestDetails =& PayPal::getType('DoExpressCheckoutPaymentRequestDetailsType');
$requestDetails->setToken($_REQUEST['token']);
$requestDetails->setPayerID($payerId);
$requestDetails->setPaymentAction($this->settings['order']['action']);
$requestDetails->setPaymentDetails($paymentDetails);
// Set up request
$request =& PayPal::getType('DoExpressCheckoutPaymentRequestType');
$request->setDoExpressCheckoutPaymentRequestDetails($requestDetails);
// Execute request
$response = $this->caller->DoExpressCheckoutPayment($request);
if (PayPal::isError($response))
{
$this->errorCode = CAKE_COMPONENT_PAYPAL_ERROR_INVALID_REQUEST;
$this->error = $response->getMessage();
return false;
}
$response_ack = $response->getAck();
if ($response_ack == CAKE_COMPONENT_PAYPAL_ACK_SUCCESS || $response_ack == CAKE_COMPONENT_PAYPAL_ACK_SUCCESS_WITH_WARNING)
{
$responseDetails = $response->getDoExpressCheckoutPaymentResponseDetails();
$responsePaymentInfo = $responseDetails->getPaymentInfo();
$response_amount = $responsePaymentInfo->getGrossAmount();
$result = array (
'transaction' => $responsePaymentInfo->getTransactionID(),
'ack' => $response_ack,
'amount' => $response_amount->_value
);
return $result;
}
else
{
$errorList = $response->getErrors();
if(!is_array($errorList))
{
$this->errorCode = $errorList->getErrorCode();
$this->error = '#' . $errorList->getErrorCode() . ': ' . $errorList->getShortMessage() . ' [' . $errorList->getLongMessage() . ']';
}
else
{
$this->error = '';
foreach($errorList as $error)
{
if (!empty($this->error))
{
$this->error .= "\n";
}
$this->errorCode = $error->getErrorCode();
$this->error .= '#' . $error->getErrorCode() . ': ' . $error->getShortMessage() . ' [' . $error->getLongMessage() . ']';
}
}
return false;
}
}
return false;
}
/**
* Initializes the PayPal API caller handler.
*
* @return bool true on success, false otherwise.
*
* @access private
* @since 1.0
*/
function _initialize()
{
$this->errorCode = 0;
$this->error = null;
$handler =& ProfileHandler_Array::getInstance(array(
'username' => $this->settings['api.username'],
'certificateFile' => null,
'subject' => null,
'environment' => $this->settings['api.environment'] ));
$pid = ProfileHandler::generateID();
$profile =& new APIProfile($pid, $handler);
$profile->setAPIUsername($this->settings['api.username']);
$profile->setAPIPassword($this->settings['api.password']);
if(isset($this->settings['api.certificate']))
{
$profile->setCertificateFile($this->settings['api.certificate']);
}
$profile->setSignature($this->settings['api.signature']);
$profile->setEnvironment($this->settings['api.environment']);
$this->caller =& PayPal::getCallerServices($profile);
if (PayPal::isError($this->caller))
{
$this->errorCode = CAKE_COMPONENT_PAYPAL_ERROR_CANT_CREATE_CALLER;
$this->error = $this->caller->getMessage();
unset($this->caller);
return false;
}
return true;
}
}
?>
Comments
Comment
1 PayPal Direct Payment API Version
Is the API used the US version for paypal pro?
I am wondering as paypal pro api is now avaliable in the uk but doesn't use the same sdk as the US version.
Would it be easy to have an all-in-one component that handled all version as this could be updated centrally instead of the uk users having to write there own?
Penfold
Comment
2 PayPal Direct Payment API Version
Penfold: Well I'm using the API they give you when you look for DirectPayment's SDK. I find it hard to believe they would change the API for another country. Can you provide a link (to my email) with the information you have that tells you their SDKs are different for US and UK?
Comment
3 Thanks
Question
4 UK version
Comment
5 Thank You
I'll be trying it out today. It will most definitely save me many, many hours.
Question
6 API Question
Controller Class:
<?php$this->Paypal->setEnvironment(CAKE_COMPONENT_PAYPAL_ENVIRONMENT_SANDBOX);
$this->Paypal->setUser('ApiUser');
$this->Paypal->setPassword('ApiPassword');
$this->Paypal->setCertificate('sandbox.paypal.com.pem');
$this->Paypal->setOrder($order);
?>
Hi, where should i put the pem file? technically it is located under {cake}/app/vendors/PEAR/PayPal/cert but the code errors up the says sandbox.paypal.com.pem cannot be located.
Please, badly need help to implement your API.
Comment
7 API Question
Comment
8 important
- Remove setCertificate and replace it with
$this->Paypal->setSignature('signature provided by paypal');.
and people it worked!!
Question
9 Cake 1.2 integration for the Paypal component
Comment
10 Cake 1.2 integration for the Paypal component
Comment
11 Fatal Error
Warning (2): require_once(PayPal.php) [function.require-once]: failed to open stream: No such file or directory [APP/controllers/components/paypal.php, line 12]Comment
12 Complicated Solutions
I try to implement the whole tutorials but it seems...it's quit difficult for me to understand with specially it doesn't give any instruction where to go. Where to put the a certain code. Specially the form and it's components.
Can u or anyone could help me how to make a step by step work in cooking paypal payment methods? Sorry but i'm just a newbie,...could anyone help me???
thnks
Comment
13 hi i have same problem... can you help me with it
i am able to copy PEAR and paypal component ...every thing is working fine (it is able to include Pear , recognize paypal component).. but i am unable to figure out hoe to implement it in my project.
please guide me to solve this issue..
i will be greatfull to you if you can send some example application if you can...
Thanks in advance
M A Rasheed
Question
14 Reset Endpoints?
So, here is my situation. I seem to have everything working fine on cake1.2 on my local box (os x leopard). However, when I deploy the same code to the pre-production box (Aplus.net shared hosting), I am getting a curl permission denied issue. Typically this can be due to proxy issues, and I think its only related to hitting the sandbox site.
Apparently, this can be fixed by resetting the endpoint URL to api-aa.sandbox.paypal.com/2.0/.
However, I can not for the life of me find the section in the code that is physically setting the URL request in the DirectPayment method. I see how its setting the environment to the constant declared above (i.e. CAKE_COMPONENT_PAYPAL_ENVIRONMENT_SANDBOX)-- and I can see in the Express Checkout where its replacing the string based off of that value. But, I can not find how the request is being built for the directPayment method to change the value to api-aa.sandbox.paypal.com/2.0/.
Please let me know if anyone has any ideas.
Thanks!
Chris
Question
15 I want guideline to integrate Paypal in any Cake Project
I am not able to understand the given code. Some part of code is not explained where to place it
Comment
16 I am getting same error on live server
Hi it is working fine on local system (localhost)but it is not working on live server.... any solution?
please help me!!
Thanks in advance
M A Rasheed
Comment
17 Instructions for use
To use this component, the following steps need to be taken:
1) The complete code for the component (located on page 4) needs to be saved as app/controller/components/paypal.php
2) In the controller you wish to use the component for, add: var $uses = array('Paypal');
3) In your controller, add the example express() method (provided on page 2).
This presumes that you have the PayPal API set up. The PayPal API is completely separate to this component. To install the PayPal API:
1) Download the PayPal API from the PayPal SDK website
2) Copy the file PayPal.php and the folder PayPal into your existing PEAR installation.
This again assume that you have a working PEAR installation, and that your include_path correctly includes your PEAR library.
Question
18 Accessing the SetExpressCheckoutRequestDetails object
At the moment, the way the component is written, it's not possible to access this object. Can the author think of a way to allow customisation without hardcoding it in the component?
Comment
19 Paypal
Question
20 How to solve Cannot modify header information Error ?
it will occur when on line number 731 in paypal.php
header('Location: ' . $payPalUrl);
Question
21 Requires more Detailed Installation
Thanks for this tutorial. I have tried to follow it through along with help from existing comments but there seem to be header modify error that is very annoying.
This is what I have done.
I have saved the PaypalComponent php script as paypal.php in components/.
I have created a folder inside vendor called "PEAR", inside I have extracted the base lib folder from pear base package as well as the Paypal SDK.
I have also created a Pear.inc.php file inside vendor that Includes the PEAR directory.
In my payment controller, I have added App::import('Vendor', 'PearVendor', array('file' => 'pear.inc.php')); on the top.
And var $components = array('othAuth', 'Paypal');
Now, when I try to access any actions inside this controller, it gives me the following error:
Warning (2): Cannot modify header information - headers already sent by (output started at F:\production\Workspace\paymenttest\dev\app\controllers\components\paypal.php:984) [CORE\cake\libs\controller\controller.php, line 640]
Code | Context
$status = "Location: http://localhost/users/login?from=users/express"
header - [internal], line ??
Controller::header() - CORE\cake\libs\controller\controller.php, line 640
Controller::redirect() - CORE\cake\libs\controller\controller.php, line 621
othAuthComponent::redirect() - APP\controllers\components\oth_auth.php, line 818
othAuthComponent::check() - APP\controllers\components\oth_auth.php, line 936
AppController::beforeFilter() - APP\app_controller.php, line 26
Dispatcher::_invoke() - CORE\cake\dispatcher.php, line 209
Dispatcher::dispatch() - CORE\cake\dispatcher.php, line 194
[main] - APP\webroot\index.php, line 88
---------
Any help is appreciated.
Comment
22 Paypal outdated endpoints
When moved to LIVE, I was getting success response messages with the transaction IDs. However, no money being charged to the CC and no money going to the Paypal's account.
After spending about an hour with the Paypal technician on the phone, we learned that the endpoint URLs are incorrect in the PEAR package !.
To correct that, you need to update these files :
(suppose you installed the PEAT into vendors (not in the app dir, but in the root of your cake installation)
vendors/PEAR/Paypal/lib/Paypal/wsdl/paypal-endpoints.php
vendors/PEAR/Paypal/lib/Paypal/wsdl/paypal-endpoints.xml
Note ; I do not know which file is used , so I update both of them
This is the new values for the LIVE environment :
'PayPalAPI' => 'https://api-3t.paypal.com',
'PayPalAPIAA' => 'https://api-3t.paypal.com',
'PayPalAPI-threetoken' => 'https://api-3t.paypal.com',
'PayPalAPIAA-threetoken' => 'https://api-3t.paypal.com',
Change the URL in the XML file to https://api-3t.paypal.com as well.
Everything should be fine and you should get the money into your account.
Note : these files also hold min and max values for the price you charge via Paypal.
Note 2 : I have created complete a complete payments controller with four (4) steps :
1) enter personal info
2) enter billing info
3) review order (with the options to edit)
4) process payment
The payments controller also have admin functionality.
Let me know if anyone were interested in this as a packge.
Good Luck
Martin
Comment
23 PayPal Direct Payment API Component for 1.2
trying to make the component work, I finally got it working in Cake
1.2. I'm relatively new to Cake, but have been working with PHP for
over 5 year, so there are a few things that I'm not sure if I used the
best way to do it.
So this are the confusions I got:
1. The PEAR directory I comes like PEAR-1.8.1/PEAR. I wasn't sure of
which of the two directories I should uncompress and where. Also I had
confusions with what directories should be uncompressed from the
PayPal SDK.
For the PEAR directory, you should uncompress the files so you get the
file PEAR.php in vendors/PEAR/ (vendors/PEAR/PEAR.php).
For the SDK, you should uncompress the php-sdk/lib directory at
vendors/PEAR/.
The final directory structure should look like the following: (only
showing the main files, not all of them)
->{CAKE}/app/vendors
->./PEAR
->./HTTP
->./Log
->./Net
->./PayPal
->./PEAR
->PayPal.php
->Multi.php
->Log.php
->PEAR.php
->Multi.php
2. The second confusion I had was how to replace the vendors call to
App::import() for Cake 1.2... Unfortunately this wasn't working for me
at all. So I (and this is the part that I think is not how it's
supposed to be done, but I worked for me) went to the bootstrap file
({cake}/app/config/bootstrap.php) and added the following code to the
bottom of the file:
set_include_path(dirnameThis sets the PEAR directory in the vendors file as part of the(__FILE__).DS.'..'.DS.'vendors'.DS.'PEAR'.PATH_SEPARATOR.get_include_path
());
include path, so all the require calls from {cake}/app/controllers/
components/paypal.php work fine.
3. I'm a bit ashamed of this one, but I forgot for a moment that they
were talking about a component for I was a bit confused of were to put
the paypal component content. It was a stupid confusion, but if
anybody has a doubt, you have to put the component in {cake}/app/
controllers/components/paypal.php.
Those were the main confusions that were bugging me during the whole
day.
To make the component work, follow this steps:
1. Download PEAR and place it how it's explained in my confusions #1
(above)
2. Download the PayPal PHP SDK (https://cms.paypal.com/us/cgi-bin/? cmd=_render-content&content_ID=developer/library_download_sdks) and
uncompress it how I explained in my confusions #1 (above) -> 3. Create the file {cake}/app/controllers/components/paypal.php and
write the components content (http://bakery.cakephp.org/leafs/view/22)
in this file.
4. Don't forget to add 'Paypal' to your controller's components: var
$components = array('Paypal');
5. Change the credentials (username,password and signature/
certificate) to your paypal's account
Note: In the example code, a certificate is being used instead of a
signature. Paypal let you use one or the other, if you use signature,
change the method call from $this->Paypal->setCertificate() to $this-
>Paypal->setSignature().
6. You should be able to use now the example code at
http://bakery.cakephp.org/leafs/view/20
I hope this was helpful for somebody. If you have any questions you
can send me an email and I'll try to help you!
Comment
24 problem with live server
i did the installation like posted by Jorge Pedret. it works fine on my local server (win xp, xampp). but i get an error on my live server:
Fatal error: require_once() [http://php.net/function.require]: Failed opening required 'PEAR.php'does anybody have any idea why?
Question
25 a bit cofused about the paypal component
I am confused about the php component, I have this example component called components/paypal.php with the class PaypalComponent extends Object, also it seems that the userful component for direct paypal payment an example given as....
Useful component that provides a wrapper for PayPal's Direct Payment API, ...
Doing a Direct Payment
$order = array(
'action' => CAKE_COMPONENT_PAYPAL_ORDER_TYPE_SALE,
'description' => 'CakePHP Component',
'total' => 100.00,
'buyer' => array (
'first' => 'Mariano',
'last' => 'Iglesias',....
My question is where does this code go???
$order = array(
'action' => CAKE_COMPONENT_PAYPAL_ORDER_TYPE_SALE,
'description' => 'CakePHP Component',
'total' => 100.00,
'buyer' => array (
'first' => 'Mariano',
'last' => 'Iglesias',....
In the component/paypal.php component?
Question
26 Live problems
I was wondering if anybody has ran into this problem when going from the sandbox with zero problems to live.
When I go to place the order i get this error message:
Safari can't find the server "www.live.paypal.com".
I've tried the endpoints that Martin posted earlier and still didn't work.
All I did was change setEnvironment to this from the sandbox:
$this->Paypal->setEnvironment(CAKE_COMPONENT_PAYPAL_ENVIRONMENT_LIVE);
Any help or suggestions would be greatly appreciated.
Comment
27 www.live.paypal.com
$payPalUrl = str_replace('live.', '', $payPalUrl);
underneath the line:
$payPalUrl = str_replace('{$environment}', $this->settings['api.environment'], $payPalUrl);
Comment
28 Internet Explorer 8.0
Somebody please let me know if its just a bug in my code or if it is with the component.
Question
29 error on Profile::Profile()
I'm fairly new to PEAR, though, have some experience with Cake ...
I'm following all the steps and think i manage to get the whole setup up and running ... I used
set_include_path(dirname(__FILE__).DS.'..'.DS.'vendors'.DS.'PEAR'.PATH_SEPARATOR.get_include_path());
to add PEAR include PATH ...
I keep getting these error messages:
----------------------------------------------
Missing argument 2 for Profile::Profile(), called in E:\workspaces\cake_1.2.5\cake\libs\class_registry.php on line 140 and defined [APP\vendors\PEAR\PayPal\Profile.php, line 59]
Undefined property: Profile::$table [CORE\cake\libs\model\model.php, line 646]
Warning (2): Cannot modify header information - headers already sent by (output started at E:\workspaces\cake_1.2.5\app\controllers\components\paypal.php:976) [CORE\cake\libs\controller\controller.php, line 644] ----------------------------------------------
I thought at first it might have to do with the Profile model i'm using in my app users section, but that sounds unlikely ..
ANyone has a clue what i might be doing wrong .. ?
Thanks all ....
////////////////////////////////
An update on this, for who might be having the same problem: setting debug to Configure::write('debug',0); seems to have fixed it, though am not sure yet cuz i still have to test the whole thing ...
cheers
ps Thanks for this copmponent by the way :-) ...
Comment
30 Nice Post.