activemerchant 1.42.7 → 1.42.8

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,15 +1,7 @@
1
1
  ---
2
- !binary "U0hBMQ==":
3
- metadata.gz: !binary |-
4
- NDRjYjY2NmNjZWUyNDMwMjdhOTZjOGMyMzJjOTUxYWMxNjFlNjVkNw==
5
- data.tar.gz: !binary |-
6
- ZTFjN2I2ZjQ3ZTdkZTIwZWY0ZGY0OTFhNzhjOTkwYWIxNDFlYTI3OA==
2
+ SHA1:
3
+ metadata.gz: 81edb8d2513f065dd9170ead1d0d6f72d4fc39c8
4
+ data.tar.gz: b14933f6c29ebd5ed555e12238a29828a959687a
7
5
  SHA512:
8
- metadata.gz: !binary |-
9
- OTZiMWRlNzE0OWZmOWQwZjk2NjNlZmJjYmYxOGNhMWIzMjk4ZDAxMDg5MjZk
10
- Y2I3ZDVhYjkzZDA4NTAyYjk0ZDU3ZjZkODAyMmEzOWVjMjg1OTJlNmRiYzg4
11
- YTQ5YTc4ZGRjMmVmZGU5N2ZlMWI0MjdiNmRjZjUyZmVlN2NhMzU=
12
- data.tar.gz: !binary |-
13
- YTY3ZGViYTg2MTIxOTFhZmFhMzE5Y2JiZmYxNzZlMzMzMDJhNDYwNTA1OTE0
14
- ZDEzNWQ1OGYyZjJkOTM5ODE0OGViNGFiNmFmNjg3NDczMmQyOTI1OWYyMDZi
15
- ZTZjYzVkZWM5ZTFhNzhkOGU4MTc3YWY1NGI3OWMyM2Q0OGIzYTA=
6
+ metadata.gz: 9350271f4a9016fd5c8d0113def4e0ec6a654578d63d824b07647a59128110f6b9bd2a33c3205ad2c89debe1b013b555d08cbd51e68b2333a24a0f98a7a20326
7
+ data.tar.gz: 3ce7cf004ee44048d02e6805dd2b63808c104a6cad0d42980246707fb8d50a302d4798c63ced00a2ff7526bedf93e84dfd88ebf2adac2326f10a5a51666bc32c
Binary file
data.tar.gz.sig CHANGED
@@ -1,3 +1,2 @@
1
- <v;�5Z����gw��@��>u�O5�h~�����P���n*L�%_��D�,c�g�`v-v����!��!>7f2s���f���B!
2
- O �,��i���Ӛ�@�%k�Ĩs}E|@�o�g��R��L9l0.7j��p����}V
3
- ,�!8TD)�(�J3��"��;�"i�l���5�B�
1
+ e��kM����6Q�)Q ��*ki-1P������{��2\v2����68B��kU�?i�� ��]CE1m����\�3�׵Jh�`�T�4���U���n�œM������B�S֌����u�zL�u������e2Q�b"ͯu��7<��S���^A-��j�n+/}n/U.R �'>>lޡvy� �o�>���?i#]Q�\$z
2
+ ��c��׵�"P����q�x �i�[(Wau�[��t)J�(Tx���;
data/CHANGELOG CHANGED
@@ -1,5 +1,15 @@
1
1
  = ActiveMerchant CHANGELOG
2
2
 
3
+ == Version 1.42.8 (April 4, 2014)
4
+
5
+ * Cecabank: Handle invalid xml response body [duff]
6
+ * Wirecard: Capture error code in the response [duff]
7
+ * Litle: Remove gem dependency [duff]
8
+ * Litle: Fix case of missing address parts [duff]
9
+ * Universal: Add universal offsite API implementation [bslobodin]
10
+ * Iridium: Add more currencies [bslobodin]
11
+ * iDeal: Add Mollie iDeal offsite implementation [wvanbergen, maartenvg]
12
+
3
13
  == Version 1.42.7 (March 18, 2014)
4
14
 
5
15
  * SagePay: Add support for ReferrerID [markabe]
@@ -113,30 +113,24 @@ module ActiveMerchant #:nodoc:
113
113
  root = REXML::Document.new(body).root
114
114
 
115
115
  response[:success] = (root.attributes['valor'] == "OK")
116
-
117
- #common params to all responses
118
116
  response[:date] = root.attributes['fecha']
119
117
  response[:operation_number] = root.attributes['numeroOperacion']
120
118
  response[:message] = root.attributes['valor']
121
119
 
122
- #success
123
120
  if root.elements['OPERACION']
124
121
  response[:operation_type] = root.elements['OPERACION'].attributes['tipo']
125
122
  response[:amount] = root.elements['OPERACION/importe'].text.strip
126
123
  end
127
124
 
128
- #optional params
129
125
  response[:description] = root.elements['OPERACION/descripcion'].text if root.elements['OPERACION/descripcion']
130
126
  response[:authorization_number] = root.elements['OPERACION/numeroAutorizacion'].text if root.elements['OPERACION/numeroAutorizacion']
131
127
  response[:reference] = root.elements['OPERACION/referencia'].text if root.elements['OPERACION/referencia']
132
128
  response[:pan] = root.elements['OPERACION/pan'].text if root.elements['OPERACION/pan']
133
129
 
134
130
  if root.elements['ERROR']
