activemerchant 1.75.0 → 1.76.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 50bb4461b27b49be6e87608a11f5db29a02d0adc
4
- data.tar.gz: d5b76878be3ec6d7334cb343d439837b6fb3d599
3
+ metadata.gz: fe5e2a6f611e503ee6acfb6453f46b914f9b93c4
4
+ data.tar.gz: ecdf4eaa3a3d3697aa769239ac59ac4121f5d46a
5
5
  SHA512:
6
- metadata.gz: 3eff219643c61371a81f0242548576dadfd8032cd3939a5859e6d5a8ca1aa4b822454754ed6aef844c14096af72a69b90b6ae5cb2ba8491fb662beba6aeb8447
7
- data.tar.gz: 2cc15eaa916a7b90c66e8686668ec01cc56cebdfe2029c555033e2e1907fc931842e24bad2c8fb7760d543940cdfb6d84da0d8e1da24ccaa4927adc7646950d9
6
+ metadata.gz: 506d1d259018973cc2d4c9b6c37442f7fdc8903595a5cd6056ab32fa2608bef32d0e94d1f09179f2b660b99e1197c85c146f0afb0a60cd07a42432ebff0d5fc8
7
+ data.tar.gz: 8e8ca9a39966245232a7b64a685939e7852eab12f28fc3f1dc540b9e10a39c04517fd912bc609d56101e4e16d0165fe5b7a1f45570b009a35aaf27be09760284
data/CHANGELOG CHANGED
@@ -2,6 +2,33 @@
2
2
 
3
3
  == HEAD
4
4
 
5
+ == Version 1.76.0 (January 3, 2018)
6
+ * PayU Latam: Change default text for description [nfarve] #2669
7
+ * Checkout V2: Allows AVS and CVV result details to come through on authorizations [deedeelavinder] #2650
8
+ * Global Collect: Adds boolean option for pre_authorization [deeedeelavinder] #2651
9
+ * Credorax: Pass Transaction Type field [curiousepic] #2653
10
+ * Add CardProcess Gateway [bpollack] #2659
11
+ * Safe Charge: Provision 3DS option for approved merchants [deedeelavinder] #2661
12
+ * PayU Latam: Require payment_country on initialize [curiousepic] #2663
13
+ * Adyen: Remove CVV as Required Field and Determines shopperInteraction [nfarve] #2665
14
+ * SafeCharge: add support for VendorID, WebsiteID, and IP logging [bpollack] #2667
15
+ * Safe Charge: Adds 3DS flag [deedeelavinder] #2668
16
+ * CardProcess: Fix success? to always return true or false [bpollack] #2674
17
+ * SagePay: Correct CVV, AVS codes for Sagepay [singhai0] #2670
18
+ * PayU Latam: Count pending Voids as successful [curiousepic] #2677
19
+ * Mercado Pago: Ensure acess tokens are URL escaped [bpollack] #2675
20
+ * MiGS: Update hash format to SHA256 and restore remote tests [bpollack] #2676
21
+ * MiGS: Support verify calls [bpollack] #2664
22
+ * iATS: Fix Messages with Failure on iATS Server [nfarve] #2680
23
+ * Barclaycard Smartpay: Correct repsonse for fraud rejects #2683
24
+ * Adyen: Allow incomplete addresses in some situations [bpollack] #2684
25
+ * Paymentez: Add new gateway [bpollack] #2685
26
+ * PayU Latam: Provide a mechanism to override the amount in verify [dtykocki] #2688
27
+ * Mercado Pago: Support X-Device-Session-ID Header [bpollack] #2689
28
+ * Mercado Pago: Support arbitrary additional_info JSON [bpollack] #2691
29
+ * FirstData E4: Override ECI value for Apple Pay transactions with Discover [jasonwebster] #2671
30
+ * Quickbooks: Add payment context to Quickbooks charges and refunds [bdewater] #2694
31
+
5
32
  == Version 1.75.0 (November 9, 2017)
6
33
  * Barclaycard Smartpay: Clean up test options hashes [bpollack] #2632
7
34
  * Barclaycard Smartpay: Extra data fields for credits [bpollack] #2631
@@ -45,6 +45,7 @@ module ActiveMerchant #:nodoc:
45
45
  add_invoice(post, money, options)
46
46
  add_payment(post, payment)
47
47
  add_extra_data(post, options)
48
+ add_shopper_interaction(post,payment,options)
48
49
  add_address(post, options)
49
50
  commit('authorise', post)
50
51
  end
@@ -97,17 +98,21 @@ module ActiveMerchant #:nodoc:
97
98
  post[:selectedBrand] = options[:selected_brand] if options[:selected_brand]
98
99
  post[:deliveryDate] = options[:delivery_date] if options[:delivery_date]
99
100
  post[:merchantOrderReference] = options[:merchant_order_reference] if options[:merchant_order_reference]
100
- post[:shopperInteraction] = options[:shopper_interaction] if options[:shopper_interaction]
101
+ end
102
+
103
+ def add_shopper_interaction(post, payment, options={})
104
+ shopper_interaction = payment.verification_value ? "Ecommerce" : "ContAuth"
105
+ post[:shopperInteraction] = options[:shopper_interaction] || shopper_interaction
101
106
  end
102
107
 
103
108
  def add_address(post, options)
104
109
  return unless post[:card] && post[:card].kind_of?(Hash)
105
- if address = options[:billing_address] || options[:address]
110
+ if (address = options[:billing_address] || options[:address]) && address[:country]
106
111
  post[:card][:billingAddress] = {}
107
- post[:card][:billingAddress][:street] = address[:address1] if address[:address1]
108
- post[:card][:billingAddress][:houseNumberOrName] = address[:address2] if address[:address2]
112
+ post[:card][:billingAddress][:street] = address[:address1] || 'N/A'
113
+ post[:card][:billingAddress][:houseNumberOrName] = address[:address2] || 'N/A'
109
114
  post[:card][:billingAddress][:postalCode] = address[:zip] if address[:zip]
110
- post[:card][:billingAddress][:city] = address[:city] if address[:city]
115
+ post[:card][:billingAddress][:city] = address[:city] || 'N/A'
111
116
  post[:card][:billingAddress][:stateOrProvince] = address[:state] if address[:state]
112
117
  post[:card][:billingAddress][:country] = address[:country] if address[:country]
113
118
  end
@@ -138,8 +143,9 @@ module ActiveMerchant #:nodoc:
138
143
  number: payment.number,
139
144
  cvc: payment.verification_value
140
145
  }
146
+
141
147
  card.delete_if{|k,v| v.blank? }
142
- requires!(card, :expiryMonth, :expiryYear, :holderName, :number, :cvc)
148
+ requires!(card, :expiryMonth, :expiryYear, :holderName, :number)
143
149
  post[:card] = card
144
150
  end
145
151
 
@@ -211,8 +211,8 @@ module ActiveMerchant #:nodoc:
211
211
  end
