activemerchant 1.29.1 → 1.34.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. data/CHANGELOG +143 -0
  2. data/CONTRIBUTORS +43 -0
  3. data/README.md +59 -51
  4. data/lib/active_merchant/billing/check.rb +15 -14
  5. data/lib/active_merchant/billing/credit_card.rb +14 -5
  6. data/lib/active_merchant/billing/credit_card_formatting.rb +8 -8
  7. data/lib/active_merchant/billing/gateway.rb +2 -2
  8. data/lib/active_merchant/billing/gateways/authorize_net.rb +36 -8
  9. data/lib/active_merchant/billing/gateways/authorize_net_cim.rb +17 -5
  10. data/lib/active_merchant/billing/gateways/balanced.rb +9 -3
  11. data/lib/active_merchant/billing/gateways/banwire.rb +15 -1
  12. data/lib/active_merchant/billing/gateways/barclays_epdq.rb +8 -1
  13. data/lib/active_merchant/billing/gateways/beanstream/beanstream_core.rb +7 -2
  14. data/lib/active_merchant/billing/gateways/beanstream.rb +26 -24
  15. data/lib/active_merchant/billing/gateways/blue_pay.rb +201 -187
  16. data/lib/active_merchant/billing/gateways/bogus.rb +1 -1
  17. data/lib/active_merchant/billing/gateways/braintree_blue.rb +7 -3
  18. data/lib/active_merchant/billing/gateways/card_stream_modern.rb +155 -0
  19. data/lib/active_merchant/billing/gateways/cc5.rb +156 -0
  20. data/lib/active_merchant/billing/gateways/cyber_source.rb +55 -22
  21. data/lib/active_merchant/billing/gateways/data_cash.rb +3 -3
  22. data/lib/active_merchant/billing/gateways/evo_ca.rb +308 -0
  23. data/lib/active_merchant/billing/gateways/eway.rb +114 -171
  24. data/lib/active_merchant/billing/gateways/eway_managed.rb +52 -22
  25. data/lib/active_merchant/billing/gateways/finansbank.rb +22 -0
  26. data/lib/active_merchant/billing/gateways/firstdata_e4.rb +314 -0
  27. data/lib/active_merchant/billing/gateways/garanti.rb +0 -4
  28. data/lib/active_merchant/billing/gateways/ideal_rabobank.rb +13 -2
  29. data/lib/active_merchant/billing/gateways/iridium.rb +8 -2
  30. data/lib/active_merchant/billing/gateways/litle.rb +354 -105
  31. data/lib/active_merchant/billing/gateways/merchant_e_solutions.rb +28 -7
  32. data/lib/active_merchant/billing/gateways/merchant_ware.rb +44 -9
  33. data/lib/active_merchant/billing/gateways/merchant_warrior.rb +190 -0
  34. data/lib/active_merchant/billing/gateways/moneris.rb +4 -6
  35. data/lib/active_merchant/billing/gateways/moneris_us.rb +1 -1
  36. data/lib/active_merchant/billing/gateways/nab_transact.rb +20 -3
  37. data/lib/active_merchant/billing/gateways/net_registry.rb +8 -3
  38. data/lib/active_merchant/billing/gateways/netaxept.rb +65 -117
  39. data/lib/active_merchant/billing/gateways/netbilling.rb +1 -0
  40. data/lib/active_merchant/billing/gateways/netpay.rb +223 -0
  41. data/lib/active_merchant/billing/gateways/ogone.rb +7 -5
  42. data/lib/active_merchant/billing/gateways/optimal_payment.rb +43 -18
  43. data/lib/active_merchant/billing/gateways/orbital.rb +190 -53
  44. data/lib/active_merchant/billing/gateways/payflow/payflow_common_api.rb +12 -10
  45. data/lib/active_merchant/billing/gateways/payment_express.rb +62 -1
  46. data/lib/active_merchant/billing/gateways/paymill.rb +179 -0
  47. data/lib/active_merchant/billing/gateways/paypal/paypal_common_api.rb +12 -7
  48. data/lib/active_merchant/billing/gateways/paypal/paypal_express_response.rb +14 -9
  49. data/lib/active_merchant/billing/gateways/paypal_express.rb +59 -18
  50. data/lib/active_merchant/billing/gateways/pin.rb +165 -0
  51. data/lib/active_merchant/billing/gateways/qbms.rb +3 -2
  52. data/lib/active_merchant/billing/gateways/quickpay.rb +66 -28
  53. data/lib/active_merchant/billing/gateways/sage/sage_bankcard.rb +16 -11
  54. data/lib/active_merchant/billing/gateways/sage/sage_core.rb +1 -1
  55. data/lib/active_merchant/billing/gateways/sage/sage_virtual_check.rb +21 -16
  56. data/lib/active_merchant/billing/gateways/sage.rb +10 -5
  57. data/lib/active_merchant/billing/gateways/sage_pay.rb +7 -0
  58. data/lib/active_merchant/billing/gateways/smart_ps.rb +1 -1
  59. data/lib/active_merchant/billing/gateways/spreedly_core.rb +233 -0
  60. data/lib/active_merchant/billing/gateways/stripe.rb +49 -21
  61. data/lib/active_merchant/billing/gateways/transnational.rb +239 -0
  62. data/lib/active_merchant/billing/gateways/usa_epay_advanced.rb +9 -4
  63. data/lib/active_merchant/billing/gateways/webpay.rb +8 -0
  64. data/lib/active_merchant/billing/gateways/wirecard.rb +15 -9
  65. data/lib/active_merchant/billing/gateways/worldpay.rb +60 -24
  66. data/lib/active_merchant/billing/integrations/direc_pay/status.rb +1 -1
  67. data/lib/active_merchant/billing/integrations/direc_pay.rb +1 -1
  68. data/lib/active_merchant/billing/integrations/dwolla/common.rb +23 -0
  69. data/lib/active_merchant/billing/integrations/dwolla/helper.rb +18 -6
  70. data/lib/active_merchant/billing/integrations/dwolla/notification.rb +16 -7
  71. data/lib/active_merchant/billing/integrations/dwolla/return.rb +16 -5
  72. data/lib/active_merchant/billing/integrations/dwolla.rb +5 -12
  73. data/lib/active_merchant/billing/integrations/notification.rb +13 -8
  74. data/lib/active_merchant/billing/integrations/payflow_link/helper.rb +19 -3
  75. data/lib/active_merchant/billing/integrations/paypal/notification.rb +39 -31
  76. data/lib/active_merchant/billing/integrations/payu_in/helper.rb +74 -0
  77. data/lib/active_merchant/billing/integrations/payu_in/notification.rb +167 -0
  78. data/lib/active_merchant/billing/integrations/payu_in/return.rb +53 -0
  79. data/lib/active_merchant/billing/integrations/payu_in.rb +43 -0
  80. data/lib/active_merchant/billing/integrations/pxpay/helper.rb +1 -0
  81. data/lib/active_merchant/billing/integrations/quickpay/helper.rb +13 -10
  82. data/lib/active_merchant/billing/integrations/quickpay/notification.rb +78 -15
  83. data/lib/active_merchant/billing/integrations/rbkmoney/helper.rb +23 -0
  84. data/lib/active_merchant/billing/integrations/rbkmoney/notification.rb +91 -0
  85. data/lib/active_merchant/billing/integrations/rbkmoney.rb +17 -0
  86. data/lib/active_merchant/billing/integrations/robokassa/common.rb +1 -1
  87. data/lib/active_merchant/billing/integrations/sage_pay_form/helper.rb +7 -3
  88. data/lib/active_merchant/billing/integrations/world_pay.rb +15 -8
  89. data/lib/active_merchant/version.rb +1 -1
  90. data.tar.gz.sig +0 -0
  91. metadata +124 -50
  92. metadata.gz.sig +0 -0
@@ -3,21 +3,47 @@ require 'digest/md5'
3
3
  module ActiveMerchant #:nodoc:
4
4
  module Billing #:nodoc:
5
5
  class BluePayGateway < Gateway
6
- class_attribute :live_url, :rebilling_url, :ignore_http_status
6
+ class_attribute :rebilling_url, :ignore_http_status
7
7
 
8
8
  self.live_url = 'https://secure.bluepay.com/interfaces/bp20post'
9
9
  self.rebilling_url = 'https://secure.bluepay.com/interfaces/bp20rebadmin'
10
10
 
11
11
  self.ignore_http_status = true
12
12
 
13
- RESPONSE_CODE, RESPONSE_REASON_CODE, RESPONSE_REASON_TEXT = 0, 2, 3
14
- AVS_RESULT_CODE, TRANSACTION_ID, CARD_CODE_RESPONSE_CODE = 5, 6, 38
15
-
16
13
  CARD_CODE_ERRORS = %w( N S )
17
14
  AVS_ERRORS = %w( A E N R W Z )
18
15
  AVS_REASON_CODES = %w(27 45)
19
16
 
20
- class_attribute :duplicate_window
17
+ FRAUD_REVIEW_STATUSES = %w( E 0 )
18
+
19
+ FIELD_MAP = {
20
+ 'TRANS_ID' => :transaction_id,
21
+ 'STATUS' => :response_code,
22
+ 'AVS' => :avs_result_code,
23
+ 'CVV2'=> :card_code,
24
+ 'AUTH_CODE' => :authorization,
25
+ 'MESSAGE' => :message,
26
+ 'REBID' => :rebid,
27
+ 'TRANS_TYPE' => :trans_type,
28
+ 'PAYMENT_ACCOUNT_MASK' => :acct_mask,
29
+ 'CARD_TYPE' => :card_type,
30
+ }
31
+
32
+ REBILL_FIELD_MAP = {
33
+ 'REBILL_ID' => :rebill_id,
34
+ 'ACCOUNT_ID'=> :account_id,
35
+ 'USER_ID' => :user_id,
36
+ 'TEMPLATE_ID' => :template_id,
37
+ 'STATUS' => :status,
38
+ 'CREATION_DATE' => :creation_date,
39
+ 'NEXT_DATE' => :next_date,
40
+ 'LAST_DATE' => :last_date,
41
+ 'SCHED_EXPR' => :schedule,
42
+ 'CYCLES_REMAIN' => :cycles_remain,
43
+ 'REB_AMOUNT' => :rebill_amount,
44
+ 'NEXT_AMOUNT' => :next_amount,
45
+ 'USUAL_DATE' => :undoc_usual_date, # Not found in the bp20rebadmin API doc.
46
+ }
21
47
 
22
48
  self.supported_countries = ['US']
23
49
  self.supported_cardtypes = [:visa, :master, :american_express, :discover, :diners_club, :jcb]
@@ -25,7 +51,6 @@ module ActiveMerchant #:nodoc:
25
51
  self.display_name = 'BluePay'
26
52
  self.money_format = :dollars
27
53
 
28
-
29
54
  # Creates a new BluepayGateway
30
55
  #
31
56
  # The gateway requires that a valid Account ID and Secret Key be passed
@@ -54,24 +79,12 @@ module ActiveMerchant #:nodoc:
54
79
  # * <tt>options</tt> -- A hash of optional parameters.
55
80
  def authorize(money, payment_object, options = {})
56
81
  post = {}
57
- post[:MASTER_ID] = ''
58
- if payment_object != nil && payment_object.class() != String
59
- payment_object.class() == ActiveMerchant::Billing::Check ?
60
- add_check(post, payment_object) :
61
- add_creditcard(post, payment_object)
62
- else
63
- post[:MASTER_ID] = payment_object
64
- end
82
+ add_payment_method(post, payment_object)
65
83
  add_invoice(post, options)
66
84
  add_address(post, options)
67
85
  add_customer_data(post, options)
68
- if options[:rebill] != nil
69
- post[:DO_REBILL] = '1'
70
- post[:REB_AMOUNT] = amount(options[:rebill_amount])
71
- post[:REB_FIRST_DATE] = options[:rebill_start_date]
72
- post[:REB_EXPR] = options[:rebill_expression]
73
- post[:REB_CYCLES] = options[:rebill_cycles]
74
- end
86
+ add_rebill(post, options) if options[:rebill]
87
+ add_duplicate_override(post, options)
75
88
  post[:TRANS_TYPE] = 'AUTH'
76
89
  commit('AUTH_ONLY', money, post)
77
90
  end
@@ -89,24 +102,12 @@ module ActiveMerchant #:nodoc:
89
102
  # * <tt>options</tt> -- A hash of optional parameters.,
90
103
  def purchase(money, payment_object, options = {})
91
104
  post = {}
92
- post[:MASTER_ID] = ''
93
- if payment_object != nil && payment_object.class() != String
94
- payment_object.class() == ActiveMerchant::Billing::Check ?
95
- add_check(post, payment_object) :
96
- add_creditcard(post, payment_object)
97
- else
98
- post[:MASTER_ID] = payment_object
99
- end
105
+ add_payment_method(post, payment_object)
100
106
  add_invoice(post, options)
101
107
  add_address(post, options)
102
108
  add_customer_data(post, options)
103
- if options[:rebill] != nil
104
- post[:DO_REBILL] = '1'
105
- post[:REB_AMOUNT] = amount(options[:rebill_amount])
106
- post[:REB_FIRST_DATE] = options[:rebill_start_date]
107
- post[:REB_EXPR] = options[:rebill_expression]
108
- post[:REB_CYCLES] = options[:rebill_cycles]
109
- end
109
+ add_rebill(post, options) if options[:rebill]
110
+ add_duplicate_override(post, options)
110
111
  post[:TRANS_TYPE] = 'SALE'
111
112
  commit('AUTH_CAPTURE', money, post)
112
113
  end
@@ -154,20 +155,17 @@ module ActiveMerchant #:nodoc:
154
155
  # If the payment_object is a token, then the transaction type will reverse a previous capture or purchase transaction, returning the funds to the customer. If the amount is nil, a full credit will be processed. This is referred to a REFUND transaction in BluePay.