135
- #error
136
131
  response[:error_code] = root.elements['ERROR/codigo'].text
137
132
  response[:error_message] = root.elements['ERROR/descripcion'].text
138
133
  else
139
- #authorization
140
134
  if("000" == root.elements['OPERACION'].attributes['numeroOperacion'])
141
135
  if(root.elements['OPERACION/numeroAutorizacion'])
142
136
  response[:authorization] = root.elements['OPERACION/numeroAutorizacion'].text
@@ -146,6 +140,12 @@ module ActiveMerchant #:nodoc:
146
140
  end
147
141
  end
148
142
 
143
+ return response
144
+
145
+ rescue REXML::ParseException => e
146
+ response[:success] = false
147
+ response[:message] = "Unable to parse the response."
148
+ response[:error_message] = e.message
149
149
  response
150
150
  end
151
151
 
@@ -24,13 +24,186 @@ module ActiveMerchant #:nodoc:
24
24
  self.display_name = 'Iridium'
25
25
 
26
26
  CURRENCY_CODES = {
27
+ "AED" => '784',
28
+ "AFN" => '971',
29
+ "ALL" => '008',
30
+ "AMD" => '051',
31
+ "ANG" => '532',
32
+ "AOA" => '973',
33
+ "ARS" => '032',
27
34
  "AUD" => '036',
35
+ "AWG" => '533',
36
+ "AZN" => '944',
37
+ "BAM" => '977',
38
+ "BBD" => '052',
39
+ "BDT" => '050',
40
+ "BGN" => '975',
41
+ "BHD" => '048',
42
+ "BIF" => '108',
43
+ "BMD" => '060',
44
+ "BND" => '096',
45
+ "BOB" => '068',
46
+ "BOV" => '984',
47
+ "BRL" => '986',
48
+ "BSD" => '044',
49
+ "BTN" => '064',
50
+ "BWP" => '072',
51
+ "BYR" => '974',
52
+ "BZD" => '084',
28
53
  "CAD" => '124',
54
+ "CDF" => '976',
55
+ "CHE" => '947',
56
+ "CHF" => '756',
57
+ "CHW" => '948',
58
+ "CLF" => '990',
59
+ "CLP" => '152',
60
+ "CNY" => '156',
61
+ "COP" => '170',
62
+ "COU" => '970',
63
+ "CRC" => '188',
64
+ "CUP" => '192',
65
+ "CVE" => '132',
66
+ "CYP" => '196',
67
+ "CZK" => '203',
68
+ "DJF" => '262',
69
+ "DKK" => '208',
70
+ "DOP" => '214',
71
+ "DZD" => '012',
72
+ "EEK" => '233',
73
+ "EGP" => '818',
74
+ "ERN" => '232',
75
+ "ETB" => '230',
29
76
  "EUR" => '978',
77
+ "FJD" => '242',
78
+ "FKP" => '238',
30
79
  "GBP" => '826',
80
+ "GEL" => '981',
81
+ "GHS" => '288',
82
+ "GIP" => '292',
83
+ "GMD" => '270',
84
+ "GNF" => '324',
85
+ "GTQ" => '320',
86
+ "GYD" => '328',
87
+ "HKD" => '344',
88
+ "HNL" => '340',
89
+ "HRK" => '191',
90
+ "HTG" => '332',
91
+ "HUF" => '348',
92
+ "IDR" => '360',
93
+ "ILS" => '376',
94
+ "INR" => '356',
95
+ "IQD" => '368',
96
+ "IRR" => '364',
97
+ "ISK" => '352',
98
+ "JMD" => '388',
99
+ "JOD" => '400',
100
+ "JPY" => '392',
101
+ "KES" => '404',
102
+ "KGS" => '417',
103
+ "KHR" => '116',
104
+ "KMF" => '174',
105
+ "KPW" => '408',
106
+ "KRW" => '410',
107
+ "KWD" => '414',
108
+ "KYD" => '136',
109
+ "KZT" => '398',
110
+ "LAK" => '418',
111
+ "LBP" => '422',
112
+ "LKR" => '144',
113
+ "LRD" => '430',
114
+ "LSL" => '426',
115
+ "LTL" => '440',
116
+ "LVL" => '428',
117
+ "LYD" => '434',
118
+ "MAD" => '504',
119
+ "MDL" => '498',
120
+ "MGA" => '969',
121
+ "MKD" => '807',
122
+ "MMK" => '104',
123
+ "MNT" => '496',
124
+ "MOP" => '446',
125
+ "MRO" => '478',
126
+ "MTL" => '470',
127
+ "MUR" => '480',
128
+ "MVR" => '462',
129
+ "MWK" => '454',
31
130
  "MXN" => '484',
131
+ "MXV" => '979',
132
+ "MYR" => '458',
133
+ "MZN" => '943',
134
+ "NAD" => '516',
135
+ "NGN" => '566',
136
+ "NIO" => '558',
137
+ "NOK" => '578',
138
+ "NPR" => '524',
32
139
  "NZD" => '554',
140
+ "OMR" => '512',
141
+ "PAB" => '590',
142
+ "PEN" => '604',
143
+ "PGK" => '598',
144
+ "PHP" => '608',
145
+ "PKR" => '586',
146
+ "PLN" => '985',
147
+ "PYG" => '600',
148
+ "QAR" => '634',
149
+ "ROL" => '642',
150
+ "RON" => '946',
151
+ "RSD" => '941',
152
+ "RUB" => '643',
153
+ "RWF" => '646',
154
+ "SAR" => '682',
155
+ "SBD" => '090',
156
+ "SCR" => '690',
157
+ "SDG" => '938',
158
+ "SEK" => '752',
159
+ "SGD" => '702',
160
+ "SHP" => '654',
161
+ "SKK" => '703',
162
+ "SLL" => '694',
163
+ "SOS" => '706',
164
+ "SRD" => '968',
165
+ "STD" => '678',
166
+ "SYP" => '760',
167
+ "SZL" => '748',
168
+ "THB" => '764',
169
+ "TJS" => '972',
170
+ "TMM" => '795',
171
+ "TND" => '788',
172
+ "TOP" => '776',
173
+ "TRY" => '949',
174
+ "TTD" => '780',
175
+ "TWD" => '901',
176
+ "TZS" => '834',
177
+ "UAH" => '980',
178
+ "UGX" => '800',
33
179
  "USD" => '840',
180
+ "USN" => '997',
181
+ "USS" => '998',
182
+ "UYU" => '858',
183
+ "UZS" => '860',
184
+ "VEB" => '862',
185
+ "VND" => '704',
186
+ "VUV" => '548',
187
+ "WST" => '882',
188
+ "XAF" => '950',
189
+ "XAG" => '961',
190
+ "XAU" => '959',
191
+ "XBA" => '955',
192
+ "XBB" => '956',
193
+ "XBC" => '957',
194
+ "XBD" => '958',
195
+ "XCD" => '951',
196
+ "XDR" => '960',
197
+ "XOF" => '952',
198
+ "XPD" => '964',
199
+ "XPF" => '953',
200
+ "XPT" => '962',
201
+ "XTS" => '963',
202
+ "XXX" => '999',
203
+ "YER" => '886',
204
+ "ZAR" => '710',
205
+ "ZMK" => '894',
206
+ "ZWD" => '716',
34
207
  }
