Skip to content

Instantly share code, notes, and snippets.

@boucher
Forked from saikat/gist:1084146
Created February 6, 2012 07:07
Show Gist options
  • Save boucher/1750368 to your computer and use it in GitHub Desktop.
Save boucher/1750368 to your computer and use it in GitHub Desktop.
Stripe sample checkout form
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
<title>Stripe Sample Form</title>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js"></script>
<script type="text/javascript" src="https://ajax.aspnetcdn.com/ajax/jquery.validate/1.8.1/jquery.validate.min.js"></script>
<script type="text/javascript" src="https://js.stripe.com/v1/"></script>
<script type="text/javascript">
Stripe.setPublishableKey('pk_YOUR_PUBLISHABLE_KEY');
$(document).ready(function() {
function addInputNames() {
// Not ideal, but jQuery's validate plugin requires fields to have names
// so we add them at the last possible minute, in case any javascript
// exceptions have caused other parts of the script to fail.
$(".card-number").attr("name", "card-number")
$(".card-cvc").attr("name", "card-cvc")
$(".card-expiry-year").attr("name", "card-expiry-year")
}
function removeInputNames() {
$(".card-number").removeAttr("name")
$(".card-cvc").removeAttr("name")
$(".card-expiry-year").removeAttr("name")
}
function submit(form) {
// remove the input field names for security
// we do this *before* anything else which might throw an exception
removeInputNames(); // THIS IS IMPORTANT!
// given a valid form, submit the payment details to stripe
$(form['submit-button']).attr("disabled", "disabled")
Stripe.createToken({
number: $('.card-number').val(),
cvc: $('.card-cvc').val(),
exp_month: $('.card-expiry-month').val(),
exp_year: $('.card-expiry-year').val()
}, function(status, response) {
if (response.error) {
// re-enable the submit button
$(form['submit-button']).removeAttr("disabled")
// show the error
$(".payment-errors").html(response.error.message);
// we add these names back in so we can revalidate properly
addInputNames();
} else {
// token contains id, last4, and card type
var token = response['id'];
// insert the stripe token
var input = $("<input name='stripeToken' value='" + token + "' style='display:none;' />");
form.appendChild(input[0])
// and submit
form.submit();
}
});
return false;
}
// add custom rules for credit card validating
jQuery.validator.addMethod("cardNumber", Stripe.validateCardNumber, "Please enter a valid card number");
jQuery.validator.addMethod("cardCVC", Stripe.validateCVC, "Please enter a valid security code");
jQuery.validator.addMethod("cardExpiry", function() {
return Stripe.validateExpiry($(".card-expiry-month").val(),
$(".card-expiry-year").val())
}, "Please enter a valid expiration");
// We use the jQuery validate plugin to validate required params on submit
$("#example-form").validate({
submitHandler: submit,
rules: {
"card-cvc" : {
cardCVC: true,
required: true
},
"card-number" : {
cardNumber: true,
required: true
},
"card-expiry-year" : "cardExpiry" // we don't validate month separately
}
});
// adding the input field names is the last step, in case an earlier step errors
addInputNames();
});
</script>
</head>
<body>
<h1>Stripe Example Form</h1>
<form action="/" method="post" id="example-form" style="display: none;">
<div class="form-row">
<label for="name" class="stripeLabel">Your Name</label>
<input type="text" name="name" class="required" />
</div>
<div class="form-row">
<label for="email">E-mail Address</label>
<input type="text" name="email" class="required" />
</div>
<div class="form-row">
<label>Card Number</label>
<input type="text" maxlength="20" autocomplete="off" class="card-number stripe-sensitive required" />
</div>
<div class="form-row">
<label>CVC</label>
<input type="text" maxlength="4" autocomplete="off" class="card-cvc stripe-sensitive required" />
</div>
<div class="form-row">
<label>Expiration</label>
<div class="expiry-wrapper">
<select class="card-expiry-month stripe-sensitive required">
</select>
<script type="text/javascript">
var select = $(".card-expiry-month"),
month = new Date().getMonth() + 1;
for (var i = 1; i <= 12; i++) {
select.append($("<option value='"+i+"' "+(month === i ? "selected" : "")+">"+i+"</option>"))
}
</script>
<span> / </span>
<select class="card-expiry-year stripe-sensitive required"></select>
<script type="text/javascript">
var select = $(".card-expiry-year"),
year = new Date().getFullYear();
for (var i = 0; i < 12; i++) {
select.append($("<option value='"+(i + year)+"' "+(i === 0 ? "selected" : "")+">"+(i + year)+"</option>"))
}
</script>
</div>
</div>
<button type="submit" name="submit-button">Submit</button>
<span class="payment-errors"></span>
</form>
<!--
The easiest way to indicate that the form requires JavaScript is to show
the form with JavaScript (otherwise it will not render). You can add a
helpful message in a noscript to indicate that users should enable JS.
-->
<script>if (window.Stripe) $("#example-form").show()</script>
<noscript><p>JavaScript is required for the registration form.</p></noscript>
</body>
</html>
@bill2004158
Copy link

{
"card": {
"exp_year": "undefined",
"cvc": "undefined",
"number": "*****ined",
"exp_month": "2"
},
"request_id": "1342460146334"
}

only exp_month has got correct value,
anyone know the reason?
it seems those request from IE.

@briancollins
Copy link

@bill2004158: Are you using the exact form, or do you have a modified version? You'll see requests like that when you remove the class names from the credit card fields. It's also possible that you have other fields on the page with those classes.

Happy to take a look at your modified version if you have it hosted somewhere.

@bill2004158
Copy link

@briancollins I found the problem is $('.card-cvc') that can not load the dom! I am not sure why that happened in IE.
I can not reduplicate it, however, my users got it (return undefined).
so I changed to query the dom when loaded, then reuse the object. that fixed the bug! (that error msg has gone from log)

but I still can not explain:

  1. why dom query fail when submit
  2. however only exp_month can be retrieved.
  3. I follow your code, it was working fine, however, get that error since my friend added method:
    updateFormFeedbackLink and jQuery(document).ready(function() {});
<script type="text/javascript">
  Stripe.setPublishableKey('<%=request.getAttribute("stripe.publickey") %>');
  jQuery(function($){
        var cardNumber = $(".card-number");
        var cardCvC = $(".card-cvc");
        var cardExpiryYear = $(".card-expiry-year");
        var cardExpiryMonth =$(".card-expiry-month");
        var cardHolderName =$(".card-cardholder-name");
        function addInputNames() {
            // Not ideal, but jQuery's validate plugin requires fields to have names
            // so we add them at the last possible minute, in case any javascript 
            // exceptions have caused other parts of the script to fail.
            cardNumber.attr("name", "card-number")
            cardCvC.attr("name", "card-cvc")
            cardExpiryYear.attr("name", "card-expiry-year")
            cardHolderName.attr("name", "card-cardholder-name")
        }

        function removeInputNames() {
            cardNumber.removeAttr("name")
            cardCvC.removeAttr("name")
            cardExpiryYear.removeAttr("name")
            cardHolderName.removeAttr("name")
        }

        function submit(form) {
            // remove the input field names for security
            // we do this *before* anything else which might throw an exception
            removeInputNames(); // THIS IS IMPORTANT!

            // given a valid form, submit the payment details to stripe
            $(':submit', form).attr("disabled", "disabled").children("img").show(); 

            Stripe.createToken({
                number: cardNumber.val(),
                cvc: cardCvC.val(),
                exp_month: cardExpiryMonth.val(), 
                exp_year: cardExpiryYear.val(),
                name: cardHolderName.val()
            }, function(status, response) {
                if (response.error) {
                    // re-enable the submit button
                    $(":submit", form).removeAttr("disabled").children("img").hide();

                    // show the error
                    $(".payment-errors").html(response.error.message);
                    
                    // we add these names back in so we can revalidate properly
                    addInputNames();
                } else {
                    // token contains id, last4, and card type
                    var token = response["id"];

                    // insert the stripe token
                    var input = $("");
                    form.appendChild(input[0])

                    // and ajax submit
                    //form.submit();
                    var xhr = $.ajax({
                        type        : "POST",
                        cache       : false,
                        dataType    : "text",
                        url         : "tws.webapp.WebApp",
                        data        : $(form).serializeArray(),
                        success     :  function(data){
                            if(xhr.getResponseHeader("X_STRIPE_CHARGE") === "OK"){
                                $("#right_page").slideUp("normal").html(data).slideDown("normal");
                            } else {
                                addInputNames();// we add these names back in so we can revalidate properly
                                $(form["stripeToken"]).remove();//the del old token
                                $(":submit", form).removeAttr("disabled").children("img").hide();
                                $(".payment-errors").html(data).effect("highlight", {}, 3000);
                            }
                        }//end method sucess
                    });
                    return false;
                }
            });
            
            return false;
        }
        
        // add custom rules for credit card validating
        jQuery.validator.addMethod("cardNumber", Stripe.validateCardNumber, "Please enter a valid card number");
        jQuery.validator.addMethod("cardCVC", Stripe.validateCVC, "Please enter a valid security code");
        jQuery.validator.addMethod("cardExpiry", function() {
            return Stripe.validateExpiry($(".card-expiry-month").val(), 
                                         $(".card-expiry-year").val())
        }, "Please enter a valid expiration");

        // We use the jQuery validate plugin to validate required params on submit
        $("#formPayment").validate({
            submitHandler: submit,
            rules: {
                "card-cvc" : {
                    <%if(hasEnabledCCValidation){%>
                    cardCVC: true,
                    <%}%>
                    required: true
                },
                "card-number" : {
                    <%if(hasEnabledCCValidation){%>
                    cardNumber: true,
                    <%}%>
                    required: true
                },
                "card-cardholder-name" : "required",
                "card-expiry-year" : "cardExpiry" // we don't validate month separately
            }
        });

        // adding the input field names is the last step, in case an earlier step errors
        addInputNames();
    });
  
    function updateFormFeedbackLink() {
        var NDigit = 4;
        var cnLength = jQuery('.card-number').val().length;
        var cn = jQuery('.card-number').val();
        if(cnLength>=NDigit) {
            cn = cn.substring(0,(cnLength-NDigit)) + "XXXX";
        } else {
            cn = "";
            for(var i=1;i<=cnLength;i++) {
                cn = cn + "X";
            }
        }
        var href = "tws.webapp.WebApp?cmd=FailedPaymentFeedback";
        href = href + "&<%=CmdFailedPaymentFeedback.PARAM_PAYER_ID%>=" + jQuery("#formPayment :input[name='payerID']").val();
        if(jQuery('.card-cardholder-name').val()!='') href = href + "&<%=CmdFailedPaymentFeedback.PARAM_NAME_ON_CARD%>=" + jQuery('.card-cardholder-name').val();
        if(cn!='') href = href + "&<%=CmdFailedPaymentFeedback.PARAM_CARD_NUMBER%>=" + cn;
        if(jQuery('.card-expiry-month').val()!='' && jQuery('.card-expiry-year').val()!='') href = href + "&<%=CmdFailedPaymentFeedback.PARAM_EXPIRATION%>=" + jQuery('.card-expiry-month').val() + "-" + jQuery('.card-expiry-year').val();
        if(jQuery('.payment-errors').html()!='') href = href + "&<%=CmdFailedPaymentFeedback.PARAM_ERROR_MESSAGE%>=" + jQuery('.payment-errors').html();
        jQuery("#LinkFeedback").attr("href",href);
    }
    
    jQuery(document).ready(function() {
        jQuery("#LinkFeedback").fancybox({
            width               : 470,
            height              : (navigator.appName=='Microsoft Internet Explorer'?300:250),
            'autoScale'         : true,
            'transitionIn'      : 'none',
            'transitionOut'     : 'none',
            'type'              : 'iframe',
            'autoDimensions'    : true,
            'scrolling'         : 'no',
            margin              : 5,
            padding             : 5
        });
    });
