recurring
Version:
a recurly v2 api client for node.js
246 lines (211 loc) • 6.22 kB
JavaScript
'use strict'
const RecurlyData = require('../recurly-data')
const handleRecurlyError = require('../util').handleRecurlyError
const querystring = require('querystring')
const data2xml = require('data2xml')({
undefined: 'empty',
null: 'closed'
})
class Subscription extends RecurlyData {
constructor(recurring) {
super({
recurring,
properties: [
'account',
'activated_at',
'bank_account_authorized_at',
'canceled_at',
'collection_method',
'converted_at',
'currency',
'current_period_ends_at',
'current_period_started_at',
'customer_notes',
'expires_at',
'href',
'net_terms',
'no_billing_info_reason',
'plan',
'quantity',
'revenue_schedule_type',
'shipping_address',
'shipping_address_id',
'state',
'started_with_gift',
'subscription_add_ons',
'tax',
'tax_in_cents',
'tax_type',
'tax_region',
'tax_rate',
'terms_and_conditions',
'trial_ends_at',
'trial_started_at',
'unit_amount_in_cents',
'updated_at',
'uuid',
'pending_subscription',
'po_number'
],
idField: 'uuid',
plural: 'subscriptions',
singular: 'subscription',
enumerable: true
})
this.__defineGetter__('account_id', () => {
if (this._account_id) {
return this._account_id
}
if (!this._resources.account) {
return undefined
}
// The account property points to a hash with an href that can be used to fetch
// the account, but sometimes I want the id.
this._account_id = this._resources.account.match(/\/([^\/]*)$/)[1]
return this._account_id
})
}
static get SINGULAR() {
return 'subscription'
}
static get PLURAL() {
return 'subscriptions'
}
static get ENDPOINT() {
return `${RecurlyData.ENDPOINT}${Subscription.PLURAL}`
}
static get validRefundTypes() {
return [ 'partial', 'full', 'none' ]
}
create(options, callback) {
if (!options.plan_code) {
throw (new Error('subscription must include "plan_code" parameter'))
}
if (!options.account) {
throw (new Error('subscription must include "account" information'))
}
if (!options.account.account_code) {
throw (new Error('subscription account info must include "account_code"'))
}
if (!options.currency) {
throw (new Error('subscription must include "currency" parameter'))
}
const body = data2xml(Subscription.SINGULAR, options)
this.post(Subscription.ENDPOINT, body, (err, response, payload) => {
const error = handleRecurlyError(err, response, payload, [ 200, 201 ])
if (error) {
return callback(error)
}
this.inflate(payload)
callback(null, this)
})
}
update(options, callback) {
if (!options.timeframe) {
throw (new Error('subscription update must include "timeframe" parameter'))
}
if (!this.href) {
throw (new Error(`cannot update a subscription without an href ${this.id}`))
}
const body = data2xml(Subscription.SINGULAR, options)
this.put(this.href, body, (err, response, payload) => {
const error = handleRecurlyError(err, response, payload, [ 200, 201 ])
if (error) {
return callback(error)
}
this.inflate(payload)
callback(null, this)
})
}
cancel(callback) {
if (!this.id) {
throw (new Error('cannot cancel a subscription without a uuid'))
}
let href
if (this.a && this.a.cancel) {
href = this.a.cancel.href
}
else {
href = `${Subscription.ENDPOINT}/${this.id}/cancel`
}
this.put(href, '', (err, response, payload) => {
const error = handleRecurlyError(err, response, payload, [ 200 ])
if (error) {
return callback(error)
}
this.inflate(payload)
callback(null, this)
})
}
reactivate(callback) {
if (!this.id) {
throw (new Error('cannot reactivate a subscription without a uuid'))
}
let href
if (this.a && this.a.reactivate) {
href = this.a.reactivate.href
}
else {
href = `${Subscription.ENDPOINT}/${this.id}/reactivate`
}
this.put(href, '', (err, response, payload) => {
const error = handleRecurlyError(err, response, payload, [ 200 ])
if (error) {
return callback(error)
}
this.inflate(payload)
callback(null, this)
})
}
postpone(nextRenewal, callback) {
if (!this.id) {
throw (new Error('cannot postpone a subscription without a uuid'))
}
if (!nextRenewal || (typeof nextRenewal !== 'object')) {
throw (new Error(`${nextRenewal} must be a valid renewal date`))
}
let href
if (this.a && this.a.postpone) {
href = this.a.postpone.href
}
else {
href = `${Subscription.ENDPOINT}/${this.id}/postpone`
}
const query = { next_renewal_date: nextRenewal.toISOString() }
href += `?${querystring.stringify(query)}`
this.put(href, '', (err, response, payload) => {
const error = handleRecurlyError(err, response, payload, [ 200 ])
if (error) {
return callback(error)
}
this.inflate(payload)
callback(null, this)
})
}
terminate(refundType, callback) {
if (!this.id) {
throw (new Error('cannot terminate a subscription without a uuid'))
}
if (Subscription.validRefundTypes.indexOf(refundType) === -1) {
throw (new Error(`refund type ${refundType} not valid`))
}
let href
if (this.a && this.a.terminate) {
href = this.a.terminate.href
}
else {
href = `${Subscription.ENDPOINT}/${this.id}/terminate`
}
const query = { refund: refundType }
href += `?${querystring.stringify(query)}`
this.put(href, '', (err, response, payload) => {
const error = handleRecurlyError(err, response, payload, [ 200, 201 ])
if (error) {
return callback(error)
}
this.inflate(payload)
callback(null, this)
})
}
}
module.exports = Subscription