35
208
 
36
209
  def initialize(options = {})
@@ -1,141 +1,119 @@
1
+ require 'nokogiri'
2
+
1
3
  module ActiveMerchant #:nodoc:
2
4
  module Billing #:nodoc:
3
5
  class LitleGateway < Gateway
4
- # Specific to Litle options:
5
- # * <tt>:merchant_id</tt> - Merchant Id assigned by Litle
6
- # * <tt>:user</tt> - Username assigned by Litle
7
- # * <tt>:password</tt> - Password assigned by Litle
8
- # * <tt>:version</tt> - The version of the api you are using (eg, '8.10')
9
- # * <tt>:proxy_addr</tt> - Proxy address - nil if not needed
10
- # * <tt>:proxy_port</tt> - Proxy port - nil if not needed
11
- # * <tt>:url</tt> - URL assigned by Litle (for testing, use the sandbox)
12
- #
13
- # Standard Active Merchant options
14
- # * <tt>:order_id</tt> - The order number
15
- # * <tt>:ip</tt> - The IP address of the customer making the purchase
16
- # * <tt>:customer</tt> - The name, customer number, or other information that identifies the customer
17
- # * <tt>:invoice</tt> - The invoice number
18
- # * <tt>:merchant</tt> - The name or description of the merchant offering the product
19
- # * <tt>:description</tt> - A description of the transaction
20
- # * <tt>:email</tt> - The email address of the customer
21
- # * <tt>:currency</tt> - The currency of the transaction. Only important when you are using a currency that is not the default with a gateway that supports multiple currencies.
22
- # * <tt>:billing_address</tt> - A hash containing the billing address of the customer.
23
- # * <tt>:shipping_address</tt> - A hash containing the shipping address of the customer.
24
- #
25
- # The <tt>:billing_address</tt>, and <tt>:shipping_address</tt> hashes can have the following keys:
26
- #
27
- # * <tt>:name</tt> - The full name of the customer.
28
- # * <tt>:company</tt> - The company name of the customer.
29
- # * <tt>:address1</tt> - The primary street address of the customer.
30
- # * <tt>:address2</tt> - Additional line of address information.
31
- # * <tt>:city</tt> - The city of the customer.
32
- # * <tt>:state</tt> - The state of the customer. The 2 digit code for US and Canadian addresses. The full name of the state or province for foreign addresses.
33
- # * <tt>:country</tt> - The [ISO 3166-1-alpha-2 code](http://www.iso.org/iso/country_codes/iso_3166_code_lists/english_country_names_and_code_elements.htm) for the customer.
34
- # * <tt>:zip</tt> - The zip or postal code of the customer.
35
- # * <tt>:phone</tt> - The phone number of the customer.
6
+ SCHEMA_VERSION = '8.18'
36
7
 
37
8
  self.test_url = 'https://www.testlitle.com/sandbox/communicator/online'
38
9
  self.live_url = 'https://payments.litle.com/vap/communicator/online'
39
10
 
40
- LITLE_SCHEMA_VERSION = '8.13'
41
-
42
- # The countries the gateway supports merchants from as 2 digit ISO country codes
43
11
  self.supported_countries = ['US']
44
-
45
- # The card types supported by the payment gateway
12
+ self.default_currency = 'USD'
46
13
  self.supported_cardtypes = [:visa, :master, :american_express, :discover, :diners_club, :jcb]
47
14
 
