b7074407e8fc7a636d3969fad2d8c8459946f6e2
[civicrm-core.git] / CRM / Core / Payment / Form.php
1 <?php
2 /*
3 +--------------------------------------------------------------------+
4 | CiviCRM version 4.5 |
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2014 |
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 /**
29 *
30 * @package CRM
31 * @copyright CiviCRM LLC (c) 2004-2014
32 * $Id$
33 *
34 */
35 class CRM_Core_Payment_Form {
36
37 /**
38 * Add payment fields are depending on payment type
39 *
40 * @param int $type eg CRM_Core_Payment::PAYMENT_TYPE_DIRECT_DEBIT
41 * @param CRM_Core_Form $form
42 */
43 static public function setPaymentFieldsByType($type, &$form) {
44 if ($type & CRM_Core_Payment::PAYMENT_TYPE_DIRECT_DEBIT) {
45 CRM_Core_Payment_Form::setDirectDebitFields($form);
46 }
47 else {
48 CRM_Core_Payment_Form::setCreditCardFields($form);
49 }
50 }
51
52 /**
53 * create all common fields needed for a credit card or direct debit transaction
54 *
55 * @param $form
56 *
57 * @return void
58 * @access protected
59 */
60 static protected function _setPaymentFields(&$form) {
61 $bltID = $form->_bltID;
62
63 $form->_paymentFields['billing_first_name'] = array(
64 'htmlType' => 'text',
65 'name' => 'billing_first_name',
66 'title' => ts('Billing First Name'),
67 'cc_field' => TRUE,
68 'attributes' => array('size' => 30, 'maxlength' => 60, 'autocomplete' => 'off'),
69 'is_required' => TRUE,
70 );
71
72 $form->_paymentFields['billing_middle_name'] = array(
73 'htmlType' => 'text',
74 'name' => 'billing_middle_name',
75 'title' => ts('Billing Middle Name'),
76 'cc_field' => TRUE,
77 'attributes' => array('size' => 30, 'maxlength' => 60, 'autocomplete' => 'off'),
78 'is_required' => FALSE,
79 );
80
81 $form->_paymentFields['billing_last_name'] = array(
82 'htmlType' => 'text',
83 'name' => 'billing_last_name',
84 'title' => ts('Billing Last Name'),
85 'cc_field' => TRUE,
86 'attributes' => array('size' => 30, 'maxlength' => 60, 'autocomplete' => 'off'),
87 'is_required' => TRUE,
88 );
89
90 $form->_paymentFields["billing_street_address-{$bltID}"] = array(
91 'htmlType' => 'text',
92 'name' => "billing_street_address-{$bltID}",
93 'title' => ts('Street Address'),
94 'cc_field' => TRUE,
95 'attributes' => array('size' => 30, 'maxlength' => 60, 'autocomplete' => 'off'),
96 'is_required' => TRUE,
97 );
98
99 $form->_paymentFields["billing_city-{$bltID}"] = array(
100 'htmlType' => 'text',
101 'name' => "billing_city-{$bltID}",
102 'title' => ts('City'),
103 'cc_field' => TRUE,
104 'attributes' => array('size' => 30, 'maxlength' => 60, 'autocomplete' => 'off'),
105 'is_required' => TRUE,
106 );
107
108 $form->_paymentFields["billing_state_province_id-{$bltID}"] = array(
109 'htmlType' => 'chainSelect',
110 'title' => ts('State/Province'),
111 'name' => "billing_state_province_id-{$bltID}",
112 'cc_field' => TRUE,
113 'is_required' => TRUE,
114 );
115
116 $form->_paymentFields["billing_postal_code-{$bltID}"] = array(
117 'htmlType' => 'text',
118 'name' => "billing_postal_code-{$bltID}",
119 'title' => ts('Postal Code'),
120 'cc_field' => TRUE,
121 'attributes' => array('size' => 30, 'maxlength' => 60, 'autocomplete' => 'off'),
122 'is_required' => TRUE,
123 );
124
125 $form->_paymentFields["billing_country_id-{$bltID}"] = array(
126 'htmlType' => 'select',
127 'name' => "billing_country_id-{$bltID}",
128 'title' => ts('Country'),
129 'cc_field' => TRUE,
130 'attributes' => array(
131 '' => ts('- select -')) +
132 CRM_Core_PseudoConstant::country(),
133 'is_required' => TRUE,
134 );
135 //CRM-15509 working towards giving control over billing fields to payment processors. For now removing tpl hard-coding
136 $smarty = CRM_Core_Smarty::singleton();
137 $smarty->assign('billingDetailsFields', array(
138 'billing_first_name',
139 'billing_middle_name',
140 'billing_last_name',
141 "billing_street_address-{$bltID}",
142 "billing_city-{$bltID}",
143 "billing_country_id-{$bltID}",
144 "billing_state_province_id-{$bltID}",
145 "billing_postal_code-{$bltID}",
146 ));
147 }
148
149 /**
150 * create all fields needed for a credit card transaction
151 *
152 * @param CRM_Core_Form $form
153 *
154 * @return void
155 * @access public
156 */
157 static function setCreditCardFields(&$form) {
158 CRM_Core_Payment_Form::_setPaymentFields($form);
159
160 $form->_paymentFields['credit_card_number'] = array(
161 'htmlType' => 'text',
162 'name' => 'credit_card_number',
163 'title' => ts('Card Number'),
164 'cc_field' => TRUE,
165 'attributes' => array('size' => 20, 'maxlength' => 20, 'autocomplete' => 'off'),
166 'is_required' => TRUE,
167 );
168
169 $form->_paymentFields['cvv2'] = array(
170 'htmlType' => 'text',
171 'name' => 'cvv2',
172 'title' => ts('Security Code'),
173 'cc_field' => TRUE,
174 'attributes' => array('size' => 5, 'maxlength' => 10, 'autocomplete' => 'off'),
175 'is_required' => CRM_Core_BAO_Setting::getItem(CRM_Core_BAO_Setting::CONTRIBUTE_PREFERENCES_NAME,
176 'cvv_backoffice_required',
177 NULL
178 ,1
179 ),
180 );
181
182 $form->_paymentFields['credit_card_exp_date'] = array(
183 'htmlType' => 'date',
184 'name' => 'credit_card_exp_date',
185 'title' => ts('Expiration Date'),
186 'cc_field' => TRUE,
187 'attributes' => CRM_Core_SelectValues::date('creditCard'),
188 'is_required' => TRUE,
189 );
190
191 $creditCardType = array('' => ts('- select -')) + CRM_Contribute_PseudoConstant::creditCard();
192 $form->_paymentFields['credit_card_type'] = array(
193 'htmlType' => 'select',
194 'name' => 'credit_card_type',
195 'title' => ts('Card Type'),
196 'cc_field' => TRUE,
197 'attributes' => $creditCardType,
198 'is_required' => FALSE,
199 );
200 //CRM-15509 this is probably a temporary resting place for these form assignments but we are working towards putting this
201 // in an option group & having php / payment processors define the billing form rather than the tpl
202 $smarty = CRM_Core_Smarty::singleton();
203 $smarty->assign('paymentTypeName', 'credit_card');
204 $smarty->assign('paymentTypeLabel', ts('Credit Card Information'));
205 $smarty->assign('paymentFields', self::getPaymentFields($form->_paymentProcessor));
206 }
207
208 /**
209 * @param CRM_Core_Form $form
210 * @param bool $useRequired
211 */
212 static function addCommonFields(&$form, $useRequired) {
213 foreach ($form->_paymentFields as $name => $field) {
214 if (!empty($field['cc_field'])) {
215 if ($field['htmlType'] == 'chainSelect') {
216 $form->addChainSelect($field['name'], array('required' => $useRequired && $field['is_required']));
217 }
218 else {
219 $form->add($field['htmlType'],
220 $field['name'],
221 $field['title'],
222 $field['attributes'],
223 $useRequired ? $field['is_required'] : FALSE
224 );
225 }
226 }
227 }
228 }
229
230 /**
231 * create all fields needed for direct debit transaction
232 *
233 * @param $form
234 *
235 * @return void
236 * @access public
237 */
238 static function setDirectDebitFields(&$form) {
239 CRM_Core_Payment_Form::_setPaymentFields($form);
240
241 $form->_paymentFields['account_holder'] = array(
242 'htmlType' => 'text',
243 'name' => 'account_holder',
244 'title' => ts('Account Holder'),
245 'cc_field' => TRUE,
246 'attributes' => array('size' => 20, 'maxlength' => 34, 'autocomplete' => 'on'),
247 'is_required' => TRUE,
248 );
249
250 //e.g. IBAN can have maxlength of 34 digits
251 $form->_paymentFields['bank_account_number'] = array(
252 'htmlType' => 'text',
253 'name' => 'bank_account_number',
254 'title' => ts('Bank Account Number'),
255 'cc_field' => TRUE,
256 'attributes' => array('size' => 20, 'maxlength' => 34, 'autocomplete' => 'off'),
257 'is_required' => TRUE,
258 );
259
260 //e.g. SWIFT-BIC can have maxlength of 11 digits
261 $form->_paymentFields['bank_identification_number'] = array(
262 'htmlType' => 'text',
263 'name' => 'bank_identification_number',
264 'title' => ts('Bank Identification Number'),
265 'cc_field' => TRUE,
266 'attributes' => array('size' => 20, 'maxlength' => 11, 'autocomplete' => 'off'),
267 'is_required' => TRUE,
268 );
269
270 $form->_paymentFields['bank_name'] = array(
271 'htmlType' => 'text',
272 'name' => 'bank_name',
273 'title' => ts('Bank Name'),
274 'cc_field' => TRUE,
275 'attributes' => array('size' => 20, 'maxlength' => 64, 'autocomplete' => 'off'),
276 'is_required' => TRUE,
277 );
278 //CRM-15509 this is probably a temporary resting place for these form assignments but we are working towards putting this
279 // in an option group & having php / payment processors define the billing form rather than the tpl
280 $smarty = CRM_Core_Smarty::singleton();
281 // replace these payment type names with an option group - moving name & label assumptions out of the tpl is a step towards that
282 $smarty->assign('paymentTypeName', 'direct_debit');
283 $smarty->assign('paymentTypeLabel', ts('Direct Debit Information'));
284 $smarty->assign('paymentFields', self::getPaymentFields($form->_paymentProcessor));
285 }
286
287 /**
288 * @param array $paymentProcessor
289 * @todo it may be necessary to set details that affect it - mostly likely take Country as a param
290 *
291 * @return mixed
292 */
293 static function getPaymentFields($paymentProcessor) {
294 return array();
295 $paymentProcessorObject = CRM_Core_Payment::singleton(($paymentProcessor['is_test'] ? 'test' : 'live'), $paymentProcessor);
296 return $paymentProcessorObject->getPaymentFields();
297 }
298
299 /**
300 * @param $form
301 *
302 * @param array $paymentProcessor
303 * @todo it may be necessary to set details that affect it - mostly likely take Country as a param
304 *
305 * @return mixed
306 */
307 protected static function setPaymentFields(&$form, $paymentProcessor) {
308 $fields = self::getPaymentFields($paymentProcessor);
309
310 }
311
312 static function buildPaymentForm(&$form, $useRequired = FALSE) {
313
314 }
315
316 /**
317 * Function to add all the credit card fields
318 *
319 * @param $form
320 * @param bool $useRequired
321 *
322 * @return void
323 * @access public
324 */
325 static function buildCreditCard(&$form, $useRequired = FALSE) {
326 if ($form->_paymentProcessor['billing_mode'] & CRM_Core_Payment::BILLING_MODE_FORM) {
327 self::setCreditCardFields($form);
328 self::addCommonFields($form, $useRequired);
329
330 $form->addRule('cvv2',
331 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.'),
332 'integer'
333 );
334
335 $form->addRule('credit_card_exp_date',
336 ts('Card expiration date cannot be a past date.'),
337 'currentDate', TRUE
338 );
339
340 }
341
342 if ($form->_paymentProcessor['billing_mode'] & CRM_Core_Payment::BILLING_MODE_BUTTON) {
343 $form->_expressButtonName = $form->getButtonName('upload', 'express');
344 $form->assign('expressButtonName', $form->_expressButtonName);
345 $form->add('image',
346 $form->_expressButtonName,
347 $form->_paymentProcessor['url_button'],
348 array('class' => 'crm-form-submit')
349 );
350 }
351 }
352
353 /**
354 * The credit card pseudo constant results only the CC label, not the key ID
355 * So we normalize the name to use it as a CSS class.
356 */
357 static function getCreditCardCSSNames() {
358 $creditCardTypes = array();
359 foreach (CRM_Contribute_PseudoConstant::creditCard() as $key => $name) {
360 // Replace anything not css-friendly by an underscore
361 // Non-latin names will not like this, but so many things are wrong with
362 // the credit-card type configurations already.
363 $key = str_replace(' ', '', $key);
364 $key = preg_replace('/[^a-zA-Z0-9]/', '_', $key);
365 $key = strtolower($key);
366 $creditCardTypes[$key] = $name;
367 }
368 return $creditCardTypes;
369 }
370
371 /**
372 * Function to add all the direct debit fields
373 *
374 * @param $form
375 * @param bool $useRequired
376 * @return void
377 * @access public
378 */
379 static function buildDirectDebit(&$form, $useRequired = FALSE) {
380 if ($form->_paymentProcessor['billing_mode'] & CRM_Core_Payment::BILLING_MODE_FORM) {
381 self::setDirectDebitFields($form);
382 self::addCommonFields($form, $useRequired);
383
384 $form->addRule('bank_identification_number',
385 ts('Please enter a valid Bank Identification Number (value must not contain punctuation characters).'),
386 'nopunctuation'
387 );
388
389 $form->addRule('bank_account_number',
390 ts('Please enter a valid Bank Account Number (value must not contain punctuation characters).'),
391 'nopunctuation'
392 );
393 }
394
395 if ($form->_paymentProcessor['billing_mode'] & CRM_Core_Payment::BILLING_MODE_BUTTON) {
396 $form->_expressButtonName = $form->getButtonName($form->buttonType(), 'express');
397 $form->add('image',
398 $form->_expressButtonName,
399 $form->_paymentProcessor['url_button'],
400 array('class' => 'crm-form-submit')
401 );
402 }
403 }
404
405 /**
406 * Function to add address block
407 *
408 * @param $form
409 * @param bool $useRequired
410 *
411 * @return void
412 * @access public
413 */
414 static function buildAddressBlock(&$form, $useRequired = FALSE) {
415 CRM_Core_Payment_Form::_setPaymentFields($form);
416 foreach ($form->_paymentFields as $name => $field) {
417 if (isset($field['cc_field']) &&
418 $field['cc_field']
419 ) {
420 $form->add($field['htmlType'],
421 $field['name'],
422 $field['title'],
423 $field['attributes'],
424 $useRequired ? $field['is_required'] : FALSE
425 );
426 }
427 }
428
429 // also take care of state country widget
430 $stateCountryMap = array(
431 1 => array(
432 'country' => "billing_country_id-{$form->_bltID}",
433 'state_province' => "billing_state_province_id-{$form->_bltID}",
434 )
435 );
436 CRM_Core_BAO_Address::addStateCountryMap($stateCountryMap);
437 }
438
439 /**
440 * Make sure that credit card number and cvv are valid
441 * Called within the scope of a QF formRule function
442 */
443 static function validateCreditCard($values, &$errors) {
444 if (!empty($values['credit_card_type'])) {
445 if (!empty($values['credit_card_number']) &&
446 !CRM_Utils_Rule::creditCardNumber($values['credit_card_number'], $values['credit_card_type'])
447 ) {
448 $errors['credit_card_number'] = ts('Please enter a valid Card Number');
449 }
450 if (!empty($values['cvv2']) &&
451 !CRM_Utils_Rule::cvv($values['cvv2'], $values['credit_card_type'])
452 ) {
453 $errors['cvv2'] = ts('Please enter a valid Card Verification Number');
454 }
455 }
456 elseif (!empty($values['credit_card_number'])) {
457 $errors['credit_card_number'] = ts('Please enter a valid Card Number');
458 }
459 }
460
461 /**
462 * function to map address fields
463 *
464 * @param $id
465 * @param $src
466 * @param $dst
467 * @param bool $reverse
468 *
469 * @return void
470 * @static
471 */
472 static function mapParams($id, &$src, &$dst, $reverse = FALSE) {
473 static $map = NULL;
474 if (!$map) {
475 $map = array(
476 'first_name' => 'billing_first_name',
477 'middle_name' => 'billing_middle_name',
478 'last_name' => 'billing_last_name',
479 'email' => "email-$id",
480 'street_address' => "billing_street_address-$id",
481 'supplemental_address_1' => "billing_supplemental_address_1-$id",
482 'city' => "billing_city-$id",
483 'state_province' => "billing_state_province-$id",
484 'postal_code' => "billing_postal_code-$id",
485 'country' => "billing_country-$id",
486 );
487 }
488
489 foreach ($map as $n => $v) {
490 if (!$reverse) {
491 if (isset($src[$n])) {
492 $dst[$v] = $src[$n];
493 }
494 }
495 else {
496 if (isset($src[$v])) {
497 $dst[$n] = $src[$v];
498 }
499 }
500 }
501 }
502
503 /**
504 * function to get the credit card expiration month
505 * The date format for this field should typically be "M Y" (ex: Feb 2011) or "m Y" (02 2011)
506 * See CRM-9017
507 *
508 * @param $src
509 *
510 * @return int
511 * @static
512 */
513 static function getCreditCardExpirationMonth($src) {
514 if ($month = CRM_Utils_Array::value('M', $src['credit_card_exp_date'])) {
515 return $month;
516 }
517
518 return CRM_Utils_Array::value('m', $src['credit_card_exp_date']);
519 }
520
521 /**
522 * function to get the credit card expiration year
523 * The date format for this field should typically be "M Y" (ex: Feb 2011) or "m Y" (02 2011)
524 * This function exists only to make it consistant with getCreditCardExpirationMonth
525 *
526 * @param $src
527 *
528 * @return int
529 * @static
530 */
531 static function getCreditCardExpirationYear($src) {
532 return CRM_Utils_Array::value('Y', $src['credit_card_exp_date']);
533 }
534 }