Class: Creditcard

Inherits:
ActiveRecord::Base
  • Object
show all
Defined in:
app/models/creditcard.rb

Direct Known Subclasses

TestCard

Defined Under Namespace

Classes: CardDetector

Instance Attribute Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#numberObject

Returns the value of attribute number.



7
8
9
# File 'app/models/creditcard.rb', line 7

def number
  @number
end

#verification_valueObject

Returns the value of attribute verification_value.



7
8
9
# File 'app/models/creditcard.rb', line 7

def verification_value
  @verification_value
end

Instance Method Details

#actionsObject



188
189
190
# File 'app/models/creditcard.rb', line 188

def actions
  %w{capture void credit}
end

#authorize(amount, payment) ⇒ Object



76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
# File 'app/models/creditcard.rb', line 76

def authorize(amount, payment)
  # ActiveMerchant is configured to use cents so we need to multiply order total by 100
  payment_gateway = payment.payment_method
  check_environment(payment_gateway)

  response = payment_gateway.authorize((amount * 100).round, self, gateway_options(payment))
  record_log payment, response

  if response.success?
    payment.response_code = response.authorization
    payment.avs_response = response.avs_result['code']
    payment.pend
  else
    payment.fail
    gateway_error(response)
  end
rescue ActiveMerchant::ConnectionError
  gateway_error I18n.t(:unable_to_connect_to_gateway)
end

#brandObject

needed for some of the ActiveMerchant gateways (eg. SagePay)



70
71
72
# File 'app/models/creditcard.rb', line 70

def brand
  cc_type
end

#can_capture?(payment) ⇒ Boolean

Indicates whether its possible to capture the payment

Returns:

  • (Boolean)


193
194
195
# File 'app/models/creditcard.rb', line 193

def can_capture?(payment)
  payment.state == "pending"
end

#can_credit?(payment) ⇒ Boolean

Indicates whether its possible to credit the payment. Note that most gateways require that the payment be settled first which generally happens within 12-24 hours of the transaction.

Returns:

  • (Boolean)


204
205
206
207
208
# File 'app/models/creditcard.rb', line 204

def can_credit?(payment)
  return false unless payment.state == "completed"
  return false unless payment.order.payment_state == "credit_owed"
  payment.credit_allowed > 0
end

#can_void?(payment) ⇒ Boolean

Indicates whether its possible to void the payment.

Returns:

  • (Boolean)


198
199
200
# File 'app/models/creditcard.rb', line 198

def can_void?(payment)
  payment.state == "void" ? false : true
end

#capture(payment) ⇒ Object



116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
# File 'app/models/creditcard.rb', line 116

def capture(payment)
  return unless payment.pending?
  payment_gateway = payment.payment_method
  check_environment(payment_gateway)

  if payment_gateway.payment_profiles_supported?
    # Gateways supporting payment profiles will need access to creditcard object because this stores the payment profile information
    # so supply the authorization itself as well as the creditcard, rather than just the authorization code
    response = payment_gateway.capture(payment, self, minimal_gateway_options(payment))
  else
    # Standard ActiveMerchant capture usage
    response = payment_gateway.capture((payment.amount * 100).round, payment.response_code, minimal_gateway_options(payment))
  end

  record_log payment, response

  if response.success?
    payment.response_code = response.authorization
    payment.complete
  else
    payment.fail
    gateway_error(response)
  end
rescue ActiveMerchant::ConnectionError
  gateway_error I18n.t(:unable_to_connect_to_gateway)
end

#check_environment(gateway) ⇒ Object

Saftey check to make sure we’re not accidentally performing operations on a live gateway. Ex. When testing in staging environment with a copy of production data.



262
263
264
265
266
# File 'app/models/creditcard.rb', line 262

def check_environment(gateway)
  return if gateway.environment == Rails.env
  message = I18n.t(:gateway_config_unavailable) + " - #{Rails.env}"
  raise Spree::GatewayError.new(message)
end

#credit(payment) ⇒ Object



160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
# File 'app/models/creditcard.rb', line 160

def credit(payment)
  payment_gateway = payment.payment_method
  check_environment(payment_gateway)

  amount = payment.credit_allowed >= payment.order.outstanding_balance.abs ? payment.order.outstanding_balance.abs : payment.credit_allowed.abs

  if payment_gateway.payment_profiles_supported?
    response = payment_gateway.credit((amount * 100).round, self, payment.response_code, minimal_gateway_options(payment))
  else
    response = payment_gateway.credit((amount * 100).round, payment.response_code, minimal_gateway_options(payment))
  end

  record_log payment, response

  if response.success?
    Payment.create(:order => payment.order,
                  :source => payment,
                  :payment_method => payment.payment_method,
                  :amount => amount.abs * -1,
                  :response_code => response.authorization,
                  :state => 'completed')
  else
    gateway_error(response)
  end
rescue ActiveMerchant::ConnectionError
  gateway_error I18n.t(:unable_to_connect_to_gateway)
end

#display_numberObject

Show the card number, with all but last 4 numbers replace with “X”. (XXXX-XXXX-XXXX-4338)



61
62
63
# File 'app/models/creditcard.rb', line 61

def display_number
 "XXXX-XXXX-XXXX-#{last_digits}"
end

#first_name?Boolean

Returns:

  • (Boolean)


44
45
46
# File 'app/models/creditcard.rb', line 44

