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 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

Include

<script src="https://js.recurly.com/v4/recurly.js"></script>

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

This exposes a single global object, recurly.

Configure

recurly.configure('sc-ABCDEFGHI123456789');

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.

recurly.configure accepts other options not detailed here. You may refer to the source for more detail.

Build a Card form

Build a form however you like. Use the data-recurly attribute to identify input field targets to Recurly.js. To identify where Recurly.js will inject card data fields, we recommend using simple div elements.

<form>
  <input type="text" data-recurly="first_name">
  <input type="text" data-recurly="last_name">
  <div data-recurly="number"></div>
  <div data-recurly="month"></div>
  <div data-recurly="year"></div>
  <div data-recurly="cvv"></div>
  <input type="hidden" name="recurly-token" data-recurly="token">
  <button>submit</button>
</form>

To collect card 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.

Note that card data is not present in the form, but merely given a placeholder element. This is because Recurly.js must inject those fields onto the page within iframes to ensure strict security of customer card data.

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 expiration 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.

Styling card fields

Since card fields must be injected within iframes, any font style information must be passed to recurly.configure

recurly.configure({
  // ...
  fields: {
    number: '#recurly-number',
    month: '#recurly-month',
    year: '#recurly-year',
    cvv: '#recurly-cvv'
  },
  style: {
    all: {
      fontFamily: 'Droid Sans',
      fontSize: '14px',
      fontColor: 'green',
      fontWeight: 'bold',
      fontVariant: 'small-caps',
      fontStyle: 'italic',
      lineHeight: '1em'
    },
    number: {
      placeholder: 'numero'
    },
    month: {
      placeholder: 'mm'
    },
    year: {
      placeholder: 'yyyy'
    },
    cvv: {
      placeholder: 'cvv'
    }
  }
});

Since Recurly.js must inject card data fields into iframes, the default browser appearance for those fields will likely not match the appearance of the other fields in your payment form. We provide the following CSS classes to achieve a look and feel similar to your form. Using these classes, you may specify field size, colors, and a full range of appearance customization to make the injected card fields blend into your payment form.

Class Name Description
recurly-hosted-field Default styles for the div surrounding each field iframe
recurly-hosted-field-focus Applied when the user focuses on a field
recurly-hosted-field-number Default styles for the div surrounding the number field iframe
recurly-hosted-field-month Default styles for the div surrounding the month field iframe
recurly-hosted-field-year Default styles for the div surrounding the year field iframe
recurly-hosted-field-cvv Default styles for the div surrounding the cvv field iframe

Fonts

You may specify font and placeholder text for card fields through recurly.configure. The example call to the right demonstrates all available style attributes you may send to recurly.configure.

Custom fonts are sourced from Google Web Fonts. Simply use the name of the font as it appears on the Google Web Fonts site.

Responsive styles

All of the built in field classes will support and respond to media queries. You may call recurly.configure again to change style properties – thus you may change any property if the window size changes.

Working with bank accounts

Just like a card form, use the data-recurly attribute to identify fields to Recurly.js. Since Recurly.js does not need to inject fields for bank accounts, all fields may be displayed as inputs on your payment form.

<form>
  <input type="text" data-recurly="routing_number">
  <input type="text" data-recurly="account_number">
  <input type="text" data-recurly="account_number_confirmation">
  <input type="text" data-recurly="account_type">
  <input type="text" data-recurly="name_on_account">
  <input type="hidden" name="recurly-token" data-recurly="token">
  <button>submit</button>
</form>

As you would a card form, you collect bank account payment information from your customers, identifying them with the data-recurly attributes on the input tags. This form demonstrates the minimum required input fields, and the table below highlights all possible input fields.

Bank Account data

Field Name Example Description
routing_number 123456780 Routing number. required
account_number 111111111 Account number. required
account_number_confirmation 111111111 Account number confirmation. required
account_type checking or savings Account type. required
name_on_account John Rambo Full name on account. required

Billing address

See card billing address.

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;
  event.preventDefault();
  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 token.id and do
      // any processing you wish
      form.submit();
    }
  });
});

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.

recurly.token

You may call recurly.token with a form element

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

Or with an Object

// alternatively..
var billingInfo = {
  // required attributes
  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 token.id
  }
}

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

returns

Nothing.

recurly.bankAccount.token

You may call recurly.bankAccount.token with a form element

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

Or with an Object

