activemerchant 1.55.0 → 1.56.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.
- checksums.yaml +4 -4
- data/CHANGELOG +22 -0
- data/CONTRIBUTORS +4 -0
- data/README.md +1 -0
- data/lib/active_merchant.rb +0 -1
- data/lib/active_merchant/billing/credit_card_methods.rb +5 -0
- data/lib/active_merchant/billing/gateways/bridge_pay.rb +56 -29
- data/lib/active_merchant/billing/gateways/card_stream.rb +2 -1
- data/lib/active_merchant/billing/gateways/cardknox.rb +328 -0
- data/lib/active_merchant/billing/gateways/cashnet.rb +4 -0
- data/lib/active_merchant/billing/gateways/creditcall.rb +3 -1
- data/lib/active_merchant/billing/gateways/elavon.rb +6 -0
- data/lib/active_merchant/billing/gateways/firstdata_e4.rb +1 -1
- data/lib/active_merchant/billing/gateways/garanti.rb +4 -2
- data/lib/active_merchant/billing/gateways/jetpay.rb +3 -2
- data/lib/active_merchant/billing/gateways/mercury.rb +10 -1
- data/lib/active_merchant/billing/gateways/micropayment.rb +20 -6
- data/lib/active_merchant/billing/gateways/omise.rb +5 -4
- data/lib/active_merchant/billing/gateways/redsys.rb +107 -21
- data/lib/active_merchant/billing/gateways/stripe.rb +17 -3
- data/lib/active_merchant/billing/gateways/trans_first.rb +107 -18
- data/lib/active_merchant/version.rb +1 -1
- metadata +6 -5
@@ -1,6 +1,8 @@
|
|
1
1
|
module ActiveMerchant #:nodoc:
|
2
2
|
module Billing #:nodoc:
|
3
3
|
class CashnetGateway < Gateway
|
4
|
+
include Empty
|
5
|
+
|
4
6
|
self.live_url = "https://commerce.cashnet.com/"
|
5
7
|
|
6
8
|
self.supported_countries = ["US"]
|
@@ -47,6 +49,7 @@ module ActiveMerchant #:nodoc:
|
|
47
49
|
post = {}
|
48
50
|
post[:origtx] = identification
|
49
51
|
add_invoice(post, options)
|
52
|
+
add_customer_data(post, options)
|
50
53
|
commit('REFUND', money, post)
|
51
54
|
end
|
52
55
|
|
@@ -106,6 +109,7 @@ module ActiveMerchant #:nodoc:
|
|
106
109
|
|
107
110
|
def add_customer_data(post, options)
|
108
111
|
post[:email_g] = options[:email]
|
112
|
+
post[:custcode] = options[:custcode] unless empty?(options[:custcode])
|
109
113
|
end
|
110
114
|
|
111
115
|
def expdate(creditcard)
|
@@ -3,6 +3,8 @@ require 'nokogiri'
|
|
3
3
|
module ActiveMerchant #:nodoc:
|
4
4
|
module Billing #:nodoc:
|
5
5
|
class CreditcallGateway < Gateway
|
6
|
+
include Empty
|
7
|
+
|
6
8
|
self.test_url = 'https://test.cardeasexml.com/generic.cex'
|
7
9
|
self.live_url = 'https://live.cardeasexml.com/generic.cex'
|
8
10
|
|
@@ -121,7 +123,7 @@ module ActiveMerchant #:nodoc:
|
|
121
123
|
xml.Manual(type: "cnp") do
|
122
124
|
xml.PAN payment_method.number
|
123
125
|
xml.ExpiryDate exp_date(payment_method)
|
124
|
-
xml.CSC payment_method.verification_value
|
126
|
+
xml.CSC payment_method.verification_value unless empty?(payment_method.verification_value)
|
125
127
|
end
|
126
128
|
|
127
129
|
if address = options[:billing_address]
|
@@ -82,6 +82,7 @@ module ActiveMerchant #:nodoc:
|
|
82
82
|
add_address(form, options)
|
83
83
|
add_customer_data(form, options)
|
84
84
|
add_test_mode(form, options)
|
85
|
+
add_ip(form, options)
|
85
86
|
commit(:purchase, money, form)
|
86
87
|
end
|
87
88
|
|
@@ -100,6 +101,7 @@ module ActiveMerchant #:nodoc:
|
|
100
101
|
add_address(form, options)
|
101
102
|
add_customer_data(form, options)
|
102
103
|
add_test_mode(form, options)
|
104
|
+
add_ip(form, options)
|
103
105
|
commit(:authorize, money, form)
|
104
106
|
end
|
105
107
|
|
@@ -296,6 +298,10 @@ module ActiveMerchant #:nodoc:
|
|
296
298
|
form[:partial_shipment_flag] = 'Y' if options[:partial_shipment_flag]
|
297
299
|
end
|
298
300
|
|
301
|
+
def add_ip(form, options)
|
302
|
+
form[:cardholder_ip] = options[:ip] if options.has_key?(:ip)
|
303
|
+
end
|
304
|
+
|
299
305
|
def message_from(response)
|
300
306
|
success?(response) ? response['result_message'] : response['errorMessage']
|
301
307
|
end
|
@@ -1,7 +1,8 @@
|
|
1
1
|
module ActiveMerchant #:nodoc:
|
2
2
|
module Billing #:nodoc:
|
3
3
|
class GarantiGateway < Gateway
|
4
|
-
self.live_url =
|
4
|
+
self.live_url = 'https://sanalposprov.garanti.com.tr/VPServlet'
|
5
|
+
self.test_url = 'https://sanalposprovtest.garanti.com.tr/VPServlet'
|
5
6
|
|
6
7
|
# The countries the gateway supports merchants from as 2 digit ISO country codes
|
7
8
|
self.supported_countries = ['US','TR']
|
@@ -217,7 +218,8 @@ module ActiveMerchant #:nodoc:
|
|
217
218
|
end
|
218
219
|
|
219
220
|
def commit(money,request)
|
220
|
-
|
221
|
+
url = test? ? self.test_url : self.live_url
|
222
|
+
raw_response = ssl_post(url, "data=" + request)
|
221
223
|
response = parse(raw_response)
|
222
224
|
|
223
225
|
success = success?(response)
|
@@ -162,7 +162,7 @@ module ActiveMerchant #:nodoc:
|
|
162
162
|
end
|
163
163
|
|
164
164
|
def capture(money, reference, options = {})
|
165
|
-
commit(money, build_capture_request(reference.split(";").first, money))
|
165
|
+
commit(money, build_capture_request(reference.split(";").first, money, options))
|
166
166
|
end
|
167
167
|
|
168
168
|
def void(reference, options = {})
|
@@ -242,9 +242,10 @@ module ActiveMerchant #:nodoc:
|
|
242
242
|
end
|
243
243
|
end
|
244
244
|
|
245
|
-
def build_capture_request(transaction_id, money)
|
245
|
+
def build_capture_request(transaction_id, money, options)
|
246
246
|
build_xml_request('CAPT', transaction_id) do |xml|
|
247
247
|
xml.tag! 'TotalAmount', amount(money)
|
248
|
+
add_user_defined_fields(xml, options)
|
248
249
|
end
|
249
250
|
end
|
250
251
|
|
@@ -195,7 +195,16 @@ module ActiveMerchant #:nodoc:
|
|
195
195
|
def add_credit_card(xml, credit_card, action)
|
196
196
|
xml.tag! 'Account' do
|
197
197
|
if credit_card.track_data.present?
|
198
|
-
|
198
|
+
# Track 1 has a start sentinel (STX) of '%' and track 2 is ';'
|
199
|
+
# Track 1 and 2 have identical end sentinels (ETX) of '?'
|
200
|
+
# If the track has no STX or is corrupt, we send it as track 1, to let Mercury
|
201
|
+
#handle with the validation error as it sees fit.
|
202
|
+
# Track 2 requires having the start and end sentinels stripped. Track 1 does not.
|
203
|
+
if credit_card.track_data[0] == ';' # track 2 start sentinel (STX)
|
204
|
+
xml.tag! 'Track2', credit_card.track_data[1..-2]
|
205
|
+
else # track 1 or a corrupt track
|
206
|
+
xml.tag! 'Track1', credit_card.track_data
|
207
|
+
end
|
199
208
|
else
|
200
209
|
xml.tag! 'AcctNo', credit_card.number
|
201
210
|
xml.tag! 'ExpDate', expdate(credit_card)
|
@@ -23,6 +23,7 @@ module ActiveMerchant #:nodoc:
|
|
23
23
|
add_invoice(post, amount, options)
|
24
24
|
add_payment_method(post, payment_method, options)
|
25
25
|
add_customer_data(post, options)
|
26
|
+
add_address(post, options)
|
26
27
|
commit("purchase", post)
|
27
28
|
end
|
28
29
|
|
@@ -31,6 +32,7 @@ module ActiveMerchant #:nodoc:
|
|
31
32
|
add_invoice(post, amount, options)
|
32
33
|
add_payment_method(post, payment_method, options)
|
33
34
|
add_customer_data(post, options)
|
35
|
+
add_address(post, options)
|
34
36
|
commit("authorize", post)
|
35
37
|
end
|
36
38
|
|
@@ -81,22 +83,34 @@ module ActiveMerchant #:nodoc:
|
|
81
83
|
post[:currency] = options[:currency] || currency(money)
|
82
84
|
end
|
83
85
|
post[:project] = options[:project] if options[:project]
|
86
|
+
post["params[title]"] = options[:description] if options[:description]
|
84
87
|
end
|
85
88
|
|
86
89
|
def add_payment_method(post, payment_method, options={})
|
87
|
-
post[:firstname] = payment_method.first_name
|
88
|
-
post[:surname] = payment_method.last_name
|
89
90
|
post[:number] = payment_method.number
|
90
91
|
post[:recurring] = 1 if options[:recurring] == true
|
91
92
|
post[:cvc2] = payment_method.verification_value
|
92
93
|
post[:expiryYear] = format(payment_method.year, :four_digits)
|
93
94
|
post[:expiryMonth] = format(payment_method.month, :two_digits)
|
95
|
+
|
96
|
+
post["params[firstname]"] = payment_method.first_name
|
97
|
+
post["params[surname]"] = payment_method.last_name
|
94
98
|
end
|
95
99
|
|
96
100
|
def add_customer_data(post, options)
|
97
|
-
post[
|
98
|
-
post[
|
99
|
-
post[
|
101
|
+
post["params[email]"] = options[:email] if options[:email]
|
102
|
+
post["params[ip]"] = options[:ip] || "1.1.1.1"
|
103
|
+
post["params[sendMail]"] = options[:send_mail] || 'false'
|
104
|
+
end
|
105
|
+
|
106
|
+
def add_address(post, options)
|
107
|
+
address = options[:billing_address]
|
108
|
+
return unless address
|
109
|
+
|
110
|
+
post["params[address]"] = address[:address1] if address[:address1]
|
111
|
+
post["params[zipcode]"] = address[:zip] if address[:zip]
|
112
|
+
post["params[town]"] = address[:city] if address[:city]
|
113
|
+
post["params[country]"] = address[:country] if address[:country]
|
100
114
|
end
|
101
115
|
|
102
116
|
def add_reference(post, authorization)
|
@@ -128,7 +142,7 @@ module ActiveMerchant #:nodoc:
|
|
128
142
|
end
|
129
143
|
|
130
144
|
def post_data(action, params)
|
131
|
-
params.map {|k, v| "#{k}=#{CGI.escape(v.to_s)}"}.join('&')
|
145
|
+
params.map {|k, v| "#{CGI.escape(k.to_s)}=#{CGI.escape(v.to_s)}"}.join('&')
|
132
146
|
end
|
133
147
|
|
134
148
|
def url(action)
|
@@ -3,7 +3,6 @@ require 'active_merchant/billing/rails'
|
|
3
3
|
module ActiveMerchant #:nodoc:
|
4
4
|
module Billing #:nodoc:
|
5
5
|
class OmiseGateway < Gateway
|
6
|
-
API_VERSION = '1.0'
|
7
6
|
API_URL = 'https://api.omise.co/'
|
8
7
|
VAULT_URL = 'https://vault.omise.co/'
|
9
8
|
|
@@ -45,8 +44,9 @@ module ActiveMerchant #:nodoc:
|
|
45
44
|
|
46
45
|
def initialize(options={})
|
47
46
|
requires!(options, :public_key, :secret_key)
|
48
|
-
@public_key
|
49
|
-
@secret_key
|
47
|
+
@public_key = options[:public_key]
|
48
|
+
@secret_key = options[:secret_key]
|
49
|
+
@api_version = options[:api_version]
|
50
50
|
super
|
51
51
|
end
|
52
52
|
|
@@ -178,7 +178,8 @@ module ActiveMerchant #:nodoc:
|
|
178
178
|
key = options[:key] || @secret_key
|
179
179
|
{
|
180
180
|
'Content-Type' => 'application/json;utf-8',
|
181
|
-
'
|
181
|
+
'Omise-Version' => @api_version || "2014-07-27",
|
182
|
+
'User-Agent' => "ActiveMerchantBindings/#{ActiveMerchant::VERSION} Ruby/#{RUBY_VERSION}",
|
182
183
|
'Authorization' => 'Basic ' + Base64.encode64(key.to_s + ':').strip,
|
183
184
|
'Accept-Encoding' => 'utf-8'
|
184
185
|
}
|
@@ -24,6 +24,16 @@ module ActiveMerchant #:nodoc:
|
|
24
24
|
#
|
25
25
|
# Written by Samuel Lown for Cabify. For implementation questions, or
|
26
26
|
# test access details please get in touch: sam@cabify.com.
|
27
|
+
#
|
28
|
+
# *** SHA256 Authentication Update ***
|
29
|
+
#
|
30
|
+
# Redsys is dropping support for the SHA1 authentication method. This
|
31
|
+
# adapter has been updated to work with the new SHA256 authentication
|
32
|
+
# method, however in your initialization options hash you will need to
|
33
|
+
# specify the key/value :signature_algorithm => "sha256" to use the
|
34
|
+
# SHA256 method. Otherwise it will default to using the SHA1.
|
35
|
+
#
|
36
|
+
#
|
27
37
|
class RedsysGateway < Gateway
|
28
38
|
self.live_url = "https://sis.sermepa.es/sis/operaciones"
|
29
39
|
self.test_url = "https://sis-t.redsys.es:25443/sis/operaciones"
|
@@ -161,9 +171,11 @@ module ActiveMerchant #:nodoc:
|
|
161
171
|
# * <tt>:secret_key</tt> -- The Redsys Secret Key. (REQUIRED)
|
162
172
|
# * <tt>:terminal</tt> -- The Redsys Terminal. Defaults to 1. (OPTIONAL)
|
163
173
|
# * <tt>:test</tt> -- +true+ or +false+. Defaults to +false+. (OPTIONAL)
|
174
|
+
# * <tt>:signature_algorithm</tt> -- +"sha256"+ Defaults to +"sha1"+. (OPTIONAL)
|
164
175
|
def initialize(options = {})
|
165
176
|
requires!(options, :login, :secret_key)
|
166
177
|
options[:terminal] ||= 1
|
178
|
+
options[:signature_algorithm] ||= "sha1"
|
167
179
|
super
|
168
180
|
end
|
169
181
|
|
@@ -247,6 +259,7 @@ module ActiveMerchant #:nodoc:
|
|
247
259
|
gsub(%r((<DS_MERCHANT_PAN>)\d+(</DS_MERCHANT_PAN>))i, '\1[FILTERED]\2').
|
248
260
|
gsub(%r((<DS_MERCHANT_CVV2>)\d+(</DS_MERCHANT_CVV2>))i, '\1[FILTERED]\2').
|
249
261
|
gsub(%r((DS_MERCHANT_CVV2)%2F%3E%0A%3C%2F)i, '\1[BLANK]').
|
262
|
+
gsub(%r((DS_MERCHANT_CVV2)%2F%3E%3C)i, '\1[BLANK]').
|
250
263
|
gsub(%r((DS_MERCHANT_CVV2%3E)(%3C%2FDS_MERCHANT_CVV2))i, '\1[BLANK]\2').
|
251
264
|
gsub(%r((<DS_MERCHANT_CVV2>)(</DS_MERCHANT_CVV2>))i, '\1[BLANK]\2').
|
252
265
|
gsub(%r((DS_MERCHANT_CVV2%3E)\++(%3C%2FDS_MERCHANT_CVV2))i, '\1[BLANK]\2').
|
@@ -289,18 +302,28 @@ module ActiveMerchant #:nodoc:
|
|
289
302
|
end
|
290
303
|
|
291
304
|
def commit(data)
|
292
|
-
|
305
|
+
parse(ssl_post(url, "entrada=#{CGI.escape(xml_request_from(data))}", headers))
|
306
|
+
end
|
307
|
+
|
308
|
+
def headers
|
309
|
+
{
|
293
310
|
'Content-Type' => 'application/x-www-form-urlencoded'
|
294
311
|
}
|
295
|
-
|
296
|
-
|
312
|
+
end
|
313
|
+
|
314
|
+
def xml_request_from(data)
|
315
|
+
if sha256_authentication?
|
316
|
+
build_sha256_xml_request(data)
|
317
|
+
else
|
318
|
+
build_sha1_xml_request(data)
|
319
|
+
end
|
297
320
|
end
|
298
321
|
|
299
322
|
def build_signature(data)
|
300
323
|
str = data[:amount] +
|
301
|
-
|
302
|
-
|
303
|
-
|
324
|
+
data[:order_id].to_s +
|
325
|
+
@options[:login].to_s +
|
326
|
+
data[:currency]
|
304
327
|
|
305
328
|
if card = data[:card]
|
306
329
|
str << card[:pan]
|
@@ -318,8 +341,30 @@ module ActiveMerchant #:nodoc:
|
|
318
341
|
Digest::SHA1.hexdigest(str)
|
319
342
|
end
|
320
343
|
|
321
|
-
def
|
344
|
+
def build_sha256_xml_request(data)
|
345
|
+
xml = Builder::XmlMarkup.new
|
346
|
+
xml.instruct!
|
347
|
+
xml.REQUEST do
|
348
|
+
build_merchant_data(xml, data)
|
349
|
+
xml.DS_SIGNATUREVERSION 'HMAC_SHA256_V1'
|
350
|
+
xml.DS_SIGNATURE sign_request(merchant_data_xml(data), data[:order_id])
|
351
|
+
end
|
352
|
+
xml.target!
|
353
|
+
end
|
354
|
+
|
355
|
+
def build_sha1_xml_request(data)
|
322
356
|
xml = Builder::XmlMarkup.new :indent => 2
|
357
|
+
build_merchant_data(xml, data)
|
358
|
+
xml.target!
|
359
|
+
end
|
360
|
+
|
361
|
+
def merchant_data_xml(data)
|
362
|
+
xml = Builder::XmlMarkup.new
|
363
|
+
build_merchant_data(xml, data)
|
364
|
+
xml.target!
|
365
|
+
end
|
366
|
+
|
367
|
+
def build_merchant_data(xml, data)
|
323
368
|
xml.DATOSENTRADA do
|
324
369
|
# Basic elements
|
325
370
|
xml.DS_Version 0.1
|
@@ -330,7 +375,7 @@ module ActiveMerchant #:nodoc:
|
|
330
375
|
xml.DS_MERCHANT_PRODUCTDESCRIPTION data[:description]
|
331
376
|
xml.DS_MERCHANT_TERMINAL @options[:terminal]
|
332
377
|
xml.DS_MERCHANT_MERCHANTCODE @options[:login]
|
333
|
-
xml.DS_MERCHANT_MERCHANTSIGNATURE build_signature(data)
|
378
|
+
xml.DS_MERCHANT_MERCHANTSIGNATURE build_signature(data) unless sha256_authentication?
|
334
379
|
|
335
380
|
# Only when card is present
|
336
381
|
if data[:card]
|
@@ -343,7 +388,6 @@ module ActiveMerchant #:nodoc:
|
|
343
388
|
xml.DS_MERCHANT_IDENTIFIER data[:credit_card_token]
|
344
389
|
end
|
345
390
|
end
|
346
|
-
xml.target!
|
347
391
|
end
|
348
392
|
|
349
393
|
def parse(data)
|
@@ -375,18 +419,23 @@ module ActiveMerchant #:nodoc:
|
|
375
419
|
end
|
376
420
|
|
377
421
|
def validate_signature(data)
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
422
|
+
if sha256_authentication?
|
423
|
+
sig = Base64.strict_encode64(mac256(get_key(data[:ds_order].to_s), xml_signed_fields(data)))
|
424
|
+
sig.upcase == data[:ds_signature].to_s.upcase
|
425
|
+
else
|
426
|
+
str = data[:ds_amount] +
|
427
|
+
data[:ds_order].to_s +
|
428
|
+
data[:ds_merchantcode] +
|
429
|
+
data[:ds_currency] +
|
430
|
+
data[:ds_response] +
|
431
|
+
data[:ds_cardnumber].to_s +
|
432
|
+
data[:ds_transactiontype].to_s +
|
433
|
+
data[:ds_securepayment].to_s +
|
434
|
+
@options[:secret_key]
|
435
|
+
|
436
|
+
sig = Digest::SHA1.hexdigest(str)
|
437
|
+
data[:ds_signature].to_s.downcase == sig
|
438
|
+
end
|
390
439
|
end
|
391
440
|
|
392
441
|
def build_authorization(params)
|
@@ -426,6 +475,43 @@ module ActiveMerchant #:nodoc:
|
|
426
475
|
"%04d%s" % [rand(0..9999), cleansed[0...8]]
|
427
476
|
end
|
428
477
|
end
|
478
|
+
|
479
|
+
def sha256_authentication?
|
480
|
+
@options[:signature_algorithm] == "sha256"
|
481
|
+
end
|
482
|
+
|
483
|
+
def sign_request(xml_request_string, order_id)
|
484
|
+
key = encrypt(@options[:secret_key], order_id)
|
485
|
+
Base64.strict_encode64(mac256(key, xml_request_string))
|
486
|
+
end
|
487
|
+
|
488
|
+
def encrypt(key, order_id)
|
489
|
+
block_length = 8
|
490
|
+
cipher = OpenSSL::Cipher::Cipher.new('DES3')
|
491
|
+
cipher.encrypt
|
492
|
+
|
493
|
+
cipher.key = Base64.strict_decode64(key)
|
494
|
+
# The OpenSSL default of an all-zeroes ("\\0") IV is used.
|
495
|
+
cipher.padding = 0
|
496
|
+
|
497
|
+
order_id += "\0" until order_id.bytesize % block_length == 0 # Pad with zeros
|
498
|
+
|
499
|
+
output = cipher.update(order_id) + cipher.final
|
500
|
+
output
|
501
|
+
end
|
502
|
+
|
503
|
+
def mac256(key, data)
|
504
|
+
OpenSSL::HMAC.digest(OpenSSL::Digest.new('sha256'), key, data)
|
505
|
+
end
|
506
|
+
|
507
|
+
def xml_signed_fields(data)
|
508
|
+
data[:ds_amount] + data[:ds_order] + data[:ds_merchantcode] + data[:ds_currency] +
|
509
|
+
data[:ds_response] + data[:ds_transactiontype] + data[:ds_securepayment]
|
510
|
+
end
|
511
|
+
|
512
|
+
def get_key(order_id)
|
513
|
+
encrypt(@options[:secret_key], order_id)
|
514
|
+
end
|
429
515
|
end
|
430
516
|
end
|
431
517
|
end
|
@@ -106,7 +106,9 @@ module ActiveMerchant #:nodoc:
|
|
106
106
|
end
|
107
107
|
|
108
108
|
def void(identification, options = {})
|
109
|
-
|
109
|
+
post = {}
|
110
|
+
post[:expand] = [:charge]
|
111
|
+
commit(:post, "charges/#{CGI.escape(identification)}/refunds", post, options)
|
110
112
|
end
|
111
113
|
|
112
114
|
def refund(money, identification, options = {})
|
@@ -115,6 +117,7 @@ module ActiveMerchant #:nodoc:
|
|
115
117
|
post[:refund_application_fee] = true if options[:refund_application_fee]
|
116
118
|
post[:reverse_transfer] = options[:reverse_transfer] if options[:reverse_transfer]
|
117
119
|
post[:metadata] = options[:metadata] if options[:metadata]
|
120
|
+
post[:expand] = [:charge]
|
118
121
|
|
119
122
|
MultiResponse.run(:first) do |r|
|
120
123
|
r.process { commit(:post, "charges/#{CGI.escape(identification)}/refunds", post, options) }
|
@@ -283,7 +286,8 @@ module ActiveMerchant #:nodoc:
|
|
283
286
|
end
|
284
287
|
|
285
288
|
def add_expand_parameters(post, options)
|
286
|
-
post[:expand]
|
289
|
+
post[:expand] ||= []
|
290
|
+
post[:expand].concat(Array.wrap(options[:expand]).map(&:to_sym)).uniq!
|
287
291
|
end
|
288
292
|
|
289
293
|
def add_customer_data(post, options)
|
@@ -453,7 +457,7 @@ module ActiveMerchant #:nodoc:
|
|
453
457
|
Response.new(success,
|
454
458
|
success ? "Transaction approved" : response["error"]["message"],
|
455
459
|
response,
|
456
|
-
:test =>
|
460
|
+
:test => response_is_test?(response),
|
457
461
|
:authorization => authorization_from(success, url, method, response),
|
458
462
|
:avs_result => { :code => avs_code },
|
459
463
|
:cvv_result => cvc_code,
|
@@ -492,6 +496,16 @@ module ActiveMerchant #:nodoc:
|
|
492
496
|
}
|
493
497
|
end
|
494
498
|
|
499
|
+
def response_is_test?(response)
|
500
|
+
if response.has_key?('livemode')
|
501
|
+
!response['livemode']
|
502
|
+
elsif response['charge'].is_a?(Hash) && response['charge'].has_key?('livemode')
|
503
|
+
!response['charge']['livemode']
|
504
|
+
else
|
505
|
+
false
|
506
|
+
end
|
507
|
+
end
|
508
|
+
|
495
509
|
def non_fractional_currency?(currency)
|
496
510
|
CURRENCIES_WITHOUT_FRACTIONS.include?(currency.to_s)
|
497
511
|
end
|