</script>

@h315
Copy link

h315 commented Aug 7, 2012

hi, everytime i have copied this code or some other in dreamweaver cs5 i get any error when i run it.the error is "An unexpected error has occured. We have been notified of the problem."
i am using card number:4242424242424242.Can you kindly help me with this.do i have to include some other files as well?

@esitefinity
Copy link

"An unexpected error has occured. We have been notified of the problem." usually means you did you put you API publish key from your account into the form.

@grand-lotus-iroh
Copy link

You can't create customers or charges with Stripe.js, only tokens -- you'll need to create charges or customers server-side.

» https://stripe.com/docs/api#charge_object
» https://stripe.com/docs/api#customer_object

Is there a missing chunk of php code for "making a charge" using the token(s) this example just generated?

@bmalloy
Copy link

bmalloy commented Jan 20, 2013

why not use stripes' validateCardNumber?

Stripe.validateCardNumber($('input.card-number').val()) can do the same without all the jquery?

@Medeno
Copy link

Medeno commented Sep 24, 2013

This works better for me for the month as it displays month in the XX format. Very simple and works with Stripe. Can anyone see any problems with this?

010203040506070809101112

@d4goxn
Copy link

d4goxn commented Oct 26, 2013

Why is it necessary to hide the names of the inputs? Anyone looking at the values could easily identify them, credit card info has a pretty distinctive format. Also, is there some reason why the stripe token is inserted as an input with no type, and then hidden with CSS, instead of just using an <input type="hidden" ...>?
And since I'm on a roll, I might as well warn people that this is a high value target for an xss attack if there is any way to get 3rd party content to show on the page. Do not show user generated content (not even "[other username] also bought this with that") or ads on the checkout page unless you are sure that you can prevent cross site scripting.