212
212
 
213
213
  def success_from(response)
214
- return true if response.has_key?('authCode')
215
214
  return true if response['result'] == 'Success'
215
+ return true if response['resultCode'] == 'Authorised'
216
216
  return true if response['resultCode'] == 'Received'
217
217
  successful_responses = %w([capture-received] [cancel-received] [refund-received])
218
218
  successful_responses.include?(response['response'])
@@ -0,0 +1,254 @@
1
+ module ActiveMerchant #:nodoc:
2
+ module Billing #:nodoc:
3
+ class CardprocessGateway < Gateway
4
+ self.test_url = 'https://test.vr-pay-ecommerce.de/v1/payments'
5
+ self.live_url = 'https://vr-pay-ecommerce.de/v1/payments'
6
+
7
+ self.supported_countries = %w[ BE BG CZ DK DE EE IE ES FR HR IT CY LV LT LU
8
+ MT HU NL AT PL PT RO SI SK FI SE GB IS LI NO
9
+ CH ME MK AL RS TR BA ]
10
+ self.default_currency = 'EUR'
11
+ self.supported_cardtypes = [:visa, :master, :american_express, :diners_club, :jcb]
12
+
13
+ self.homepage_url = 'https://vr-pay-ecommerce.docs.oppwa.com/'
14
+ self.display_name = 'CardProcess VR-Pay'
15
+ self.money_format = :dollars
16
+
17
+ STANDARD_ERROR_CODE_MAPPING = {}
18
+
19
+ # Creates a new CardProcess Gateway
20
+ #
21
+ # The gateway requires a valid login, password, and entity ID
22
+ # to be passed in the +options+ hash.
23
+ #
24
+ # === Options
25
+ #
26
+ # * <tt>:user_id</tt> -- The CardProcess user ID
27
+ # * <tt>:password</tt> -- The CardProcess password
28
+ # * <tt>:entity_id</tt> -- The CardProcess channel or entity ID for any transactions
29
+ def initialize(options={})
30
+ requires!(options, :user_id, :password, :entity_id)
31
+ super
32
+ # This variable exists purely to allow remote tests to force error codes;
33
+ # the lack of a setter despite its usage is intentional.
34
+ @test_options = {}
35
+ end
36
+
37
+ def purchase(money, payment, options = {})
38
+ post = {}
39
+ add_invoice(post, money, options)
40
+ add_payment(post, payment)
41
+ add_address(post, payment, options)
42
+ add_customer_data(post, options)
43
+
44
+ commit('DB', post)
45
+ end
46
+
47
+ def authorize(money, payment, options = {})
48
+ post = {}
49
+ add_invoice(post, money, options)
50
+ add_payment(post, payment)
51
+ add_address(post, payment, options)
52
+ add_customer_data(post, options)
53
+
54
+ commit('PA', post)
55
+ end
56
+
57
+ def capture(money, authorization, options = {})
58
+ post = {
59
+ id: authorization
60
+ }
61
+ add_invoice(post, money, options)
62
+ commit('CP', post)
63
+ end
64
+
65
+ def refund(money, authorization, options = {})
66
+ post = {
67
+ id: authorization
68
+ }
69
+ add_invoice(post, money, options)
70
+ commit('RF', post)
71
+ end
72
+
73
+ def credit(money, payment, options = {})
74
+ post = {}
75
+ add_invoice(post, money, options)
76
+ add_payment(post, payment)
77
+ add_address(post, payment, options)
78
+ add_customer_data(post, options)
79
+
80
+ commit('CD', post)
81
+ end
82
+
83
+ def void(authorization, _options = {})
84
+ post = {
85
+ id: authorization
86
+ }
87
+ commit('RV', post)
88
+ end
89
+
90
+ def verify(credit_card, options = {})
91
+ MultiResponse.run do |r|
92
+ r.process { authorize(100, credit_card, options) }
93
+ r.process { void(r.authorization, options) }
94
+ end
95
+ end
96
+
97
+ def supports_scrubbing?
98
+ true
99
+ end
100
+
101
+ def scrub(transcript)
102
+ transcript
103
+ .gsub(%r{(authentication\.[^=]+=)[^&]+}, '\1[FILTERED]')
104
+ .gsub(%r{(card\.number=)\d+}, '\1[FILTERED]')
105
+ .gsub(%r{(cvv=)\d{3,4}}, '\1[FILTERED]\2')
106
+ end
107
+
108
+ private
109
+
110
+ def add_customer_data(post, options)
111
+ post['customer.ip'] = options[:ip] if options[:ip]
112
+ end
113
+
114
+ def add_address(post, _card, options)
115
+ if (address = options[:billing_address] || options[:address])
116
+ post[:billing] = hashify_address(address)
117
+ end
118
+
119
+ if (shipping = options[:shipping_address])
120
+ post[:shipping] = hashify_address(shipping)
121
+ end
122
+ end
123
+
124
+ def add_invoice(post, money, options)
125
+ return if money.nil?
126
+ post[:amount] = amount(money)
127
+ post[:currency] = (options[:currency] || currency(money))
128
+ post[:merchantInvoiceId] = options[:merchant_invoice_id] if options[:merchant_invoice_id]
129
+ post[:merchantTransactionId] = options[:merchant_transaction_id] if options[:merchant_transaction_id]
130
+ post[:transactionCategory] = options[:transaction_category] if options[:transaction_category]
131
+ end
132
+
133
+ def add_payment(post, payment)
134
+ return if payment.is_a?(String)
135
+ post[:paymentBrand] = payment.brand.upcase if payment.brand
136
+ post[:card] ||= {}
137
+ post[:card][:number] = payment.number
138
+ post[:card][:holder] = payment.name
139
+ post[:card][:expiryMonth] = sprintf('%02d', payment.month)
140
+ post[:card][:expiryYear] = sprintf('%02d', payment.year)
141
+ post[:card][:cvv] = payment.verification_value
142
+ end
143
+
144
+ def parse(body)
145
+ JSON.parse(body)
146
+ end
147
+
148
+ def commit(action, parameters)
149
+ url = (test? ? test_url : live_url)
150
+ if (id = parameters.delete(:id))
151
+ url += "/#{id}"
152
+ end
153
+
154
+ begin
155
+ raw_response = ssl_post(url, post_data(action, parameters.merge(@test_options)))
156
+ rescue ResponseError => e
157
+ raw_response = e.response.body
158
+ end
159
+ response = parse(raw_response)
160
+
161
+ Response.new(
162
+ success_from(response),
163
+ message_from(response),
164
+ response,
165
+ authorization: authorization_from(response),
166
+ avs_result: AVSResult.new(code: response['result']['avsResponse']),
167
+ cvv_result: CVVResult.new(response['result']['cvvResponse']),
168
+ test: test?,
169
+ error_code: error_code_from(response)
170
+ )
171
+ end
172
+
173
+ def success_from(response)
174
+ !(response['result']['code'] =~ /^(000\.000\.|000\.100\.1|000\.[36])/).nil?
175
+ end
176
+
177
+ def message_from(response)
178
+ response['result']['description']
179
+ end
180
+
181
+ def authorization_from(response)
182
+ response['id']
183
+ end
184
+
185
+ def post_data(action, parameters = {})
186
+ post = parameters.clone
187
+ post[:authentication] ||= {}
188
+ post[:authentication][:userId] = @options[:user_id]
189
+ post[:authentication][:password] = @options[:password]
190
+ post[:authentication][:entityId] = @options[:entity_id]
191
+ post[:paymentType] = action
192
+ dot_flatten_hash(post).map {|key, value| "#{key}=#{CGI.escape(value.to_s)}"}.join("&")
193
+ end
194
+
195
+ def error_code_from(response)
196
+ unless success_from(response)
197
+ case response['result']['code']
198
+ when '100.100.101'
199
+ STANDARD_ERROR_CODE[:incorrect_number]
200
+ when '100.100.303'
201
+ STANDARD_ERROR_CODE[:expired_card]
202
+ when /100\.100\.(201|301|305)/
203
+ STANDARD_ERROR_CODE[:invalid_expiry_date]
204
+ when /100.100.60[01]/
205
+ STANDARD_ERROR_CODE[:invalid_cvc]
206
+ when '800.100.151'
207
+ STANDARD_ERROR_CODE[:invalid_number]
208
+ when '800.100.153'
209
+ STANDARD_ERROR_CODE[:incorrect_cvc]
210
+ when /800.800.(102|302)/
211
+ STANDARD_ERROR_CODE[:incorrect_address]
212
+ when '800.800.202'
213
+ STANDARD_ERROR_CODE[:invalid_zip]
214
+ when '800.100.166'
215
+ STANDARD_ERROR_CODE[:incorrect_pin]
216
+ when '800.100.171'
217
+ STANDARD_ERROR_CODE[:pickup_card]
218
+ when /^(200|700)\./
219
+ STANDARD_ERROR_CODE[:config_error]
220
+ when /^(800\.[17]00|800\.800\.[123])/
221
+ STANDARD_ERROR_CODE[:card_declined]
222
+ when /^(900\.[1234]00)/
223
+ STANDARD_ERROR_CODE[:processing_error]
224
+ else
225
+ STANDARD_ERROR_CODE[:processing_error]
226
+ end
227
+ end
228
+ end
229
+
230
+ def hashify_address(address)
231
+ hash = {}
232
+ hash[:street1] = address[:address1] if address[:address1]
233
+ hash[:street2] = address[:address2] if address[:address2]
234
+ hash[:city] = address[:city] if address[:city]
235
+ hash[:state] = address[:state] if address[:state]
236
+ hash[:postcode] = address[:zip] if address[:zip]
237
+ hash[:country] = address[:country] if address[:country]
238
+ hash
239
+ end
240
+
241
+ def dot_flatten_hash(hash, prefix = '')
242
+ h = {}
243
+ hash.each_pair do |k, v|
244
+ if v.is_a?(Hash)
245
+ h.merge!(dot_flatten_hash(v, prefix + k.to_s + '.'))
246
+ else
247
+ h[prefix + k.to_s] = v
248
+ end
249
+ end
250
+ h
251
+ end
252
+ end
253
+ end
254
+ end
@@ -25,16 +25,7 @@ module ActiveMerchant #:nodoc:
25
25
  merged_params = multi.responses.map { |r| r.params }.reduce({}, :merge)