48
- # The homepage URL of the gateway
49
- self.homepage_url = 'http://www.litle.com/'
50
-
51
- # The name of the gateway
52
- self.display_name = 'Litle & Co.'
15
+ self.homepage_url = 'http://www.litle.com/'
16
+ self.display_name = 'Litle & Co.'
53
17
 
54
- self.default_currency = 'USD'
18
+ # Public: Create a new Litle gateway.
19
+ #
20
+ # options - A hash of options:
21
+ # :login - The user.
22
+ # :password - The password.
23
+ # :merchant_id - The merchant id.
24
+ def initialize(options={})
25
+ requires!(options, :login, :password, :merchant_id)
26
+ super
27
+ end
55
28
 
56
- def initialize(options = {})
57
- begin
58
- require 'LitleOnline'
59
- rescue LoadError
60
- raise "Could not load the LitleOnline gem (> 08.15.0). Use `gem install LitleOnline` to install it."
29
+ def purchase(money, payment_method, options={})
30
+ request = build_xml_request do |doc|
31
+ add_authentication(doc)
32
+ doc.sale(transaction_attributes(options)) do
33
+ add_auth_purchase_params(doc, money, payment_method, options)
34
+ end
61
35
  end
62
36
 
63
- if wiredump_device
64
- LitleOnline::Configuration.logger = ((Logger === wiredump_device) ? wiredump_device : Logger.new(wiredump_device))
65
- LitleOnline::Configuration.logger.level = Logger::DEBUG
66
- else
67
- LitleOnline::Configuration.logger = Logger.new(STDOUT)
68
- LitleOnline::Configuration.logger.level = Logger::WARN
37
+ commit(:sale, request)
38
+ end
39
+
40
+ def authorize(money, payment_method, options={})
41
+ request = build_xml_request do |doc|
42
+ add_authentication(doc)
43
+ doc.authorization(transaction_attributes(options)) do
44
+ add_auth_purchase_params(doc, money, payment_method, options)
45
+ end
69
46
  end
70
47
 
71
- @litle = LitleOnline::LitleOnlineRequest.new
48
+ commit(:authorization, request)
49
+ end
72
50
 
73
- options[:version] ||= LITLE_SCHEMA_VERSION
74
- options[:merchant] ||= 'Default Report Group'
75
- options[:user] ||= options[:login]
51
+ def capture(money, authorization, options={})
52
+ transaction_id, kind = split_authorization(authorization)
76
53
 
77
- requires!(options, :merchant_id, :user, :password, :merchant, :version)
54
+ request = build_xml_request do |doc|
55
+ add_authentication(doc)
56
+ doc.capture_(transaction_attributes(options)) do
57
+ doc.litleTxnId(transaction_id)
58
+ doc.amount(money) if money
59
+ end
60
+ end
78
61
 
79
- super
62
+ commit(:capture, request)
80
63
  end
81
64
 
82
- def authorize(money, creditcard_or_token, options = {})
83
- to_pass = build_authorize_request(money, creditcard_or_token, options)
84
- build_response(:authorization, @litle.authorization(to_pass))
65
+ def credit(money, authorization, options = {})
66
+ deprecated CREDIT_DEPRECATION_MESSAGE
67
+ refund(money, authorization, options)
85
68
  end
86
69
 
87
- def purchase(money, creditcard_or_token, options = {})
88
- to_pass = build_purchase_request(money, creditcard_or_token, options)
89
- build_response(:sale, @litle.sale(to_pass))
70
+ def refund(money, authorization, options={})
71
+ transaction_id, kind = split_authorization(authorization)
72
+
73
+ request = build_xml_request do |doc|
74
+ add_authentication(doc)
75
+ doc.credit(transaction_attributes(options)) do
76
+ doc.litleTxnId(transaction_id)
77
+ doc.amount(money) if money
78
+ end
79
+ end
80
+
81
+ commit(:credit, request)
90
82
  end
91
83
 
92
- def capture(money, authorization, options = {})
84
+ def void(authorization, options={})
93
85
  transaction_id, kind = split_authorization(authorization)
94
- to_pass = create_capture_hash(money, transaction_id, options)
95
- build_response(:capture, @litle.capture(to_pass))
96
- end
97
86
 
98
- # Note: Litle requires that authorization requests be voided via auth_reversal
99
- # and other requests via void. To maintain the same interface as the other
100
- # gateways the transaction_id and the kind of transaction are concatenated
101
- # together with a ; separator (e.g. 1234;authorization)
102
- #
103
- # A partial auth_reversal can be accomplished by passing :amount as an option
104
- def void(identification, options = {})
105
- transaction_id, kind = split_authorization(identification)
106
- if(kind == 'authorization')
107
- to_pass = create_auth_reversal_hash(transaction_id, options[:amount], options)
108
- build_response(:authReversal, @litle.auth_reversal(to_pass))
109
- else
110
- to_pass = create_void_hash(transaction_id, options)
111
- build_response(:void, @litle.void(to_pass))
87
+ request = build_xml_request do |doc|
88
+ add_authentication(doc)
89
+ doc.send(void_type(kind), transaction_attributes(options)) do
90
+ doc.litleTxnId(transaction_id)
91
+ end
112
92
  end
113
- end
114
93
 
115
- def refund(money, authorization, options = {})
116
- to_pass = build_credit_request(money, authorization, options)
117
- build_response(:credit, @litle.credit(to_pass))
94
+ commit(void_type(kind), request)
118
95
  end
