Giter Site home page Giter Site logo

recurring's Introduction

A node client for recurly's v2 api, with support for secure parameter signing for recurly.js embedded forms.

on npm Tests Dependencies io.js supported

This code is still in development. We do not have complete coverage of the API yet.

The recurly API has a number of different versions. In order to provide clarity as to which version of the recurly API this package supports, we aim to keep our release versions in sync with the recurly versioning scheme.

The following versions of the recurring package are currently supported:

Recurly API version NPM dist-tag Git development branch
2.7 recurly-v2.7 (latest) 2.7 (master)
2.3 recurly-v2.3 2.3

Recurly API

An example of typical usage:

var Recurring = require('recurring');
var recurly = new Recurring();

recurly.setAPIKey('your-api-key');
recurly.setRateLimit(400);
recurly.setCache(false);

var account = recurly.Account();
account.id = 'account-uuid';
account.fetch(function(err) {
  account.fetchSubscriptions(function(err, subscriptions {
    subscriptions[0].cancel(function(err, updated) {
      console.log(updated.state); // will be 'canceled'
    });
  });
});

recurly.Account.all(function(err, accounts) {
  // accounts is an array containing all customer accounts
});

recurly.Plan.all(function(err, plans) {
  // plans is an array containing all plans set up for your account
});

Configuration

recurly.setAPIKey()
In order to access the Recurly API you must supply your API key which can be setterFunc by calling the setAPIKey() method.

var Recurring = require('recurring');
var recurly = new Recurring();
recurly.setAPIKey('your-api-key');

recurly.setRateLimit()
The recurly API has a rate limit policy in place that prevents excessive calls being made to the API. By default sandbox accounts have a limit of 400 requests per minute and live accounts have a limit of 1000 requests per minute. In order to help ensure that you do not exceed these limits Recurring provides a configurable rate limiter. The rate limiter can be configured by calling the setRateLimit() method.

var Recurring = require('recurring');
var recurly = new Recurring();
recurly.setRateLimit(400);

recurly.setCache()

Basic in memory result caching can be enabled by calling the setCache() method so that subsequent requests to fetch the same data does not result in additional API calls to Recurly.

var Recurring = require('recurring');
var recurly = new Recurring();
recurly.setCache(true);

recurly.clearCache()

The recurly result cache can be cleared by calling the clearCache() method.

var Recurring = require('recurring');
var recurly = new Recurring();
recurly.clearCache();

All data types

Recurly is not consistent about how it names the ID fields for each data type. For some it's uuid and for others foo_code. Recurring hides this away: every data type has an id property that sets the correct field name for Recurly.

instance.create()
Create an object of the given type by POSTing to Recurly.

DataType.create(optionsHash, function(err, object));

instance.fetch()
Fetch an item of a given type from Recurly. The item must have an id.

instance.fetch(function(err, instance))

instance.destroy()
Destroy, delete, close, cancel, or otherwise remove the specified object. Invokes http DELETE on the item's href. The item must have an id.

instance.destroy(function(err));

instance.update()
Most data types have an update() method that changes the stored data.

instance.update(options, function(err, updated));

Plan

Plan.all()
Fetch a list of plans. Responds with an array of all plans that match the passed-in (optional) filters.

Plan.all(filter, function(err, plans));

Plan.iterator()
Fetch a list of plans. Responds with an async iterator that lazy loads data from recurly in batches of 200.

var iterators = require('async-iterators');
var iterator = Plan.iterator();
iterators.forEachAsync(iterator, function (err, data, next) {
  ...
  next();
});

plan.fetchAddOns()
Fetch plan addons. Responds with a array of plan addons.

plan.fetchAddOns(function(err, addons));

Account

Account.all()
Fetch a list of accounts. Responds with an array of all accounts that match the passed-in (optional) filters.

Account.all(filter, function(err, accounts));

Account.iterator()
Fetch a list of accounts. Responds with an async iterator that lazy loads data from recurly in batches of 200.

var iterators = require('async-iterators');
var iterator = Account.iterator();
iterators.forEachAsync(iterator, function (err, data, next) {
  ...
  next();
});

account.close()
Close an account. Alias for delete.

account.close(function(err));

account.reopen()
Reopen a closed account:

account.reopen(function(err, updated));

account.fetchAdjustments() Fetch adjustment information for an account. Responds with an array of adjustments for this account.

account.fetchAdjustments(function(err, adjustments));

account.fetchBillingInfo()
Fetch billing information for an account. Responds with an BillingInfo object.

account.fetchBillingInfo(function(err, info));

account.fetchSubscriptions()
Fetch subscription information for an account. Responds with an array of subscriptions for this account.

account.fetchSubscriptions(function(err, subscriptions));

account.fetchTransactions()
Fetch transaction information for an account. Responds with an array of transactions for this account.

account.fetchTransactions(function(err, transactions));

account.fetchInvoices()
Fetch invoice information for an account. Responds with an array of invoices for this account.

account.fetchInvoices(function(err, invoices));

Billing Info

billingInfo.update()
Add/update billing information for an account.

binfo = recurly.BillingInfo();
binfo.account_code = '1234';
var billing_data = {
  first_name: 'Dummy',
  last_name: 'User',
  number: '4111-1111-1111-1111',
  month: 1,
  year: 2020,
  verification_value: '123',
  address1: '760 Market Street',
  address2: 'Suite 500',
  city: 'San Francisco',
  state: 'CA',
  country: 'USA',
  zip: '94102'
};

binfo.update(billing_data, function(err, binfo) {
  demand(err).not.exist();
  binfo.last_four.must.equal('1111');
});

Using a billing token

You can also update billing information using a recurly.js billing token in place of the raw billing information (recommended)

binfo = recurly.BillingInfo();
binfo.account_code = '1234';
var billing_data = {
  token_id: 'bunYTdIdjfJJY6Z87j5NtA' // <- recurly.js billing token.
};
binfo.update(billing_data);

skipAuthorization

When adding billing information to an account Recurly may ake an authorization attempt against the card which may incur charges depending on your payment gateway. In order to prevent Recurly from making this authorization attempt, a skipAuthorization paramater can be supplied along with the billing information.

binfo = recurly.BillingInfo();
binfo.account_code = '1234';
var billing_data = {
  number: '4111-1111-1111-1111',
  month: 1,
  year: 2020,
  skipAuthorization: true // <- do not make authorization request.
};
binfo.update(billing_data);

Subscription

Subscription.all()
Fetch a list of subscriptions. Responds with an array of all subscriptions that match the passed-in (optional) filters.

Subscription.all(filter, function(err, subscriptions));

Subscription.iterator()
Fetch a list of subscriptions. Responds with an async iterator that lazy loads data from recurly in batches of 200.

var iterators = require('async-iterators');
var iterator = Subscription.iterator();
iterators.forEachAsync(iterator, function (err, data, next) {
  ...
  next();
});

subscription.cancel()
Cancel a subscription.

subscription.cancel(function(err, updated));

subscription.reactivate()
Reactivate a cancelled subscription.

subscription.reactivate(function(err, updated));

subscription.postpone()
Postpone a subscription.

subscription.postpone(nextRenewalDate, function(err, updated));

subscription.terminate()
Terminate a subscription using the specific refund type. Valid refund types are 'partial', 'full', and 'none'.

subscription.terminate(refundType, function(err, updated));

Coupon

Coupon.all()
Fetch a list of coupons. Responds with an array of all coupons.

Coupon.all(function(err, coupons));

Coupon.iterator()
Fetch a list of coupons. Responds with an async iterator that lazy loads data from recurly in batches of 200.

var iterators = require('async-iterators');
var iterator = Coupon.iterator();
iterators.forEachAsync(iterator, function (err, data, next) {
  ...
  next();
});

coupon.redeem()
Redeem a coupon.

coupon.redeem(options, function(err, redemption));

Redemption

[TODO]

Transaction

Transaction.all()
Fetch a list of transactions. Responds with an array of all transactions that match the passed-in (optional) filters.

Transaction.all(filter, function(err, transactions));

Transaction.iterator()
Fetch a list of transactions. Responds with an async iterator that lazy loads data from recurly in batches of 200.

var iterators = require('async-iterators');
var iterator = Transaction.iterator();
iterators.forEachAsync(iterator, function (err, data, next) {
  ...
  next();
});

transaction.refund(amountInCents, function(err)) (DEPRECIATED)
If amountInCents is omitted, the transaction is refunded in full. Responds with any errors; the transaction object is updated.

Invoice

Invoice.all()
Fetch a list of invoices. Responds with an array of all invoices that match the passed-in (optional) filters.

Invoice.all(filter, function(err, invoices));

Invoice.iterator()
Fetch a list of invoices. Responds with an async iterator that lazy loads data from recurly in batches of 200.

var iterators = require('async-iterators');
var iterator = Invoice.iterator();
iterators.forEachAsync(iterator, function (err, data, next) {
  ...
  next();
});

invoice.refund(options, function(err))
If options.amount_in_cents is omitted, the invoice is refunded in full. Responds with any errors; the invoice object is updated.

options can include:

  • amount_in_cents (default: undefined) - The specific amount to be refunded from the original invoice. If left empty, the remaining refundable amount will be refunded.

  • refund_apply_order (default: 'credit') - If credit line items exist on the invoice, this parameter specifies which refund method to use first. Most relevant in a partial refunds, you can chose to refund credit back to the account first or a transaction giving money back to the customer first. Set as 'credit' or 'transaction'.

invoice.markSuccessful(function(err))
Mark an Invoice as Paid Successfully.

invoice.markFailed(function(err))
Mark an Invoice as Failed Collection.

Errors

All callbacks follow the node convention of reporting any error in the first parameter. If a transaction with Recurly succeeds but is rejected by Recurly for some reason-- inconsistent data, perhaps, or some other reason-- that err parameter is an instance of RecurlyError. The original transaction errors reported by Recurly are available as an array of structs in the errors parameter. For instance, here's the result of a billing info update with an invalid, expired CC:

{
	name: 'RecurlyError',
	message: '2 transaction errors',
	errors: [
		{
			field: 'billing_info.number',
			symbol: 'invalid',
			message: 'is not a valid credit card number'
		},
		{
			field: 'billing_info.number',
			symbol: 'expired',
			message: 'is expired or has an invalid expiration date'
		}
	]
}

SignedQuery

This provides the back-end support for signing parameters for forms embedded using recurly.js. See Recurly's signature documentation for details on which parameters must be signed for each form type.

var Recurring = require('recurring');
var recurly = new Recurring();

var signer = new recurly.SignedQuery('your-private-api-key');
signer.set('account', { account_code: 'account-id' });
var signedParameters = signer.toString();

The nonce & timestamp parameters are generated for you if you don't provide them. The nonce is created using node-uuid.

FormResponseToken

After Recurly handles a form submission, it posts to you a token pointing to the form results. Use a FormResponseToken object to fetch the results object represented by the token.

var Recurring = require('recurring');
var recurly = new Recurring();

var recurlyResponse = new recurly.FormResponseToken(token, 'subscription');
recurlyResponse.process(function(err, subscription) {
	if (err)
		return handleError(err);

	// subscription contains the new subscription data;
});

Having to hint about the type of the response is clunky; TODO fix.

Contributing

Unit tests for whatever you fix/implement/improve would be awesome. Recurring's are written with mocha and chai.

License

MIT. See accompanying LICENSE file.

recurring's People

Contributors

bhelx avatar bounty-land-travis-ci-bot avatar capaj avatar ceejbot avatar jgautier avatar marcbachmann avatar mattlorey avatar mgarlanger avatar mrfelton avatar pdehaan avatar pwshugar avatar rahulbile avatar sakaenakajima avatar simontabor avatar ssbrewster avatar surftour avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

recurring's Issues

Ability to fetch/process items in batches

the various .all() methods literally fetch all results from Recurly and return as one big list of objects. When dealing with large datasets in Recurly, this is not usable as the memory consumption would be out of control.

In addition to .all() it would be useful to have a way to processes the results in batches.

Ability to clear internal caches

The way the module is right now, if you make a call to (for example), Plan.all() the fetched data is cached and subsequent calls to Plan.all() return the exact same data without actually making a call to Recurly.This is not always desirable and some method to clear the static caches is needed.

Infinite recursion(no pun intended) with BillingInfo.account_code

When serializing an instance of BillingInfo, a RangeError will be encountered due to the following code:
https://github.com/ceejbot/recurring/blob/master/lib/models/billing-info.js#L53

The class is creating a property getter that points to itself.

An error similar to the following is produced:

"err": {
    "message": "Maximum call stack size exceeded",
    "name": "RangeError",
    "stack": "RangeError: Maximum call stack size exceeded\n    at BillingInfo.__defineGetter__ [as account_code] (/var/task/node_modules/recurring/lib/models/billing-info.js:52:43)\n    at BillingInfo.__defineGetter__ [as account_code] (/var/task/node_modules/recurring/lib/models/billing-info.js:53:19)\n    at BillingInfo.__defineGetter__ [as account_code] (/var/task/node_modules/recurring/lib/models/billing-info.js:53:19)\n    at BillingInfo.__defineGetter__ [as account_code] (/var/task/node_modules/recurring/lib/models/billing-info.js:53:19)\n    at BillingInfo.__defineGetter__ [as account_code] (/var/task/node_modules/recurring/lib/models/billing-info.js:53:19)\n    at BillingInfo.__defineGetter__ [as account_code] (/var/task/node_modules/recurring/lib/models/billing-info.js:53:19)\n    at BillingInfo.__defineGetter__ [as account_code] (/var/task/node_modules/recurring/lib/models/billing-info.js:53:19)\n    at BillingInfo.__defineGetter__ [as account_code] (/var/task/node_modules/recurring/lib/models/billing-info.js:53:19)\n    at BillingInfo.__defineGetter__ [as account_code] (/var/task/node_modules/recurring/lib/models/billing-info.js:53:19)\n    at BillingInfo.__defineGetter__ [as account_code] (/var/task/node_modules/recurring/lib/models/billing-info.js:53:19)\n    at BillingInfo.__defineGetter__ [as account_code] (/var/task/node_modules/recurring/lib/models/billing-info.js:53:19)\n    at BillingInfo.__defineGetter__ [as account_code] (/var/task/node_modules/recurring/lib/models/billing-info.js:53:19)\n    at BillingInfo.__defineGetter__ [as account_code] (/var/task/node_modules/recurring/lib/models/billing-info.js:53:19)\n    at BillingInfo.__defineGetter__ [as account_code] (/var/task/node_modules/recurring/lib/models/billing-info.js:53:19)\n    at BillingInfo.__defineGetter__ [as account_code] (/var/task/node_modules/recurring/lib/models/billing-info.js:53:19)\n    at BillingInfo.__defineGetter__ [as account_code] (/var/task/node_modules/recurring/lib/models/billing-info.js:53:19)"
  }

Looks like

 this.__defineGetter__('account_code', () => {
      return this.account_code
    })

Should be

 this.__defineGetter__('account_code', () => {
      return this.properties.account_code
    })

Happy to send a PR if this repo is still active.

Invalid data in Account.create results in an attempt to reopen an account

Currently, in Account.create it sets the codes 201 and 422 as valid return codes. If the code is a 422 it assumes that the account already exists and attempts to reopen it by calling account.reopen.

According to https://dev.recurly.com/docs/welcome#section-client-error-status-codes-4xx- a 422 code means that the entity was unprocesable. It does not mention that it means the account already exists.

If you try to create an account with an invalid email address, you get a 422 error. recurring then tries to reopen an account, which results in a not_found error, masking the real reason for the error.

Support invoice refunds (line item refunds are depreciated)

We are moving to refunds through the Invoice object. Transaction refunds will no longer work if you have switched to Invoice Refunds in your Site Settings. If you created your site after November 11, 2014, you are automatically on Invoice refunds. Please see our API documentation on Invoices.

Trying to issue a refund results in the following RecurlyError

{"name":"RecurlyError","errors":[{"symbol":"feature_not_enabled","description":"Cannot refund or void transactions when line item refunds are enabled."}],"message":"Cannot refund or void transactions when line item refunds are enabled."}

Ability to send billing data using a token (support recurlyjs)

When using Recurly.js credit card data is sent to Recurly directly from the browser. Recurly returns a token which can then be used in place of the billing data in subsiquent requests. see https://docs.recurly.com/api/billing-info#update-billing-info-token

Currently the validation in https://github.com/ceejbot/recurring/blob/master/lib/recurly.js#L696-L707 (and other places) prevents this from being possible with this library.

That validation should be made more flexible to support sending billing info using a token.

Coupon's `coupon_code` gets overwritten with value of `id`

Hi,

I noticed coupon_code returned from coupon.fetch has the value of id and not of the coupon code. I think it's because coupon_code is used as the idField for the Coupon model (here).

var coupon = recurly.Coupon();
coupon.coupon_code = 'off20';

coupon.fetch(function(err, response) {
  if (err) return res.status(400).json(err);

  console.assert(response.coupon_code, 'off20'); // false
});

I think this piece is responsible. It looks like when setting id (in . inflate()), the value of id is also assigned to the idField, which is coupon_code.

Properties not fully mapped

Some properties returned by recurly are not fully mapped into .properties so they end up as top level properties on the object. This is inconsistent with other properties.

Subscription creation

Hi,

I see that you have the subscription create api call implemented yet your docs don't mention it. Is it not fully implemented or you just forgot to mention it in the README?

Error('"name" and "value" are required for setHeader().');

I'm getting the following error when calling Subscription.fetch()

_http_outgoing.js:333
web-1 | 2015-08-30T12:44:27.652788759Z App 817 stderr: throw new Error('"name" and "value" are required for setHeader().');
web-1 | 2015-08-30T12:44:27.652788759Z App 817 stderr: ^
web-1 | 2015-08-30T12:44:27.652788759Z App 817 stderr: Error: "name" and "value" are required for setHeader().
web-1 | 2015-08-30T12:44:27.652788759Z App 817 stderr: at ClientRequest.OutgoingMessage.setHeader (_http_outgoing.js:333:11)
web-1 | 2015-08-30T12:44:27.652788759Z App 817 stderr: at new ClientRequest (_http_client.js:101:14)
web-1 | 2015-08-30T12:44:27.652788759Z App 817 stderr: at Object.exports.request (http.js:49:10)
web-1 | 2015-08-30T12:44:27.652788759Z App 817 stderr: at Object.exports.request (https.js:136:15)
web-1 | 2015-08-30T12:44:27.652788759Z App 817 stderr: at Request.start (/home/app/node_modules/recurring/node_modules/request/request.js:812:30)
web-1 | 2015-08-30T12:44:27.652788759Z App 817 stderr: at Request.end (/home/app/node_modules/recurring/node_modules/request/request.js:1494:28)
web-1 | 2015-08-30T12:44:27.652788759Z App 817 stderr: at end (/home/app/node_modules/recurring/node_modules/request/request.js:550:16)
web-1 | 2015-08-30T12:44:27.652788759Z App 817 stderr: at /home/app/node_modules/recurring/node_modules/request/request.js:564:9
web-1 | 2015-08-30T12:44:27.652788759Z App 817 stderr: at /home/app/node_modules/loopback/node_modules/continuation-local-storage/node_modules/async-listener/glue.js:188:31
web-1 | 2015-08-30T12:44:27.652788759Z App 817 stderr: at process._tickDomainCallback (node.js:381:11)
web-1 | 2015-08-30T12:44:27.652788759Z App 817 stderr: at process. (/home/app/node_modules/loopback/node_modules/continuation-local-storage/node_modules/async-listener/index.js:19:15)

I'm not clear where exactly the undefined header is coming from, however I have found that more recent versions of the request package fix this issue.

See request/request#1522

This was rolled into v2.55.0: https://github.com/request/request/blob/master/CHANGELOG.md#v2550-20150405

Updating request would be a good idea.

Fetch addons

The second argument of RecurlyData.fetchAll should be URI.

RecurlyData.fetchAll(Addon, this.addons, function(err, results) {
}

Ability to set Recurly-Skip-Authorization header

You will need to add the HTTP Header The "Recurly-Skip-Authorization: true" when performing the API requests that adds billing info to an account. This header allows you to save billing information on an account without processing an authorization charge, and should only be used for an initial customer import.

This header works on the endpoint that creates an account and the endpoint that saves billing info to an account. Please note: It does not work on the subscription API endpoints - you will need to create the account & billing info before creating the subscription in order to use this header.

Provide more information to aid debugging XML parsing errors

I currently have a situation where a call to Subscription.fetch() is resulting in an XML parsing error, but there is not enough information to allow me to see what really went wrong.

My error log looks like this

recurly.get xml parsing error {"message":"Non-whitespace before first tag.\nLine: 0\nColumn: 1\nChar: H","stack":"Error: Non-whitespace before first tag.\nLine: 0\nColumn: 1\nChar: H\n    at error (/home/app/node_modules/recurring/node_modules/xml2js/node_modules/sax/lib/sax.js:652:8)\n    at strictFail (/home/app/node_modules/recurring/node_modules/xml2js/node_modules/sax/lib/sax.js:675:22)\n    at Object.write (/home/app/node_modules/recurring/node_modules/xml2js/node_modules/sax/lib/sax.js:955:11)\n    at Parser.exports.Parser.Parser.parseString (/home/app/node_modules/recurring/node_modules/xml2js/lib/xml2js.js:479:31)\n    at Parser.parseString (/home/app/node_modules/recurring/node_modules/xml2js/lib/xml2js.js:7:59)\n    at RecurlyParser.parseXML (/home/app/node_modules/recurring/lib/parser.js:89:14)\n    at Request._callback (/home/app/node_modules/recurring/lib/recurly.js:124:11)\n    at Request.self.callback (/home/app/node_modules/request/request.js
web-1 | :368:22)\n    at Request.emit (events.js:110:17)\n    at Request. (/home/app/node_modules/request/request.js:1219:14)\n    at Request.emit (events.js:129:20)\n    at IncomingMessage. (/home/app/node_modules/request/request.js:1167:12)\n    at IncomingMessage.emit (events.js:129:20)\n    at _stream_readable.js:908:16\n    at /home/app/node_modules/loopback/node_modules/continuation-local-storage/node_modules/async-listener/glue.js:188:31\n    at process._tickDomainCallback (node.js:381:11)"}

From this, all I can really lean is "Non-whitespace before first tag". But I have no idea what XML or data was received and what the actual cause of the issue was.

It would be more helpful if the XML that failed to parse was included in the error message.

Following code example I get "Plan.all is not a function"

In following your first example, this code results in an error "Plan.all is not a function":

var Recurring = require('recurring');
var recurly = new Recurring();

recurly.setAPIKey(<my key>);
recurly.setRateLimit(400);
recurly.setCache(false);

recurly.Plan.all(function(err, plans) {
  // Never gets here.
});

Listing this project on the Recurly Dev site

Hey Everyone,

First off I just want to say thank you for building and maintaining this library. I know from experience that it takes a lot of time and persistence to keep an open source project like this moving.

In 2016 I added this user agent so we could track who is using this library. Since then, it's been adopted by a good portion of our node users. We've also been impressed with the responsiveness in which y'all deal with issues.

On Recurly's dev documentation site, we have this project listed as the unofficial node library: https://github.com/cgerrior/node-recurly

It's in a pretty bad state and I'd like to switch that link over to your project. I just wanted to come here to ask if that's okay. We unfortunately can't officially support the library at this time, but I also wanted to see if there is anything that I (or Recurly) can do to help out.

Thanks Again!

recurly is a dictionary

I have this:

var recurly = require("recurring")();
recurly.setAPIKey(cfg.recurlyApiKey);
recurly.setRateLimit(400);
recurly.setCache(false);

And this:

> recurly
{ AUTH_BASIC: 'Basic fdsfdsfdsfds' }
> typeof recurly
'object'
> recurly.accounts
> typeof recurly.accounts
'undefined'
> recurly.accounts.list
TypeError: Cannot read property 'list' of undefined
> 

Why?

Gracefully handle the case where API key is not set or invalid

Attempting to make a request without having first set the API key results in a fatal error trying to parse the result as XML. In this case, the result is not XML, but a 401 error with the body set as a string "HTTP Basic: Access denied.\n". This case should be handled gracefully.

Update API to 2.11

Please update this lib to match Recurly API 2.11

Few important API endpoints missing, like Shipping Address API

Updating accounts

Based on README, instance.update takes new data as an first argument, but for account this is not true. Instead I need to set new data to account instance, and then call update with callback as first argument.

var account = recurly.Account();
account.id = 'ACCOUNT_ID';
account.email = 'NEW_EMAIL;
account.update(function(err, account)

instead of documented

var account = recurly.Account();
account.id = 'ACCOUNT_ID';
account.update({email: 'NEW_EMAIL'}, function(err, account)

BillingInfo, Plan, Subscription seems to work as documented..

offer promise api

most libraries today offer both callback and promise api. It would be great if recurring could offer the same for it's consumers.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.