Merge pull request #15698 from jitendrapurohit/fix-participant-fields
[civicrm-core.git] / CRM / Admin / Form / PaymentProcessor.php
1 <?php
2 /*
3 +--------------------------------------------------------------------+
4 | CiviCRM version 5 |
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2019 |
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-2019
32 */
33
34 /**
35 * This class generates form components for Payment Processor.
36 */
37 class CRM_Admin_Form_PaymentProcessor extends CRM_Admin_Form {
38 use CRM_Core_Form_EntityFormTrait;
39
40 protected $_paymentProcessorDAO;
41
42 /**
43 * @var int
44 * Payment processor Type ID
45 */
46 protected $_paymentProcessorType;
47
48 /**
49 * Fields for the entity to be assigned to the template.
50 *
51 * Fields may have keys
52 * - name (required to show in tpl from the array)
53 * - description (optional, will appear below the field)
54 * Auto-added by setEntityFieldsMetadata unless specified here (use description => '' to hide)
55 * - not-auto-addable - this class will not attempt to add the field using addField.
56 * (this will be automatically set if the field does not have html in it's metadata
57 * or is not a core field on the form's entity).
58 * - help (option) add help to the field - e.g ['id' => 'id-source', 'file' => 'CRM/Contact/Form/Contact']]
59 * - template - use a field specific template to render this field
60 * - required
61 * - is_freeze (field should be frozen).
62 *
63 * @var array
64 */
65 protected $entityFields = [];
66
67 /**
68 * Set entity fields to be assigned to the form.
69 */
70 protected function setEntityFields() {
71 $this->entityFields = [
72 'payment_processor_type_id' => [
73 'name' => 'payment_processor_type_id',
74 'required' => TRUE,
75 ],
76 'name' => [
77 'name' => 'name',
78 'required' => TRUE,
79 ],
80 'title' => [
81 'name' => 'title',
82 ],
83 'description' => [
84 'name' => 'description',
85 ],
86 ];
87
88 $this->setEntityFieldsMetadata();
89 }
90
91 /**
92 * Get the name of the base entity being edited.
93 *
94 * @return string
95 */
96 public function getDefaultEntity() {
97 return 'PaymentProcessor';
98 }
99
100 /**
101 * Set the delete message.
102 *
103 * We do this from the constructor in order to do a translation.
104 */
105 public function setDeleteMessage() {
106 $this->deleteMessage = ts('Deleting this Payment Processor may result in some transaction pages being rendered inactive.') . ' ' . ts('Do you want to continue?');
107 }
108
109 public function preProcess() {
110 parent::preProcess();
111
112 if ($this->_id) {
113 $this->_paymentProcessorType = CRM_Utils_Request::retrieve('pp', 'String', $this, FALSE, NULL);
114 if (!$this->_paymentProcessorType) {
115 $this->_paymentProcessorType = CRM_Core_DAO::getFieldValue('CRM_Financial_DAO_PaymentProcessor',
116 $this->_id,
117 'payment_processor_type_id'
118 );
119 }
120 $this->set('pp', $this->_paymentProcessorType);
121 }
122 else {
123 $this->_paymentProcessorType = CRM_Utils_Request::retrieve('pp', 'String', $this, TRUE, NULL);
124 }
125
126 $this->assign('ppType', $this->_paymentProcessorType);
127 $ppTypeName = CRM_Core_DAO::getFieldValue('CRM_Financial_DAO_PaymentProcessorType',
128 $this->_paymentProcessorType,
129 'name'
130 );
131 $this->assign('ppTypeName', $ppTypeName);
132
133 $this->_paymentProcessorDAO = new CRM_Financial_DAO_PaymentProcessorType();
134 $this->_paymentProcessorDAO->id = $this->_paymentProcessorType;
135
136 $this->_paymentProcessorDAO->find(TRUE);
137
138 if ($this->_id) {
139 $refreshURL = CRM_Utils_System::url('civicrm/admin/paymentProcessor',
140 "reset=1&action=update&id={$this->_id}",
141 FALSE, NULL, FALSE
142 );
143 }
144 else {
145 $refreshURL = CRM_Utils_System::url('civicrm/admin/paymentProcessor',
146 "reset=1&action=add",
147 FALSE, NULL, FALSE
148 );
149 }
150
151 //CRM-4129
152 $destination = CRM_Utils_Request::retrieve('civicrmDestination', 'String', $this);
153 if ($destination) {
154 $destination = urlencode($destination);
155 $refreshURL .= "&civicrmDestination=$destination";
156 }
157
158 $this->refreshURL = $refreshURL;
159 $this->assign('refreshURL', $refreshURL);
160
161 $this->assign('is_recur', $this->_paymentProcessorDAO->is_recur);
162
163 $this->_fields = [
164 [
165 'name' => 'user_name',
166 'label' => $this->_paymentProcessorDAO->user_name_label,
167 ],
168 [
169 'name' => 'password',
170 'label' => $this->_paymentProcessorDAO->password_label,
171 ],
172 [
173 'name' => 'signature',
174 'label' => $this->_paymentProcessorDAO->signature_label,
175 ],
176 [
177 'name' => 'subject',
178 'label' => $this->_paymentProcessorDAO->subject_label,
179 ],
180 [
181 'name' => 'url_site',
182 'label' => ts('Site URL'),
183 'rule' => 'url',
184 'msg' => ts('Enter a valid URL'),
185 ],
186 ];
187
188 if ($this->_paymentProcessorDAO->is_recur) {
189 $this->_fields[] = [
190 'name' => 'url_recur',
191 'label' => ts('Recurring Payments URL'),
192 'rule' => 'url',
193 'msg' => ts('Enter a valid URL'),
194 ];
195 }
196
197 if (!empty($this->_paymentProcessorDAO->url_button_default)) {
198 $this->_fields[] = [
199 'name' => 'url_button',
200 'label' => ts('Button URL'),
201 'rule' => 'url',
202 'msg' => ts('Enter a valid URL'),
203 ];
204 }
205
206 if (!empty($this->_paymentProcessorDAO->url_api_default)) {
207 $this->_fields[] = [
208 'name' => 'url_api',
209 'label' => ts('API URL'),
210 'rule' => 'url',
211 'msg' => ts('Enter a valid URL'),
212 ];
213 }
214 }
215
216 /**
217 * Build the form object.
218 *
219 * @param bool $check
220 */
221 public function buildQuickForm($check = FALSE) {
222 $this->buildQuickEntityForm();
223
224 if ($this->isDeleteContext()) {
225 return;
226 }
227
228 $attributes = CRM_Core_DAO::getAttribute('CRM_Financial_DAO_PaymentProcessor');
229
230 $this->addRule('name', ts('Name already exists in Database.'), 'objectExists', [
231 'CRM_Financial_DAO_PaymentProcessor',
232 $this->_id,
233 'name',
234 CRM_Core_Config::domainID(),
235 ]);
236
237 // @todo - remove this & let the entityForm do it - need to make sure we are handling the js though.
238 $this->add('select',
239 'payment_processor_type_id',
240 ts('Payment Processor Type'),
241 CRM_Financial_BAO_PaymentProcessor::buildOptions('payment_processor_type_id'),
242 TRUE,
243 ['onchange' => "reload(true)"]
244 );
245
246 // Financial Account of account type asset CRM-11515
247 $accountType = CRM_Core_PseudoConstant::accountOptionValues('financial_account_type', NULL, " AND v.name = 'Asset' ");
248 $financialAccount = CRM_Contribute_PseudoConstant::financialAccount(NULL, key($accountType));
249 if ($fcount = count($financialAccount)) {
250 $this->assign('financialAccount', $fcount);
251 }
252 $this->add('select', 'financial_account_id', ts('Financial Account'),
253 ['' => ts('- select -')] + $financialAccount,
254 TRUE
255 );
256 $this->addSelect('payment_instrument_id',
257 [
258 'entity' => 'contribution',
259 'label' => ts('Payment Method'),
260 'placeholder' => NULL,
261 ]
262 );
263
264 // is this processor active ?
265 $this->add('checkbox', 'is_active', ts('Is this Payment Processor active?'));
266 $this->add('checkbox', 'is_default', ts('Is this Payment Processor the default?'));
267 $creditCardTypes = CRM_Contribute_PseudoConstant::creditCard();
268 $this->addCheckBox('accept_credit_cards', ts('Accepted Credit Card Type(s)'),
269 $creditCardTypes, NULL, NULL, NULL, NULL, '&nbsp;&nbsp;&nbsp;');
270 foreach ($this->_fields as $field) {
271 if (empty($field['label'])) {
272 continue;
273 }
274
275 $this->addField($field['name'], ['label' => $field['label']]);
276
277 $fieldSpec = civicrm_api3($this->getDefaultEntity(), 'getfield', [
278 'name' => $field['name'],
279 'action' => 'create',
280 ]);
281 $this->add($fieldSpec['values']['html']['type'], "test_{$field['name']}",
282 $field['label'], $attributes[$field['name']]
283 );
284 if (!empty($field['rule'])) {
285 $this->addRule($field['name'], $field['msg'], $field['rule']);
286 $this->addRule("test_{$field['name']}", $field['msg'], $field['rule']);
287 }
288 }
289
290 $this->addFormRule(['CRM_Admin_Form_PaymentProcessor', 'formRule']);
291 }
292
293 /**
294 * @param $fields
295 *
296 * @return array|bool
297 */
298 public static function formRule($fields) {
299
300 // make sure that at least one of live or test is present
301 // and we have at least name and url_site
302 // would be good to make this processor specific
303 $errors = [];
304
305 if (!(self::checkSection($fields, $errors) ||
306 self::checkSection($fields, $errors, 'test')
307 )
308 ) {
309 $errors['_qf_default'] = ts('You must have at least the test or live section filled');
310 }
311
312 if (!empty($errors)) {
313 return $errors;
314 }
315
316 return empty($errors) ? TRUE : $errors;
317 }
318
319 /**
320 * @param $fields
321 * @param $errors
322 * @param null $section
323 *
324 * @return bool
325 */
326 public static function checkSection(&$fields, &$errors, $section = NULL) {
327 $names = ['user_name'];
328
329 $present = FALSE;
330 $allPresent = TRUE;
331 foreach ($names as $name) {
332 if ($section) {
333 $name = "{$section}_$name";
334 }
335 if (!empty($fields[$name])) {
336 $present = TRUE;
337 }
338 else {
339 $allPresent = FALSE;
340 }
341 }
342
343 if ($present) {
344 if (!$allPresent) {
345 $errors['_qf_default'] = ts('You must have at least the user_name specified');
346 }
347 }
348 return $present;
349 }
350
351 /**
352 * @return array
353 */
354 public function setDefaultValues() {
355 $defaults = [];
356
357 if (!$this->_id) {
358 $defaults['is_active'] = $defaults['is_default'] = 1;
359 $defaults['url_site'] = $this->_paymentProcessorDAO->url_site_default;
360 $defaults['url_api'] = $this->_paymentProcessorDAO->url_api_default;
361 $defaults['url_recur'] = $this->_paymentProcessorDAO->url_recur_default;
362 $defaults['url_button'] = $this->_paymentProcessorDAO->url_button_default;
363 $defaults['test_url_site'] = $this->_paymentProcessorDAO->url_site_test_default;
364 $defaults['test_url_api'] = $this->_paymentProcessorDAO->url_api_test_default;
365 $defaults['test_url_recur'] = $this->_paymentProcessorDAO->url_recur_test_default;
366 $defaults['test_url_button'] = $this->_paymentProcessorDAO->url_button_test_default;
367 $defaults['payment_instrument_id'] = $this->_paymentProcessorDAO->payment_instrument_id;
368 // When user changes payment processor type, it is passed in via $this->_ppType so update defaults array.
369 if ($this->_paymentProcessorType) {
370 $defaults['payment_processor_type_id'] = $this->_paymentProcessorType;
371 }
372 $defaults['financial_account_id'] = CRM_Financial_BAO_PaymentProcessor::getDefaultFinancialAccountID();
373 return $defaults;
374 }
375 $domainID = CRM_Core_Config::domainID();
376
377 $dao = new CRM_Financial_DAO_PaymentProcessor();
378 $dao->id = $this->_id;
379 $dao->domain_id = $domainID;
380 if (!$dao->find(TRUE)) {
381 return $defaults;
382 }
383
384 CRM_Core_DAO::storeValues($dao, $defaults);
385 // If payment processor ID does not exist, $paymentProcessorName will be FALSE
386 $paymentProcessorName = CRM_Core_PseudoConstant::getName('CRM_Financial_BAO_PaymentProcessor', 'payment_processor_type_id', $this->_paymentProcessorType);
387 if ($this->_paymentProcessorType && $paymentProcessorName) {
388 // When user changes payment processor type, it is passed in via $this->_ppType so update defaults array.
389 $defaults['payment_processor_type_id'] = $this->_paymentProcessorType;
390 }
391 else {
392 CRM_Core_Session::setStatus('Payment Processor Type (ID=' . $this->_paymentProcessorType . ') not found. Did you disable the payment processor extension?', 'Missing Payment Processor', 'alert');
393 }
394
395 $cards = json_decode(CRM_Core_DAO::getFieldValue('CRM_Financial_DAO_PaymentProcessor',
396 $this->_id,
397 'accepted_credit_cards'
398 ), TRUE);
399 $acceptedCards = [];
400 if (!empty($cards)) {
401 foreach ($cards as $card => $val) {
402 $acceptedCards[$card] = 1;
403 }
404 }
405 $defaults['accept_credit_cards'] = $acceptedCards;
406 unset($defaults['accepted_credit_cards']);
407 // now get testID
408 $testDAO = new CRM_Financial_DAO_PaymentProcessor();
409 $testDAO->name = $dao->name;
410 $testDAO->is_test = 1;
411 $testDAO->domain_id = $domainID;
412 if ($testDAO->find(TRUE)) {
413 $this->_testID = $testDAO->id;
414
415 foreach ($this->_fields as $field) {
416 $testName = "test_{$field['name']}";
417 $defaults[$testName] = $testDAO->{$field['name']};
418 }
419 }
420 $defaults['financial_account_id'] = CRM_Contribute_PseudoConstant::getRelationalFinancialAccount($dao->id, NULL, 'civicrm_payment_processor');
421
422 return $defaults;
423 }
424
425 /**
426 * Process the form submission.
427 *
428 * @throws \CiviCRM_API3_Exception
429 * @throws \CRM_Core_Exception
430 */
431 public function postProcess() {
432
433 if ($this->_action & CRM_Core_Action::DELETE) {
434 CRM_Financial_BAO_PaymentProcessor::del($this->_id);
435 CRM_Core_Session::setStatus("", ts('Payment Processor Deleted.'), "success");
436 return NULL;
437 }
438
439 $values = $this->controller->exportValues($this->_name);
440 $domainID = CRM_Core_Config::domainID();
441
442 if (!empty($values['is_default'])) {
443 $query = "UPDATE civicrm_payment_processor SET is_default = 0 WHERE domain_id = $domainID";
444 CRM_Core_DAO::executeQuery($query);
445 }
446
447 if ($this->_paymentProcessorType !== $values['payment_processor_type_id']) {
448 // If we changed the payment processor type, need to update the object as well
449 $this->_paymentProcessorType = $values['payment_processor_type_id'];
450 $this->_paymentProcessorDAO = new CRM_Financial_DAO_PaymentProcessorType();
451 $this->_paymentProcessorDAO->id = $values['payment_processor_type_id'];
452 $this->_paymentProcessorDAO->find(TRUE);
453 }
454 $this->updatePaymentProcessor($values, $domainID, FALSE);
455 $this->updatePaymentProcessor($values, $domainID, TRUE);
456
457 $processor = civicrm_api3('payment_processor', 'getsingle', ['name' => $values['name'], 'is_test' => 0]);
458 $errors = Civi\Payment\System::singleton()->checkProcessorConfig($processor);
459 if ($errors) {
460 CRM_Core_Session::setStatus($errors, 'Payment processor configuration invalid', 'error');
461 Civi::log()->error('Payment processor configuration invalid: ' . $errors);
462 CRM_Core_Session::singleton()->pushUserContext($this->refreshURL);
463 }
464 else {
465 CRM_Core_Session::setStatus(ts('Payment processor %1 has been saved.', [1 => "<em>{$values['name']}</em>"]), ts('Saved'), 'success');
466 }
467 }
468
469 /**
470 * Save a payment processor.
471 *
472 * @param array $values
473 * @param int $domainID
474 * @param bool $test
475 *
476 * @throws \CiviCRM_API3_Exception
477 */
478 public function updatePaymentProcessor(&$values, $domainID, $test) {
479 if ($test) {
480 foreach (['user_name', 'password', 'signature', 'url_site', 'url_recur', 'url_api', 'url_button', 'subject'] as $field) {
481 $values[$field] = empty($values["test_{$field}"]) ? CRM_Utils_Array::value($field, $values) : $values["test_{$field}"];
482 }
483 }
484 if (!empty($values['accept_credit_cards'])) {
485 $creditCards = [];
486 $accptedCards = array_keys($values['accept_credit_cards']);
487 $creditCardTypes = CRM_Contribute_PseudoConstant::creditCard();
488 foreach ($creditCardTypes as $type => $val) {
489 if (in_array($type, $accptedCards)) {
490 $creditCards[$type] = $creditCardTypes[$type];
491 }
492 }
493 $creditCards = json_encode($creditCards);
494 }
495 else {
496 $creditCards = "NULL";
497 }
498 $params = array_merge([
499 'id' => $test ? $this->_testID : $this->_id,
500 'domain_id' => $domainID,
501 'is_test' => $test,
502 'is_active' => 0,
503 'is_default' => 0,
504 'is_recur' => $this->_paymentProcessorDAO->is_recur,
505 'billing_mode' => $this->_paymentProcessorDAO->billing_mode,
506 'class_name' => $this->_paymentProcessorDAO->class_name,
507 'payment_type' => $this->_paymentProcessorDAO->payment_type,
508 'payment_instrument_id' => $this->_paymentProcessorDAO->payment_instrument_id,
509 'financial_account_id' => $values['financial_account_id'],
510 'accepted_credit_cards' => $creditCards,
511 ], $values);
512
513 civicrm_api3('PaymentProcessor', 'create', $params);
514 }
515
516 }