Add in fixes for CRM-18708 as well as its needed on 4.7 and make minor fix as suggest...
[civicrm-core.git] / CRM / Core / Payment.php
CommitLineData
6a488035
TO
1<?php
2/*
3 +--------------------------------------------------------------------+
7e9e8871 4 | CiviCRM version 4.7 |
6a488035 5 +--------------------------------------------------------------------+
fa938177 6 | Copyright CiviCRM LLC (c) 2004-2016 |
6a488035
TO
7 +--------------------------------------------------------------------+
8 | This file is a part of CiviCRM. |
9 | |
10 | CiviCRM is free software; you can copy, modify, and distribute it |
11 | under the terms of the GNU Affero General Public License |
12 | Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
13 | |
14 | CiviCRM is distributed in the hope that it will be useful, but |
15 | WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
17 | See the GNU Affero General Public License for more details. |
18 | |
19 | You should have received a copy of the GNU Affero General Public |
20 | License and the CiviCRM Licensing Exception along |
21 | with this program; if not, contact CiviCRM LLC |
22 | at info[AT]civicrm[DOT]org. If you have questions about the |
23 | GNU Affero General Public License or the licensing of CiviCRM, |
24 | see the CiviCRM license FAQ at http://civicrm.org/licensing |
25 +--------------------------------------------------------------------+
d25dd0ee 26 */
6a488035 27
914a49bf 28use Civi\Payment\System;
7758bd2b 29use Civi\Payment\Exception\PaymentProcessorException;
353ffa53 30
6a488035 31/**
3782df3e 32 * Class CRM_Core_Payment.
6a488035 33 *
3782df3e 34 * This class is the main class for the payment processor subsystem.
6a488035 35 *
3782df3e
EM
36 * It is the parent class for payment processors. It also holds some IPN related functions
37 * that need to be moved. In particular handlePaymentMethod should be moved to a factory class.
6a488035 38 */
6a488035
TO
39abstract class CRM_Core_Payment {
40
41 /**
05c302ec
EM
42 * Component - ie. event or contribute.
43 *
44 * This is used for setting return urls.
45 *
46 * @var string
47 */
fd359255
EM
48 protected $_component;
49
05c302ec
EM
50 /**
51 * How are we getting billing information.
52 *
53 * We are trying to completely deprecate these parameters.
6a488035
TO
54 *
55 * FORM - we collect it on the same page
56 * BUTTON - the processor collects it and sends it back to us via some protocol
57 */
7da04cde 58 const
6a488035
TO
59 BILLING_MODE_FORM = 1,
60 BILLING_MODE_BUTTON = 2,
61 BILLING_MODE_NOTIFY = 4;
62
63 /**
100fef9d 64 * Which payment type(s) are we using?
6a488035
TO
65 *
66 * credit card
67 * direct debit
68 * or both
43e5f0f6 69 * @todo create option group - nb omnipay uses a 3rd type - transparent redirect cc
6a488035 70 */
7da04cde 71 const
6a488035
TO
72 PAYMENT_TYPE_CREDIT_CARD = 1,
73 PAYMENT_TYPE_DIRECT_DEBIT = 2;
74
75 /**
76 * Subscription / Recurring payment Status
77 * START, END
6a488035 78 */
7da04cde 79 const
6a488035
TO
80 RECURRING_PAYMENT_START = 'START',
81 RECURRING_PAYMENT_END = 'END';
82
353ffa53 83 protected $_paymentProcessor;
6a488035 84
ec022878 85 /**
86 * Base url of the calling form.
87 *
88 * This is used for processors that need to return the browser back to the CiviCRM site.
89 *
90 * @var string
91 */
92 protected $baseReturnUrl;
93
1d1fee72 94 /**
95 * The profile configured to show on the billing form.
96 *
97 * Currently only the pseudo-profile 'billing' is supported but hopefully in time we will take an id and
98 * load that from the DB and the processor will be able to return a set of fields that combines it's minimum
99 * requirements with the configured requirements.
100 *
101 * Currently only the pseudo-processor 'manual' or 'pay-later' uses this setting to return a 'curated' set
102 * of fields.
103 *
104 * Note this change would probably include converting 'billing' to a reserved profile.
105 *
106 * @var int|string
107 */
108 protected $billingProfile;
109
ec022878 110 /**
111 * Set Base return URL.
112 *
113 * @param string $url
114 * Url of site to return browser to.
115 */
116 public function setBaseReturnUrl($url) {
117 $this->baseReturnUrl = $url;
118 }
119
1d1fee72 120 /**
121 * Set the configured payment profile.
122 *
123 * @param int|string $value
124 */
125 public function setBillingProfile($value) {
126 $this->billingProfile = $value;
127 }
128
dbbd55dc
EM
129 /**
130 * Opportunity for the payment processor to override the entire form build.
131 *
132 * @param CRM_Core_Form $form
133 *
134 * @return bool
135 * Should form building stop at this point?
136 */
137 public function buildForm(&$form) {
31d31a05 138 return FALSE;
dbbd55dc
EM
139 }
140
e2bef985 141 /**
3782df3e
EM
142 * Log payment notification message to forensic system log.
143 *
43e5f0f6 144 * @todo move to factory class \Civi\Payment\System (or similar)
3782df3e
EM
145 *
146 * @param array $params
147 *
e2bef985 148 * @return mixed
149 */
150 public static function logPaymentNotification($params) {
414e3596 151 $message = 'payment_notification ';
e2bef985 152 if (!empty($params['processor_name'])) {
414e3596 153 $message .= 'processor_name=' . $params['processor_name'];
e2bef985 154 }
155 if (!empty($params['processor_id'])) {
156 $message .= 'processor_id=' . $params['processor_id'];
157 }
414e3596 158
159 $log = new CRM_Utils_SystemLogger();
160 $log->alert($message, $_REQUEST);
e2bef985 161 }
162
fbcb6fba 163 /**
d09edf64 164 * Check if capability is supported.
3782df3e
EM
165 *
166 * Capabilities have a one to one relationship with capability-related functions on this class.
167 *
168 * Payment processor classes should over-ride the capability-specific function rather than this one.
169 *
6a0b768e
TO
170 * @param string $capability
171 * E.g BackOffice, LiveMode, FutureRecurStartDate.
fbcb6fba
EM
172 *
173 * @return bool
174 */
175 public function supports($capability) {
176 $function = 'supports' . ucfirst($capability);
177 if (method_exists($this, $function)) {
178 return $this->$function();
179 }
180 return FALSE;
181 }
182
183 /**
3782df3e
EM
184 * Are back office payments supported.
185 *
186 * e.g paypal standard won't permit you to enter a credit card associated
187 * with someone else's login.
188 * The intention is to support off-site (other than paypal) & direct debit but that is not all working yet so to
189 * reach a 'stable' point we disable.
190 *
fbcb6fba
EM
191 * @return bool
192 */
d8ce0d68 193 protected function supportsBackOffice() {
9c39fb25
EM
194 if ($this->_paymentProcessor['billing_mode'] == 4 || $this->_paymentProcessor['payment_type'] != 1) {
195 return FALSE;
196 }
197 else {
198 return TRUE;
199 }
fbcb6fba
EM
200 }
201
75ead8de
EM
202 /**
203 * Can more than one transaction be processed at once?
204 *
205 * In general processors that process payment by server to server communication support this while others do not.
206 *
207 * In future we are likely to hit an issue where this depends on whether a token already exists.
208 *
209 * @return bool
210 */
211 protected function supportsMultipleConcurrentPayments() {
212 if ($this->_paymentProcessor['billing_mode'] == 4 || $this->_paymentProcessor['payment_type'] != 1) {
213 return FALSE;
214 }
215 else {
216 return TRUE;
217 }
218 }
219
fbcb6fba 220 /**
3782df3e
EM
221 * Are live payments supported - e.g dummy doesn't support this.
222 *
fbcb6fba
EM
223 * @return bool
224 */
d8ce0d68 225 protected function supportsLiveMode() {
fbcb6fba
EM
226 return TRUE;
227 }
228
52767de0 229 /**
d09edf64 230 * Are test payments supported.
3782df3e 231 *
52767de0
EM
232 * @return bool
233 */
234 protected function supportsTestMode() {
235 return TRUE;
236 }
237
fbcb6fba 238 /**
d09edf64 239 * Should the first payment date be configurable when setting up back office recurring payments.
3782df3e 240 *
fbcb6fba 241 * We set this to false for historical consistency but in fact most new processors use tokens for recurring and can support this
3782df3e 242 *
fbcb6fba
EM
243 * @return bool
244 */
d8ce0d68 245 protected function supportsFutureRecurStartDate() {
fbcb6fba
EM
246 return FALSE;
247 }
248
1e483eb5
EM
249 /**
250 * Does this processor support cancelling recurring contributions through code.
251 *
3e473c0b 252 * If the processor returns true it must be possible to take action from within CiviCRM
253 * that will result in no further payments being processed. In the case of token processors (e.g
254 * IATS, eWay) updating the contribution_recur table is probably sufficient.
255 *
1e483eb5
EM
256 * @return bool
257 */
258 protected function supportsCancelRecurring() {
259 return method_exists(CRM_Utils_System::getClassName($this), 'cancelSubscription');
260 }
261
3910048c
EM
262 /**
263 * Does this processor support pre-approval.
264 *
265 * This would generally look like a redirect to enter credentials which can then be used in a later payment call.
266 *
267 * Currently Paypal express supports this, with a redirect to paypal after the 'Main' form is submitted in the
268 * contribution page. This token can then be processed at the confirm phase. Although this flow 'looks' like the
269 * 'notify' flow a key difference is that in the notify flow they don't have to return but in this flow they do.
270 *
271 * @return bool
272 */
273 protected function supportsPreApproval() {
274 return FALSE;
275 }
276
677fe56c
EM
277 /**
278 * Can recurring contributions be set against pledges.
279 *
280 * In practice all processors that use the baseIPN function to finish transactions or
281 * call the completetransaction api support this by looking up previous contributions in the
282 * series and, if there is a prior contribution against a pledge, and the pledge is not complete,
283 * adding the new payment to the pledge.
284 *
285 * However, only enabling for processors it has been tested against.
286 *
287 * @return bool
288 */
289 protected function supportsRecurContributionsForPledges() {
290 return FALSE;
291 }
292
3910048c
EM
293 /**
294 * Function to action pre-approval if supported
295 *
296 * @param array $params
297 * Parameters from the form
3910048c
EM
298 *
299 * This function returns an array which should contain
300 * - pre_approval_parameters (this will be stored on the calling form & available later)
301 * - redirect_url (if set the browser will be redirected to this.
302 */
677730bd 303 public function doPreApproval(&$params) {}
3910048c 304
3105efd2
EM
305 /**
306 * Get any details that may be available to the payment processor due to an approval process having happened.
307 *
308 * In some cases the browser is redirected to enter details on a processor site. Some details may be available as a
309 * result.
310 *
311 * @param array $storedDetails
312 *
313 * @return array
314 */
315 public function getPreApprovalDetails($storedDetails) {
316 return array();
317 }
318
6a488035 319 /**
3782df3e
EM
320 * Default payment instrument validation.
321 *
a479fe60 322 * Implement the usual Luhn algorithm via a static function in the CRM_Core_Payment_Form if it's a credit card
3782df3e
EM
323 * Not a static function, because I need to check for payment_type.
324 *
325 * @param array $values
326 * @param array $errors
a479fe60 327 */
328 public function validatePaymentInstrument($values, &$errors) {
c319039f 329 CRM_Core_Form::validateMandatoryFields($this->getMandatoryFields(), $values, $errors);
a479fe60 330 if ($this->_paymentProcessor['payment_type'] == 1) {
331 CRM_Core_Payment_Form::validateCreditCard($values, $errors);
332 }
333 }
334
80bcd255
EM
335 /**
336 * Getter for the payment processor.
337 *
338 * The payment processor array is based on the civicrm_payment_processor table entry.
339 *
340 * @return array
341 * Payment processor array.
342 */
343 public function getPaymentProcessor() {
344 return $this->_paymentProcessor;
345 }
346
347 /**
348 * Setter for the payment processor.
349 *
350 * @param array $processor
351 */
352 public function setPaymentProcessor($processor) {
353 $this->_paymentProcessor = $processor;
354 }
355
6a488035 356 /**
3782df3e
EM
357 * Setter for the payment form that wants to use the processor.
358 *
43e5f0f6 359 * @deprecated
3782df3e 360 *
ac32ed13 361 * @param CRM_Core_Form $paymentForm
6a488035 362 */
00be9182 363 public function setForm(&$paymentForm) {
6a488035
TO
364 $this->_paymentForm = $paymentForm;
365 }
366
367 /**
d09edf64 368 * Getter for payment form that is using the processor.
43e5f0f6 369 * @deprecated
16b10e64
CW
370 * @return CRM_Core_Form
371 * A form object
6a488035 372 */
00be9182 373 public function getForm() {
6a488035
TO
374 return $this->_paymentForm;
375 }
376
377 /**
d09edf64 378 * Getter for accessing member vars.
6c99ada1 379 *
43e5f0f6 380 * @todo believe this is unused
6c99ada1 381 *
100fef9d 382 * @param string $name
dc913073
EM
383 *
384 * @return null
6a488035 385 */
00be9182 386 public function getVar($name) {
6a488035
TO
387 return isset($this->$name) ? $this->$name : NULL;
388 }
389
dc913073 390 /**
d09edf64 391 * Get name for the payment information type.
43e5f0f6 392 * @todo - use option group + name field (like Omnipay does)
dc913073
EM
393 * @return string
394 */
395 public function getPaymentTypeName() {
459091e1 396 return $this->_paymentProcessor['payment_type'] == 1 ? 'credit_card' : 'direct_debit';
dc913073
EM
397 }
398
399 /**
d09edf64 400 * Get label for the payment information type.
43e5f0f6 401 * @todo - use option group + labels (like Omnipay does)
dc913073
EM
402 * @return string
403 */
404 public function getPaymentTypeLabel() {
459091e1 405 return $this->_paymentProcessor['payment_type'] == 1 ? 'Credit Card' : 'Direct Debit';
dc913073
EM
406 }
407
44b6505d 408 /**
d09edf64 409 * Get array of fields that should be displayed on the payment form.
44b6505d
EM
410 * @todo make payment type an option value & use it in the function name - currently on debit & credit card work
411 * @return array
412 * @throws CiviCRM_API3_Exception
413 */
414 public function getPaymentFormFields() {
dc913073 415 if ($this->_paymentProcessor['billing_mode'] == 4) {
44b6505d
EM
416 return array();
417 }
418 return $this->_paymentProcessor['payment_type'] == 1 ? $this->getCreditCardFormFields() : $this->getDirectDebitFormFields();
419 }
420
0c6c47a5 421 /**
422 * Get an array of the fields that can be edited on the recurring contribution.
423 *
424 * Some payment processors support editing the amount and other scheduling details of recurring payments, especially
425 * those which use tokens. Others are fixed. This function allows the processor to return an array of the fields that
426 * can be updated from the contribution recur edit screen.
427 *
428 * The fields are likely to be a subset of these
429 * - 'amount',
430 * - 'installments',
431 * - 'frequency_interval',
432 * - 'frequency_unit',
433 * - 'cycle_day',
434 * - 'next_sched_contribution_date',
435 * - 'end_date',
436 * - 'failure_retry_day',
437 *
438 * The form does not restrict which fields from the contribution_recur table can be added (although if the html_type
439 * metadata is not defined in the xml for the field it will cause an error.
440 *
441 * Open question - would it make sense to return membership_id in this - which is sometimes editable and is on that
442 * form (UpdateSubscription).
443 *
444 * @return array
445 */
446 public function getEditableRecurringScheduleFields() {
447 if (method_exists($this, 'changeSubscriptionAmount')) {
448 return array('amount');
449 }
450 }
451
452 /**
453 * Get the help text to present on the recurring update page.
454 *
455 * This should reflect what can or cannot be edited.
456 *
457 * @return string
458 */
459 public function getRecurringScheduleUpdateHelpText() {
460 if (!in_array('amount', $this->getEditableRecurringScheduleFields())) {
461 return ts('Updates made using this form will change the recurring contribution information stored in your CiviCRM database, but will NOT be sent to the payment processor. You must enter the same changes using the payment processor web site.');
462 }
463 return ts('Use this form to change the amount or number of installments for this recurring contribution. Changes will be automatically sent to the payment processor. You can not change the contribution frequency.');
464 }
465
c319039f 466 /**
467 * Get the metadata for all required fields.
468 *
469 * @return array;
470 */
471 protected function getMandatoryFields() {
472 $mandatoryFields = array();
473 foreach ($this->getAllFields() as $field_name => $field_spec) {
474 if (!empty($field_spec['is_required'])) {
475 $mandatoryFields[$field_name] = $field_spec;
476 }
477 }
478 return $mandatoryFields;
479 }
480
481 /**
482 * Get the metadata of all the fields configured for this processor.
483 *
484 * @return array
485 */
486 protected function getAllFields() {
487 $paymentFields = array_intersect_key($this->getPaymentFormFieldsMetadata(), array_flip($this->getPaymentFormFields()));
488 $billingFields = array_intersect_key($this->getBillingAddressFieldsMetadata(), array_flip($this->getBillingAddressFields()));
489 return array_merge($paymentFields, $billingFields);
490 }
44b6505d 491 /**
d09edf64 492 * Get array of fields that should be displayed on the payment form for credit cards.
dc913073 493 *
44b6505d
EM
494 * @return array
495 */
496 protected function getCreditCardFormFields() {
497 return array(
498 'credit_card_type',
499 'credit_card_number',
500 'cvv2',
501 'credit_card_exp_date',
502 );
503 }
504
505 /**
d09edf64 506 * Get array of fields that should be displayed on the payment form for direct debits.
dc913073 507 *
44b6505d
EM
508 * @return array
509 */
510 protected function getDirectDebitFormFields() {
511 return array(
512 'account_holder',
513 'bank_account_number',
514 'bank_identification_number',
515 'bank_name',
516 );
517 }
518
dc913073 519 /**
d09edf64 520 * Return an array of all the details about the fields potentially required for payment fields.
3782df3e 521 *
dc913073
EM
522 * Only those determined by getPaymentFormFields will actually be assigned to the form
523 *
a6c01b45
CW
524 * @return array
525 * field metadata
dc913073
EM
526 */
527 public function getPaymentFormFieldsMetadata() {
528 //@todo convert credit card type into an option value
529 $creditCardType = array('' => ts('- select -')) + CRM_Contribute_PseudoConstant::creditCard();
530 return array(
531 'credit_card_number' => array(
532 'htmlType' => 'text',
533 'name' => 'credit_card_number',
534 'title' => ts('Card Number'),
535 'cc_field' => TRUE,
536 'attributes' => array(
537 'size' => 20,
538 'maxlength' => 20,
21dfd5f5 539 'autocomplete' => 'off',
f803aacb 540 'class' => 'creditcard',
dc913073
EM
541 ),
542 'is_required' => TRUE,
543 ),
544 'cvv2' => array(
545 'htmlType' => 'text',
546 'name' => 'cvv2',
547 'title' => ts('Security Code'),
548 'cc_field' => TRUE,
549 'attributes' => array(
550 'size' => 5,
551 'maxlength' => 10,
21dfd5f5 552 'autocomplete' => 'off',
dc913073 553 ),
d356cdeb 554 'is_required' => Civi::settings()->get('cvv_backoffice_required'),
dc913073
EM
555 'rules' => array(
556 array(
557 'rule_message' => ts('Please enter a valid value for your card security code. This is usually the last 3-4 digits on the card\'s signature panel.'),
558 'rule_name' => 'integer',
559 'rule_parameters' => NULL,
7c550ca0 560 ),
353ffa53 561 ),
dc913073
EM
562 ),
563 'credit_card_exp_date' => array(
564 'htmlType' => 'date',
565 'name' => 'credit_card_exp_date',
566 'title' => ts('Expiration Date'),
567 'cc_field' => TRUE,
568 'attributes' => CRM_Core_SelectValues::date('creditCard'),
569 'is_required' => TRUE,
570 'rules' => array(
571 array(
572 'rule_message' => ts('Card expiration date cannot be a past date.'),
573 'rule_name' => 'currentDate',
574 'rule_parameters' => TRUE,
7c550ca0 575 ),
353ffa53 576 ),
dc913073
EM
577 ),
578 'credit_card_type' => array(
579 'htmlType' => 'select',
580 'name' => 'credit_card_type',
581 'title' => ts('Card Type'),
582 'cc_field' => TRUE,
583 'attributes' => $creditCardType,
584 'is_required' => FALSE,
585 ),
586 'account_holder' => array(
587 'htmlType' => 'text',
588 'name' => 'account_holder',
589 'title' => ts('Account Holder'),
590 'cc_field' => TRUE,
591 'attributes' => array(
592 'size' => 20,
593 'maxlength' => 34,
21dfd5f5 594 'autocomplete' => 'on',
dc913073
EM
595 ),
596 'is_required' => TRUE,
597 ),
598 //e.g. IBAN can have maxlength of 34 digits
599 'bank_account_number' => array(
600 'htmlType' => 'text',
601 'name' => 'bank_account_number',
602 'title' => ts('Bank Account Number'),
603 'cc_field' => TRUE,
604 'attributes' => array(
605 'size' => 20,
606 'maxlength' => 34,
21dfd5f5 607 'autocomplete' => 'off',
dc913073
EM
608 ),
609 'rules' => array(
610 array(
611 'rule_message' => ts('Please enter a valid Bank Identification Number (value must not contain punctuation characters).'),
612 'rule_name' => 'nopunctuation',
613 'rule_parameters' => NULL,
7c550ca0 614 ),
353ffa53 615 ),
dc913073
EM
616 'is_required' => TRUE,
617 ),
618 //e.g. SWIFT-BIC can have maxlength of 11 digits
619 'bank_identification_number' => array(
620 'htmlType' => 'text',
621 'name' => 'bank_identification_number',
622 'title' => ts('Bank Identification Number'),
623 'cc_field' => TRUE,
624 'attributes' => array(
625 'size' => 20,
626 'maxlength' => 11,
21dfd5f5 627 'autocomplete' => 'off',
dc913073
EM
628 ),
629 'is_required' => TRUE,
630 'rules' => array(
631 array(
632 'rule_message' => ts('Please enter a valid Bank Identification Number (value must not contain punctuation characters).'),
633 'rule_name' => 'nopunctuation',
634 'rule_parameters' => NULL,
7c550ca0 635 ),
353ffa53 636 ),
dc913073
EM
637 ),
638 'bank_name' => array(
639 'htmlType' => 'text',
640 'name' => 'bank_name',
641 'title' => ts('Bank Name'),
642 'cc_field' => TRUE,
643 'attributes' => array(
644 'size' => 20,
645 'maxlength' => 64,
21dfd5f5 646 'autocomplete' => 'off',
dc913073
EM
647 ),
648 'is_required' => TRUE,
649
21dfd5f5 650 ),
dc913073
EM
651 );
652 }
44b6505d 653
3310ab71 654 /**
655 * Get billing fields required for this processor.
656 *
657 * We apply the existing default of returning fields only for payment processor type 1. Processors can override to
658 * alter.
659 *
660 * @param int $billingLocationID
661 *
662 * @return array
663 */
b576d770 664 public function getBillingAddressFields($billingLocationID = NULL) {
665 if (!$billingLocationID) {
666 // Note that although the billing id is passed around the forms the idea that it would be anything other than
667 // the result of the function below doesn't seem to have eventuated.
668 // So taking this as a param is possibly something to be removed in favour of the standard default.
669 $billingLocationID = CRM_Core_BAO_LocationType::getBilling();
670 }
3310ab71 671 if ($this->_paymentProcessor['billing_mode'] != 1 && $this->_paymentProcessor['billing_mode'] != 3) {
672 return array();
673 }
674 return array(
675 'first_name' => 'billing_first_name',
676 'middle_name' => 'billing_middle_name',
677 'last_name' => 'billing_last_name',
678 'street_address' => "billing_street_address-{$billingLocationID}",
679 'city' => "billing_city-{$billingLocationID}",
680 'country' => "billing_country_id-{$billingLocationID}",
681 'state_province' => "billing_state_province_id-{$billingLocationID}",
682 'postal_code' => "billing_postal_code-{$billingLocationID}",
683 );
684 }
685
686 /**
687 * Get form metadata for billing address fields.
688 *
689 * @param int $billingLocationID
690 *
691 * @return array
692 * Array of metadata for address fields.
693 */
b576d770 694 public function getBillingAddressFieldsMetadata($billingLocationID = NULL) {
695 if (!$billingLocationID) {
696 // Note that although the billing id is passed around the forms the idea that it would be anything other than
697 // the result of the function below doesn't seem to have eventuated.
698 // So taking this as a param is possibly something to be removed in favour of the standard default.
699 $billingLocationID = CRM_Core_BAO_LocationType::getBilling();
700 }
3310ab71 701 $metadata = array();
702 $metadata['billing_first_name'] = array(
703 'htmlType' => 'text',
704 'name' => 'billing_first_name',
705 'title' => ts('Billing First Name'),
706 'cc_field' => TRUE,
707 'attributes' => array(
708 'size' => 30,
709 'maxlength' => 60,
710 'autocomplete' => 'off',
711 ),
712 'is_required' => TRUE,
713 );
714
715 $metadata['billing_middle_name'] = array(
716 'htmlType' => 'text',
717 'name' => 'billing_middle_name',
718 'title' => ts('Billing Middle Name'),
719 'cc_field' => TRUE,
720 'attributes' => array(
721 'size' => 30,
722 'maxlength' => 60,
723 'autocomplete' => 'off',
724 ),
725 'is_required' => FALSE,
726 );
727
728 $metadata['billing_last_name'] = array(
729 'htmlType' => 'text',
730 'name' => 'billing_last_name',
731 'title' => ts('Billing Last Name'),
732 'cc_field' => TRUE,
733 'attributes' => array(
734 'size' => 30,
735 'maxlength' => 60,
736 'autocomplete' => 'off',
737 ),
738 'is_required' => TRUE,
739 );
740
741 $metadata["billing_street_address-{$billingLocationID}"] = array(
742 'htmlType' => 'text',
743 'name' => "billing_street_address-{$billingLocationID}",
744 'title' => ts('Street Address'),
745 'cc_field' => TRUE,
746 'attributes' => array(
747 'size' => 30,
748 'maxlength' => 60,
749 'autocomplete' => 'off',
750 ),
751 'is_required' => TRUE,
752 );
753
754 $metadata["billing_city-{$billingLocationID}"] = array(
755 'htmlType' => 'text',
756 'name' => "billing_city-{$billingLocationID}",
757 'title' => ts('City'),
758 'cc_field' => TRUE,
759 'attributes' => array(
760 'size' => 30,
761 'maxlength' => 60,
762 'autocomplete' => 'off',
763 ),
764 'is_required' => TRUE,
765 );
766
767 $metadata["billing_state_province_id-{$billingLocationID}"] = array(
768 'htmlType' => 'chainSelect',
769 'title' => ts('State/Province'),
770 'name' => "billing_state_province_id-{$billingLocationID}",
771 'cc_field' => TRUE,
772 'is_required' => TRUE,
773 );
774
775 $metadata["billing_postal_code-{$billingLocationID}"] = array(
776 'htmlType' => 'text',
777 'name' => "billing_postal_code-{$billingLocationID}",
778 'title' => ts('Postal Code'),
779 'cc_field' => TRUE,
780 'attributes' => array(
781 'size' => 30,
782 'maxlength' => 60,
783 'autocomplete' => 'off',
784 ),
785 'is_required' => TRUE,
786 );
787
788 $metadata["billing_country_id-{$billingLocationID}"] = array(
789 'htmlType' => 'select',
790 'name' => "billing_country_id-{$billingLocationID}",
791 'title' => ts('Country'),
792 'cc_field' => TRUE,
793 'attributes' => array(
794 '' => ts('- select -'),
795 ) + CRM_Core_PseudoConstant::country(),
796 'is_required' => TRUE,
797 );
798 return $metadata;
799 }
800
aefd7f6b
EM
801 /**
802 * Get base url dependent on component.
803 *
ec022878 804 * (or preferably set it using the setter function).
805 *
806 * @return string
aefd7f6b
EM
807 */
808 protected function getBaseReturnUrl() {
ec022878 809 if ($this->baseReturnUrl) {
810 return $this->baseReturnUrl;
811 }
aefd7f6b
EM
812 if ($this->_component == 'event') {
813 $baseURL = 'civicrm/event/register';
814 }
815 else {
816 $baseURL = 'civicrm/contribute/transact';
817 }
818 return $baseURL;
819 }
820
821 /**
822 * Get url to return to after cancelled or failed transaction
823 *
824 * @param $qfKey
825 * @param $participantID
826 *
827 * @return string cancel url
828 */
abfb35ee 829 public function getCancelUrl($qfKey, $participantID) {
aefd7f6b
EM
830 if ($this->_component == 'event') {
831 return CRM_Utils_System::url($this->getBaseReturnUrl(), array(
832 'reset' => 1,
833 'cc' => 'fail',
834 'participantId' => $participantID,
835 ),
836 TRUE, NULL, FALSE
837 );
838 }
839
840 return CRM_Utils_System::url($this->getBaseReturnUrl(), array(
841 '_qf_Main_display' => 1,
842 'qfKey' => $qfKey,
843 'cancel' => 1,
844 ),
845 TRUE, NULL, FALSE
846 );
847 }
848
849 /**
49c882a3 850 * Get URL to return the browser to on success.
aefd7f6b
EM
851 *
852 * @param $qfKey
853 *
854 * @return string
855 */
856 protected function getReturnSuccessUrl($qfKey) {
857 return CRM_Utils_System::url($this->getBaseReturnUrl(), array(
858 '_qf_ThankYou_display' => 1,
05237b76 859 'qfKey' => $qfKey,
aefd7f6b
EM
860 ),
861 TRUE, NULL, FALSE
862 );
863 }
864
49c882a3
EM
865 /**
866 * Get URL to return the browser to on failure.
867 *
868 * @param string $key
869 * @param int $participantID
870 * @param int $eventID
871 *
872 * @return string
873 * URL for a failing transactor to be redirected to.
874 */
875 protected function getReturnFailUrl($key, $participantID = NULL, $eventID = NULL) {
6109d8df 876 $test = $this->_is_test ? '&action=preview' : '';
49c882a3
EM
877 if ($this->_component == "event") {
878 return CRM_Utils_System::url('civicrm/event/register',
879 "reset=1&cc=fail&participantId={$participantID}&id={$eventID}{$test}&qfKey={$key}",
880 FALSE, NULL, FALSE
881 );
882 }
883 else {
884 return CRM_Utils_System::url('civicrm/contribute/transact',
885 "_qf_Main_display=1&cancel=1&qfKey={$key}{$test}",
886 FALSE, NULL, FALSE
887 );
888 }
889 }
890
891 /**
892 * Get URl for when the back button is pressed.
893 *
894 * @param $qfKey
895 *
896 * @return string url
897 */
898 protected function getGoBackUrl($qfKey) {
899 return CRM_Utils_System::url($this->getBaseReturnUrl(), array(
900 '_qf_Confirm_display' => 'true',
6109d8df 901 'qfKey' => $qfKey,
49c882a3
EM
902 ),
903 TRUE, NULL, FALSE
904 );
905 }
906
907 /**
908 * Get the notify (aka ipn, web hook or silent post) url.
909 *
910 * If there is no '.' in it we assume that we are dealing with localhost or
911 * similar and it is unreachable from the web & hence invalid.
912 *
913 * @return string
914 * URL to notify outcome of transaction.
915 */
916 protected function getNotifyUrl() {
917 $url = CRM_Utils_System::url(
918 'civicrm/payment/ipn/' . $this->_paymentProcessor['id'],
919 array(),
920 TRUE
921 );
922 return (stristr($url, '.')) ? $url : '';
923 }
924
6a488035 925 /**
8319cf11
EM
926 * Calling this from outside the payment subsystem is deprecated - use doPayment.
927 *
928 * Does a server to server payment transaction.
929 *
6a0b768e
TO
930 * @param array $params
931 * Assoc array of input parameters for this transaction.
6a488035 932 *
a6c01b45 933 * @return array
863fadaa 934 * the result in an nice formatted array (or an error object - but throwing exceptions is preferred)
6a488035 935 */
863fadaa
EM
936 protected function doDirectPayment(&$params) {
937 return $params;
938 }
6a488035 939
c1cc3e0c 940 /**
6c99ada1
EM
941 * Process payment - this function wraps around both doTransferPayment and doDirectPayment.
942 *
943 * The function ensures an exception is thrown & moves some of this logic out of the form layer and makes the forms
944 * more agnostic.
c1cc3e0c 945 *
3910048c 946 * Payment processors should set payment_status_id. This function adds some historical defaults ie. the
7758bd2b
EM
947 * assumption that if a 'doDirectPayment' processors comes back it completed the transaction & in fact
948 * doTransferCheckout would not traditionally come back.
949 *
950 * doDirectPayment does not do an immediate payment for Authorize.net or Paypal so the default is assumed
951 * to be Pending.
952 *
3910048c
EM
953 * Once this function is fully rolled out then it will be preferred for processors to throw exceptions than to
954 * return Error objects
955 *
c1cc3e0c
EM
956 * @param array $params
957 *
7758bd2b 958 * @param string $component
c1cc3e0c 959 *
a6c01b45 960 * @return array
7758bd2b
EM
961 * Result array
962 *
963 * @throws \Civi\Payment\Exception\PaymentProcessorException
c1cc3e0c 964 */
8319cf11 965 public function doPayment(&$params, $component = 'contribute') {
05c302ec 966 $this->_component = $component;
7758bd2b 967 $statuses = CRM_Contribute_BAO_Contribution::buildOptions('contribution_status_id');
c51070b5 968
969 // If we have a $0 amount, skip call to processor and set payment_status to Completed.
970 // Conceivably a processor might override this - perhaps for setting up a token - but we don't
971 // have an example of that at the mome.
972 if ($params['amount'] == 0) {
973 $result['payment_status_id'] = array_search('Completed', $statuses);
974 return $result;
975 }
976
c1cc3e0c
EM
977 if ($this->_paymentProcessor['billing_mode'] == 4) {
978 $result = $this->doTransferCheckout($params, $component);
7c85dc65
EM
979 if (is_array($result) && !isset($result['payment_status_id'])) {
980 $result['payment_status_id'] = array_search('Pending', $statuses);
7758bd2b 981 }
c1cc3e0c
EM
982 }
983 else {
f8453bef 984 $result = $this->doDirectPayment($params, $component);
7c85dc65 985 if (is_array($result) && !isset($result['payment_status_id'])) {
9dd58272 986 if (!empty($params['is_recur'])) {
7758bd2b 987 // See comment block.
0816949d 988 $result['payment_status_id'] = array_search('Pending', $statuses);
7758bd2b
EM
989 }
990 else {
7c85dc65 991 $result['payment_status_id'] = array_search('Completed', $statuses);
7758bd2b
EM
992 }
993 }
c1cc3e0c
EM
994 }
995 if (is_a($result, 'CRM_Core_Error')) {
7758bd2b 996 throw new PaymentProcessorException(CRM_Core_Error::getMessages($result));
c1cc3e0c 997 }
a9cf9972 998 return $result;
c1cc3e0c
EM
999 }
1000
9d2f24ee 1001 /**
1002 * Query payment processor for details about a transaction.
1003 *
1004 * @param array $params
1005 * Array of parameters containing one of:
1006 * - trxn_id Id of an individual transaction.
1007 * - processor_id Id of a recurring contribution series as stored in the civicrm_contribution_recur table.
1008 *
1009 * @return array
1010 * Extra parameters retrieved.
1011 * Any parameters retrievable through this should be documented in the function comments at
1012 * CRM_Core_Payment::doQuery. Currently:
1013 * - fee_amount Amount of fee paid
1014 */
1015 public function doQuery($params) {
1016 return array();
1017 }
1018
6a488035 1019 /**
d09edf64 1020 * This function checks to see if we have the right config values.
6a488035 1021 *
a6c01b45
CW
1022 * @return string
1023 * the error message if any
6a488035 1024 */
7c550ca0 1025 abstract protected function checkConfig();
6a488035 1026
a0ee3941 1027 /**
6c99ada1
EM
1028 * Redirect for paypal.
1029 *
1030 * @todo move to paypal class or remove
1031 *
a0ee3941 1032 * @param $paymentProcessor
6c99ada1 1033 *
a0ee3941
EM
1034 * @return bool
1035 */
00be9182 1036 public static function paypalRedirect(&$paymentProcessor) {
6a488035
TO
1037 if (!$paymentProcessor) {
1038 return FALSE;
1039 }
1040
1041 if (isset($_GET['payment_date']) &&
1042 isset($_GET['merchant_return_link']) &&
1043 CRM_Utils_Array::value('payment_status', $_GET) == 'Completed' &&
1044 $paymentProcessor['payment_processor_type'] == "PayPal_Standard"
1045 ) {
1046 return TRUE;
1047 }
1048
1049 return FALSE;
1050 }
1051
1052 /**
6c99ada1
EM
1053 * Handle incoming payment notification.
1054 *
1055 * IPNs, also called silent posts are notifications of payment outcomes or activity on an external site.
1056 *
43e5f0f6 1057 * @todo move to0 \Civi\Payment\System factory method
6a488035 1058 * Page callback for civicrm/payment/ipn
6a488035 1059 */
00be9182 1060 public static function handleIPN() {
6a488035
TO
1061 self::handlePaymentMethod(
1062 'PaymentNotification',
1063 array(
1064 'processor_name' => @$_GET['processor_name'],
42b90e8f 1065 'processor_id' => @$_GET['processor_id'],
6a488035 1066 'mode' => @$_GET['mode'],
4164f4e1 1067 'q' => @$_GET['q'],
6a488035
TO
1068 )
1069 );
160c9df2 1070 CRM_Utils_System::civiExit();
6a488035
TO
1071 }
1072
1073 /**
3782df3e
EM
1074 * Payment callback handler.
1075 *
1076 * The processor_name or processor_id is passed in.
43d1ae00
EM
1077 * Note that processor_id is more reliable as one site may have more than one instance of a
1078 * processor & ideally the processor will be validating the results
6a488035
TO
1079 * Load requested payment processor and call that processor's handle<$method> method
1080 *
3782df3e
EM
1081 * @todo move to \Civi\Payment\System factory method
1082 *
1083 * @param string $method
1084 * 'PaymentNotification' or 'PaymentCron'
4691b077 1085 * @param array $params
23de1ac0
EM
1086 *
1087 * @throws \CRM_Core_Exception
1088 * @throws \Exception
6a488035 1089 */
00be9182 1090 public static function handlePaymentMethod($method, $params = array()) {
42b90e8f 1091 if (!isset($params['processor_id']) && !isset($params['processor_name'])) {
4164f4e1
EM
1092 $q = explode('/', CRM_Utils_Array::value('q', $params, ''));
1093 $lastParam = array_pop($q);
1094 if (is_numeric($lastParam)) {
1095 $params['processor_id'] = $_GET['processor_id'] = $lastParam;
1096 }
1097 else {
0ac9fd52 1098 throw new CRM_Core_Exception("Either 'processor_id' (recommended) or 'processor_name' (deprecated) is required for payment callback.");
4164f4e1 1099 }
6a488035 1100 }
4164f4e1 1101
e2bef985 1102 self::logPaymentNotification($params);
6a488035 1103
42b90e8f
CB
1104 $sql = "SELECT ppt.class_name, ppt.name as processor_name, pp.id AS processor_id
1105 FROM civicrm_payment_processor_type ppt
1106 INNER JOIN civicrm_payment_processor pp
1107 ON pp.payment_processor_type_id = ppt.id
9ff0c7a1 1108 AND pp.is_active";
42b90e8f
CB
1109
1110 if (isset($params['processor_id'])) {
1111 $sql .= " WHERE pp.id = %2";
1112 $args[2] = array($params['processor_id'], 'Integer');
0ac9fd52 1113 $notFound = ts("No active instances of payment processor %1 were found.", array(1 => $params['processor_id']));
42b90e8f
CB
1114 }
1115 else {
9ff0c7a1
EM
1116 // This is called when processor_name is passed - passing processor_id instead is recommended.
1117 $sql .= " WHERE ppt.name = %2 AND pp.is_test = %1";
6c99ada1
EM
1118 $args[1] = array(
1119 (CRM_Utils_Array::value('mode', $params) == 'test') ? 1 : 0,
1120 'Integer',
1121 );
42b90e8f 1122 $args[2] = array($params['processor_name'], 'String');
0ac9fd52 1123 $notFound = ts("No active instances of payment processor '%1' were found.", array(1 => $params['processor_name']));
42b90e8f
CB
1124 }
1125
1126 $dao = CRM_Core_DAO::executeQuery($sql, $args);
6a488035 1127
3782df3e 1128 // Check whether we found anything at all.
6a488035 1129 if (!$dao->N) {
3782df3e 1130 CRM_Core_Error::fatal($notFound);
6a488035
TO
1131 }
1132
1133 $method = 'handle' . $method;
1134 $extension_instance_found = FALSE;
1135
1136 // In all likelihood, we'll just end up with the one instance returned here. But it's
1137 // possible we may get more. Hence, iterate through all instances ..
1138
1139 while ($dao->fetch()) {
5495e78a 1140 // Check pp is extension - is this still required - surely the singleton below handles it.
6a488035
TO
1141 $ext = CRM_Extension_System::singleton()->getMapper();
1142 if ($ext->isExtensionKey($dao->class_name)) {
6a488035
TO
1143 $paymentClass = $ext->keyToClass($dao->class_name, 'payment');
1144 require_once $ext->classToPath($paymentClass);
1145 }
6a488035 1146
7a3b0ca3 1147 $processorInstance = System::singleton()->getById($dao->processor_id);
6a488035
TO
1148
1149 // Should never be empty - we already established this processor_id exists and is active.
81ebda7b 1150 if (empty($processorInstance)) {
6a488035
TO
1151 continue;
1152 }
1153
6a488035
TO
1154 // Does PP implement this method, and can we call it?
1155 if (!method_exists($processorInstance, $method) ||
1156 !is_callable(array($processorInstance, $method))
1157 ) {
43d1ae00
EM
1158 // on the off chance there is a double implementation of this processor we should keep looking for another
1159 // note that passing processor_id is more reliable & we should work to deprecate processor_name
1160 continue;
6a488035
TO
1161 }
1162
1163 // Everything, it seems, is ok - execute pp callback handler
1164 $processorInstance->$method();
a5ef96f6 1165 $extension_instance_found = TRUE;
6a488035
TO
1166 }
1167
4f99ca55 1168 if (!$extension_instance_found) {
0ac9fd52
CB
1169 $message = "No extension instances of the '%1' payment processor were found.<br />" .
1170 "%2 method is unsupported in legacy payment processors.";
1171 CRM_Core_Error::fatal(ts($message, array(1 => $params['processor_name'], 2 => $method)));
2aa397bc 1172 }
6a488035
TO
1173 }
1174
1175 /**
100fef9d 1176 * Check whether a method is present ( & supported ) by the payment processor object.
6a488035 1177 *
1e483eb5
EM
1178 * @deprecated - use $paymentProcessor->supports(array('cancelRecurring');
1179 *
6a0b768e
TO
1180 * @param string $method
1181 * Method to check for.
6a488035 1182 *
7c550ca0 1183 * @return bool
6a488035 1184 */
1524a007 1185 public function isSupported($method) {
6a488035
TO
1186 return method_exists(CRM_Utils_System::getClassName($this), $method);
1187 }
1188
1ba4a3aa
EM
1189 /**
1190 * Some processors replace the form submit button with their own.
1191 *
1192 * Returning false here will leave the button off front end forms.
1193 *
1194 * At this stage there is zero cross-over between back-office processors and processors that suppress the submit.
1195 */
1196 public function isSuppressSubmitButtons() {
1197 return FALSE;
1198 }
1199
d253aeb8
EM
1200 /**
1201 * Checks to see if invoice_id already exists in db.
1202 *
1203 * It's arguable if this belongs in the payment subsystem at all but since several processors implement it
1204 * it is better to standardise to being here.
1205 *
1206 * @param int $invoiceId The ID to check.
1207 *
1208 * @param null $contributionID
1209 * If a contribution exists pass in the contribution ID.
1210 *
1211 * @return bool
1212 * True if invoice ID otherwise exists, else false
1213 */
1214 protected function checkDupe($invoiceId, $contributionID = NULL) {
1215 $contribution = new CRM_Contribute_DAO_Contribution();
1216 $contribution->invoice_id = $invoiceId;
1217 if ($contributionID) {
1218 $contribution->whereAdd("id <> $contributionID");
1219 }
1220 return $contribution->find();
1221 }
1222
a0ee3941 1223 /**
3782df3e
EM
1224 * Get url for users to manage this recurring contribution for this processor.
1225 *
100fef9d 1226 * @param int $entityID
a0ee3941
EM
1227 * @param null $entity
1228 * @param string $action
1229 *
1230 * @return string
1231 */
00be9182 1232 public function subscriptionURL($entityID = NULL, $entity = NULL, $action = 'cancel') {
03cfff4c
KW
1233 // Set URL
1234 switch ($action) {
2aa397bc 1235 case 'cancel':
03cfff4c
KW
1236 $url = 'civicrm/contribute/unsubscribe';
1237 break;
2aa397bc
TO
1238
1239 case 'billing':
03cfff4c 1240 //in notify mode don't return the update billing url
68acd6ae 1241 if (!$this->isSupported('updateSubscriptionBillingInfo')) {
03cfff4c
KW
1242 return NULL;
1243 }
68acd6ae 1244 $url = 'civicrm/contribute/updatebilling';
03cfff4c 1245 break;
2aa397bc
TO
1246
1247 case 'update':
03cfff4c
KW
1248 $url = 'civicrm/contribute/updaterecur';
1249 break;
6a488035
TO
1250 }
1251
b7e7f943 1252 $userId = CRM_Core_Session::singleton()->get('userID');
353ffa53 1253 $contactID = 0;
03cfff4c 1254 $checksumValue = '';
353ffa53 1255 $entityArg = '';
03cfff4c
KW
1256
1257 // Find related Contact
1258 if ($entityID) {
1259 switch ($entity) {
2aa397bc 1260 case 'membership':
03cfff4c
KW
1261 $contactID = CRM_Core_DAO::getFieldValue("CRM_Member_DAO_Membership", $entityID, "contact_id");
1262 $entityArg = 'mid';
1263 break;
1264
2aa397bc 1265 case 'contribution':
03cfff4c
KW
1266 $contactID = CRM_Core_DAO::getFieldValue("CRM_Contribute_DAO_Contribution", $entityID, "contact_id");
1267 $entityArg = 'coid';
1268 break;
1269
2aa397bc 1270 case 'recur':
03cfff4c 1271 $sql = "
6a488035
TO
1272 SELECT con.contact_id
1273 FROM civicrm_contribution_recur rec
1274INNER JOIN civicrm_contribution con ON ( con.contribution_recur_id = rec.id )
1275 WHERE rec.id = %1
1276 GROUP BY rec.id";
03cfff4c
KW
1277 $contactID = CRM_Core_DAO::singleValueQuery($sql, array(1 => array($entityID, 'Integer')));
1278 $entityArg = 'crid';
1279 break;
6a488035 1280 }
6a488035
TO
1281 }
1282
03cfff4c
KW
1283 // Add entity arguments
1284 if ($entityArg != '') {
1285 // Add checksum argument
1286 if ($contactID != 0 && $userId != $contactID) {
1287 $checksumValue = '&cs=' . CRM_Contact_BAO_Contact_Utils::generateChecksum($contactID, NULL, 'inf');
1288 }
1289 return CRM_Utils_System::url($url, "reset=1&{$entityArg}={$entityID}{$checksumValue}", TRUE, NULL, FALSE, TRUE);
1290 }
1291
1292 // Else login URL
6a488035
TO
1293 if ($this->isSupported('accountLoginURL')) {
1294 return $this->accountLoginURL();
1295 }
03cfff4c
KW
1296
1297 // Else default
735d988f 1298 return isset($this->_paymentProcessor['url_recur']) ? $this->_paymentProcessor['url_recur'] : '';
6a488035 1299 }
96025800 1300
efa36d6e 1301 /**
1302 * Get description of payment to pass to processor.
1303 *
1304 * This is often what people see in the interface so we want to get
1305 * as much unique information in as possible within the field length (& presumably the early part of the field)
1306 *
1307 * People seeing these can be assumed to be advanced users so quantity of information probably trumps
1308 * having field names to clarify
1309 *
1310 * @param array $params
1311 * @param int $length
1312 *
1313 * @return string
1314 */
1315 protected function getPaymentDescription($params, $length = 24) {
1316 $parts = array('contactID', 'contributionID', 'description', 'billing_first_name', 'billing_last_name');
1317 $validParts = array();
1318 if (isset($params['description'])) {
1319 $uninformativeStrings = array(ts('Online Event Registration: '), ts('Online Contribution: '));
1320 $params['description'] = str_replace($uninformativeStrings, '', $params['description']);
1321 }
1322 foreach ($parts as $part) {
1323 if ((!empty($params[$part]))) {
1324 $validParts[] = $params[$part];
1325 }
1326 }
1327 return substr(implode('-', $validParts), 0, $length);
1328 }
1329
95974e8e
DG
1330 /**
1331 * Checks if backoffice recurring edit is allowed
1332 *
1333 * @return bool
1334 */
1335 public function supportsEditRecurringContribution() {
1336 return FALSE;
1337 }
1338
cd3bc162 1339 /**
1340 * Should a receipt be sent out for a pending payment.
1341 *
1342 * e.g for traditional pay later & ones with a delayed settlement a pending receipt makes sense.
1343 */
1344 public function isSendReceiptForPending() {
1345 return FALSE;
1346 }
1347
6a488035 1348}