@relipse
Copy link

relipse commented Nov 8, 2013

d4goxn: you hide the names of the inputs because then the browser will not pass that information to the next page. It is a form of security knowing those fields are not sent at all.

@rlbruderick
Copy link

It seems to me that something this example lacks is the purchase-specific information.
For example:
data-amount="7199"
data-name="Serious Coder; Happy Client"
data-description="1 textbook ($71.99)"

What needs to be sent (POSTed) from the client to the server in terms of items purchased, their costs, and the total price? And are there certain key words or formats to be used for that?
I know that, using Stripe.js or the form about, the client-facing page will POST the form data to the server as a secret token. Does that token include Amount, ItemName and Description? Or should the purchase info be sent separately?
After the token is received by the server, the card is charged with something like this (which is from stripe.com/docs/tutorials/charges):

$charge = Stripe_Charge::create(array(
"amount" => 1000, // amount in cents, again
"currency" => "usd",
"card" => $token,
"description" => "payinguser@example.com")
);

Am I missing something obvious? I'd like a complete example, which includes a sample purchase item and price.
Thanks!
Rick

@Stevel9698
Copy link

I get the following error when I click the submit button:

HTTP Error 405.0 - Method Not Allowed
The page you are looking for cannot be displayed because an invalid method (HTTP verb) is being used.

Any ideas?

Copy link

ghost commented Mar 20, 2015

Have to agree with RLBruderick - why is there no straightforward example of a page in which a user picks one or more of a variety of the products available and clicks to pay? Overall, the Stripe docs overemphasize simple stuff that need not be said and casually miss out both a much-needed overview and a real-world example. You have to google around looking for some way of having the data-amount and data-description adapt to what the user is buying. Stripe would be great if we wanted to make one-product checkouts, but I do (likewise) feel that either I'm missing something very basic or some thing(s) very basic is/are missing from the support docs. Most use cases, the customer chooses a product and we want to arrange payment for that product with all relevant data for the transaction being returned for order fulfillment - and isn't that just normal, the usual thing?

@chweedel
Copy link

chweedel commented Apr 8, 2015

When i look at the documentation, i see that the checkout form have about 10 lines of code. It works as far as it shows the form. I do not want to grab credit card info i want to have a payment processor that does it all. How do we simply send back the token and where then again and how do we receive the ok so we can deliver what ever the customer bought.

I have to say that even if stripe people pretend it is easy, it is not.

May i suggest that you rocket science guys put up a simple system to generate all the necessary to simply copy and paste what we need... It would take 5 minutes instead of 5 days.

Too bad, but i like your interface, but i really do not like the amazing complexity of the whole integration process.

Simple is beautyful, take example from Paypal...

@vrsotto
Copy link

vrsotto commented Aug 12, 2015

Base on Stripe Docs. Input fields representing sensitive card data (number, CVC, expiration month and year) do not have a "name" attribute to prevent hitting the server. I guess, another option of having "addInputNames()" is to make "jquery validation" target input field(s) ID ("id" attribute), ref: http://jqueryvalidation.org/reference/ under "Fields with complex names (brackets, dots)" section.

@ivanthemeh
Copy link

Where can I go for help with stripe in meteor? Are there any predefined templates available?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment