StripeIPN.php 17.2 KB
Newer Older
bgm's avatar
bgm committed
1 2 3 4 5 6 7
<?php
/*
 * @file
 * Handle Stripe Webhooks for recurring payments.
 */

class CRM_Core_Payment_StripeIPN extends CRM_Core_Payment_BaseIPN {
Mathieu Lutfy's avatar
Mathieu Lutfy committed
8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24

  protected $_paymentProcessor;

  /**
   * Transaction ID is the contribution in the redirect flow and a random number in the on-site->POST flow
   * Ideally the contribution id would always be created at this point in either flow for greater consistency
   * @var
   */
  protected $transaction_id;

  /**
   * Do we send an email receipt for each contribution?
   *
   * @var int
   */
  protected $is_email_receipt = 1;

bgm's avatar
bgm committed
25 26 27
  // By default, always retrieve the event from stripe to ensure we are
  // not being fed garbage. However, allow an override so when we are 
  // testing, we can properly test a failed recurring contribution.
Mathieu Lutfy's avatar
Mathieu Lutfy committed
28
  protected $verify_event = TRUE;
bgm's avatar
bgm committed
29 30

  // Properties of the event.
Mathieu Lutfy's avatar
Mathieu Lutfy committed
31 32 33 34 35 36 37 38 39 40 41
  protected $event_type = NULL;
  protected $subscription_id = NULL;
  protected $customer_id = NULL;
  protected $charge_id = NULL;
  protected $previous_plan_id = NULL;
  protected $plan_id = NULL;
  protected $plan_amount = NULL;
  protected $frequency_interval = NULL;
  protected $frequency_unit = NULL;
  protected $plan_name = NULL;
  protected $plan_start = NULL;
bgm's avatar
bgm committed
42 43
       
