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
@@ -37,7 +37,7 @@ module ActiveMerchant #:nodoc:
37
37
  self.test_url = 'https://www.testlitle.com/sandbox/communicator/online'
38
38
  self.live_url = 'https://payments.litle.com/vap/communicator/online'
39
39
 
40
- LITLE_SCHEMA_VERSION = '8.10'
40
+ LITLE_SCHEMA_VERSION = '8.13'
41
41
 
42
42
  # The countries the gateway supports merchants from as 2 digit ISO country codes
43
43
  self.supported_countries = ['US']
@@ -46,10 +46,10 @@ module ActiveMerchant #:nodoc:
46
46
  self.supported_cardtypes = [:visa, :master, :american_express, :discover, :diners_club, :jcb]
47
47
 
48
48
  # The homepage URL of the gateway
49
- self.homepage_url = 'http://www.litle.com/'
49
+ self.homepage_url = 'http://www.litle.com/'
50
50
 
51
51
  # The name of the gateway
52
- self.display_name = 'Litle & Co.'
52
+ self.display_name = 'Litle & Co.'
53
53
 
54
54
  self.default_currency = 'USD'
55
55
 
@@ -71,63 +71,76 @@ module ActiveMerchant #:nodoc:
71
71
  super
72
72
  end
73
73
 
74
- def authorize(money, creditcard, options = {})
75
- to_pass = create_credit_card_hash(money, creditcard, options)
74
+ def authorize(money, creditcard_or_token, options = {})
75
+ to_pass = build_authorize_request(money, creditcard_or_token, options)
76
76
  build_response(:authorization, @litle.authorization(to_pass))
77
77
  end
78
78
 
79
- def purchase(money, creditcard, options = {})
80
- to_pass = create_credit_card_hash(money, creditcard, options)
79
+ def purchase(money, creditcard_or_token, options = {})
80
+ to_pass = build_purchase_request(money, creditcard_or_token, options)
81
81
  build_response(:sale, @litle.sale(to_pass))
82
82
  end
83
83
 
84
84
  def capture(money, authorization, options = {})
85
- to_pass = create_capture_hash(money, authorization, options)
85
+ transaction_id, kind = split_authorization(authorization)
86
+ to_pass = create_capture_hash(money, transaction_id, options)
86
87
  build_response(:capture, @litle.capture(to_pass))
87
88
  end
88
89
 
90
+ # Note: Litle requires that authorization requests be voided via auth_reversal
91
+ # and other requests via void. To maintain the same interface as the other
92
+ # gateways the transaction_id and the kind of transaction are concatenated
93
+ # together with a ; separator (e.g. 1234;authorization)
94
+ #
95
+ # A partial auth_reversal can be accomplished by passing :amount as an option
89
96
  def void(identification, options = {})
90
- to_pass = create_void_hash(identification, options)
91
- build_response(:void, @litle.void(to_pass))
97
+ transaction_id, kind = split_authorization(identification)
98
+ if(kind == 'authorization')
99
+ to_pass = create_auth_reversal_hash(transaction_id, options[:amount], options)
100
+ build_response(:authReversal, @litle.auth_reversal(to_pass))
101
+ else
102
+ to_pass = create_void_hash(transaction_id, options)
103
+ build_response(:void, @litle.void(to_pass))
104
+ end
92
105
  end
93
106
 
94
- def credit(money, identification, options = {})
95
- to_pass = create_credit_hash(money, identification, options)
107
+ def credit(money, identification_or_token, options = {})
108
+ to_pass = build_credit_request(money, identification_or_token, options)
96
109
  build_response(:credit, @litle.credit(to_pass))
97
110
  end
98
111
 
99
112
  def store(creditcard, options = {})
100
113
  to_pass = create_token_hash(creditcard, options)
101
- build_response(:registerToken, @litle.register_token_request(to_pass), %w(801 802))
114
+ build_response(:registerToken, @litle.register_token_request(to_pass), %w(000 801 802))
102
115
  end
103
116
 
104
117
  private
105
118
 
106
119
  CARD_TYPE = {
107
- 'visa' => 'VI',
108
- 'master' => 'MC',
109
- 'american_express' => 'AX',
110
- 'discover' => 'DI',
111
- 'jcb' => 'DI',
112
- 'diners_club' => 'DI'
120
+ 'visa' => 'VI',
121
+ 'master' => 'MC',
122
+ 'american_express' => 'AX',
123
+ 'discover' => 'DI',
124
+ 'jcb' => 'DI',
125
+ 'diners_club' => 'DI'
113
126
  }
114
127
 
115
128
  AVS_RESPONSE_CODE = {
116
- '00' => 'Y',
117
- '01' => 'X',
118
- '02' => 'D',
119
- '10' => 'Z',
120
- '11' => 'W',
121
- '12' => 'A',
122
- '13' => 'A',
123
- '14' => 'P',
124
- '20' => 'N',
125
- '30' => 'S',
126
- '31' => 'R',
127
- '32' => 'U',
128
- '33' => 'R',
129
- '34' => 'I',
130
- '40' => 'E'
129
+ '00' => 'Y',
130
+ '01' => 'X',
131
+ '02' => 'D',
132
+ '10' => 'Z',
133
+ '11' => 'W',
134
+ '12' => 'A',
135
+ '13' => 'A',
136
+ '14' => 'P',
137
+ '20' => 'N',
138
+ '30' => 'S',
139
+ '31' => 'R',
140
+ '32' => 'U',
141
+ '33' => 'R',
142
+ '34' => 'I',
143
+ '40' => 'E'
131
144
  }
132
145
 
133
146
  def url
@@ -140,69 +153,153 @@ module ActiveMerchant #:nodoc:
140
153
  response = Hash.from_xml(litle_response.raw_xml.to_s)['litleOnlineResponse']
141
154
 
142
155
  if response['response'] == "0"
143
- detail = response["#{kind}Response"]
144
- fraud = fraud_result(detail)
156
+ detail = response["#{kind}Response"]
157
+ fraud = fraud_result(detail)
145
158
  Response.new(
146
- valid_responses.include?(detail['response']),
147
- detail['message'],
148
- {:litleOnlineResponse => response},
149
- :authorization => detail['litleTxnId'],
150
- :avs_result => {:code => fraud['avs']},
151
- :cvv_result => fraud['cvv'],
152
- :test => test?
159
+ valid_responses.include?(detail['response']),
160
+ detail['message'],
161
+ { :litleOnlineResponse => response },
162
+ :authorization => authorization_from(detail, kind),
163
+ :avs_result => { :code => fraud['avs'] },
164
+ :cvv_result => fraud['cvv'],
165
+ :test => test?
153
166
  )
154
167
  else
155
168
  Response.new(false, response['message'], :litleOnlineResponse => response, :test => test?)
156
169
  end
157
170
  end
158
171
 
159
- def create_credit_card_hash(money, creditcard, options)
160
- cc_type = CARD_TYPE[creditcard.brand]
172
+ # Generates an authorization string of the appropriate id and the kind of transaction
173
+ # See #void for how the kind is used
174
+ def authorization_from(litle_response, kind)
175
+ case kind
176
+ when :registerToken
177
+ authorization = litle_response['litleToken']
178
+ else
179
+ authorization = [litle_response['litleTxnId'], kind.to_s].join(";")
180
+ end
181
+ end
182
+
183
+ def split_authorization(authorization)
184
+ transaction_id, kind = authorization.to_s.split(';')
185
+ [transaction_id, kind]
186
+ end
187
+
188
+ def build_authorize_request(money, creditcard_or_token, options)
189
+ payment_method = build_payment_method(creditcard_or_token, options)
190
+
191
+ hash = create_hash(money, options)
192
+
193
+ add_creditcard_or_cardtoken_hash(hash, payment_method)
194
+
195
+ hash
196
+ end
197
+
198
+ def build_purchase_request(money, creditcard_or_token, options)
199
+ payment_method = build_payment_method(creditcard_or_token, options)
200
+
201
+ hash = create_hash(money, options)
202
+
203
+ add_creditcard_or_cardtoken_hash(hash, payment_method)
204
+
205
+ hash
206
+ end
207
+
208
+ def build_credit_request(money, identification_or_token, options)
209
+ payment_method = build_payment_method(identification_or_token, options)
210
+
211
+ hash = create_hash(money, options)
212
+
213
+ add_identification_or_cardtoken_hash(hash, payment_method)
214
+
215
+ unless payment_method.is_a?(LitleCardToken)
216
+ hash['orderSource'] = nil
217
+ hash['orderId'] = nil
218
+ end
219
+
220
+ hash
221
+ end
222
+
223
+ def build_payment_method(payment_method, options)
224
+ result = payment_method
225
+
226
+ # Build instance of the LitleCardToken class for internal use if this is a token request.
227
+ if payment_method.is_a?(String) && options.has_key?(:token)
228
+ result = LitleCardToken.new(:token => payment_method)
229
+ result.month = options[:token][:month]
230
+ result.year = options[:token][:year]
231
+ result.verification_value = options[:token][:verification_value]
232
+ result.brand = options[:token][:brand]
233
+ end
234
+
235
+ result
236
+ end
161
237
 
162
- exp_date_yr = creditcard.year.to_s()[2..3]
238
+ def add_creditcard_or_cardtoken_hash(hash, creditcard_or_cardtoken)
239
+ if creditcard_or_cardtoken.is_a?(LitleCardToken)
240
+ add_cardtoken_hash(hash, creditcard_or_cardtoken)
241
+ else
242
+ add_creditcard_hash(hash, creditcard_or_cardtoken)
243
+ end
244
+ end
163
245
 
164
- if( creditcard.month.to_s().length == 1 )
165
- exp_date_mo = '0' + creditcard.month.to_s()
246
+ def add_identification_or_cardtoken_hash(hash, identification_or_cardtoken)
247
+ if identification_or_cardtoken.is_a?(LitleCardToken)
248
+ add_cardtoken_hash(hash, identification_or_cardtoken)
166
249
  else
167
- exp_date_mo = creditcard.month.to_s()
250
+ transaction_id, kind = split_authorization(identification_or_cardtoken)
251
+ hash['litleTxnId'] = transaction_id
168
252
  end
253
+ end
254
+
255
+ def add_cardtoken_hash(hash, cardtoken)
256
+ token_info = {}
257
+ token_info['litleToken'] = cardtoken.token
258
+ token_info['expDate'] = cardtoken.exp_date if cardtoken.exp_date?
259
+ token_info['cardValidationNum'] = cardtoken.verification_value unless cardtoken.verification_value.blank?
260
+ token_info['type'] = cardtoken.type unless cardtoken.type.blank?
261
+
262
+ hash['token'] = token_info
263
+ hash
264
+ end
169
265
 
170
- exp_date = exp_date_mo + exp_date_yr
266
+ def add_creditcard_hash(hash, creditcard)
267
+ cc_type = CARD_TYPE[creditcard.brand]
268
+ exp_date_yr = creditcard.year.to_s[2..3]
269
+ exp_date_mo = '%02d' % creditcard.month.to_i
270
+ exp_date = exp_date_mo + exp_date_yr
171
271
 
172
272
  card_info = {
173
- 'type' => cc_type,
174
- 'number' => creditcard.number,
175
- 'expDate' => exp_date,
176
- 'cardValidationNum' => creditcard.verification_value
273
+ 'type' => cc_type,
274
+ 'number' => creditcard.number,
275
+ 'expDate' => exp_date,
276
+ 'cardValidationNum' => creditcard.verification_value
177
277
  }
178
278
 
179
- hash = create_hash(money, options)
180
279
  hash['card'] = card_info
181
280
  hash
182
281
  end
183
282
 
184
283
  def create_capture_hash(money, authorization, options)
185
- hash = create_hash(money, options)
284
+ hash = create_hash(money, options)
186
285
  hash['litleTxnId'] = authorization
187
286
  hash
188
287
  end
189
288
 
190
- def create_credit_hash(money, identification, options)
191
- hash = create_hash(money, options)
192
- hash['litleTxnId'] = identification
193
- hash['orderSource'] = nil
194
- hash['orderId'] = nil
195
- hash
196
- end
197
-
198
289
  def create_token_hash(creditcard, options)
199
- hash = create_hash(0, options)
290
+ hash = create_hash(0, options)
200
291
  hash['accountNumber'] = creditcard.number
201
292
  hash
202
293
  end
203
294
 
204
295
  def create_void_hash(identification, options)
205
- hash = create_hash(nil, options)
296
+ hash = create_hash(nil, options)
297
+ hash['litleTxnId'] = identification
298
+ hash
299
+ end
300
+
301
+ def create_auth_reversal_hash(identification, money, options)
302
+ hash = create_hash(money, options)
206
303
  hash['litleTxnId'] = identification
207
304
  hash
208
305
  end
@@ -224,54 +321,54 @@ module ActiveMerchant #:nodoc:
224
321
 
225
322
  if options[:billing_address]
226
323
  bill_to_address = {
227
- 'name' => options[:billing_address][:name],
228
- 'companyName' => options[:billing_address][:company],
229
- 'addressLine1' => options[:billing_address][:address1],
230
- 'addressLine2' => options[:billing_address][:address2],
231
- 'city' => options[:billing_address][:city],
232
- 'state' => options[:billing_address][:state],
233
- 'zip' => options[:billing_address][:zip],
234
- 'country' => options[:billing_address][:country],
235
- 'email' => options[:email],
236
- 'phone' => options[:billing_address][:phone]
324
+ 'name' => options[:billing_address][:name],
325
+ 'companyName' => options[:billing_address][:company],
326
+ 'addressLine1' => options[:billing_address][:address1],
327
+ 'addressLine2' => options[:billing_address][:address2],
328
+ 'city' => options[:billing_address][:city],
329
+ 'state' => options[:billing_address][:state],
330
+ 'zip' => options[:billing_address][:zip],
331
+ 'country' => options[:billing_address][:country],
332
+ 'email' => options[:email],
333
+ 'phone' => options[:billing_address][:phone]
237
334
  }
238
335
  end
239
336
  if options[:shipping_address]
240
337
  ship_to_address = {
241
- 'name' => options[:shipping_address][:name],
242
- 'companyName' => options[:shipping_address][:company],
243
- 'addressLine1' => options[:shipping_address][:address1],
244
- 'addressLine2' => options[:shipping_address][:address2],
245
- 'city' => options[:shipping_address][:city],
246
- 'state' => options[:shipping_address][:state],
247
- 'zip' => options[:shipping_address][:zip],
248
- 'country' => options[:shipping_address][:country],
249
- 'email' => options[:email],
250
- 'phone' => options[:shipping_address][:phone]
338
+ 'name' => options[:shipping_address][:name],
339
+ 'companyName' => options[:shipping_address][:company],
340
+ 'addressLine1' => options[:shipping_address][:address1],
341
+ 'addressLine2' => options[:shipping_address][:address2],
342
+ 'city' => options[:shipping_address][:city],
343
+ 'state' => options[:shipping_address][:state],
344
+ 'zip' => options[:shipping_address][:zip],
345
+ 'country' => options[:shipping_address][:country],
346
+ 'email' => options[:email],
347
+ 'phone' => options[:shipping_address][:phone]
251
348
  }
252
349
  end
253
350
 
