Commit | Line | Data |
---|---|---|
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 | 11 | |
1ba7b504 | 12 | use Civi\Api4\Contribution; |
13 | ||
6a488035 | 14 | /** |
1d86918a | 15 | * Class CRM_Core_Payment_BaseIPN. |
6a488035 TO |
16 | */ |
17 | class CRM_Core_Payment_BaseIPN { | |
18 | ||
518fa0ee | 19 | public static $_now = NULL; |
8196c759 | 20 | |
c8aa607b | 21 | /** |
22 | * Input parameters from payment processor. Store these so that | |
23 | * the code does not need to keep retrieving from the http request | |
24 | * @var array | |
25 | */ | |
be2fb01f | 26 | protected $_inputParameters = []; |
c8aa607b | 27 | |
59cdadfc | 28 | /** |
29 | * Only used by AuthorizeNetIPN. | |
518fa0ee | 30 | * @var bool |
59cdadfc | 31 | * |
32 | * @deprecated | |
33 | * | |
59cdadfc | 34 | */ |
937cf542 EM |
35 | protected $_isRecurring = FALSE; |
36 | ||
59cdadfc | 37 | /** |
38 | * Only used by AuthorizeNetIPN. | |
518fa0ee | 39 | * @var bool |
59cdadfc | 40 | * |
41 | * @deprecated | |
42 | * | |
59cdadfc | 43 | */ |
937cf542 | 44 | protected $_isFirstOrLastRecurringPayment = FALSE; |
353ffa53 | 45 | |
8196c759 | 46 | /** |
fe482240 | 47 | * Constructor. |
8196c759 | 48 | */ |
00be9182 | 49 | public function __construct() { |
6a488035 TO |
50 | self::$_now = date('YmdHis'); |
51 | } | |
52 | ||
c8aa607b | 53 | /** |
fe482240 | 54 | * Store input array on the class. |
77b97be7 | 55 | * |
c8aa607b | 56 | * @param array $parameters |
77b97be7 EM |
57 | * |
58 | * @throws CRM_Core_Exception | |
c8aa607b | 59 | */ |
00be9182 | 60 | public function setInputParameters($parameters) { |
22e263ad | 61 | if (!is_array($parameters)) { |
cc0c30cc | 62 | throw new CRM_Core_Exception('Invalid input parameters'); |
c8aa607b | 63 | } |
64 | $this->_inputParameters = $parameters; | |
65 | } | |
353ffa53 | 66 | |
8196c759 | 67 | /** |
1d86918a EM |
68 | * Validate incoming data. |
69 | * | |
70 | * This function is intended to ensure that incoming data matches | |
8196c759 | 71 | * It provides a form of pseudo-authentication - by checking the calling fn already knows |
72 | * the correct contact id & contribution id (this can be problematic when that has changed in | |
73 | * the meantime for transactions that are delayed & contacts are merged in-between. e.g | |
74 | * Paypal allows you to resend Instant Payment Notifications if you, for example, moved site | |
75 | * and didn't update your IPN URL. | |
76 | * | |
6a0b768e TO |
77 | * @param array $input |
78 | * Interpreted values from the values returned through the IPN. | |
79 | * @param array $ids | |
80 | * More interpreted values (ids) from the values returned through the IPN. | |
81 | * @param array $objects | |
82 | * An empty array that will be populated with loaded object. | |
83 | * @param bool $required | |
84 | * Boolean Return FALSE if the relevant objects don't exist. | |
85 | * @param int $paymentProcessorID | |
86 | * Id of the payment processor ID in use. | |
1d86918a | 87 | * |
727e6481 | 88 | * @deprecated |
89 | * | |
5c766a0b | 90 | * @return bool |
8196c759 | 91 | */ |
ad64fa72 | 92 | public function validateData($input, &$ids, &$objects, $required = TRUE, $paymentProcessorID = NULL) { |
727e6481 | 93 | CRM_Core_Error::deprecatedFunctionWarning('unused'); |
21843539 | 94 | // Check if the contribution exists |
6a488035 | 95 | // make sure contribution exists and is valid |
5a9c68ac | 96 | $contribution = new CRM_Contribute_BAO_Contribution(); |
6a488035 TO |
97 | $contribution->id = $ids['contribution']; |
98 | if (!$contribution->find(TRUE)) { | |
eb0fd0ce | 99 | throw new CRM_Core_Exception('Failure: Could not find contribution record for ' . (int) $contribution->id, NULL, ['context' => "Could not find contribution record: {$contribution->id} in IPN request: " . print_r($input, TRUE)]); |
6a488035 | 100 | } |
21843539 SL |
101 | |
102 | // make sure contact exists and is valid | |
103 | // use the contact id from the contribution record as the id in the IPN may not be valid anymore. | |
104 | $contact = new CRM_Contact_BAO_Contact(); | |
105 | $contact->id = $contribution->contact_id; | |
106 | $contact->find(TRUE); | |
107 | if ($contact->id != $ids['contact']) { | |
108 | // If the ids do not match then it is possible the contact id in the IPN has been merged into another contact which is why we use the contact_id from the contribution | |
109 | CRM_Core_Error::debug_log_message("Contact ID in IPN {$ids['contact']} not found but contact_id found in contribution {$contribution->contact_id} used instead"); | |
110 | echo "WARNING: Could not find contact record: {$ids['contact']}<p>"; | |
111 | $ids['contact'] = $contribution->contact_id; | |
112 | } | |
113 | ||
114 | if (!empty($ids['contributionRecur'])) { | |
115 | $contributionRecur = new CRM_Contribute_BAO_ContributionRecur(); | |
116 | $contributionRecur->id = $ids['contributionRecur']; | |
117 | if (!$contributionRecur->find(TRUE)) { | |
118 | CRM_Core_Error::debug_log_message("Could not find contribution recur record: {$ids['ContributionRecur']} in IPN request: " . print_r($input, TRUE)); | |
119 | echo "Failure: Could not find contribution recur record: {$ids['ContributionRecur']}<p>"; | |
120 | return FALSE; | |
121 | } | |
122 | } | |
123 | ||
6a488035 TO |
124 | $objects['contact'] = &$contact; |
125 | $objects['contribution'] = &$contribution; | |
d0488f03 | 126 | |
127 | // CRM-19478: handle oddity when p=null is set in place of contribution page ID, | |
128 | if (!empty($ids['contributionPage']) && !is_numeric($ids['contributionPage'])) { | |
129 | // We don't need to worry if about removing contribution page id as it will be set later in | |
130 | // CRM_Contribute_BAO_Contribution::loadRelatedObjects(..) using $objects['contribution']->contribution_page_id | |
131 | unset($ids['contributionPage']); | |
132 | } | |
133 | ||
6a488035 TO |
134 | if (!$this->loadObjects($input, $ids, $objects, $required, $paymentProcessorID)) { |
135 | return FALSE; | |
136 | } | |
6a488035 TO |
137 | return TRUE; |
138 | } | |
139 | ||
8196c759 | 140 | /** |
fe482240 | 141 | * Load objects related to contribution. |
6a488035 | 142 | * |
f2c927f5 | 143 | * @deprecated |
144 | * | |
6a488035 | 145 | * @input array information from Payment processor |
dd244018 | 146 | * |
3aaa68fb | 147 | * @param array $input |
8196c759 | 148 | * @param array $ids |
149 | * @param array $objects | |
6a0b768e TO |
150 | * @param bool $required |
151 | * @param int $paymentProcessorID | |
dd244018 | 152 | * |
3aaa68fb | 153 | * @return bool|array |
49ed4888 | 154 | * @throws \CRM_Core_Exception |
6a488035 | 155 | */ |
49ed4888 | 156 | public function loadObjects($input, &$ids, &$objects, $required, $paymentProcessorID) { |
f2c927f5 | 157 | CRM_Core_Error::deprecatedFunctionWarning('use api methods in ipn'); |
eb0fd0ce | 158 | $contribution = &$objects['contribution']; |
6a488035 | 159 | $ids['paymentProcessor'] = $paymentProcessorID; |
49ed4888 | 160 | $success = $contribution->loadRelatedObjects($input, $ids); |
6a488035 TO |
161 | $objects = array_merge($objects, $contribution->_relatedObjects); |
162 | return $success; | |
163 | } | |
164 | ||
8196c759 | 165 | /** |
fe482240 | 166 | * Set contribution to failed. |
28de42d1 | 167 | * |
8196c759 | 168 | * @param array $objects |
28de42d1 | 169 | * |
61470a1a | 170 | * @deprecated use the api. |
171 | * | |
5c766a0b | 172 | * @return bool |
09cafd47 | 173 | * @throws \CiviCRM_API3_Exception|\CRM_Core_Exception |
8196c759 | 174 | */ |
09cafd47 | 175 | public function failed($objects) { |
61470a1a | 176 | CRM_Core_Error::deprecatedFunctionWarning('use the api'); |
6a488035 | 177 | $contribution = &$objects['contribution']; |
be2fb01f | 178 | $memberships = []; |
a7488080 | 179 | if (!empty($objects['membership'])) { |
6a488035 TO |
180 | $memberships = &$objects['membership']; |
181 | if (is_numeric($memberships)) { | |
be2fb01f | 182 | $memberships = [$objects['membership']]; |
6a488035 TO |
183 | } |
184 | } | |
185 | ||
4586a81f | 186 | $addLineItems = empty($contribution->id); |
6a488035 | 187 | $participant = &$objects['participant']; |
4586a81f | 188 | $contribution->contribution_status_id = CRM_Core_PseudoConstant::getKey('CRM_Contribute_DAO_Contribution', 'contribution_status_id', 'Failed'); |
6a488035 TO |
189 | $contribution->save(); |
190 | ||
28de42d1 | 191 | // Add line items for recurring payments. |
a7488080 | 192 | if (!empty($objects['contributionRecur']) && $objects['contributionRecur']->id && $addLineItems) { |
e577770c | 193 | CRM_Contribute_BAO_ContributionRecur::addRecurLineItems($objects['contributionRecur']->id, $contribution); |
6a488035 TO |
194 | } |
195 | ||
391ccd27 | 196 | if (!empty($memberships)) { |
197 | foreach ($memberships as $membership) { | |
198 | // @fixme Should we cancel only Pending memberships? per cancelled() | |
199 | $this->cancelMembership($membership, $membership->status_id, FALSE); | |
6a488035 | 200 | } |
391ccd27 | 201 | } |
d63f4fc3 | 202 | |
391ccd27 | 203 | if ($participant) { |
204 | $this->cancelParticipant($participant->id); | |
6a488035 TO |
205 | } |
206 | ||
e44c82e1 | 207 | Civi::log()->debug("Setting contribution status to Failed"); |
6a488035 TO |
208 | return TRUE; |
209 | } | |
210 | ||
8196c759 | 211 | /** |
fe482240 | 212 | * Handled pending contribution status. |
1d86918a | 213 | * |
0e89200f | 214 | * @deprecated |
215 | * | |
8196c759 | 216 | * @param array $objects |
217 | * @param object $transaction | |
1d86918a | 218 | * |
5c766a0b | 219 | * @return bool |
8196c759 | 220 | */ |
00be9182 | 221 | public function pending(&$objects, &$transaction) { |
0e89200f | 222 | CRM_Core_Error::deprecatedFunctionWarning('This function will be removed at some point'); |
6a488035 | 223 | $transaction->commit(); |
0e89200f | 224 | Civi::log()->debug('Returning since contribution status is Pending'); |
225 | echo 'Success: Returning since contribution status is pending<p>'; | |
6a488035 TO |
226 | return TRUE; |
227 | } | |
228 | ||
6c786a9b | 229 | /** |
1d86918a EM |
230 | * Process cancelled payment outcome. |
231 | * | |
6ff69e9c | 232 | * @deprecated The intended replacement code is |
233 | * | |
234 | * Contribution::update(FALSE)->setValues([ | |
235 | * 'cancel_date' => 'now', | |
236 | * 'contribution_status_id:name' => 'Cancelled', | |
237 | * ])->addWhere('id', '=', $contribution->id)->execute(); | |
238 | * | |
3aaa68fb | 239 | * @param array $objects |
6c786a9b EM |
240 | * |
241 | * @return bool | |
01ebb9d3 | 242 | * @throws \CiviCRM_API3_Exception|\CRM_Core_Exception |
6c786a9b | 243 | */ |
01ebb9d3 | 244 | public function cancelled($objects) { |
a201e208 | 245 | CRM_Core_Error::deprecatedFunctionWarning('Use Contribution create api to cancel the contribution'); |
6a488035 | 246 | $contribution = &$objects['contribution']; |
6a488035 | 247 | |
6a488035 | 248 | if (empty($contribution->id)) { |
6ff69e9c | 249 | // This code is believed to be unreachable. |
250 | // this entire function is due to be deprecated in the near future so | |
251 | // this code will live in a deprecated function until it gets removed. | |
6a488035 | 252 | $addLineItems = TRUE; |
1ba7b504 | 253 | // CRM-15546 |
254 | $contributionStatuses = CRM_Core_PseudoConstant::get('CRM_Contribute_DAO_Contribution', 'contribution_status_id', [ | |
255 | 'labelColumn' => 'name', | |
256 | 'flip' => 1, | |
257 | ]); | |
258 | $contribution->contribution_status_id = $contributionStatuses['Cancelled']; | |
259 | $contribution->cancel_date = self::$_now; | |
260 | $contribution->save(); | |
261 | // Add line items for recurring payments. | |
262 | if (!empty($objects['contributionRecur']) && $objects['contributionRecur']->id && $addLineItems) { | |
263 | CRM_Contribute_BAO_ContributionRecur::addRecurLineItems($objects['contributionRecur']->id, $contribution); | |
264 | } | |
6ff69e9c | 265 | $memberships = []; |
266 | if (!empty($objects['membership'])) { | |
267 | $memberships = &$objects['membership']; | |
268 | if (is_numeric($memberships)) { | |
269 | $memberships = [$objects['membership']]; | |
270 | } | |
271 | } | |
272 | if (!empty($memberships)) { | |
273 | foreach ($memberships as $membership) { | |
274 | if ($membership) { | |
275 | $this->cancelMembership($membership, $membership->status_id); | |
276 | } | |
277 | } | |
278 | } | |
a201e208 | 279 | $participant = &$objects['participant']; |
280 | ||
281 | if ($participant) { | |
282 | $this->cancelParticipant($participant->id); | |
283 | } | |
6a488035 | 284 | } |
1ba7b504 | 285 | else { |
286 | Contribution::update(FALSE)->setValues([ | |
287 | 'cancel_date' => 'now', | |
288 | 'contribution_status_id:name' => 'Cancelled', | |
289 | ])->addWhere('id', '=', $contribution->id)->execute(); | |
6a488035 | 290 | } |
4eba8926 | 291 | |
e44c82e1 | 292 | Civi::log()->debug("Setting contribution status to Cancelled"); |
6a488035 TO |
293 | return TRUE; |
294 | } | |
295 | ||
6c786a9b | 296 | /** |
1d86918a EM |
297 | * Rollback unhandled outcomes. |
298 | * | |
85ce114d | 299 | * @deprecated |
300 | * | |
3aaa68fb | 301 | * @param array $objects |
302 | * @param CRM_Core_Transaction $transaction | |
6c786a9b EM |
303 | * |
304 | * @return bool | |
305 | */ | |
00be9182 | 306 | public function unhandled(&$objects, &$transaction) { |
85ce114d | 307 | CRM_Core_Error::deprecatedFunctionWarning('This function will be removed at some point'); |
6a488035 | 308 | $transaction->rollback(); |
85ce114d | 309 | Civi::log()->debug('Returning since contribution status is not handled'); |
310 | echo 'Failure: contribution status is not handled<p>'; | |
6a488035 TO |
311 | return FALSE; |
312 | } | |
313 | ||
56c7d914 MWMC |
314 | /** |
315 | * Logic to cancel a participant record when the related contribution changes to failed/cancelled. | |
316 | * @todo This is part of a bigger refactor for dev/core/issues/927 - "duplicate" functionality exists in CRM_Contribute_BAO_Contribution::cancel() | |
317 | * | |
a201e208 | 318 | * @deprecated |
319 | * | |
56c7d914 MWMC |
320 | * @param $participantID |
321 | * | |
322 | * @throws \CiviCRM_API3_Exception | |
323 | */ | |
324 | private function cancelParticipant($participantID) { | |
325 | // @fixme https://lab.civicrm.org/dev/core/issues/927 Cancelling membership etc is not desirable for all use-cases and we should be able to disable it | |
326 | $participantParams['id'] = $participantID; | |
327 | $participantParams['status_id'] = 'Cancelled'; | |
328 | civicrm_api3('Participant', 'create', $participantParams); | |
329 | } | |
330 | ||
331 | /** | |
332 | * Logic to cancel a membership record when the related contribution changes to failed/cancelled. | |
333 | * @todo This is part of a bigger refactor for dev/core/issues/927 - "duplicate" functionality exists in CRM_Contribute_BAO_Contribution::cancel() | |
334 | * @param \CRM_Member_BAO_Membership $membership | |
335 | * @param int $membershipStatusID | |
aec7c57d | 336 | * @param bool $onlyCancelPendingMembership |
56c7d914 MWMC |
337 | * Do we only cancel pending memberships? OR memberships in any status? (see CRM-18688) |
338 | * @fixme Historically failed() cancelled membership in any status, cancelled() cancelled only pending memberships so we retain that behaviour for now. | |
6ff69e9c | 339 | * @deprecated |
56c7d914 MWMC |
340 | */ |
341 | private function cancelMembership($membership, $membershipStatusID, $onlyCancelPendingMembership = TRUE) { | |
6ff69e9c | 342 | CRM_Core_Error::deprecatedFunctionWarning('use the api'); |
56c7d914 MWMC |
343 | // @fixme https://lab.civicrm.org/dev/core/issues/927 Cancelling membership etc is not desirable for all use-cases and we should be able to disable it |
344 | // Cancel only Pending memberships | |
345 | $pendingMembershipStatusId = CRM_Core_PseudoConstant::getKey('CRM_Member_BAO_Membership', 'status_id', 'Pending'); | |
346 | if (($membershipStatusID == $pendingMembershipStatusId) || ($onlyCancelPendingMembership == FALSE)) { | |
347 | $cancelledMembershipStatusId = CRM_Core_PseudoConstant::getKey('CRM_Member_BAO_Membership', 'status_id', 'Cancelled'); | |
348 | ||
349 | $membership->status_id = $cancelledMembershipStatusId; | |
350 | $membership->save(); | |
351 | ||
352 | $params = ['status_id' => $cancelledMembershipStatusId]; | |
353 | CRM_Member_BAO_Membership::updateRelatedMemberships($membership->id, $params); | |
354 | ||
355 | // @todo Convert the above to API | |
356 | // $membershipParams = [ | |
357 | // 'id' => $membership->id, | |
358 | // 'status_id' => $cancelledMembershipStatusId, | |
359 | // ]; | |
360 | // civicrm_api3('Membership', 'create', $membershipParams); | |
361 | // CRM_Member_BAO_Membership::updateRelatedMemberships($membershipParams['id'], ['status_id' => $cancelledMembershipStatusId]); | |
362 | } | |
363 | ||
364 | } | |
365 | ||
6c786a9b | 366 | /** |
4a1ba425 | 367 | * @deprecated |
368 | * | |
3ccde016 | 369 | * Jumbled up function. |
1d86918a | 370 | * |
3ccde016 EM |
371 | * The purpose of this function is to transition a pending transaction to Completed including updating any |
372 | * related entities. | |
373 | * | |
374 | * It has been overloaded to also add recurring transactions to the database, cloning the original transaction and | |
375 | * updating related entities. | |
376 | * | |
377 | * It is recommended to avoid calling this function directly and call the api functions: | |
378 | * - contribution.completetransaction | |
379 | * - contribution.repeattransaction | |
380 | * | |
381 | * These functions are the focus of testing efforts and more accurately reflect the division of roles | |
382 | * (the job of the IPN class is to determine the outcome, transaction id, invoice id & to validate the source | |
383 | * and from there it should be possible to pass off transaction management.) | |
384 | * | |
385 | * This function has been problematic for some time but there are now several tests via the api_v3_Contribution test | |
386 | * and the Paypal & Authorize.net IPN tests so any refactoring should be done in conjunction with those. | |
387 | * | |
5ca657dd | 388 | * This function needs to have the 'body' moved to the CRM_Contribute_BAO_Contribute class and to undergo |
3ccde016 EM |
389 | * refactoring to separate the complete transaction and repeat transaction functionality into separate functions with |
390 | * a shared function that updates related components. | |
391 | * | |
392 | * Note that it is not necessary payment processor extension to implement an IPN class now. In general the code on the | |
393 | * IPN class is better accessed through the api which de-jumbles it a bit. | |
394 | * | |
395 | * e.g the payment class can have a function like (based on Omnipay extension): | |
396 | * | |
397 | * public function handlePaymentNotification() { | |
398 | * $response = $this->getValidatedOutcome(); | |
399 | * if ($response->isSuccessful()) { | |
400 | * try { | |
401 | * // @todo check if it is a repeat transaction & call repeattransaction instead. | |
402 | * civicrm_api3('contribution', 'completetransaction', array('id' => $this->transaction_id)); | |
403 | * } | |
404 | * catch (CiviCRM_API3_Exception $e) { | |
405 | * if (!stristr($e->getMessage(), 'Contribution already completed')) { | |
406 | * $this->handleError('error', $this->transaction_id . $e->getMessage(), 'ipn_completion', 9000, 'An error may | |
407 | * have occurred. Please check your receipt is correct'); | |
408 | * $this->redirectOrExit('success'); | |
409 | * } | |
410 | * elseif ($this->transaction_id) { | |
411 | * civicrm_api3('contribution', 'create', array('id' => $this->transaction_id, 'contribution_status_id' => | |
412 | * 'Failed')); | |
413 | * } | |
414 | * | |
415 | * @param array $input | |
416 | * @param array $ids | |
417 | * @param array $objects | |
e44c82e1 MWMC |
418 | * |
419 | * @throws \CRM_Core_Exception | |
420 | * @throws \CiviCRM_API3_Exception | |
6c786a9b | 421 | */ |
c7497653 | 422 | public function completeTransaction($input, $ids, $objects) { |
6067e16b | 423 | CRM_Core_Error::deprecatedFunctionWarning('Use Payment.create api'); |
5f31e5f4 | 424 | CRM_Contribute_BAO_Contribution::completeOrder($input, !empty($objects['contributionRecur']) ? $objects['contributionRecur']->id : NULL, $objects['contribution']->id ?? NULL); |
6a488035 TO |
425 | } |
426 | ||
6c786a9b | 427 | /** |
e9a11021 | 428 | * @deprecated |
1d86918a EM |
429 | * Get site billing ID. |
430 | * | |
431 | * @param array $ids | |
6c786a9b EM |
432 | * |
433 | * @return bool | |
434 | */ | |
00be9182 | 435 | public function getBillingID(&$ids) { |
e9a11021 | 436 | CRM_Core_Error::deprecatedFunctionWarning('CRM_Core_BAO_LocationType::getBilling()'); |
b576d770 | 437 | $ids['billing'] = CRM_Core_BAO_LocationType::getBilling(); |
6a488035 | 438 | if (!$ids['billing']) { |
be2fb01f | 439 | CRM_Core_Error::debug_log_message(ts('Please set a location type of %1', [1 => 'Billing'])); |
6a488035 TO |
440 | echo "Failure: Could not find billing location type<p>"; |
441 | return FALSE; | |
442 | } | |
443 | return TRUE; | |
444 | } | |
445 | ||
c490a46a | 446 | /** |
db59bb73 EM |
447 | * @deprecated |
448 | * | |
6626a693 | 449 | * @todo confirm this function is not being used by any payment processor outside core & remove. |
450 | * | |
1d86918a | 451 | * Note that the compose message part has been moved to contribution |
6a488035 TO |
452 | * In general LoadObjects is called first to get the objects but the composeMessageArray function now calls it |
453 | * | |
6a0b768e TO |
454 | * @param array $input |
455 | * Incoming data from Payment processor. | |
456 | * @param array $ids | |
457 | * Related object IDs. | |
3aaa68fb | 458 | * @param array $objects |
5909727d | 459 | * |
e44c82e1 | 460 | * @throws \CiviCRM_API3_Exception |
6c786a9b | 461 | */ |
5909727d | 462 | public function sendMail($input, $ids, $objects) { |
463 | CRM_Core_Error::deprecatedFunctionWarning('this should be done via completetransaction api'); | |
464 | civicrm_api3('Contribution', 'sendconfirmation', [ | |
465 | 'id' => $objects['contribution']->id, | |
466 | ]); | |
6a488035 TO |
467 | } |
468 | ||
b2b0530a | 469 | } |