  // Derived properties.
Mathieu Lutfy's avatar
Mathieu Lutfy committed
44 45 46 47 48 49 50 51
  protected $contribution_recur_id = NULL;
  protected $event_id = NULL;
  protected $invoice_id = NULL;
  protected $receive_date = NULL;
  protected $amount = NULL;
  protected $fee = NULL;
  protected $net_amount = NULL;
  protected $previous_contribution = [];
bgm's avatar
bgm committed
52

Mathieu Lutfy's avatar
Mathieu Lutfy committed
53 54 55 56 57 58 59 60
  /**
   * CRM_Core_Payment_StripeIPN constructor.
   *
   * @param $inputData
   * @param bool $verify
   *
   * @throws \CRM_Core_Exception
   */
bgm's avatar
bgm committed
61 62 63 64 65
  public function __construct($inputData, $verify = TRUE) {
    $this->verify_event = $verify;
    $this->setInputParameters($inputData);
    parent::__construct();
  }
Mathieu Lutfy's avatar
Mathieu Lutfy committed
66

bgm's avatar
bgm committed
67 68 69 70 71 72 73
  /**
   * Store input array on the class.
   * We override base because our input parameter is an object
   *
   * @param array $parameters
  */
  public function setInputParameters($parameters) {
Mathieu Lutfy's avatar
Mathieu Lutfy committed
74 75
    if (!is_object($parameters)) {
      $this->exception('Invalid input parameters');
bgm's avatar
bgm committed
76 77 78 79
    }

    // Determine the proper Stripe Processor ID so we can get the secret key
    // and initialize Stripe.
Mathieu Lutfy's avatar
Mathieu Lutfy committed
80

bgm's avatar
bgm committed
81
    // The $_GET['processor_id'] value is set by CRM_Core_Payment::handlePaymentMethod.
Mathieu Lutfy's avatar
Mathieu Lutfy committed
82 83 84
    $paymentProcessorId = (int) CRM_Utils_Array::value('processor_id', $_GET);
    if (empty($paymentProcessorId)) {
      $this->exception('Cannot determine payment processor id');
bgm's avatar
bgm committed
85 86 87 88
    }

    // Get the Stripe secret key.
    try {
Mathieu Lutfy's avatar
Mathieu Lutfy committed
89
      $this->_paymentProcessor = \Civi\Payment\System::singleton()->getById($paymentProcessorId)->getPaymentProcessor();
bgm's avatar
bgm committed
90 91
    }
    catch(Exception $e) {
Mathieu Lutfy's avatar
Mathieu Lutfy committed
92
      $this->exception('Failed to get Stripe secret key');
bgm's avatar
bgm committed
93 94 95
    }

    // Now re-retrieve the data from Stripe to ensure it's legit.
Mathieu Lutfy's avatar
Mathieu Lutfy committed
96
    \Stripe\Stripe::setApiKey($this->_paymentProcessor['user_name']);
bgm's avatar
bgm committed
97 98 99 100 101 102
    if ($this->verify_event) {
      $this->_inputParameters = \Stripe\Event::retrieve($parameters->id);
    }
    else {
      $this->_inputParameters = $parameters;
    }
Mathieu Lutfy's avatar
Mathieu Lutfy committed
103
    http_response_code(200);
bgm's avatar
bgm committed
104
  }
Mathieu Lutfy's avatar
Mathieu Lutfy committed
105

bgm's avatar
bgm committed
106
  /**
Mathieu Lutfy's avatar
Mathieu Lutfy committed
107 108 109 110 111 112 113 114
   * Get a parameter given to us by Stripe.
   *
   * @param string $name
   * @param $type
   * @param bool $abort
   *
   * @return false|int|null|string
   * @throws \CRM_Core_Exception
bgm's avatar
bgm committed
115 116
   */
  public function retrieve($name, $type, $abort = TRUE) {
Mathieu Lutfy's avatar
Mathieu Lutfy committed
117
    $className = get_class($this->_inputParameters->data->object);
bgm's avatar
bgm committed
118
    $value = NULL;
Mathieu Lutfy's avatar
Mathieu Lutfy committed
119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140
    switch ($className) {
      case 'Stripe\Charge':
        switch ($name) {
          case 'charge_id':
            $value = $this->_inputParameters->data->object->id;
            break;

          case 'failure_code':
            $value = $this->_inputParameters->data->object->failure_code;
            break;

          case 'failure_message':
            $value = $this->_inputParameters->data->object->failure_message;
            break;

          case 'refunded':
            $value = $this->_inputParameters->data->object->refunded;
            break;

          case 'amount_refunded':
            $value = $this->_inputParameters->data->object->amount_refunded;
            break;
bgm's avatar
bgm committed
141 142
        }
        break;
Mathieu Lutfy's avatar
Mathieu Lutfy committed
143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160

      case 'Stripe\Invoice':
        switch ($name) {
          case 'charge_id':
            $value = $this->_inputParameters->data->object->charge;
            break;

          case 'invoice_id':
            $value = $this->_inputParameters->data->object->id;
            break;

          case 'receive_date':
            $value = date("Y-m-d H:i:s", $this->_inputParameters->data->object->date);
            break;

          case 'subscription_id':
            $value = $this->_inputParameters->data->object->subscription;
            break;
bgm's avatar
bgm committed
161 162
        }
        break;
Mathieu Lutfy's avatar
Mathieu Lutfy committed
163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192

      case 'Stripe\Subscription':
        switch ($name) {
          case 'frequency_interval':
            $value = $this->_inputParameters->data->object->plan->interval_count;
            break;

          case 'frequency_unit':
            $value = $this->_inputParameters->data->object->plan->interval;
            break;

          case 'plan_amount':
            $value = $this->_inputParameters->data->object->plan->amount / 100;
            break;

          case 'plan_id':
            $value = $this->_inputParameters->data->object->plan->id;
            break;

          case 'plan_name':
            $value = $this->_inputParameters->data->object->plan->name;
            break;

          case 'plan_start':
            $value = date("Y-m-d H:i:s", $this->_inputParameters->data->object->start);
            break;

          case 'subscription_id':
            $value = $this->_inputParameters->data->object->id;
            break;
bgm's avatar
bgm committed
193 194
        }
        break;
Mathieu Lutfy's avatar
Mathieu Lutfy committed
195 196 197 198 199 200
    }

    // Common parameters
    switch ($name) {
      case 'customer_id':
        $value = $this->_inputParameters->data->object->customer;
bgm's avatar
bgm committed
201
        break;
Mathieu Lutfy's avatar
Mathieu Lutfy committed
202

bgm's avatar
bgm committed
203 204 205
      case 'event_type':
        $value = $this->_inputParameters->type;
        break;
Mathieu Lutfy's avatar
Mathieu Lutfy committed
206

bgm's avatar
bgm committed
207 208 209 210 211 212
      case 'previous_plan_id':
        if (preg_match('/\.updated$/', $this->_inputParameters->type)) {
          $value = $this->_inputParameters->data->previous_attributes->plan->id;
        }
        break;
    }
Mathieu Lutfy's avatar
Mathieu Lutfy committed
213

bgm's avatar
bgm committed
214 215
    $value = CRM_Utils_Type::validate($value, $type, FALSE);
    if ($abort && $value === NULL) {
Mathieu Lutfy's avatar
Mathieu Lutfy committed
216 217
      echo "Failure: Missing Parameter<p>" . CRM_Utils_Type::escape($name, 'String');
      $this->exception("Could not find an entry for $name");
bgm's avatar
bgm committed
218 219 220 221
    }
    return $value;
  }

Mathieu Lutfy's avatar
Mathieu Lutfy committed
222 223 224 225 226
  /**
   * @throws \CRM_Core_Exception
   * @throws \CiviCRM_API3_Exception
   */
  public function main() {
bgm's avatar
bgm committed
227
    // Collect and determine all data about this event.
Mathieu Lutfy's avatar
Mathieu Lutfy committed
228 229 230
    $this->event_type = $this->retrieve('event_type', 'String');

    $pendingStatusId = CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'contribution_status_id', 'Pending');
bgm's avatar
bgm committed
231 232 233 234