155
156
  # If the payment_object is either a CreditCard or Check object, then the transaction type will be an unmatched credit placing funds in the specified account. This is referred to a CREDIT transaction in BluePay.
156
157
  # * <tt>options</tt> -- A hash of parameters.
157
- def refund(money, payment_object, options = {})
158
- post = {}
159
- post[:PAYMENT_ACCOUNT] = ''
160
- if payment_object != nil && payment_object.class() != String
161
- payment_object.class() == ActiveMerchant::Billing::Check ?
162
- add_check(post, payment_object) :
163
- add_creditcard(post, payment_object)
164
- post[:TRANS_TYPE] = 'CREDIT'
165
- else
166
- post[:MASTER_ID] = payment_object
167
- post[:TRANS_TYPE] = 'REFUND'
158
+ def refund(money, identification, options = {})
159
+ if(identification && !identification.kind_of?(String))
160
+ deprecated "refund should only be used to refund a referenced transaction"
161
+ return credit(money, identification, options)
168
162
  end
169
163
 
170
- options[:first_name] ? post[:NAME1] = options[:first_name] : post[:NAME1] = ''
164
+ post = {}
165
+ post[:PAYMENT_ACCOUNT] = ''
166
+ post[:MASTER_ID] = identification
167
+ post[:TRANS_TYPE] = 'REFUND'
168
+ post[:NAME1] = (options[:first_name] ? options[:first_name] : "")
171
169
  post[:NAME2] = options[:last_name] if options[:last_name]
172
170
  post[:ZIP] = options[:zip] if options[:zip]
173
171
  add_invoice(post, options)
@@ -176,9 +174,24 @@ module ActiveMerchant #:nodoc:
176
174
  commit('CREDIT', money, post)
177
175
  end
178
176
 
179
- def credit(money, identification, options = {})
180
- deprecated CREDIT_DEPRECATION_MESSAGE
181
- refund(money, identification, options)
177
+ def credit(money, payment_object, options = {})
178
+ if(payment_object && payment_object.kind_of?(String))
179
+ deprecated "credit should only be used to credit a payment method"
180
+ return refund(money, payment_object, options)
181
+ end
182
+
183
+ post = {}
184
+ post[:PAYMENT_ACCOUNT] = ''
185
+ add_payment_method(post, payment_object)
186
+ post[:TRANS_TYPE] = 'CREDIT'
187
+
188
+ post[:NAME1] = (options[:first_name] ? options[:first_name] : "")
189
+ post[:NAME2] = options[:last_name] if options[:last_name]
190
+ post[:ZIP] = options[:zip] if options[:zip]
191
+ add_invoice(post, options)
192
+ add_address(post, options)
193
+ add_customer_data(post, options)
194
+ commit('CREDIT', money, post)
182
195
  end
183
196
 
184
197
  # Create a new recurring payment.
@@ -214,9 +227,12 @@ module ActiveMerchant #:nodoc:
214
227
  # A money object of 1995 cents would be passed into the 'money' parameter.
215
228
  def recurring(money, payment_object, options = {})
216
229
  requires!(options, :rebill_start_date, :rebill_expression)
217
- options[:rebill] = '1'
218
- money == nil ? authorize(money, payment_object, options) :
219
- purchase(money, payment_object, options)
230
+ options[:rebill] = true
231
+ if money
232
+ purchase(money, payment_object, options)
233
+ else
234
+ authorize(money, payment_object, options)
235
+ end
220
236
  end
221
237
 
222
238
  # View a recurring payment
@@ -252,11 +268,11 @@ module ActiveMerchant #:nodoc:
252
268
  requires!(options, :rebill_id)
253
269
  post[:REBILL_ID] = options[:rebill_id]
254
270
  post[:TRANS_TYPE] = 'SET'
255
- post[:REB_AMOUNT] = amount(options[:rebill_amount]) if !options[:rebill_amount].nil?
256
- post[:NEXT_DATE] = options[:rebill_next_date] if !options[:rebill_next_date].nil?
257
- post[:REB_EXPR] = options[:rebill_expression] if !options[:rebill_expression].nil?
258
- post[:REB_CYCLES] = options[:rebill_cycles] if !options[:rebill_cycles].nil?
259
- post[:NEXT_AMOUNT] = options[:rebill_next_amount] if !options[:rebill_next_amount].nil?
271
+ post[:REB_AMOUNT] = amount(options[:rebill_amount]) if options[:rebill_amount]
272
+ post[:NEXT_DATE] = options[:rebill_next_date]
273
+ post[:REB_EXPR] = options[:rebill_expression]
274
+ post[:REB_CYCLES] = options[:rebill_cycles]
275
+ post[:NEXT_AMOUNT] = options[:rebill_next_amount]
260
276
  commit('rebill', 'nil', post)
