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