Merge pull request #15815 from artfulrobot/issue-1108-fix-unsubscribe
[civicrm-core.git] / CRM / Extension / Manager / Payment.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 * This class stores logic for managing CiviCRM extensions.
14 *
15 * @package CRM
16 * @copyright CiviCRM LLC https://civicrm.org/licensing
17 */
18 class CRM_Extension_Manager_Payment extends CRM_Extension_Manager_Base {
19
20 /**
21 * @var CRM_Extension_Mapper
22 */
23 protected $mapper;
24
25 /**
26 * @param CRM_Extension_Mapper $mapper
27 */
28 public function __construct(CRM_Extension_Mapper $mapper) {
29 parent::__construct(TRUE);
30 $this->mapper = $mapper;
31 }
32
33 /**
34 * @inheritDoc
35 *
36 * @param CRM_Extension_Info $info
37 */
38 public function onPreInstall(CRM_Extension_Info $info) {
39 $paymentProcessorTypes = $this->_getAllPaymentProcessorTypes('class_name');
40
41 if (array_key_exists($info->key, $paymentProcessorTypes)) {
42 CRM_Core_Error::fatal(ts('This payment processor type is already installed.'));
43 }
44
45 $ppByName = $this->_getAllPaymentProcessorTypes('name');
46 if (array_key_exists($info->name, $ppByName)) {
47 CRM_Core_Error::fatal(ts('This payment processor type already exists.'));
48 }
49
50 $dao = new CRM_Financial_DAO_PaymentProcessorType();
51
52 $dao->is_active = 1;
53 $dao->class_name = trim($info->key);
54 $dao->title = trim($info->name) . ' (' . trim($info->key) . ')';
55 $dao->name = trim($info->name);
56 $dao->description = trim($info->description);
57
58 $dao->user_name_label = trim($info->typeInfo['userNameLabel']);
59 $dao->password_label = trim($info->typeInfo['passwordLabel']);
60 $dao->signature_label = trim($info->typeInfo['signatureLabel']);
61 $dao->subject_label = trim($info->typeInfo['subjectLabel']);
62 $dao->url_site_default = trim($info->typeInfo['urlSiteDefault']);
63 $dao->url_api_default = trim($info->typeInfo['urlApiDefault']);
64 $dao->url_recur_default = trim($info->typeInfo['urlRecurDefault']);
65 $dao->url_site_test_default = trim($info->typeInfo['urlSiteTestDefault']);
66 $dao->url_api_test_default = trim($info->typeInfo['urlApiTestDefault']);
67 $dao->url_recur_test_default = trim($info->typeInfo['urlRecurTestDefault']);
68 $dao->url_button_default = trim($info->typeInfo['urlButtonDefault']);
69 $dao->url_button_test_default = trim($info->typeInfo['urlButtonTestDefault']);
70
71 switch (trim($info->typeInfo['billingMode'])) {
72 case 'form':
73 $dao->billing_mode = CRM_Core_Payment::BILLING_MODE_FORM;
74 break;
75
76 case 'button':
77 $dao->billing_mode = CRM_Core_Payment::BILLING_MODE_BUTTON;
78 break;
79
80 case 'notify':
81 $dao->billing_mode = CRM_Core_Payment::BILLING_MODE_NOTIFY;
82 break;
83
84 default:
85 CRM_Core_Error::fatal(ts('Billing mode in info file has wrong value.'));
86 }
87
88 $dao->is_recur = trim($info->typeInfo['isRecur']);
89 $dao->payment_type = trim($info->typeInfo['paymentType']);
90
91 $dao->save();
92 }
93
94 /**
95 * @inheritDoc
96 *
97 * @param CRM_Extension_Info $info
98 */
99 public function onPostInstall(CRM_Extension_Info $info) {
100 $this->_runPaymentHook($info, 'install');
101 }
102
103 /**
104 * @inheritDoc
105 *
106 * @param CRM_Extension_Info $info
107 */
108 public function onPreUninstall(CRM_Extension_Info $info) {
109 $paymentProcessorTypes = $this->_getAllPaymentProcessorTypes('class_name');
110 if (!array_key_exists($info->key, $paymentProcessorTypes)) {
111 CRM_Core_Error::fatal(ts('This payment processor type is not registered.'));
112 }
113
114 $dao = new CRM_Financial_DAO_PaymentProcessor();
115 $dao->payment_processor_type_id = $paymentProcessorTypes[$info->key];
116 $dao->find();
117 while ($dao->fetch()) {
118 throw new CRM_Extension_Exception_DependencyException('payment');
119 }
120
121 $this->_runPaymentHook($info, 'uninstall');
122 return CRM_Financial_BAO_PaymentProcessorType::del($paymentProcessorTypes[$info->key]);
123 }
124
125 /**
126 * @inheritDoc
127 *
128 * @param CRM_Extension_Info $info
129 */
130 public function onPreDisable(CRM_Extension_Info $info) {
131 // HMM? // if ($this->type == 'payment' && $this->status != 'missing') {
132 $this->_runPaymentHook($info, 'disable');
133
134 $paymentProcessorTypes = $this->_getAllPaymentProcessorTypes('class_name');
135 CRM_Financial_BAO_PaymentProcessorType::setIsActive($paymentProcessorTypes[$info->key], 0);
136 }
137
138 /**
139 * @inheritDoc
140 *
141 * @param CRM_Extension_Info $info
142 */
143 public function onPreEnable(CRM_Extension_Info $info) {
144 $paymentProcessorTypes = $this->_getAllPaymentProcessorTypes('class_name');
145 CRM_Financial_BAO_PaymentProcessorType::setIsActive($paymentProcessorTypes[$info->key], 1);
146 }
147
148 /**
149 * @inheritDoc
150 *
151 * @param CRM_Extension_Info $info
152 */
153 public function onPostEnable(CRM_Extension_Info $info) {
154 // HMM? // if ($this->type == 'payment' && $this->status != 'missing') {
155 $this->_runPaymentHook($info, 'enable');
156 }
157
158 /**
159 * @param string $attr
160 * The attribute used to key the array.
161 * @return array
162 * ($attr => $id)
163 */
164 private function _getAllPaymentProcessorTypes($attr) {
165 $ppt = [];
166 $dao = new CRM_Financial_DAO_PaymentProcessorType();
167 $dao->find();
168 while ($dao->fetch()) {
169 $ppt[$dao->$attr] = $dao->id;
170 }
171 return $ppt;
172 }
173
174 /**
175 * Run hooks in the payment processor class.
176 * Load requested payment processor and call the method specified.
177 *
178 * @param CRM_Extension_Info $info
179 * @param string $method
180 * The method to call in the payment processor class.
181 */
182 private function _runPaymentHook(CRM_Extension_Info $info, $method) {
183 // Not concerned about performance at this stage, as these are seldom performed tasks
184 // (payment processor enable/disable/install/uninstall). May wish to implement some
185 // kind of registry/caching system if more hooks are added.
186
187 try {
188 $paymentClass = $this->mapper->keyToClass($info->key, 'payment');
189 $file = $this->mapper->classToPath($paymentClass);
190 if (!file_exists($file)) {
191 CRM_Core_Session::setStatus(ts('Failed to load file (%3) for payment processor (%1) while running "%2"', [
192 1 => $info->key,
193 2 => $method,
194 3 => $file,
195 ]), '', 'error');
196 return;
197 }
198 else {
199 require_once $file;
200 }
201 }
202 catch (CRM_Extension_Exception $e) {
203 CRM_Core_Session::setStatus(ts('Failed to determine file path for payment processor (%1) while running "%2"', [
204 1 => $info->key,
205 2 => $method,
206 ]), '', 'error');
207 return;
208 }
209
210 $processorDAO = CRM_Core_DAO::executeQuery(
211 " SELECT pp.id, ppt.class_name
212 FROM civicrm_extension ext
213 INNER JOIN civicrm_payment_processor_type ppt
214 ON ext.name = ppt.name
215 LEFT JOIN civicrm_payment_processor pp
216 ON ppt.id = pp.payment_processor_type_id
217 WHERE ext.type = 'payment'
218 AND ext.full_name = %1
219 ",
220 [
221 1 => [$info->key, 'String'],
222 ]
223 );
224
225 while ($processorDAO->fetch()) {
226 $class_name = $processorDAO->class_name;
227 $processor_id = $processorDAO->id;
228 }
229
230 if (empty($class_name)) {
231 CRM_Core_Error::fatal("Unable to find payment processor in " . __CLASS__ . '::' . __METHOD__);
232 }
233
234 // In the case of uninstall, check for instances of PP first.
235 // Don't run hook if any are found.
236 if ($method == 'uninstall' && $processor_id > 0) {
237 return;
238 }
239
240 switch ($method) {
241 case 'install':
242 case 'uninstall':
243 case 'enable':
244 case 'disable':
245 // Instantiate PP - the getClass function allows us to do this when no payment processor instances exist.
246 $processorInstance = Civi\Payment\System::singleton()->getByClass($class_name);
247
248 // Does PP implement this method, and can we call it?
249 if (method_exists($processorInstance, $method) && is_callable([
250 $processorInstance,
251 $method,
252 ])) {
253 // If so, call it ...
254 $processorInstance->$method();
255 }
256 break;
257
258 default:
259 CRM_Core_Session::setStatus(ts("Unrecognized payment hook (%1) in %2::%3",
260 [1 => $method, 2 => __CLASS__, 3 => __METHOD__]),
261 '', 'error');
262 }
263 }
264
265 }