261
277
  end
262
278
 
@@ -279,145 +295,155 @@ module ActiveMerchant #:nodoc:
279
295
  private
280
296
 
281
297
  def commit(action, money, fields)
282
- fields[:AMOUNT] = amount(money) unless (fields[:TRANS_TYPE] == 'VOID' or action == 'rebill')
283
- test? == true || @options[:test] == true ? fields[:MODE] = 'TEST' : fields[:MODE] = 'LIVE'
284
- action == 'rebill' ? begin url = rebilling_url; fields[:TAMPER_PROOF_SEAL] = calc_rebill_tps(fields) end : begin url = live_url; fields[:TAMPER_PROOF_SEAL] = calc_tps(amount(money), fields) end
298
+ fields[:AMOUNT] = amount(money) unless(fields[:TRANS_TYPE] == 'VOID' || action == 'rebill')
299
+ fields[:MODE] = (test? ? 'TEST' : 'LIVE')
285
300
  fields[:ACCOUNT_ID] = @options[:login]
286
- data = ssl_post url, post_data(action, fields)
287
- response = parse(data)
288
- message = message_from(response)
289
- test_mode = test? || fields[:MODE] == 'TEST'
290
- if (response.has_key?('TRANS_ID'))
291
- response_id = response['TRANS_ID'].to_s()
292
- elsif (response.has_key?('rebill_id'))
293
- response_id = response['rebill_id'][0]
301
+
302
+ if action == 'rebill'
303
+ url = rebilling_url
304
+ fields[:TAMPER_PROOF_SEAL] = calc_rebill_tps(fields)
294
305
  else
295
- response_id = response[TRANSACTION_ID]
306
+ url = live_url
307
+ fields[:TAMPER_PROOF_SEAL] = calc_tps(amount(money), fields)
296
308
  end
297
- avs = (response[AVS_RESULT_CODE] != '' ? response[AVS_RESULT_CODE] : '')
298
- cvv2 = (!response[CARD_CODE_RESPONSE_CODE].empty? ? response[CARD_CODE_RESPONSE_CODE] : '')
299
- Response.new(success?(response), message, response,
300
- :test => test_mode,
301
- :authorization => response_id,
302
- :fraud_review => fraud_review?(response),
303
- :avs_result => { :code => avs },
304
- :cvv_result => cvv2
305
- )
309
+ parse(ssl_post(url, post_data(action, fields)))
306
310
  end
307
311
 
308
- def success?(response)
309
- if (response['STATUS'] == '1' || message_from(response) =~ /approved/ || response.has_key?('rebill_id') || response[RESPONSE_REASON_TEXT] =~ /approved/)
310
- return true
311
- else
312
- return false
312
+ def parse_recurring(response_fields, opts={}) # expected status?
313
+ parsed = {}
314
+ response_fields.each do |k,v|
315
+ mapped_key = REBILL_FIELD_MAP.include?(k) ? REBILL_FIELD_MAP[k] : k
316
+ parsed[mapped_key] = v
313
317
  end
314
- end
315
318
 
316
- def fraud_review?(response)
317
- response['STATUS'] == 'E' || response['STATUS'] == '0' || response[RESPONSE_REASON_TEXT] =~ /being reviewed/
318
- end
319
+ success = parsed[:status] != 'error'
320
+ message = parsed[:status]
319
321
 
320
- def get_rebill_id(response)
321
- response['REBID'] if response_has.key?('REBID')
322
+ Response.new(success, message, parsed,
323
+ :test => test?,
324
+ :authorization => parsed[:rebill_id])
322
325
  end
323
326
 
324
327
  def parse(body)
325
- fields = CGI::parse(body)
326
- if fields.has_key?('MESSAGE') or fields.has_key?('rebill_id')
327
- if fields.has_key?('MESSAGE')
328
- fields['MESSAGE'][0] == "Missing ACCOUNT_ID" ? message = "The merchant login ID or password is invalid" : message = fields['MESSAGE']
329
- fields['MESSAGE'][0] =~ /Approved/ ? message = "This transaction has been approved" : message = fields['MESSAGE'] if message == fields['MESSAGE']
330
- fields['MESSAGE'][0] =~ /Expired/ ? message = "The credit card has expired" : message = fields['MESSAGE'] if message == fields['MESSAGE']
331
- fields.delete('MESSAGE')
332
- end
333
- fields.has_key?('STATUS') ? begin status = fields['STATUS']; fields.delete('STATUS') end : status = ''
334
- fields.has_key?('AVS') ? begin avs = fields['AVS']; fields.delete('AVS') end : avs = ''
335
- fields.has_key?('CVV2') ? begin cvv2 = fields['CVV2']; fields.delete('CVV2') end : cvv2 = ''
336
- fields.has_key?('MASTER_ID') ? begin trans_id = fields['MASTER_ID']; fields.delete('MASTER_ID') end : trans_id = ''
337
- fields[:avs_result_code] = avs
338
- fields[:card_code] = cvv2
339
- fields[:response_code] = status
340
- fields[:response_reason_code] = ''
341
- fields[:response_reason_text] = message
342
- fields[:transaction_id] = trans_id
343
- return fields
328
+ # The bp20api has max one value per form field.
329
+ response_fields = Hash[CGI::parse(body).map{|k,v| [k.upcase,v.first]}]
330
+
331
+ if response_fields.include? "REBILL_ID"
332
+ return parse_recurring(response_fields)
344
333
  end
