Skip to content
Snippets Groups Projects
Commit 125a8233 authored by drastik's avatar drastik
Browse files

first updates for 1.2

parent de071489
Branches
Tags
No related merge requests found
<?php
/*
* Payment Processor class for Stripe
*/
class CRM_Core_Payment_Stripe extends CRM_Core_Payment {
/**
* We only need one instance of this object. So we use the singleton
* pattern and cache the instance in this variable
*
* @var object
* @static
*/
static private $_singleton = null;
/**
* mode of operation: live or test
*
* @var object
* @static
*/
static protected $_mode = null;
/**
* Constructor
*
* @param string $mode the mode of operation: live or test
*
* @return void
*/
function __construct($mode, &$paymentProcessor) {
$this->_mode = $mode;
$this->_paymentProcessor = $paymentProcessor;
$this->_processorName = ts('Stripe');
}
/**
* Singleton function used to manage this object
*
* @param string $mode the mode of operation: live or test
*
* @return object
* @static
*
*/
static function &singleton($mode, &$paymentProcessor) {
$processorName = $paymentProcessor['name'];
if (self::$_singleton[$processorName] === NULL ) {
self::$_singleton[$processorName] = new self($mode, $paymentProcessor);
}
return self::$_singleton[$processorName];
}
/**
* This function checks to see if we have the right config values
*
* @return string the error message if any
* @public
*/
function checkConfig() {
$config = CRM_Core_Config::singleton();
$error = array();
if (empty($this->_paymentProcessor['user_name'])) {
$error[] = ts('The "Secret Key" is not set in the Stripe Payment Processor settings.');
}
if (empty($this->_paymentProcessor['password'])) {
$error[] = ts('The "Publishable Key" is not set in the Stripe Payment Processor settings.');
}
if (!empty($error)) {
return implode('<p>', $error);
}
else {
return NULL;
}
}
/**
* Submit a payment using Stripe's PHP API:
* https://stripe.com/docs/api?lang=php
*
* @param array $params assoc array of input parameters for this transaction
*
* @return array the result in a nice formatted array (or an error object)
* @public
*/
function doDirectPayment(&$params) {
//Include Stripe library & Set API credentials.
require_once("stripe-php/lib/Stripe.php");
Stripe::setApiKey($this->_paymentProcessor['user_name']);
//Stripe amount required in cents.
$amount = $params['amount'] * 100;
//It would require 3 digits after the decimal for one to make it this far, CiviCRM prevents this, but let's be redundant.
$amount = number_format($amount, 0, '', '');
//Check for existing customer, create new otherwise.
$email = $params['email'];
$customer_query = CRM_Core_DAO::singleValueQuery("SELECT id FROM civicrm_stripe_customers WHERE email = '$email'");
//Use Stripe.js instead of raw card details.
if(isset($params['stripe_token'])) {
$card_details = $params['stripe_token'];
} else {
CRM_Core_Error::fatal(ts('Stripe.js token was not passed! Have you turned on the CiviCRM-Stripe CMS module?'));
}
/****
* If for some reason you cannot use Stripe.js and you are aware of PCI Compliance issues, here is the alternative to Stripe.js:
****/
//Prepare Card details in advance to use for new Stripe Customer object if we need.
/*
$cc_name = $params['first_name'] . " ";
if (strlen($params['middle_name']) > 0) {
$cc_name .= $params['middle_name'] . " ";
}
$cc_name .= $params['last_name'];
$card_details = array(
'number' => $params['credit_card_number'],
'exp_month' => $params['month'],
'exp_year' => $params['year'],
'cvc' => $params['cvv2'],
'name' => $cc_name,
'address_line1' => $params['street_address'],
'address_state' => $params['state_province'],
'address_zip' => $params['postal_code'],
);
*/
//Create a new Customer in Stripe
if(!isset($customer_query)) {
$stripe_customer = Stripe_Customer::create(array(
'description' => 'Payment from CiviCRM',
'card' => $card_details,
'email' => $email,
));
//Store the relationship between CiviCRM's email address for the Contact & Stripe's Customer ID
if(isset($stripe_customer)) {
CRM_Core_DAO::executeQuery("INSERT INTO civicrm_stripe_customers (email, id) VALUES ('$email', '$stripe_customer->id')");
} else {
CRM_Core_Error::fatal(ts('There was an error saving new customer within Stripe. Is Stripe down?'));
}
} else {
$stripe_customer = Stripe_Customer::retrieve($customer_query);
if(!empty($stripe_customer)) {
$stripe_customer->card = $card_details;
$stripe_customer->save();
} else {
$stripe_customer = Stripe_Customer::create(array(
'description' => 'Donor from CiviCRM',
'card' => $card_details,
'email' => $email,
));
//Somehow a customer ID saved in the system no longer pairs with a Customer within Stripe. (Perhaps deleted using Stripe interface?)
//Store the relationship between CiviCRM's email address for the Contact & Stripe's Customer ID
if(isset($stripe_customer)) {
CRM_Core_DAO::executeQuery("DELETE FROM civicrm_stripe_customers WHERE email = '$email'");
CRM_Core_DAO::executeQuery("INSERT INTO civicrm_stripe_customers (email, id) VALUES ('$email', '$stripe_customer->id')");
} else {
CRM_Core_Error::fatal(ts('There was an error saving new customer within Stripe. Is Stripe down?'));
}
}
}
//Prepare the charge array, minus Customer/Card details.
$stripe_charge = array(
'amount' => $amount,
'currency' => 'usd',
'description' => '# CiviCRM Donation Page # ' . $params['description'] . ' # Invoice ID # ' . $params['invoiceID'],
);
//Use Stripe Customer if we have a valid one. Otherwise just use the card.
if(!empty($stripe_customer->id)) {
$stripe_charge['customer'] = $stripe_customer->id;
} else {
$stripe_charge['card'] = $card_details;
}
//Handle recurring payments in doRecurPayment().
if (CRM_Utils_Array::value('is_recur', $params) && $params['contributionRecurID']) {
return $this->doRecurPayment($params, $amount, $stripe_customer);
}
//Fire away!
$stripe_response = Stripe_Charge::create($stripe_charge);
$params['trxn_id'] = $stripe_response->id;
return $params;
}
/**
* Submit a recurring payment using Stripe's PHP API:
* https://stripe.com/docs/api?lang=php
*
* @param array $params assoc array of input parameters for this transaction
* @param int $amount transaction amount in USD cents
* @param object $stripe_customer Stripe customer object generated by Stripe API
*
* @return array the result in a nice formatted array (or an error object)
* @public
*/
function doRecurPayment(&$params, $amount, $stripe_customer) {
switch($this->_mode) {
case 'test':
$transaction_mode = 0;
break;
case 'live':
$transaction_mode = 1;
}
$frequency = $params['frequency_unit'];
$installments = $params['installments'];
$plan_id = "$frequency-$amount";
$stripe_plan_query = CRM_Core_DAO::singleValueQuery("SELECT plan_id FROM civicrm_stripe_plans WHERE plan_id = '$plan_id'");
if(!isset($stripe_plan_query)) {
$formatted_amount = "$" . number_format(($amount / 100), 2);
//Create a new Plan
$stripe_plan = Stripe_Plan::create(array(
"amount" => $amount,
"interval" => $frequency,
"name" => "CiviCRM $frequency" . 'ly ' . $formatted_amount,
"currency" => "usd",
"id" => $plan_id));
CRM_Core_DAO::executeQuery("INSERT INTO civicrm_stripe_plans (plan_id) VALUES ('$plan_id')");
}
//Attach the Subscription to the Stripe Customer
$stripe_response = $stripe_customer->updateSubscription(array('prorate' => FALSE, 'plan' => $plan_id));
$existing_subscription_query = CRM_Core_DAO::singleValueQuery("SELECT invoice_id FROM civicrm_stripe_subscriptions WHERE customer_id = '$stripe_customer->id'");
if(!empty($existing_subscription_query)) {
//Cancel existing Recurring Contribution in CiviCRM
$cancel_date = date("Y-m-d H:i:s");
CRM_Core_DAO::executeQuery("UPDATE civicrm_contribution_recur SET cancel_date = '$cancel_date', contribution_status_id = '3' WHERE invoice_id = '$existing_subscription_query'");
//Delete the Stripe Subscription from our cron watch list.
CRM_Core_DAO::executeQuery("DELETE FROM civicrm_stripe_subscriptions WHERE invoice_id = '$existing_subscription_query'");
}
//Calculate timestamp for the last installment
$end_time = strtotime("+$installments $frequency");
$invoice_id = $params['invoiceID'];
CRM_Core_DAO::executeQuery("INSERT INTO civicrm_stripe_subscriptions (customer_id, invoice_id, end_time, is_live) VALUES ('$stripe_customer->id', '$invoice_id', '$end_time', '$transaction_mode')");
$params['trxn_id'] = $stripe_response->id;
return $params;
}
/**
* Transfer method not in use
*
* @param array $params name value pair of contribution data
*
* @return void
* @access public
*
*/
function doTransferCheckout(&$params, $component) {
CRM_Core_Error::fatal(ts('Use direct billing instead of Transfer method.'));
}
<?php
/*
* Payment Processor class for Stripe
*/
class CRM_Core_Payment_Stripe extends CRM_Core_Payment {
/**
* We only need one instance of this object. So we use the singleton
* pattern and cache the instance in this variable
*
* @var object
* @static
*/
static private $_singleton = null;
/**
* mode of operation: live or test
*
* @var object
* @static
*/
static protected $_mode = null;
/**
* Constructor
*
* @param string $mode the mode of operation: live or test
*
* @return void
*/
function __construct($mode, &$paymentProcessor) {
$this->_mode = $mode;
$this->_paymentProcessor = $paymentProcessor;
$this->_processorName = ts('Stripe');
}
/**
* Singleton function used to manage this object
*
* @param string $mode the mode of operation: live or test
*
* @return object
* @static
*
*/
static function &singleton($mode, &$paymentProcessor) {
$processorName = $paymentProcessor['name'];
if (self::$_singleton[$processorName] === NULL ) {
self::$_singleton[$processorName] = new self($mode, $paymentProcessor);
}
return self::$_singleton[$processorName];
}
/**
* This function checks to see if we have the right config values
*
* @return string the error message if any
* @public
*/
function checkConfig() {
$config = CRM_Core_Config::singleton();
$error = array();
if (empty($this->_paymentProcessor['user_name'])) {
$error[] = ts('The "Secret Key" is not set in the Stripe Payment Processor settings.');
}
if (empty($this->_paymentProcessor['password'])) {
$error[] = ts('The "Publishable Key" is not set in the Stripe Payment Processor settings.');
}
if (!empty($error)) {
return implode('<p>', $error);
}
else {
return NULL;
}
}
/**
* Submit a payment using Stripe's PHP API:
* https://stripe.com/docs/api?lang=php
*
* @param array $params assoc array of input parameters for this transaction
*
* @return array the result in a nice formatted array (or an error object)
* @public
*/
function doDirectPayment(&$params) {
// Let a $0 transaction pass.
if(empty($params['amount']) || $params['amount'] == 0) {
return;
}
//Include Stripe library & Set API credentials.
require_once("stripe-php/lib/Stripe.php");
Stripe::setApiKey($this->_paymentProcessor['user_name']);
//Stripe amount required in cents.
$amount = $params['amount'] * 100;
//It would require 3 digits after the decimal for one to make it this far, CiviCRM prevents this, but let's be redundant.
$amount = number_format($amount, 0, '', '');
//Check for existing customer, create new otherwise.
if(isset($params['email'])) {
$email = $params['email'];
} elseif(isset($params['email-5'])) {
$email = $params['email-5'];
} elseif(isset($params['email-Primary'])) {
$email = $params['email-Primary'];
}
$customer_query = CRM_Core_DAO::singleValueQuery("SELECT id FROM civicrm_stripe_customers WHERE email = '$email'");
//Use Stripe.js instead of raw card details.
if(isset($params['stripe_token'])) {
$card_details = $params['stripe_token'];
} else {
CRM_Core_Error::fatal(ts('Stripe.js token was not passed! Have you turned on the CiviCRM-Stripe CMS module?'));
}
/****
* If for some reason you cannot use Stripe.js and you are aware of PCI Compliance issues, here is the alternative to Stripe.js:
****/
//Prepare Card details in advance to use for new Stripe Customer object if we need.
/*
$cc_name = $params['first_name'] . " ";
if (strlen($params['middle_name']) > 0) {
$cc_name .= $params['middle_name'] . " ";
}
$cc_name .= $params['last_name'];
$card_details = array(
'number' => $params['credit_card_number'],
'exp_month' => $params['month'],
'exp_year' => $params['year'],
'cvc' => $params['cvv2'],
'name' => $cc_name,
'address_line1' => $params['street_address'],
'address_state' => $params['state_province'],
'address_zip' => $params['postal_code'],
);
*/
//Create a new Customer in Stripe
if(!isset($customer_query)) {
$stripe_customer = Stripe_Customer::create(array(
'description' => 'Payment from CiviCRM',
'card' => $card_details,
'email' => $email,
));
//Store the relationship between CiviCRM's email address for the Contact & Stripe's Customer ID
if(isset($stripe_customer)) {
CRM_Core_DAO::executeQuery("INSERT INTO civicrm_stripe_customers (email, id) VALUES ('$email', '$stripe_customer->id')");
} else {
CRM_Core_Error::fatal(ts('There was an error saving new customer within Stripe. Is Stripe down?'));
}
} else {
$stripe_customer = Stripe_Customer::retrieve($customer_query);
if(!empty($stripe_customer)) {
$stripe_customer->card = $card_details;
$stripe_customer->save();
} else {
$stripe_customer = Stripe_Customer::create(array(
'description' => 'Donor from CiviCRM',
'card' => $card_details,
'email' => $email,
));
//Somehow a customer ID saved in the system no longer pairs with a Customer within Stripe. (Perhaps deleted using Stripe interface?)
//Store the relationship between CiviCRM's email address for the Contact & Stripe's Customer ID
if(isset($stripe_customer)) {
CRM_Core_DAO::executeQuery("DELETE FROM civicrm_stripe_customers WHERE email = '$email'");
CRM_Core_DAO::executeQuery("INSERT INTO civicrm_stripe_customers (email, id) VALUES ('$email', '$stripe_customer->id')");
} else {
CRM_Core_Error::fatal(ts('There was an error saving new customer within Stripe. Is Stripe down?'));
}
}
}
//Prepare the charge array, minus Customer/Card details.
$stripe_charge = array(
'amount' => $amount,
'currency' => 'usd',
'description' => '# CiviCRM Donation Page # ' . $params['description'] . ' # Invoice ID # ' . $params['invoiceID'],
);
//Use Stripe Customer if we have a valid one. Otherwise just use the card.
if(!empty($stripe_customer->id)) {
$stripe_charge['customer'] = $stripe_customer->id;
} else {
$stripe_charge['card'] = $card_details;
}
//Handle recurring payments in doRecurPayment().
if (CRM_Utils_Array::value('is_recur', $params) && $params['contributionRecurID']) {
return $this->doRecurPayment($params, $amount, $stripe_customer);
}
//Fire away!
$stripe_response = Stripe_Charge::create($stripe_charge);
$params['trxn_id'] = $stripe_response->id;
return $params;
}
/**
* Submit a recurring payment using Stripe's PHP API:
* https://stripe.com/docs/api?lang=php
*
* @param array $params assoc array of input parameters for this transaction
* @param int $amount transaction amount in USD cents
* @param object $stripe_customer Stripe customer object generated by Stripe API
*
* @return array the result in a nice formatted array (or an error object)
* @public
*/
function doRecurPayment(&$params, $amount, $stripe_customer) {
switch($this->_mode) {
case 'test':
$transaction_mode = 0;
break;
case 'live':
$transaction_mode = 1;
}
$frequency = $params['frequency_unit'];
$installments = $params['installments'];
$plan_id = "$frequency-$amount";
$stripe_plan_query = CRM_Core_DAO::singleValueQuery("SELECT plan_id FROM civicrm_stripe_plans WHERE plan_id = '$plan_id'");
if(!isset($stripe_plan_query)) {
$formatted_amount = "$" . number_format(($amount / 100), 2);
//Create a new Plan
$stripe_plan = Stripe_Plan::create(array(
"amount" => $amount,
"interval" => $frequency,
"name" => "CiviCRM $frequency" . 'ly ' . $formatted_amount,
"currency" => "usd",
"id" => $plan_id));
CRM_Core_DAO::executeQuery("INSERT INTO civicrm_stripe_plans (plan_id) VALUES ('$plan_id')");
}
//Attach the Subscription to the Stripe Customer
$stripe_response = $stripe_customer->updateSubscription(array('prorate' => FALSE, 'plan' => $plan_id));
$existing_subscription_query = CRM_Core_DAO::singleValueQuery("SELECT invoice_id FROM civicrm_stripe_subscriptions WHERE customer_id = '$stripe_customer->id'");
if(!empty($existing_subscription_query)) {
//Cancel existing Recurring Contribution in CiviCRM
$cancel_date = date("Y-m-d H:i:s");
CRM_Core_DAO::executeQuery("UPDATE civicrm_contribution_recur SET cancel_date = '$cancel_date', contribution_status_id = '3' WHERE invoice_id = '$existing_subscription_query'");
//Delete the Stripe Subscription from our cron watch list.
CRM_Core_DAO::executeQuery("DELETE FROM civicrm_stripe_subscriptions WHERE invoice_id = '$existing_subscription_query'");
}
//Calculate timestamp for the last installment
$end_time = strtotime("+$installments $frequency");
$invoice_id = $params['invoiceID'];
CRM_Core_DAO::executeQuery("INSERT INTO civicrm_stripe_subscriptions (customer_id, invoice_id, end_time, is_live) VALUES ('$stripe_customer->id', '$invoice_id', '$end_time', '$transaction_mode')");
$params['trxn_id'] = $stripe_response->id;
return $params;
}
/**
* Transfer method not in use
*
* @param array $params name value pair of contribution data
*
* @return void
* @access public
*
*/
function doTransferCheckout(&$params, $component) {
CRM_Core_Error::fatal(ts('Use direct billing instead of Transfer method.'));
}
}
\ No newline at end of file
<?php
/*
* Handle Stripe Webhooks for recurring payments
*/
require_once 'CRM/Core/Page.php';
class CRM_Stripe_Page_Webhook extends CRM_Core_Page {
function run() {
//Get the data from stripe
$data_raw = file_get_contents("php://input");
$data = json_decode($data_raw);
if(!$data) {
CRM_Core_Error::Fatal("Stripe Callback: cannot json_decode data, exiting. <br /> $data");
}
$test_mode = ! $data->livemode;
$stripe_key = CRM_Core_DAO::singleValueQuery("SELECT user_name FROM civicrm_payment_processor WHERE payment_processor_type = 'Stripe' AND is_test = '$test_mode'");
require_once ("packages/stripe-php/lib/Stripe.php");
Stripe::setApiKey($stripe_key);
//Retrieve Event from Stripe using ID even though we already have the values now.
//This is for extra security precautions mentioned here: https://stripe.com/docs/webhooks
$stripe_event_data = Stripe_Event::retrieve($data->id);
$customer_id = $stripe_event_data->data->object->customer;
switch($stripe_event_data->type) {
//Successful recurring payment
case 'invoice.payment_succeeded':
//Get the Stripe charge object
try {
$charge = Stripe_Charge::retrieve($stripe_event_data->data->object->charge);
} catch(Exception $e) {
CRM_Core_Error::Fatal("Failed to retrieve Stripe charge. Message: " . $e->getMessage());
break;
}
//Find the recurring contribution in CiviCRM by mapping it from Stripe
$rel_info_query = CRM_Core_DAO::executeQuery("SELECT invoice_id, end_time FROM civicrm_stripe_subscriptions WHERE customer_id = '$customer_id'");
if(!empty($rel_info_query)) {
$rel_info_query->fetch();
$invoice_id = $rel_info_query->invoice_id;
$end_time = $rel_info_query->end_time;
} else {
CRM_Core_Error::Fatal("Error relating this customer ($customer_id) to the one in civicrm_stripe_subscriptions");
}
//Compare against now + 24hrs to prevent charging 1 extra day.
$time_compare = time() + 86400;
//Fetch Civi's info about this recurring object
$recur_contrib_query = CRM_Core_DAO::executeQuery("SELECT id, contact_id, currency, contribution_status_id, is_test, contribution_type_id, payment_instrument_id, campaign_id FROM civicrm_contribution_recur WHERE invoice_id = '$invoice_id'");
if(!empty($recur_contrib_query)) {
$recur_contrib_query->fetch();
} else {
CRM_Core_Error::Fatal("ERROR: Stripe triggered a Webhook on an invoice not found in civicrm_contribution_recur: " . $stripe_event_data);
}
//Build some params
$stripe_customer = Stripe_Customer::retrieve($customer_id);
$recieve_date = date("Y-m-d H:i:s", $charge->created);
$total_amount = $charge->amount / 100;
$fee_amount = $charge->fee / 100;
$net_amount = $total_amount - $fee_amount;
$transaction_id = $charge->id;
$new_invoice_id = $stripe_event_data->data->object->id;
if(empty($recur_contrib_query->campaign_id)) {
$recur_contrib_query->campaign_id = 'NULL';
}
$first_contrib_check = CRM_Core_DAO::singleValueQuery("SELECT id FROM civicrm_contribution WHERE invoice_id = '$invoice_id' AND contribution_status_id = '2'");
if(!empty($first_contrib_check)) {
CRM_Core_DAO::executeQuery("UPDATE civicrm_contribution SET contribution_status_id = '1' WHERE id = '$first_contrib_check'");
return;
}
//Create this instance of the contribution for accounting in CiviCRM
CRM_Core_DAO::executeQuery("
INSERT INTO civicrm_contribution (
contact_id, contribution_type_id, payment_instrument_id, receive_date,
total_amount, fee_amount, net_amount, trxn_id, invoice_id, currency,
contribution_recur_id, is_test, contribution_status_id, campaign_id
) VALUES (
'$recur_contrib_query->contact_id', '$recur_contrib_query->contribution_type_id', '$recur_contrib_query->payment_instrument_id', '$recieve_date',
'$total_amount', '$fee_amount', '$net_amount', '$transaction_id', '$new_invoice_id', '$recur_contrib_query->currency',
'$recur_contrib_query->id', '$recur_contrib_query->is_test', '1', $recur_contrib_query->campaign_id
)");
if($time_compare > $end_time) {
$end_date = date("Y-m-d H:i:s", $end_time);
//Final payment. Recurring contribution complete
$stripe_customer->cancelSubscription();
CRM_Core_DAO::executeQuery("DELETE FROM civicrm_stripe_subscriptions WHERE invoice_id = '$invoice_id'");
CRM_Core_DAO::executeQuery("UPDATE civicrm_contribution_recur SET end_date = '$end_date', contribution_status_id = '1' WHERE invoice_id = '$invoice_id'");
return;
}
//Successful charge & more to come so set recurring contribution status to In Progress
if($recur_contrib_query->contribution_status_id != 5) {
CRM_Core_DAO::executeQuery("UPDATE civicrm_contribution_recur SET contribution_status_id = 5 WHERE invoice_id = '$invoice_id'");
return;
}
break;
//Failed recurring payment
case 'invoice.payment_failed':
//Get the Stripe charge object
try {
$charge = Stripe_Charge::retrieve($stripe_event_data->data->object->charge);
} catch(Exception $e) {
CRM_Core_Error::Fatal("Failed to retrieve Stripe charge. Message: " . $e->getMessage());
break;
}
//Find the recurring contribution in CiviCRM by mapping it from Stripe
$invoice_id = CRM_Core_DAO::singleValueQuery("SELECT invoice_id FROM civicrm_stripe_subscriptions WHERE customer_id = '$customer_id'");
if(empty($invoice_id)) {
CRM_Core_Error::Fatal("Error relating this customer ($customer_id) to the one in civicrm_stripe_subscriptions");
}
//Fetch Civi's info about this recurring object
$recur_contrib_query = CRM_Core_DAO::executeQuery("SELECT id, contact_id, currency, contribution_status_id, is_test, contribution_type_id, payment_instrument_id, campaign_id FROM civicrm_contribution_recur WHERE invoice_id = '$invoice_id'");
if(!empty($recur_contrib_query)) {
$recur_contrib_query->fetch();
} else {
CRM_Core_Error::Fatal("ERROR: Stripe triggered a Webhook on an invoice not found in civicrm_contribution_recur: " . $stripe_event_data);
}
//Build some params
$recieve_date = date("Y-m-d H:i:s", $charge->created);
$total_amount = $charge->amount / 100;
$fee_amount = $charge->fee / 100;
$net_amount = $total_amount - $fee_amount;
$transaction_id = $charge->id;
if(empty($recur_contrib_query->campaign_id)) {
$recur_contrib_query->campaign_id = 'NULL';
}
//Create this instance of the contribution for accounting in CiviCRM
CRM_Core_DAO::executeQuery("
INSERT INTO civicrm_contribution (
contact_id, contribution_type_id, payment_instrument_id, receive_date,
total_amount, fee_amount, net_amount, trxn_id, invoice_id, currency,
contribution_recur_id, is_test, contribution_status_id, campaign_id
) VALUES (
'$recur_contrib_query->contact_id', '$recur_contrib_query->contribution_type_id', '$recur_contrib_query->payment_instrument_id', '$recieve_date',
'$total_amount', '$fee_amount', '$net_amount', '$transaction_id', '$invoice_id', '$recur_contrib_query->currency',
'$recur_contrib_query->id', '$recur_contrib_query->is_test', '4', $recur_contrib_query->campaign_id
)");
//Failed charge. Set to status to: Failed
if($recur_contrib_query->contribution_status_id != 4) {
CRM_Core_DAO::executeQuery("UPDATE civicrm_contribution_recur SET contribution_status_id = 4 WHERE invoice_id = '$invoice_id'");
return;
} else {
//This has failed more than once. Now what?
}
break;
//One-time donation and per invoice payment
case 'charge.succeeded':
//Not implemented
break;
}
parent::run();
}
}
<?php
/*
* Handle Stripe Webhooks for recurring payments
*/
require_once 'CRM/Core/Page.php';
class CRM_Stripe_Page_Webhook extends CRM_Core_Page {
function run() {
//Get the data from stripe
$data_raw = file_get_contents("php://input");
$data = json_decode($data_raw);
if(!$data) {
CRM_Core_Error::Fatal("Stripe Callback: cannot json_decode data, exiting. <br /> $data");
}
$test_mode = ! $data->livemode;
$stripe_key = CRM_Core_DAO::singleValueQuery("SELECT user_name FROM civicrm_payment_processor WHERE payment_processor_type = 'Stripe' AND is_test = '$test_mode'");
require_once ("packages/stripe-php/lib/Stripe.php");
Stripe::setApiKey($stripe_key);
//Retrieve Event from Stripe using ID even though we already have the values now.
//This is for extra security precautions mentioned here: https://stripe.com/docs/webhooks
$stripe_event_data = Stripe_Event::retrieve($data->id);
$customer_id = $stripe_event_data->data->object->customer;
switch($stripe_event_data->type) {
//Successful recurring payment
case 'invoice.payment_succeeded':
//Get the Stripe charge object
try {
$charge = Stripe_Charge::retrieve($stripe_event_data->data->object->charge);
} catch(Exception $e) {
CRM_Core_Error::Fatal("Failed to retrieve Stripe charge. Message: " . $e->getMessage());
break;
}
//Find the recurring contribution in CiviCRM by mapping it from Stripe
$rel_info_query = CRM_Core_DAO::executeQuery("SELECT invoice_id, end_time FROM civicrm_stripe_subscriptions WHERE customer_id = '$customer_id'");
if(!empty($rel_info_query)) {
$rel_info_query->fetch();
$invoice_id = $rel_info_query->invoice_id;
$end_time = $rel_info_query->end_time;
} else {
CRM_Core_Error::Fatal("Error relating this customer ($customer_id) to the one in civicrm_stripe_subscriptions");
}
//Compare against now + 24hrs to prevent charging 1 extra day.
$time_compare = time() + 86400;
//Fetch Civi's info about this recurring object
$recur_contrib_query = CRM_Core_DAO::executeQuery("SELECT id, contact_id, currency, contribution_status_id, is_test, contribution_type_id, payment_instrument_id, campaign_id FROM civicrm_contribution_recur WHERE invoice_id = '$invoice_id'");
if(!empty($recur_contrib_query)) {
$recur_contrib_query->fetch();
} else {
CRM_Core_Error::Fatal("ERROR: Stripe triggered a Webhook on an invoice not found in civicrm_contribution_recur: " . $stripe_event_data);
}
//Build some params
$stripe_customer = Stripe_Customer::retrieve($customer_id);
$recieve_date = date("Y-m-d H:i:s", $charge->created);
$total_amount = $charge->amount / 100;
$fee_amount = $charge->fee / 100;
$net_amount = $total_amount - $fee_amount;
$transaction_id = $charge->id;
$new_invoice_id = $stripe_event_data->data->object->id;
if(empty($recur_contrib_query->campaign_id)) {
$recur_contrib_query->campaign_id = 'NULL';
}
$first_contrib_check = CRM_Core_DAO::singleValueQuery("SELECT id FROM civicrm_contribution WHERE invoice_id = '$invoice_id' AND contribution_status_id = '2'");
if(!empty($first_contrib_check)) {
CRM_Core_DAO::executeQuery("UPDATE civicrm_contribution SET contribution_status_id = '1' WHERE id = '$first_contrib_check'");
return;
}
//Create this instance of the contribution for accounting in CiviCRM
CRM_Core_DAO::executeQuery("
INSERT INTO civicrm_contribution (
contact_id, contribution_type_id, payment_instrument_id, receive_date,
total_amount, fee_amount, net_amount, trxn_id, invoice_id, currency,
contribution_recur_id, is_test, contribution_status_id, campaign_id
) VALUES (
'$recur_contrib_query->contact_id', '$recur_contrib_query->contribution_type_id', '$recur_contrib_query->payment_instrument_id', '$recieve_date',
'$total_amount', '$fee_amount', '$net_amount', '$transaction_id', '$new_invoice_id', '$recur_contrib_query->currency',
'$recur_contrib_query->id', '$recur_contrib_query->is_test', '1', $recur_contrib_query->campaign_id
)");
if($time_compare > $end_time) {
$end_date = date("Y-m-d H:i:s", $end_time);
//Final payment. Recurring contribution complete
$stripe_customer->cancelSubscription();
CRM_Core_DAO::executeQuery("DELETE FROM civicrm_stripe_subscriptions WHERE invoice_id = '$invoice_id'");
CRM_Core_DAO::executeQuery("UPDATE civicrm_contribution_recur SET end_date = '$end_date', contribution_status_id = '1' WHERE invoice_id = '$invoice_id'");
return;
}
//Successful charge & more to come so set recurring contribution status to In Progress
if($recur_contrib_query->contribution_status_id != 5) {
CRM_Core_DAO::executeQuery("UPDATE civicrm_contribution_recur SET contribution_status_id = 5 WHERE invoice_id = '$invoice_id'");
return;
}
break;
//Failed recurring payment
case 'invoice.payment_failed':
//Get the Stripe charge object
try {
$charge = Stripe_Charge::retrieve($stripe_event_data->data->object->charge);
} catch(Exception $e) {
CRM_Core_Error::Fatal("Failed to retrieve Stripe charge. Message: " . $e->getMessage());
break;
}
//Find the recurring contribution in CiviCRM by mapping it from Stripe
$invoice_id = CRM_Core_DAO::singleValueQuery("SELECT invoice_id FROM civicrm_stripe_subscriptions WHERE customer_id = '$customer_id'");
if(empty($invoice_id)) {
CRM_Core_Error::Fatal("Error relating this customer ($customer_id) to the one in civicrm_stripe_subscriptions");
}
//Fetch Civi's info about this recurring object
$recur_contrib_query = CRM_Core_DAO::executeQuery("SELECT id, contact_id, currency, contribution_status_id, is_test, contribution_type_id, payment_instrument_id, campaign_id FROM civicrm_contribution_recur WHERE invoice_id = '$invoice_id'");
if(!empty($recur_contrib_query)) {
$recur_contrib_query->fetch();
} else {
CRM_Core_Error::Fatal("ERROR: Stripe triggered a Webhook on an invoice not found in civicrm_contribution_recur: " . $stripe_event_data);
}
//Build some params
$recieve_date = date("Y-m-d H:i:s", $charge->created);
$total_amount = $charge->amount / 100;
$fee_amount = $charge->fee / 100;
$net_amount = $total_amount - $fee_amount;
$transaction_id = $charge->id;
if(empty($recur_contrib_query->campaign_id)) {
$recur_contrib_query->campaign_id = 'NULL';
}
//Create this instance of the contribution for accounting in CiviCRM
CRM_Core_DAO::executeQuery("
INSERT INTO civicrm_contribution (
contact_id, contribution_type_id, payment_instrument_id, receive_date,
total_amount, fee_amount, net_amount, trxn_id, invoice_id, currency,
contribution_recur_id, is_test, contribution_status_id, campaign_id
) VALUES (
'$recur_contrib_query->contact_id', '$recur_contrib_query->contribution_type_id', '$recur_contrib_query->payment_instrument_id', '$recieve_date',
'$total_amount', '$fee_amount', '$net_amount', '$transaction_id', '$invoice_id', '$recur_contrib_query->currency',
'$recur_contrib_query->id', '$recur_contrib_query->is_test', '4', $recur_contrib_query->campaign_id
)");
//Failed charge. Set to status to: Failed
if($recur_contrib_query->contribution_status_id != 4) {
CRM_Core_DAO::executeQuery("UPDATE civicrm_contribution_recur SET contribution_status_id = 4 WHERE invoice_id = '$invoice_id'");
return;
} else {
//This has failed more than once. Now what?
}
break;
//One-time donation and per invoice payment
case 'charge.succeeded':
//Not implemented
break;
}
parent::run();
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment