Forums

CF4 and Stripe payments

georgefweb 15 Jan, 2013
Hi,

I'm trying to create a CF4 form for Joomla registration that also allows me to set up payment through Stripe (http://www.stripe.com). I think I'm close but I can't get the final step to work so I'm hoping for your guidance.

In short, there are three steps to the process:

1. CUSTOM REGISTRATION FORM
Create a custom Joomla registration form (#chronoform_Registration) that includes the fields necessary to set up payment through Stripe (card number, expiration month, expiration year, CVC). Followed the helpful tutorial to set up the base registration form, then created the credit card fields with no names to prevent those fields from getting passed to the server. No problems here.

2. ADD CUSTOM JAVASCRIPT
Add some Javascript that hijacks the SUBMIT from the #chronoform_Registration form, sends the credit card form data directly to Stripe, receives a response from Stripe in the form of a token and completes the SUBMIT with the token appended to the form data. I put the JS snippet in a custom code action triggered by the On Load event. I'm able to confirm that the token is generated, so I believe also that there are no problems here, but here's the code snippet for completeness and context.

<head>
   <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
   <title>Stripe Getting Started Form</title>
   <script type="text/javascript" src="https://js.stripe.com/v1/"></script>
   <!-- jQuery is used only for this example; it isn't required to use Stripe -->
   <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js"></script>
   <script type="text/javascript">
      // this identifies your website in the createToken call below
      Stripe.setPublishableKey('MYPUBLICKEY');

      function stripeResponseHandler(status, response) {
         if (response.error) {
            // re-enable the submit button
            $('.submit-button').removeAttr("disabled");
            // show the errors on the form
            $(".payment-errors").html(response.error.message);
         } else {
            var form$ = $("#chronoform_Registration");
            // token contains id, last4, and card type
            var token = response['id'];
            // insert the token into the form so it gets submitted to the server
            form$.append("<input type='hidden' name='stripeToken' value='" + token + "' />");
            // and submit
            form$.get(0).submit();
         }
      }

      $(document).ready(function() {
         $("#chronoform_Registration").submit(function(event) {
            // disable the submit button to prevent repeated clicks
            $('.submit-button').attr("disabled", "disabled");
            // createToken returns immediately - the supplied callback submits the form if there are no errors
            Stripe.createToken({
               name: $('.cardholder-name').val(),
               number: $('.card-number').val(),
               cvc: $('.card-cvc').val(),
               exp_month: $('.card-expiry-month').val(),
               exp_year: $('.card-expiry-year').val()
            }, stripeResponseHandler);
            return false; // submit from callback
         });
      });
   </script>
</head>


3. INVOKE PHP CODE TO CHARGE THE USER IN STRIPE
The last step is to invoke some PHP code to charge the user (via the token) in Stripe once the form has been posted. On the assumption that the form data is submitted to the same registration page (at least that's what it looks like from the ACTION attribute), I tried putting the following PHP snippet in a custom code action in two places with no luck--with the Javascript custom code triggered by On Load, and separately in a custom code action under Joomla User Registration/On Success. Neither of them worked, was hoping you could point me in the right direction.

The PHP snippet looks like this:

<?php
require './stripe/lib/stripe.php';

if ($_POST) {
   Stripe::setApiKey("MYSECRETKEY");
   $error = '';
   $success = '';
   try {
      if (!isset($_POST['stripeToken']))
         throw new Exception("The Stripe Token was not generated correctly");
         Stripe_Charge::create(array("amount" => 1000,
            "currency" => "usd",
            "card" => $_POST['stripeToken']));
        $success = 'Your payment was successful.';
    }
    catch (Exception $e) {
       $error = $e->getMessage();
    }
}
?>


Thanks in advance for your help!
GreyHead 15 Jan, 2013
Hi georgefweb,

A few things here.

Your JavaScript works and is probably OK but I personally wouldn't use JQuery on a Joomla! site without explicit;y putting it into noConflict mode and using jQuery instead of $ (I'd probably write the code in MooTools anyhow).

You can load the scripts from a Custom Code action in the form OnLoad event rather than adding them to the template - then they only load with the form. Here's an example (using JQuery):
<?php
$doc= & JFactory::getDocument();
$url = JURI::root().'/components/com_chronoforms/extras/signature_pad/';
$doc->addScript($url.'jquery-1.8.3.min.js');
$doc->addScript($url.'jquery.signaturepad.min.js');
$doc->addScript($url.'json2.min.js');
$doc->addStylesheet($url.'jquery.signaturepad.css');
$script = "
if (typeof jQuery != 'undefined' ) {
	jQuery.noConflict();
}
";
$doc->addScriptDeclaration($script);
$script = "
jQuery(document).ready(function () {
	jQuery('.Chronoform').signaturePad();
});
";
$doc->addScriptDeclaration($script);
?>


The PHP code should go into a Custom Code action in the On Submit event after the Joomla! Registration (I prefer not to use the OnSuccess events unless strictly necessary, 99.9% of the time putting the following actions afterwards works perfectly well).

The PHP looks OK except that I would use an absolute path for the included file so that it is unambigious. You can’t be certain where the calling folder will be e.g.
require (JPATH_SITE.'/stripe/lib/stripe.php';


I'd also use the ChronoForms $form->data['stripeToken'] rather than $_FORM['stripeToken'] - mostly for consistency, either should work.

I see that Stripe supports CURL - that might be a neat way of doing the transaction too.

Bob
georgefweb 22 Jan, 2013
Bob, thanks for your help, the basics are working well but I've run into a validation and exception handling issue I'd appreciate your thoughts on. In a nutshell, I'd like to be able to feed error messages returned by Stripe into my Chronoform.

To refresh your memory, I'm using a custom code action in an OnLoad event to handle Stripe. The Javascript snippet hijacks the credit card form fields on submit, sends it via Ajax to Stripe and receives a tokenized version of the credit card data that can be passed to our servers for additional processing. In the event the received token has an error, I'd like to have it put the error message in my Chronoform rather than display an alert (e.g. "Your credit card number is invalid" or "Credit card declined").

Here's the current code snippet:

  <script type="text/javascript">
    // this identifies your website in the createToken call below
    Stripe.setPublishableKey('MY_PUBLIC_KEY');

    function stripeResponseHandler(status, response) {
      if (response.error) {
        // re-enable the submit button
        $('.submit-button').removeAttr("disabled");

        // ALERT USER TO ERRORS
        alert(response.error.message);

      } else {
        var form$ = $("#chronoform_Registration");
        // token contains id, last4, and card type
        var token = response['id'];
        // insert the token into the form so it gets submitted to the server
        form$.append("<input type='hidden' name='stripeToken' value='" + token + "' />");
        // and submit
        form$.get(0).submit();
      }
    }

    $(document).ready(function() {
      $("#chronoform_Registration").submit(function(event) {
      // disable the submit button to prevent repeated clicks
      $('.submit-button').attr("disabled", "disabled");

      // createToken  using Stripe API
      Stripe.createToken({
        name: $('.cardholder-name').val(),
        number: $('.card-number').val(),
        cvc: $('.card-cvc').val(),
        exp_month: $('.card-expiry-month').val(),
        exp_year: $('.card-expiry-year').val()
      }, stripeResponseHandler);
      
      // Prevent the form from submitting with the default action
      return false;
    });
});
</script>


The function stripeResponseHandler currently just issues an alert with the message but I'd like to feed this into the Chronoforms error messaging system. Based on your FAQ, I thought I would simply add the following to the class of the card number element in the HTML form:

<div class="ccms_form_element cfdiv_text" id="2_container_div" style="">
     <label>Card Number</label>
     <input maxlength="20" size="20" class="card-number validate['required','%stripeResponseHandler']" 
                 title="" type="text" value="" name="" />
     <div class="clear"></div>
     <div id="error-message-card-number"></div>
</div>


Is this correct? And what would I use to replace the alert(response.error.message) line in the Javascript?

Thanks!
GreyHead 22 Jan, 2013
Hi georgefweb,

I think that you have a couple of things knotted together here that might be better separated.

Because this is a specialised validation I'd be inclined not to link it to the ChronoForms validation. So just attach your validation function to an event Listener on the CC inputs (it probably needs to be on all of them as you assemble several values to run the check).

I'd then have the result display in a separate span attached to the end of the CC input. Not quite the same but look at the demo form for the MooStars FAQ and you'll see a span like that changing as you hover over the stars.

I'm quite fluent with MooTools but only a beginner in jQuery so can't easily suggest the code that you need to use to do this.

Bob

PS The CF validation code requires that you register and de-register validations, you can’t add them by adding functions or classes after the page loads.
georgefweb 04 Mar, 2013
Greyhead,

Hope all is well. Things are coming along pretty well with the project. My registration form uses both client-side and server side validation, and on the server side validation piece I ended up using try/catch along with alerts to handle the credit card-specific info.

This brings me to my final question (I hope): is there a way to pass a variable from one action to another?

Here's the situation. When the registration form is submitted, I want to do three things:

1. Create a customer object in Stripe that's associated to a card token, email and subscription plan
2. Register the user in Joomla using the Joomla Registration action
3. Add a row to a separate Registration table I've created to store additional profile information captured in the form

I'm using three actions, one for each step: customer server side validation for Step 1, the Joomla Registration action for Step 2 and a custom code action for Step 3.

One of the fields I'd like to add in Step 3 is something I'll call StripeID, which is the Stripe ID for the registering customer. This value is returned in Step 1 when I create a customer object and lets me associate a Joomla user to their corresponding Stripe account.

How do I pass this value (call it $stripe) from Step 1 to Step 3?

Thanks in advance for your help.
georgefweb 05 Mar, 2013
Found the simple answer: just add the variable to the form data
$form->data['stripeid']=$stripeid;

Question: would this piece of info be visible to client browsers or is this safely hidden from view on the server side?

Thanks
GreyHead 05 Mar, 2013
Hi georgefweb,

The $form->data[] array is only on the server until you send a value somewhere else.

Bob
georgefweb 06 Mar, 2013
Bob, thanks.

I'm now having an issue with a custom Javascript reloading on a page after a form has been filled out.

I have a custom form that contains among other things the fields for cardholder-name, card-number, card-expiry-month, card-expiry-year and card-cvc. If you'll recall, I have a JS snippet that is loaded during On Load event to detect when the card data has been entered and to intercept this information and convert it into a token that is then passed to the server. I then have server-side logic that processes the changes.

The first time I run the form and enter credit card information, it works correctly. However, after the reload, no credit card changes are caught. If I look at the source code in Chrome, the difference is that the Javascript snippet isn't reloaded.

Any suggestions for fixing?

Here is the hierarchy:
On Load
   Custom Javascript Snippet Action
   Style Form Action
   Show HTML Action

On Submit
  Custom Server Side Validation
    On Success
    On Fail
         Event Loop (target event: OnLoad, quit next action:Yes)
  Show HTML Action


Thanks
GreyHead 07 Mar, 2013
Hi georgefweb,

I'm not quite clear how you want this to work but you have a Show HTML action in the On Submit event which has no associated JavaScript so that would explain the problem.

It might be better to use an Event Loop action (or possibly a Show Form action) there to go to the On Load event.

Bob
georgefweb 07 Mar, 2013
Bob, replacing the Show HTML action with the Event Loop action did the trick.

So to be clear:
- the Show HTML action simply renders the form (no JS) whereas the Event Loop action triggers a desired event
- the Event Loop triggers the On Load event; this in turn causes Chronoforms to execute the actions triggered by the On Load event
- in my case, this includes loading up the custom JS snippet, style form and shows the html
- since the JS has been reloaded, the form is able to continue to work as intended

Correct?

Thanks a bunch. Hey, are you available to review my two forms (registration and user profile)? Happy to pay you for your help (and/or buy you a beer), I just want an expert eye to take a look and make sure I haven't done something silly.

Regards,
Georgefweb
GreyHead 07 Mar, 2013
Hi georgefweb,

That's pretty much how it works. An event is a container for a sequence of actions. By Default the Event Loop points to the On Load action but you can have is set to point to any event in your form.

You may find the two 'Basics' docs here helpful.

By all means email or PM me the site URL and a SuperAdmin login and, for a beer or three, I'll take a quick look.

Bob
georgefweb 08 Mar, 2013
Bob, just sent you the info on the greyhead.net site. Looking forward to your feedback.
GreyHead 08 Mar, 2013
Hi georgefweb,

Got it, it will be the morning before I get to look at it though.

Bob
This topic is locked and no more replies can be posted.