CRM-16808 standardisation of paypal express
[civicrm-core.git] / CRM / Core / Payment / PayPalImpl.php
index 19608b8d8aecc34fd12a573780b269e988b0fb99..7cc8755026538f3caf43a50fd68ef0380e551e38 100644 (file)
@@ -1,9 +1,9 @@
 <?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;
 
@@ -42,24 +42,24 @@ class CRM_Core_Payment_PayPalImpl extends CRM_Core_Payment {
    * 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');
@@ -69,51 +69,103 @@ class CRM_Core_Payment_PayPalImpl extends CRM_Core_Payment {
       $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
@@ -139,14 +191,15 @@ class CRM_Core_Payment_PayPalImpl extends CRM_Core_Payment {
   }
 
   /**
-   * 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');
@@ -180,28 +233,27 @@ class CRM_Core_Payment_PayPalImpl extends CRM_Core_Payment {
   }
 
   /**
-   * 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
@@ -230,11 +282,11 @@ class CRM_Core_Payment_PayPalImpl extends CRM_Core_Payment {
 
   //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');
@@ -243,7 +295,7 @@ class CRM_Core_Payment_PayPalImpl extends CRM_Core_Payment {
     $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'];
@@ -258,8 +310,7 @@ class CRM_Core_Payment_PayPalImpl extends CRM_Core_Payment {
     //$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']}" .
@@ -293,31 +344,32 @@ class CRM_Core_Payment_PayPalImpl extends CRM_Core_Payment {
    * @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'];
@@ -349,7 +401,7 @@ class CRM_Core_Payment_PayPalImpl extends CRM_Core_Payment {
       $args['billingfrequency'] = $params['frequency_interval'];
       $args['method'] = "CreateRecurringPaymentsProfile";
       $args['profilestartdate'] = $start_date;
-      $args['desc'] =
+      $args['desc'] = "" .
         $params['description'] . ": " .
         $params['amount'] . " Per " .
         $params['frequency_interval'] . " " .
@@ -357,7 +409,7 @@ class CRM_Core_Payment_PayPalImpl extends CRM_Core_Payment {
       $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'];
@@ -387,22 +439,14 @@ class CRM_Core_Payment_PayPalImpl extends CRM_Core_Payment {
   }
 
   /**
-   * 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 &raquo; System Settings &raquo; 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'])) {
@@ -413,6 +457,9 @@ class CRM_Core_Payment_PayPalImpl extends CRM_Core_Payment {
         $error[] = ts('Password is not set in the Administer &raquo; System Settings &raquo; Payment Processors.');
       }
     }
+    if (!$this->_paymentProcessor['user_name']) {
+      $error[] = ts('User Name is not set in the Administer &raquo; System Settings &raquo; Payment Processors.');
+    }
 
     if (!empty($error)) {
       return implode('<p>', $error);
@@ -425,7 +472,7 @@ class CRM_Core_Payment_PayPalImpl extends CRM_Core_Payment {
   /**
    * @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']);
     }
@@ -435,14 +482,14 @@ class CRM_Core_Payment_PayPalImpl extends CRM_Core_Payment {
   }
 
   /**
-   * 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.
@@ -451,21 +498,33 @@ class CRM_Core_Payment_PayPalImpl extends CRM_Core_Payment {
     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')) {
@@ -477,14 +536,27 @@ class CRM_Core_Payment_PayPalImpl extends CRM_Core_Payment {
     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();
@@ -498,13 +570,13 @@ class CRM_Core_Payment_PayPalImpl extends CRM_Core_Payment {
       $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')) {
@@ -522,7 +594,7 @@ class CRM_Core_Payment_PayPalImpl extends CRM_Core_Payment {
    *
    * @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();
@@ -544,13 +616,31 @@ class CRM_Core_Payment_PayPalImpl extends CRM_Core_Payment {
     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') {
@@ -578,8 +668,8 @@ class CRM_Core_Payment_PayPalImpl extends CRM_Core_Payment {
       }
     }
 
-    $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
@@ -670,9 +760,9 @@ class CRM_Core_Payment_PayPalImpl extends CRM_Core_Payment {
 
       $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),
@@ -706,21 +796,21 @@ class CRM_Core_Payment_PayPalImpl extends CRM_Core_Payment {
       $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'])) {
@@ -789,16 +879,17 @@ class CRM_Core_Payment_PayPalImpl extends CRM_Core_Payment {
     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
@@ -816,5 +907,5 @@ class CRM_Core_Payment_PayPalImpl extends CRM_Core_Payment {
 
     return $result;
   }
-}
 
+}