254
351
  hash = {
255
- 'billToAddress' => bill_to_address,
256
- 'shipToAddress' => ship_to_address,
257
- 'orderId' => (options[:order_id] || @options[:order_id]),
258
- 'customerId' => options[:customer],
259
- 'reportGroup' => (options[:merchant] || @options[:merchant]),
260
- 'merchantId' => (options[:merchant_id] || @options[:merchant_id]),
261
- 'orderSource' => 'ecommerce',
262
- 'enhancedData' => enhanced_data,
263
- 'fraudCheckType' => fraud_check_type,
264
- 'user' => (options[:user] || @options[:user]),
265
- 'password' => (options[:password] || @options[:password]),
266
- 'version' => (options[:version] || @options[:version]),
267
- 'url' => (options[:url] || url),
268
- 'proxy_addr' => (options[:proxy_addr] || @options[:proxy_addr]),
269
- 'proxy_port' => (options[:proxy_port] || @options[:proxy_port]),
270
- 'id' => (options[:id] || options[:order_id] || @options[:order_id])
352
+ 'billToAddress' => bill_to_address,
353
+ 'shipToAddress' => ship_to_address,
354
+ 'orderId' => (options[:order_id] || @options[:order_id]),
355
+ 'customerId' => options[:customer],
356
+ 'reportGroup' => (options[:merchant] || @options[:merchant]),
357
+ 'merchantId' => (options[:merchant_id] || @options[:merchant_id]),
358
+ 'orderSource' => (options[:order_source] || 'ecommerce'),
359
+ 'enhancedData' => enhanced_data,
360
+ 'fraudCheckType' => fraud_check_type,
361
+ 'user' => (options[:user] || @options[:user]),
362
+ 'password' => (options[:password] || @options[:password]),
363
+ 'version' => (options[:version] || @options[:version]),
364
+ 'url' => (options[:url] || url),
365
+ 'proxy_addr' => (options[:proxy_addr] || @options[:proxy_addr]),
366
+ 'proxy_port' => (options[:proxy_port] || @options[:proxy_port]),
367
+ 'id' => (options[:id] || options[:order_id] || @options[:order_id])
271
368
  }
272
369
 
273
- if( !money.nil? && money.to_s.length > 0 )
274
- hash.merge!({'amount' => money})
370
+ if (!money.nil? && money.to_s.length > 0)
371
+ hash.merge!({ 'amount' => money })
275
372
  end
276
373
  hash
277
374
  end
@@ -284,7 +381,159 @@ module ActiveMerchant #:nodoc:
284
381
 
285
382
  avs_to_pass = AVS_RESPONSE_CODE[result['avsResult']] unless result['avsResult'].blank?
286
383
  end