119
96
 
120
- def credit(money, authorization, options = {})
121
- deprecated CREDIT_DEPRECATION_MESSAGE
122
- refund(money, authorization, options)
123
- end
97
+ def store(creditcard, options = {})
98
+ request = build_xml_request do |doc|
99
+ add_authentication(doc)
100
+ doc.registerTokenRequest(transaction_attributes(options)) do
101
+ doc.orderId(truncated(options[:order_id]))
102
+ doc.accountNumber(creditcard.number)
103
+ end
104
+ end
124
105
 
125
- def store(creditcard_or_paypage_registration_id, options = {})
126
- to_pass = create_token_hash(creditcard_or_paypage_registration_id, options)
127
- build_response(:registerToken, @litle.register_token_request(to_pass), %w(000 801 802))
106
+ commit(:registerToken, request)
128
107
  end
129
108
 
130
109
  private
131
-
132
110
  CARD_TYPE = {
133
- 'visa' => 'VI',
134
- 'master' => 'MC',
135
- 'american_express' => 'AX',
136
- 'discover' => 'DI',
137
- 'jcb' => 'DI',
138
- 'diners_club' => 'DI'
111
+ 'visa' => 'VI',
112
+ 'master' => 'MC',
113
+ 'american_express' => 'AX',
114
+ 'discover' => 'DI',
115
+ 'jcb' => 'JC',
116
+ 'diners_club' => 'DC'
139
117
  }
140
118
 
141
119
  AVS_RESPONSE_CODE = {
@@ -156,410 +134,172 @@ module ActiveMerchant #:nodoc:
156
134
  '40' => 'E'
157
135
  }
158
136
 
159
- def url
160
- return @options[:url] if @options[:url].present?
161
-
162
- test? ? self.test_url : self.live_url
163
- end
164
-
165
- def build_response(kind, litle_response, valid_responses=%w(000))
166
- response = Hash.from_xml(litle_response.raw_xml.to_s)['litleOnlineResponse']
167
-
168
- if response['response'] == "0"
169
- detail = response["#{kind}Response"]
170
- fraud = fraud_result(detail)
171
- Response.new(
172
- valid_responses.include?(detail['response']),
173
- detail['message'],
174
- { litleOnlineResponse: response, response_code: detail['response'] },
175
- authorization: authorization_from(detail, kind),
176
- avs_result: { :code => fraud['avs'] },
177
- cvv_result: fraud['cvv'],
178
- test: test?
179
- )
180
- else
181
- Response.new(false, response['message'], :litleOnlineResponse => response, :test => test?)
182
- end
137
+ def void_type(kind)
138
+ (kind == 'authorization') ? :authReversal : :void
183
139
  end
184
140
 
185
- # Generates an authorization string of the appropriate id and the kind of transaction
186
- # See #void for how the kind is used
187
- def authorization_from(litle_response, kind)
188
- case kind
189
- when :registerToken
190
- authorization = litle_response['litleToken']
191
- else
192
- authorization = [litle_response['litleTxnId'], kind.to_s].join(";")
141
+ def add_authentication(doc)
142
+ doc.authentication do
143
+ doc.user(@options[:login])
144
+ doc.password(@options[:password])
193
145
  end
194
146
  end
195
147
 
196
- def split_authorization(authorization)
197
- transaction_id, kind = authorization.to_s.split(';')
198
- [transaction_id, kind]
199
- end
200
-
201
- def build_authorize_request(money, creditcard_or_token, options)
202
- payment_method = build_payment_method(creditcard_or_token, options)
203
-
204
- hash = create_hash(money, options)
205
-
206
- add_creditcard_or_cardtoken_hash(hash, payment_method)
207
-
208
- hash
148
+ def add_auth_purchase_params(doc, money, payment_method, options)
149
+ doc.orderId(truncated(options[:order_id]))
150
+ doc.amount(money)
151
+ doc.orderSource('ecommerce')
152
+ add_billing_address(doc, payment_method, options)
153
+ add_shipping_address(doc, payment_method, options)
154
+ add_payment_method(doc, payment_method)
209
155
  end
210
156
 
211
- def build_purchase_request(money, creditcard_or_token, options)
212
- payment_method = build_payment_method(creditcard_or_token, options)
213
-
214
- hash = create_hash(money, options)
215
-
216
- add_creditcard_or_cardtoken_hash(hash, payment_method)
217
-
218
- hash
157
+ def add_payment_method(doc, payment_method)
158
+ if payment_method.is_a?(String)
159
+ doc.token do
160
+ doc.litleToken(payment_method)
161
+ end
162
+ else
163
+ doc.card do
164
+ doc.type_(CARD_TYPE[payment_method.brand])
165
+ doc.number(payment_method.number)
166
+ doc.expDate(exp_date(payment_method))
167
+ doc.cardValidationNum(payment_method.verification_value)
168
+ end
169
+ end
219
170
  end
220
171
 
221
- def build_credit_request(money, identification_or_token, options)
222
- payment_method = build_payment_method(identification_or_token, options)
172
+ def add_billing_address(doc, payment_method, options)
173
+ return if payment_method.is_a?(String)
223
174
 
224
- hash = create_hash(money, options)
175
+ doc.billToAddress do
176
+ doc.name(payment_method.name)
177
+ doc.email(options[:email]) if options[:email]
225
178
 
