NAV

Introduction

An Erlang/LFE Client for the Recurly Billing REST API

From the Recurly docs site:

“Recurly provides a complete recurring billing system designed to remove all the headaches from subscription billing. Whether you’ve integrated with our Hosted Payment Pages, Recurly.js embedded forms, or API, these documents will help manage your day-to-day billing.”

The LFE library for the Recurly REST service was created in order to support the billing needs of LFE, Erlang, and other BEAM language applications that need to offer billing features.

Getting Started

Installation

Just add the rcrly repo to your rebar.config deps:

  {deps, [
    ...
    {rcrly, ".*",
      {git, "git@github.com:cinova/rcrly.git", "master"}}
      ]}.

Or, if you use lfe.config:

    #(project (#(deps (#("cinova/rcrly" "master")))))

And then do the usual:

    $ make compile

To install the LFE rcrly REST client library, follow the standard rebar practics or use an lfe.config file.

Configuration

This project comes with a sample configuration file you can copy and then edit:

$ cp sample-lfe.ini ~/.rcrly/lfe.ini

Or you can just use the following as a template:

[REST API]
key = GFEDCBA9876543210
host = yourname.recurly.com
timeout = 10000
version = v2

The LFE rcrly library supports two modes of configuration:

OS environment variables take precedence over values in the configuration file. If you would like to use environment variables, the following may be set:

You have the option of using values stored in a configuration file, instead.

Starting rcrly

Execute the following before using rcrly, when not using the make targets which start it automatically:

> (rcrly:start)
(#(gproc ok)
 #(econfig ok)
 #(inets ok)
 #(ssl ok)
 #(lhttpc ok))

The make targets for both the LFE REPL and the Erlang shell start rcrly automatically. If you didn’t use either of those, then you will need to start rcrly manually.

If you’re not in the REPL and you will be using this library programmatically, you will want to make that call when your own application starts.

Authentication

In your OS shell, export your Recurly API key and your subdomain, e.g.:

$ export RECURLY_API_KEY=GFEDCBA9876543210
$ export RECURLY_HOST=yourname.recurly.com

Or be sure to have these defined in your ~/.rcrly/lfe.ini file:

[REST API]
key = GFEDCBA9876543210
host = yourname.recurly.com

Recurly clients authenticate by setting HTTP headers. The rcrly library does this for you automatically by extracting the necessary data from OS environment variables or from your rcrly configuration file.

Making Calls

Making calls from LFE are pretty standard:

> (rcrly:get-accounts)

Which gives results like the following:

#(ok
  (#(account ...)
   #(account ...)
   ...))

Erlang calls are done in the usual way, escaping hypenated LFE atoms as necessary:

1> rcrly:'get-accounts'().

Which will give results in the following form:

{ok,[{account, [...]},
     {account, [...]},
     ...]}

As aluded to above, even though rcrly is written in LFE, it can be used from an BEAM language which supports Core Erlang.

Options

Every rcrly get and post API call takes an optional final positional argument which supprts keyword arguments. The following are supported (or planned) such options:

return-type

When the return-type is set to data (the default), the data from the response is what is returned:

> (rcrly:get-account 1 '(#(return-type data)))

Results:

#(ok
  (#(adjustments ...)
   #(invoices ...)
   #(subscriptions ...)
   #(transactions ...)
   #(account_code () ("1"))
   ...
   #(address ...)
   ...))

When the return-type is set to full, the response is annotated and returned:

> (rcrly:get-account 1 '(#(return-type full)))

Results:

#(ok
  (#(response ok)
   #(status #(200 "OK"))
   #(headers ...)
   #(body
     (#(tag "account")
      #(attr (#(href "https://yourname.recurly.com/v2/accounts/1")))
      #(content
        #(account
          (#(adjustments ...)
           #(invoices ...)
           #(subscriptions ...)
           #(transactions ...)
           ...
           #(account_code () ("1"))
           ...
           #(address ...)
           ...)))
      #(tail "\n")))))

When the return-type is set to xml, the “raw” binary value is returned, as it is obtained from lhttpc, without modification or any parsing:

> (rcrly:get-account 1 '(#(return-type xml)))

Results:

#(ok
  #(#(200 "OK")
    (#("strict-transport-security" "max-age=15768000; includeSubDomains")
     #("x-request-id" "ac52s06cmfugp9oauclg")
     #("cache-control" "max-age=0, private, must-revalidate")
     ...)
    #B(60 63 120 109 108 32 118 101 114 115 105 111 110  ...)))

The rcrly client library lets you specify the format of the returned results by setting the value of the return-type option.

log-level

Set a new log level:

> (rcrly:get-account 1 '(#(log-level debug)))

At any point you may change the log level for the client.

endpoint

Normal “endpoint” call:

> (rcrly:get "/some/recurly/endpoint")

Set the endpoint option to false to make a direct, “URL” call:

> (set options '(#(endpoint false)))
> (rcrly:get "https://some.domain/path/to/resource" options)

If you wish to make a request to a full URL, you will need to pass the option #(endpoint false) to override the default behaviour of the rcrly library creating the URL for you, based upon the provided endpoint.

batch-size

TBD

TBD

Options for lhttpc

If you wish to pass general HTTP client options to lhttpc, then you will need to use rcrly-httpc:request/7, which takes the arguments endpoint, method, headers, body, timeout, options, and lhttpc-options.

The options parameter is for the rcrly options discussed above, and lhttpc-options are the regular lhttpc options, the most significant of which are:

Creating Payloads

To demonstrate creating XML, slurp the following file:

> (slurp "src/rcrly-xml.lfe")
#(ok rcrly-xml)

Now you can use the rcrly macros to create XML in LFE syntax:

> (xml/account (xml/company_name "Bob's Red Mill"))
"<account><company_name>Bob's Red Mill</company_name></account>"

This also works for modules that will be genereating XML payloads: simply include-lib them like they are in rcrly-xml:

(include-lib "rcrly/include/xml.lfe")

Here’s a sample payload from the Recurly docs (note that multiple children need to be wrapped in a list):

> (xml/billing_info
    (list (xml/first_name "Verena")
          (xml/last_name "Example")
          (xml/number "4111-1111-1111-1111")
          (xml/verification_value "123")
          (xml/month "11")
          (xml/year "2015")))

Which produces the following result:

"<billing_info>
  <first_name>Verena</first_name>
  <last_name>Example</last_name>
  <number>4111-1111-1111-1111</number>
  <verification_value>123</verification_value>
  <month>11</month>
  <year>2015</year>
</billing_info>"

Payloads for PUT and POST data in the Recurly REST API are XML documents. As such, we need to be able to create XML for such things as update actions. To facilitate this, The LFE rcrly library provides XML-generating macros. in the REPL, you can slurp the rcrly-xml module, and then have access to them. For instance:

Working with Results

Example form for multi-valued results:

#(ok
  #(accounts
    (...) ; attributes
    (#(account
        (...) ; attributes
        (...) ; child elements
        )
     #(account ...)
     #(account ...)
     ...)))

Example form for single-valued results:

#(ok
  #(account
    (...) ; attributes
    (...) ; child elements
    ))

All results in rcrly are of the form #(ok ...) or #(error ...), with the elided contents of those tuples changing depending upon context. This is the standard approach for Erlang libraries, so should be quite familiar to users.

Recurly’s API is XML-based; the rcrly API inherits some of its characteristics from this fact. In particular, data structures representing the parsed XML data are regularly returned by rcrly calls. Parsed rcrly results have the following:

As such, many results are often 3-tuples. rcrly includes functions (see below) for working with this 3-tuple data.

The rcrly LFE library distinguishes between two different result types:

