module Saucy module Account extend ActiveSupport::Concern included do CUSTOMER_ATTRIBUTES = { :cardholder_name => :cardholder_name, :billing_email => :email, :card_number => :number, :expiration_month => :expiration_month, :expiration_year => :expiration_year, :verification_code => :cvv } extend ActiveSupport::Memoizable has_many :memberships, :dependent => :destroy has_many :users, :through => :memberships has_many :projects, :dependent => :destroy has_many :admins, :through => :memberships, :source => :user, :conditions => { 'memberships.admin' => true } belongs_to :plan delegate :free?, :billed?, :to => :plan validates_uniqueness_of :name, :keyword validates_presence_of :name, :keyword, :plan_id attr_accessor *CUSTOMER_ATTRIBUTES.keys attr_accessible :name, :keyword validates_format_of :keyword, :with => %r{^[a-z0-9]+$}, :message => "must be only lower case letters." before_create :create_customer before_create :create_subscription, :if => :billed? memoize :customer end module InstanceMethods def to_param keyword end def has_member?(user) memberships.exists?(:user_id => user.id) end def users_by_name users.by_name end def projects_by_name projects.by_name end def projects_visible_to(user) projects.visible_to(user) end def memberships_by_name memberships.by_name end def customer Braintree::Customer.find(customer_token) if customer_token end def credit_card customer.credit_cards[0] if customer && customer.credit_cards.any? end def subscription Braintree::Subscription.find(subscription_token) if subscription_token end def save_braintree!(attributes) successful = true self.plan = ::Plan.find(attributes[:plan_id]) if attributes[:plan_id].present? if CUSTOMER_ATTRIBUTES.keys.any? { |attribute| attributes[attribute].present? } CUSTOMER_ATTRIBUTES.keys.each do |attribute| send("#{attribute}=", attributes[attribute]) if attributes[attribute].present? end result = Braintree::Customer.update(customer_token, customer_attributes) successful = result.success? handle_customer_result(result) end if successful && attributes[:plan_id].present? if subscription Braintree::Subscription.update(subscription_token, :plan_id => attributes[:plan_id]) else create_subscription end end successful && save end def can_change_plan_to?(new_plan) plan.limits.where(:value_type => :number).all? do |limit| new_plan.limit(limit.name).value >= send(limit.name).count end end private def customer_attributes { :email => billing_email, :credit_card => credit_card_attributes } end def credit_card_attributes if plan.billed? card_attributes = { :cardholder_name => cardholder_name, :number => card_number, :expiration_month => expiration_month, :expiration_year => expiration_year, :cvv => verification_code } if credit_card card_attributes.merge!(:options => credit_card_options) end card_attributes else {} end end def credit_card_options if customer && customer.credit_cards.any? { :update_existing_token => customer.credit_cards[0].token } else {} end end def create_customer result = Braintree::Customer.create(customer_attributes) handle_customer_result(result) end def handle_customer_result(result) if result.success? self.customer_token = result.customer.id flush_cache :customer else verification = result.credit_card_verification if verification && verification.status == "processor_declined" errors[:card_number] << "was denied by the payment processor with the message: #{verification.processor_response_text}" elsif verification && verification.status == "gateway_rejected" errors[:verification_code] << "did not match" elsif result.errors.any? result.errors.each do |error| if error.attribute == "number" errors[:card_number] << error.message.gsub("Credit card number ", "") elsif error.attribute == "CVV" errors[:verification_code] << error.message.gsub("CVV ", "") elsif error.attribute == "expiration_month" errors[:expiration_month] << error.message.gsub("Expiration month ", "") elsif error.attribute == "expiration_year" errors[:expiration_year] << error.message.gsub("Expiration year ", "") end end end end result.success? end def create_subscription result = Braintree::Subscription.create( :payment_method_token => credit_card.token, :plan_id => plan_id ) if result.success? self.subscription_token = result.subscription.id else false end end end end end