26
26
  succeeded = success_from(merged_params)
27
27
 
28
- Response.new(
29
- succeeded,
30
- message_from(succeeded, merged_params),
31
- merged_params,
32
- authorization: authorization_from(merged_params),
33
- avs_result: avs_result(:purchase, succeeded, merged_params),
34
- cvv_result: cvv_result(:purchase, succeeded, merged_params),
35
- error_code: error_code_from(succeeded, merged_params),
36
- test: test?
37
- )
28
+ response(:purchase, succeeded, merged_params)
38
29
  end
39
30
 
40
31
  def authorize(amount, payment_method, options={})
@@ -132,6 +123,15 @@ module ActiveMerchant #:nodoc:
132
123
  end
133
124
 
134
125
  succeeded = success_from(response)
126
+
127
+ response(action, succeeded, response)
128
+ end
129
+
130
+ def response(action, succeeded, response)
131
+ successful_response = succeeded && action == :purchase || action == :authorize
132
+ avs_result = successful_response ? avs_result(response) : nil
133
+ cvv_result = successful_response ? cvv_result(response) : nil
134
+
135
135
  Response.new(
136
136
  succeeded,
137
137
  message_from(succeeded, response),
@@ -139,8 +139,9 @@ module ActiveMerchant #:nodoc:
139
139
  authorization: authorization_from(response),
140
140
  error_code: error_code_from(succeeded, response),
141
141
  test: test?,
142
- avs_result: avs_result(action, succeeded, response),
143
- cvv_result: cvv_result(action, succeeded, response))
142
+ avs_result: avs_result,
143
+ cvv_result: cvv_result
144
+ )
144
145
  end
145
146
 
146
147
  def headers
@@ -162,20 +163,12 @@ module ActiveMerchant #:nodoc:
162
163
  test? ? test_url : live_url
163
164
  end
164
165
 
165
- def avs_result(action, succeeded, response)
166
- if succeeded
167
- action == :purchase ? AVSResult.new(code: response["card"]["avsCheck"]) : nil
168
- else
169
- nil
170
- end
166
+ def avs_result(response)
167
+ response['card'] && response['card']['avsCheck'] ? AVSResult.new(code: response['card']['avsCheck']) : nil
171
168
  end
172
169
 
173
- def cvv_result(action, succeeded, response)
174
- if succeeded
175
- action == :purchase ? CVVResult.new(response["card"]["cvvCheck"]) : nil
176
- else
177
- nil
178
- end
170
+ def cvv_result(response)
171
+ response['card'] && response['card']['cvvCheck'] ? CVVResult.new(response['card']['cvvCheck']) : nil
179
172
  end
180
173
 
181
174
  def parse(body)
@@ -129,6 +129,7 @@ module ActiveMerchant #:nodoc:
129
129
  add_email(post, options)
130
130
  add_3d_secure(post, options)
131
131
  add_echo(post, options)
132
+ add_transaction_type(post, options)
132
133
 
133
134
  commit(:purchase, post)
134
135
  end
@@ -141,6 +142,7 @@ module ActiveMerchant #:nodoc:
141
142
  add_email(post, options)
142
143
  add_3d_secure(post, options)
143
144
  add_echo(post, options)
145
+ add_transaction_type(post, options)
144
146
 
145
147
  commit(:authorize, post)
146
148
  end
@@ -182,7 +184,8 @@ module ActiveMerchant #:nodoc:
182
184
  add_customer_data(post, options)
