676 lines
24 KiB
Ruby
676 lines
24 KiB
Ruby
require 'recurly'
|
|
module JamRuby
|
|
class RecurlyClient
|
|
def initialize()
|
|
@log = Logging.logger[self]
|
|
end
|
|
|
|
def create_account(current_user, billing_info)
|
|
options = account_hash(current_user, billing_info)
|
|
account = nil
|
|
begin
|
|
#puts "Recurly.api_key: #{Recurly.api_key}"
|
|
account = Recurly::Account.create(options)
|
|
if account.errors.any?
|
|
puts "Errors encountered while creating account: #{account.errors}"
|
|
raise RecurlyClientError.new(account.errors) if account.errors.any?
|
|
end
|
|
rescue Recurly::Error, NoMethodError => x
|
|
raise RecurlyClientError, x.to_s
|
|
else
|
|
if account
|
|
current_user.update_attribute(:recurly_code, account.account_code)
|
|
end
|
|
end
|
|
account
|
|
end
|
|
|
|
def has_account?(current_user)
|
|
account = get_account(current_user)
|
|
!!account
|
|
end
|
|
|
|
def delete_account(current_user)
|
|
account = get_account(current_user)
|
|
if account
|
|
begin
|
|
account.destroy
|
|
rescue Recurly::Error, NoMethodError => x
|
|
raise RecurlyClientError, x.to_s
|
|
end
|
|
else
|
|
raise RecurlyClientError, "Could not find account to delete."
|
|
end
|
|
account
|
|
end
|
|
|
|
|
|
def update_desired_subscription(current_user, plan_code)
|
|
subscription = nil
|
|
account = nil
|
|
current_user.desired_plan_code = plan_code
|
|
current_user.desired_plan_code_set_at = DateTime.now
|
|
current_user.save(validate: false)
|
|
|
|
puts "updating desired subscription for #{current_user.email} to #{plan_code}"
|
|
|
|
account = get_account(current_user)
|
|
|
|
if account
|
|
if plan_code.nil? || plan_code == ''
|
|
begin
|
|
# user wants a free subscription. If they have a subscription, let's cancel it.
|
|
subscription, account = find_subscription(current_user, account)
|
|
if subscription
|
|
puts "Canceling user's #{current_user.email} subscription"
|
|
subscription.cancel
|
|
# do not delete the recurly_subscription_id ; we'll use that to try and reactivate later if they user re-activates their account
|
|
else
|
|
# if no subscription and past trial, you goin down -- because there must have never been payment??
|
|
if current_user.subscription_trial_ended?
|
|
current_user.subscription_plan_code = nil
|
|
current_user.subscription_plan_code_set_at = DateTime.now
|
|
current_user.save(validate: false)
|
|
end
|
|
end
|
|
|
|
# do not set the subscription _plan_code either; because the user has paid through the month; they still
|
|
# get their old plan
|
|
#current_user.subscription_plan_code = nil
|
|
#current_user.save(validate: false)
|
|
rescue => e
|
|
puts "Could not cancel subscription for user #{current_user.email}. #{e}"
|
|
return false, subscription, account
|
|
end
|
|
else
|
|
# user wants to pay. let's get it goin
|
|
return handle_create_subscription(current_user, plan_code, account)
|
|
end
|
|
end
|
|
|
|
return true, subscription, account
|
|
|
|
end
|
|
|
|
|
|
def get_account(current_user)
|
|
begin
|
|
account = current_user && current_user.recurly_code ? Recurly::Account.find(current_user.recurly_code) : nil
|
|
rescue Recurly::Error => x
|
|
puts "Swallow find acct for user #{current_user.email} error initial #{x}"
|
|
end
|
|
|
|
# check again, assuming account_code is the user ID (can happen in error scenarios where we create the account
|
|
# on recurly, but couldn't save the account_code to the user.recurly_code field)
|
|
|
|
puts "get_account for #{current_user.email} found #{account}"
|
|
if !account
|
|
begin
|
|
account = Recurly::Account.find(current_user.id)
|
|
rescue Recurly::Error => x
|
|
puts "Swallow find acct for user #{current_user.email} error #{x}"
|
|
end
|
|
|
|
# repair user local account info
|
|
if !account.nil?
|
|
current_user.update_attribute(:recurly_code, account.account_code)
|
|
end
|
|
end
|
|
|
|
account
|
|
|
|
rescue Recurly::Error => x
|
|
raise RecurlyClientError, x.to_s
|
|
end
|
|
|
|
|
|
def update_account(current_user, billing_info=nil)
|
|
account = get_account(current_user)
|
|
if(account.present?)
|
|
options = account_hash(current_user, billing_info)
|
|
begin
|
|
account.update_attributes(options)
|
|
rescue Recurly::Error, NoMethodError => x
|
|
raise RecurlyClientError, x.to_s
|
|
end
|
|
end
|
|
account
|
|
end
|
|
|
|
def list_invoices(account)
|
|
invoices = []
|
|
count = 0
|
|
account.invoices.find_each do |invoice|
|
|
count = count + 1
|
|
invoices << invoice
|
|
if count == 50
|
|
break
|
|
end
|
|
end
|
|
invoices
|
|
end
|
|
|
|
|
|
def payment_history(current_user, params ={})
|
|
|
|
limit = params[:limit]
|
|
limit ||= 20
|
|
limit = limit.to_i
|
|
|
|
cursor = params[:cursor]
|
|
|
|
payments = []
|
|
account = get_account(current_user)
|
|
if(account.present?)
|
|
begin
|
|
|
|
account.transactions.paginate(per_page:limit, cursor:cursor).each do |transaction|
|
|
# XXX this isn't correct because we create 0 dollar transactions too (for free stuff)
|
|
#if transaction.amount_in_cents > 0 # Account creation adds a transaction record
|
|
payments << {
|
|
:created_at => transaction.created_at,
|
|
:amount_in_cents => transaction.amount_in_cents,
|
|
:tax_in_cents=> transaction.tax_in_cents,
|
|
:status => transaction.status,
|
|
:action => transaction.action,
|
|
:payment_method => transaction.payment_method,
|
|
:reference => transaction.reference,
|
|
:currency => transaction.currency
|
|
}
|
|
#end
|
|
end
|
|
rescue Recurly::Error, NoMethodError => x
|
|
puts "Recurly error #{current_user.email} #{x}"
|
|
raise RecurlyClientError, x.to_s
|
|
end
|
|
end
|
|
payments
|
|
end
|
|
|
|
|
|
def invoice_history(current_user, params ={})
|
|
|
|
limit = params[:limit]
|
|
limit ||= 20
|
|
limit = limit.to_i
|
|
|
|
cursor = params[:cursor]
|
|
|
|
payments = []
|
|
account = get_account(current_user)
|
|
if(account.present?)
|
|
begin
|
|
|
|
account.invoices.paginate(per_page:limit, cursor:cursor).each do |invoice|
|
|
# XXX this isn't correct because we create 0 dollar transactions too (for free stuff)
|
|
#if transaction.amount_in_cents > 0 # Account creation adds a transaction record
|
|
payments << {
|
|
:created_at => invoice.created_at,
|
|
:subtotal_in_cents => invoice.subtotal_in_cents,
|
|
:tax_in_cents=> invoice.tax_in_cents,
|
|
:total_in_cents => invoice.total_in_cents,
|
|
:state => invoice.state,
|
|
:description => invoice.line_items.map(&:description).join(", "),
|
|
:currency => invoice.currency
|
|
}
|
|
#end
|
|
end
|
|
rescue Recurly::Error, NoMethodError => x
|
|
puts "Recurly error #{current_user.email} #{x}"
|
|
raise RecurlyClientError, x.to_s
|
|
end
|
|
end
|
|
return payments, account
|
|
end
|
|
|
|
def update_billing_info(current_user, billing_info=nil, account = nil)
|
|
account = get_account(current_user) if account.nil?
|
|
if account.present?
|
|
begin
|
|
account.billing_info = billing_info
|
|
account.billing_info.save
|
|
rescue Recurly::Error, NoMethodError => x
|
|
raise RecurlyClientError, x.to_s
|
|
end
|
|
|
|
raise RecurlyClientError.new(account.errors) if account.errors.any?
|
|
else
|
|
raise RecurlyClientError, "Could not find account to update billing info."
|
|
end
|
|
account
|
|
end
|
|
|
|
# token was created in the web ui. we can tell recurly to update the billing info on the account with just the token
|
|
def update_billing_info_from_token(current_user, account, recurly_token)
|
|
account.billing_info = {
|
|
token_id: recurly_token
|
|
}
|
|
account.billing_info.save!
|
|
end
|
|
|
|
def refund_user_subscription(current_user, jam_track)
|
|
jam_track_right=JamRuby::JamTrackRight.where("user_id=? AND jam_track_id=?", current_user.id, jam_track.id).first
|
|
if jam_track_right
|
|
refund_subscription(jam_track_right)
|
|
else
|
|
raise RecurlyClientError, "The user #{current_user} does not have a subscription to #{jam_track}"
|
|
end
|
|
end
|
|
|
|
def refund_subscription(jam_track_right)
|
|
account = get_account(jam_track_right.user)
|
|
if (account.present?)
|
|
terminated = false
|
|
begin
|
|
jam_track = jam_track_right.jam_track
|
|
account.subscriptions.find_each do |subscription|
|
|
#puts "subscription.plan.plan_code: #{subscription.plan.plan_code} / #{jam_track.plan_code} / #{subscription.plan.plan_code == jam_track.plan_code}"
|
|
if(subscription.plan.plan_code == jam_track.plan_code)
|
|
subscription.terminate(:full)
|
|
raise RecurlyClientError.new(subscription.errors) if subscription.errors.any?
|
|
terminated = true
|
|
end
|
|
end
|
|
|
|
if terminated
|
|
jam_track_right.destroy()
|
|
else
|
|
raise RecurlyClientError, "Subscription '#{jam_track.plan_code}' not found for this user; could not issue refund."
|
|
end
|
|
|
|
rescue Recurly::Error, NoMethodError => x
|
|
raise RecurlyClientError, x.to_s
|
|
end
|
|
|
|
else
|
|
raise RecurlyClientError, "Could not find account to refund order."
|
|
end
|
|
account
|
|
end
|
|
|
|
def find_jam_track_plan(jam_track)
|
|
plan = nil
|
|
begin
|
|
plan = Recurly::Plan.find(jam_track.plan_code)
|
|
rescue Recurly::Resource::NotFound
|
|
end
|
|
plan
|
|
end
|
|
|
|
def create_jam_track_plan(jam_track)
|
|
plan = Recurly::Plan.create(accounting_code: "",
|
|
bypass_hosted_confirmation: false,
|
|
cancel_url: nil,
|
|
description: jam_track.description,
|
|
display_donation_amounts: false,
|
|
display_phone_number: false,
|
|
display_quantity: false,
|
|
name: "JamTrack: #{jam_track.name}",
|
|
payment_page_css: nil,
|
|
payment_page_tos_link: nil,
|
|
plan_code: jam_track.plan_code,
|
|
plan_interval_length: 1,
|
|
plan_interval_unit: "months",
|
|
setup_fee_in_cents: Recurly::Money.new(:USD => 0), # <Recurly::Money USD: 0_00>
|
|
success_url: "",
|
|
tax_exempt: false,
|
|
total_billing_cycles: 1,
|
|
trial_interval_length: 0,
|
|
trial_interval_unit: "days",
|
|
unit_amount_in_cents: Recurly::Money.new(:USD => 1_99),
|
|
unit_name: "unit"
|
|
)
|
|
raise RecurlyClientError.new(plan.errors) if plan.errors.any?
|
|
end
|
|
|
|
def handle_create_subscription(current_user, plan_code, account)
|
|
begin
|
|
subscription = create_subscription(current_user, plan_code, account, current_user.subscription_trial_ended? ? nil : current_user.subscription_trial_ends_at)
|
|
current_user.recurly_subscription_id = subscription.uuid
|
|
if current_user.subscription_trial_ended?
|
|
current_user.subscription_plan_code = plan_code
|
|
current_user.subscription_plan_code_set_at = DateTime.now
|
|
else
|
|
# we could force a platinum plan since the user has put forward payment already, even in trial
|
|
puts "user #{current_user.email} is in trial"
|
|
if plan_code == SubscriptionDefinitions::JAM_PLATINUM || plan_code == SubscriptionDefinitions::JAM_PLATINUM_YEARLY
|
|
puts "user #{current_user.email} is in trial and buying platinum ; upgrade them already"
|
|
current_user.subscription_plan_code = plan_code
|
|
current_user.subscription_plan_code_set_at = DateTime.now
|
|
else
|
|
current_user.subscription_plan_code = SubscriptionDefinitions::JAM_GOLD
|
|
current_user.subscription_plan_code_set_at = DateTime.now
|
|
end
|
|
|
|
end
|
|
|
|
current_user.save(validate: false)
|
|
rescue => e
|
|
puts "Could not create subscription for user #{current_user.email}. #{e}"
|
|
return false, subscription, account
|
|
end
|
|
|
|
return true, subscription, account
|
|
end
|
|
|
|
# https://dev.recurly.com/docs/create-subscription
|
|
def create_subscription(user, plan_code, account, starts_at = nil)
|
|
|
|
old_subscription_id = user.recurly_subscription_id
|
|
|
|
if old_subscription_id
|
|
# first, let's try to reactivate it
|
|
old_subscription = Recurly::Subscription.find(old_subscription_id)
|
|
begin
|
|
old_subscription.reactivate
|
|
puts "reactivated plan! Let's check if it needs changing"
|
|
if plan_code != old_subscription.plan.plan_code
|
|
result = old_subscription.update_attributes(
|
|
:plan_code => plan_code,
|
|
:timeframe => starts_at.nil? ? 'bill_date' : 'now'
|
|
)
|
|
end
|
|
return old_subscription
|
|
rescue => e
|
|
puts "Unable to reactivate/update old plan #{e}"
|
|
user.update_attribute(:recurly_subscription_id, nil)
|
|
end
|
|
end
|
|
|
|
if account.billing_info
|
|
puts "Creating subscription for #{user.email} with plan_code #{plan_code}"
|
|
subscription = Recurly::Subscription.create(
|
|
:plan_code => plan_code,
|
|
:currency => 'USD',
|
|
:customer_notes => 'Thank you for your business!',
|
|
:account => {
|
|
:account_code => account.account_code
|
|
},
|
|
:starts_at => starts_at,
|
|
:auto_renew => true
|
|
)
|
|
subscription
|
|
else
|
|
puts "User has no billing info; not trying to create a subscription #{user.email}"
|
|
end
|
|
subscription
|
|
end
|
|
|
|
def find_subscription(user, fed_account = nil)
|
|
subscription = nil
|
|
account = nil
|
|
|
|
if fed_account.nil?
|
|
account = get_account(user)
|
|
else
|
|
account = fed_account
|
|
end
|
|
|
|
# first try to find the current subscription. If it's gone, delete our state. If expired, delete our state.
|
|
if user.recurly_subscription_id
|
|
begin
|
|
subscription = Recurly::Subscription.find(user.recurly_subscription_id)
|
|
rescue Recurly::Resource::NotFound
|
|
puts "subscription is gone. delete it!"
|
|
user.update_attribute(:recurly_subscription_id, nil)
|
|
user.recurly_subscription_id = nil
|
|
subscription = nil
|
|
end
|
|
|
|
puts "Subscription state: #{subscription.state}"
|
|
if subscription.state == 'expired'
|
|
puts "subscription is expired. stop tracking it!"
|
|
user.update_attribute(:recurly_subscription_id, nil)
|
|
user.recurly_subscription_id = nil
|
|
subscription = nil
|
|
end
|
|
end
|
|
|
|
if user.recurly_subscription_id.nil?
|
|
if account
|
|
active_subscription = nil
|
|
account.subscriptions.find_each do |subscription|
|
|
puts "Subscription: #{subscription.inspect} #{subscription.state}"
|
|
if subscription.state == "active" || subscription.state == "future"
|
|
active_subscription = subscription
|
|
break
|
|
end
|
|
end
|
|
subscription = active_subscription
|
|
else
|
|
puts "can't find subscription for account #{account}"
|
|
end
|
|
end
|
|
|
|
if subscription && user.recurly_subscription_id.nil?
|
|
puts "Repairing subscription ID on account"
|
|
user.update_attribute(:recurly_subscription_id, subscription.uuid)
|
|
user.recurly_subscription_id = subscription.uuid
|
|
end
|
|
|
|
return [subscription, account]
|
|
end
|
|
|
|
def change_subscription_plan(current_user, plan_code)
|
|
subscription, account = find_subscription(current_user)
|
|
|
|
if subscription.nil?
|
|
puts "no subscription found for user #{current_user.email}"
|
|
return false
|
|
end
|
|
|
|
puts "subscription.plan #{subscription.plan}"
|
|
if subscription.plan.plan_code == plan_code
|
|
puts "plan code was the same as requested: #{plan_code}"
|
|
return false
|
|
end
|
|
|
|
result = subscription.update_attributes(
|
|
:plan_code => plan_code,
|
|
:timeframe => 'bill_date'
|
|
)
|
|
puts "change subscription plan #{result}"
|
|
|
|
return result
|
|
end
|
|
|
|
|
|
def sync_subscription(user)
|
|
|
|
begin
|
|
# edge case: admin controlled
|
|
if user.admin_override_plan_code
|
|
# check if it's expired first...
|
|
if Time.now > user.admin_override_ends_at.to_time
|
|
puts "admin control expired. clear override and set Free plan"
|
|
user.admin_override_plan_code = nil
|
|
# logic below will catch this
|
|
#user.subscription_plan_code = nil
|
|
user.admin_override_ends_at = nil
|
|
user.subscription_sync_code = 'undo_admin_control'
|
|
user.subscription_sync_msg = "admin control expired. clear override and set Free plan"
|
|
user.subscription_last_checked_at = Time.now
|
|
user.save(validate: false)
|
|
# don't return; let this fall through to next states
|
|
else
|
|
puts "admin controlled plan #{user.email}"
|
|
user.subscription_plan_code = user.admin_override_plan_code
|
|
user.subscription_plan_code_set_at = Time.now
|
|
user.subscription_last_checked_at = Time.now
|
|
user.subscription_sync_code = 'admin_control'
|
|
user.subscription_sync_msg = "admin override - plan_code set to #{user.admin_override_plan_code}"
|
|
user.save(validate: false)
|
|
return
|
|
end
|
|
|
|
end
|
|
|
|
# edge case: user is in a licensed school
|
|
if user.has_active_license?
|
|
puts "user has school license #{user.email}"
|
|
user.subscription_plan_code = SubscriptionDefinitions::JAM_PLATINUM
|
|
user.subscription_plan_code_set_at = DateTime.now
|
|
user.subscription_last_checked_at = DateTime.now
|
|
user.subscription_sync_code = 'school_license'
|
|
user.subscription_sync_msg = "has school license - plan_code set to #{SubscriptionDefinitions::JAM_PLATINUM}"
|
|
user.save(validate: false)
|
|
return
|
|
end
|
|
|
|
# if user is in trial still, not much book-keeping
|
|
if !user.subscription_trial_ended?
|
|
puts "user has a trial still #{user.email}"
|
|
# there is actually nothing to do, because we don't start billing for any plan until trial is over.
|
|
user.subscription_last_checked_at = DateTime.now
|
|
user.subscription_sync_code = 'in_trial'
|
|
user.subscription_sync_msg = "trial still active - plan_code not altered"
|
|
user.save(validate: false)
|
|
return
|
|
end
|
|
|
|
# if there is no recurly action here, then they must be coming off of a trial and we have to mark them down
|
|
if user.recurly_code.nil? && !user.subscription_plan_code.nil?
|
|
puts "new user #{user.email} has no payment info and is ending their trial"
|
|
# TODO: send email
|
|
user.subscription_plan_code = nil
|
|
user.subscription_plan_code_set_at = DateTime.now
|
|
user.subscription_last_checked_at = DateTime.now
|
|
user.subscription_sync_code = 'trial_ended'
|
|
user.subscription_sync_msg = "trial ended and no subscription set - plan_code set to Free"
|
|
user.save(validate: false)
|
|
return
|
|
end
|
|
|
|
account = get_account(user)
|
|
|
|
if account.nil?
|
|
puts "Account is nil? #{user.email}. Strange. Could happen in some weird admin messing around scenarios"
|
|
user.subscription_last_checked_at = DateTime.now
|
|
user.save(validate: false)
|
|
user.subscription_sync_code = 'no_recurly_account'
|
|
user.subscription_sync_msg = "user has no recurly account - plan_code not altered"
|
|
user.save(validate: false)
|
|
return
|
|
end
|
|
|
|
user.is_past_due = account.has_past_due_invoice
|
|
|
|
subscription, account = find_subscription(user, account)
|
|
if subscription
|
|
user.recurly_subscription_state = subscription.state
|
|
else
|
|
user.recurly_subscription_state = nil
|
|
end
|
|
|
|
|
|
if subscription.nil? || subscription.state == 'expired'
|
|
puts "user has expired or no plan"
|
|
user.subscription_plan_code = nil
|
|
user.subscription_plan_code_set_at = DateTime.now
|
|
user.subscription_sync_code = 'no_subscription_or_expired'
|
|
user.subscription_sync_msg = "user has no or expired subscription - plan_code set to Free"
|
|
else
|
|
if user.is_past_due
|
|
if !user.subscription_plan_code.nil?
|
|
puts "user #{user.email} has a past due plan. We gotta bring them down"
|
|
user.subscription_plan_code = nil
|
|
user.subscription_plan_code_set_at = DateTime.now
|
|
user.subscription_sync_code = 'is_past_due_changed'
|
|
user.subscription_sync_msg = "payment has gone past due - plan_code set to Free"
|
|
else
|
|
puts "user is past due and #{user.email} had no changes"
|
|
user.subscription_sync_code = 'is_past_due_unchanged'
|
|
user.subscription_sync_msg = "payment has gone past due, plan_code not altered because already set to free"
|
|
end
|
|
else
|
|
if user.subscription_plan_code != user.desired_plan_code
|
|
puts "they are back! get them back into their desired plan #{user.email}"
|
|
if !SubscriptionDefinitions.is_downgrade(user.desired_plan_code, user.subscription_plan_code)
|
|
user.subscription_plan_code = user.desired_plan_code
|
|
user.subscription_plan_code_set_at = DateTime.now
|
|
user.subscription_sync_code = 'good_standing_repaired'
|
|
user.subscription_sync_msg = "user is in good standing but desired != effective; plan_code set to #{user.desired_plan_code}"
|
|
else
|
|
#user.subscription_plan_code = user.desired_plan_code
|
|
#user.subscription_plan_code_set_at = DateTime.now
|
|
user.subscription_sync_code = 'good_standing_ignored'
|
|
user.subscription_sync_msg = "user is in good standing but the desired plan is less than subscription plan; plan_code not touched"
|
|
end
|
|
|
|
|
|
else
|
|
puts "good standing user #{user.email} had no changes"
|
|
user.subscription_sync_code = 'good_standing_unchanged'
|
|
user.subscription_sync_msg = "user is in good standing but already set correctly; plan_code not altered"
|
|
end
|
|
|
|
end
|
|
end
|
|
|
|
user.subscription_last_checked_at = DateTime.now
|
|
user.save(validate: false)
|
|
rescue => e
|
|
puts "Unexpected error in sync_subscription for user #{user.email}"
|
|
puts e.message
|
|
user.subscription_last_checked_at = DateTime.now
|
|
user.subscription_sync_code = 'failed_sync'
|
|
user.subscription_sync_msg = e.message
|
|
user.save(validate: false)
|
|
end
|
|
|
|
end
|
|
|
|
def find_or_create_account(current_user, billing_info, recurly_token = nil)
|
|
account = get_account(current_user)
|
|
|
|
if !account
|
|
account = create_account(current_user, billing_info)
|
|
elsif !billing_info.nil?
|
|
update_billing_info(current_user, billing_info, account)
|
|
end
|
|
|
|
if !recurly_token.nil?
|
|
update_billing_info_from_token(current_user, account, recurly_token)
|
|
end
|
|
account
|
|
end
|
|
|
|
private
|
|
def account_hash(current_user, billing_info)
|
|
options = {
|
|
account_code: current_user.id,
|
|
email: current_user.email,
|
|
first_name: current_user.first_name,
|
|
last_name: current_user.last_name,
|
|
address: {
|
|
city: current_user.city,
|
|
state: current_user.state,
|
|
country: current_user.country
|
|
}
|
|
}
|
|
|
|
options[:billing_info] = billing_info if billing_info
|
|
|
|
options
|
|
end
|
|
end # class
|
|
|
|
class RecurlyClientError < Exception
|
|
attr_accessor :errors
|
|
def initialize(data)
|
|
if data.respond_to?('has_key?')
|
|
self.errors = data
|
|
else
|
|
self.errors = {:message=>data.to_s}
|
|
end
|
|
end # initialize
|
|
|
|
def to_s
|
|
s=super
|
|
s << ", errors: #{errors.inspect}" if self.errors.any?
|
|
s
|
|
end
|
|
|
|
end # RecurlyClientError
|
|
end # module
|
|
|