287
- {'cvv'=>cvv_to_pass, 'avs'=>avs_to_pass}
384
+ { 'cvv' => cvv_to_pass, 'avs' => avs_to_pass }
385
+ end
386
+
387
+ # A +LitleCardToken+ object represents a tokenized credit card, and is capable of validating the various
388
+ # data associated with these.
389
+ #
390
+ # == Example Usage
391
+ # token = LitleCardToken.new(
392
+ # :token => '1234567890123456',
393
+ # :month => '9',
394
+ # :year => '2010',
395
+ # :brand => 'visa',
396
+ # :verification_value => '123'
397
+ # )
398
+ #
399
+ # token.valid? # => true
400
+ # cc.exp_date # => 0910
401
+ #
402
+ class LitleCardToken
403
+ include Validateable
404
+
405
+ # Returns or sets the token. (required)
406
+ #
407
+ # @return [String]
408
+ attr_accessor :token
409
+
410
+ # Returns or sets the expiry month for the card associated with token. (optional)
411
+ #
412
+ # @return [Integer]
413
+ attr_accessor :month
414
+
415
+ # Returns or sets the expiry year for the card associated with token. (optional)
416
+ #
417
+ # @return [Integer]
418
+ attr_accessor :year
419
+
420
+ # Returns or sets the card verification value. (optional)
421
+ #
422
+ # @return [String] the verification value
423
+ attr_accessor :verification_value
424
+
425
+ # Returns or sets the credit card brand. (optional)
426
+ #
427
+ # Valid card types are
428
+ #
429
+ # * +'visa'+
430
+ # * +'master'+
431
+ # * +'discover'+
432
+ # * +'american_express'+
433
+ # * +'diners_club'+
434
+ # * +'jcb'+
435
+ # * +'switch'+
436
+ # * +'solo'+
437
+ # * +'dankort'+
438
+ # * +'maestro'+
439
+ # * +'forbrugsforeningen'+
440
+ # * +'laser'+
441
+ #
442
+ # @return (String) the credit card brand
443
+ attr_accessor :brand
444
+
445
+ # Returns the Litle credit card type identifier.
446
+ #
447
+ # @return (String) the credit card type identifier
448
+ def type
449
+ CARD_TYPE[brand] unless brand.blank?
450
+ end
451
+
452
+ # Returns true if the expiration date is set.
453
+ #
454
+ # @return (Boolean)
455
+ def exp_date?
456
+ !month.to_i.zero? && !year.to_i.zero?
457
+ end
458
+
459
+ # Returns the card token expiration date in MMYY format.
460
+ #
461
+ # @return (String) the expiration date in MMYY format
462
+ def exp_date
463
+ result = ''
464
+ if exp_date?
465
+ exp_date_yr = year.to_s[2..3]
466
+ exp_date_mo = '%02d' % month.to_i
467
+
468
+ result = exp_date_mo + exp_date_yr
469
+ end
470
+ result
471
+ end
472
+
473
+ # Validates the card token details.
474
+ #
475
+ # Any validation errors are added to the {#errors} attribute.
476
+ def validate
477
+ validate_card_token
478
+ validate_expiration_date
479
+ validate_card_brand
480
+ end
481
+
482
+ def check?
483
+ false
484
+ end
485
+
486
+ private
487
+
488
+ CARD_TYPE = {
489
+ 'visa' => 'VI',
490
+ 'master' => 'MC',
491
+ 'american_express' => 'AX',
492
+ 'discover' => 'DI',
493
+ 'jcb' => 'DI',
494
+ 'diners_club' => 'DI'
495
+ }
496
+
497
+ def before_validate #:nodoc:
498
+ self.month = month.to_i
499
+ self.year = year.to_i
500
+ end
501
+
502
+ # Litle XML Reference Guide 1.8.2
503
+ #
504
+ # The length of the original card number is reflected in the token, so a
505
+ # submitted 16-digit number results in a 16-digit token. Also, all tokens
506
+ # use only numeric characters, so you do not have to change your
507
+ # systems to accept alpha-numeric characters.
508
+ #
509
+ # The credit card token numbers themselves have two parts.
510
+ # The last four digits match the last four digits of the card number.
511
+ # The remaining digits (length can vary based upon original card number
512
+ # length) are a randomly generated.
513
+ def validate_card_token #:nodoc:
514
+ if token.to_s.length < 12 || token.to_s.match(/\A\d+\Z/).nil?
515
+ errors.add :token, "is not a valid card token"
516
+ end
517
+ end
518
+
519
+ def validate_expiration_date #:nodoc:
520
+ if !month.to_i.zero? || !year.to_i.zero?
521
+ errors.add :month, "is not a valid month" unless valid_month?(month)
522
+ errors.add :year, "is not a valid year" unless valid_expiry_year?(year)
523
+ end
524
+ end
525
+
526
+ def validate_card_brand #:nodoc:
527
+ errors.add :brand, "is invalid" unless brand.blank? || CreditCard.card_companies.keys.include?(brand)
528
+ end
529
+
530
+ def valid_month?(month)
531
+ (1..12).include?(month.to_i)
532
+ end
533
+
534
+ def valid_expiry_year?(year)
535
+ year.to_s =~ /\A\d{4}\Z/ && year.to_i > 1987
536
+ end
288
537
  end
289
538
  end
290
539
  end
@@ -23,6 +23,8 @@ module ActiveMerchant #:nodoc:
23
23
 
24
24
  def authorize(money, creditcard_or_card_id, options = {})
25
25
  post = {}
26
+ post[:client_reference_number] = options[:customer] if options.has_key?(:customer)
27
+ post[:moto_ecommerce_ind] = options[:moto_ecommerce_ind] if options.has_key?(:moto_ecommerce_ind)
26
28
  add_invoice(post, options)
27
29
  add_payment_source(post, creditcard_or_card_id, options)
