First working (hackish) autocancel code
[tc-ipn-receiver.git] / trustcommerceIPN.php
index e8655d0c3a235a643dda57621a9a3cab3800dba9..a7c7be3a87ece113773eec9dcbd4e569123b651b 100644 (file)
  * You should have received a copy of the GNU General Public License
  * along with CiviCRM.  If not, see <http://www.gnu.org/licenses/>.
  *
- * Copyright (C) 2012
- * Licensed to CiviCRM under the GPL v3 or higher
- *
- * Modified by Lisa Marie Maginnis <lisa@fsf.org> (http://www.fsf.org)
+ * Copyright 2016, Lisa Marie Maginnis <lisa@fsf.org> (http://www.fsf.org)
  *
  */
 
+/**
+  * CiviCRM (Instant Payment Notification) IPN processor module for
+  * TrustCommerece.
+  *
+  * For full documentation on the 
+  * TrustCommerece API, please see the TCDevGuide for more information:
+  * https://vault.trustcommerce.com/downloads/TCDevGuide.htm
+  *
+  * This module supports the following features: Single credit/debit card
+  * transactions, AVS checking, recurring (create, update, and cancel
+  * subscription) optional blacklist with fail2ban,
+  *
+  * @copyright Lisa Marie Maginnis <lisa@fsf.org> (http://www.fsf.org)
+  * @version   1.0
+  * @package   org.fsf.payment.trustcommerce.ipn
+  */
+
+define("MAX_FAILURES", 4);
+
 class CRM_Core_Payment_trustcommerce_IPN extends CRM_Core_Payment_BaseIPN {
+
+  /**
+   * Inheret  
+   *
+   * @return void
+   */
   function __construct() {
     parent::__construct();
   }
 
+  function getLastFailures($recur_id) {
+    $sql=<<<EOF
+SELECT count(*) as numfails
+   FROM civicrm_contribution
+   WHERE contribution_recur_id = $recur_id
+    AND
+    id > (SELECT MAX(id) FROM civicrm_contribution WHERE contribution_recur_id = $recur_id AND contribution_status_id = 1);
+EOF;
+
+    $result = CRM_Core_DAO::executeQuery($sql);
+    if($result->fetch()) {
+      $failures = $result->numfails;
+    } else {
+      $failures = NULL;
+    }
+
+    return $failures;
+
+  }
 
   function main($component = 'contribute') {
   static $no = NULL;
@@ -55,12 +96,11 @@ class CRM_Core_Payment_trustcommerce_IPN extends CRM_Core_Payment_BaseIPN {
        $ids['trxn_id'] = $input['trxn_id'];
 
        if($this->checkDuplicate($input, $ids) != NULL) {
-        CRM_Core_Error::debug_log_message("Success: This payment has already been processed.");
-        echo "Success: This payment has already been processed<p>\n";
+        $msg = 'TrustCommerceIPN: Skipping duplicate contribution: for contact: '.$ids['contact'].' amount: $'.$input['amount'].' trxn_id: '.$input['trxn_id']."\n";
+        echo $msg;
+        CRM_Core_Error::debug_log_message($msg);
         exit;
        }
-       var_dump($ids);
-       var_dump($input);
 
        if(array_key_exists('membership', $ids)) {
         $membership = array();
@@ -68,8 +108,6 @@ class CRM_Core_Payment_trustcommerce_IPN extends CRM_Core_Payment_BaseIPN {
         $obj = CRM_Member_BAO_Membership::retrieve($params, $membership);
         $objects['membership'] = array(&$obj);
        }
-       var_dump($ids);
-       var_dump($input);
 
       $paymentProcessorID = CRM_Core_DAO::getFieldValue('CRM_Financial_DAO_PaymentProcessorType',
                                                        'TrustCommerce', 'id', 'name'
@@ -90,10 +128,48 @@ class CRM_Core_Payment_trustcommerce_IPN extends CRM_Core_Payment_BaseIPN {
        
        return $this->processRecur($input, $ids, $objects, $first);
        
+      }
+
     }
+  }
+
+  protected function disableAutorenew($recur_id) {
+    /* Load payment processor object */
+    // HARD CODED
+    $msg = 'TrustCommerceIPN: MAX_FAILURES hit, unstoring billing ID: '.$recur_id."\n";
+
+    CRM_Core_Error::debug_log_message($msg);
+    echo $msg;
+
+    $sql = "SELECT user_name, password, url_site FROM civicrm_payment_processor WHERE id =  8 LIMIT 1";
+
+    $result = CRM_Core_DAO::executeQuery($sql);
+    if($result->fetch()) {
+      $request = array(
+                     'custid' => $result->user_name,
+                     'password' => $result->password,
+                     'action' => 'unstore',
+                     'billingid' => $recur_id
+                     );
+
+      $update = 'UPDATE civicrm_contribution_recur SET contribution_status_id = 3 WHERE processor_id = "'.$recur_id.'";';
+    $result1 = CRM_Core_DAO::executeQuery($update);
+
+      $tc = tclink_send($request);
+      if(!$tc) {
+       return -1;
+      }
+
+      return TRUE;
+      
+    } else {
+      echo 'CRITICAL ERROR: Could not load payment processor object';
+      return;
+    }
+
+    
 
   }
-}
 
   protected function checkDuplicate($input, $ids) {
 //    $sql='select id from civicrm_contribution where receive_date like \''.$input['date'].'%\' and total_amount='.$input['amount'].' and contact_id='.$ids['contact'].' and contribution_status_id =  1 limit 1';
@@ -101,8 +177,12 @@ class CRM_Core_Payment_trustcommerce_IPN extends CRM_Core_Payment_BaseIPN {
 
 
     $result = CRM_Core_DAO::executeQuery($sql);
-    $result->fetch();
-    $id = $result->id;
+    if($result->fetch()) {
+      $id = $result->id;
+    } else {
+      $id = NULL;
+    }
+
     return $id;
   }
   protected function processRecur($input, $ids, $objects, $first) {
@@ -133,20 +213,33 @@ class CRM_Core_Payment_trustcommerce_IPN extends CRM_Core_Payment_BaseIPN {
       $contribution->payment_instrument_id = 1;
       $contribution->amount_level = $objects['contribution']->amount_level;
       $contribution->address_id = $objects['contribution']->address_id;
-      $contribution->honor_contact_id = $objects['contribution']->honor_contact_id;
-      $contribution->honor_type_id = $objects['contribution']->honor_type_id;
       $contribution->campaign_id = $objects['contribution']->campaign_id;
       $contribution->total_amount = $input['amount']; 
 
       $objects['contribution'] = &$contribution;
     }
+
     $objects['contribution']->invoice_id = md5(uniqid(rand(), TRUE));
     //    $objects['contribution']->total_amount = $objects['contribution']->total_amount;
     $objects['contribution']->trxn_id = $input['trxn_id'];
 
-    // since we have processor loaded for sure at this point,
+    // check if contribution is already completed, if so we ignore this ipn
+    if ($objects['contribution']->contribution_status_id == 1) {
+      $transaction->commit();
+      CRM_Core_Error::debug_log_message("returning since contribution has already been handled");
+      echo 'Success: Contribution has already been handled<p>';
+      echo '';
+      return TRUE;
+    }
 
     $sendNotification = FALSE;
+
+    $recur->trxn_id = $input['trxn_id'];
+    $recur->total_amount = $input['amount'];
+    $recur->payment_instrument_id = 1;
+    $recur->fee = 0;
+    $recur->net_amount = $input['amount'];
+
     if ($input['status'] == 1) {
 
       // Approved
@@ -166,68 +259,44 @@ class CRM_Core_Payment_trustcommerce_IPN extends CRM_Core_Payment_BaseIPN {
         $sendNotification = TRUE;
         $subscriptionPaymentStatus = CRM_Core_Payment::RECURRING_PAYMENT_END;
       }
-      $recur->trxn_id = $input['trxn_id'];
-      $recur->total_amount = $input['amount'];
-      $recur->payment_instrument_id = 1;
-      $recur->fee = NULL;
-      $recur->net_amount = NULL;
 
       $recur->modified_date = $now;
       $recur->contribution_status_id = array_search($statusName, $contributionStatus);
       $recur->save();
+      $input['is_test'] = 0;
+      $msg = 'TrustCommerceIPN: Created contribution: for contact: '.$ids['contact'].' amount: $'.$input['amount'].' trxn_id: '.$input['trxn_id'].' status: Completed'."\n";
+      echo $msg;
+      CRM_Core_Error::debug_log_message($msg);
+
+      $this->completeTransaction($input, $ids, $objects, $transaction, $recur);
     }
-    else {
+    else if( $input['status'] == 4 ) {
       // Declined
       // failed status
-
-      $recur->trxn_id = $input['trxn_id'];
-      $recur->total_amount = $input['amount'];
-      $recur->payment_instrument_id = 1;
-      $recur->fee = NULL;
-      $recur->net_amount = NULL;
-
-
+      
       $recur->contribution_status_id = array_search('Failed', $contributionStatus);
       $recur->cancel_date = $now;
       $recur->save();
-
-      CRM_Core_Error::debug_log_message("Subscription payment failed");
-
-      // the recurring contribution has declined a payment or has failed
-      // so we just fix the recurring contribution and not change any of
-      // the existing contribiutions
-      // CRM-9036
-      return TRUE;
-
-    }
-
-
-    // check if contribution is already completed, if so we ignore this ipn
-    if ($objects['contribution']->contribution_status_id == 1) {
-      $transaction->commit();
-      CRM_Core_Error::debug_log_message("returning since contribution has already been handled");
-      echo 'Success: Contribution has already been handled<p>';
-      echo '';
-      return TRUE;
+      
+      $msg = 'TrustCommerceIPN: Created contribution: for contact: '.$ids['contact'].' amount: $'.$input['amount'].' trxn_id: '.$input['trxn_id'].' status: Failed'."\n";
+      echo $msg;
+      CRM_Core_Error::debug_log_message($msg);
+
+      /* Action for repeated failures */
+      if(MAX_FAILURES <= $this->getLastFailures($ids['contributionRecur'])) {
+       //$this->disableAutoRenew(($ids['contributionRecur']));
+       $this->disableAutorenew($ids['processor_id']);
+      }
+      
+      return $this->failed($objects, $transaction);
     }
-    $input['is_test'] = 0;
-
-    $this->completeTransaction($input, $ids, $objects, $transaction, $recur);
-
-      echo 'Success: Created new contribution: '.$ids['contribution'].' for cid: '.$ids['contact'].'\n';
-      CRM_Core_Error::debug_log_message('Success: Created new contribution: '.$ids['contribution'].' for cid: '.$ids['contact']);
 
     if ($sendNotification) {
       $autoRenewMembership = FALSE;
-      if ($recur->id &&
-         isset($ids['membership']) && $ids['membership']
-         ) {
+      if ($recur->id && isset($ids['membership']) && $ids['membership'] ) {
         $autoRenewMembership = TRUE;
       }
 
-
-
-
       //send recurring Notification email for user
       CRM_Contribute_BAO_ContributionPage::recurringNotify($subscriptionPaymentStatus,
                                                           $ids['contact'],
@@ -235,14 +304,7 @@ class CRM_Core_Payment_trustcommerce_IPN extends CRM_Core_Payment_BaseIPN {
                                                           $recur,
                                                           $autoRenewMembership
                                                           );
-
-
-
-
     }
-
-
-
   }
 
  protected function getIDs($billingid, $input, $module) {
@@ -257,6 +319,7 @@ class CRM_Core_Payment_trustcommerce_IPN extends CRM_Core_Payment_BaseIPN {
     $ids['contribution'] = $result->coid;
     $ids['contributionRecur'] = $result->id;
     $ids['contact'] = $result->contact_id;
+    $ids['processor_id'] = $billingid;
 
     if (!$ids['contributionRecur']) {
       CRM_Core_Error::debug_log_message("Could not find billingid: ".$billingid);