183
185
  add_email(post, options)
184
186
  add_echo(post, options)
185
-
187
+ add_transaction_type(post, options)
188
+
186
189
  commit(:credit, post)
187
190
  end
188
191
 
@@ -264,6 +267,10 @@ module ActiveMerchant #:nodoc:
264
267
  post[:d2] = options[:echo] unless options[:echo].blank?
265
268
  end
266
269
 
270
+ def add_transaction_type(post, options)
271
+ post[:a9] = options[:transaction_type] if options[:transaction_type]
272
+ end
273
+
267
274
  ACTIONS = {
268
275
  purchase: '1',
269
276
  authorize: '2',
@@ -237,13 +237,24 @@ module ActiveMerchant #:nodoc:
237
237
  xml.tag! "CardHoldersName", credit_card.name
238
238
  xml.tag! "CardType", card_type(credit_card.brand)
239
239
 
240
- eci = (credit_card.respond_to?(:eci) ? credit_card.eci : nil) || options[:eci] || DEFAULT_ECI
241
- xml.tag! "Ecommerce_Flag", eci.to_s =~ /^[0-9]+$/ ? eci.to_s.rjust(2, '0') : eci
242
-
240
+ add_credit_card_eci(xml, credit_card, options)
243
241
  add_credit_card_verification_strings(xml, credit_card, options)
244
242
  end
245
243
  end
246
244
 
245
+ def add_credit_card_eci(xml, credit_card, options)
246
+ eci = if credit_card.is_a?(NetworkTokenizationCreditCard) && credit_card.source == :apple_pay && card_brand(credit_card) == "discover"
247
+ # Discover requires any Apple Pay transaction, regardless of in-app
248
+ # or web, and regardless of the ECI contained in the PKPaymentToken,
249
+ # to have an ECI value explicitly of 04.
250
+ "04"
251
+ else
252
+ (credit_card.respond_to?(:eci) ? credit_card.eci : nil) || options[:eci] || DEFAULT_ECI
253
+ end
254
+
255
+ xml.tag! "Ecommerce_Flag", eci.to_s =~ /^[0-9]+$/ ? eci.to_s.rjust(2, '0') : eci
256
+ end
257
+
247
258
  def add_credit_card_verification_strings(xml, credit_card, options)
248
259
  address = options[:billing_address] || options[:address]
249
260
  if address
@@ -30,7 +30,7 @@ module ActiveMerchant #:nodoc:
30
30
  def authorize(money, payment, options={})
31
31
  post = nestable_hash
32
32
  add_order(post, money, options)
33
- add_payment(post, payment)
33
+ add_payment(post, payment, options)
34
34
  add_customer_data(post, options, payment)
35
35
  add_address(post, payment, options)
36
36
 
@@ -106,15 +106,17 @@ module ActiveMerchant #:nodoc:
106
106
  }
107
107
  end
108
108
 
109
- def add_payment(post, payment)
109
+ def add_payment(post, payment, options)
110
110
  year = format(payment.year, :two_digits)
111
111
  month = format(payment.month, :two_digits)
112
112
  expirydate = "#{month}#{year}"
113
+ pre_authorization = options[:pre_authorization] ? 'PRE_AUTHORIZATION' : 'FINAL_AUTHORIZATION'
113
114
 
114
115
  post["cardPaymentMethodSpecificInput"] = {
115
116
  "paymentProductId" => BRAND_MAP[payment.brand],
116
117
  "skipAuthentication" => "true", # refers to 3DSecure
117
- "skipFraudService" => "true"
118
+ "skipFraudService" => "true",
119
+ "authorizationMode" => pre_authorization
118
120
  }
119
121
  post["cardPaymentMethodSpecificInput"]["card"] = {
120
122
  "cvv" => payment.verification_value,
@@ -225,7 +225,7 @@ module ActiveMerchant #:nodoc:
225
225
  end
226
226
 
227
227
  def successful_result_message?(response)
228
- response[:authorization_result].start_with?('OK')
228
+ response[:authorization_result] ? response[:authorization_result].start_with?('OK') : false
229
229
  end
230
230
 
231
231
  def success_from(response)
@@ -233,7 +233,7 @@ module ActiveMerchant #:nodoc:
233
233
  end
234
234
 
235
235
  def message_from(response)
236
- if(!successful_result_message?(response))
236
+ if !successful_result_message?(response) && response[:authorization_result]
237
237
  return response[:authorization_result].strip
238
238
  elsif(response[:status] == 'Failure')
239
239
  return response[:errors]
@@ -114,9 +114,11 @@ module ActiveMerchant #:nodoc:
114
114
 
115
115
  def add_additional_data(post, options)
116
116
  post[:sponsor_id] = options[:sponsor_id]
117
+ post[:device_id] = options[:device_id] if options[:device_id]
117
118
  post[:additional_info] = {
118
119
  ip_address: options[:ip_address]
119
- }
120
+ }.merge(options[:additional_info] || {})
121
+
120
122
 
121
123
  add_address(post, options)
122
124
  add_shipping_address(post, options)
@@ -191,7 +193,7 @@ module ActiveMerchant #:nodoc:
191
193
  if ["capture", "void"].include?(action)
192
194
  response = parse(ssl_request(:put, url(path), post_data(parameters), headers))
193
195
  else
194
- response = parse(ssl_post(url(path), post_data(parameters), headers))
196
+ response = parse(ssl_post(url(path), post_data(parameters), headers(parameters)))
195
197
  end
196
198
 
