activemerchant 1.90.0 → 1.99.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +202 -0
  3. data/README.md +6 -2
  4. data/lib/active_merchant/billing/avs_result.rb +4 -5
  5. data/lib/active_merchant/billing/credit_card.rb +8 -0
  6. data/lib/active_merchant/billing/credit_card_methods.rb +81 -5
  7. data/lib/active_merchant/billing/gateway.rb +10 -0
  8. data/lib/active_merchant/billing/gateways/adyen.rb +206 -54
  9. data/lib/active_merchant/billing/gateways/bambora_apac.rb +226 -0
  10. data/lib/active_merchant/billing/gateways/barclaycard_smartpay.rb +43 -10
  11. data/lib/active_merchant/billing/gateways/beanstream/beanstream_core.rb +3 -0
  12. data/lib/active_merchant/billing/gateways/beanstream.rb +11 -6
  13. data/lib/active_merchant/billing/gateways/blue_pay.rb +10 -8
  14. data/lib/active_merchant/billing/gateways/blue_snap.rb +211 -36
  15. data/lib/active_merchant/billing/gateways/bpoint.rb +4 -4
  16. data/lib/active_merchant/billing/gateways/braintree_blue.rb +79 -18
  17. data/lib/active_merchant/billing/gateways/card_connect.rb +6 -1
  18. data/lib/active_merchant/billing/gateways/cecabank.rb +20 -9
  19. data/lib/active_merchant/billing/gateways/checkout_v2.rb +98 -61
  20. data/lib/active_merchant/billing/gateways/credorax.rb +69 -4
  21. data/lib/active_merchant/billing/gateways/cyber_source.rb +85 -14
  22. data/lib/active_merchant/billing/gateways/d_local.rb +3 -3
  23. data/lib/active_merchant/billing/gateways/decidir.rb +245 -0
  24. data/lib/active_merchant/billing/gateways/elavon.rb +9 -0
  25. data/lib/active_merchant/billing/gateways/epay.rb +13 -2
  26. data/lib/active_merchant/billing/gateways/eway_rapid.rb +42 -12
  27. data/lib/active_merchant/billing/gateways/fat_zebra.rb +26 -7
  28. data/lib/active_merchant/billing/gateways/firstdata_e4_v27.rb +42 -3
  29. data/lib/active_merchant/billing/gateways/global_collect.rb +3 -7
  30. data/lib/active_merchant/billing/gateways/hps.rb +46 -1
  31. data/lib/active_merchant/billing/gateways/ipp.rb +1 -0
  32. data/lib/active_merchant/billing/gateways/kushki.rb +1 -1
  33. data/lib/active_merchant/billing/gateways/litle.rb +61 -3
  34. data/lib/active_merchant/billing/gateways/mastercard.rb +30 -5
  35. data/lib/active_merchant/billing/gateways/mercado_pago.rb +1 -1
  36. data/lib/active_merchant/billing/gateways/migs.rb +8 -0
  37. data/lib/active_merchant/billing/gateways/monei.rb +31 -0
  38. data/lib/active_merchant/billing/gateways/moneris.rb +3 -4
  39. data/lib/active_merchant/billing/gateways/mundipagg.rb +37 -9
  40. data/lib/active_merchant/billing/gateways/nab_transact.rb +1 -1
  41. data/lib/active_merchant/billing/gateways/netbanx.rb +4 -0
  42. data/lib/active_merchant/billing/gateways/nmi.rb +45 -5
  43. data/lib/active_merchant/billing/gateways/openpay.rb +1 -1
  44. data/lib/active_merchant/billing/gateways/opp.rb +20 -1
  45. data/lib/active_merchant/billing/gateways/orbital.rb +92 -11
  46. data/lib/active_merchant/billing/gateways/payflow.rb +64 -14
  47. data/lib/active_merchant/billing/gateways/payment_express.rb +7 -0
  48. data/lib/active_merchant/billing/gateways/paymentez.rb +7 -11
  49. data/lib/active_merchant/billing/gateways/paymill.rb +5 -0
  50. data/lib/active_merchant/billing/gateways/paypal.rb +14 -1
  51. data/lib/active_merchant/billing/gateways/paypal_express.rb +3 -1
  52. data/lib/active_merchant/billing/gateways/payu_latam.rb +6 -2
  53. data/lib/active_merchant/billing/gateways/pin.rb +19 -6
  54. data/lib/active_merchant/billing/gateways/pro_pay.rb +1 -1
  55. data/lib/active_merchant/billing/gateways/quickpay/quickpay_v10.rb +7 -1
  56. data/lib/active_merchant/billing/gateways/qvalent.rb +54 -1
  57. data/lib/active_merchant/billing/gateways/realex.rb +42 -5
  58. data/lib/active_merchant/billing/gateways/redsys.rb +113 -30
  59. data/lib/active_merchant/billing/gateways/spreedly_core.rb +43 -29
  60. data/lib/active_merchant/billing/gateways/stripe.rb +66 -34
  61. data/lib/active_merchant/billing/gateways/stripe_payment_intents.rb +271 -0
  62. data/lib/active_merchant/billing/gateways/tns.rb +10 -5
  63. data/lib/active_merchant/billing/gateways/trans_first_transaction_express.rb +5 -5
  64. data/lib/active_merchant/billing/gateways/trust_commerce.rb +46 -6
  65. data/lib/active_merchant/billing/gateways/usa_epay_transaction.rb +19 -8
  66. data/lib/active_merchant/billing/gateways/visanet_peru.rb +22 -10
  67. data/lib/active_merchant/billing/gateways/worldpay.rb +237 -34
  68. data/lib/active_merchant/country.rb +2 -1
  69. data/lib/active_merchant/version.rb +1 -1
  70. metadata +20 -4