345
- # parse response if using other old API
346
- hash = Hash.new
347
- fields = fields.first[0].split(",")
348
- fields.each_index do |x|
349
- hash[x] = fields[x].tr('$','')
334
+
335
+ parsed = {}
336
+ response_fields.each do |k,v|
337
+ mapped_key = FIELD_MAP.include?(k) ? FIELD_MAP[k] : k
338
+ parsed[mapped_key] = v
339
+ end
340
+
341
+ # normalize message
342
+ message = message_from(parsed)
343
+ success = parsed[:response_code] == '1'
344
+ Response.new(success, message, parsed,
345
+ :test => test?,
346
+ :authorization => (parsed[:rebid] && parsed[:rebid] != '' ? parsed[:rebid] : parsed[:transaction_id]),
347
+ :fraud_review => FRAUD_REVIEW_STATUSES.include?(parsed[:response_code]),
348
+ :avs_result => { :code => parsed[:avs_result_code] },
349
+ :cvv_result => parsed[:card_code]
350
+ )
351
+ end
352
+
353
+ def message_from(parsed)
354
+ message = parsed[:message]
355
+ if(parsed[:response_code].to_i == 2)
356
+ if CARD_CODE_ERRORS.include?(parsed[:card_code])
357
+ message = CVVResult.messages[parsed[:card_code]]
358
+ elsif AVS_ERRORS.include?(parsed[:avs_result_code])
359
+ message = AVSResult.messages[ parsed[:avs_result_code] ]
360
+ else
361
+ message = message.chomp('.')
362
+ end
363
+ elsif message == "Missing ACCOUNT_ID"
364
+ message = "The merchant login ID or password is invalid"
365
+ elsif message =~ /Approved/
366
+ message = "This transaction has been approved"
367
+ elsif message =~ /Expired/
368
+ message = "The credit card has expired"
350
369
  end
351
- hash
370
+ message
352
371
  end
353
372
 
354
373
  def add_invoice(post, options)
355
- post[:ORDER_ID] = options[:order_id] if options.has_key? :order_id
356
- post[:INVOICE_ID] = options[:invoice] if options.has_key? :invoice
357
- post[:invoice_num] = options[:order_id] if options.has_key? :order_id
358
- post[:MEMO] = options[:description] if options.has_key? :description
359
- post[:description] = options[:description] if options.has_key? :description
374
+ post[:ORDER_ID] = options[:order_id]
375
+ post[:INVOICE_ID] = options[:invoice]
376
+ post[:invoice_num] = options[:order_id]
377
+ post[:MEMO] = options[:description]
378
+ post[:description] = options[:description]
379
+ end
380
+
381
+ def add_payment_method(post, payment_object)
382
+ post[:MASTER_ID] = ''
383
+ case payment_object
384
+ when String
385
+ post[:MASTER_ID] = payment_object
386
+ when Check
387
+ add_check(post, payment_object)
388
+ else
389
+ add_creditcard(post, payment_object)
390
+ end
360
391
  end
361
392
 
362
393
  def add_creditcard(post, creditcard)
363
394
  post[:PAYMENT_TYPE] = 'CREDIT'
364
395
  post[:PAYMENT_ACCOUNT] = creditcard.number
365
- post[:CARD_CVV2] = creditcard.verification_value if
366
- creditcard.verification_value?
396
+ post[:CARD_CVV2] = creditcard.verification_value
367
397
  post[:CARD_EXPIRE] = expdate(creditcard)
368
398
  post[:NAME1] = creditcard.first_name
369
399
  post[:NAME2] = creditcard.last_name
370
400
  end
371
401
 
402
+ CHECK_ACCOUNT_TYPES = {
403
+ "checking" => "C",
404
+ "savings" => "S"
405
+ }
406
+
372
407
  def add_check(post, check)
373
408
  post[:PAYMENT_TYPE] = 'ACH'