// alternatively..
var billingInfo = {
  // required attributes
  routing_number: '123456780',
  account_number: '111111111',
  account_number_confirmation: '111111111',
  account_type: 'checking',
  name_on_account: 'John Rambo',

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

recurly.bankAccount.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 token.id
  }
}

Sends bank account billing information to Recurly to store as a token, sending that token id back to you. Just as with card tokenization, there are two ways to call recurly.bankAccount.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.bankAccount.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

returns

Nothing.

Using a Token

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

$ curl https://:subdomain.recurly.com/v2/subscriptions \
    -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>john@firstblood.com</email> \
    <first_name>John</first_name> \
    <last_name>Rambo</last_name> \
    <billing_info> \
      <token_id>TOKEN_ID</token_id> \
    </billing_info> \
  </account> \
</subscription>'
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.save
$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 = 'john@firstblood.com';

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

$subscription->account->create();
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");

subscription.Create();

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.

Events

Listen to events using Emitter methods

// Listen to the 'change' event
recurly.on('change', changeHandler);

// But we're feeling indecisive today. Let's detach this event
recurly.off('change', changeHandler);

// .once will listen for one event then detach itself
recurly.once('field:submit', function () {
  $('#my-payment-form').submit();
});

function changeHandler (state) {
  // state.fields
}

Example RecurlyState object

var recurlyState = {
  fields: {
    number: {
      valid: false,
      firstSix: '',
      lastFour: '',
      brand: 'unknown',
      empty: true,
      length: 0,
      focus: false
    },
    month: {
      valid: false,
      empty: true,
      length: 0,
      focus: false
    },
    year: {
      // same as month
    },
    cvv: {
      // same as month
    }
  }
}

A Recurly instance is an event emitter, and will emit events throughout the lifecycle of your customer’s interaction with your payment form. Events can be attached using the recurly.on method and removed using recurly.off. The example to the right shows the various ways that you can attach and remove events.

Event: change

change is emitted whenever a customer changes the state of hosted card fields, those that you may not otherwise observe directly with DOM events. For example, if your customer types ‘4’ into the number field, then the state of the number field will change, and the change event will emit.

The change event emits a RecurlyState object, whose values are demonstrated to the right. This will give you useful insight into the entire state of the recurly-controlled components of your payment form.

Event: field:submit

field:submit is emitted when a user presses the enter key while they are focused on a hosted field. Since this action typically submits a form, we recommend performing a payment form submission when this event is emitted.

PayPal

<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.

recurly.paypal

$('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 token.id;
    }
  });
});

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

arguments

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

returns

nothing.

Pricing

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!

recurly.Pricing

var pricing = recurly.Pricing();

Creates a Pricing instance.

no arguments

returns

recurly.Pricing a new Pricing instance.

pricing.attach

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

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

var pricing = recurly.Pricing();

pricing.attach(document.querySelector('#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.

arguments

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

returns

nothing.

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

Events

Example price object emitted by the change event.

var pricingState = {
  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 pricing.off, similarly to how events are managed on recurly.

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.
set.coupon Coupon object.
set.address Address object.
set.currency Currency code.
set.tax Tax object.

Pricing API

var pricing = recurly.Pricing();

pricing
  .plan('basic', { quantity: 1 })
  .currency('USD')
  .addon('addon1', { quantity: 2 })
  .coupon('coop')
  .address({
    country: 'US',
    postal_code: '90210'
  })
  .tax({
    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.

PricingPromise

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.

Validation

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.

recurly.bankAccount.bankInfo

Get additional bank account info

var lookupData = {
  routingNumber: '123456780'
};
recurly.bankAccount.bankInfo(lookupData, infoHandler);

The bankInfo method requires a handler like this one

function infoHandler (err, bankInfo) {
  if (err) {
    // handle error
  } else {
    // handle success using bankInfo
  }
}

Looks up additional bank information based from a given routing number. If the routing number supplied is invalid, invalid-routing-number will be the error code returned.

Lookup data

Field Name Example Description
routingNumber 123456780 The routing number for a bank

Bank info data

Field Name Example Description
bank_name Bank of Recurly The name for the found bank

Errors

Example RecurlyError object.

{
  name: 'validation',
  code: 'validation',
  message: 'There was an error validating your request.',
  fields: [
    'number',
    'year'
  ]
}

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
Configuration
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.
Tokenization
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.
Pricing
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
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.
invalid-routing-number The bank routing number is not valid

Examples

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

Support

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 support@recurly.com for help with your account or other general questions.

For copies of documentation for previous versions of Recurly.js, please contact support.