* @throws \Civi\Payment\Exception\PaymentProcessorException
*/
public function doPayment(&$params, $component = 'contribute') {
+ $this->_component = $component;
if ($this->isPayPalType($this::PAYPAL_EXPRESS) || ($this->isPayPalType($this::PAYPAL_PRO) && !empty($params['token']))) {
- $this->_component = $component;
return $this->doExpressCheckout($params);
}
- return parent::doPayment($params, $component);
+
+ $statuses = CRM_Contribute_BAO_Contribution::buildOptions('contribution_status_id', 'validate');
+
+ // If we have a $0 amount, skip call to processor and set payment_status to Completed.
+ // Conceivably a processor might override this - perhaps for setting up a token - but we don't
+ // have an example of that at the mome.
+ if ($params['amount'] == 0) {
+ $result['payment_status_id'] = array_search('Completed', $statuses);
+ $result['payment_status'] = 'Completed';
+ return $result;
+ }
+
+ if ($this->_paymentProcessor['billing_mode'] == 4) {
+ $this->doPaymentRedirectToPayPal($params, $component);
+ // redirect calls CiviExit() so execution is stopped
+ }
+ else {
+ $result = $this->doPaymentPayPalButton($params, $component);
+ if (is_array($result) && !isset($result['payment_status_id'])) {
+ if (!empty($params['is_recur'])) {
+ // See comment block.
+ $result['payment_status_id'] = array_search('Pending', $statuses);
+ $result['payment_status'] = 'Pending';
+ }
+ else {
+ $result['payment_status_id'] = array_search('Completed', $statuses);
+ $result['payment_status'] = 'Completed';
+ }
+ }
+ }
+ if (is_a($result, 'CRM_Core_Error')) {
+ CRM_Core_Error::deprecatedFunctionWarning('payment processors should throw exceptions rather than return errors');
+ throw new PaymentProcessorException(CRM_Core_Error::getMessages($result));
+ }
+ return $result;
+ }
+
+ /**
+ * Temporary function to catch transition to doPaymentPayPalButton()
+ * @deprecated
+ */
+ public function doDirectPayment(&$params) {
+ CRM_Core_Error::deprecatedFunctionWarning('doPayment');
+ return $this->doPaymentPayPalButton($params);
}
/**
* the result in an nice formatted array (or an error object)
* @throws \Civi\Payment\Exception\PaymentProcessorException
*/
- public function doDirectPayment(&$params, $component = 'contribute') {
+ public function doPaymentPayPalButton(&$params, $component = 'contribute') {
$args = [];
$this->initialize($args, 'DoDirectPayment');
}
/**
- * @return null|string
- * @throws \Civi\Payment\Exception\PaymentProcessorException
+ * Get url for users to manage this recurring contribution for this processor.
+ *
+ * @param int $entityID
+ * @param null $entity
+ * @param string $action
+ *
+ * @return string|null
+ * @throws \CRM_Core_Exception
*/
- public function cancelSubscriptionURL() {
+ public function subscriptionURL($entityID = NULL, $entity = NULL, $action = 'cancel') {
if ($this->isPayPalType($this::PAYPAL_STANDARD)) {
+ if ($action !== 'cancel') {
+ return NULL;
+ }
return "{$this->_paymentProcessor['url_site']}cgi-bin/webscr?cmd=_subscr-find&alias=" . urlencode($this->_paymentProcessor['user_name']);
}
- else {
- return NULL;
- }
+ return parent::subscriptionURL($entityID, $entity, $action);
}
/**
}
$this->_component = $params['component'];
$token = $this->setExpressCheckOut($params);
+ $siteUrl = rtrim($this->_paymentProcessor['url_site'], '/');
return [
'pre_approval_parameters' => ['token' => $token],
- 'redirect_url' => $this->_paymentProcessor['url_site'] . "/cgi-bin/webscr?cmd=_express-checkout&token=$token",
+ 'redirect_url' => $siteUrl . "/cgi-bin/webscr?cmd=_express-checkout&token=$token",
];
}
+ /**
+ * Temporary function to catch transition to doPaymentRedirectToPayPal()
+ * @deprecated
+ */
+ public function doTransferCheckout(&$params, $component = 'contribute') {
+ CRM_Core_Error::deprecatedFunctionWarning('doPayment');
+ $this->doPaymentRedirectToPayPal($params);
+ }
+
/**
* @param array $params
* @param string $component
*
* @throws Exception
*/
- public function doTransferCheckout(&$params, $component = 'contribute') {
-
+ public function doPaymentRedirectToPayPal(&$params, $component = 'contribute') {
$notifyParameters = ['module' => $component];
$notifyParameterMap = [
'contactID' => 'contactID',
// Allow further manipulation of the arguments via custom hooks ..
CRM_Utils_Hook::alterPaymentProcessorParams($this, $params, $paypalParams);
+ /*
+ * PayPal urlencodes the IPN Notify URL. For sites not using Clean URLs (or
+ * using Shortcodes in WordPress) this results in "%2F" becoming "%252F" and
+ * therefore incomplete transactions. We need to prevent that.
+ * @see https://lab.civicrm.org/dev/core/-/issues/1931
+ */
+ $paypalParams['notify_url'] = rawurldecode($paypalParams['notify_url']);
+
$uri = '';
foreach ($paypalParams as $key => $value) {
if ($value === NULL) {