374
- post[:PAYMENT_ACCOUNT] = check.account_type + ":" + check.routing_number + ":" + check.account_number
409
+ post[:PAYMENT_ACCOUNT] = [CHECK_ACCOUNT_TYPES[check.account_type], check.routing_number, check.account_number].join(":")
375
410
  post[:NAME1] = check.first_name
376
411
  post[:NAME2] = check.last_name
377
412
  end
378
413
 
379
414
  def add_customer_data(post, options)
380
- post[:EMAIL] = options[:email] if options.has_key? :email
381
- post[:CUSTOM_ID] = options[:customer] if options.has_key? :customer
415
+ post[:EMAIL] = options[:email]
416
+ post[:CUSTOM_ID] = options[:customer]
382
417
  end
383
418
 
384
- def add_duplicate_window(post)
385
- unless duplicate_window.nil?
386
- post[:duplicate_window] = duplicate_window
387
- post[:DUPLICATE_OVERRIDE] = duplicate_window
388
- end
419
+ def add_duplicate_override(post, options)
420
+ post[:DUPLICATE_OVERRIDE] = options[:duplicate_override]
389
421
  end
390
422
 
391
423
  def add_address(post, options)
392
- if address = options[:billing_address] || options[:address]
393
- post[:NAME1] = address[:first_name]
394
- post[:NAME2] = address[:last_name]
424
+ if address = (options[:shipping_address] || options[:billing_address] || options[:address])
395
425
  post[:ADDR1] = address[:address1]
396
426
  post[:ADDR2] = address[:address2]
397
427
  post[:COMPANY_NAME] = address[:company]
398
428
  post[:PHONE] = address[:phone]
399
429
  post[:CITY] = address[:city]
400
- post[:STATE] = address[:state].blank? ? 'n/a' : address[:state]
401
- post[:ZIP] = address[:zip]
402
- post[:COUNTRY] = address[:country]
403
- end
404
- if address = options[:shipping_address]
405
- post[:NAME1] = address[:first_name]
406
- post[:NAME2] = address[:last_name]
407
- post[:ADDR1] = address[:address1]
408
- post[:ADDR1] = address[:address1]
409
- post[:COMPANY_NAME] = address[:company]
410
- post[:PHONE] = address[:phone]
430
+ post[:STATE] = (address[:state].blank? ? 'n/a' : address[:state])
411
431
  post[:ZIP] = address[:zip]
412
- post[:CITY] = address[:city]
413
432
  post[:COUNTRY] = address[:country]
414
- post[:STATE] = address[:state].blank? ? 'n/a' : address[:state]
415
433
  end
416
434
  end
417
435
 
436
+ def add_rebill(post, options)
437
+ post[:DO_REBILL] = '1'
438
+ post[:REB_AMOUNT] = amount(options[:rebill_amount])
439
+ post[:REB_FIRST_DATE] = options[:rebill_start_date]
440
+ post[:REB_EXPR] = options[:rebill_expression]
441
+ post[:REB_CYCLES] = options[:rebill_cycles]
442
+ end
443
+
418
444
  def post_data(action, parameters = {})
419
445
  post = {}
420
- post[:version] = '3.0'
446
+ post[:version] = '1'
421
447
  post[:login] = ''
422
448
  post[:tran_key] = ''
423
449
  post[:relay_response] = "FALSE"
@@ -427,60 +453,48 @@ module ActiveMerchant #:nodoc:
427
453
  post[:encap_char] = "$"
428
454
  post[:card_num] = '4111111111111111'
429
455
  post[:exp_date] = '1212'
430
- post[:solution_ID] = application_id if application_id.present? && application_id != "ActiveMerchant"
431
- request = post.merge(parameters).collect { |key, value| "#{key}=#{CGI.escape(value.to_s)}" }.join("&")
432
- request
433
- end
434
-
435
- def message_from(results)
436
- if results[:response_code] == 2
437
- return CVVResult.messages[ results[:card_code] ] if CARD_CODE_ERRORS.include?(results[:card_code])
438
- if AVS_REASON_CODES.include?(results[:response_reason_code]) && AVS_ERRORS.include?(results[:avs_result_code])
439
- return AVSResult.messages[ results[:avs_result_code] ]
440
- end
441
- return (results[:response_reason_text] ? results[:response_reason_text].chomp('.') : '')
442
- end
443
- if results.has_key?(:response_reason_text)
444
- return results[:response_reason_text].to_s()
445
- end
446
- if !results.has_key?('STATUS')
447
- return results[RESPONSE_REASON_TEXT] ? results[RESPONSE_REASON_TEXT].chomp('.') : ''
448
- end
456
+ post[:solution_ID] = application_id if(application_id && application_id != "ActiveMerchant")
457
+ post.merge(parameters).collect { |key, value| "#{key}=#{CGI.escape(value.to_s)}" }.join("&")
449
458
  end
