<?php
/*
+--------------------------------------------------------------------+
- | CiviCRM version 4.5 |
+ | CiviCRM version 4.6 |
+--------------------------------------------------------------------+
- | Copyright CiviCRM LLC (c) 2004-2014 |
+ | Copyright CiviCRM LLC (c) 2004-2015 |
+--------------------------------------------------------------------+
| This file is a part of CiviCRM. |
| |
| GNU Affero General Public License or the licensing of CiviCRM, |
| see the CiviCRM license FAQ at http://civicrm.org/licensing |
+--------------------------------------------------------------------+
-*/
+ */
/**
*
* @package CRM
- * @copyright CiviCRM LLC (c) 2004-2014
+ * @copyright CiviCRM LLC (c) 2004-2015
* $Id$
*
*/
class CRM_Core_Payment_PayPalImpl extends CRM_Core_Payment {
- CONST CHARSET = 'iso-8859-1';
+ const CHARSET = 'iso-8859-1';
protected $_mode = NULL;
* pattern and cache the instance in this variable
*
* @var object
- * @static
*/
static private $_singleton = NULL;
/**
- * Constructor
+ * Constructor.
*
- * @param string $mode the mode of operation: live or test
+ * @param string $mode
+ * The mode of operation: live or test.
*
* @param $paymentProcessor
*
* @return \CRM_Core_Payment_PayPalImpl
*/
- function __construct($mode, &$paymentProcessor) {
+ public function __construct($mode, &$paymentProcessor) {
$this->_mode = $mode;
$this->_paymentProcessor = $paymentProcessor;
$this->_processorName = ts('PayPal Pro');
- $paymentProcessorType = CRM_Core_PseudoConstant::paymentProcessorType(false, null, 'name');
+ $paymentProcessorType = CRM_Core_PseudoConstant::paymentProcessorType(FALSE, NULL, 'name');
if ($this->_paymentProcessor['payment_processor_type_id'] == CRM_Utils_Array::key('PayPal_Standard', $paymentProcessorType)) {
$this->_processorName = ts('PayPal Standard');
$this->_processorName = ts('PayPal Express');
}
- if (!$this->_paymentProcessor['user_name']) {
- CRM_Core_Error::fatal(ts('Could not find user name for payment processor'));
+ }
+
+ /**
+ * Are back office payments supported - e.g paypal standard won't permit you to enter a credit card associated with someone else's login
+ * @return bool
+ */
+ protected function supportsBackOffice() {
+ if ($this->_processorName == ts('PayPal Pro')) {
+ return TRUE;
+ }
+ return FALSE;
+ }
+
+ /**
+ * Does this processor support pre-approval.
+ *
+ * This would generally look like a redirect to enter credentials which can then be used in a later payment call.
+ *
+ * Currently Paypal express supports this, with a redirect to paypal after the 'Main' form is submitted in the
+ * contribution page. This token can then be processed at the confirm phase. Although this flow 'looks' like the
+ * 'notify' flow a key difference is that in the notify flow they don't have to return but in this flow they do.
+ *
+ * @return bool
+ */
+ protected function supportsPreApproval() {
+ if ($this->_processorName == ts('PayPal_Express')) {
+ return TRUE;
}
+ return FALSE;
}
/**
- * singleton function used to manage this object
+ * Opportunity for the payment processor to override the entire form build.
*
- * @param string $mode the mode of operation: live or test
+ * @param CRM_Core_Form $form
*
- * @param object $paymentProcessor
- * @param null $paymentForm
- * @param bool $force
+ * @return bool
+ * Should form building stop at this point?
+ */
+ public function buildForm(&$form) {
+ if ($this->_processorName == 'PayPal Express' || $this->_processorName == 'PayPal Pro') {
+ $this->addPaypalExpressCode($form);
+ if ($this->_processorName == 'PayPal Express') {
+ CRM_Core_Region::instance('billing-block-post')->add(array(
+ 'template' => 'CRM/Financial/Form/PaypalExpress.tpl',
+ 'name' => 'paypal_express',
+ ));
+ }
+ if ($this->_processorName == 'PayPal Pro') {
+ CRM_Core_Region::instance('billing-block-pre')->add(array(
+ 'template' => 'CRM/Financial/Form/PaypalPro.tpl',
+ ));
+ }
+ }
+ return FALSE;
+ }
+
+ /**
+ * Billing mode button is basically synonymous with paypal express - this is probably a good example of 'odds & sods' code we
+ * need to find a way for the payment processor to assign. A tricky aspect is that the payment processor may need to set the order
*
- * @return object
- * @static
+ * @param $form
*/
- static function &singleton($mode, &$paymentProcessor, &$paymentForm = NULL, $force = FALSE) {
- $processorName = $paymentProcessor['name'];
- if (!isset(self::$_singleton[$processorName]) || self::$_singleton[$processorName] === NULL) {
- self::$_singleton[$processorName] = new CRM_Core_Payment_PaypalImpl($mode, $paymentProcessor);
+ protected function addPaypalExpressCode(&$form) {
+ if (empty($form->isBackOffice)) {
+ $form->_expressButtonName = $form->getButtonName('upload', 'express');
+ $form->assign('expressButtonName', $form->_expressButtonName);
+ $form->add(
+ 'image',
+ $form->_expressButtonName,
+ $this->_paymentProcessor['url_button'],
+ array('class' => 'crm-form-submit')
+ );
}
- return self::$_singleton[$processorName];
}
/**
- * express checkout code. Check PayPal documentation for more information
+ * Express checkout code. Check PayPal documentation for more information
*
- * @param array $params assoc array of input parameters for this transaction
+ * @param array $params
+ * Assoc array of input parameters for this transaction.
*
- * @return array the result in an nice formatted array (or an error object)
- * @public
+ * @return array
+ * the result in an nice formatted array (or an error object)
*/
- function setExpressCheckOut(&$params) {
+ public function setExpressCheckOut(&$params) {
$args = array();
$this->initialize($args, 'SetExpressCheckout');
- $args['paymentAction'] = $params['payment_action'];
+ $args['paymentAction'] = 'Sale';
$args['amt'] = $params['amount'];
$args['currencyCode'] = $params['currencyID'];
$args['desc'] = CRM_Utils_Array::value('description', $params);
$args['invnum'] = $params['invoiceID'];
- $args['returnURL'] = $params['returnURL'];
- $args['cancelURL'] = $params['cancelURL'];
+ $args['returnURL'] = $this->getReturnSuccessUrl($params['qfKey']);
+ $args['cancelURL'] = $this->getCancelUrl($params['qfKey'], NULL);
$args['version'] = '56.0';
//LCD if recurring, collect additional data and set some values
}
/**
- * get details from paypal. Check PayPal documentation for more information
+ * Get details from paypal. Check PayPal documentation for more information
*
- * @param string $token the key associated with this transaction
+ * @param string $token
+ * The key associated with this transaction.
*
- * @return array the result in an nice formatted array (or an error object)
- * @public
+ * @return array
+ * the result in an nice formatted array (or an error object)
*/
- function getExpressCheckoutDetails($token) {
+ public function getExpressCheckoutDetails($token) {
$args = array();
$this->initialize($args, 'GetExpressCheckoutDetails');
}
/**
- * do the express checkout at paypal. Check PayPal documentation for more information
+ * Do the express checkout at paypal. Check PayPal documentation for more information
*
- * @param $params
+ * @param array $params
*
* @internal param string $token the key associated with this transaction
*
- * @return array the result in an nice formatted array (or an error object)
- * @public
+ * @return array
+ * the result in an nice formatted array (or an error object)
*/
- function doExpressCheckout(&$params) {
+ public function doExpressCheckout(&$params) {
$args = array();
$this->initialize($args, 'DoExpressCheckoutPayment');
-
$args['token'] = $params['token'];
- $args['paymentAction'] = $params['payment_action'];
+ $args['paymentAction'] = 'Sale';
$args['amt'] = $params['amount'];
$args['currencyCode'] = $params['currencyID'];
$args['payerID'] = $params['payer_id'];
$args['invnum'] = $params['invoiceID'];
- $args['returnURL'] = CRM_Utils_Array::value('returnURL', $params);
- $args['cancelURL'] = CRM_Utils_Array::value('cancelURL', $params);
+ $args['returnURL'] = $this->getReturnSuccessUrl($params['qfKey']);
+ $args['cancelURL'] = $this->getCancelUrl($params['qfKey'], NULL);
$args['desc'] = $params['description'];
// add CiviCRM BN code
//LCD add new function for handling recurring payments for PayPal Express
/**
- * @param $params
+ * @param array $params
*
* @return mixed
*/
- function createRecurringPayments(&$params) {
+ public function createRecurringPayments(&$params) {
$args = array();
$this->initialize($args, 'CreateRecurringPaymentsProfile');
$start_date = date('Y-m-d\T00:00:00\Z', $start_time);
$args['token'] = $params['token'];
- $args['paymentAction'] = $params['payment_action'];
+ $args['paymentAction'] = 'Sale';
$args['amt'] = $params['amount'];
$args['currencyCode'] = $params['currencyID'];
$args['payerID'] = $params['payer_id'];
//$args['desc'] = 'Recurring Contribution';
$args['totalbillingcycles'] = $params['installments'];
$args['version'] = '56.0';
- $args['profilereference'] =
- "i={$params['invoiceID']}" .
+ $args['profilereference'] = "i={$params['invoiceID']}" .
"&m=$component" .
"&c={$params['contactID']}" .
"&r={$params['contributionRecurID']}" .
* @param $args
* @param $method
*/
- function initialize(&$args, $method) {
- $args['user'] = $this->_paymentProcessor['user_name'];
- $args['pwd'] = $this->_paymentProcessor['password'];
- $args['version'] = 3.0;
+ public function initialize(&$args, $method) {
+ $args['user'] = $this->_paymentProcessor['user_name'];
+ $args['pwd'] = $this->_paymentProcessor['password'];
+ $args['version'] = 3.0;
$args['signature'] = $this->_paymentProcessor['signature'];
- $args['subject'] = $this->_paymentProcessor['subject'];
- $args['method'] = $method;
+ $args['subject'] = CRM_Utils_Array::value('subject', $this->_paymentProcessor);
+ $args['method'] = $method;
}
/**
* This function collects all the information from a web/api form and invokes
* the relevant payment processor specific functions to perform the transaction
*
- * @param array $params assoc array of input parameters for this transaction
+ * @param array $params
+ * Assoc array of input parameters for this transaction.
*
* @param string $component
- * @return array the result in an nice formatted array (or an error object)
- * @public
+ * @return array
+ * the result in an nice formatted array (or an error object)
*/
- function doDirectPayment(&$params, $component = 'contribute') {
+ public function doDirectPayment(&$params, $component = 'contribute') {
$args = array();
$this->initialize($args, 'DoDirectPayment');
- $args['paymentAction'] = $params['payment_action'];
+ $args['paymentAction'] = 'Sale';
$args['amt'] = $params['amount'];
$args['currencyCode'] = $params['currencyID'];
$args['invnum'] = $params['invoiceID'];
$args['billingfrequency'] = $params['frequency_interval'];
$args['method'] = "CreateRecurringPaymentsProfile";
$args['profilestartdate'] = $start_date;
- $args['desc'] =
+ $args['desc'] = "" .
$params['description'] . ": " .
$params['amount'] . " Per " .
$params['frequency_interval'] . " " .
$args['amt'] = $params['amount'];
$args['totalbillingcycles'] = $params['installments'];
$args['version'] = 56.0;
- $args['PROFILEREFERENCE'] =
+ $args['PROFILEREFERENCE'] = "" .
"i=" . $params['invoiceID'] . "&m=" . $component .
"&c=" . $params['contactID'] . "&r=" . $params['contributionRecurID'] .
"&b=" . $params['contributionID'] . "&p=" . $params['contributionPageID'];
}
/**
- * This function checks to see if we have the right config values
+ * This function checks to see if we have the right config values.
*
- * @return string the error message if any
- * @public
+ * @return string
+ * the error message if any
*/
- function checkConfig() {
+ public function checkConfig() {
$error = array();
- $paymentProcessorType = CRM_Core_PseudoConstant::paymentProcessorType(false, null, 'name');
- if (
- $this->_paymentProcessor['payment_processor_type_id'] == CRM_Utils_Array::key('PayPal_Standard', $paymentProcessorType) ||
- $this->_paymentProcessor['payment_processor_type_id'] == CRM_Utils_Array::key('PayPal', $paymentProcessorType)
- ) {
- if (empty($this->_paymentProcessor['user_name'])) {
- $error[] = ts('User Name is not set in the Administer » System Settings » Payment Processors.');
- }
- }
+ $paymentProcessorType = CRM_Core_PseudoConstant::paymentProcessorType(FALSE, NULL, 'name');
if ($this->_paymentProcessor['payment_processor_type_id'] != CRM_Utils_Array::key('PayPal_Standard', $paymentProcessorType)) {
if (empty($this->_paymentProcessor['signature'])) {
$error[] = ts('Password is not set in the Administer » System Settings » Payment Processors.');
}
}
+ if (!$this->_paymentProcessor['user_name']) {
+ $error[] = ts('User Name is not set in the Administer » System Settings » Payment Processors.');
+ }
if (!empty($error)) {
return implode('<p>', $error);
/**
* @return null|string
*/
- function cancelSubscriptionURL() {
+ public function cancelSubscriptionURL() {
if ($this->_paymentProcessor['payment_processor_type'] == 'PayPal_Standard') {
return "{$this->_paymentProcessor['url_site']}cgi-bin/webscr?cmd=_subscr-find&alias=" . urlencode($this->_paymentProcessor['user_name']);
}
}
/**
- * Function to check whether a method is present ( & supported ) by the payment processor object.
+ * Check whether a method is present ( & supported ) by the payment processor object.
*
- * @param string $method method to check for.
+ * @param string $method
+ * Method to check for.
*
- * @return boolean
- * @public
+ * @return bool
*/
- function isSupported($method = 'cancelSubscription') {
+ public function isSupported($method = 'cancelSubscription') {
if ($this->_paymentProcessor['payment_processor_type'] != 'PayPal') {
// since subscription methods like cancelSubscription or updateBilling is not yet implemented / supported
// by standard or express.
return parent::isSupported($method);
}
+ /**
+ * Paypal express replaces the submit button with it's own.
+ *
+ * @return bool
+ * Should the form button by suppressed?
+ */
+ public function isSuppressSubmitButtons() {
+ if ($this->_paymentProcessor['payment_processor_type'] == 'PayPal_Express') {
+ return TRUE;
+ }
+ return FALSE;
+ }
+
/**
* @param string $message
* @param array $params
*
* @return array|bool|object
*/
- function cancelSubscription(&$message = '', $params = array(
- )) {
+ public function cancelSubscription(&$message = '', $params = array()) {
if ($this->_paymentProcessor['payment_processor_type'] == 'PayPal') {
$args = array();
$this->initialize($args, 'ManageRecurringPaymentsProfileStatus');
$args['PROFILEID'] = CRM_Utils_Array::value('subscriptionId', $params);
- $args['ACTION'] = 'Cancel';
- $args['NOTE'] = CRM_Utils_Array::value('reason', $params);
+ $args['ACTION'] = 'Cancel';
+ $args['NOTE'] = CRM_Utils_Array::value('reason', $params);
$result = $this->invokeAPI($args);
if (is_a($result, 'CRM_Core_Error')) {
return FALSE;
}
+ /**
+ * Process incoming notification.
+ *
+ * This is only supported for paypal pro at the moment & no specific plans to add this path to core
+ * for paypal standard as the goal must be to separate the 2.
+ *
+ * We don't need to handle paypal standard using this path as there has never been any historic support
+ * for paypal standard to call civicrm/payment/ipn as a path.
+ */
+ static public function handlePaymentNotification() {
+ $paypalIPN = new CRM_Core_Payment_PayPalProIPN($_REQUEST);
+ $paypalIPN->main();
+ }
+
/**
* @param string $message
* @param array $params
*
* @return array|bool|object
*/
- function updateSubscriptionBillingInfo(&$message = '', $params = array(
- )) {
+ public function updateSubscriptionBillingInfo(&$message = '', $params = array()) {
if ($this->_paymentProcessor['payment_processor_type'] == 'PayPal') {
$config = CRM_Core_Config::singleton();
$args = array();
$args['EXPDATE'] = sprintf('%02d', $params['month']) . $params['year'];
$args['CVV2'] = $params['cvv2'];
- $args['FIRSTNAME'] = $params['first_name'];
- $args['LASTNAME'] = $params['last_name'];
- $args['STREET'] = $params['street_address'];
- $args['CITY'] = $params['city'];
- $args['STATE'] = $params['state_province'];
+ $args['FIRSTNAME'] = $params['first_name'];
+ $args['LASTNAME'] = $params['last_name'];
+ $args['STREET'] = $params['street_address'];
+ $args['CITY'] = $params['city'];
+ $args['STATE'] = $params['state_province'];
$args['COUNTRYCODE'] = $params['postal_code'];
- $args['ZIP'] = $params['country'];
+ $args['ZIP'] = $params['country'];
$result = $this->invokeAPI($args);
if (is_a($result, 'CRM_Core_Error')) {
*
* @return array|bool|object
*/
- function changeSubscriptionAmount(&$message = '', $params = array()) {
+ public function changeSubscriptionAmount(&$message = '', $params = array()) {
if ($this->_paymentProcessor['payment_processor_type'] == 'PayPal') {
$config = CRM_Core_Config::singleton();
$args = array();
return FALSE;
}
+ /**
+ * Function to action pre-approval if supported
+ *
+ * @param array $params
+ * Parameters from the form
+ *
+ * @return array
+ * - pre_approval_parameters (this will be stored on the calling form & available later)
+ * - redirect_url (if set the browser will be redirected to this.
+ */
+ public function doPreApproval(&$params) {
+ $token = $this->setExpressCheckOut($params);
+ return array(
+ 'pre_approval_parameters' => array('token' => $token),
+ 'redirect_url' => $this->_paymentProcessor['url_site'] . "/cgi-bin/webscr?cmd=_express-checkout&token=$token",
+ );
+ }
+
/**
- * @param $params
+ * @param array $params
* @param string $component
*
* @throws Exception
*/
- function doTransferCheckout(&$params, $component = 'contribute') {
+ public function doTransferCheckout(&$params, $component = 'contribute') {
$config = CRM_Core_Config::singleton();
if ($component != 'contribute' && $component != 'event') {
}
}
- $url = ($component == 'event') ? 'civicrm/event/register' : 'civicrm/contribute/transact';
- $cancel = ($component == 'event') ? '_qf_Register_display' : '_qf_Main_display';
+ $url = ($component == 'event') ? 'civicrm/event/register' : 'civicrm/contribute/transact';
+ $cancel = ($component == 'event') ? '_qf_Register_display' : '_qf_Main_display';
$returnURL = CRM_Utils_System::url($url,
"_qf_ThankYou_display=1&qfKey={$params['qfKey']}",
TRUE, NULL, FALSE
$paypalParams += array(
'cmd' => '_xclick-subscriptions',
- 'a3' => $params['amount'],
- 'p3' => $params['frequency_interval'],
- 't3' => ucfirst(substr($params['frequency_unit'], 0, 1)),
+ 'a3' => $params['amount'],
+ 'p3' => $params['frequency_interval'],
+ 't3' => ucfirst(substr($params['frequency_unit'], 0, 1)),
'src' => 1,
'sra' => 1,
'srt' => CRM_Utils_Array::value('installments', $params),
$uri .= "&{$key}={$value}";
}
- $uri = substr($uri, 1);
- $url = $this->_paymentProcessor['url_site'];
- $sub = empty($params['is_recur']) ? 'cgi-bin/webscr' : 'subscriptions';
+ $uri = substr($uri, 1);
+ $url = $this->_paymentProcessor['url_site'];
+ $sub = empty($params['is_recur']) ? 'cgi-bin/webscr' : 'subscriptions';
$paypalURL = "{$url}{$sub}?$uri";
CRM_Utils_System::redirect($paypalURL);
}
/**
- * hash_call: Function to perform the API call to PayPal using API signature
+ * Hash_call: Function to perform the API call to PayPal using API signature
* @methodName is name of API method.
* @nvpStr is nvp string.
* returns an associtive array containing the response from the server.
*/
- function invokeAPI($args, $url = NULL) {
+ public function invokeAPI($args, $url = NULL) {
if ($url === NULL) {
if (empty($this->_paymentProcessor['url_api'])) {
return $result;
}
- /** This function will take NVPString and convert it to an Associative Array and it will decode the response.
- * It is usefull to search for a particular key and displaying arrays.
+ /**
+ * This function will take NVPString and convert it to an Associative Array and it will decode the response.
+ * It is useful to search for a particular key and displaying arrays.
* @nvpstr is NVPString.
* @nvpArray is Associative Array.
*/
- static function deformat($str) {
+ public static function deformat($str) {
$result = array();
while (strlen($str)) {
- // postion of key
+ // position of key
$keyPos = strpos($str, '=');
// position of value
return $result;
}
-}
+}