How it Works

When a customer submits your payment form, Recurly.js sends customer payment information to be encrypted and stored at Recurly and gives you an authorization key to complete the subscription process using our powerful API.

With this authorization key (or token), you can do anything with our API that requires payment information. Because you never handle any sensitive payment information, your PCI scope is drastically reduced.

Getting Started


<script src=""></script>

To begin, you’ll include the Recurly.js script on your page.

This exposes a single global object, recurly.



Simply call recurly.configure anywhere on your page, passing your public key. This identifies your site to Recurly servers. You can find your public key in the API Access section of your admin app.

Build a form

Build a form however you like. Use the data-recurly attribute to identify fields to Recurly.js.

  <input type="text" data-recurly="number">
  <input type="text" data-recurly="month">
  <input type="text" data-recurly="year">
  <input type="text" data-recurly="cvv">
  <input type="text" data-recurly="first_name">
  <input type="text" data-recurly="last_name">
  <input type="hidden" name="recurly-token" data-recurly="token">

To collect payment information from your customers, you’ll create a form similar to this one. Recurly.js uses the data-recurly attributes on the input tags to gather customer information before sending it to our servers. This particular form contains the minimum required input fields, and the table below demonstrates all possible input fields.

Card data

Field Name Example Description
number 4111-1111-1111-1111 Credit card number. required
month 8 or 02 Card expiration month as a number. required
year 18 or 2018 Card exiration year as a number. required
first_name John Cardholder first name. required
last_name Rambo Cardholder last name. required
cvv 1234 Card security code. recommended

Depending on how you’ve configured your billing address requirements, some of the following fields may be required.

Billing address

Field Name Example Description
address1 1313 Main St. First line of a street address
address2 Unit 1 Second line of a street address
city Hope Town or locality
state WA Province or region
postal_code 98552 Postal code
country US ISO 3166-1 alpha-2 country code
phone 555-867-5309 Phone number
vat_number SE0000 Customer VAT number. Used for VAT exclusion.

Getting a Token

Interrupt the form submit to send billing info to recurly and get a secure token in exchange. Once you have the token, submit the form to your server.