def first_name?
  first_name.present?
end

#gateway_error(error) ⇒ Object



218
219
220
221
222
223
224
225
226
227
# File 'app/models/creditcard.rb', line 218

def gateway_error(error)
  if error.is_a? ActiveMerchant::Billing::Response
    text = error.params['message'] || error.params['response_reason_text'] || error.message
  else
    text = error.to_s
  end
  logger.error(I18n.t('gateway_error'))
  logger.error("  #{error.to_yaml}")
  raise Spree::GatewayError.new(text)
end

#gateway_options(payment) ⇒ Object



229
230
231
232
233
# File 'app/models/creditcard.rb', line 229

def gateway_options(payment)
  options = {:billing_address  => generate_address_hash(payment.order.bill_address),
             :shipping_address => generate_address_hash(payment.order.ship_address)}
  options.merge minimal_gateway_options(payment)
end

#generate_address_hash(address) ⇒ Object

Generates an ActiveMerchant compatible address hash from one of Spree’s address objects



236
237
238
239
240
# File 'app/models/creditcard.rb', line 236

def generate_address_hash(address)
  return {} if address.nil?
  {:name => address.full_name, :address1 => address.address1, :address2 => address.address2, :city => address.city,
   :state => address.state_text, :zip => address.zipcode, :country => address.country.iso, :phone => address.phone}
end

#has_payment_profile?Boolean

Returns:

  • (Boolean)


210
211
212
# File 'app/models/creditcard.rb', line 210

def has_payment_profile?
  gateway_customer_profile_id.present?
end

#last_name?Boolean

Returns:

  • (Boolean)


48
49
50
# File 'app/models/creditcard.rb', line 48

def last_name?
  last_name.present?
end

#minimal_gateway_options(payment) ⇒ Object

Generates a minimal set of gateway options. There appears to be some issues with passing in a billing address when authorizing/voiding a previously captured transaction. So omits these options in this case since they aren’t necessary.



245
246
247
248
249
250
251
252
253
# File 'app/models/creditcard.rb', line 245

def minimal_gateway_options(payment)
  {:email    => payment.order.email,
   :customer => payment.order.email,
   :ip       => payment.order.ip_address,
   :order_id => payment.order.number,
   :shipping => payment.order.ship_total * 100,
   :tax      => payment.order.tax_total * 100,
   :subtotal => payment.order.item_total * 100}
end

#nameObject



52
53
54
# File 'app/models/creditcard.rb', line 52

def name
  "#{first_name} #{last_name}"
end

#name?Boolean

Returns:

  • (Boolean)


40
41
42
# File 'app/models/creditcard.rb', line 40

def name?
  first_name? && last_name?
end

#process!(payment) ⇒ Object



13
14
15
16
17
18
19
# File 'app/models/creditcard.rb', line 13

def process!(payment)
  if Spree::Config[:auto_capture]
    purchase(payment.amount.to_f, payment)
  else
    authorize(payment.amount.to_f, payment)
  end
end

#purchase(amount, payment) ⇒ Object



96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
# File 'app/models/creditcard.rb', line 96

def purchase(amount, payment)
  #combined Authorize and Capture that gets processed by the ActiveMerchant gateway as one single transaction.
  payment_gateway = payment.payment_method
  check_environment(payment_gateway)

  response = payment_gateway.purchase((amount * 100).round, self, gateway_options(payment))
  record_log payment, response

  if response.success?
    payment.response_code = response.authorization
    payment.avs_response = response.avs_result['code']
    payment.complete
  else
    payment.fail
    gateway_error(response) unless response.success?
  end
rescue ActiveMerchant::ConnectionError
  gateway_error I18n.t(:unable_to_connect_to_gateway)
end

#record_log(payment, response) ⇒ Object



214
215
216
# File 'app/models/creditcard.rb', line 214

def record_log(payment, response)
  payment.log_entries.create(:details => response.to_yaml)
end

#set_card_typeObject

sets self.cc_type while we still have the card number



36
37
38
# File 'app/models/creditcard.rb', line 36

def set_card_type
  self.cc_type ||= CardDetector.type?(self.number)
end

#set_last_digitsObject



21
22
23
24
25
# File 'app/models/creditcard.rb', line 21

def set_last_digits
  number.to_s.gsub!(/\s/,'') unless number.nil?
  verification_value.to_s.gsub!(/\s/,'') unless number.nil?
  self.last_digits ||= number.to_s.length <= 4 ? number : number.to_s.slice(-4..-1)
end

#spree_cc_typeObject



255
256
257
258
# File 'app/models/creditcard.rb', line 255

def spree_cc_type
  return "visa" if ::Rails.env == "development"
  self.cc_type
end

#verification_value?Boolean

Returns:

  • (Boolean)


56
57
58
# File 'app/models/creditcard.rb', line 56

def verification_value?
  verification_value.present?
end

#void(payment) ⇒ Object



143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
# File 'app/models/creditcard.rb', line 143

def void(payment)
  payment_gateway = payment.payment_method
  check_environment(payment_gateway)

  response = payment_gateway.void(payment.response_code, minimal_gateway_options(payment))
  record_log payment, response

  if response.success?
    payment.response_code = response.authorization
    payment.void
  else
    gateway_error(response)
  end
rescue ActiveMerchant::ConnectionError
  gateway_error I18n.t(:unable_to_connect_to_gateway)
end