    switch($this->event_type) {
      // Successful recurring payment.
      case 'invoice.payment_succeeded':
Mathieu Lutfy's avatar
Mathieu Lutfy committed
235 236 237
        $this->setInfo();
        if ($this->previous_contribution['contribution_status_id'] == $pendingStatusId) {
          $this->completeContribution();
bgm's avatar
bgm committed
238
        }
Mathieu Lutfy's avatar
Mathieu Lutfy committed
239
        elseif ($this->previous_contribution['trxn_id'] != $this->charge_id) {
bgm's avatar
bgm committed
240 241
          // The first contribution was completed, so create a new one.
          // api contribution repeattransaction repeats the appropriate contribution if it is given
Mathieu Lutfy's avatar
Mathieu Lutfy committed
242 243 244 245
          // simply the recurring contribution id. It also updates the membership for us.
          civicrm_api3('Contribution', 'repeattransaction', array(
            'contribution_recur_id' => $this->contribution_recur_id,
            'contribution_status_id' => 'Completed',
bgm's avatar
bgm committed
246 247 248 249 250 251 252 253 254
            'receive_date' => $this->receive_date,
            'trxn_id' => $this->charge_id,
            'total_amount' => $this->amount,
            'fee_amount' => $this->fee,
            'is_email_receipt' => $this->is_email_receipt,
          ));
        }

        // Successful charge & more to come. 
Mathieu Lutfy's avatar
Mathieu Lutfy committed
255
        civicrm_api3('ContributionRecur', 'create', array(
bgm's avatar
bgm committed
256 257
          'id' => $this->contribution_recur_id,
          'failure_count' => 0,
Mathieu Lutfy's avatar
Mathieu Lutfy committed
258
          'contribution_status_id' => 'In Progress'
bgm's avatar
bgm committed
259 260 261 262 263
        ));
        return TRUE;

      // Failed recurring payment.
      case 'invoice.payment_failed':
Mathieu Lutfy's avatar
Mathieu Lutfy committed
264 265
        $this->setInfo();
        $failDate = date('YmdHis');
bgm's avatar
bgm committed
266

Mathieu Lutfy's avatar
Mathieu Lutfy committed
267
        if ($this->previous_contribution['contribution_status_id'] == $pendingStatusId) {
bgm's avatar
bgm committed
268
          // If this contribution is Pending, set it to Failed.
Mathieu Lutfy's avatar
Mathieu Lutfy committed
269 270
          civicrm_api3('Contribution', 'create', array(
            'id' => $this->previous_contribution['id'],
bgm's avatar
bgm committed
271
            'contribution_status_id' => "Failed",
Mathieu Lutfy's avatar
Mathieu Lutfy committed
272
            'receive_date' => $failDate,
bgm's avatar
bgm committed
273 274 275 276
            'is_email_receipt' => $this->is_email_receipt,
          ));
        }
        else {
Mathieu Lutfy's avatar
Mathieu Lutfy committed
277
          $contributionParams = [
bgm's avatar
bgm committed
278
            'contribution_recur_id' => $this->contribution_recur_id,
Mathieu Lutfy's avatar
Mathieu Lutfy committed
279 280
            'contribution_status_id' => 'Failed',
            'receive_date' => $failDate,
bgm's avatar
bgm committed
281 282
            'total_amount' => $this->amount,
            'is_email_receipt' => $this->is_email_receipt,
Mathieu Lutfy's avatar
Mathieu Lutfy committed
283 284
          ];
          civicrm_api3('Contribution', 'repeattransaction', $contributionParams);
bgm's avatar
bgm committed
285 286
        }

Mathieu Lutfy's avatar
Mathieu Lutfy committed
287
        $failureCount = civicrm_api3('ContributionRecur', 'getvalue', array(
bgm's avatar
bgm committed
288 289 290
         'id' => $this->contribution_recur_id,
         'return' => 'failure_count',
        ));
Mathieu Lutfy's avatar
Mathieu Lutfy committed
291
        $failureCount++;
bgm's avatar
bgm committed
292

Mathieu Lutfy's avatar
Mathieu Lutfy committed
293 294
        // Change the status of the Recurring and update failed attempts.
        civicrm_api3('ContributionRecur', 'create', array(
bgm's avatar
bgm committed
295 296
          'id' => $this->contribution_recur_id,
          'contribution_status_id' => "Failed",
Mathieu Lutfy's avatar
Mathieu Lutfy committed
297 298
          'failure_count' => $failureCount,
          'modified_date' => $failDate,
bgm's avatar
bgm committed
299 300 301
        ));
        return TRUE;

Mathieu Lutfy's avatar
Mathieu Lutfy committed
302
      // Subscription is cancelled
bgm's avatar
bgm committed
303
      case 'customer.subscription.deleted':
Mathieu Lutfy's avatar
Mathieu Lutfy committed
304 305 306
        $this->setInfo();
        // Cancel the recurring contribution
        civicrm_api3('ContributionRecur', 'cancel', array(
bgm's avatar
bgm committed
307 308
          'id' => $this->contribution_recur_id,
        ));
Mathieu Lutfy's avatar
Mathieu Lutfy committed
309
        return TRUE;
bgm's avatar
bgm committed
310

Mathieu Lutfy's avatar
Mathieu Lutfy committed
311 312 313 314 315 316 317 318 319 320 321 322
      // One-time donation and per invoice payment.
      case 'charge.failed':
        $chargeId = $this->retrieve('charge_id', 'String');
        $failureCode = $this->retrieve('failure_code', 'String');
        $failureMessage = $this->retrieve('failure_message', 'String');
        $contribution = civicrm_api3('Contribution', 'getsingle', ['trxn_id' => $chargeId]);
        $failedStatusId = CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'contribution_status_id', 'Failed');
        if ($contribution['contribution_status_id'] != $failedStatusId) {
          $note = $failureCode . ' : ' . $failureMessage;
          civicrm_api3('Contribution', 'create', ['id' => $contribution['id'], 'contribution_status_id' => $failedStatusId, 'note' => $note]);
        }
        return TRUE;
bgm's avatar
bgm committed
323

Mathieu Lutfy's avatar
Mathieu Lutfy committed
324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346
      case 'charge.refunded':
        $chargeId = $this->retrieve('charge_id', 'String');
        $refunded = $this->retrieve('refunded', 'Boolean');
        $refundAmount = $this->retrieve('amount_refunded', 'Integer');
        $contribution = civicrm_api3('Contribution', 'getsingle', ['trxn_id' => $chargeId]);
        if ($refunded) {
          $refundedStatusId = CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'contribution_status_id', 'Refunded');
          if ($contribution['contribution_status_id'] != $refundedStatusId) {
            civicrm_api3('Contribution', 'create', [
              'id' => $contribution['id'],
              'contribution_status_id' => $refundedStatusId
            ]);
          }
          elseif ($refundAmount > 0) {
            $partiallyRefundedStatusId = CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'contribution_status_id', 'Partially Refunded');
            if ($contribution['contribution_status_id'] != $partiallyRefundedStatusId) {
              civicrm_api3('Contribution', 'create', [
                'id' => $contribution['id'],
                'contribution_status_id' => $refundedStatusId
              ]);
            }
          }
        }