197
199
  Response.new(
@@ -221,7 +223,7 @@ module ActiveMerchant #:nodoc:
221
223
  end
222
224
 
223
225
  def post_data(parameters = {})
224
- parameters.to_json
226
+ parameters.clone.tap { |p| p.delete(:device_id) }.to_json
225
227
  end
226
228
 
227
229
  def error_code_from(action, response)
@@ -236,13 +238,15 @@ module ActiveMerchant #:nodoc:
236
238
 
237
239
  def url(action)
238
240
  full_url = (test? ? test_url : live_url)
239
- full_url + "/#{action}?access_token=#{@options[:access_token]}"
241
+ full_url + "/#{action}?access_token=#{CGI.escape(@options[:access_token])}"
240
242
  end
241
243
 
242
- def headers
243
- {
244
+ def headers(options = {})
245
+ headers = {
244
246
  "Content-Type" => "application/json"
245
247
  }
248
+ headers['X-Device-Session-ID'] = options[:device_id] if options[:device_id]
249
+ headers
246
250
  end
247
251
 
248
252
  def handle_response(response)
@@ -1,6 +1,6 @@
1
1
  require 'active_merchant/billing/gateways/migs/migs_codes'
2
2
 
3
- require 'digest/md5' # Used in add_secure_hash
3
+ require 'openssl' # Used in add_secure_hash
4
4
 
5
5
  module ActiveMerchant #:nodoc:
6
6
  module Billing #:nodoc:
@@ -121,6 +121,13 @@ module ActiveMerchant #:nodoc:
121
121
  refund(money, authorization, options)
122
122
  end
123
123
 
124
+ def verify(credit_card, options={})
125
+ MultiResponse.run do |r|
126
+ r.process { authorize(100, credit_card, options) }
127
+ r.process(:ignore_result) { void(r.authorization, options) }
128
+ end
129
+ end
130
+
124
131
  # Checks the status of a previous transaction
125
132
  # This can be useful when a response is not received due to network issues
126
133
  #
@@ -187,7 +194,7 @@ module ActiveMerchant #:nodoc:
187
194
 
188
195
  response_hash = parse(data)
189
196
 
190
- expected_secure_hash = calculate_secure_hash(response_hash.reject{|k, v| k == :SecureHash}, @options[:secure_hash])
197
+ expected_secure_hash = calculate_secure_hash(response_hash, @options[:secure_hash])
191
198
  unless response_hash[:SecureHash] == expected_secure_hash
192
199
  raise SecurityError, "Secure Hash mismatch, response may be tampered with"
193
200
  end
@@ -245,6 +252,7 @@ module ActiveMerchant #:nodoc:
245
252
  end
246
253
 
247
254
  def commit(post)
255
+ add_secure_hash(post) if @options[:secure_hash]
248
256
  data = ssl_post self.merchant_hosted_url, post_data(post)
249
257
  response_hash = parse(data)
250
258
  response_object(response_hash)
@@ -290,12 +298,16 @@ module ActiveMerchant #:nodoc:
290
298
 
291
299
  def add_secure_hash(post)
292
300
  post[:SecureHash] = calculate_secure_hash(post, @options[:secure_hash])
301
+ post[:SecureHashType] = 'SHA256'
293
302
  end
294
303
 
295
304
  def calculate_secure_hash(post, secure_hash)
296
- sorted_values = post.sort_by(&:to_s).map(&:last)
297
- input = secure_hash + sorted_values.join
298
- Digest::MD5.hexdigest(input).upcase
305
+ input = post
306
+ .reject { |k| %i[SecureHash SecureHashType].include?(k) }
307
+ .sort
308
+ .map { |(k, v)| "vpc_#{k}=#{v}" }
309
+ .join('&')
310
+ OpenSSL::HMAC.hexdigest('SHA256', [secure_hash].pack('H*'), input).upcase
299
311
  end
300
312
  end
301
313
  end
@@ -0,0 +1,273 @@
1
+ require 'base64'
2
+ require 'digest'
3
+
4
+ module ActiveMerchant #:nodoc:
5
+ module Billing #:nodoc:
6
+ class PaymentezGateway < Gateway #:nodoc:
7
+ self.test_url = 'https://ccapi-stg.paymentez.com/v2/'
8
+ self.live_url = 'https://ccapi.paymentez.com/v2/'
9
+
10
+ self.supported_countries = %w[MX EC VE CO BR CL]
11
+ self.default_currency = 'USD'
12
+ self.supported_cardtypes = %i[visa master american_express diners_club]
13
+
14
+ self.homepage_url = 'https://secure.paymentez.com/'
15
+ self.display_name = 'Paymentez'
16
+
17
+ STANDARD_ERROR_CODE_MAPPING = {
18
+ 1 => :processing_error,
19
+ 6 => :card_declined,
20
+ 9 => :card_declined,
21
+ 10 => :processing_error,
22
+ 11 => :card_declined,
23
+ 12 => :config_error,
24
+ 13 => :config_error,
25
+ 19 => :invalid_cvc,
26
+ 20 => :config_error,
27
+ 21 => :card_declined,
28
+ 22 => :card_declined,
29
+ 23 => :card_declined,
30
+ 24 => :card_declined,
31
+ 25 => :card_declined,
32
+ 26 => :card_declined,
33
+ 27 => :card_declined,
34
+ 28 => :card_declined
35
+ }.freeze
36
+
37
+ CARD_MAPPING = {
38
+ 'visa' => 'vi',
39
+ 'master' => 'mc',
40
+ 'american_express' => 'ax',
41
+ 'diners_club' => 'di'
42
+ }.freeze
43
+
44
+ def initialize(options = {})
45
+ requires!(options, :application_code, :app_key)
46
+ super
47
+ end
48
+
49
+ def purchase(money, payment, options = {})
50
+ post = {}
51
+
52
+ add_invoice(post, money, options)
53
+ add_payment(post, payment)
54
+ add_customer_data(post, options)
55
+
56
+ commit_transaction('debit_cc', post)
57
+ end
58
+
59
+ def authorize(money, payment, options = {})
60
+ post = {}
61
+
62
+ add_invoice(post, money, options)
63
+ add_customer_data(post, options)
64
+
65
+ MultiResponse.run do |r|
66
+ r.process { store(payment, options) }
67
+ post[:card] = { token: r.authorization }
68
+ r.process { commit_transaction('authorize', post) }
69
+ end
70
+ end
71
+
72
+ def capture(_money, authorization, _options = {})
73
+ post = { transaction: { id: authorization } }
74
+
75
+ commit_transaction('capture', post)
76
+ end
77
+
78
+ def refund(_money, authorization, options = {})
79
+ void(authorization, options)
80
+ end
81
+
82
+ def void(authorization, _options = {})
83
+ post = { transaction: { id: authorization } }
84
+ commit_transaction('refund', post)
85
+ end
86
+
87
+ def verify(credit_card, options = {})
88
+ MultiResponse.run do |r|
89
+ r.process { authorize(100, credit_card, options) }
90
+ r.process { void(r.authorization, options) }
91
+ end
92
+ end
93
+
94
+ def store(credit_card, options = {})
95
+ post = {}
96
+
97
+ add_customer_data(post, options)
98
+ add_payment(post, credit_card)
99
+
100
+ response = commit_card('add', post)
101
+ if !response.success? && !(token = extract_previous_card_token(response)).nil?
102
+ unstore(token, options)
103
+ response = commit_card('add', post)
104
+ end
105
+ response
106
+ end
107
+
108
+ def unstore(identification, options = {})
109
+ post = { card: { token: identification }, user: { id: options[:user_id] }}
110
+ commit_card('delete', post)
111
+ end
112
+
113
+ def supports_scrubbing?
114
+ true
115
+ end
116
+
117
+ def scrub(transcript)
118
+ transcript
119
+ .gsub(%r{(\\?"number\\?":)(\\?"[^"]+\\?")}, '\1[FILTERED]')
120
+ .gsub(%r{(\\?"cvc\\?":)(\\?"[^"]+\\?")}, '\1[FILTERED]')
121
+ .gsub(%r{(Auth-Token: )([A-Za-z0-9=]+)}, '\1[FILTERED]')
122
+ end
123
+
124
+ private
125
+
126
+ def add_customer_data(post, options)
127
+ requires!(options, :user_id, :email)
128
+ post[:user] ||= {}
129
+ post[:user][:id] = options[:user_id]
130
+ post[:user][:email] = options[:email]
131
+ post[:user][:ip_address] = options[:ip] if options[:ip]
132
+ post[:user][:fiscal_number] = options[:fiscal_number] if options[:fiscal_number]
133
+ end
134
+
135
+ def add_invoice(post, money, options)
136
+ post[:session_id] = options[:session_id] if options[:session_id]
137
+
138
+ post[:order] ||= {}
139
+ post[:order][:amount] = amount(money).to_f
140
+ post[:order][:vat] = options[:vat] if options[:vat]
141
+ post[:order][:dev_reference] = options[:dev_reference] if options[:dev_reference]
142
+ post[:order][:description] = options[:description] if options[:description]
143
+ post[:order][:discount] = options[:discount] if options[:discount]
144
+ post[:order][:installments] = options[:installments] if options[:installments]
145
+ post[:order][:installments_type] = options[:installments_type] if options[:installments_type]
146
+ post[:order][:taxable_amount] = options[:taxable_amount] if options[:taxable_amount]
147
+ end
148
+
149
+ def add_payment(post, payment)
150
+ post[:card] ||= {}
151
+ post[:card][:number] = payment.number
152
+ post[:card][:holder_name] = payment.name
153
+ post[:card][:expiry_month] = payment.month
154
+ post[:card][:expiry_year] = payment.year
155
+ post[:card][:cvc] = payment.verification_value
156
+ post[:card][:type] = CARD_MAPPING[payment.brand]
157
+ end
158
+
159
+ def parse(body)
160
+ JSON.parse(body)
161
+ end
162
+
163
+ def commit_raw(object, action, parameters)
164
+ url = "#{(test? ? test_url : live_url)}#{object}/#{action}"
165
+
166
+ begin
167
+ raw_response = ssl_post(url, post_data(parameters), headers)
168
+ rescue ResponseError => e
169
+ raw_response = e.response.body
170
+ end
171
+ parse(raw_response)
172
+ end
173
+
174
+ def commit_transaction(action, parameters)
175
+ response = commit_raw('transaction', action, parameters)
176
+ Response.new(
177
+ success_from(response),
178
+ message_from(response),
179
+ response,
180
+ authorization: authorization_from(response),
181
+ test: test?,
182
+ error_code: error_code_from(response)
183
+ )
184
+ end
185
+
186
+ def commit_card(action, parameters)
187
+ response = commit_raw('card', action, parameters)
188
+ Response.new(
189
+ card_success_from(response),
190
+ card_message_from(response),
191
+ response,
192
+ authorization: card_authorization_from(response),
193
+ test: test?,
194
+ error_code: card_error_code_from(response)
195
+ )
196
+ end
197
+
198
+ def headers
199
+ {
200
+ 'Auth-Token' => authentication_code,
201
+ 'Content-Type' => 'application/json'
202
+ }
203
+ end
204
+
205
+ def success_from(response)
206
+ !response.include?('error') && (response['status'] || response['transaction']['status']) == 'success'
207
+ end
208
+
209
+ def card_success_from(response)
210
+ return false if response.include?('error')
211
+ return true if response['message'] == 'card deleted'
212
+ response['card']['status'] == 'valid'
213
+ end
214
+
215
+ def message_from(response)
216
+ if success_from(response)
217
+ response['transaction'] && response['transaction']['message']
218
+ else
219
+ response['error'] && response['error']['type']
220
+ end
221
+ end
222
+
223
+ def card_message_from(response)
224
+ if !response.include?('error')
225
+ response['message'] || response['card']['message']
226
+ else
227
+ response['error']['type']
228
+ end
229
+ end
230
+
231
+ def authorization_from(response)
232
+ response['transaction'] && response['transaction']['id']
233
+ end
234
+
235
+ def card_authorization_from(response)
236
+ response['card'] && response['card']['token']
237
+ end
238
+
239
+ def extract_previous_card_token(response)
240
+ match = /Card already added: (\d+)/.match(response.message)
241
+ match && match[1]
242
+ end
243
+
244
+ def post_data(parameters = {})
245
+ JSON.dump(parameters)
246
+ end
247
+
248
+ def error_code_from(response)
249
+ return if success_from(response)
250
+ if response['transaction']
251
+ detail = response['transaction']['status_detail']
252
+ if STANDARD_ERROR_CODE_MAPPING.include?(detail)
253
+ return STANDARD_ERROR_CODE[STANDARD_ERROR_CODE_MAPPING[detail]]
254
+ end
255
+ elsif response['error']
256
+ return STANDARD_ERROR_CODE[:config_error]
257
+ end
258
+ STANDARD_ERROR_CODE[:processing_error]
259
+ end
260
+
261
+ def card_error_code_from(response)
262
+ STANDARD_ERROR_CODE[:processing_error] unless card_success_from(response)
263
+ end
264
+
265
+ def authentication_code
266
+ timestamp = Time.new.to_i
267
+ unique_token = Digest::SHA256.hexdigest("#{@options[:app_key]}#{timestamp}")
268
+ authentication_string = "#{@options[:application_code]};#{timestamp};#{unique_token}"
269
+ Base64.encode64(authentication_string).delete("\n")
270
+ end
271
+ end
272
+ end
273
+ end
@@ -29,9 +29,8 @@ module ActiveMerchant #:nodoc:
29
29
  }
30
30
 
31
31
  def initialize(options={})
32
- requires!(options, :merchant_id, :account_id, :api_login, :api_key)
32
+ requires!(options, :merchant_id, :account_id, :api_login, :api_key, :payment_country)
33
33
  super
34
- @options[:payment_country] ||= options[:payment_country] if options[:payment_country]
35
34
  end
36
35
 
37
36
  def purchase(amount, payment_method, options={})
@@ -78,7 +77,7 @@ module ActiveMerchant #:nodoc:
78
77
 
79
78
  def verify(credit_card, options={})
80
79
  minimum = MINIMUMS[options[:currency].upcase] if options[:currency]
81
- amount = minimum || 100
80
+ amount = options[:verify_amount] || minimum || 100
82
81
 
83
82
  MultiResponse.run(:use_first_response) do |r|
84
83
  r.process { authorize(amount, credit_card, options) }
@@ -139,7 +138,7 @@ module ActiveMerchant #:nodoc:
139
138
 
140
139
  def add_transaction_elements(post, type, options)
141
140
  transaction = {}
142
- transaction[:paymentCountry] = @options[:payment_country] || (options[:billing_address][:country] if options[:billing_address])
141
+ transaction[:paymentCountry] = @options[:payment_country]
143
142
  transaction[:type] = type
144
143
  transaction[:ipAddress] = options[:ip] || ''
145
144
  transaction[:userAgent] = options[:user_agent] if options[:user_agent]
@@ -153,7 +152,7 @@ module ActiveMerchant #:nodoc:
153
152
  order[:accountId] = @options[:account_id]
154
153
  order[:partnerId] = options[:partner_id] if options[:partner_id]
155
154
  order[:referenceCode] = options[:order_id] || generate_unique_id
156
- order[:description] = options[:description] || 'unspecified'
155
+ order[:description] = options[:description] || 'Compra en ' + @options[:merchant_id]
157
156
  order[:language] = 'en'
158
157
  order[:shippingAddress] = shipping_address_fields(options) if options[:shipping_address]
159
158
  post[:transaction][:order] = order
@@ -167,7 +166,7 @@ module ActiveMerchant #:nodoc:
167
166
  payer[:dniNumber] = options[:dni_number] if options[:dni_number]
168
167
  payer[:dniType] = options[:dni_type] if options[:dni_type]
169
168
  payer[:emailAddress] = options[:email] if options[:email]
170
- payer[:birthdate] = options[:birth_date] if options[:birth_date] && options[:payment_country] == 'MX'
169
+ payer[:birthdate] = options[:birth_date] if options[:birth_date] && @options[:payment_country] == 'MX'
171
170
  payer[:billingAddress] = billing_address_fields(options)
172
171
  post[:transaction][:payer] = payer
173
172
  end
@@ -180,7 +179,7 @@ module ActiveMerchant #:nodoc:
180
179
  billing_address[:city] = address[:city]
181
180
  billing_address[:state] = address[:state]
182
181
  billing_address[:country] = address[:country]
183
- billing_address[:postalCode] = address[:zip] if options[:payment_country] == 'MX'
182
+ billing_address[:postalCode] = address[:zip] if @options[:payment_country] == 'MX'
184
183
  billing_address[:phone] = address[:phone]
185
184
  billing_address
186
185
  end
@@ -191,7 +190,7 @@ module ActiveMerchant #:nodoc:
191
190
  buyer[:fullName] = buyer_hash[:name]
192
191
  buyer[:dniNumber] = buyer_hash[:dni_number]
193
192
  buyer[:dniType] = buyer_hash[:dni_type]
194
- buyer[:cnpj] = buyer_hash[:cnpj] if options[:payment_country] == 'BR'
193
+ buyer[:cnpj] = buyer_hash[:cnpj] if @options[:payment_country] == 'BR'
195
194
  buyer[:emailAddress] = buyer_hash[:email]
196
195
  buyer[:contactPhone] = (options[:billing_address][:phone] if options[:billing_address]) || (options[:shipping_address][:phone] if options[:shipping_address]) || ''
197
196
  buyer[:shippingAddress] = shipping_address_fields(options) if options[:shipping_address]
@@ -199,7 +198,7 @@ module ActiveMerchant #:nodoc:
199
198
  buyer[:fullName] = payment_method.name.strip
200
199
  buyer[:dniNumber] = options[:dni_number]
201
200
  buyer[:dniType] = options[:dni_type]
202
- buyer[:cnpj] = options[:cnpj] if options[:payment_country] == 'BR'
201
+ buyer[:cnpj] = options[:cnpj] if @options[:payment_country] == 'BR'
203
202
  buyer[:emailAddress] = options[:email]
204
203
  buyer[:contactPhone] = (options[:billing_address][:phone] if options[:billing_address]) || (options[:shipping_address][:phone] if options[:shipping_address]) || ''
205
204
  buyer[:shippingAddress] = shipping_address_fields(options) if options[:shipping_address]
@@ -235,8 +234,8 @@ module ActiveMerchant #:nodoc:
235
234
 
236
235
  additional_values = {}
237
236
  additional_values[:TX_VALUE] = tx_value
238
- additional_values[:TX_TAX] = tx_tax if options[:payment_country] == 'CO'
239
- additional_values[:TX_TAX_RETURN_BASE] = tx_tax_return_base if options[:payment_country] == 'CO'
237
+ additional_values[:TX_TAX] = tx_tax if @options[:payment_country] == 'CO'
238
+ additional_values[:TX_TAX_RETURN_BASE] = tx_tax_return_base if @options[:payment_country] == 'CO'
240
239
 
241
240
  post[:transaction][:order][:additionalValues] = additional_values
242
241
  end
@@ -358,7 +357,7 @@ module ActiveMerchant #:nodoc:
358
357
  response["code"] == "SUCCESS" && response["creditCardToken"] && response["creditCardToken"]["creditCardTokenId"].present?
359
358
  when 'verify_credentials'
360
359
  response["code"] == "SUCCESS"
361
- when 'refund'
360
+ when 'refund', 'void'
362
361
  response["code"] == "SUCCESS" && response["transactionResponse"] && (response["transactionResponse"]["state"] == "PENDING" || response["transactionResponse"]["state"] == "APPROVED")
363
362
  else
364
363
  response["code"] == "SUCCESS" && response["transactionResponse"] && (response["transactionResponse"]["state"] == "APPROVED")
@@ -375,8 +374,10 @@ module ActiveMerchant #:nodoc:
375
374
  return "VERIFIED" if success
376
375
  "FAILED"
377
376
  else
378
- response_message = response["transactionResponse"]["responseMessage"] if response["transactionResponse"]
379
- response_code = response["transactionResponse"]["responseCode"] if response["transactionResponse"]
377
+ if response["transactionResponse"]
378
+ response_message = response["transactionResponse"]["responseMessage"]
379
+ response_code = response["transactionResponse"]["responseCode"] || response["transactionResponse"]["pendingReason"]
380
+ end
380
381
  return response_code if success
381
382
  response["error"] || response_message || response_code || "FAILED"
382
383
  end
@@ -78,6 +78,7 @@ module ActiveMerchant #:nodoc:
78
78
  post = {}
79
79
  capture_uri = "#{ENDPOINT}/#{CGI.escape(authorization)}/capture"
80
80
  post[:amount] = localized_amount(money, currency(money))
81
+ add_context(post, options)
81
82
 
82
83
  commit(capture_uri, post)
83
84
  end
@@ -85,6 +86,7 @@ module ActiveMerchant #:nodoc:
85
86
  def refund(money, authorization, options = {})
86
87
  post = {}
87
88
  post[:amount] = localized_amount(money, currency(money))
89
+ add_context(post, options)
88
90
 
89
91
  commit(refund_uri(authorization), post)
90
92
  end
@@ -137,6 +139,7 @@ module ActiveMerchant #:nodoc:
137
139
 
138
140
  def add_payment(post, payment, options = {})
139
141
  add_creditcard(post, payment, options)
142
+ add_context(post, options)
140
143
  end
141
144
 
142
145
  def add_creditcard(post, creditcard, options = {})
@@ -151,6 +154,13 @@ module ActiveMerchant #:nodoc:
151
154
  post[:card] = card
152
155
  end
153
156
 
157
+ def add_context(post, options = {})
158
+ post[:context] = {
159
+ mobile: options.fetch(:mobile, false),
160
+ isEcommerce: options.fetch(:ecommerce, true)
161
+ }
162
+ end
163
+
154
164
  def parse(body)
155
165
  JSON.parse(body)
156
166
  end
@@ -22,7 +22,9 @@ module ActiveMerchant #:nodoc:
22
22
 
23
23
  def purchase(money, payment, options={})
24
24
  post = {}
25
- add_transaction_data("Sale", post, money, options)
25
+ post[:sg_APIType] = 1 if options[:three_d_secure]
26
+ trans_type = options[:three_d_secure] ? "Sale3D" : "Sale"
27
+ add_transaction_data(trans_type, post, money, options)
26
28
  add_payment(post, payment)
27
29
  add_customer_details(post, payment, options)
28
30
 
@@ -120,6 +122,9 @@ module ActiveMerchant #:nodoc:
120
122
  post[:sg_UserID] = options[:user_id] if options[:user_id]
121
123
  post[:sg_AuthType] = options[:auth_type] if options[:auth_type]
122
124
  post[:sg_ExpectedFulfillmentCount] = options[:expected_fulfillment_count] if options[:expected_fulfillment_count]
125
+ post[:sg_WebsiteID] = options[:website_id] if options[:website_id]
126
+ post[:sg_IPAddress] = options[:ip] if options[:ip]
127
+ post[:sg_VendorID] = options[:vendor_id] if options[:vendor_id]
123
128
  end
124
129
 
125
130
  def add_payment(post, payment)
@@ -150,21 +155,32 @@ module ActiveMerchant #:nodoc:
150
155
 
151
156
  doc = Nokogiri::XML(xml)
152
157
  doc.root.xpath('*').each do |node|
153
- response[node.name.underscore.downcase.to_sym] = node.text
158
+ if node.elements.size == 0
159
+ response[node.name.underscore.downcase.to_sym] = node.text
160
+ else
161
+ node.traverse do |childnode|
162
+ childnode_to_response(response, childnode)
163
+ end
164
+ end
154
165
  end
155
-
156
166
  response
157
167
  end
158
168
 
159
- def childnode_to_response(response, node, childnode)
160
- name = "#{node.name.downcase}_#{childnode.name.downcase}"
161
- if name == 'payment_method_data' && !childnode.elements.empty?
162
- response[name.to_sym] = Hash.from_xml(childnode.to_s).values.first
169
+ def childnode_to_response(response, childnode)
170
+ if childnode.elements.size == 0
171
+ element_name_to_symbol(response, childnode)
163
172
  else
164
- response[name.to_sym] = childnode.text
173
+ childnode.traverse do |childnode|
174
+ element_name_to_symbol(response, childnode)
175
+ end
165
176
  end
166
177
  end
167
178
 
179
+ def element_name_to_symbol(response, childnode)
180
+ name = "#{childnode.name.downcase}"
181
+ response[name.to_sym] = childnode.text
182
+ end
183
+
168
184
  def commit(parameters)
169
185
  url = (test? ? test_url : live_url)
170
186
  response = parse(ssl_post(url, post_data(parameters)))
@@ -37,13 +37,20 @@ module ActiveMerchant #:nodoc:
37
37
  :jcb => "JCB"
38
38
  }
39
39
 
40
- AVS_CVV_CODE = {
40
+ AVS_CODE = {
41
41
  "NOTPROVIDED" => nil,
42
42
  "NOTCHECKED" => 'X',
43
43
  "MATCHED" => 'Y',
44
44
  "NOTMATCHED" => 'N'
45
45
  }
46
46
 
47
+ CVV_CODE = {
48
+ "NOTPROVIDED" => 'S',
49
+ "NOTCHECKED" => 'X',
50
+ "MATCHED" => 'M',
51
+ "NOTMATCHED" => 'N'
52
+ }
53
+
47
54
  OPTIONAL_REQUEST_FIELDS = {
48
55
  paypal_callback_url: :PayPalCallbackURL,
49
56
  basket: :Basket,
@@ -348,10 +355,10 @@ module ActiveMerchant #:nodoc:
348
355
  :test => test?,
349
356
  :authorization => authorization_from(response, parameters, action),
350
357
  :avs_result => {
351
- :street_match => AVS_CVV_CODE[ response["AddressResult"] ],
352
- :postal_match => AVS_CVV_CODE[ response["PostCodeResult"] ],
358
+ :street_match => AVS_CODE[ response["AddressResult"] ],
359
+ :postal_match => AVS_CODE[ response["PostCodeResult"] ],
353
360
  },
354
- :cvv_result => AVS_CVV_CODE[ response["CV2Result"] ]
361
+ :cvv_result => CVV_CODE[ response["CV2Result"] ]
355
362
  )
356
363
  end
357
364
 
@@ -25,7 +25,7 @@ module ActiveMerchant
25
25
  attr_accessor :ca_path
26
26
  attr_accessor :pem
27
27
  attr_accessor :pem_password
28
- attr_accessor :wiredump_device
28
+ attr_reader :wiredump_device
29
29
  attr_accessor :logger
30
30
  attr_accessor :tag
31
31
  attr_accessor :ignore_http_status
@@ -48,6 +48,11 @@ module ActiveMerchant
48
48
  @proxy_port = nil
49
49
  end
50
50
 
51
+ def wiredump_device=(device)
52
+ raise ArgumentError, "can't wiredump to frozen #{device.class}" if device && device.frozen?
53
+ @wiredump_device = device
54
+ end
55
+
51
56
  def request(method, body, headers = {})
52
57
  request_start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
53
58
 
@@ -1,3 +1,3 @@
1
1
  module ActiveMerchant
2
- VERSION = "1.75.0"
2
+ VERSION = "1.76.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activemerchant
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.75.0
4
+ version: 1.76.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tobias Luetke
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-11-09 00:00:00.000000000 Z
11
+ date: 2018-01-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -190,6 +190,7 @@ files:
190
190
  - lib/active_merchant/billing/gateways/card_save.rb
191
191
  - lib/active_merchant/billing/gateways/card_stream.rb
192
192
  - lib/active_merchant/billing/gateways/cardknox.rb
193
+ - lib/active_merchant/billing/gateways/cardprocess.rb
193
194
  - lib/active_merchant/billing/gateways/cashnet.rb
194
195
  - lib/active_merchant/billing/gateways/cc5.rb
195
196
  - lib/active_merchant/billing/gateways/cecabank.rb
@@ -307,6 +308,7 @@ files:
307
308
  - lib/active_merchant/billing/gateways/payflow_express_uk.rb
308
309
  - lib/active_merchant/billing/gateways/payflow_uk.rb
309
310
  - lib/active_merchant/billing/gateways/payment_express.rb
311
+ - lib/active_merchant/billing/gateways/paymentez.rb
310
312
  - lib/active_merchant/billing/gateways/paymill.rb
311
313
  - lib/active_merchant/billing/gateways/paypal.rb
312
314
  - lib/active_merchant/billing/gateways/paypal/paypal_common_api.rb