@@ -9,7 +9,7 @@ module ActiveMerchant #:nodoc:
9
9
 
10
10
  self.supported_countries = %w[MX EC VE CO BR CL]
11
11
  self.default_currency = 'USD'
12
- self.supported_cardtypes = %i[visa master american_express diners_club]
12
+ self.supported_cardtypes = %i[visa master american_express diners_club elo]
13
13
 
14
14
  self.homepage_url = 'https://secure.paymentez.com/'
15
15
  self.display_name = 'Paymentez'
@@ -38,7 +38,8 @@ module ActiveMerchant #:nodoc:
38
38
  'visa' => 'vi',
39
39
  'master' => 'mc',
40
40
  'american_express' => 'ax',
41
- 'diners_club' => 'di'
41
+ 'diners_club' => 'di',
42
+ 'elo' => 'el'
42
43
  }.freeze
43
44
 
44
45
  def initialize(options = {})
@@ -171,15 +172,10 @@ module ActiveMerchant #:nodoc:
171
172
  end
172
173
 
173
174
  def add_extra_params(post, options)
174
- if options[:extra_params]
175
- items = {}
176
- options[:extra_params].each do |param|
177
- param.each do |key, value|
178
- items[key.to_sym] = value
179
- end
180
- end
181
- post[:extra_params] = items
182
- end
175
+ extra_params = {}
176
+ extra_params.merge!(options[:extra_params]) if options[:extra_params]
177
+
178
+ post['extra_params'] = extra_params unless extra_params.empty?
183
179
  end
184
180
 
185
181
  def parse(body)
@@ -48,6 +48,11 @@ module ActiveMerchant #:nodoc:
48
48
  end
49
49
 
50
50
  def store(credit_card, options={})
51
+ # The store request requires a currency and amount of at least $1 USD.
52
+ # This is used for an authorization that is handled internally by Paymill.
53
+ options[:currency] = 'USD'
54
+ options[:money] = 100
55
+
51
56
  save_card(credit_card, options)
52
57
  end
53
58
 
@@ -9,7 +9,7 @@ module ActiveMerchant #:nodoc:
9
9
  include PaypalRecurringApi
10
10
 
11
11
  self.supported_cardtypes = [:visa, :master, :american_express, :discover]
12
- self.supported_countries = ['US']
12
+ self.supported_countries = ['CA', 'NZ', 'GB', 'US']
13
13
  self.homepage_url = 'https://www.paypal.com/us/webapps/mpp/paypal-payments-pro'
14
14
  self.display_name = 'PayPal Payments Pro (US)'
15
15
 
@@ -90,6 +90,8 @@ module ActiveMerchant #:nodoc:
90
90
  xml.tag! 'n2:Payer', options[:email]
91
91
  add_address(xml, 'n2:Address', address)
92
92
  end
93
+
94
+ add_three_d_secure(xml, options) if options[:three_d_secure]
93
95
  end
94
96
  end
95
97
 
@@ -98,6 +100,17 @@ module ActiveMerchant #:nodoc:
98
100
  xml.tag! 'n2:SoftDescriptorCity', options[:soft_descriptor_city] unless options[:soft_descriptor_city].blank?