$('form').on('submit', function (event) {
  var form = this;
  recurly.token(form, function (err, token) {
    if (err) {
      // handle error using err.code and err.fields
    } else {
      // recurly.js has filled in the 'token' field, so now we can submit the
      // form to your server; alternatively, you can access and do
      // any processing you wish

Recurly.js works with Tokens, which represent secure and temporary storage for your customer’s sensitive billing information. They are stored directly on Recurly servers to reduce your PCI exposure.

When your customers submit your billing form, you’ll need to interrupt the submit and ask Recurly.js to create a token from the form. You may have noticed an additional hidden field in the form above, token. When you ask Recurly.js for a token during submit, it will automatically populate this field for you. After you get the token, you will submit it to your servers and use it there to talk to any endpoint in our API that accepts a billing_info.


You may call recurly.token with a form element

recurly.token(document.querySelector('form'), tokenHandler);

Or with an Object

// alternatively..
var billingInfo = {
  // required attributes
  number: '4111-1111-1111-1111',
  month: '1',
  year: '18',
  first_name: 'John',
  last_name: 'Rambo',

  // optional attributes
  cvv: '123',
  address1: '123 Main St.',
  address2: 'Unit 1',
  city: 'Hope',
  state: 'WA',
  postal_code: '98552',
  country: 'US',
  vat_number: 'SE0000'

recurly.token(billingInfo, tokenHandler);

Both methods require using a handler function like this one

function tokenHandler (err, token) {
  if (err) {
    // handle error using err.code and err.fields
  } else {
    // handle success using

Sends billing information to Recurly to store as a token, sending that token id back to you. There are two ways to call recurly.token: with a form, and with an object.

The simplest is to pass it a form element containing form fields with their corresponding data-recurly attributes.

arguments (form)

Param Type Description
form HTMLFormElement Parent form containing data-recurly fields
callback Function Callback function to accept the returned token


Alternatively, you can call recurly.token with a plain JavaScript object. This allows a more direct interface to the payment flow, eliminating any need to use the DOM.

arguments (object)

Param Type Description
options Object An object with billing information properties matching those outlined above
callback Function Callback function to accept the returned token


A callback is always required

callback arguments

Param Type Description
err RecurlyError or null A RecurlyError if an error occurred; otherwise null.
token Object An object containing a token id
    id String A token id



Using a Token

Create a new subscription with a token using one of our client libraries or API v2.

$ curl \
    -u API_KEY \
    -X POST \
    -d '<?xml version="1.0" encoding="UTF-8"?> \
<subscription> \
  <plan_code>bazooka_monthly</plan_code> \
  <currency>USD</currency> \
  <account> \
    <account_code>john_rambo</account_code> \
    <email></email> \
    <first_name>John</first_name> \
    <last_name>Rambo</last_name> \
    <billing_info> \
      <token_id>TOKEN_ID</token_id> \
    </billing_info> \
  </account> \
Recurly::Subscription.create! plan_code: :bazooka_monthly,
  account: {
    account_code: 'john_rambo',
    billing_info: { token_id: 'TOKEN_ID' }
subscription = recurly.Subscription(
  plan_code = 'bazooka_monthly',
  account = recurly.Account(
    account_code = 'john_rambo',
    billing_info = recurly.BillingInfo(token_id = 'TOKEN_ID')
$subscription = new Recurly_Subscription();
$subscription->plan_code = 'bazooka_monthly';

$subscription->account = new Recurly_Account();
$subscription->account->account_code = 'john_rambo';
$subscription->account->first_name = 'John'; 
$subscription->account->last_name = 'Rambo';
$subscription->account->email = '';

$subscription->account->billing_info = new Recurly_BillingInfo();
$subscription->account->billing_info->token_id = 'TOKEN_ID';

var plan = Plans.Get("bazooka_monthly");
var account = new Account("john_rambo");
var billingInfo = new BillingInfo(account) { TokenId = "TOKEN_ID" };
var subscription = new Subscription(account, plan, "USD");


Once Recurly.js has stored your customer’s sensitive data and given you a token reference, you will have 20 minutes to use it in our API. Expired tokens are permanently removed from the Recurly servers.

Tokens can be used to populate any account Billing Info data through our API. Simply assign it to the Billing Info’s token_id property and we’ll do the rest.

These endpoints accept tokens within Billing Info.


<button>Subscribe with PayPal</button>

Place a button on your page, then bind its click event to a recurly.paypal call.

Recurly.js supports both traditional credit card and PayPal reference transactions. A PayPal transaction is handled entirely within the PayPal checkout flow in a new window. Your customer will authorize a transaction within PayPal, then Recurly will record the authorization of the billing agreement and you’ll receive a token just as if your customer had used a credit card directly.

The same Recurly token rules apply when generated by recurly.paypal. You will need to use the token within our API before it expires, and expired tokens cannot be retrieved.


$('button').on('click', function () {
  var opts = { description: 'Bazooka Monthly' };
  recurly.paypal(opts, function (err, token) {
    if (err) {
      // handle errors using err.code
    } else {
      // handle success using;

Launches a new window with a PayPal reference transaction flow, eventually tokenizing the PayPal billing agreement for use within the Recurly API.


Param Type Description
options Object Options to configure the PayPal agreement flow.
    description String Agreement description. This may include any text you desire, including pricing information.
callback Function A callback function to receive the token or any errors.

A callback is always required.

callback arguments

Param Type Description
err RecurlyError or null A RecurlyError if an error occurred; otherwise null.
token Object An object containing a token id
    id String A token id




Recurly automates complicated subscriptions, with many factors influencing total subscription price. With this in mind, Recurly.js provides a robust pricing module designed to make determining actual subscription costs as simple and flexible as possible.

A Recurly.js pricing module can be attached to the form we created above, or to any other section of your page meant to display subscription pricing. Let’s get to the specifics!


var pricing = recurly.Pricing();

Creates a Pricing instance.

no arguments


recurly.Pricing a new Pricing instance.


<section id="pricing">
  <select data-recurly="plan">
    <option value="basic">Basic</option>
    <option value="notbasic">Not Basic</option>
  <input type="text" data-recurly="coupon">

Use pricing.attach to bind the <section> to the pricing calculator.

var pricing = recurly.Pricing();


This is the simplest way to use the pricing module. Simply pass a container element, and the pricing module with use all elements with a valid data-recurly attribute to determine price. When a value changes, the pricing module will automatically update its values. This allows your customers to manipulate a pricing form at will, and you will be able to react dynamically in any number of ways.


Param Type Description
container HTMLElement Parent element containing all data-recurly elements




Elements bound to a pricing module may be for either input or output.

Input elements should be user-manipulable elements like input or select. If you want to input a value but not let a customer manipulate it, use an <input type="hidden">.

Input elements

Field Name Example value Description
plan basic Plan code.
plan_quantity 1 Play quantity. Defaults to 1 if not specified.
coupon 15_off Coupon code.
addon 1 Addon quantity. To identify the addon being modified, use the data-recurly-addon attribute to set the addon code.
currency USD ISO-4217 currency code.
country US ISO 3166-1 alpha-2 country code.
postal_code 90210 Customer postal code. Used primarily to compute taxes.
tax_code digital Product tax code.
vat_number SE0000 Customer VAT number. Used for VAT exclusion.

Output elements should be plain text elements like output, span, or div.

Output elements

Field Name Example output Description
total_now 100.00 Total subscription cost due now.
subtotal_now 90.00 Subtotal of the following pricing components.
    addons_now 10.00 Total addon cost.
    discount_now 5.00 Amount discounted with coupon use.
    setup_fee_now 5.00 Subscription setup fee total.
    tax_now 15.00 Total subscription taxation.
currency_code USD, EUR ISO-4217 currency code.
currency_symbol $, Symbolic representation of currency_code


Pricing instances are event emitters, exposing a few helpful eventing methods.

var pricing = recurly.Pricing();

// Here we listen for the 'set.addon' event
pricing.on('set.addon', addonHandler);

// But we're feeling indecisive today. Let's detach this event'set.addon', addonHandler);

// .once will listen for one event then detach itself
pricing.once('change', function (price) {

function addonHandler (addon) {
  // addon.code

Example price object emitted by the change event.

  now: {
    subtotal: '25.00',
    addons: '0.00',
    discount: '0.00',
    setup_fee: '25.00',
    tax: '0.00',
    total: '25.00'
  next: {
    subtotal: '10.00',
    addons: '0.00',
    discount: '0.00',
    tax: '0.00',
    total: '10.00'
  base: {
    plan: {
      setup_fee: '25.00',
      unit: '10.00'
    addons: {
      thing1: '14.00', // cost of one 'thing1' addon
      thing2: '8.00'
  currency: {
    code: 'USD',
    symbol: '$'

Pricing instances emit events when various values are set or the price changes.

A Pricing instance itself behaves as an event emitter, where events can be attached using the pricing.on method and removed using The example to the right shows the various ways that you can attach and remove events.

Event: change

change is emitted whenever a pricing module has updated any of its pricing values. You can use this event to update your pricing display, compute total shopping costs, aggregate to analytics, etc.

Change emits a price object, shown in detail to the right.

Event: set.*

set.* events are emitted when specific pricing objects change on a pricing module. For example, when a customer changes their plan, the pricing module will send set.plan. This is especially useful for updating checkout previews based on what the customer has selected as one example.

Event Emits
set.plan Plan object.
set.addon Addon object. Coupon object.
set.address Address object.
set.currency Currency code. Tax object.

Pricing API

var pricing = recurly.Pricing();

  .plan('basic', { quantity: 1 })
  .addon('addon1', { quantity: 2 })
    country: 'US',
    postal_code: '90210'
    tax_code: 'digital',
    vat_number: ''
  .catch(function (err) {
    // err.code
  .done(function (price) {
    // price object as emitted by 'change' event

The pricing module can be manipulated with a set of direct method calls. This is useful if you would like to set up a complex pricing schema for your customers, or would just like to use a more programmatic method of determining subscription price. Events fire just as they normally would when using pricing.attach.

Note that Recurly.js’s DOM binding is one-way. Thus if you use the Pricing API on a pricing instance already attached to a container, the elements within will not update with your Pricing API calls. If you would like two-way DOM binding, we suggest using a framework such as AngularJS and using the Pricing API without attaching it to a container.


Each Pricing API method will return a PricingPromise. This allows you to chain many asynchronous calls together without having to manage a complex chain of callbacks.

You don’t need to worry much about the internals of a PricingPromise, as it is designed to stay out of your way and facilitate asynchronous calls for you.

The catch method, as shown in the example, is used to handle error scenarios, such as when an addon cannot be applied to the selected plan.


Recurly.js bundles a few helpful methods for validating payment information prior to processing. These methods are used when generating tokens, but you can also use them to enhance your form validations and checkout flow.


// > true

// > true

// > false

Determines whether a given credit card number appears to be valid. Card numbers can include spaces or dashes.


Param Type Description
number String, Number Credit card number. May contain dashes or spaces.


Boolean. Whether or not the card number is valid.


// > 'visa'

// > 'american_express'

// > 'unknown'

Returns a card provider code matching the given number, or ‘unknown’. This is especially useful if you would like to display custom CVV location hints.


Param Type Description
number String Credit Card Number.


String. One of discover, master, american_express, visa, jcb, diners_club, or unknown.


recurly.validate.expiry(1, 2020);
// > true

recurly.validate.expiry('01', '16');
// > true

recurly.validate.expiry('12', '2013');
// > false

Checks of the given expiry month and year indicate a valid card. Cards set to expire in the current month are considered valid.


Param Type Description
month String, Number Month as one or two digits: 01, 1
year String, number Year as two or four digits: 18, 2020


Boolean. Whether the given expiration date is valid.


// > true

// > false

Determines whether the given card security code appears to be valid.


Param Type Description
cvv String, Number Card security code (CVV, CID, CVC, etc)


Boolean. Whether the given cvv is valid.


Example RecurlyError object.

  name: 'validation',
  code: 'validation',
  message: 'There was an error validating your request.',
  fields: [

Errors are encapsulated by a RecurlyError, which contains a few standard properties to help you diagnose error cases and inform your customers accordingly.

Errors will be thrown if the exception will prevent proper execution. If an error can be recovered, it will be passed to the proper error handling event listener, callback, or PricingPromise handler for you to inspect.

Best Practices

The message property contains diagnostic information intended to help you diagnose problems with the form, and we do not recommend displaying its contents to your customers.

To provide the best customer experience, we recommend that you provide your own error text to be displayed, based on the error code you receive.

Error Codes

Code Description
not-configured This error appears when you try to perform an operation without first calling recurly.configure.
missing-public-key When you call recurly.configure, you must do so with a publicKey property.
invalid-public-key Check the publicKey to ensure it matches that of your admin app’s API Access section.
already-configured A recurly instance may only be configured once. Doing so more than once is excessive.
validation A request validation error has occured. This can indicate many possible issues, and you should check the fields property to determine which fields caused the error.
invalid-parameter Occurs when a tokenization parameter does not pass our internal validations. Check the fields property to determine which fields caused the error.
api-error A request to the Recurly API has encountered an issue. This too can indicate many possible issues, and we recommend inspecting the message and fields properties for more information.
not-found This happens when a nonexistent plan is requested.
missing-plan A Pricing instance will emit this if a plan has not been specified before trying to set a proeprty that depends on a plan, such as a coupon or addon.
invalid-addon Occurs when an addon is added to a Pricing instance but is not valid for the instance’s selected plan.
invalid-currency Similarly, if a currency is requested which is not valid for the selected plan.
paypal-not-configured In order to perform a PayPal transaction, your site must be configured to accept PayPal reference transactions.
paypal-canceled The customer canceled the PayPal agreement flow.
paypal-error A generic PayPal error has occurred. Inspect message to learn more.


Integration examples

We’ve prepared a full suite of example integrations for Ruby, Node.js, Python, and PHP using popular web frameworks for each language. These examples demonstrate the simplest method of integration, with a no-frills UI.

Code on GitHub

Integration examples with UI

To demonstrate the bare minimum to more advanced features of Recurly.js, we’ve prepared example merchant integrations of varying complexity. These examples also include a fancy UI to get you going with something nice.

Subscribe with minimal billing Info

Subscribe with more billing Info

Subscribe with advanced pricing

One-time transactions

Update billing info

The Kale Krate site is open source. Its backend is written in Ruby with Sinatra and the Recurly gem. It is deployable out of the box to Heroku.

Code on GitHub


Recurly.js supports Chrome, Firefox, Safari, iOS 6+, and IE 9+.

We’re also here to lend a hand on any Recurly.js integration questions! You can get help from us in a handful of ways:

For other Recurly related questions, please contact for help with your account or other general questions.