PermaLink Integrating Stripe.js/Checkout.js with AJAX'd/SPA frameworks like AngularJS06/27/2013 02:32 PM
I was looking for an AngularJS shopping cart example when I found one on CodeProject but it didn't support Stripe for credit card payments (though it did have Paypal and Google Checkout support).  This turned out to be a bit more convoluted than I expected because Stripe doesn't let you submit an order directly their web site like Paypal/Google do.  Paypal/Google handle this by taking the form data you POST to their web site and displaying a confirmation form to verify w/ the user that the charge is valid.  Stripe, on the other hand, uses a 2-step process that provides a Checkout form that basically does a pre-authorization of a payment, which you then have to process on your server to finish the charge.

One stickler with Stripe's Checkout.js library is that everything is based on the premise of a submitted form. Their simple example of using their checkout form ties the script into it so the form submit is overridden to post to their web site before submitting to yours:
<form action="/charge" method="post">
 <script src="
https://checkout.stripe.com/v2/checkout.js"
    class="stripe-button"
   data-key="pk_test_2iBij8T4Frl6c3jF179f8m5l"></script>
</form>


When you have a Single Page Application (aka an AJAX'd site), you no longer have hard browser web page refreshes. Instead, your web page is loaded once and then pieces are AJAX'd w/ DOM manipulation. Doing a <form> POST would cause a full page reload. Luckily, a few jQuery plugins I've used in the past allow a workaround: malsup's jquery.form.js and jquery.blockui.js. The former converts a regular form into an AJAX'd form and the latter keeps the user from clicking on parts of the UI while the longish Stripe transaction is running in the background.

The following Javascript files (you can use cdnjs to speed up loading of the jQuery files) are necessary:

<script src="https://checkout.stripe.com/v2/checkout.js"></script>
<script src="/jquery.form.js"></script>
<script src="/jquery.blockUI.min.js"></script>

The AngularJS Cart HTML changes to provide a button were simple:
<button
class="btn btn-block btn-primary"
ng-click="cart.checkout('Stripe')"
ng-disabled="cart.getTotalCount() < 1">
<i class="icon-ok icon-white" /> check out using Stripe
</button>
<!-- Stripe needs a form to post to -->
<form class="form-stripe"></form>

And then a new Javascript function had to be added to the shoppingCart.js:

shoppingCart.prototype.checkoutStripe = function (parms, clearCart) {
// global data
var data = {};

// item data
for (var i = 0; i < this.items.length; i++) {
var item = this.items[i];
var ctr = i + 1;
data["item_name_" + ctr] = item.sku;
data["item_description_" + ctr] = item.name;
data["item_price_" + ctr] = item.price.toFixed(2);
data["item_quantity_" + ctr] = item.quantity;
}

// build form
var form = $('.form-stripe');
form.empty();
// NOTE: in production projects, you have to handle the post
// with a few simple calls to the Stripe API.
// See https://stripe.com/docs/checkout
// You'll get a POST to the address below w/ a stripeToken.
// First, you have to initialize the Stripe API w/ your public/private keys.
// You then call Customer.create() w/ the stripeToken and your email address.
// Then you call Charge.create() w/ the customer ID from the
// previous call and your charge amount.
form.attr("action", parms.options['chargeurl']);
form.attr("method", "POST");
form.attr("style", "display:none;");
this.addFormFields(form, data);
this.addFormFields(form, parms.options);
$("body").append(form);

// ajaxify form
form.ajaxForm({
success: function () {
$.unblockUI();
alert('Thanks for your order!');
},
error: function (result) {
$.unblockUI();
alert('Error submitting order: ' + result.statusText);
}
});

var token = function (res) {
var $input = $('<input type=hidden name=stripeToken />').val(res.id);

// show processing message and block UI until form is submitted and returns
$.blockUI({ message: 'Processing order...' });

// submit form
form.append($input).submit();
this.clearCart = clearCart == null || clearCart;
form.submit();
};

StripeCheckout.open({
key: parms.merchantID,
address: false,
amount: this.getTotalPrice() *100, /** expects an integer **/
currency: 'usd',
name: 'Purchase',
description: 'Description',
panelLabel: 'Checkout',
token: token
});
}

The code basically initializes the form w/ the shopping cart items/quantity so you can store that info on your server later. Then it AJAXifies the <form> so that it won't cause a browser refresh, then brings up the Stripe payment form from Checkout.js. When the user finishes the Stripe payment, the token() function is called by the Stripe checkout code with a stripeToken. This then blocks the UI so the user can't click multiple times on things (the UI is unblocked when the AJAX form submission completes) and does the actual form submit.

There were a few other minor changes to the AngularJS Cart project to allow "Stripe" to be one of the checkout types, to configure the POST URL on your server, and to add the public Stripe key (the private Stripe key should only be kept on your server), but those were specific to the AngularJS Cart application (I gave my changes to Bernardo so they should end up in that AngularJS Cart project). The general structure of this can be used to add the simple Stripe Checkout form to any AJAX based client-side framework.

Comments :v

1. ken04/30/2014 23:09:08


@Alessio: the chargeurl is on your web server...the data will be posted there

@Dino: the loading animation is in the Stripe JS code...you'll have to rewrite it to load your own animation




2. Alessio04/27/2014 13:22:19


hi
but where you take the chargeurl?




3. Dino03/25/2014 16:51:37


Great stuff. Question maybe not exactly related to your post, but couldn't find it anywhere. Is there a way to change the loading animation which comes with stripe (before checkout box is presented)?

thanks for your answer




4. ken10/22/2013 15:10:24


With Stripe, no the server side is not skipped. On the client side, you get a transaction ID from the Stripe server and post this and the cart contents to the server. The server then takes that transaction ID and recalculates all the items in your cart before submitting the final order to the Stripe server. So even if you change all the pricing on the front end cart form via Javascript, the final calculations are done on the server-side...




5. Rad10/21/2013 04:09:14


Is the server side skipped when doing payment processing from the client (web page). If so then it is very easy to change the price by stepping through JavaScript debugger.
How do you protect and validate user selection?
Rad




Start Pages
RSS News Feed RSS Comments Feed CoComment Integrated
The BlogRoll
Calendar
December 2024
Su
Mo
Tu
We
Th
Fr
Sa
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
Search
Contact Me
About Ken
Full-stack developer (consultant) working with .Net, Java, Android, Javascript (jQuery, Meteor.js, AngularJS), Lotus Domino