class CRM_Core_Payment_PayPalImpl extends CRM_Core_Payment {
const CHARSET = 'iso-8859-1';
+ const PAYPAL_PRO = 'PayPal';
+ const PAYPAL_STANDARD = 'PayPal_Standard';
+ const PAYPAL_EXPRESS = 'PayPal_Express';
+
protected $_mode = NULL;
/**
* @param CRM_Core_Payment $paymentProcessor
*
* @return \CRM_Core_Payment_PayPalImpl
+ * @throws \Civi\Payment\Exception\PaymentProcessorException
*/
public function __construct($mode, &$paymentProcessor) {
$this->_mode = $mode;
$this->_paymentProcessor = $paymentProcessor;
- $this->_processorName = ts('PayPal Pro');
- $paymentProcessorType = CRM_Core_PseudoConstant::paymentProcessorType(FALSE, NULL, 'name');
- if ($this->_paymentProcessor['payment_processor_type_id'] == CRM_Utils_Array::key('PayPal_Standard', $paymentProcessorType)) {
+ if ($this->isPayPalType($this::PAYPAL_STANDARD)) {
$this->_processorName = ts('PayPal Standard');
- return;
}
- elseif ($this->_paymentProcessor['payment_processor_type_id'] == CRM_Utils_Array::key('PayPal_Express', $paymentProcessorType)) {
+ elseif ($this->isPayPalType($this::PAYPAL_EXPRESS)) {
$this->_processorName = ts('PayPal Express');
}
+ elseif ($this->isPayPalType($this::PAYPAL_PRO)) {
+ $this->_processorName = ts('PayPal Pro');
+ }
+ else {
+ throw new PaymentProcessorException('CRM_Core_Payment_PayPalImpl: Payment processor type is not defined!');
+ }
+ }
+ /**
+ * Helper function to check which payment processor type is being used.
+ *
+ * @param $typeName
+ *
+ * @return bool
+ * @throws \Civi\Payment\Exception\PaymentProcessorException
+ */
+ public function isPayPalType($typeName) {
+ // Historically payment_processor_type may have been set to the name of the processor but newer versions of CiviCRM use the id set in payment_processor_type_id
+ if (empty($this->_paymentProcessor['payment_processor_type_id']) && empty($this->_paymentProcessor['payment_processor_type'])) {
+ // We need one of them to be set!
+ throw new PaymentProcessorException('CRM_Core_Payment_PayPalImpl: Payment processor type is not defined!');
+ }
+ if (empty($this->_paymentProcessor['payment_processor_type_id']) && !empty($this->_paymentProcessor['payment_processor_type'])) {
+ // Handle legacy case where payment_processor_type was set, but payment_processor_type_id was not.
+ $this->_paymentProcessor['payment_processor_type_id']
+ = CRM_Core_PseudoConstant::getKey('CRM_Financial_BAO_PaymentProcessor', 'payment_processor_type_id', $this->_paymentProcessor['payment_processor_type']);
+ }
+ if ((int) $this->_paymentProcessor['payment_processor_type_id'] ===
+ CRM_Core_PseudoConstant::getKey('CRM_Financial_BAO_PaymentProcessor', 'payment_processor_type_id', $typeName)) {
+ return TRUE;
+ }
+ return FALSE;
}
/**
* with someone else's login.
*
* @return bool
+ * @throws \Civi\Payment\Exception\PaymentProcessorException
*/
protected function supportsBackOffice() {
- if ($this->_processorName == ts('PayPal Pro')) {
+ if ($this->isPayPalType($this::PAYPAL_PRO)) {
return TRUE;
}
return FALSE;
* '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
+ * @throws \Civi\Payment\Exception\PaymentProcessorException
*/
protected function supportsPreApproval() {
- if ($this->_processorName == ts('PayPal Express') || $this->_processorName == ts('PayPal Pro')) {
+ if ($this->isPayPalType($this::PAYPAL_EXPRESS) || $this->isPayPalType($this::PAYPAL_PRO)) {
return TRUE;
}
return FALSE;
*
* @return bool
* Should form building stop at this point?
+ * @throws \Civi\Payment\Exception\PaymentProcessorException
*/
public function buildForm(&$form) {
if ($this->supportsPreApproval()) {
$this->addPaypalExpressCode($form);
- if ($this->_processorName == ts('PayPal Express')) {
+ if ($this->isPayPalType($this::PAYPAL_EXPRESS)) {
CRM_Core_Region::instance('billing-block-post')->add(array(
'template' => 'CRM/Financial/Form/PaypalExpress.tpl',
'name' => 'paypal_express',
));
}
- if ($this->_processorName == ts('PayPal Pro')) {
+ if ($this->isPayPalType($this::PAYPAL_PRO)) {
CRM_Core_Region::instance('billing-block-pre')->add(array(
'template' => 'CRM/Financial/Form/PaypalPro.tpl',
));
*
* @param array $values
* @param array $errors
+ *
+ * @throws \Civi\Payment\Exception\PaymentProcessorException
*/
public function validatePaymentInstrument($values, &$errors) {
- if ($this->_paymentProcessor['payment_processor_type'] == 'PayPal' && !$this->isPaypalExpress($values)) {
+ if ($this->isPayPalType($this::PAYPAL_PRO) && !$this->isPaypalExpress($values)) {
CRM_Core_Payment_Form::validateCreditCard($values, $errors, $this->_paymentProcessor['id']);
CRM_Core_Form::validateMandatoryFields($this->getMandatoryFields(), $values, $errors);
}
* @param array $storedDetails
*
* @return array
+ * @throws \Civi\Payment\Exception\PaymentProcessorException
*/
public function getPreApprovalDetails($storedDetails) {
return empty($storedDetails['token']) ? array() : $this->getExpressCheckoutDetails($storedDetails['token']);
*
* @return array
* the result in an nice formatted array (or an error object)
+ * @throws \Civi\Payment\Exception\PaymentProcessorException
*/
public function getExpressCheckoutDetails($token) {
$args = array();
/**
* Create recurring payments.
*
+ * Use a pre-authorisation token to activate a recurring payment profile
+ * https://developer.paypal.com/docs/classic/api/merchant/CreateRecurringPaymentsProfile_API_Operation_NVP/
+ *
* @param array $params
*
* @return mixed
+ * @throws \Exception
*/
public function createRecurringPayments(&$params) {
$args = array();
- // @todo this function is riddled with enotices - perhaps use $this->mapPaypalParamsToCivicrmParams($fieldMap, $result)
$this->initialize($args, 'CreateRecurringPaymentsProfile');
$start_time = strtotime(date('m/d/Y'));
$args['currencyCode'] = $params['currencyID'];
$args['payerID'] = $params['payer_id'];
$args['invnum'] = $params['invoiceID'];
- $args['returnURL'] = $params['returnURL'];
- $args['cancelURL'] = $params['cancelURL'];
$args['profilestartdate'] = $start_date;
$args['method'] = 'CreateRecurringPaymentsProfile';
$args['billingfrequency'] = $params['frequency_interval'];
$args['billingperiod'] = ucwords($params['frequency_unit']);
$args['desc'] = $params['amount'] . " Per " . $params['frequency_interval'] . " " . $params['frequency_unit'];
- //$args['desc'] = 'Recurring Contribution';
- $args['totalbillingcycles'] = $params['installments'];
+ $args['totalbillingcycles'] = CRM_Utils_Array::value('installments', $params);
$args['version'] = '56.0';
$args['profilereference'] = "i={$params['invoiceID']}" .
"&m=" .
return $result;
}
- /* Success */
- $params['trxn_id'] = $result['transactionid'];
- $params['gross_amount'] = $result['amt'];
- $params['fee_amount'] = $result['feeamt'];
- $params['net_amount'] = $result['settleamt'];
- if ($params['net_amount'] == 0 && $params['fee_amount'] != 0) {
- $params['net_amount'] = number_format(($params['gross_amount'] - $params['fee_amount']), 2);
- }
- $params['payment_status'] = $result['paymentstatus'];
- $params['pending_reason'] = $result['pendingreason'];
+ /* Success - result looks like"
+ * array (
+ * 'profileid' => 'I-CP1U0PLG91R2',
+ * 'profilestatus' => 'ActiveProfile',
+ * 'timestamp' => '2018-05-07T03:55:52Z',
+ * 'correlationid' => 'e717999e9bf62',
+ * 'ack' => 'Success',
+ * 'version' => '56.0',
+ * 'build' => '39949200',)
+ */
+ $params['trxn_id'] = $result['profileid'];
+ $params['payment_status_id'] = CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'contribution_status_id', 'Pending');
return $params;
}
}
/**
- * Process payment - this function wraps around both doTransferPayment and doDirectPayment.
+ * Process payment - this function wraps around both doTransferCheckout and doDirectPayment.
*
* The function ensures an exception is thrown & moves some of this logic out of the form layer and makes the forms
* more agnostic.
* @throws \Civi\Payment\Exception\PaymentProcessorException
*/
public function doPayment(&$params, $component = 'contribute') {
- if ($this->_paymentProcessor['payment_processor_type'] == 'PayPal_Express'
- || ($this->_paymentProcessor['payment_processor_type'] == 'PayPal' && !empty($params['token']))
- ) {
+ if ($this->isPayPalType($this::PAYPAL_EXPRESS) || ($this->isPayPalType($this::PAYPAL_PRO) && !empty($params['token']))) {
$this->_component = $component;
return $this->doExpressCheckout($params);
* @param string $component
* @return array
* the result in an nice formatted array (or an error object)
+ * @throws \Civi\Payment\Exception\PaymentProcessorException
*/
public function doDirectPayment(&$params, $component = 'contribute') {
$args = array();
$result = $this->invokeAPI($args);
- //WAG
+ // WAG
if (is_a($result, 'CRM_Core_Error')) {
return $result;
}
/**
* This function checks to see if we have the right config values.
*
- * @return string
+ * @return null|string
* the error message if any
+ * @throws \Civi\Payment\Exception\PaymentProcessorException
*/
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)) {
+ if (!$this->isPayPalType($this::PAYPAL_STANDARD)) {
if (empty($this->_paymentProcessor['signature'])) {
$error[] = ts('Signature is not set in the Administer » System Settings » Payment Processors.');
}
/**
* @return null|string
+ * @throws \Civi\Payment\Exception\PaymentProcessorException
*/
public function cancelSubscriptionURL() {
- if ($this->_paymentProcessor['payment_processor_type'] == 'PayPal_Standard') {
+ if ($this->isPayPalType($this::PAYPAL_STANDARD)) {
return "{$this->_paymentProcessor['url_site']}cgi-bin/webscr?cmd=_subscr-find&alias=" . urlencode($this->_paymentProcessor['user_name']);
}
else {
* Method to check for.
*
* @return bool
+ * @throws \Civi\Payment\Exception\PaymentProcessorException
*/
public function isSupported($method) {
- if ($this->_paymentProcessor['payment_processor_type'] != 'PayPal') {
+ if (!$this->isPayPalType($this::PAYPAL_PRO)) {
// since subscription methods like cancelSubscription or updateBilling is not yet implemented / supported
// by standard or express.
return FALSE;
*
* @return bool
* Should the form button by suppressed?
+ * @throws \Civi\Payment\Exception\PaymentProcessorException
*/
public function isSuppressSubmitButtons() {
- if ($this->_paymentProcessor['payment_processor_type'] == 'PayPal_Express') {
+ if ($this->isPayPalType($this::PAYPAL_EXPRESS)) {
return TRUE;
}
return FALSE;
* @param array $params
*
* @return array|bool|object
+ * @throws \Civi\Payment\Exception\PaymentProcessorException
*/
public function cancelSubscription(&$message = '', $params = array()) {
- if ($this->_paymentProcessor['payment_processor_type'] == 'PayPal') {
+ if ($this->isPayPalType($this::PAYPAL_PRO) || $this->isPayPalType($this::PAYPAL_EXPRESS)) {
$args = array();
$this->initialize($args, 'ManageRecurringPaymentsProfileStatus');
/**
* Process incoming notification.
+ *
+ * @throws \CRM_Core_Exception
+ * @throws \CiviCRM_API3_Exception
*/
static public function handlePaymentNotification() {
$params = array_merge($_GET, $_REQUEST);
* @param array $params
*
* @return array|bool|object
+ * @throws \Civi\Payment\Exception\PaymentProcessorException
*/
public function updateSubscriptionBillingInfo(&$message = '', $params = array()) {
- if ($this->_paymentProcessor['payment_processor_type'] == 'PayPal') {
+ if ($this->isPayPalType($this::PAYPAL_PRO)) {
$config = CRM_Core_Config::singleton();
$args = array();
$this->initialize($args, 'UpdateRecurringPaymentsProfile');
* @param array $params
*
* @return array|bool|object
+ * @throws \Civi\Payment\Exception\PaymentProcessorException
*/
public function changeSubscriptionAmount(&$message = '', $params = array()) {
- if ($this->_paymentProcessor['payment_processor_type'] == 'PayPal') {
+ if ($this->isPayPalType($this::PAYPAL_PRO)) {
$config = CRM_Core_Config::singleton();
$args = array();
$this->initialize($args, 'UpdateRecurringPaymentsProfile');
* @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.
+ * @throws \Civi\Payment\Exception\PaymentProcessorException
*/
public function doPreApproval(&$params) {
if (!$this->isPaypalExpress($params)) {
* Get array of fields that should be displayed on the payment form.
*
* @return array
- * @throws CiviCRM_API3_Exception
+ * @throws \Civi\Payment\Exception\PaymentProcessorException
*/
public function getPaymentFormFields() {
- if ($this->_processorName == ts('PayPal Pro')) {
+ if ($this->isPayPalType($this::PAYPAL_PRO)) {
return $this->getCreditCardFormFields();
}
else {
* @param array $params
*
* @return bool
+ * @throws \Civi\Payment\Exception\PaymentProcessorException
*/
protected function isPaypalExpress($params) {
- if ($this->_processorName == ts('PayPal Express')) {
+ if ($this->isPayPalType($this::PAYPAL_EXPRESS)) {
return TRUE;
}