28
30
  add_address(post, options)
@@ -31,6 +33,8 @@ module ActiveMerchant #:nodoc:
31
33
 
32
34
  def purchase(money, creditcard_or_card_id, options = {})
33
35
  post = {}
36
+ post[:client_reference_number] = options[:customer] if options.has_key?(:customer)
37
+ post[:moto_ecommerce_ind] = options[:moto_ecommerce_ind] if options.has_key?(:moto_ecommerce_ind)
34
38
  add_invoice(post, options)
35
39
  add_payment_source(post, creditcard_or_card_id, options)
36
40
  add_address(post, options)
@@ -40,33 +44,48 @@ module ActiveMerchant #:nodoc:
40
44
  def capture(money, transaction_id, options = {})
41
45
  post ={}
42
46
  post[:transaction_id] = transaction_id
47
+ post[:client_reference_number] = options[:customer] if options.has_key?(:customer)
43
48
  commit('S', money, post)
44
49
  end
45
50
 
46
51
  def store(creditcard, options = {})
47
52
  post = {}
53
+ post[:client_reference_number] = options[:customer] if options.has_key?(:customer)
48
54
  add_creditcard(post, creditcard, options)
49
55
  commit('T', nil, post)
50
56
  end
51
57
 
52
- def unstore(card_id)
58
+ def unstore(card_id, options = {})
53
59
  post = {}
60
+ post[:client_reference_number] = options[:customer] if options.has_key?(:customer)
54
61
  post[:card_id] = card_id
55
62
  commit('X', nil, post)
56
63
  end
57
64
 
58
65
  def refund(money, identification, options = {})
59
- commit('U', money, options.merge(:transaction_id => identification))
66
+ post = {}
67
+ post[:transaction_id] = identification
68
+ post[:client_reference_number] = options[:customer] if options.has_key?(:customer)
69
+ options.delete(:customer)
70
+ options.delete(:billing_address)
71
+ commit('U', money, options.merge(post))
60
72
  end
61
73
 
62
74
  def credit(money, creditcard_or_card_id, options = {})
63
75
  post = {}
76
+ post[:client_reference_number] = options[:customer] if options.has_key?(:customer)
77
+ add_invoice(post, options)
64
78
  add_payment_source(post, creditcard_or_card_id, options)
65
79
  commit('C', money, post)
66
80
  end
67
81
 
68
82
  def void(transaction_id, options = {})
69
- commit('V', nil, options.merge(:transaction_id => transaction_id))
83
+ post = {}
84
+ post[:transaction_id] = transaction_id
85
+ post[:client_reference_number] = options[:customer] if options.has_key?(:customer)
86
+ options.delete(:customer)
87
+ options.delete(:billing_address)
88
+ commit('V', nil, options.merge(post))
70
89
  end
71
90
 
72
91
  private
@@ -111,11 +130,15 @@ module ActiveMerchant #:nodoc:
111
130
  end
112
131
 
113
132
  def commit(action, money, parameters)
114
-
115
133
  url = test? ? self.test_url : self.live_url
116
134
  parameters[:transaction_amount] = amount(money) if money unless action == 'V'
117
135
 
118
- response = parse( ssl_post(url, post_data(action,parameters)) )
136
+
137
+ response = begin
138
+ parse( ssl_post(url, post_data(action,parameters)) )
139
+ rescue ActiveMerchant::ResponseError => e
140
+ { "error_code" => "404", "auth_response_text" => e.to_s }
141
+ end
119
142
 
120
143
  Response.new(response["error_code"] == "000", message_from(response), response,
121
144
  :authorization => response["transaction_id"],
@@ -123,7 +146,6 @@ module ActiveMerchant #:nodoc:
123
146
  :cvv_result => response["cvv2_result"],
124
147
  :avs_result => { :code => response["avs_result"] }
125
148
  )
126
-
127
149
  end
128
150
 
129
151
  def expdate(creditcard)
@@ -152,4 +174,3 @@ module ActiveMerchant #:nodoc:
152
174
  end
153
175
  end
154
176
  end
155
-