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