rename cvv to verification value
[monkeycharger.git] / trunk / app / models / credit_card.rb
blob2a372b46f5ab285d0c848146e2b1b14597c88ee5
1 class CreditCard < ActiveRecord::Base
2    include ActiveMerchant::Billing::CreditCardMethods
3    include ActiveMerchant::Billing::CreditCardMethods::ClassMethods
5    attr_accessor :verification_value, :number, :passphrase
7    validates_presence_of :name, :number, :street_address, :state, :zip, :country, :number, :city
9    validate :check_for_credit_card_validity
10    validate :month_and_year_should_be_in_future
11    validate :at_least_two_words_in_name
12    validates_presence_of :passphrase
13    has_many :authorizations
14    has_many :captures
16    before_validation :convert_number_to_string
17    before_create :crypt_number
18    before_create :save_last_four_digits
20    # Needed for authorize.net and active merchant
21    def verification_value?
22      verification_value
23    end
25    def to_xml
26       super(:only => [:last_four_digits, :name, :month, :year, :card_type, :id])
27    end
29    def last_four_digits
30      new_record? ? save_last_four_digits : read_attribute(:last_four_digits)
31    end
33    def decrypt!(passphrase)
34       @number = decrypt_number(passphrase)
35       self
36    end
38    # Gets the first name from name
39    def first_name
40       name.split[0] if name
41    end
43    # Gets the last name from name
44    def last_name
45       name.split[1..-1].join(" ") if name
46    end
48    private
50    def check_for_credit_card_validity
51       errors.add(:year, "is not a valid year") unless valid_expiry_year?(year.to_i)
52       errors.add(:month, "is not a valid month") unless valid_month?(month.to_i)
53       errors.add(:number, "is not a valid credit card number") unless valid_number?(number)
54       errors.add(:verification_value, "must be provided") unless verification_value
55       self.card_type = type?(number)
56       errors.add_to_base("We only accept Visa and MasterCard.") unless self.card_type == 'master' or self.card_type == 'visa'
57    end
59    def month_and_year_should_be_in_future
60       if (Date.new(year.to_i, month.to_i, 1) >> 1) < Date.today
61          errors.add_to_base("The expiration date must be in the future.") and return false
62       end
63    rescue ArgumentError => e
64       errors.add_to_base("Date is not valid") and return false
65    end
67    def at_least_two_words_in_name
68       errors.add(:name, "must be two words long.") and return false if name and name.split.size < 2
69    end
71    # Encrypts the credit card number
72    def crypt_number
73       c = cipher
74       c.encrypt
75       c.key = key 
76       c.iv = self.iv = generate_iv(passphrase)
77       temp_number = c.update(@number)
78       temp_number << c.final
79       self.crypted_number = encode_into_base64(temp_number) 
80    end
82    # Decrypts the credit card number
83    def decrypt_number(passphrase)
84       c = cipher
85       c.decrypt
86       c.key = key
87       c.iv = generate_iv(passphrase)
88       d = c.update(decode_from_base64(self.crypted_number))
89       d << c.final
90    end
92    def cipher
93       OpenSSL::Cipher::Cipher.new("aes-256-cbc")
94    end
96    def key
97       Digest::SHA256.digest(@@CreditCardSecretKey)
98    end
100    def generate_iv(passphrase)
101       raise ArgumentError.new("be sure to set the passphrase") if passphrase.blank?
102       encode_into_base64(Digest::SHA1.hexdigest(passphrase))
103    end
105    # Chomping is necessary for postgresql
106    def encode_into_base64 string
107       Base64.encode64(string).chomp
108    end
110    def decode_from_base64 string
111       Base64.decode64(string)
112    end
114    def save_last_four_digits
115       self.last_four_digits = @number[-4..-1]
116    end
118    def convert_number_to_string
119       @passphrase = @passphrase.to_s
120       @number = @number.to_s
121    end