99
101
  end
100
102
 
103
+ def add_three_d_secure(xml, options)
104
+ three_d_secure = options[:three_d_secure]
105
+ xml.tag! 'ThreeDSecureRequest' do
106
+ xml.tag! 'MpiVendor3ds', 'Y'
107
+ xml.tag! 'AuthStatus3ds', three_d_secure[:trans_status] unless three_d_secure[:trans_status].blank?
108
+ xml.tag! 'Cavv', three_d_secure[:cavv] unless three_d_secure[:cavv].blank?
109
+ xml.tag! 'Eci3ds', three_d_secure[:eci] unless three_d_secure[:eci].blank?
110
+ xml.tag! 'Xid', three_d_secure[:xid] unless three_d_secure[:xid].blank?
111
+ end
112
+ end
113
+
101
114
  def credit_card_type(type)
102
115
  case type
103
116
  when 'visa' then 'Visa'
@@ -146,7 +146,9 @@ module ActiveMerchant #:nodoc:
146
146
  xml.tag! 'n2:cpp-payflow-color', options[:background_color] unless options[:background_color].blank?
147
147
  if options[:allow_guest_checkout]
148
148
  xml.tag! 'n2:SolutionType', 'Sole'
149
- xml.tag! 'n2:LandingPage', options[:landing_page] || 'Billing'
149
+ unless options[:paypal_chooses_landing_page]
150
+ xml.tag! 'n2:LandingPage', options[:landing_page] || 'Billing'
151
+ end
150
152
  end
151
153
  xml.tag! 'n2:BuyerEmail', options[:email] unless options[:email].blank?
152
154
 
@@ -12,13 +12,15 @@ module ActiveMerchant #:nodoc:
12
12
  self.supported_countries = ['AR', 'BR', 'CL', 'CO', 'MX', 'PA', 'PE']
13
13
  self.default_currency = 'USD'
14
14
  self.money_format = :dollars
15
- self.supported_cardtypes = [:visa, :master, :american_express, :diners_club]
15
+ self.supported_cardtypes = [:visa, :master, :american_express, :diners_club, :naranja, :cabal]
16
16
 
17
17
  BRAND_MAP = {
18
18
  'visa' => 'VISA',
19
19
  'master' => 'MASTERCARD',
20
20
  'american_express' => 'AMEX',
21
- 'diners_club' => 'DINERS'
21
+ 'diners_club' => 'DINERS',
22
+ 'naranja' => 'NARANJA',
23
+ 'cabal' => 'CABAL'
22
24
  }
23
25
 
24
26
  MINIMUMS = {
@@ -195,6 +197,7 @@ module ActiveMerchant #:nodoc:
195
197
  buyer[:fullName] = buyer_hash[:name]
196
198
  buyer[:dniNumber] = buyer_hash[:dni_number]
197
199
  buyer[:dniType] = buyer_hash[:dni_type]
200
+ buyer[:merchantBuyerId] = buyer_hash[:merchant_buyer_id]
198
201
  buyer[:cnpj] = buyer_hash[:cnpj] if @options[:payment_country] == 'BR'
199
202
  buyer[:emailAddress] = buyer_hash[:email]
200
203
  buyer[:contactPhone] = (options[:billing_address][:phone] if options[:billing_address]) || (options[:shipping_address][:phone] if options[:shipping_address]) || ''
@@ -203,6 +206,7 @@ module ActiveMerchant #:nodoc:
203
206
  buyer[:fullName] = payment_method.name.strip
204
207
  buyer[:dniNumber] = options[:dni_number]
205
208
  buyer[:dniType] = options[:dni_type]
209
+ buyer[:merchantBuyerId] = options[:merchant_buyer_id]
206
210
  buyer[:cnpj] = options[:cnpj] if @options[:payment_country] == 'BR'
207
211
  buyer[:emailAddress] = options[:email]
208
212
  buyer[:contactPhone] = (options[:billing_address][:phone] if options[:billing_address]) || (options[:shipping_address][:phone] if options[:shipping_address]) || ''
@@ -1,14 +1,14 @@
1
1
  module ActiveMerchant #:nodoc:
2
2
  module Billing #:nodoc:
3
3
  class PinGateway < Gateway
4
- self.test_url = 'https://test-api.pin.net.au/1'
5
- self.live_url = 'https://api.pin.net.au/1'
4
+ self.test_url = 'https://test-api.pinpayments.com/1'
5
+ self.live_url = 'https://api.pinpayments.com/1'
6
6
 
7
7
  self.default_currency = 'AUD'
8
8
  self.money_format = :cents
9
9
  self.supported_countries = ['AU']
10
10
  self.supported_cardtypes = [:visa, :master, :american_express]
11
- self.homepage_url = 'http://www.pin.net.au/'
11
+ self.homepage_url = 'http://www.pinpayments.com/'
12
12
  self.display_name = 'Pin Payments'
13
13
 
14
14
  def initialize(options = {})
@@ -67,6 +67,7 @@ module ActiveMerchant #:nodoc:
67
67
  # Updates the credit card for the customer.
68
68
  def update(token, creditcard, options = {})
69
69
  post = {}
70
+ token = get_customer_token(token)
70
71
 
71
72
  add_creditcard(post, creditcard)
72
73
  add_customer_data(post, options)
@@ -137,13 +138,21 @@ module ActiveMerchant #:nodoc:
137
138
  )
