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