226
- add_identification_or_cardtoken_hash(hash, payment_method)
227
-
228
- unless payment_method.is_a?(LitleCardToken)
229
- hash['orderSource'] = nil
230
- hash['orderId'] = nil
179
+ add_address(doc, options[:billing_address])
231
180
  end
232
-
233
- hash
234
181
  end
235
182
 
236
- def build_payment_method(payment_method, options)
237
- result = payment_method
183
+ def add_shipping_address(doc, payment_method, options)
184
+ return if payment_method.is_a?(String)
238
185
 
239
- # Build instance of the LitleCardToken class for internal use if this is a token request.
240
- if payment_method.is_a?(String) && options.has_key?(:token)
241
- result = LitleCardToken.new(:token => payment_method)
242
- result.month = options[:token][:month]
243
- result.year = options[:token][:year]
244
- result.verification_value = options[:token][:verification_value]
245
- result.brand = options[:token][:brand]
186
+ doc.shipToAddress do
187
+ add_address(doc, options[:shipping_address])
246
188
  end
247
-
248
- result
249
189
  end
250
190
 
251
- def add_creditcard_or_cardtoken_hash(hash, creditcard_or_cardtoken)
252
- if creditcard_or_cardtoken.is_a?(LitleCardToken)
253
- add_cardtoken_hash(hash, creditcard_or_cardtoken)
254
- else
255
- add_creditcard_hash(hash, creditcard_or_cardtoken)
256
- end
191
+ def add_address(doc, address)
192
+ return unless address
193
+
194
+ doc.companyName(address[:company]) unless address[:company].blank?
195
+ doc.addressLine1(address[:address1]) unless address[:address1].blank?
196
+ doc.addressLine2(address[:address2]) unless address[:address2].blank?
197
+ doc.city(address[:city]) unless address[:city].blank?
198
+ doc.state(address[:state]) unless address[:state].blank?
199
+ doc.zip(address[:zip]) unless address[:zip].blank?
200
+ doc.country(address[:country]) unless address[:country].blank?
201
+ doc.phone(address[:phone]) unless address[:phone].blank?
257
202
  end
258
203
 
259
- def add_identification_or_cardtoken_hash(hash, identification_or_cardtoken)
260
- if identification_or_cardtoken.is_a?(LitleCardToken)
261
- add_cardtoken_hash(hash, identification_or_cardtoken)
262
- else
263
- transaction_id, kind = split_authorization(identification_or_cardtoken)
264
- hash['litleTxnId'] = transaction_id
265
- end
204
+ def exp_date(payment_method)
205
+ "#{format(payment_method.month, :two_digits)}#{format(payment_method.year, :two_digits)}"
266
206
  end
267
207
 
268
- def add_cardtoken_hash(hash, cardtoken)
269
- token_info = {}
270
- token_info['litleToken'] = cardtoken.token
271
- token_info['expDate'] = cardtoken.exp_date if cardtoken.exp_date?
272
- token_info['cardValidationNum'] = cardtoken.verification_value unless cardtoken.verification_value.blank?
273
- token_info['type'] = cardtoken.type unless cardtoken.type.blank?
208
+ def parse(kind, xml)
209
+ parsed = {}
210
+
211
+ doc = Nokogiri::XML(xml).remove_namespaces!
212
+ doc.xpath("//litleOnlineResponse/#{kind}Response/*").each do |node|
213
+ if (node.elements.empty?)
214
+ parsed[node.name.to_sym] = node.text
215
+ else
216
+ node.elements.each do |childnode|
217
+ name = "#{node.name}_#{childnode.name}"
218
+ parsed[name.to_sym] = childnode.text
219
+ end
220
+ end
221
+ end
274
222
 
275
- hash['token'] = token_info
276
- hash
223
+ parsed
277
224
  end
278
225
 