138
139
  elsif creditcard.kind_of?(String)
139
140
  if creditcard =~ /^card_/
140
- post[:card_token] = creditcard
141
+ post[:card_token] = get_card_token(creditcard)
141
142
  else
142
143
  post[:customer_token] = creditcard
143
144
  end
144
145
  end
145
146
  end
146
147
 
148
+ def get_customer_token(token)
149
+ token.split(/;(?=cus)/).last
150
+ end
151
+
152
+ def get_card_token(token)
153
+ token.split(/;(?=cus)/).first
154
+ end
155
+
147
156
  def add_metadata(post, options)
148
157
  post[:metadata] = options[:metadata] if options[:metadata]
149
158
  end
@@ -200,13 +209,17 @@ module ActiveMerchant #:nodoc:
200
209
  end
201
210
 
202
211
  def unparsable_response(raw_response)
203
- message = 'Invalid JSON response received from Pin Payments. Please contact support@pin.net.au if you continue to receive this message.'
212
+ message = 'Invalid JSON response received from Pin Payments. Please contact support@pinpayments.com if you continue to receive this message.'
204
213
  message += " (The raw response returned by the API was #{raw_response.inspect})"
205
214
  return Response.new(false, message)
206
215
  end
207
216
 
208
217
  def token(response)
209
- response['token']
218
+ if response['token'].start_with?('cus')
219
+ "#{response.dig('card', 'token')};#{response['token']}"
220
+ else
221
+ response['token']
222
+ end
210
223
  end
211
224
 
212
225
  def parse(body)
@@ -234,7 +234,7 @@ module ActiveMerchant #:nodoc:
234
234
  xml.aptNum address[:address2]
235
235
  xml.city address[:city]
236
236
  xml.state address[:state]
237
- xml.zip address[:zip]
237
+ xml.zip address[:zip].to_s.delete('-')
238
238
  end
239
239
  end
240
240
 
@@ -103,7 +103,7 @@ module ActiveMerchant
103
103
  post = {}
104
104
 
105
105
  add_amount(post, money, options)
106
- add_credit_card_or_reference(post, credit_card_or_reference)
106
+ add_credit_card_or_reference(post, credit_card_or_reference, options)
107
107
  add_additional_params(:authorize, post, options)
108
108
 
109
109
  post
@@ -217,6 +217,12 @@ module ActiveMerchant
217
217
  post[:card][:expiration] = expdate(credit_card_or_reference)
218
218
  post[:card][:issued_to] = credit_card_or_reference.name
219
219
  end
220
+
221
+ if options[:three_d_secure]
222
+ post[:card][:cavv]= options.dig(:three_d_secure, :cavv)
223
+ post[:card][:eci] = options.dig(:three_d_secure, :eci)
224
+ post[:card][:xav] = options.dig(:three_d_secure, :xid)
225
+ end
220
226
  end
221
227
 
222
228
  def parse(body)
@@ -12,6 +12,10 @@ module ActiveMerchant #:nodoc:
12
12
  self.money_format = :cents
13
13
  self.supported_cardtypes = [:visa, :master, :american_express, :discover, :jcb, :diners]
14
14
 
15
+ CVV_CODE_MAPPING = {
16
+ 'S' => 'D'
17
+ }
18
+
15
19
  def initialize(options={})
16
20
  requires!(options, :username, :password, :merchant, :pem, :pem_password)
17
21
  super
@@ -23,6 +27,7 @@ module ActiveMerchant #:nodoc:
23
27
  add_order_number(post, options)