bgm's avatar
bgm committed
347 348 349
        return TRUE;

      case 'charge.succeeded':
Mathieu Lutfy's avatar
Mathieu Lutfy committed
350 351 352 353
        $this->setInfo();
        if ($this->previous_contribution['contribution_status_id'] == $pendingStatusId) {
          $this->completeContribution();
        }
bgm's avatar
bgm committed
354 355
        return TRUE;

Mathieu Lutfy's avatar
Mathieu Lutfy committed
356 357
      case 'customer.subscription.updated':
       $this->setInfo();
bgm's avatar
bgm committed
358 359 360 361 362
       if (empty($this->previous_plan_id)) {
         // Not a plan change...don't care.
         return TRUE;
       }

Mathieu Lutfy's avatar
Mathieu Lutfy committed
363 364
       civicrm_api3('ContributionRecur', 'create', [
         'id' => $this->contribution_recur_id,
bgm's avatar
bgm committed
365 366 367 368 369
          'amount' => $this->plan_amount,
          'auto_renew' => 1,
          'created_date' => $this->plan_start,
          'frequency_unit' => $this->frequency_unit,
          'frequency_interval' => $this->frequency_interval,
Mathieu Lutfy's avatar
Mathieu Lutfy committed
370
       ]);
bgm's avatar
bgm committed
371

