Merge remote branch 'canonical/master' into merge-forward
[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' => 'select',
110 'name' => "billing_state_province_id-{$bltID}",
111 'title' => ts('State / Province'),
112 'cc_field' => TRUE,
113 'attributes' => array(
114 '' => ts('- select -')) +
115 CRM_Core_PseudoConstant::stateProvince(),
116 'is_required' => self::checkRequiredStateProvince($form, "billing_country_id-{$bltID}"),
117 );
118
119 $form->_paymentFields["billing_postal_code-{$bltID}"] = array(
120 'htmlType' => 'text',
121 'name' => "billing_postal_code-{$bltID}",
122 'title' => ts('Postal Code'),
123 'cc_field' => TRUE,
124 'attributes' => array('size' => 30, 'maxlength' => 60, 'autocomplete' => 'off'),
125 'is_required' => TRUE,
126 );
127
128 $form->_paymentFields["billing_country_id-{$bltID}"] = array(
129 'htmlType' => 'select',
130 'name' => "billing_country_id-{$bltID}",
131 'title' => ts('Country'),
132 'cc_field' => TRUE,
133 'attributes' => array(
134 '' => ts('- select -')) +
135 CRM_Core_PseudoConstant::country(),
136 'is_required' => TRUE,
137 );
138 }
139
140 /**
141 * create all fields needed for a credit card transaction
142 *
143 * @param $form
144 *
145 * @return void
146 * @access public
147 */
148 static function setCreditCardFields(&$form) {
149 CRM_Core_Payment_Form::_setPaymentFields($form);
150
151 $form->_paymentFields['credit_card_number'] = array(
152 'htmlType' => 'text',
153 'name' => 'credit_card_number',
154 'title' => ts('Card Number'),
155 'cc_field' => TRUE,
156 'attributes' => array('size' => 20, 'maxlength' => 20, 'autocomplete' => 'off'),
157 'is_required' => TRUE,
158 );
159
160 $form->_paymentFields['cvv2'] = array(
161 'htmlType' => 'text',
162 'name' => 'cvv2',
163 'title' => ts('Security Code'),
164 'cc_field' => TRUE,
165 'attributes' => array('size' => 5, 'maxlength' => 10, 'autocomplete' => 'off'),
166 'is_required' => CRM_Core_BAO_Setting::getItem(CRM_Core_BAO_Setting::CONTRIBUTE_PREFERENCES_NAME,
167 'cvv_backoffice_required',
168 NULL
169 ,1
170 ),
171 );
172
173 $form->_paymentFields['credit_card_exp_date'] = array(
174 'htmlType' => 'date',
175 'name' => 'credit_card_exp_date',
176 'title' => ts('Expiration Date'),
177 'cc_field' => TRUE,
178 'attributes' => CRM_Core_SelectValues::date('creditCard'),
179 'is_required' => TRUE,
180 );
181
182 $creditCardType = array('' => ts('- select -')) + CRM_Contribute_PseudoConstant::creditCard();
183 $form->_paymentFields['credit_card_type'] = array(
184 'htmlType' => 'select',
185 'name' => 'credit_card_type',
186 'title' => ts('Card Type'),
187 'cc_field' => TRUE,
188 'attributes' => $creditCardType,
189 'is_required' => FALSE,
190 );
191 }
192
193 /**
194 * create all fields needed for direct debit transaction
195 *
196 * @param $form
197 *
198 * @return void
199 * @access public
200 */
201 static function setDirectDebitFields(&$form) {
202 CRM_Core_Payment_Form::_setPaymentFields($form);
203
204 $form->_paymentFields['account_holder'] = array(
205 'htmlType' => 'text',
206 'name' => 'account_holder',
207 'title' => ts('Account Holder'),
208 'cc_field' => TRUE,
209 'attributes' => array('size' => 20, 'maxlength' => 34, 'autocomplete' => 'on'),
210 'is_required' => TRUE,
211 );
212
213 //e.g. IBAN can have maxlength of 34 digits
214 $form->_paymentFields['bank_account_number'] = array(
215 'htmlType' => 'text',
216 'name' => 'bank_account_number',
217 'title' => ts('Bank Account Number'),
218 'cc_field' => TRUE,
219 'attributes' => array('size' => 20, 'maxlength' => 34, 'autocomplete' => 'off'),
220 'is_required' => TRUE,
221 );
222
223 //e.g. SWIFT-BIC can have maxlength of 11 digits
224 $form->_paymentFields['bank_identification_number'] = array(
225 'htmlType' => 'text',
226 'name' => 'bank_identification_number',
227 'title' => ts('Bank Identification Number'),
228 'cc_field' => TRUE,
229 'attributes' => array('size' => 20, 'maxlength' => 11, 'autocomplete' => 'off'),
230 'is_required' => TRUE,
231 );
232
233 $form->_paymentFields['bank_name'] = array(
234 'htmlType' => 'text',
235 'name' => 'bank_name',
236 'title' => ts('Bank Name'),
237 'cc_field' => TRUE,
238 'attributes' => array('size' => 20, 'maxlength' => 64, 'autocomplete' => 'off'),
239 'is_required' => TRUE,
240 );
241 }
242
243 /**
244 * Function to add all the credit card fields
245 *
246 * @param $form
247 * @param bool $useRequired
248 *
249 * @return void
250 * @access public
251 */
252 static function buildCreditCard(&$form, $useRequired = FALSE) {
253 if ($form->_paymentProcessor['billing_mode'] & CRM_Core_Payment::BILLING_MODE_FORM) {
254 self::setCreditCardFields($form);
255 foreach ($form->_paymentFields as $name => $field) {
256 if (isset($field['cc_field']) &&
257 $field['cc_field']
258 ) {
259 $form->add($field['htmlType'],
260 $field['name'],
261 $field['title'],
262 $field['attributes'],
263 $useRequired ? $field['is_required'] : FALSE
264 );
265 }
266 }
267
268 $form->addRule('cvv2',
269 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.'),
270 'integer'
271 );
272
273 $form->addRule('credit_card_exp_date',
274 ts('Card expiration date cannot be a past date.'),
275 'currentDate', TRUE
276 );
277
278 // also take care of state country widget
279 $stateCountryMap = array(
280 1 => array('country' => "billing_country_id-{$form->_bltID}",
281 'state_province' => "billing_state_province_id-{$form->_bltID}",
282 ));
283 CRM_Core_BAO_Address::addStateCountryMap($stateCountryMap);
284 }
285
286 if ($form->_paymentProcessor['billing_mode'] & CRM_Core_Payment::BILLING_MODE_BUTTON) {
287 $form->_expressButtonName = $form->getButtonName('upload', 'express');
288 $form->assign('expressButtonName', $form->_expressButtonName);
289 $form->add('image',
290 $form->_expressButtonName,
291 $form->_paymentProcessor['url_button'],
292 array('class' => 'form-submit')
293 );
294 }
295 }
296
297 /**
298 * The credit card pseudo constant results only the CC label, not the key ID
299 * So we normalize the name to use it as a CSS class.
300 */
301 static function getCreditCardCSSNames() {
302 $creditCardTypes = array();
303 foreach (CRM_Contribute_PseudoConstant::creditCard() as $key => $name) {
304 // Replace anything not css-friendly by an underscore
305 // Non-latin names will not like this, but so many things are wrong with
306 // the credit-card type configurations already.
307 $key = str_replace(' ', '', $key);
308 $key = preg_replace('/[^a-zA-Z0-9]/', '_', $key);
309 $key = strtolower($key);
310 $creditCardTypes[$key] = $name;
311 }
312 return $creditCardTypes;
313 }
314
315 /**
316 * Function to add all the direct debit fields
317 *
318 * @param $form
319 * @param bool $useRequired
320 * @return void
321 * @access public
322 */
323 static function buildDirectDebit(&$form, $useRequired = FALSE) {
324 if ($form->_paymentProcessor['billing_mode'] & CRM_Core_Payment::BILLING_MODE_FORM) {
325 self::setDirectDebitFields($form);
326 foreach ($form->_paymentFields as $name => $field) {
327 if (isset($field['cc_field']) &&
328 $field['cc_field']
329 ) {
330 $form->add($field['htmlType'],
331 $field['name'],
332 $field['title'],
333 $field['attributes'],
334 $useRequired ? $field['is_required'] : FALSE
335 );
336 }
337 }
338
339 $form->addRule('bank_identification_number',
340 ts('Please enter a valid Bank Identification Number (value must not contain punctuation characters).'),
341 'nopunctuation'
342 );
343
344 $form->addRule('bank_account_number',
345 ts('Please enter a valid Bank Account Number (value must not contain punctuation characters).'),
346 'nopunctuation'
347 );
348 }
349
350 if ($form->_paymentProcessor['billing_mode'] & CRM_Core_Payment::BILLING_MODE_BUTTON) {
351 $form->_expressButtonName = $form->getButtonName($form->buttonType(), 'express');
352 $form->add('image',
353 $form->_expressButtonName,
354 $form->_paymentProcessor['url_button'],
355 array('class' => 'form-submit')
356 );
357 }
358 }
359
360 /**
361 * Function to add address block
362 *
363 * @param $form
364 * @param bool $useRequired
365 *
366 * @return void
367 * @access public
368 */
369 static function buildAddressBlock(&$form, $useRequired = FALSE) {
370 CRM_Core_Payment_Form::_setPaymentFields($form);
371 foreach ($form->_paymentFields as $name => $field) {
372 if (isset($field['cc_field']) &&
373 $field['cc_field']
374 ) {
375 $form->add($field['htmlType'],
376 $field['name'],
377 $field['title'],
378 $field['attributes'],
379 $useRequired ? $field['is_required'] : FALSE
380 );
381 }
382 }
383
384 // also take care of state country widget
385 $stateCountryMap = array(
386 1 => array(
387 'country' => "billing_country_id-{$form->_bltID}",
388 'state_province' => "billing_state_province_id-{$form->_bltID}",
389 )
390 );
391 CRM_Core_BAO_Address::addStateCountryMap($stateCountryMap);
392 }
393
394 /**
395 * Make sure that credit card number and cvv are valid
396 * Called within the scope of a QF formRule function
397 */
398 static function validateCreditCard($values, &$errors) {
399 if (!empty($values['credit_card_type'])) {
400 if (!empty($values['credit_card_number']) &&
401 !CRM_Utils_Rule::creditCardNumber($values['credit_card_number'], $values['credit_card_type'])
402 ) {
403 $errors['credit_card_number'] = ts('Please enter a valid Card Number');
404 }
405 if (!empty($values['cvv2']) &&
406 !CRM_Utils_Rule::cvv($values['cvv2'], $values['credit_card_type'])
407 ) {
408 $errors['cvv2'] = ts('Please enter a valid Card Verification Number');
409 }
410 }
411 elseif (!empty($values['credit_card_number'])) {
412 $errors['credit_card_number'] = ts('Please enter a valid Card Number');
413 }
414 }
415
416 /**
417 * function to map address fields
418 *
419 * @param $id
420 * @param $src
421 * @param $dst
422 * @param bool $reverse
423 *
424 * @return void
425 * @static
426 */
427 static function mapParams($id, &$src, &$dst, $reverse = FALSE) {
428 static $map = NULL;
429 if (!$map) {
430 $map = array(
431 'first_name' => 'billing_first_name',
432 'middle_name' => 'billing_middle_name',
433 'last_name' => 'billing_last_name',
434 'email' => "email-$id",
435 'street_address' => "billing_street_address-$id",
436 'supplemental_address_1' => "billing_supplemental_address_1-$id",
437 'city' => "billing_city-$id",
438 'state_province' => "billing_state_province-$id",
439 'postal_code' => "billing_postal_code-$id",
440 'country' => "billing_country-$id",
441 );
442 }
443
444 foreach ($map as $n => $v) {
445 if (!$reverse) {
446 if (isset($src[$n])) {
447 $dst[$v] = $src[$n];
448 }
449 }
450 else {
451 if (isset($src[$v])) {
452 $dst[$n] = $src[$v];
453 }
454 }
455 }
456 }
457
458 /**
459 * function to get the credit card expiration month
460 * The date format for this field should typically be "M Y" (ex: Feb 2011) or "m Y" (02 2011)
461 * See CRM-9017
462 *
463 * @param $src
464 *
465 * @return int
466 * @static
467 */
468 static function getCreditCardExpirationMonth($src) {
469 if ($month = CRM_Utils_Array::value('M', $src['credit_card_exp_date'])) {
470 return $month;
471 }
472
473 return CRM_Utils_Array::value('m', $src['credit_card_exp_date']);
474 }
475
476 /**
477 * function to get the credit card expiration year
478 * The date format for this field should typically be "M Y" (ex: Feb 2011) or "m Y" (02 2011)
479 * This function exists only to make it consistant with getCreditCardExpirationMonth
480 *
481 * @param $src
482 *
483 * @return int
484 * @static
485 */
486 static function getCreditCardExpirationYear($src) {
487 return CRM_Utils_Array::value('Y', $src['credit_card_exp_date']);
488 }
489
490 /**
491 * function to return state/province is_required = true/false
492 *
493 * @param obj $form: Form object
494 * @param string $name: Country index name on $_submitValues array
495 * @param bool $onBehalf: Is 'On Behalf Of' profile?
496 *
497 * @return bool
498 * TRUE/FALSE for is_required if country consist/not consist of state/province respectively
499 * @static
500 */
501 static function checkRequiredStateProvince($form, $name, $onBehalf = FALSE) {
502 // If selected country has possible values for state/province mark the
503 // state/province field as required.
504 $config = CRM_Core_Config::singleton();
505 $stateProvince = new CRM_Core_DAO_StateProvince();
506
507 if ($onBehalf) {
508 $stateProvince->country_id = CRM_Utils_Array::value($name, $form->_submitValues['onbehalf']);
509 }
510 else {
511 $stateProvince->country_id = CRM_Utils_Array::value($name, $form->_submitValues);
512 }
513
514 $limitCountryId = $stateProvince->country_id;
515
516 if ($stateProvince->count() > 0) {
517 // check that the state/province data is not excluded by a
518 // limitation in the localisation settings.
519 $countryIsoCodes = CRM_Core_PseudoConstant::countryIsoCode();
520 $limitCodes = $config->provinceLimit();
521 $limitIds = array();
522 foreach ($limitCodes as $code) {
523 $limitIds = array_merge($limitIds, array_keys($countryIsoCodes, $code));
524 }
525
526 if ($limitCountryId && in_array($limitCountryId, $limitIds)) {
527 return TRUE;
528 }
529 return FALSE;
530 }
531 return FALSE;
532 }
533 }
534