24
28
  add_payment_method(post, payment_method)
25
29
  add_verification_value(post, payment_method)
30
+ add_stored_credential_data(post, payment_method, options)
26
31
  add_customer_data(post, options)
27
32
  add_soft_descriptors(post, options)
28
33
 
@@ -35,6 +40,7 @@ module ActiveMerchant #:nodoc:
35
40
  add_order_number(post, options)
36
41
  add_payment_method(post, payment_method)
37
42
  add_verification_value(post, payment_method)
43
+ add_stored_credential_data(post, payment_method, options)
38
44
  add_customer_data(post, options)
39
45
  add_soft_descriptors(post, options)
40
46
 
@@ -57,6 +63,7 @@ module ActiveMerchant #:nodoc:
57
63
  add_reference(post, authorization, options)
58
64
  add_customer_data(post, options)
59
65
  add_soft_descriptors(post, options)
66
+ post['order.ECI'] = options[:eci] || 'SSL'
60
67
 
61
68
  commit('refund', post)
62
69
  end
@@ -120,7 +127,6 @@ module ActiveMerchant #:nodoc:
120
127
  def add_invoice(post, money, options)
121
128
  post['order.amount'] = amount(money)
122
129
  post['card.currency'] = CURRENCY_CODES[options[:currency] || currency(money)]
123
- post['order.ECI'] = options[:eci] || 'SSL'
124
130
  end
125
131
 
126
132
  def add_payment_method(post, payment_method)
@@ -130,6 +136,46 @@ module ActiveMerchant #:nodoc:
130
136
  post['card.expiryMonth'] = format(payment_method.month, :two_digits)
131
137
  end
132
138
 
139
+ def add_stored_credential_data(post, payment_method, options)
140
+ post['order.ECI'] = options[:eci] || eci(options)
141
+ if (stored_credential = options[:stored_credential]) && %w(visa master).include?(payment_method.brand)
142
+ post['card.posEntryMode'] = stored_credential[:initial_transaction] ? 'MANUAL' : 'STORED_CREDENTIAL'
143
+ stored_credential_usage(post, payment_method, options) unless stored_credential[:initiator] && stored_credential[:initiator] == 'cardholder'
144
+ post['order.authTraceId'] = stored_credential[:network_transaction_id] if stored_credential[:network_transaction_id]
145
+ end
146
+ end
147
+
148
+ def stored_credential_usage(post, payment_method, options)
149
+ return unless payment_method.brand == 'visa'
150
+ stored_credential = options[:stored_credential]
151
+ if stored_credential[:initial_transaction]
152
+ post['card.storedCredentialUsage'] = 'INITIAL_STORAGE'
153
+ elsif stored_credential[:reason_type] == ('recurring' || 'installment')
154
+ post['card.storedCredentialUsage'] = 'RECURRING'
155
+ elsif stored_credential[:reason_type] == 'unscheduled'
156
+ post['card.storedCredentialUsage'] = 'UNSCHEDULED'
157
+ end
158
+ end
159
+
160
+ def eci(options)
161
+ if options.dig(:stored_credential, :initial_transaction)
162
+ 'SSL'
163
+ elsif options.dig(:stored_credential, :initiator) && options[:stored_credential][:initiator] == 'cardholder'
164
+ 'MTO'
165
+ elsif options.dig(:stored_credential, :reason_type)
166
+ case options[:stored_credential][:reason_type]
167
+ when 'recurring'
168
+ 'REC'
169
+ when 'installment'
170
+ 'INS'
171
+ when 'unscheduled'
172
+ 'MTO'
173
+ end
174
+ else
175
+ 'SSL'
176
+ end
177
+ end
178
+
133
179
  def add_verification_value(post, payment_method)
134
180
  post['card.CVN'] = payment_method.verification_value
135
181
  end
@@ -168,11 +214,18 @@ module ActiveMerchant #:nodoc:
168
214
  message_from(succeeded, raw),
169
215
  raw,
170
216
  authorization: raw['response.orderNumber'] || raw['response.customerReferenceNumber'],
217
+ cvv_result: cvv_result(succeeded, raw),
171
218
  error_code: error_code_from(succeeded, raw),
172
219
  test: test?
173
220
  )
174
221
  end
175
222
 
223
+ def cvv_result(succeeded, raw)
224
+ return unless succeeded
225
+ code = CVV_CODE_MAPPING[raw['response.cvnResponse']] || raw['response.cvnResponse']
226
+ CVVResult.new(code)
227
+ end
228
+
176
229
  def headers
