Supporting a Variable Amount in Checkout

Add a field to your payments page that enables users to specify the amount to be charged. If you need help after reading this, check out our answers to common questions or chat live with other developers in #stripe on freenode.

Stripe Checkout takes care of building an HTML form for securely collecting your customers’ card data. When loading the Checkout modal, you can specify the payment amount presented to the user. In many applications, this amount has already been determined when the page is rendered and is simply included as an attribute on Checkout’s script tag:

<!-- Checkout with $20 amount -->

However, some applications prefer to let their users specify a specific amount of money to be charged. This is most commonly seen in businesses that accept donations or empower the user to pay what they think an item is worth. Checkout supports this approach, too, and this recipe walks through a sample implementation:

  1. Create a form with a text field for the donation amount, along with a “Donate” button.
  2. Define a JavaScript handler that validates the user input and launches Checkout with the user-specified amount.
  3. Write a controller on the server that processes the amount and makes the charge.

It’s important to note that the JavaScript modifies the amount displayed in Checkout purely for usability purposes. The server-side request to Stripe dictates the actual amount being charged.

This implementation uses Rails, but the concepts can be easily ported to other languages and frameworks. The JavaScript in this example uses jQuery.

Setup the application

The steps below prepare the scaffolding for a basic payments page. If you already have a payments page, there’s no need to change anything according to these instructions. You can easily adapt the rest of this recipe to your existing implementation.

First, ensure the Stripe gem is in your application’s Gemfile, and then run bundle install. Next, add an initializer that configures the Stripe client library:

Rails.configuration.stripe = {
  :publishable_key => ENV['PUBLISHABLE_KEY'],
  :secret_key      => ENV['SECRET_KEY']
Stripe.api_key = Rails.configuration.stripe[:secret_key]

Note that this example uses environment variables for the Stripe API keys. By doing so, sensitive credentials cannot be accidentally submitted to your version control repository. The end of this recipe demonstrates launching the server with values provided for these environment variables.

Then, generate a new Charges controller:

rails g controller charges new create

This creates a few files, depending on your setup. For this recipe, the files you’ll be working with are:

  • app/controllers/charges_controller.rb
  • app/views/charges/new.html.erb
  • app/views/charges/create.html.erb

Last, edit routes.rb, replacing any Rails-generated routes for charges with:

resources :charges

Create the payment form and the confirmation page

The application requires two view files. The first is the payments page, containing the form in which the user specifies the donation amount and a button that ultimately launches Checkout. The second view is a basic confirmation page the user is redirected to after completing the payment. Once the views are in place, you’ll proceed to adding the JavaScript to properly communicate the donation amount to Checkout.

As a point of reference, for applications with a fixed payment amount, a basic Checkout page might be:

<!-- Example Checkout page that *does not* support a variable amount! -->
<%= form_tag charges_path do %>
  <div id="error_explanation">
    <% if flash[:error].present? %>
      <p><%= flash[:error] %></p>
    <% end %>
    <label class="amount">
      <span>Amount: <%= number_to_currency(@amount * 100) %></span>
  <script src="" class="stripe-button"
  data-key="<%= Rails.configuration.stripe[:publishable_key] %>"
  data-description="One-time donation"
  data-amount="<%= @amount %>"
<% end %>

Note that the page is rendered with a known amount, @amount, that is also the provided value for the data-amount attribute in the Checkout script tag. This enables the Checkout modal to display the amount to the user as they’re entering their card details. Note also that the Checkout script is responsible for rendering a button on the form.

For this donation example, two immediate changes are required:

  • You want a text field where the user can specify an amount to donate.
  • You don’t want Checkout to render a button on the page. Instead, you’ll have your own “Donate” button. This way, when the user clicks the “Donate” button, you can run some JavaScript that verifies the user’s input and then launches the Checkout modal.

With those changes in mind, begin your form in new.html.erb:

<%= form_tag charges_path do %>
  <div id="error_explanation">
    <% if flash[:error].present? %>
      <p><%= flash[:error] %></p>
    <% end %>
    <%= label_tag(:amount, 'Donation Amount:') %>
    <%= text_field_tag(:amount) %>
    <%= hidden_field_tag(:stripeToken) %>
  <button id='donateButton'>Donate</button>
<% end %>

Along with the two changes already mentioned, there’s a new hidden field called stripeToken. The hidden field is how the stripeToken generated by Checkout is submitted to your server. Your JavaScript will set this field after Checkout has collected the user’s payment information and received the token from Stripe.

Now add the code for the other view, create.html.erb: the donation confirmation page. You can use the handy Rails helper number_to_currency() to format the amount, converting it from Stripe-compatible cents into human-friendly dollars:

<h2>Thank you!</h2>
<p>Your donation of <strong><%= number_to_currency(@amount * 0.01) %></strong>
 has been received.</p>

Define the JavaScript

The first bit of JavaScript to add to the payment view configures basic Checkout functionality. You can give Checkout a function to call after the user has entered their payment information. This callback sets the stripeToken field on the form and submits the form to the server.

Add a script tag to include the Checkout library as well as your custom JavaScript to the bottom of new.html.erb. This should go after the end tag for the form. Write the StripeCheckout handler first:

<script src=""></script>

var handler = StripeCheckout.configure({
  key: '<%= Rails.configuration.stripe[:publishable_key] %>',
  locale: 'auto',
  name: 'Sand Castles United',
  description: 'One-time donation',
  token: function(token) {

The handler object is configured with a few options, including your public API key and a callback for the token. See the Checkout docs for a comprehensive list of all available configuration options.

Next, clicking the “Donate” button needs to call a JavaScript function that verifies the user-specified amount. If the amount is valid, the function launches Checkout, passing in the amount. Still within the script tags, add the following click handler (explained below):

$('#donateButton').on('click', function(e) {


  var amount = $('input#amount').val();
  amount = amount.replace(/\$/g, '').replace(/\,/g, '')

  amount = parseFloat(amount);

  if (isNaN(amount)) {
    $('#error_explanation').html('<p>Please enter a valid amount in USD ($).</p>');
  else if (amount < 5.00) {
    $('#error_explanation').html('<p>Donation amount must be at least $5.</p>');
  else {
    amount = amount * 100; // Needs to be an integer!{
      amount: Math.round(amount)

First, an event handler is added to #donateButton. When that button is clicked, the function calls preventDefault() on the event to halt the form’s submission. Remember, you want Checkout to collect the user’s payment information and set the stripeToken before the form is submitted.

The user’s input is then verified:

  • Any dollar signs or commas are removed
  • parseFloat() then attempts to convert the string into a float
  • If conversion to a float fails (by returning NaN), #error_explanation is set to inform the user

Note that this verification has some flexibility: 7, 07 and 7.00 are all treated as $7.

This example also enforces a minimum donation amount of $5.00. This is not required, but as a minimum of $0.50 is needed for a Stripe charge, you will want to enforce a minimum of at least that amount in your implementation.

Note that this code assumes a user-specified amount in US dollars. Applications in other countries or with international audiences will want to consider other strategies, discussed at the end of this recipe.

The Checkout modal is opened with It’s now dynamically provided with an amount, converted to integer cents, as required by Checkout.

Finally, still within the script tag and right below the click handler, add an event handler for popstate. This ensures the Checkout modal is closed should the user attempt to navigate away from the page:

// Close Checkout on page navigation
$(window).on('popstate', function() {

With the views and client-side code prepared, you can now turn your attention to setting up the server.

Write the controller

After the user has completed Checkout, the form will be submitted to the server with the parameters amount and stripeToken. At this point, you want to process and verify the user-specified amount once more server-side, using the same logic as in the JavaScript.

Add the following methods to charges_controller.rb:

def new

def create
  @amount = params[:amount]

  @amount = @amount.gsub('$', '').gsub(',', '')

    @amount = Float(@amount).round(2)
    flash[:error] = 'Charge not completed. Please enter a valid amount in USD ($).'
    redirect_to new_charge_path

  @amount = (@amount * 100).to_i # Must be an integer!

  if @amount < 500
    flash[:error] = 'Charge not completed. Donation amount must be at least $5.'
    redirect_to new_charge_path

    :amount => @amount,
    :currency => 'usd',
    :source => params[:stripeToken],
    :description => 'Custom donation'

  rescue Stripe::CardError => e
    flash[:error] = e.message
    redirect_to new_charge_path

Here the amount submitted to the server is processed in the same manner as in the JavaScript. Parsing the string using Float() raises errors if the string has any non-numeric characters. Again, the minimum donation amount of $5.00 or 500 cents is enforced. If the amount fails any of these checks, the charge is not made and the user is alerted to the problem. Otherwise, the charge is completed.

It’s important that these validations are repeated on the server in case the user has manipulated or bypassed your JavaScript validations. It is also necessary to ensure your parsing and validation logic are robust. You want your client logic to come to the same conclusion as your server logic as to what the user should be charged. JavaScript’s parseFloat() function is fairly lenient, but Ruby’s Float() function is strict. Having this strictness on the server ensures input with ambiguity (like ‘5 5’) is rejected before a charge is made.

With everything in place, you’re ready to test your solution. Remember to boot your app with your Stripe credentials:

SECRET_KEY=sk_test_BQokikJOvBiI2HlWgH4olfQ2 bundle exec rails s

We’ve placed random API keys in the code. Replace these with your actual API keys to test this code for yourself.

Then you can find your payments page in your web browser at http://localhost:3000/charges/new.

Advanced features

The recipe to this point explains all the basic functionality needed to accept user-defined payments through Checkout. As always, there are ways you can improve upon this approach.

Supporting multiple currencies

The validation and parsing logic above assumes the payment being made is in US dollars. It ignores the dollar symbol ($) and the digit group separator used in the US (the comma). Should your payment form accept a different currency, you may need to modify this logic.

If you’d like to support multiple currencies, one solution is to add a drop-down currency selector next to the amount input field. The currency selected in this drop-down would then be considered both by JavaScript and the server, perhaps using a switch statement to determine what parsing and validation logic to use. When launching Checkout, you would then pass in the parameter currency. This is the 3-letter ISO code for the currency. For more information about this parameter and other configuration options for Checkout, see the Checkout documentation.

Supporting coupons

Another situation wherein you might dynamically change the amount displayed in Checkout is if you have your own coupon system. We have a detailed recipe on supporting coupons for standalone charges. However, the solution in that recipe modifies only the server’s charge request and not the amount displayed in Checkout to the user.

Using the concepts from this recipe, you could use JavaScript to validate a user-submitted coupon code by making an Ajax request to an endpoint on the server, such as /coupons. The endpoint would return a discount percentage, and the JavaScript would then launch Checkout with the appropriate, discounted amount.

Localizing error messages

Checkout supports localization in multiple languages whereas the error messages generated by this application are only in English. If your site has a multilingual audience, you’ll want to localize the application’s error messages, too, for a better user experience.