Multi-valued results are simply results that have items in a lis. Many rcrly API calls will return a list of items, for example, get-all-invoices/0, get-plans/0, or get-accounts/0. The rcrly library provides map and foldl functions for easily working with these results.

By single-value results, we mean API calls which do not return a list of values, but intstead return a single-item data structure. Examples of API calls which do this are get-account/1, get-billing-info/1, get-plan/1, etc. The rcrly library provides functions like get-in and get-linked for easily working with these results.

Format

A standard Recurly XML result:

<account href="https://yourname.recurly.com/v2/accounts/1">
  <adjustments href="https://yourname.recurly.com/v2/accounts/1/adjustments"/>
  <billing_info href="https://yourname.recurly.com/v2/accounts/1/billing_info"/>
  <invoices href="https://yourname.recurly.com/v2/accounts/1/invoices"/>
  <redemption href="https://yourname.recurly.com/v2/accounts/1/redemption"/>
  <subscriptions href="https://yourname.recurly.com/v2/accounts/1/subscriptions"/>
  <transactions href="https://yourname.recurly.com/v2/accounts/1/transactions"/>
  <account_code>1</account_code>
  <state>active</state>
  <username nil="nil"></username>
  <email>verena@example.com</email>
  <first_name>Verena</first_name>
  <last_name>Example</last_name>
  <company_name></company_name>
  <vat_number nil="nil"></vat_number>
  <tax_exempt type="boolean">false</tax_exempt>
  <address>
    <address1>108 Main St.</address1>
    <address2>Apt #3</address2>
    <city>Fairville</city>
    <state>WI</state>
    <zip>12345</zip>
    <country>US</country>
    <phone nil="nil"></phone>
  </address>
  <accept_language nil="nil"></accept_language>
  <hosted_login_token>a92468579e9c4231a6c0031c4716c01d</hosted_login_token>
  <created_at type="datetime">2011-10-25T12:00:00</created_at>
</account>

Here is that same result as parsed by the LFE rcrly library:

#(account
  (#(href "https://yourname.recurly.com/v2/accounts/1"))
  (#(adjustments
     (#(href "https://yourname.recurly.com/v2/accounts/1/adjustments"))
     ())
   #(invoices
     (#(href "https://yourname.recurly.com/v2/accounts/1/invoices"))
     ())
   #(subscriptions
     (#(href "https://yourname.recurly.com/v2/accounts/1/subscriptions"))
     ())
   #(transactions
     (#(href "https://yourname.recurly.com/v2/accounts/1/transactions"))
     ())
   #(account_code () ("1"))
   #(state () ("active"))
   #(username () ())
   #(email () ("verena@example.com"))
   #(first_name () ("Verena"))
   #(last_name () ("Example"))
   #(company_name () ())
   #(vat_number (#(nil "nil")) ())
   #(tax_exempt (#(type "boolean")) ("false"))
   #(address ()
     (#(address1 () ("108 Main St."))
      #(address2 () ("Apt #3"))
      #(city () ("Fairville"))
      #(state () ("WI"))
      #(zip () ("12345"))
      #(country () ("US"))
      #(phone (#(nil "nil")) ())))
   #(accept_language (#(nil "nil")) ())
   #(hosted_login_token () ("a92468579e9c4231a6c0031c4716c01d"))
   #(created_at (#(type "datetime")) ("2011-10-25T12:00:00"))))

As noted above, the format of the results depend upon what value you have passed as the return-type; by default, the data type is passed and this simply returns the data requested by the particular API call (not the headers, HTTP status, body, XML conversion info, etc. – if you want that, you’ll need to pass the full value associated with the return-type).

The API calls return XML that has been parsed and converted to LFE data structures by the erlsom library.

The rcrly library offers a couple of convenience functions for extracting data from this sort of structure – see the next two sections for more information about data extraction.

get-data

First, get some data using the full return type:

> (set `#(ok ,results) (rcrly:get-accounts `(#(return-type full))))

Results:

#(ok
  (#(response ok)
   #(status #(200 "OK"))
   #(headers (...))
   #(body
     (#(tag "accounts")
      #(attr (#(type "array")))
      #(content
        #(accounts ...))))))

Example get-data call:

> (rcrly:get-data results)
#(accounts
  (#(type "array"))
  (#(account ...)
   #(account ...)))

The get-data utility function is provided in the rcrly module and is useful for extracing response data returned from client requests made with the full option. It assumes a nested property list structure with the content key in the body’s property list.

Though this is useful when dealing with response data from full the return type, you may find that it is more convenient to use the default data return type with the rcrly:get-in function instead, as it allows you to extract just the data you need. See below for an example.

get-in

Get some data:

> (set `#(ok ,account) (rcrly:get-account 1))

Results:

#(ok
  #(account
    (#(href ...))
    (#(adjustments ...)
    ...
    #(address ()
     (...
      #(city () ("Fairville"))
      ...))
    ...)))

Example get-in call:

> (rcrly:get-in '(account address city) account)
"Fairville"

Note that the city field is nested in the address field. The address data is nested in the account.

The utillity function rcrly:get-in is inspired by the Clojure get-in function, but in this case, tailored to work with the rcrly results which have been converted from XML to LFE/Erlang data structures. With a single call, you are able to retrieve data which is nested at any depth, providing just the keys needed to locate it.

get-linked

Using the same account data as tthe last example, here’s how you get the linked transactions:

> (rcrly:get-linked '(account transactions) account)
#(ok
  #(transactions
    (#(type "array"))
    (#(transaction ...)
     #(transaction ...)
     #(transaction ...)
     ...)))

In the Recurly REST API, data relationships are encoded in media links, per common best REST practices. Linked data may be retreived easily using the get-linked/2 utility function (analog to the get-in/2 function). get-linked/2 performs most of the same work that get-in/2 does, with the exception being that the last item in the list of keys points to linked data. As such, get-linked/2 will then perform an HTTP GET for the linked media.

map and foldl

Example map/2 call that lists all the plan names in the system:

> (rcrly:map
    (lambda (x)
      (rcrly:get-in '(plan name) x))
    (rcrly:get-plans))

Results:

("Silver Plan" "Gold plan" "30-Day Free Trial")

Example foldl/3 call that gets the total of all invoices (ignoring currency type), starting with an “add” function:

> (defun add-invoice (invoice subtotal)
    (+ subtotal
      (/ (list_to_integer
           (rcrly:get-in '(invoice total_in_cents) invoice))
         100)))
add-invoice

Now to use that function with rcrly:foldl/3:

> (rcrly:foldl
    #'add-invoice/2
    0
    (rcrly:get-all-invoices))

Result:

120.03

Recurly’s API is XML-based, so parsed results have the following: * a tag * attributes * contents (which may itself contain nested tag/attrs/contents)

The map/2 and foldl/3 functions provided by rcrly aim to make working with these results easier, especially for iterating through multi-valued results.

It is important to note: map/2 and foldl/3 both take a complete result – this inlcudes the #(ok ...).

Composing Results

To use the ->> macro we’ll need to slurp a file that has included it:

> (slurp "src/rcrly.lfe")
#(ok rcrly)
>

We’re going to need some helper functions:

> (defun get-xacts (acct)
    (rcrly:get-linked '(account transactions) acct))
get-xacts
> (defun check-xacts (xacts)
    (rcrly:map #'check-xact/1 xacts))
check-xacts
> (defun check-xact (xact)
    (if (=/= (rcrly:get-in '(transaction recurring) xact) "true")
        (if (=:= (rcrly:get-in '(transaction status) xact) "success")
            (rcrly:get-in '(transaction uuid) xact))))
check-xact
> (defun id?
    ((id) (when (is_list id))
     'true)
    ((x) x))
id?
>

Now we can perform our defined task:

> (->> (rcrly:get-accounts)        ; this returns a multi-valued result
       (rcrly:map #'get-xacts/1)   ; this returns a list of multi-valued results
       (lists:map #'check-xacts/1) ; this returns a list of lists
       (lists:foldl #'++/2 '())    ; this flattns the list, preserving strings
       (lists:filter #'id?/1))     ; just returns results that are ids

Results:

("2d9d1054c2716a3d38260146d28ebc7c"
 "2dc20791440f9313a877414fe1a6f7a4"
 "2dc2076ab55c2054cfaf3b427589437a"
 "2dbc6c2d09c5aed53a9ede41138f63df"
 "2dbc6c17524ca5cda869684a6bb7aae3")

Of the 12 transactions in the accounts this was tested against, those five satisfied the criteria of being non-recurring and in a successful state.

This section might be more accurately called “processing results through function composition” but that was a bit long. We hope you’ll forgive the poetic license we took!

With that said, here’s an example of a potential “data flow” using function composition to get the following:

Batched Results and Paging

TBD

Relationships and Linked Data

In the Recurly REST API, data relationships are encoded in media links, per common best REST practices. Linked data may be retreived easily using the get-linked/2 utility function (analog to the get-in/2 function).

For more information, see the get-linked section above.

Handling Errors

The Recurly API will return errors under various circumstances. For instance, an error is returned when attempting to look up billing information with a non-existent account:

> (set `#(error ,error) (rcrly:get-billing-info 'noaccountid))

Resulting error:

#(error
  #(error ()
    (#(symbol () ("not_found"))
     #(description
       (#(lang "en-US"))
       ("Couldn't find Account with account_code = noaccountid")))))

Note that we pattern-matched against #(error ...). Also, you may use the get-in function to extract error information:

> (rcrly:get-in '(error description) error)
"Couldn't find Account with account_code = noaccountid"

Any HTTP request that generates an HTTP status code equal to or greater than 400 will be converted to an error. For example, requesting account information with an id that no account has will generate a 404 - Not Found which will be converted by rcrly to an application error:

> (set `#(error ,error) (rcrly:get-account 'noaccountid))

Resulting error:

#(error
  #(error ()
    (#(symbol () ("not_found"))
     #(description
       (#(lang "en-US"))
       ("Couldn't find Account with account_code = noaccountid")))))

Extracting the message:

> (rcrly:get-in '(error description) error)
"Couldn't find Account with account_code = noaccountid"

rcrly Errors: TBD

lhttpc Errors: TBD

As mentioned in the “Working with Results” section, all parsed responses from Recurly are a tuple of either #(ok ...) or #(error ...). All processing of rcrly results should pattern match against these typles, handling the error cases as appropriate for the application using the rcrly library.

There are four types of errors that rcrly aims to address:

Logging

rcrly uses the LFE logjam library for logging. The log level may be configured in two places:

The default log level is emergency, so you should never notice it’s there (unless, of course, you have lots ot logging defined for the emergency level …). The intended use for rcrly logging is on a per-request basis for debugging purposes (though, of course, this may be easily overridden in your application code by setting the log level you desire in the lfe.config file).

Note that when passing the log-level option in an API call, it sets the log level for the logging service which is running in the background. As such, the log-level option does not need to be passed again until you wish to change it. In other words, when passed as an option, it is set for all future API calls.

For more details on logging per-request, see the “Options” section above.

The API

Each API call has a default arity and then an arity+1 where the “+1” is an argument for rcrly client options (see the “Options” section above).

For each of the API functions listed below, be sure to examine the linked Recurly documentation for information about payloads.

Accounts

Recurly Accounts documentation

get-accounts

Get all accounts:

> (set `#(ok ,accounts) (rcrly:get-accounts))

Results:

#(ok
  (#(account ...)
   #(account ...)))
> (length accounts)
2

Pages on all accounts in the system.

get-account

Takes a single arguement and returns data for the account associated with the provided id:

> (set `#(ok ,account) (rcrly:get-account 1))

Results:

#(ok
  #(account
    (#(adjustments ...)
     ...)))
> (rcrly:get-in '(account state) account)
"active"
> (rcrly:get-in '(account address city) account)
"Fairville"

Get a particular account by account ID.

create-account

To use from the REPL, first, pull in the XML macros:

> (slurp "src/rcrly-xml.lfe")
#(ok rcrly-xml)

Now create your payload:

> (set payload
    (xml/account
      (list
        (xml/account_code "123")
        (xml/email "alice@example.com")
        (xml/first_name "Alice")
        (xml/last_name "Guthrie"))))

Which will give you:

"<account>...</account>"

Now make the API call to create the account:

> (set `#(ok ,account) (rcrly:create-account payload))
#(ok
  #(account ...))

With the account created, we can extract data from the results:

> (rcrly:get-in '(account email) account)
"alice@example.com"

Create an account based upon provided payload data.

update-account

Create your payload:

> (set payload
    (xml/account
        (xml/company_name "Alice's Hacker Cafe")))
"<account>...</account>"

Now make the API call to update the account:

> (set `#(ok ,account) (rcrly:update-account 123 payload))
#(ok
  #(account ...))

With the account updated, we can extract data from the results:

> (rcrly:get-in '(account email) account)
"alice@example.com"
> (rcrly:get-in '(account company_name) account)
"Alice's Hacker Cafe"

This function takes account id and payload data.

close-account

Close an account:

> (set `#(ok "") (rcrly:close-account 123))
#(ok ())

Takes an account id.

reopen-account

Re-open an account that had been closed:

> (set `#(ok ,account) (rcrly:reopen-account 123))
#(ok
  #(account ...))

Takes an account id.

Adjustments

Recurly Adjustments documentation

get-adjustments

Get all adjustments:

> (set `#(ok ,adjustments) (rcrly:get-adjustments 1))

Results:

#(ok
  #(adjustments
    (#(type "array"))
    (#(adjustment ...)
     #(adjustment ...)
     ...)))

Takes an account id.

get-adjustment

Get a particular adjustment:

> (set `#(ok ,adjustment) (rcrly:get-adjustment "2d97cfa52e80a675a532ba4e8ea25401"))

Results:

#(ok
  #(adjustment
    (#(type "credit")
     #(href
       "https://yourname.recurly.com/v2/adjustments/2d97cf12a5...."))
    (#(account (#(href "https://yourname.recurly.com/v2/accounts/1")) ())
     #(uuid () ("2d97cfa52e80a675a532ba4e8ea25401"))
     #(state () ("pending"))
     ...
     #(origin () ("credit"))
     ...
     #(total_in_cents (#(type "integer")) ("-100"))
     #(currency () ("USD"))
     #(taxable (#(type "boolean")) ("false"))
     #(start_date (#(type "datetime")) ("2015-03-17T18:34:56Z"))
     #(end_date (#(nil "nil")) ())
     #(created_at (#(type "datetime")) ("2015-03-17T18:34:56Z")))))

Now you can do the usual things:

> (rcrly:get-in '(adjustment total_in_cents) adjustment)
"-100"
> (rcrly:get-in '(adjustment state) adjustment)
"pending"
> (rcrly:get-in '(adjustment origin) adjustment)
"credit"

Takes a UUID.

Billing Info

Recurly Billing Info documentation

get-billing-info

Get billing info for a particular account:

> (set `#(ok ,info) (rcrly:get-billing-info 1))

Results:

#(ok
  #(billing_info
    (#(type "credit_card")
     #(href "https://yourname.recurly.com/v2/accounts/1/billing_info"))
    (#(account (#(href "https://yourname.recurly.com/v2/accounts/1")) ())
     ...
     #(company (#(nil "nil")) ())
     #(address1 () ("108 Main St"))
     ...
     #(city () ("Fairville"))
     #(state () ("WI"))
     #(zip () ("12345"))
     ...
     #(card_type () ("Visa"))
     #(year (#(type "integer")) ("2016"))
     #(month (#(type "integer")) ("3"))
     ...)))

Extract some data:

> (rcrly:get-in '(billing_info card_type) info)
"Visa"

Takes an account id.

update-billing-info

To update billing info from the REPL, first pull in the XML macros:

> (slurp "src/rcrly-xml.lfe")
#(ok rcrly-xml)

Now set some argument values (helps to keep things more readable):

> (set account-id 1)
1
> (set payload
    (xml/billing_info
        (list (xml/first_name "Verena")
              (xml/last_name "Example"))))
"<billing_info> ... </billing_info>"

With those in place, you can made your API call to update the billing info:

> (set `#(ok ,info) (rcrly:update-billing-info account-id payload))

Results:

#(ok
  #(billing_info
    (#(type "credit_card")
     #(href "https://yourname.recurly.com/v2/accounts/1/billing_info"))
    (#(account (#(href "https://yourname.recurly.com/v2/accounts/1")) ())
     ...
     #(company (#(nil "nil")) ())
     #(address1 () ("108 Main St"))
     ...
     #(city () ("Fairville"))
     #(state () ("WI"))
     #(zip () ("12345"))
     ...
     #(card_type () ("Visa"))
     #(year (#(type "integer")) ("2016"))
     #(month (#(type "integer")) ("3"))
     ...)))

We can easily confirm that our results have the updated data:

> (rcrly:get-in '(billing_info first_name) info)
"Verena"

Takes payload data.

clear-billing-info

TBD

Invoices

Recurly Invoices documentation

get-all-invoices

Get all the invoices for all accounts:

> (set `#(ok ,invoices) (rcrly:get-all-invoices))
#(ok
  #(invoices ...))

Takes no arguments.

get-invoices

Get all the invoices for one account:

> (set `#(ok ,invoices) (rcrly:get-invoices 123))

Results:

#(ok
  #(invoices ...))

Takes an account id.

get-invoice

Get a particular invoice:

> (set `#(ok ,invoice) (rcrly:get-invoice 1402))

Result has the follwoing form:

#(ok
  #(invoice ...))

Takes an invoice number:

get-invoice-pdf

TBD

preview-invoice

TBD

invoice

TBD

set-paid-invoice

TBD

set-failed-invoice

TBD

set-line-refund-invoice

TBD

set-open-refund-invoice

TBD

Plans

Recurly Invoices documentation

get-plans

Get all plans:

> (set `#(ok ,plans) (rcrly:get-plans))

Results:

#(ok
  #(plans
    (#(type "array"))
    (#(plan ...))))

Takes no arguments.

get-plan

Get a specific plan:

> (set `#(ok ,plan) (rcrly:get-plan 1402))
#(ok
  #(plan ...))

Extract the plan name:

> (rcrly:get-in '(plan name) plan)
"30-Day Free Trial"

Takes a plan code.

create-plan

Create your payload:

> (set payload
    (xml/plan
      (list
        (xml/plan_code "gold")
        (xml/name "Gold plan")
        (xml/setup_fee_in_cents
          (list
            (xml/USD "1000")
            (xml/EUR "800")))
        (xml/unit_amount_in_cents
          (list
            (xml/USD "6000")
            (xml/EUR "4500")))
        (xml/plan_interval_length "1")
        (xml/plan_interval_unit "months")
        (xml/tax_exempt "false"))))
"<plan>...</plan>"

Now make the API call to create the plan:

> (set `#(ok ,plan) (rcrly:create-plan payload))
#(ok
  #(plan ...))

With the plan created, we can extract data from the results:

> (rcrly:get-in '(plan setup_fee_in_cents EUR) plan)
"4500"

Takes payload data.

update-plan

TBD

delete-plan

To delete a plan, simply pass the plan code to delete-plan:

> (set `#(ok ,results) (rcrly:delete-plan "gold"))
[response TBD]

Subscriptions

Recurly InvoicesSubscriptions documentation

get-all-subscriptions

Get all subscriptions:

> (set `#(ok ,subs) (rcrly:get-all-subscriptions '()))
#(ok
  #(subscriptions
    (#(type "array"))
    (#(subscription ...))))

Takes no arguments.

get-subscriptions

Get all subscriptions for an account:

> (set `#(ok ,subs) (rcrly:get-subscriptions 123))
#(ok
  #(subscriptions
    (#(type "array"))
    (#(subscription ...))))

Takes an account id:

get-subscription

Get a particular subscription:

> (set uuid "2dbc6c2cb823174353853a409c90d419")
"2dbc6c2cb823174353853a409c90d419"
> (set `#(ok ,subs) (rcrly:get-subscription uuid))
#(ok
  #(subscription ...))

Extract data as needed:

> (rcrly:get-in '(subscription current_period_started_at) subs)
"2015-03-24T21:12:14Z"

Takes a subscription UUID:

create-subscription

To create a subscription, first create your payload:

> (set payload
    (xml/subscription
      (list
        (xml/plan_code "gold")
        (xml/currency "USD")
        (xml/account
          (xml/account_code "123")))))
"<subscription>...</subscription>"

Now make the API call to create the plan:

> (set `#(ok ,subs) (rcrly:create-subscription payload))
#(ok
  #(subscription ...))

With the plan created, we can extract data from the results:

> (rcrly:get-in '(subscription plan name) subs)
"Gold plan"

Takes payload data.

preview-subscription

TBD

update-subscription

To update a subscription, create your payload:

> (set uuid "2dbc6c2cb823174353853a409c90d419")
"2dbc6c2cb823174353853a409c90d419"
> (set payload
    (xml/subscription
      (list
        (xml/timeframe "now")
        (xml/plan_code "silver"))))
"<subscription>...</subscription>"

Now make the API call to create the plan:

> (set `#(ok ,subs) (rcrly:update-subscription uuid payload))
#(ok
  #(subscription ...))

With the plan created, we can extract data from the results:

> (rcrly:get-in '(subscription plan name) subs)
"Silver plan"

Takes subscription UUID and payload data.

update-subscription-notes

TBD

cancel-subscription

To cancel a subscription:

> (set uuid "2dbc6c2cb823174353853a409c90d419")
"2dbc6c2cb823174353853a409c90d419"
> (set #(ok ,subs) (rcrly:cancel-subscription uuid))
#(ok
  #(subscription ...))

And you can check the subscription state:

> (rcrly:get-in '(subscription state) subs)
"canceled"

Takes a subscription UUID.

reactivate-subscription

To reactivate a subscription:

> (set uuid "2dbc6c2cb823174353853a409c90d419")
"2dbc6c2cb823174353853a409c90d419"
> (set #(ok ,subs) (rcrly:reactivate-subscription uuid))
#(ok
  #(subscription ...))

Check the subscription state:

> (rcrly:get-in '(subscription state) subs)
"active"

Takes a subscription UUID

terminate-subscription

To terminate a subscription:

> (set uuid "2dbc6c2cb823174353853a409c90d419")
"2dbc6c2cb823174353853a409c90d419"
> (set #(ok ,subs) (rcrly:terminate-subscription uuid))
#(ok
  #(subscription ...))

Check the subscription state:

> (rcrly:get-in '(subscription statue) subs)
"expired"

Takes a subscription UUID

postpone-subscription

Transactions

Recurly Transactions documentation

get-all-transactions

TBD

get-transactions

TBD