450
459
 
451
460
  def expdate(creditcard)
452
- year = sprintf("%.4i", creditcard.year)
453
- month = sprintf("%.2i", creditcard.month)
461
+ year = format(creditcard.year, :two_digits)
462
+ month = format(creditcard.month, :two_digits)
454
463
 
455
- "#{month}#{year[-2..-1]}"
464
+ "#{month}#{year}"
456
465
  end
457
466
 
458
467
  def calc_tps(amount, post)
459
- post[:NAME1] = '' if post[:NAME1].nil?
460
- digest = Digest::MD5.hexdigest(@options[:password] +
461
- @options[:login] + post[:TRANS_TYPE] +
462
- amount.to_s() + post[:MASTER_ID].to_s() +
463
- post[:NAME1].to_s() + post[:PAYMENT_ACCOUNT].to_s())
464
- return digest
468
+ post[:NAME1] ||= ''
469
+ Digest::MD5.hexdigest(
470
+ [
471
+ @options[:password],
472
+ @options[:login],
473
+ post[:TRANS_TYPE],
474
+ amount,
475
+ post[:MASTER_ID],
476
+ post[:NAME1],
477
+ post[:PAYMENT_ACCOUNT]
478
+ ].join("")
479
+ )
465
480
  end
466
481
 
467
482
  def calc_rebill_tps(post)
468
- digest = Digest::MD5.hexdigest(@options[:password] +
469
- @options[:login] + post[:TRANS_TYPE] + post[:REBILL_ID][0].to_s())
470
- return digest
483
+ Digest::MD5.hexdigest(
484
+ [
485
+ @options[:password],
486
+ @options[:login],
487
+ post[:TRANS_TYPE],
488
+ post[:REBILL_ID]
489
+ ].join("")
490
+ )
471
491
  end
472
492
 
473
493
  def handle_response(response)
474
- if ignore_http_status then
494
+ if ignore_http_status || (200...300).include?(response.code.to_i)
475
495
  return response.body
476
- else
477
- case response.code.to_i
478
- when 200...300
479
- response.body
480
- else
481
- raise ResponseError.new(response)
482
- end
483
496
  end
497
+ raise ResponseError.new(response)
484
498
  end
485
499
  end
486
500
  end
@@ -34,7 +34,7 @@ module ActiveMerchant #:nodoc:
34
34
  money = amount(money)
35
35
  case normalize(credit_card_or_reference)
36
36
  when /1$/, AUTHORIZATION
37
- Response.new(true, SUCCESS_MESSAGE, {:paid_amount => money}, :test => true)
37
+ Response.new(true, SUCCESS_MESSAGE, {:paid_amount => money}, :test => true, :authorization => AUTHORIZATION)
38
38
  when /2$/
39
39
  Response.new(false, FAILURE_MESSAGE, {:paid_amount => money, :error => FAILURE_MESSAGE },:test => true)
40
40
  else
@@ -68,8 +68,10 @@ module ActiveMerchant #:nodoc:
68
68
  Braintree::Configuration.custom_user_agent = "ActiveMerchant #{ActiveMerchant::VERSION}"
69
69
 
70
70
  if wiredump_device
71
- Braintree::Configuration.logger = wiredump_device
71
+ Braintree::Configuration.logger = ((Logger === wiredump_device) ? wiredump_device : Logger.new(wiredump_device))
72
72
  Braintree::Configuration.logger.level = Logger::DEBUG
73
+ else
74
+ Braintree::Configuration.logger.level = Logger::WARN
73
75
  end
74
76
  end
75
77
 
@@ -135,7 +137,8 @@ module ActiveMerchant #:nodoc:
135
137
  {
136
138
  :braintree_customer => (customer_hash(result.customer) if result.success?),
137
139
  :customer_vault_id => (result.customer.id if result.success?)
138
- }
140
+ },
141
+ :authorization => (result.customer.id if result.success?)
139
142
  )
140
143
  end
141
144
  end
@@ -169,7 +172,7 @@ module ActiveMerchant #:nodoc:
169
172
  end
170
173
  end
171
174
 
172
- def unstore(customer_vault_id)
175
+ def unstore(customer_vault_id, options = {})
173
176
  commit do
174
177
  Braintree::Customer.delete(customer_vault_id)
175
178
  Response.new(true, "OK")
@@ -337,6 +340,7 @@ module ActiveMerchant #:nodoc:
337
340
  "bin" => transaction.credit_card_details.bin,
338
341
  "last_4" => transaction.credit_card_details.last_4,
339
342
  "card_type" => transaction.credit_card_details.card_type,
343
+ "token" => transaction.credit_card_details.token
340
344
  }
341
345
 
342
346
  {