module ActiveRecord module Acts #:nodoc: module MuckOrderTransaction #:nodoc: def self.included(base) base.extend(ClassMethods) end module ClassMethods def acts_as_muck_order_transaction(options = {}) belongs_to :transactionable, :polymorphic => true serialize :params cattr_accessor :gateway cattr_accessor :cim_gateway cattr_accessor :paypal_express_gateway named_scope :by_newest, :order => 'order_transactions.created_at DESC' named_scope :by_oldest, :order => 'order_transactions.created_at ASC' include ActiveRecord::Acts::MuckOrderTransaction::InstanceMethods extend ActiveRecord::Acts::MuckOrderTransaction::SingletonMethods end end # class methods module SingletonMethods # Make an authorization using the billing_information object provided. The billing_information # object must have a valid credit card def authorize(amount, billing, options = {}) validate_card(billing) process(I18n.translate('muck.commerce.authorization'), amount) do |gw| gw.authorize(amount, billing.credit_card, options) end end # Capture funds from the given authorization def capture(amount, authorization, options = {}) process(I18n.translate('muck.commerce.capture'), amount) do |gw| gw.capture(amount, authorization, options) end end # Make a purchase using the billing_information object provided. The billing_information # object must have a valid credit card def purchase(amount, billing_information, options = {}) validate_card(billing_information) process(I18n.translate('muck.commerce.first_time_purchase'), amount) do |gw| gw.purchase(amount, billing_information.credit_card, options) end end # Make an authorization using the CIM. This assumes that the billing_information object has a # valid entry in the CIM def cim_authorize(amount, billing_information, options = {}) transaction = { :type => :auth_only, :amount => sprintf("%.2f", amount.to_f / 100) # Convert into decimal amount. HACK/TODO the rest of the library uses cents. One day this value might need to be cents instead of dollars } transaction[:customer_profile_id] = billing_information.billable.customer_profile_id transaction[:customer_payment_profile_id] = billing_information.customer_payment_profile_id process(I18n.translate('muck.commerce.authorization_cim'), amount, true) do |gw| gw.create_customer_profile_transaction(:transaction => transaction) end end # Make a purchase using the CIM. This assumes that the billing_information object has a # valid entry in the CIM def cim_purchase(amount, billing_information, options = {}) transaction = { :type => :auth_capture, :amount => sprintf("%.2f", amount.to_f / 100) # Convert into decimal amount. HACK/TODO the rest of the library uses cents. One day this value might need to be cents instead of dollars } transaction[:customer_profile_id] = billing_information.billable.customer_profile_id transaction[:customer_payment_profile_id] = billing_information.customer_payment_profile_id process(I18n.translate('muck.commerce.purchase_cim'), amount, true) do |gw| gw.create_customer_profile_transaction(:transaction => transaction) end end # Creates an entry in the CIM system for the given billing information def cim_create(billable, billing_information) process(I18n.translate('muck.commerce.cim_create'), 0, true) do |gw| response = gw.create_customer_profile(:profile => get_cim_profile(billing_information.billable, billing_information)) if response.success? # these are protected attributes so mass updates are not allowed billable.customer_profile_id = response.authorization billable.save! billing_information.customer_payment_profile_id = response.params["customer_payment_profile_id_list"]["numeric_string"] billing_information.save! billing_information.reload # associated billable object has changed and needs to be reloaded end response end end # Updates user information in the CIM system def cim_update(billable, validate = true) profile = get_cim_profile(billable, nil, validate) profile[:customer_profile_id] = billable.customer_profile_id process(I18n.translate('muck.commerce.cim_update_user'), 0, true) do |gw| gw.update_customer_profile(:profile => profile) end end # Updates billing information in the CIM system def cim_update_payment_profile(billing_information, validate = true) profile = get_cim_payment_profile(billing_information, validate) process(I18n.translate('muck.commerce.cim_update_payment_profile'), 0, true) do |gw| gw.update_customer_payment_profile( :customer_profile_id => billing_information.billable.customer_profile_id, :payment_profile => profile) end end # Creates or updates billing information in the CIM based on whether or not it already exists. def cim_create_update(billable, billing_information, validate = true) if billing_information.has_billing_profile? response = self.cim_update(billable, validate) if response.success? self.cim_update_payment_profile(billing_information, validate) else response end else self.cim_create(billable, billing_information) end end # Remove billing information from the CIM system. def cim_delete(customer_profile_id) cim_delete_by_customer_profile_id(customer_profile_id) end def cim_delete_by_customer_profile_id(customer_profile_id) process(I18n.translate('muck.commerce.cim_delete_user'), 0, true) do |gw| gw.delete_customer_profile(:customer_profile_id => customer_profile_id) end end # validates an existing profile def cim_validate_customer_payment_profile(billing_information) process(I18n.translate('muck.commerce.cim_validate_profile'), 0, true) do |gw| gw.validate_customer_payment_profile( :customer_profile_id => billing_information.billable.customer_profile_id, :customer_payment_profile_id => billing_information.customer_payment_profile_id, :validation_mode => :live) end end # Gets an existing profile def cim_get_customer_profile(customer_profile_id) process(I18n.translate('muck.commerce.cim_get_customer_profile'), 0, true) do |gw| gw.get_customer_profile( :customer_profile_id => customer_profile_id) end end # Gets an existing customer payment profile def cim_get_customer_payment_profile(customer_profile_id, customer_payment_profile_id) process(I18n.translate('muck.commerce.cim_get_payment_customer_profile'), 0, true) do |gw| gw.get_customer_payment_profile( :customer_profile_id => customer_profile_id, :customer_payment_profile_id => customer_payment_profile_id) end end # Transfer money to an email account using paypal def send_money_via_paypal(amount, email, options = {}) process(I18n.translate('muck.commerce.send_money_via_paypal'), amount) do |gw| gw.transfer(amount, email, options) end end # Get a profile object compatible with the CIM system. def get_cim_profile(billable, billing_information = nil, validate = true, customer_type = 'individual') profile = { :merchant_customer_id => billable.id, :description => billable.cim_description, :email => billable.email } if billing_information if billing_information.has_billing_profile? profile[:payment_profile] = get_cim_payment_profile(billing_information, validate, customer_type) else profile[:payment_profiles] = get_cim_payment_profile(billing_information, validate, customer_type) end end profile end def get_cim_payment_profile(billing_information, validate = true, customer_type = 'individual') validate_card(billing_information) if validate payment = { :credit_card => billing_information.credit_card_for_cim } address = { :first_name => billing_information.get_first_name, :last_name => billing_information.get_last_name } address[:company] = billing_information.company unless billing_information.company.blank? address[:address] = "#{billing_information.address1} #{billing_information.address2}" unless billing_information.address1.blank? address[:city] = billing_information.city unless billing_information.city.blank? address[:state] = billing_information.state.name unless billing_information.state.blank? address[:country] = billing_information.country.name unless billing_information.country.blank? address[:zip] = billing_information.postal_code unless billing_information.postal_code.blank? payment_profile = { :customer_type => customer_type, :bill_to => address, :payment => payment } payment_profile[:customer_payment_profile_id] = billing_information.customer_payment_profile_id if billing_information.customer_payment_profile_id payment_profile end def validate_card(billing) raise MuckCommerce::Exceptions::PaymentGatewayError, I18n.translate('muck.commerce.invalid_credit_card_information') if !billing.credit_card.valid? end def process(action, amount = nil, cim_transaction = false) result = OrderTransaction.new result.amount = amount result.action = action begin if cim_transaction response = yield cim_gateway else response = yield gateway end result.success = response.success? result.reference = response.authorization result.message = response.message result.params = response.params result.test = response.test? rescue ActiveMerchant::ActiveMerchantError => e result.success = false result.reference = nil result.message = e.message result.params = {} result.test = gateway.test? end result end end # All the methods available to a record that has had acts_as_muck_order_transaction specified. module InstanceMethods def direct_message self.params['direct_response']['message'] end end end end end