279
- def add_creditcard_hash(hash, creditcard)
280
- cc_type = CARD_TYPE[creditcard.brand]
281
- exp_date_yr = creditcard.year.to_s[2..3]
282
- exp_date_mo = '%02d' % creditcard.month.to_i
283
- exp_date = exp_date_mo + exp_date_yr
284
-
285
- card_info = {
286
- 'type' => cc_type,
287
- 'number' => creditcard.number,
288
- 'expDate' => exp_date,
289
- 'cardValidationNum' => creditcard.verification_value
226
+ def commit(kind, request)
227
+ parsed = parse(kind, ssl_post(url, request, headers))
228
+
229
+ options = {
230
+ authorization: authorization_from(kind, parsed),
231
+ test: test?,
232
+ :avs_result => { :code => AVS_RESPONSE_CODE[parsed[:fraudResult_avsResult]] },
233
+ :cvv_result => parsed[:fraudResult_cardValidationResult]
290
234
  }
291
235
 
292
- hash['card'] = card_info
293
- hash
236
+ Response.new(success_from(kind, parsed), parsed[:message], parsed, options)
294
237
  end
295
238
 
296
- def create_capture_hash(money, authorization, options={})
297
- hash = create_hash(money, options)
298
- hash['partial'] = true if money
299
- hash['litleTxnId'] = authorization
300
- hash
239
+ def success_from(kind, parsed)
240
+ return (parsed[:response] == '000') unless kind == :registerToken
241
+ %w(000 801 802).include?(parsed[:response])
301
242
  end
302
243
 
303
- def create_token_hash(creditcard_or_paypage_registration_id, options)
304
- hash = create_hash(0, options)
305
-
306
- if creditcard_or_paypage_registration_id.is_a?(String)
307
- hash['paypageRegistrationId'] = creditcard_or_paypage_registration_id
308
- else
309
- hash['accountNumber'] = creditcard_or_paypage_registration_id.number
310
- end
311
-
312
- hash
244
+ def authorization_from(kind, parsed)
245
+ (kind == :registerToken) ? parsed[:litleToken] : "#{parsed[:litleTxnId]};#{kind}"
313
246
  end
314
247
 
315
- def create_void_hash(identification, options)
316
- hash = create_hash(nil, options)
317
- hash['litleTxnId'] = identification
318
- hash
248
+ def split_authorization(authorization)
249
+ transaction_id, kind = authorization.to_s.split(';')
250
+ [transaction_id, kind]
319
251
  end
320
252
 
321
- def create_auth_reversal_hash(identification, money, options)
322
- hash = create_hash(money, options)
323
- hash['litleTxnId'] = identification
324
- hash
253
+ def transaction_attributes(options)
254
+ attributes = {}
255
+ attributes[:id] = truncated(options[:id] || options[:order_id])
256
+ attributes[:reportGroup] = options[:merchant] || 'Default Report Group'
257
+ attributes[:customerId] = options[:customer]
258
+ attributes.delete_if { |key, value| value == nil }
259
+ attributes
325
260
  end
326
261
 
327
- def create_hash(money, options)
328
- fraud_check_type = {}
329
- if options[:ip]
330
- fraud_check_type['customerIpAddress'] = options[:ip]
331
- end
332
-
333
- enhanced_data = {}
334
- if options[:invoice]
335
- enhanced_data['invoiceReferenceNumber'] = options[:invoice]
336
- end
337
-
338
- if options[:description]
339
- enhanced_data['customerReference'] = options[:description][0..16]
340
- end
262
+ def root_attributes
263
+ {
264
+ merchantId: @options[:merchant_id],
265
+ version: SCHEMA_VERSION,
266
+ xmlns: "http://www.litle.com/schema"
267
+ }
268
+ end
341
269
 
342
- if options[:billing_address]
343
- bill_to_address = {
344
- 'name' => options[:billing_address][:name],
345
- 'companyName' => options[:billing_address][:company],
346
- 'addressLine1' => options[:billing_address][:address1],
347
- 'addressLine2' => options[:billing_address][:address2],
348
- 'city' => options[:billing_address][:city],
349
- 'state' => options[:billing_address][:state],
350
- 'zip' => options[:billing_address][:zip],
351
- 'country' => options[:billing_address][:country],
352
- 'email' => options[:email],
353
- 'phone' => options[:billing_address][:phone]
354
- }
270
+ def build_xml_request
271
+ builder = Nokogiri::XML::Builder.new
272
+ builder.__send__('litleOnlineRequest', root_attributes) do |doc|
273
+ yield(doc)
355
274
  end
356
- if options[:shipping_address]
357
- ship_to_address = {
358
- 'name' => options[:shipping_address][:name],
359
- 'companyName' => options[:shipping_address][:company],
360
- 'addressLine1' => options[:shipping_address][:address1],
361
- 'addressLine2' => options[:shipping_address][:address2],
362
- 'city' => options[:shipping_address][:city],
363
- 'state' => options[:shipping_address][:state],
364
- 'zip' => options[:shipping_address][:zip],
365
- 'country' => options[:shipping_address][:country],
366
- 'email' => options[:email],
367
- 'phone' => options[:shipping_address][:phone]
368
- }
369
- end
370
-
371
- hash = {
372
- 'billToAddress' => bill_to_address,
373
- 'shipToAddress' => ship_to_address,
374
- 'orderId' => truncated_order_id(options),
375
- 'customerId' => options[:customer],
376
- 'reportGroup' => (options[:merchant] || @options[:merchant]),
377
- 'merchantId' => (options[:merchant_id] || @options[:merchant_id]),
378
- 'orderSource' => (options[:order_source] || 'ecommerce'),
379
- 'enhancedData' => enhanced_data,
380
- 'fraudCheckType' => fraud_check_type,
381
- 'user' => (options[:user] || @options[:user]),
382
- 'password' => (options[:password] || @options[:password]),
383
- 'version' => (options[:version] || @options[:version]),
384
- 'url' => (options[:url] || url),
385
- 'proxy_addr' => (options[:proxy_addr] || @options[:proxy_addr]),
386
- 'proxy_port' => (options[:proxy_port] || @options[:proxy_port]),
387
- 'id' => (options[:id] || options[:order_id] || @options[:order_id])
388
- }
275
+ builder.doc.root.to_xml
276
+ end
389
277
 
390
- hash.merge!('amount' => money) if(money && (money != ""))
278
+ def url
279
+ test? ? test_url : live_url
280
+ end
391
281
 
392
- hash
282
+ def truncated(value)
283
+ return unless value
284
+ value[0..24]
393
285
  end
394
286
 
395
287
  def truncated_order_id(options)
396
- order_id = options[:order_id] || @options[:order_id]
397
- return unless order_id
398
- order_id[0..24]
288
+ return unless options[:order_id]
289
+ options[:order_id][0..24]
399
290
  end
400
291
 
401
- def fraud_result(authorization_response)
402
- if result = authorization_response['fraudResult']
403
- if result.key?('cardValidationResult')
404
- cvv_to_pass = result['cardValidationResult'].blank? ? "P" : result['cardValidationResult']
405
- end
406
-
407
- avs_to_pass = AVS_RESPONSE_CODE[result['avsResult']] unless result['avsResult'].blank?
408
- end
409
- { 'cvv' => cvv_to_pass, 'avs' => avs_to_pass }
292
+ def truncated_id(options)
293
+ return unless options[:id]
294
+ options[:id][0..24]
410
295
  end
411
296
 
412
- # A +LitleCardToken+ object represents a tokenized credit card, and is capable of validating the various
413
- # data associated with these.
414
- #
415
- # == Example Usage
416
- # token = LitleCardToken.new(
417
- # :token => '1234567890123456',
418
- # :month => '9',
419
- # :year => '2010',
420
- # :brand => 'visa',
421
- # :verification_value => '123'
422
- # )
423
- #
424
- # token.valid? # => true
425
- # cc.exp_date # => 0910
426
- #
427
- class LitleCardToken
428
- include Validateable
429
-
430
- # Returns or sets the token. (required)
431
- #
432
- # @return [String]
433
- attr_accessor :token
434
-
435
- # Returns or sets the expiry month for the card associated with token. (optional)
436
- #
437
- # @return [Integer]
438
- attr_accessor :month
439
-
440
- # Returns or sets the expiry year for the card associated with token. (optional)
441
- #
442
- # @return [Integer]
443
- attr_accessor :year
444
-
445
- # Returns or sets the card verification value. (optional)
446
- #
447
- # @return [String] the verification value
448
- attr_accessor :verification_value
449
-
450
- # Returns or sets the credit card brand. (optional)
451
- #
452
- # Valid card types are
453
- #
454
- # * +'visa'+
455
- # * +'master'+
456
- # * +'discover'+
457
- # * +'american_express'+
458
- # * +'diners_club'+
459
- # * +'jcb'+
460
- # * +'switch'+
461
- # * +'solo'+
462
- # * +'dankort'+
463
- # * +'maestro'+
464
- # * +'forbrugsforeningen'+
465
- # * +'laser'+
466
- #
467
- # @return (String) the credit card brand
468
- attr_accessor :brand
469
-
470
- # Returns the Litle credit card type identifier.
471
- #
472
- # @return (String) the credit card type identifier
473
- def type
474
- CARD_TYPE[brand] unless brand.blank?
475
- end
476
-
477
- # Returns true if the expiration date is set.
478
- #
479
- # @return (Boolean)
480
- def exp_date?
481
- !month.to_i.zero? && !year.to_i.zero?
482
- end
483
-
484
- # Returns the card token expiration date in MMYY format.
485
- #
486
- # @return (String) the expiration date in MMYY format
487
- def exp_date
488
- result = ''
489
- if exp_date?
490
- exp_date_yr = year.to_s[2..3]
491
- exp_date_mo = '%02d' % month.to_i
492
-
493
- result = exp_date_mo + exp_date_yr
494
- end
495
- result
496
- end
497
-
498
- # Validates the card token details.
499
- #
500
- # Any validation errors are added to the {#errors} attribute.
501
- def validate
502
- validate_card_token
503
- validate_expiration_date
504
- validate_card_brand
505
- end
506
-
507
- def check?
508
- false
509
- end
510
-
511
- private
512
-
513
- CARD_TYPE = {
514
- 'visa' => 'VI',
515
- 'master' => 'MC',
516
- 'american_express' => 'AX',
517
- 'discover' => 'DI',
518
- 'jcb' => 'DI',
519
- 'diners_club' => 'DI'
297
+ def headers
298
+ {
299
+ 'Content-Type' => 'text/xml'
520
300
  }
521
-
522
- def before_validate #:nodoc:
523
- self.month = month.to_i
524
- self.year = year.to_i
525
- end
526
-
527
- # Litle XML Reference Guide 1.8.2
528
- #
529
- # The length of the original card number is reflected in the token, so a
530
- # submitted 16-digit number results in a 16-digit token. Also, all tokens
531
- # use only numeric characters, so you do not have to change your
532
- # systems to accept alpha-numeric characters.
533
- #
534
- # The credit card token numbers themselves have two parts.
535
- # The last four digits match the last four digits of the card number.
536
- # The remaining digits (length can vary based upon original card number
537
- # length) are a randomly generated.
538
- def validate_card_token #:nodoc:
539
- if token.to_s.length < 12 || token.to_s.match(/\A\d+\Z/).nil?
540
- errors.add :token, "is not a valid card token"
541
- end
542
- end
543
-
544
- def validate_expiration_date #:nodoc:
545
- if !month.to_i.zero? || !year.to_i.zero?
546
- errors.add :month, "is not a valid month" unless valid_month?(month)
547
- errors.add :year, "is not a valid year" unless valid_expiry_year?(year)
548
- end
549
- end
550
-
551
- def validate_card_brand #:nodoc:
552
- errors.add :brand, "is invalid" unless brand.blank? || CreditCard.card_companies.keys.include?(brand)
553
- end
554
-
555
- def valid_month?(month)
556
- (1..12).include?(month.to_i)
557
- end
558
-
559
- def valid_expiry_year?(year)
560
- year.to_s =~ /\A\d{4}\Z/ && year.to_i > 1987
561
- end
562
301
  end
302
+
563
303
  end
564
304
  end
565
305
  end