Merge pull request #449 from pradpnayak/CRM-12333
[civicrm-core.git] / CRM / Core / Payment / BaseIPN.php
CommitLineData
6a488035
TO
1<?php
2/*
3 +--------------------------------------------------------------------+
4 | CiviCRM version 4.3 |
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2013 |
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-2013
32 * $Id$
33 *
34 */
35class CRM_Core_Payment_BaseIPN {
36
37 static $_now = NULL;
38 function __construct() {
39 self::$_now = date('YmdHis');
40 }
41
42 function validateData(&$input, &$ids, &$objects, $required = TRUE, $paymentProcessorID = NULL) {
43
44 // make sure contact exists and is valid
45 $contact = new CRM_Contact_DAO_Contact();
46 $contact->id = $ids['contact'];
47 if (!$contact->find(TRUE)) {
48 CRM_Core_Error::debug_log_message("Could not find contact record: {$ids['contact']} in IPN request: ".print_r($input, TRUE));
49 echo "Failure: Could not find contact record: {$ids['contact']}<p>";
50 return FALSE;
51 }
52
53 // make sure contribution exists and is valid
54 $contribution = new CRM_Contribute_DAO_Contribution();
55 $contribution->id = $ids['contribution'];
56 if (!$contribution->find(TRUE)) {
57 CRM_Core_Error::debug_log_message("Could not find contribution record: {$contribution->id} in IPN request: ".print_r($input, TRUE));
58 echo "Failure: Could not find contribution record for {$contribution->id}<p>";
59 return FALSE;
60 }
61 $contribution->receive_date = CRM_Utils_Date::isoToMysql($contribution->receive_date);
62
63 $objects['contact'] = &$contact;
64 $objects['contribution'] = &$contribution;
65 if (!$this->loadObjects($input, $ids, $objects, $required, $paymentProcessorID)) {
66 return FALSE;
67 }
68
69 return TRUE;
70 }
71
72 function createContact(&$input, &$ids, &$objects) {
73 $params = array();
74 $billingID = $ids['billing'];
75 $lookup = array(
76 'first_name',
77 'last_name',
78 "street_address-{$billingID}",
79 "city-{$billingID}",
80 "state-{$billingID}",
81 "postal_code-{$billingID}",
82 "country-{$billingID}",
83 );
84 foreach ($lookup as $name) {
85 $params[$name] = $input[$name];
86 }
87 if (!empty($params)) {
88 // update contact record
89 $contact = CRM_Contact_BAO_Contact::createProfileContact($params, CRM_Core_DAO::$_nullArray, $ids['contact']);
90 }
91
92 return TRUE;
93 }
94
95 /*
96 * Load objects related to contribution
97 *
98 * @input array information from Payment processor
99 */
100 function loadObjects(&$input, &$ids, &$objects, $required, $paymentProcessorID, $error_handling = NULL) {
101 if (empty($error_handling)) {
102 // default options are that we log an error & echo it out
103 // note that we should refactor this error handling into error code @ some point
104 // but for now setting up enough separation so we can do unit tests
105 $error_handling = array(
106 'log_error' => 1,
107 'echo_error' => 1,
108 );
109 }
110 $ids['paymentProcessor'] = $paymentProcessorID;
111 if (is_a($objects['contribution'], 'CRM_Contribute_BAO_Contribution')) {
112 $contribution = &$objects['contribution'];
113 }
114 else {
115 //legacy support - functions are 'used' to be able to pass in a DAO
116 $contribution = new CRM_Contribute_BAO_Contribution();
117 $contribution->id = CRM_Utils_Array::value('contribution', $ids);
118 $contribution->find(TRUE);
119 $objects['contribution'] = &$contribution;
120 }
121 try {
122 $success = $contribution->loadRelatedObjects($input, $ids, $required);
123 }
124 catch(Exception$e) {
125 if (CRM_Utils_Array::value('log_error', $error_handling)) {
126 CRM_Core_Error::debug_log_message($e->getMessage());
127 }
128 if (CRM_Utils_Array::value('echo_error', $error_handling)) {
129 echo ($e->getMessage());
130 }
131 if (CRM_Utils_Array::value('return_error', $error_handling)) {
132 return array(
133 'is_error' => 1,
134 'error_message' => ($e->getMessage()),
135 );
136 }
137 }
138 $objects = array_merge($objects, $contribution->_relatedObjects);
139 return $success;
140 }
141
142 function failed(&$objects, &$transaction, $input = array()) {
143 $contribution = &$objects['contribution'];
144 $memberships = array();
145 if (CRM_Utils_Array::value('membership', $objects)) {
146 $memberships = &$objects['membership'];
147 if (is_numeric($memberships)) {
148 $memberships = array($objects['membership']);
149 }
150 }
151
152 $addLineItems = FALSE;
153 if (empty($contribution->id)) {
154 $addLineItems = TRUE;
155 }
156 $participant = &$objects['participant'];
157
158 $contributionStatus = CRM_Contribute_PseudoConstant::contributionStatus(NULL, 'name');
159 $contribution->contribution_status_id = array_search('Failed', $contributionStatus);
160 $contribution->save();
161
162 //add lineitems for recurring payments
163 if (CRM_Utils_Array::value('contributionRecur', $objects) && $objects['contributionRecur']->id && $addLineItems) {
164 $this->addrecurLineItems($objects['contributionRecur']->id, $contribution->id);
165 }
166
167 if (!CRM_Utils_Array::value('skipComponentSync', $input)) {
168 if (!empty($memberships)) {
169 foreach ($memberships as $membership) {
170 if ($membership) {
171 $membership->status_id = 4;
172 $membership->save();
d63f4fc3 173
6a488035
TO
174 //update related Memberships.
175 $params = array('status_id' => 4);
176 CRM_Member_BAO_Membership::updateRelatedMemberships($membership->id, $params);
177 }
178 }
179 }
d63f4fc3 180
6a488035
TO
181 if ($participant) {
182 $participant->status_id = 4;
183 $participant->save();
184 }
185 }
186
187 $transaction->commit();
188 CRM_Core_Error::debug_log_message("Setting contribution status to failed");
189 //echo "Success: Setting contribution status to failed<p>";
190 return TRUE;
191 }
192
193 function pending(&$objects, &$transaction) {
194 $transaction->commit();
195 CRM_Core_Error::debug_log_message("returning since contribution status is pending");
196 echo "Success: Returning since contribution status is pending<p>";
197 return TRUE;
198 }
199
200 function cancelled(&$objects, &$transaction, $input = array()) {
201 $contribution = &$objects['contribution'];
202 $memberships = &$objects['membership'];
203 if (is_numeric($memberships)) {
204 $memberships = array($objects['membership']);
205 }
206
207 $participant = &$objects['participant'];
208 $addLineItems = FALSE;
209 if (empty($contribution->id)) {
210 $addLineItems = TRUE;
211 }
212 $contribution->contribution_status_id = 3;
213 $contribution->cancel_date = self::$_now;
214 $contribution->cancel_reason = CRM_Utils_Array::value('reasonCode', $input);
215 $contribution->receive_date = CRM_Utils_Date::isoToMysql($contribution->receive_date);
216 $contribution->receipt_date = CRM_Utils_Date::isoToMysql($contribution->receipt_date);
217 $contribution->thankyou_date = CRM_Utils_Date::isoToMysql($contribution->thankyou_date);
218 $contribution->save();
219
220 //add lineitems for recurring payments
221 if (CRM_Utils_Array::value('contributionRecur', $objects) && $objects['contributionRecur']->id && $addLineItems) {
222 $this->addrecurLineItems($objects['contributionRecur']->id, $contribution->id);
223 }
224
225 if (!CRM_Utils_Array::value('skipComponentSync', $input)) {
226 if (!empty($memberships)) {
227 foreach ($memberships as $membership) {
228 if ($membership) {
229 $membership->status_id = 6;
230 $membership->save();
d63f4fc3 231
6a488035
TO
232 //update related Memberships.
233 $params = array('status_id' => 6);
234 CRM_Member_BAO_Membership::updateRelatedMemberships($membership->id, $params);
235 }
236 }
237 }
d63f4fc3 238
6a488035
TO
239 if ($participant) {
240 $participant->status_id = 4;
241 $participant->save();
242 }
243 }
244 $transaction->commit();
245 CRM_Core_Error::debug_log_message("Setting contribution status to cancelled");
246 //echo "Success: Setting contribution status to cancelled<p>";
247 return TRUE;
248 }
249
250 function unhandled(&$objects, &$transaction) {
251 $transaction->rollback();
252 // we dont handle this as yet
253 CRM_Core_Error::debug_log_message("returning since contribution status: $status is not handled");
254 echo "Failure: contribution status $status is not handled<p>";
255 return FALSE;
256 }
257
258 function completeTransaction(&$input, &$ids, &$objects, &$transaction, $recur = FALSE) {
259 $contribution = &$objects['contribution'];
260 $memberships = &$objects['membership'];
261 if (is_numeric($memberships)) {
262 $memberships = array($objects['membership']);
263 }
264 $participant = &$objects['participant'];
265 $event = &$objects['event'];
266 $changeToday = CRM_Utils_Array::value('trxn_date', $input, self::$_now);
267 $recurContrib = &$objects['contributionRecur'];
268
269 $values = array();
270 $source = NULL;
271 if ($input['component'] == 'contribute') {
272 if ($contribution->contribution_page_id) {
273 CRM_Contribute_BAO_ContributionPage::setValues($contribution->contribution_page_id, $values);
274 $source = ts('Online Contribution') . ': ' . $values['title'];
275 }
276 elseif ($recurContrib && $recurContrib->id) {
277 $contribution->contribution_page_id = NULL;
278 $values['amount'] = $recurContrib->amount;
279 $values['financial_type_id'] = $objects['contributionType']->id;
280 $values['title'] = $source = ts('Offline Recurring Contribution');
281 $values['is_email_receipt'] = $recurContrib->is_email_receipt;
282 $domainValues = CRM_Core_BAO_Domain::getNameAndEmail();
283 $values['receipt_from_name'] = $domainValues[0];
284 $values['receipt_from_email'] = $domainValues[1];
285 }
286
287 $contribution->source = $source;
288 if (CRM_Utils_Array::value('is_email_receipt', $values)) {
289 $contribution->receipt_date = self::$_now;
290 }
291
292 if (!empty($memberships)) {
293 $membershipsUpdate = array( );
294 foreach ($memberships as $membershipTypeIdKey => $membership) {
295 if ($membership) {
296 $format = '%Y%m%d';
297
298 $currentMembership = CRM_Member_BAO_Membership::getContactMembership($membership->contact_id,
299 $membership->membership_type_id,
300 $membership->is_test, $membership->id
301 );
302
303 // CRM-8141 update the membership type with the value recorded in log when membership created/renewed
304 // this picks up membership type changes during renewals
305 $sql = "
306SELECT membership_type_id
307FROM civicrm_membership_log
308WHERE membership_id=$membership->id
309ORDER BY id DESC
310LIMIT 1;";
311 $dao = new CRM_Core_DAO;
312 $dao->query($sql);
313 if ($dao->fetch()) {
314 if (!empty($dao->membership_type_id)) {
315 $membership->membership_type_id = $dao->membership_type_id;
316 $membership->save();
317 }
318 // else fall back to using current membership type
319 }
320 // else fall back to using current membership type
321 $dao->free();
322
323 if ($currentMembership) {
324 /*
325 * Fixed FOR CRM-4433
326 * In BAO/Membership.php(renewMembership function), we skip the extend membership date and status
327 * when Contribution mode is notify and membership is for renewal )
328 */
329 CRM_Member_BAO_Membership::fixMembershipStatusBeforeRenew($currentMembership, $changeToday);
330
331 $dates = CRM_Member_BAO_MembershipType::getRenewalDatesForMembershipType($membership->id,
332 $changeToday
333 );
334 $dates['join_date'] = CRM_Utils_Date::customFormat($currentMembership['join_date'], $format);
335 }
336 else {
337 $dates = CRM_Member_BAO_MembershipType::getDatesForMembershipType($membership->membership_type_id);
338 }
339
340 //get the status for membership.
341 $calcStatus = CRM_Member_BAO_MembershipStatus::getMembershipStatusByDate($dates['start_date'],
342 $dates['end_date'],
343 $dates['join_date'],
344 'today',
345 TRUE
346 );
347
348 $formatedParams = array('status_id' => CRM_Utils_Array::value('id', $calcStatus, 2),
349 'join_date' => CRM_Utils_Date::customFormat(CRM_Utils_Array::value('join_date', $dates), $format),
350 'start_date' => CRM_Utils_Date::customFormat(CRM_Utils_Array::value('start_date', $dates), $format),
351 'end_date' => CRM_Utils_Date::customFormat(CRM_Utils_Array::value('end_date', $dates), $format),
352 );
353 //we might be renewing membership,
354 //so make status override false.
355 $formatedParams['is_override'] = FALSE;
356 $membership->copyValues($formatedParams);
357 $membership->save();
358
359 //updating the membership log
360 $membershipLog = array();
361 $membershipLog = $formatedParams;
362
363 $logStartDate = $formatedParams['start_date'];
364 if (CRM_Utils_Array::value('log_start_date', $dates)) {
365 $logStartDate = CRM_Utils_Date::customFormat($dates['log_start_date'], $format);
366 $logStartDate = CRM_Utils_Date::isoToMysql($logStartDate);
367 }
368
369 $membershipLog['start_date'] = $logStartDate;
370 $membershipLog['membership_id'] = $membership->id;
371 $membershipLog['modified_id'] = $membership->contact_id;
372 $membershipLog['modified_date'] = date('Ymd');
373 $membershipLog['membership_type_id'] = $membership->membership_type_id;
374
375 CRM_Member_BAO_MembershipLog::add($membershipLog, CRM_Core_DAO::$_nullArray);
376
377 //update related Memberships.
378 CRM_Member_BAO_Membership::updateRelatedMemberships($membership->id, $formatedParams);
d63f4fc3 379
6a488035
TO
380 //update the membership type key of membership relatedObjects array
381 //if it has changed after membership update
382 if ($membershipTypeIdKey != $membership->membership_type_id) {
383 $membershipsUpdate[$membership->membership_type_id] = $membership;
384 $contribution->_relatedObjects['membership'][$membership->membership_type_id] = $membership;
385 unset($contribution->_relatedObjects['membership'][$membershipTypeIdKey]);
386 unset($memberships[$membershipTypeIdKey]);
387 }
388 }
389 }
390 //update the memberships object with updated membershipTypeId data
391 //if membershipTypeId has changed after membership update
392 if (!empty($membershipsUpdate)) {
393 $memberships = $memberships + $membershipsUpdate;
394 }
395 }
396 }
397 else {
398 // event
399 $eventParams = array('id' => $objects['event']->id);
400 $values['event'] = array();
401
402 CRM_Event_BAO_Event::retrieve($eventParams, $values['event']);
403
404 //get location details
405 $locationParams = array('entity_id' => $objects['event']->id, 'entity_table' => 'civicrm_event');
406 $values['location'] = CRM_Core_BAO_Location::getValues($locationParams);
407
408 $ufJoinParams = array(
409 'entity_table' => 'civicrm_event',
410 'entity_id' => $ids['event'],
411 'module' => 'CiviEvent',
412 );
d63f4fc3 413
6a488035
TO
414 list($custom_pre_id,
415 $custom_post_ids
416 ) = CRM_Core_BAO_UFJoin::getUFGroupIds($ufJoinParams);
d63f4fc3 417
6a488035
TO
418 $values['custom_pre_id'] = $custom_pre_id;
419 $values['custom_post_id'] = $custom_post_ids;
420
421 $contribution->source = ts('Online Event Registration') . ': ' . $values['event']['title'];
422
423 if ($values['event']['is_email_confirm']) {
424 $contribution->receipt_date = self::$_now;
425 $values['is_email_receipt'] = 1;
426 }
427 if (!CRM_Utils_Array::value('skipComponentSync', $input)) {
428 $participant->status_id = 1;
429 }
430 $participant->save();
431 }
432
433 if (CRM_Utils_Array::value('net_amount', $input, 0) == 0 &&
434 CRM_Utils_Array::value('fee_amount', $input, 0) != 0
435 ) {
436 $input['net_amount'] = $input['amount'] - $input['fee_amount'];
437 }
438 $addLineItems = FALSE;
439 if (empty($contribution->id)) {
440 $addLineItems = TRUE;
441 }
d63f4fc3 442
6a488035
TO
443 $contribution->contribution_status_id = 1;
444 $contribution->is_test = $input['is_test'];
445 $contribution->fee_amount = CRM_Utils_Array::value('fee_amount', $input, 0);
446 $contribution->net_amount = CRM_Utils_Array::value('net_amount', $input, 0);
447 $contribution->trxn_id = $input['trxn_id'];
448 $contribution->receive_date = CRM_Utils_Date::isoToMysql($contribution->receive_date);
449 $contribution->thankyou_date = CRM_Utils_Date::isoToMysql($contribution->thankyou_date);
450 $contribution->cancel_date = 'null';
451
452 if (CRM_Utils_Array::value('check_number', $input)) {
453 $contribution->check_number = $input['check_number'];
454 }
455
456 if (CRM_Utils_Array::value('payment_instrument_id', $input)) {
457 $contribution->payment_instrument_id = $input['payment_instrument_id'];
458 }
d63f4fc3 459
6a488035
TO
460 if ($contribution->id) {
461 $contributionId['id'] = $contribution->id;
462 $input['prevContribution'] = CRM_Contribute_BAO_Contribution::getValues($contributionId, CRM_Core_DAO::$_nullArray, CRM_Core_DAO::$_nullArray);
463 }
464 $contribution->save();
465
466 //add lineitems for recurring payments
467 if (CRM_Utils_Array::value('contributionRecur', $objects) && $objects['contributionRecur']->id && $addLineItems) {
468 $this->addrecurLineItems($objects['contributionRecur']->id, $contribution->id);
469 }
470
471 // next create the transaction record
472 $paymentProcessor = $paymentProcessorId = '';
473 if (isset($objects['paymentProcessor'])) {
474 if (is_array($objects['paymentProcessor'])) {
475 $paymentProcessor = $objects['paymentProcessor']['payment_processor_type'];
476 $paymentProcessorId = $objects['paymentProcessor']['id'];
477 }
478 else {
479 $paymentProcessor = $objects['paymentProcessor']->payment_processor_type;
480 $paymentProcessorId = $objects['paymentProcessor']->id;
481 }
482 }
483
484 if ($contribution->id) {
485 $contributionStatuses = CRM_Contribute_PseudoConstant::contributionStatus(NULL, 'name');
d63f4fc3 486 if (!$input['prevContribution']->is_pay_later &&
6a488035
TO
487 $input['prevContribution']->contribution_status_id == array_search('Pending', $contributionStatuses)) {
488 $input['payment_processor'] = $paymentProcessorId;
489 }
490 $input['total_amount'] = $input['amount'];
491 $input['contribution'] = $contribution;
492 if (CRM_Utils_Array::value('participant', $contribution->_relatedObjects)) {
493 $input['contribution_mode'] = 'participant';
494 $input['participant_id'] = $contribution->_relatedObjects['participant']->id;
495 }
d63f4fc3 496
6a488035
TO
497 CRM_Contribute_BAO_Contribution::recordFinancialAccounts($input, NULL);
498 }
499
500 self::updateRecurLinkedPledge($contribution);
501
502 // create an activity record
503 if ($input['component'] == 'contribute') {
504 //CRM-4027
505 $targetContactID = NULL;
506 if (CRM_Utils_Array::value('related_contact', $ids)) {
507 $targetContactID = $contribution->contact_id;
508 $contribution->contact_id = $ids['related_contact'];
509 }
510 CRM_Activity_BAO_Activity::addActivity($contribution, NULL, $targetContactID);
511 // event
512 }
513 else {
514 CRM_Activity_BAO_Activity::addActivity($participant);
515 }
516
517 CRM_Core_Error::debug_log_message("Contribution record updated successfully");
518 $transaction->commit();
519
520 // CRM-9132 legacy behaviour was that receipts were sent out in all instances. Still sending
521 // when array_key 'is_email_receipt doesn't exist in case some instances where is needs setting haven't been set
522 if (!array_key_exists('is_email_receipt', $values) ||
523 $values['is_email_receipt'] == 1
524 ) {
525 self::sendMail($input, $ids, $objects, $values, $recur, FALSE);
bf014492 526 CRM_Core_Error::debug_log_message("Receipt sent");
6a488035
TO
527 }
528
bf014492 529 CRM_Core_Error::debug_log_message("Success: Database updated");
6a488035
TO
530 }
531
532 function getBillingID(&$ids) {
533 // get the billing location type
534 $locationTypes = CRM_Core_PseudoConstant::locationType();
535 // CRM-8108 remove the ts around the Billing locationtype
536 //$ids['billing'] = array_search( ts('Billing'), $locationTypes );
537 $ids['billing'] = array_search('Billing', $locationTypes);
538 if (!$ids['billing']) {
539 CRM_Core_Error::debug_log_message(ts('Please set a location type of %1', array(1 => 'Billing')));
540 echo "Failure: Could not find billing location type<p>";
541 return FALSE;
542 }
543 return TRUE;
544 }
545
546 /*
547 * Send receipt from contribution. Note that the compose message part has been moved to contribution
548 * In general LoadObjects is called first to get the objects but the composeMessageArray function now calls it
549 *
550 * @params array $input Incoming data from Payment processor
551 * @params array $ids Related object IDs
552 * @params array $values values related to objects that have already been loaded
553 * @params bool $recur is it part of a recurring contribution
554 * @params bool $returnMessageText Should text be returned instead of sent. This
555 * is because the function is also used to generate pdfs
556 */
557 function sendMail(&$input, &$ids, &$objects, &$values, $recur = FALSE, $returnMessageText = FALSE) {
558 $contribution = &$objects['contribution'];
559 $input['is_recur'] = $recur;
560 // set receipt from e-mail and name in value
561 if (!$returnMessageText) {
562 $session = CRM_Core_Session::singleton();
563 $userID = $session->get('userID');
564 if (!empty($userID)) {
565 list($userName, $userEmail) = CRM_Contact_BAO_Contact_Location::getEmailDetails($userID);
566 $values['receipt_from_email'] = $userEmail;
567 $values['receipt_from_name'] = $userName;
568 }
569 }
570 return $contribution->composeMessageArray($input, $ids, $values, $recur, $returnMessageText);
571 }
572
573 function updateContributionStatus(&$params) {
574 // get minimum required values.
575 $statusId = CRM_Utils_Array::value('contribution_status_id', $params);
576 $componentId = CRM_Utils_Array::value('component_id', $params);
577 $componentName = CRM_Utils_Array::value('componentName', $params);
578 $contributionId = CRM_Utils_Array::value('contribution_id', $params);
579
580 if (!$contributionId || !$componentId || !$componentName || !$statusId) {
581 return;
582 }
583
584 $input = $ids = $objects = array();
585
586 //get the required ids.
587 $ids['contribution'] = $contributionId;
588
589 if (!$ids['contact'] = CRM_Utils_Array::value('contact_id', $params)) {
590 $ids['contact'] = CRM_Core_DAO::getFieldValue('CRM_Contribute_DAO_Contribution',
591 $contributionId,
592 'contact_id'
593 );
594 }
595
596 if ($componentName == 'Event') {
597 $name = 'event';
598 $ids['participant'] = $componentId;
599
600 if (!$ids['event'] = CRM_Utils_Array::value('event_id', $params)) {
601 $ids['event'] = CRM_Core_DAO::getFieldValue('CRM_Event_DAO_Participant',
602 $componentId,
603 'event_id'
604 );
605 }
606 }
607
608 if ($componentName == 'Membership') {
609 $name = 'contribute';
610 $ids['membership'] = $componentId;
611 }
612 $ids['contributionPage'] = NULL;
613 $ids['contributionRecur'] = NULL;
614 $input['component'] = $name;
615
616 $baseIPN = new CRM_Core_Payment_BaseIPN();
617 $transaction = new CRM_Core_Transaction();
618
619 // reset template values.
620 $template = CRM_Core_Smarty::singleton();
621 $template->clearTemplateVars();
622
623 if (!$baseIPN->validateData($input, $ids, $objects, FALSE)) {
624 CRM_Core_Error::fatal();
625 }
626
627 $contribution = &$objects['contribution'];
628
629 $contributionStatuses = CRM_Contribute_PseudoConstant::contributionStatus(NULL, 'name');
630 $input['skipComponentSync'] = CRM_Utils_Array::value('skipComponentSync', $params);
631 if ($statusId == array_search('Cancelled', $contributionStatuses)) {
632 $baseIPN->cancelled($objects, $transaction, $input);
633 $transaction->commit();
634 return $statusId;
635 }
636 elseif ($statusId == array_search('Failed', $contributionStatuses)) {
637 $baseIPN->failed($objects, $transaction, $input);
638 $transaction->commit();
639 return $statusId;
640 }
641
642 // status is not pending
643 if ($contribution->contribution_status_id != array_search('Pending', $contributionStatuses)) {
644 $transaction->commit();
645 return;
646 }
647
648 //set values for ipn code.
649 foreach (array(
650 'fee_amount', 'check_number', 'payment_instrument_id') as $field) {
651 if (!$input[$field] = CRM_Utils_Array::value($field, $params)) {
652 $input[$field] = $contribution->$field;
653 }
654 }
655 if (!$input['trxn_id'] = CRM_Utils_Array::value('trxn_id', $params)) {
656 $input['trxn_id'] = $contribution->invoice_id;
657 }
658 if (!$input['amount'] = CRM_Utils_Array::value('total_amount', $params)) {
659 $input['amount'] = $contribution->total_amount;
660 }
661 $input['is_test'] = $contribution->is_test;
662 $input['net_amount'] = $contribution->net_amount;
663 if (CRM_Utils_Array::value('fee_amount', $input) && CRM_Utils_Array::value('amount', $input)) {
664 $input['net_amount'] = $input['amount'] - $input['fee_amount'];
665 }
666
667 //complete the contribution.
668 $baseIPN->completeTransaction($input, $ids, $objects, $transaction, FALSE);
669
670 // reset template values before processing next transactions
671 $template->clearTemplateVars();
672
673 return $statusId;
674 }
675
676 /*
677 * Update pledge associated with a recurring contribution
678 *
679 * If the contribution has a pledge_payment record pledge, then update the pledge_payment record & pledge based on that linkage.
680 *
681 * If a previous contribution in the recurring contribution sequence is linked with a pledge then we assume this contribution
682 * should be linked with the same pledge also. Currently only back-office users can apply a recurring payment to a pledge &
683 * it should be assumed they
684 * do so with the intention that all payments will be linked
685 *
686 * The pledge payment record should already exist & will need to be updated with the new contribution ID.
687 * If not the contribution will also need to be linked to the pledge
688 */
689 function updateRecurLinkedPledge(&$contribution) {
690 $returnProperties = array('id', 'pledge_id');
691 $paymentDetails = $paymentIDs = array();
692
693 if (CRM_Core_DAO::commonRetrieveAll('CRM_Pledge_DAO_PledgePayment', 'contribution_id', $contribution->id,
694 $paymentDetails, $returnProperties
695 )) {
696 foreach ($paymentDetails as $key => $value) {
697 $paymentIDs[] = $value['id'];
698 $pledgeId = $value['pledge_id'];
699 }
700 }
701 else {
702 //payment is not already linked - if it is linked with a pledge we need to create a link.
703 // return if it is not recurring contribution
704 if (!$contribution->contribution_recur_id) {
705 return;
706 }
707
708 $relatedContributions = new CRM_Contribute_DAO_Contribution();
709 $relatedContributions->contribution_recur_id = $contribution->contribution_recur_id;
710 $relatedContributions->find();
711
712 while ($relatedContributions->fetch()) {
713 CRM_Core_DAO::commonRetrieveAll('CRM_Pledge_DAO_PledgePayment', 'contribution_id', $relatedContributions->id,
714 $paymentDetails, $returnProperties
715 );
716 }
717
718 if (empty($paymentDetails)) {
719 // payment is not linked with a pledge and neither are any other contributions on this
720 return;
721 }
722
723 foreach ($paymentDetails as $key => $value) {
724 $pledgeId = $value['pledge_id'];
725 }
726
727 // we have a pledge now we need to get the oldest unpaid payment
728 $paymentDetails = CRM_Pledge_BAO_PledgePayment::getOldestPledgePayment($pledgeId);
e3f3156b 729 if(empty($paymentDetails['id'])){
730 // we can assume this pledge is now completed
731 // return now so we don't create a core error & roll back
732 return;
733 }
6a488035
TO
734 $paymentDetails['contribution_id'] = $contribution->id;
735 $paymentDetails['status_id'] = $contribution->contribution_status_id;
736 $paymentDetails['actual_amount'] = $contribution->total_amount;
737
738 // put contribution against it
739 $payment = CRM_Pledge_BAO_PledgePayment::add($paymentDetails);
740 $paymentIDs[] = $payment->id;
741 }
742
743 // update pledge and corresponding payment statuses
744 CRM_Pledge_BAO_PledgePayment::updatePledgePaymentStatus($pledgeId, $paymentIDs, $contribution->contribution_status_id,
745 NULL, $contribution->total_amount
746 );
747 }
748
749 function addrecurLineItems($recurId, $contributionId) {
750 $lineSets = $lineItems = array();
751
752 //Get the first contribution id with recur id
753 if ($recurId) {
754 $contriID = CRM_Core_DAO::getFieldValue('CRM_Contribute_DAO_Contribution', $recurId, 'id', 'contribution_recur_id');
755 $lineItems = CRM_Price_BAO_LineItem::getLineItems($contriID, 'contribution');
756 if (!empty($lineItems)) {
757 foreach ($lineItems as $key => $value) {
758 $pricesetID = new CRM_Price_DAO_Field();
759 $pricesetID->id = $value['price_field_id'];
760 $pricesetID->find(TRUE);
761 $lineSets[$pricesetID->price_set_id][] = $value;
762 }
763 }
764
765 CRM_Price_BAO_LineItem::processPriceSet($contributionId, $lineSets);
766 }
767 }
768}
769