177
230
  {
178
231
  'Content-Type' => 'application/x-www-form-urlencoded'
@@ -42,7 +42,8 @@ module ActiveMerchant
42
42
 
43
43
  def initialize(options = {})
44
44
  requires!(options, :login, :password)
45
- options[:refund_hash] = Digest::SHA1.hexdigest(options[:rebate_secret]) if options.has_key?(:rebate_secret)
45
+ options[:refund_hash] = Digest::SHA1.hexdigest(options[:rebate_secret]) if options[:rebate_secret].present?
46
+ options[:credit_hash] = Digest::SHA1.hexdigest(options[:refund_secret]) if options[:refund_secret].present?
46
47
  super
47
48
  end
48
49
 
@@ -70,9 +71,9 @@ module ActiveMerchant
70
71
  commit(request)
71
72
  end
72
73
 
73
- def credit(money, authorization, options = {})
74
- ActiveMerchant.deprecated CREDIT_DEPRECATION_MESSAGE
75
- refund(money, authorization, options)
74
+ def credit(money, creditcard, options = {})
75
+ request = build_credit_request(money, creditcard, options)
76
+ commit(request)
76
77
  end
77
78
 
78
79
  def void(authorization, options = {})
@@ -145,7 +146,11 @@ module ActiveMerchant
145
146
  add_card(xml, credit_card)
146
147
  xml.tag! 'autosettle', 'flag' => auto_settle_flag(action)
147
148
  add_signed_digest(xml, timestamp, @options[:login], sanitize_order_id(options[:order_id]), amount(money), (options[:currency] || currency(money)), credit_card.number)
148
- add_network_tokenization_card(xml, credit_card) if credit_card.is_a?(NetworkTokenizationCreditCard)
149
+ if credit_card.is_a?(NetworkTokenizationCreditCard)
150
+ add_network_tokenization_card(xml, credit_card)
151
+ else
152
+ add_three_d_secure(xml, options)
153
+ end
149
154
  add_comments(xml, options)
150
155
  add_address_and_customer_info(xml, options)
151
156
  end
@@ -180,6 +185,22 @@ module ActiveMerchant
180
185
  xml.target!
181
186
  end
182
187
 
188
+ def build_credit_request(money, credit_card, options)
189
+ timestamp = new_timestamp
190
+ xml = Builder::XmlMarkup.new :indent => 2
191
+ xml.tag! 'request', 'timestamp' => timestamp, 'type' => 'credit' do
192
+ add_merchant_details(xml, options)
193
+ xml.tag! 'orderid', sanitize_order_id(options[:order_id])
194
+ add_amount(xml, money, options)
195
+ add_card(xml, credit_card)
196
+ xml.tag! 'refundhash', @options[:credit_hash] if @options[:credit_hash]
197
+ xml.tag! 'autosettle', 'flag' => 1
198
+ add_comments(xml, options)
199
+ add_signed_digest(xml, timestamp, @options[:login], sanitize_order_id(options[:order_id]), amount(money), (options[:currency] || currency(money)), credit_card.number)
200
+ end
201
+ xml.target!
202
+ end
203
+
183
204
  def build_void_request(authorization, options)
184
205
  timestamp = new_timestamp
185
206
  xml = Builder::XmlMarkup.new :indent => 2
@@ -284,6 +305,22 @@ module ActiveMerchant
284
305
  end
285
306
  end
286
307
 
308
+ def add_three_d_secure(xml, options)
309
+ return unless three_d_secure = options[:three_d_secure]
310
+ version = three_d_secure.fetch(:version, '')
311
+ xml.tag! 'mpi' do
312
+ if version =~ /^2/
313
+ xml.tag! 'authentication_value', three_d_secure[:cavv]
314
+ xml.tag! 'ds_trans_id', three_d_secure[:ds_transaction_id]
315
+ else
316
+ xml.tag! 'cavv', three_d_secure[:cavv]
317
+ xml.tag! 'xid', three_d_secure[:xid]
318
+ end
319
+ xml.tag! 'eci', three_d_secure[:eci]
320
+ xml.tag! 'message_version', version
321
+ end
322
+ end
323
+
287
324
  def format_address_code(address)
288
325
  code = [address[:zip].to_s, address[:address1].to_s + address[:address2].to_s]
289
326
  code.collect { |e| e.gsub(/\D/, '') }.reject(&:empty?).join('|')