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