Mathieu Lutfy's avatar
Mathieu Lutfy committed
372 373 374 375 376
       civicrm_api3('Contribution', 'create', [
          'id' => $this->previous_contribution['id'],
          'total_amount' => $this->plan_amount,
          'contribution_recur_id' => $this->contribution_recur_id,
       ]);
bgm's avatar
bgm committed
377 378 379 380 381 382 383
        return TRUE;
    }
    // Unhandled event type.
    return TRUE;
  }

  /**
Mathieu Lutfy's avatar
Mathieu Lutfy committed
384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409
   * Complete a pending contribution and update associated entities (recur/membership)
   *
   * @throws \CiviCRM_API3_Exception
   */
  public function completeContribution() {
    // Update the contribution to include the fee.
    civicrm_api3('Contribution', 'create', array(
      'id' => $this->previous_contribution['id'],
      'total_amount' => $this->amount,
      'fee_amount' => $this->fee,
      'net_amount' => $this->net_amount,
    ));
    // The last one was not completed, so complete it.
    civicrm_api3('Contribution', 'completetransaction', array(
      'id' => $this->previous_contribution['id'],
      'trxn_date' => $this->receive_date,
      'trxn_id' => $this->charge_id,
      'total_amount' => $this->amount,
      'net_amount' => $this->net_amount,
      'fee_amount' => $this->fee,
      'payment_processor_id' => $this->_paymentProcessor['id'],
      'is_email_receipt' => $this->is_email_receipt,
    ));
  }

    /**
bgm's avatar
bgm committed
410 411 412 413 414
   * Gather and set info as class properties.
   *
   * Given the data passed to us via the Stripe Event, try to determine
   * as much as we can about this event and set that information as 
   * properties to be used later.
Mathieu Lutfy's avatar
Mathieu Lutfy committed
415 416
   *
   * @throws \CRM_Core_Exception
bgm's avatar
bgm committed
417
   */
