Skip to content
Snippets Groups Projects
Commit 59eb00b4 authored by Joshua Walker's avatar Joshua Walker
Browse files

Merge pull request #57 from kurund/4.5-1.8

updated stripe to v2, make compatiable with webform_civicrm, code cleanu...
parents f715ed4b cb2d26c3
No related branches found
No related tags found
No related merge requests found
<?php
/*
* Payment Processor class for Stripe
*/
class CRM_Core_Payment_Stripe extends CRM_Core_Payment {
/**
......@@ -11,7 +13,7 @@ class CRM_Core_Payment_Stripe extends CRM_Core_Payment {
* @var object
* @static
*/
static private $_singleton = null;
static private $_singleton = NULL;
/**
* Mode of operation: live or test.
......@@ -19,7 +21,7 @@ class CRM_Core_Payment_Stripe extends CRM_Core_Payment {
* @var object
* @static
*/
static protected $_mode = null;
static protected $_mode = NULL;
/**
* Constructor
......@@ -30,29 +32,29 @@ class CRM_Core_Payment_Stripe extends CRM_Core_Payment {
* @return void
*/
function __construct($mode, &$paymentProcessor) {
self::$_mode = $mode;
self::$_mode = $mode;
$this->_paymentProcessor = $paymentProcessor;
$this->_processorName = ts('Stripe');
$this->_processorName = ts('Stripe');
}
/**
* Singleton function used to manage this object.
*
* @param string $mode the mode of operation: live or test
* @param object $paymentProcessor the details of the payment processor being invoked
* @param object $paymentForm reference to the form object if available
* @param boolean $force should we force a reload of this payment object
* @param string $mode the mode of operation: live or test
* @param object $paymentProcessor the details of the payment processor being invoked
* @param object $paymentForm reference to the form object if available
* @param boolean $force should we force a reload of this payment object
*
* @return object
* @static
*
*/
static function &singleton($mode = 'test', &$paymentProcessor, &$paymentForm = NULL, $force = FALSE) {
$processorName = $paymentProcessor['name'];
if (self::$_singleton[$processorName] === NULL || $force) {
self::$_singleton[$processorName] = new self($mode, $paymentProcessor);
}
return self::$_singleton[$processorName];
$processorName = $paymentProcessor['name'];
if (self::$_singleton[$processorName] === NULL || $force) {
self::$_singleton[$processorName] = new self($mode, $paymentProcessor);
}
return self::$_singleton[$processorName];
}
/**
......@@ -115,39 +117,38 @@ class CRM_Core_Payment_Stripe extends CRM_Core_Payment {
switch ($op) {
case 'create_customer':
$return = Stripe_Customer::create($params);
break;
break;
case 'charge':
$return = Stripe_Charge::create($params);
break;
break;
case 'save':
$return = $params->save();
break;
break;
case 'create_plan':
$return = Stripe_Plan::create($params);
break;
break;
case 'retrieve_customer':
$return = Stripe_Customer::retrieve($params);
break;
break;
case 'retrieve_balance_transaction':
$return = Stripe_BalanceTransaction::retrieve($params);
break;
break;
default:
$return = Stripe_Customer::create($params);
break;
$return = Stripe_Customer::create($params);
break;
}
}
catch(Stripe_CardError $e) {
} catch (Stripe_CardError $e) {
$this->logStripeException($op, $e);
$error_message = '';
// Since it's a decline, Stripe_CardError will be caught
$body = $e->getJsonBody();
$err = $body['error'];
$err = $body['error'];
//$error_message .= 'Status is: ' . $e->getHttpStatus() . "<br />";
////$error_message .= 'Param is: ' . $err['param'] . "<br />";
......@@ -156,8 +157,9 @@ class CRM_Core_Payment_Stripe extends CRM_Core_Payment {
$error_message .= 'Message: ' . $err['message'] . '<br />';
// Check Event vs Contribution for redirect. There must be a better way.
if(empty($params['selectMembership'])
&& empty($params['contributionPageID'])) {
if (empty($params['selectMembership'])
&& empty($params['contributionPageID'])
) {
$error_url = CRM_Utils_System::url('civicrm/event/register',
"_qf_Main_display=1&cancel=1&qfKey={$qfKey}", FALSE, NULL, FALSE);
}
......@@ -168,25 +170,24 @@ class CRM_Core_Payment_Stripe extends CRM_Core_Payment {
CRM_Core_Error::statusBounce("Oops! Looks like there was an error. Payment Response:
<br /> $error_message", $error_url);
}
catch (Exception $e) {
} catch (Exception $e) {
if (is_a($e, 'Stripe_Error')) {
foreach ($ignores as $ignore) {
if (is_a($e, $ignore['class'])) {
foreach ($ignores as $ignore) {
if (is_a($e, $ignore['class'])) {
$body = $e->getJsonBody();
$error = $body['error'];
if ($error['type'] == $ignore['type'] && $error['message'] == $ignore['message']) {
return $return;
}
}
}
return $return;
}
}
}
$this->logStripeException($op, $e);
}
// Something else happened, completely unrelated to Stripe
$error_message = '';
// Since it's a decline, Stripe_CardError will be caught
$body = $e->getJsonBody();
$err = $body['error'];
$err = $body['error'];
//$error_message .= 'Status is: ' . $e->getHttpStatus() . "<br />";
////$error_message .= 'Param is: ' . $err['param'] . "<br />";
......@@ -194,8 +195,9 @@ class CRM_Core_Payment_Stripe extends CRM_Core_Payment {
$error_message .= 'Code: ' . $err['code'] . "<br />";
$error_message .= 'Message: ' . $err['message'] . "<br />";
if(empty($params['selectMembership'])
&& empty($params['contributionPageID'])) {
if (empty($params['selectMembership'])
&& empty($params['contributionPageID'])
) {
$error_url = CRM_Utils_System::url('civicrm/event/register',
"_qf_Main_display=1&cancel=1&qfKey={$qfKey}", FALSE, NULL, FALSE);
}
......@@ -250,10 +252,10 @@ class CRM_Core_Payment_Stripe extends CRM_Core_Payment {
if (isset($params['email'])) {
$email = $params['email'];
}
elseif(isset($params['email-5'])) {
elseif (isset($params['email-5'])) {
$email = $params['email-5'];
}
elseif(isset($params['email-Primary'])) {
elseif (isset($params['email-Primary'])) {
$email = $params['email-Primary'];
}
// Prepare escaped query params.
......@@ -279,19 +281,19 @@ class CRM_Core_Payment_Stripe extends CRM_Core_Payment {
* here is the alternative to Stripe.js (also need to uncomment lines 211-215 & 275):
****/
/*
// Prepare Card details in advance to use for new Stripe Customer object if we need.
$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'],
);
*/
/*
// Prepare Card details in advance to use for new Stripe Customer object if we need.
$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'],
);
*/
// drastik - Uncomment this for Drupal debugging to dblog.
/*
......@@ -327,14 +329,19 @@ class CRM_Core_Payment_Stripe extends CRM_Core_Payment {
}
}
else {
if (empty($params['qfKey'])) {
$params['qfKey'] = '';
}
$stripe_customer = CRM_Core_Payment_Stripe::stripeCatchErrors('retrieve_customer', $customer_query, $params['qfKey']);
if (!empty($stripe_customer)) {
// Avoid the 'use same token twice' issue while still using latest card.
if(!empty($params['selectMembership'])
if (!empty($params['selectMembership'])
&& $params['selectMembership']
&& empty($params['contributionPageID'])) {
// This is a Contribution form w/ Membership option and charge is
// coming through for the 2nd time. Don't need to update customer again.
&& empty($params['contributionPageID'])
) {
// This is a Contribution form w/ Membership option and charge is
// coming through for the 2nd time. Don't need to update customer again.
}
else {
$stripe_customer->card = $card_details;
......@@ -387,8 +394,7 @@ class CRM_Core_Payment_Stripe extends CRM_Core_Payment {
$stripe_charge = array(
'amount' => $amount,
'currency' => strtolower($params['currencyID']),
'description' => $params['description'] .
' # Invoice ID # ' . $params['invoiceID'],
'description' => $params['description'] . ' # Invoice ID # ' . CRM_Utils_Array::value('invoiceID', $params),
);
// Use Stripe Customer if we have a valid one. Otherwise just use the card.
......@@ -420,7 +426,7 @@ class CRM_Core_Payment_Stripe extends CRM_Core_Payment {
}
else {
// There was no response from Stripe on the create charge command.
if(empty($params['selectMembership']) && empty($params['contributionPageID'])) {
if (empty($params['selectMembership']) && empty($params['contributionPageID'])) {
$error_url = CRM_Utils_System::url('civicrm/event/register',
'_qf_Main_display=1&cancel=1&qfKey=' . $params['qfKey'], FALSE, NULL, FALSE);
}
......@@ -473,7 +479,7 @@ class CRM_Core_Payment_Stripe extends CRM_Core_Payment {
WHERE plan_id = %1", $query_params);
if (!isset($stripe_plan_query)) {
$formatted_amount = '$' . number_format(($amount / 100), 2);
$formatted_amount = '$' . number_format(($amount / 100), 2);
// Create a new Plan.
$stripe_plan = array(
'amount' => $amount,
......@@ -485,11 +491,11 @@ class CRM_Core_Payment_Stripe extends CRM_Core_Payment {
);
$ignores = array(
array(
'class' => Stripe_InvalidRequestError,
'type' => 'invalid_request_error',
'message' => 'Plan already exists.',
),
array(
'class' => Stripe_InvalidRequestError,
'type' => 'invalid_request_error',
'message' => 'Plan already exists.',
),
);
CRM_Core_Payment_Stripe::stripeCatchErrors('create_plan', $stripe_plan, $params['qfKey'], $ignores);
// Prepare escaped query params.
......@@ -562,7 +568,8 @@ class CRM_Core_Payment_Stripe extends CRM_Core_Payment {
CRM_Core_DAO::executeQuery("INSERT INTO civicrm_stripe_subscriptions
(customer_id, invoice_id, is_live)
VALUES (%1, %2, '$transaction_mode')", $query_params);
} else {
}
else {
// Add the end time to the query params.
$query_params[3] = array($end_time, 'Integer');
CRM_Core_DAO::executeQuery("INSERT INTO civicrm_stripe_subscriptions
......
......@@ -3,23 +3,21 @@
* JS Integration between CiviCRM & Stripe.
*/
(function ($) {
// Response from Stripe.createToken.
function stripeResponseHandler(status, response) {
if (response.error) {
$('html, body').animate({ scrollTop: 0 }, 300);
$('html, body').animate({scrollTop: 0}, 300);
// Show the errors on the form.
if ($(".messages.crm-error.stripe-message").length > 0) {
$(".messages.crm-error.stripe-message").slideUp();
$(".messages.crm-error.stripe-message:first").remove();
}
$("form.stripe-payment-form").prepend('<div class="messages crm-error stripe-message">'
+'<strong>Payment Error Response:</strong>'
+'<ul id="errorList">'
+'<li>Error: ' + response.error.message + '</li>'
+'</ul>'
+'</div>');
+ '<strong>Payment Error Response:</strong>'
+ '<ul id="errorList">'
+ '<li>Error: ' + response.error.message + '</li>'
+ '</ul>'
+ '</div>');
$('form.stripe-payment-form input.form-submit').removeAttr("disabled");
}
......@@ -27,33 +25,41 @@
var token = response['id'];
// Update form with the token & submit.
$("input#stripe-token").val(token);
$("form.stripe-payment-form").get(0).submit();
// clear actual credit card information and set dummy cc details
// we are setting dummy cc details to prevent validation errors
// this is a work around so that we don't transmit sensitive data
$('#credit_card_number').val('4111111111111111');
$('#cvv2').val('111');
$('form.stripe-payment-form input.form-submit').removeAttr("disabled");
$("input[type='submit']:last").click();
}
}
// Prepare the form.
$(document).ready(function() {
$.getScript('https://js.stripe.com/v1/', function() {
Stripe.setPublishableKey(CRM.stripe.pub_key);
$.getScript('https://js.stripe.com/v2/', function () {
Stripe.setPublishableKey($('#stripe-pub-key').val());
});
/*
* Identify the payment form.
* Don't reference by form#id since it changes between payment pages
* (Contribution / Event / etc).
*/
//Patch - remove direct child selector and account for dialog forms
//Patch - remove direct child selector and account for dialog forms
$('#billing-payment-block').closest('form').addClass('stripe-payment-form');
$('#crm-container form').addClass('stripe-payment-form');
if($('#crm-ajax-dialog-1 form').length){
$('#crm-ajax-dialog-1 form').addClass('stripe-payment-form');
if ($('#crm-ajax-dialog-1 form').length) {
$('#crm-ajax-dialog-1 form').addClass('stripe-payment-form');
}
$('form.stripe-payment-form').unbind('submit');
// Intercept form submission.
$("form.stripe-payment-form").submit(function(event) {
// Intercept form submission.
$("form.stripe-payment-form").submit(function (event) {
var $form = $(this);
// Disable the submit button to prevent repeated clicks.
$form.find('.crm-form-submit').prop('disabled', true);
$("input[type='submit']:last").attr('disabled', true);
if ($form.find("#priceset input[type='radio']:checked").data('amount') == 0) {
return true;
......@@ -67,8 +73,7 @@
}
// Handle pay later (option value '0' in payment_processor radio group)
if ($form.find('input[name="payment_processor"]:checked').length &&
!parseInt($form.find('input[name="payment_processor"]:checked').val())) {
if ($form.find('input[name="payment_processor"]:checked').length && !parseInt($form.find('input[name="payment_processor"]:checked').val())) {
return true;
}
......@@ -81,8 +86,7 @@
var cc_month = $form.find('#credit_card_exp_date\\[M\\]').val();
var cc_year = $form.find('#credit_card_exp_date\\[Y\\]').val();
}
Stripe.createToken({
Stripe.card.createToken({
name: $('#billing_first_name').val() + ' ' + $('#billing_last_name').val(),
address_zip: $("#billing_postal_code-5").val(),
number: $('#credit_card_number').val(),
......@@ -94,9 +98,6 @@
// Prevent the form from submitting with the default action.
return false;
});
});
}(jQuery));
......@@ -105,25 +105,27 @@ function stripe_civicrm_upgrade($op, CRM_Queue_Queue $queue = NULL) {
*/
function stripe_civicrm_buildForm($formName, &$form) {
if (isset($form->_paymentProcessor['payment_processor_type'])
&& $form->_paymentProcessor['payment_processor_type'] == 'Stripe') {
&& $form->_paymentProcessor['payment_processor_type'] == 'Stripe'
) {
if (!stristr($formName, '_Confirm') && !stristr($formName, '_ThankYou')) {
if (empty($_GET['type'])) {
if (!isset($form->_elementIndex['stripe_token'])) {
$form->addElement('hidden', 'stripe_token', NULL, array('id'=> 'stripe-token'));
stripe_add_stripe_js($form);
}
//if (empty($_GET['type'])) {
if (!isset($form->_elementIndex['stripe_token'])) {
$form->addElement('hidden', 'stripe_token', NULL, array('id' => 'stripe-token'));
stripe_add_stripe_js($form);
}
//}
}
}
// For the 'Record Contribution' backend page.
if ($formName == 'CRM_Contribute_Form_Contribution' || $formName == 'CRM_Event_Form_Participant' || $formName == 'CRM_Member_Form_Membership' && !empty($form->_processors)) {
if (($formName == 'CRM_Contribute_Form_Contribution' || $formName == 'CRM_Event_Form_Participant' || $formName == 'CRM_Member_Form_Membership' && !empty($form->_processors)) || stristr($formName, '_Main')) {
if (!isset($form->_elementIndex['stripe_token'])) {
$form->addElement('hidden', 'stripe_token', NULL, array('id'=> 'stripe-token'));
$form->addElement('hidden', 'stripe_token', NULL, array('id' => 'stripe-token'));
stripe_add_stripe_js($form);
}
// Add email field as it would usually be found on donation forms.
if (!isset($form->_elementIndex['email']) && !empty($form->userEmail)) {
$form->addElement('hidden', 'email', $form->userEmail, array('id'=> 'user-email'));
$form->addElement('hidden', 'email', $form->userEmail, array('id' => 'user-email'));
}
}
}
......@@ -132,49 +134,46 @@ function stripe_civicrm_buildForm($formName, &$form) {
* Add publishable key and event bindings for Stripe.js.
*/
function stripe_add_stripe_js($form) {
CRM_Core_Resources::singleton()->addScriptFile('com.drastikbydesign.stripe', 'js/civicrm_stripe.js');
if (!empty($form->_paymentProcessor['password'])) {
$stripe_pub_key = $form->_paymentProcessor['password'];
}
else {
// Find Stripe's payproc ID in Civi.
$query_params = array(
1 => array('Stripe', 'String'),
);
$stripe_pp_id = CRM_Core_DAO::singleValueQuery("SELECT id
// Find Stripe's payproc ID in Civi.
$query_params = array(
1 => array('Stripe', 'String'),
);
$stripe_pp_id = CRM_Core_DAO::singleValueQuery("SELECT id
FROM civicrm_payment_processor_type
WHERE name = %1", $query_params);
// Find out if the form might use Stripe.
if (!empty($stripe_pp_id)) {
$mode = ($form->_mode == 'live' ? 0 : 1);
$query_params = array(
1 => array($stripe_pp_id, 'Integer'),
2 => array($mode, 'Integer'),
);
$stripe_procs_query = CRM_Core_DAO::executeQuery("SELECT name, password
// Find out if the form might use Stripe.
if (!empty($stripe_pp_id)) {
$mode = ($form->_mode == 'live' ? 0 : 1);
$query_params = array(
1 => array($stripe_pp_id, 'Integer'),
2 => array($mode, 'Integer'),
);
$stripe_procs_query = CRM_Core_DAO::executeQuery("SELECT name, password
FROM civicrm_payment_processor
WHERE payment_processor_type_id = %1 AND is_test = %2", $query_params);
// Loop through and see if Stripe is on this form.
while ($stripe_procs_query->fetch()) {
foreach ($form->_processors as $form_processor) {
if ($form_processor == $stripe_procs_query->name) {
$stripe_pub_key = $stripe_procs_query->password;
break;
}
if (!empty($stripe_pub_key)) { break; }
// Loop through and see if Stripe is on this form.
while ($stripe_procs_query->fetch()) {
foreach ($form->_processors as $form_processor) {
if ($form_processor == $stripe_procs_query->name) {
$stripe_pub_key = $stripe_procs_query->password;
break;
}
if (!empty($stripe_pub_key)) {
break;
}
}
}
}
}
// Add Stripe publishable key to CRM.stripe namespace.
CRM_Core_Resources::singleton()->addSetting(array(
'stripe' => array(
'pub_key' => $stripe_pub_key,
)
));
$form->addElement('hidden', 'stripe_pub_key', $stripe_pub_key, array('id' => 'stripe-pub-key'));
CRM_Core_Resources::singleton()
->addScriptFile('com.drastikbydesign.stripe', 'js/civicrm_stripe.js', 0, 'billing-block', FALSE);
}
/**
......@@ -197,10 +196,10 @@ function stripe_civicrm_managed(&$entities) {
'billing_mode' => 'form',
'user_name_label' => 'Secret Key',
'password_label' => 'Publishable Key',
'url_site_default'=> 'https://api.stripe.com/v1',
'url_recur_default' => 'https://api.stripe.com/v1',
'url_site_test_default' => 'https://api.stripe.com/v1',
'url_recur_test_default' => 'https://api.stripe.com/v1',
'url_site_default' => 'https://api.stripe.com/v2',
'url_recur_default' => 'https://api.stripe.com/v2',
'url_site_test_default' => 'https://api.stripe.com/v2',
'url_recur_test_default' => 'https://api.stripe.com/v2',
'is_recur' => 1,
'payment_type' => 1
),
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment