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

Merge pull request #75 from rgburton/issue43-webform_civicrm

Make civicrm stripe extension work with webform_civicrm.
Determine if stripe is really the intended payment processor.
Present "Edit CC Details" button if users "Go Back" in the submission process.
parents 7e609013 a7c40820
Branches
Tags
No related merge requests found
......@@ -98,6 +98,22 @@ class CRM_Core_Payment_Stripe extends CRM_Core_Payment {
CRM_Core_Error::debug_log_message("Stripe_Error {$op}: <pre> {$body} </pre>");
}
/**
* Check if return from stripeCatchErrors was an error object
* that should be passed back to original api caller.
*
* @param $stripeReturn
* The return from a call to stripeCatchErrors
* @return bool
*
*/
function isErrorReturn($stripeReturn) {
if (is_object($stripeReturn) && get_class($stripeReturn) == 'CRM_Core_Error') {
return true;
}
return false;
}
/**
* Run Stripe calls through this to catch exceptions gracefully.
*
......@@ -157,9 +173,23 @@ class CRM_Core_Payment_Stripe extends CRM_Core_Payment {
$error_message .= 'Code: ' . $err['code'] . '<br />';
$error_message .= 'Message: ' . $err['message'] . '<br />';
if (isset($error_url)) {
// Redirect to first page of form and present error.
CRM_Core_Error::statusBounce("Oops! Looks like there was an error. Payment Response:
<br /> {$error_message}", $error_url);
}
else {
// Don't have return url - return error object to api
$core_err = CRM_Core_Error::singleton();
$message = 'Oops! Looks like there was an error. Payment Response: <br />' . $error_message;
if ($err['code']) {
$core_err->push($err['code'], 0, NULL, $message);
}
else {
$core_err->push(9000, 0, NULL, 'Unknown Error');
}
return $core_err;
}
}
catch (Exception $e) {
if (is_a($e, 'Stripe_Error')) {
......@@ -186,9 +216,23 @@ class CRM_Core_Payment_Stripe extends CRM_Core_Payment {
$error_message .= 'Code: ' . $err['code'] . "<br />";
$error_message .= 'Message: ' . $err['message'] . "<br />";
if (isset($error_url)) {
// Redirect to first page of form and present error.
CRM_Core_Error::statusBounce("Oops! Looks like there was an error. Payment Response:
<br /> {$error_message}", $error_url);
}
else {
// Don't have return url - return error object to api
$core_err = CRM_Core_Error::singleton();
$message = 'Oops! Looks like there was an error. Payment Response: <br />' . $error_message;
if ($err['code']) {
$core_err->push($err['code'], 0, NULL, $message);
}
else {
$core_err->push(9000, 0, NULL, 'Unknown Error');
}
return $core_err;
}
}
return $return;
......@@ -213,11 +257,18 @@ class CRM_Core_Payment_Stripe extends CRM_Core_Payment {
}
// Get proper entry URL for returning on error.
$qfKey = $params['qfKey'];
$parsed_url = parse_url($params['entryURL']);
$url_path = substr($parsed_url['path'], 1);
$params['stripe_error_url'] = $error_url = CRM_Utils_System::url($url_path,
if (!(array_key_exists('qfKey', $params))) {
// Probably not called from a civicrm form (e.g. webform) -
// will return error object to original api caller.
$params['stripe_error_url'] = $error_url = null;
}
else {
$qfKey = $params['qfKey'];
$parsed_url = parse_url($params['entryURL']);
$url_path = substr($parsed_url['path'], 1);
$params['stripe_error_url'] = $error_url = CRM_Utils_System::url($url_path,
$parsed_url['query'] . "&_qf_Main_display=1&qfKey={$qfKey}", FALSE, NULL, FALSE);
}
// Include Stripe library & Set API credentials.
require_once('stripe-php/lib/Stripe.php');
......@@ -330,6 +381,9 @@ class CRM_Core_Payment_Stripe extends CRM_Core_Payment {
// Store the relationship between CiviCRM's email address for the Contact & Stripe's Customer ID.
if (isset($stripe_customer)) {
if ($this->isErrorReturn($stripe_customer)) {
return $stripe_customer;
}
// Prepare escaped query params.
$query_params = array(
1 => array($email, 'String'),
......@@ -347,6 +401,9 @@ class CRM_Core_Payment_Stripe extends CRM_Core_Payment {
// Customer was found in civicrm_stripe database, fetch from Stripe.
$stripe_customer = $this->stripeCatchErrors('retrieve_customer', $customer_query, $params);
if (!empty($stripe_customer)) {
if ($this->isErrorReturn($stripe_customer)) {
return $stripe_customer;
}
// Avoid the 'use same token twice' issue while still using latest card.
if (!empty($params['selectMembership'])
&& $params['selectMembership']
......@@ -357,7 +414,10 @@ class CRM_Core_Payment_Stripe extends CRM_Core_Payment {
}
else {
$stripe_customer->card = $card_details;
$this->stripeCatchErrors('save', $stripe_customer, $params);
$response = $this->stripeCatchErrors('save', $stripe_customer, $params);
if (isset($response) && $this->isErrorReturn($response)) {
return $response;
}
}
}
else {
......@@ -375,6 +435,9 @@ class CRM_Core_Payment_Stripe extends CRM_Core_Payment {
// 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)) {
if ($this->isErrorReturn($stripe_customer)) {
return $stripe_customer;
}
// Delete whatever we have for this customer.
$query_params = array(
1 => array($email, 'String'),
......@@ -429,19 +492,33 @@ class CRM_Core_Payment_Stripe extends CRM_Core_Payment {
// Fire away! Check for errors before trying to submit.
$stripe_response = $this->stripeCatchErrors('charge', $stripe_charge, $params);
if (!empty($stripe_response)) {
if ($this->isErrorReturn($stripe_response)) {
return $stripe_response;
}
// Success! Return some values for CiviCRM.
$params['trxn_id'] = $stripe_response->id;
// Return fees & net amount for Civi reporting.
// Uses new Balance Trasaction object.
$balance_transaction = $this->stripeCatchErrors('retrieve_balance_transaction', $stripe_response->balance_transaction, $params);
if (!empty($balance_transaction)) {
if ($this->isErrorReturn($balance_transaction)) {
return $balance_transaction;
}
$params['fee_amount'] = $balance_transaction->fee / 100;
$params['net_amount'] = $balance_transaction->net / 100;
}
}
else {
// There was no response from Stripe on the create charge command.
CRM_Core_Error::statusBounce('Stripe transaction response not recieved! Check the Logs section of your stripe.com account.', $error_url);
if (isset($error_url)) {
CRM_Core_Error::statusBounce('Stripe transaction response not recieved! Check the Logs section of your stripe.com account.', $error_url);
}
else {
// Don't have return url - return error object to api
$core_err = CRM_Core_Error::singleton();
$core_err->push(9000, 0, NULL, 'Stripe transaction response not recieved! Check the Logs section of your stripe.com account.');
return $core_err;
}
}
return $params;
......
......@@ -5,6 +5,7 @@
(function($, CRM) {
var $form, $submit, buttonText;
var isWebform = false;
// Response from Stripe.createToken.
function stripeResponseHandler(status, response) {
......@@ -29,6 +30,8 @@
var token = response['id'];
// Update form with the token & submit.
$form.find("input#stripe-token").val(token);
$form.find("input#credit_card_number").removeAttr('name');
$form.find("input#cvv2").removeAttr('name');
$submit.prop('disabled', false);
window.onbeforeunload = null;
$form.get(0).submit();
......@@ -41,8 +44,49 @@
Stripe.setPublishableKey($('#stripe-pub-key').val());
});
if ($('.webform-client-form').length) {
isWebform = true;
$('form.webform-client-form').addClass('stripe-payment-form');
}
else {
$('#crm-container>form').addClass('stripe-payment-form');
}
$form = $('form.stripe-payment-form');
$submit = $form.find('[type="submit"]');
if (isWebform) {
$submit = $form.find('.button-primary');
}
else {
$submit = $form.find('input[type="submit"]');
}
if (isWebform) {
if (!($('#action').length)) {
$form.append('<input type="hidden" name="op" id="action" />');
}
$(document).keypress(function(event) {
if (event.which == 13) {
event.preventDefault();
$submit.click();
}
});
$(":submit").click(function() {
$('#action').val(this.value);
});
$('#billingcheckbox:input').hide();
$('label[for="billingcheckbox"]').hide();
}
else {
// This is native civicrm form - check for existing token
if ($form.find("input#stripe-token").val()) {
$('.credit_card_info-group').hide();
$('#billing-payment-block').append('<input type="button" value="Edit CC details" id="ccButton" />');
$('#ccButton').click(function() {
$('.credit_card_info-group').show();
$('#ccButton').hide();
$form.find('input#stripe-token').val('');
});
}
}
$submit.removeAttr('onclick');
......@@ -50,25 +94,61 @@
// Intercept form submission.
$form.submit(function (event) {
if (isWebform) {
var $processorFields = $('.civicrm-enabled[name$="civicrm_1_contribution_1_contribution_payment_processor_id]"]');
if ($('#action').attr('value') == "< Previous Page") {
return true;
}
if ($('#wf-crm-billing-total').length) {
if ($('#wf-crm-billing-total').data('data-amount') == '0') {
return true;
}
}
if ($processorFields.length) {
if ($processorFields.filter(':checked').val() == '0') {
return true;
}
if (!($form.find('input[name="stripe_token"]').length)) {
return true;
}
}
}
// Disable the submit button to prevent repeated clicks, cache button text, restore if Stripe returns error
buttonText = $submit.attr('value');
$submit.prop('disabled', true).attr('value', 'Processing');
if ($('#priceset').length) {
if ($form.find("#priceset input[type='radio']:checked").data('amount') == 0) {
return true;
}
}
// Handle multiple payment options and Stripe not being chosen.
if ($form.find(".crm-section.payment_processor-section").length > 0) {
if (!($form.find('input[name="hidden_processor"]').length > 0)) {
return true;
}
if ($form.find('input[name="payment_processor"]:checked').length) {
processorId=$form.find('input[name="payment_processor"]:checked').val();
if (!($form.find('input[name="stripe_token"]').length) || ($('#stripe-id').length && $('#stripe-id').val() != processorId)) {
return true;
}
}
}
// 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())) {
return true;
}
// Handle reuse of existing token
if ($form.find("input#stripe-token").val()) {
$form.find("input#credit_card_number").removeAttr('name');
$form.find("input#cvv2").removeAttr('name');
return true;
}
event.preventDefault();
event.stopPropagation();
......
......@@ -111,6 +111,28 @@ function stripe_civicrm_upgrade($op, CRM_Queue_Queue $queue = NULL) {
return _stripe_civix_civicrm_upgrade($op, $queue);
}
/**
* Implementation of hook_civicrm_validateForm().
*
* Prevent server validation of cc fields
*
* @param $formName - the name of the form
* @param $fields - Array of name value pairs for all 'POST'ed form values
* @param $files - Array of file properties as sent by PHP POST protocol
* @param $form - reference to the form object
* @param $errors - Reference to the errors array.
*/
function stripe_civicrm_validateForm($formName, &$fields, &$files, &$form, &$errors) {
if (isset($form->_paymentProcessor['payment_processor_type']) &&$form->_paymentProcessor['payment_processor_type'] == 'Stripe') {
if($form->elementExists('credit_card_number')){
$form->removeElement('credit_card_number');
}
if($form->elementExists('cvv2')){
$form->removeElement('cvv2');
}
}
}
/**
* Implementation of hook_civicrm_buildForm().
*
......@@ -122,11 +144,15 @@ function stripe_civicrm_buildForm($formName, &$form) {
if (!stristr($formName, '_Confirm') && !stristr($formName, '_ThankYou')) {
// This is the 'Main', or first step of the form that collects CC data.
if (!isset($form->_elementIndex['stripe_token'])) {
if (empty($form->_attributes['class'])) {
$form->_attributes['class'] = '';
}
$form->_attributes['class'] .= ' stripe-payment-form';
/*
* Moved this to civicrm_stripe.js for webform patch
* if (empty($form->_attributes['class'])) {
* $form->_attributes['class'] = '';
* }
* $form->_attributes['class'] .= ' stripe-payment-form';
*/
$form->addElement('hidden', 'stripe_token', NULL, array('id' => 'stripe-token'));
$form->addElement('hidden', 'stripe_id', $form->_paymentProcessor['id'], array('id' => 'stripe-id'));
stripe_add_stripe_js($form);
}
}
......@@ -140,6 +166,8 @@ function stripe_civicrm_buildForm($formName, &$form) {
if (!empty($params['stripe_token'])) {
// Stash the token (including its value) in Confirm, in case they go backwards.
$form->addElement('hidden', 'stripe_token', $params['stripe_token'], array('id' => 'stripe-token'));
// Stash stripe payment processor id
$form->addElement('hidden', 'stripe_id', $form->_paymentProcessor['id'], array('id' => 'stripe-id'));
}
}
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment