require 'recurly' module JamRuby class RecurlyClient def initialize() 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) raise RecurlyClientError.new(account.errors) if account.errors.any? rescue Recurly::Error, NoMethodError => x #puts "Error: #{x} : #{Kernel.caller}" raise RecurlyClientError, x.to_s else if account current_user.update_attribute(:recurly_code, account.account_code) end end 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 get_account(current_user) current_user && current_user.recurly_code ? Recurly::Account.find(current_user.recurly_code) : nil 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 payment_history(current_user) payments = [] account = get_account(current_user) if(account.present?) begin account.transactions.find_each do |transaction| 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, :status => transaction.status, :payment_method => transaction.payment_method, :reference => transaction.reference } end end rescue Recurly::Error, NoMethodError => x raise RecurlyClientError, x.to_s end end payments end def update_billing_info(current_user, billing_info=nil) account = get_account(current_user) 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 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), # 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 place_order(current_user, jam_track, shopping_cart) jam_track_right = nil account = get_account(current_user) if (account.present?) begin # see if we can find existing plan for this plan_code, which should occur for previous-in-time error scenarios recurly_subscription_uuid = nil account.subscriptions.find_each do |subscription| if subscription.plan.plan_code == jam_track.plan_code recurly_subscription_uuid = subscription.uuid break end end free = false # this means we already have a subscription, so don't try to create a new one for the same plan (Recurly would fail this anyway) unless recurly_subscription_uuid # if the shopping cart was specified, see if the item should be free free = shopping_cart.nil? ? false : shopping_cart.free? # and if it's free, squish the charge to 0. unit_amount_in_cents = free ? 0 : nil subscription = Recurly::Subscription.create(:account=>account, :plan_code=>jam_track.plan_code, unit_amount_in_cents: unit_amount_in_cents) raise RecurlyClientError.new(subscription.errors) if subscription.errors.any? # delete from shopping cart the subscription shopping_cart.destroy if shopping_cart # Reload and make sure it went through: account = get_account(current_user) account.subscriptions.find_each do |subscription| if subscription.plan.plan_code == jam_track.plan_code recurly_subscription_uuid = subscription.uuid break end end end raise RecurlyClientError, "Plan code '#{paid_subscription.plan_code}' doesn't match jam track: '#{jam_track.plan_code}'" unless recurly_subscription_uuid jam_track_right = JamRuby::JamTrackRight.find_or_create_by_user_id_and_jam_track_id(current_user.id, jam_track.id) do |jam_track_right| jam_track_right.redeemed = free end User.where(id: current_user.id).update_all(has_redeemable_jamtrack: false) if free # also if the purchase was a free one, then update the user record to no longer allow redeemed jamtracks # this can't go in the block above, as it's here to fix bad subscription UUIDs in an update path if jam_track_right.recurly_subscription_uuid != recurly_subscription_uuid jam_track_right.recurly_subscription_uuid = recurly_subscription_uuid jam_track_right.save end raise RecurlyClientError.new("Error creating jam_track_right for jam_track: #{jam_track.id}") if jam_track_right.nil? raise RecurlyClientError.new(jam_track_right.errors) if jam_track_right.errors.any? 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 place order." end jam_track_right end def find_or_create_account(current_user, billing_info) account = get_account(current_user) if(account.nil?) account = create_account(current_user, billing_info) else update_billing_info(current_user, billing_info) 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