Mathieu Lutfy's avatar
Mathieu Lutfy committed
418
  public function setInfo() {
bgm's avatar
bgm committed
419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434
    $abort = FALSE;
    $this->customer_id = $this->retrieve('customer_id', 'String');
    $this->subscription_id = $this->retrieve('subscription_id', 'String', $abort);
    $this->invoice_id = $this->retrieve('invoice_id', 'String', $abort);
    $this->receive_date = $this->retrieve('receive_date', 'String', $abort);
    $this->charge_id = $this->retrieve('charge_id', 'String', $abort);
    $this->plan_id = $this->retrieve('plan_id', 'String', $abort);
    $this->previous_plan_id = $this->retrieve('previous_plan_id', 'String', $abort);
    $this->plan_amount = $this->retrieve('plan_amount', 'String', $abort);
    $this->frequency_interval = $this->retrieve('frequency_interval', 'String', $abort);
    $this->frequency_unit = $this->retrieve('frequency_unit', 'String', $abort);
    $this->plan_name = $this->retrieve('plan_name', 'String', $abort);
    $this->plan_start = $this->retrieve('plan_start', 'String', $abort);

    // Gather info about the amount and fee.
    // Get the Stripe charge object if one exists. Null charge still needs processing.
Mathieu Lutfy's avatar
Mathieu Lutfy committed
435
    if ($this->charge_id !== null) {
bgm's avatar
bgm committed
436 437 438 439 440 441 442 443 444 445 446 447 448 449 450
      try {
        $charge = \Stripe\Charge::retrieve($this->charge_id);
        $balance_transaction_id = $charge->balance_transaction;
        // If the transaction is declined, there won't be a balance_transaction_id.
        if ($balance_transaction_id) {
          $balance_transaction = \Stripe\BalanceTransaction::retrieve($balance_transaction_id);
          $this->amount = $charge->amount / 100;
          $this->fee = $balance_transaction->fee / 100;
        }
        else {
          $this->amount = 0;
          $this->fee = 0;
        }
      }
      catch(Exception $e) {
Mathieu Lutfy's avatar
Mathieu Lutfy committed
451
        $this->exception('Cannot get contribution amounts');
bgm's avatar
bgm committed
452 453 454 455 456 457 458 459 460
      }
    } else {
      // The customer had a credit on their subscription from a downgrade or gift card.
      $this->amount = 0;
      $this->fee = 0;
    }

    $this->net_amount = $this->amount - $this->fee;

Mathieu Lutfy's avatar
Mathieu Lutfy committed
461
    // Additional processing of values is only relevant if there is a subscription id.
bgm's avatar
bgm committed
462 463
    if ($this->subscription_id) {
      // Get info related to recurring contributions.
Mathieu Lutfy's avatar
Mathieu Lutfy committed
464 465 466
      try {
        $contributionRecur = civicrm_api3('ContributionRecur', 'getsingle', ['trxn_id' => $this->subscription_id]);
        $this->contribution_recur_id = $contributionRecur['id'];
bgm's avatar
bgm committed
467

Mathieu Lutfy's avatar
Mathieu Lutfy committed
468
        // Same approach as api repeattransaction. Find last contribution associated
bgm's avatar
bgm committed
469
        // with our recurring contribution.
Mathieu Lutfy's avatar
Mathieu Lutfy committed
470 471
        $contribution = civicrm_api3('contribution', 'getsingle', array(
          'return' => array('id', 'contribution_status_id', 'total_amount', 'trxn_id'),
bgm's avatar
bgm committed
472
          'contribution_recur_id' => $this->contribution_recur_id,
Mathieu Lutfy's avatar
Mathieu Lutfy committed
473
          'options' => array('limit' => 1, 'sort' => 'id DESC'),
bgm's avatar
bgm committed
474
        ));
Mathieu Lutfy's avatar
Mathieu Lutfy committed
475 476 477 478
        $this->previous_contribution = $contribution;
      }
      catch (Exception $e) {
        $this->exception('Cannot find recurring contribution for subscription ID: ' . $this->subscription_id . '. ' . $e->getMessage());
bgm's avatar
bgm committed
479 480 481
      }
    }
  }
Mathieu Lutfy's avatar
Mathieu Lutfy committed
482 483 484 485 486 487 488

  public function exception($message) {
    $errorMessage = 'StripeIPN Exception: Event: ' . $this->event_type . ' Error: ' . $message;
    Civi::log()->debug($errorMessage);
    http_response_code(400);
    exit(1);
  }
bgm's avatar
bgm committed
489
}