activemerchant 1.106.0 → 1.108.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +79 -0
  3. data/README.md +2 -2
  4. data/lib/active_merchant/billing/gateways/adyen.rb +1 -1
  5. data/lib/active_merchant/billing/gateways/authorize_net.rb +1 -1
  6. data/lib/active_merchant/billing/gateways/blue_pay.rb +2 -2
  7. data/lib/active_merchant/billing/gateways/borgun.rb +15 -4
  8. data/lib/active_merchant/billing/gateways/braintree_blue.rb +11 -9
  9. data/lib/active_merchant/billing/gateways/checkout_v2.rb +20 -9
  10. data/lib/active_merchant/billing/gateways/clearhaus.rb +1 -1
  11. data/lib/active_merchant/billing/gateways/cyber_source.rb +74 -21
  12. data/lib/active_merchant/billing/gateways/d_local.rb +17 -5
  13. data/lib/active_merchant/billing/gateways/decidir.rb +29 -0
  14. data/lib/active_merchant/billing/gateways/ebanx.rb +13 -1
  15. data/lib/active_merchant/billing/gateways/elavon.rb +58 -5
  16. data/lib/active_merchant/billing/gateways/element.rb +13 -5
  17. data/lib/active_merchant/billing/gateways/firstdata_e4.rb +2 -2
  18. data/lib/active_merchant/billing/gateways/firstdata_e4_v27.rb +2 -2
  19. data/lib/active_merchant/billing/gateways/forte.rb +7 -6
  20. data/lib/active_merchant/billing/gateways/global_collect.rb +30 -24
  21. data/lib/active_merchant/billing/gateways/hps.rb +4 -2
  22. data/lib/active_merchant/billing/gateways/iats_payments.rb +31 -14
  23. data/lib/active_merchant/billing/gateways/iridium.rb +4 -2
  24. data/lib/active_merchant/billing/gateways/iveri.rb +4 -1
  25. data/lib/active_merchant/billing/gateways/ixopay.rb +1 -0
  26. data/lib/active_merchant/billing/gateways/kushki.rb +34 -5
  27. data/lib/active_merchant/billing/gateways/litle.rb +6 -1
  28. data/lib/active_merchant/billing/gateways/mercado_pago.rb +1 -0
  29. data/lib/active_merchant/billing/gateways/netaxept.rb +1 -1
  30. data/lib/active_merchant/billing/gateways/netbanx.rb +1 -1
  31. data/lib/active_merchant/billing/gateways/opp.rb +13 -7
  32. data/lib/active_merchant/billing/gateways/optimal_payment.rb +4 -0
  33. data/lib/active_merchant/billing/gateways/orbital.rb +44 -2
  34. data/lib/active_merchant/billing/gateways/payflow/payflow_common_api.rb +1 -1
  35. data/lib/active_merchant/billing/gateways/payment_express.rb +2 -2
  36. data/lib/active_merchant/billing/gateways/payu_latam.rb +1 -1
  37. data/lib/active_merchant/billing/gateways/pin.rb +1 -1
  38. data/lib/active_merchant/billing/gateways/quantum.rb +1 -1
  39. data/lib/active_merchant/billing/gateways/realex.rb +10 -3
  40. data/lib/active_merchant/billing/gateways/redsys.rb +1 -1
  41. data/lib/active_merchant/billing/gateways/secure_pay_au.rb +1 -1
  42. data/lib/active_merchant/billing/gateways/so_easy_pay.rb +1 -1
  43. data/lib/active_merchant/billing/gateways/stripe.rb +7 -2
  44. data/lib/active_merchant/billing/gateways/stripe_payment_intents.rb +40 -6
  45. data/lib/active_merchant/billing/gateways/transact_pro.rb +2 -2
  46. data/lib/active_merchant/billing/gateways/trexle.rb +1 -1
  47. data/lib/active_merchant/billing/gateways/worldpay.rb +8 -6
  48. data/lib/active_merchant/billing/network_tokenization_credit_card.rb +1 -1
  49. data/lib/active_merchant/connection.rb +40 -42
  50. data/lib/active_merchant/network_connection_retries.rb +10 -12
  51. data/lib/active_merchant/version.rb +1 -1
  52. metadata +5 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f734d2204b43b893e645e5c47b9afe26506c0c383b355af486ac93cd6a8677ed
4
- data.tar.gz: 611450e08256619a1d8aea5bc5795c47ff9220f0abde707f0facc82dd76e1661
3
+ metadata.gz: 11b837a742d2fd39e941f6aa861b3c3557fe2ddb6142b2e119c48d22e7b39749
4
+ data.tar.gz: 3aa9e72328ca32c4e84c2919dc61f3b58350d806f49fe2ea342fc9acb19a8056
5
5
  SHA512:
6
- metadata.gz: 98fa68d855a398089f9a088d8fa12edae2b34e8ed9fecab64511dc7e64b1ead409977adc00c3bc6aaf18f3f552f2b1fa3a38b8ceb5779d4a32a91e171a5556c8
7
- data.tar.gz: 78a05f047cb7de339104b39785394b75ca51be7d33368ba44549006e2429e3d2a2796a10d30a18a38fae4aff34e6f4a605f862ec3103ad101c9805c09af586f7
6
+ metadata.gz: c5454b152692725df9ee05bf8cfc202642ca7fb535950996d5c521c76788f0bec214f707596293185fac78d38785ef1a53924f3b99fd38137f09014c4339d92d
7
+ data.tar.gz: d724f10b88a939d856454396e0a1b9a80d975fd40d0fe2db4e8a8c0da7cdc434f4fe8c2117f86e16acb40479295d8ba766a691e6accf059973e6af1359b26387
data/CHANGELOG CHANGED
@@ -2,6 +2,85 @@
2
2
 
3
3
  == HEAD
4
4
 
5
+ == Version 1.108.0 (Jun 9, 2020)
6
+ * Cybersource: Send cavv as xid is xid is missing [pi3r] #3658
7
+ * Forte: Change default sec_code value to PPD [molbrown] #3653
8
+ * Elavon: Add merchant initiated unscheduled field [leila-alderman] #3647
9
+ * Decidir: Add aggregate data fields [leila-alderman] #3648
10
+ * Vantiv: Vantiv(Element): add option to send terminal id in transactions [cdmackeyfree] #3654
11
+ * Update supported Ruby and Rails versions [leila-alderman] #3656
12
+ * CI: Drop unused sudo: false Travis directive [olleolleolle] #3616
13
+ * PayU Latam: Prevent blank country in billing_address [britth] #3657
14
+ * DLocal: Fix address field names [molbrown] #3651
15
+
16
+ == Version 1.107.4 (Jun 2, 2020)
17
+ * Elavon: Implement true verify action [leila-alderman] #3610
18
+ * Vantiv Express: Implement true verify [leila-alderman] #3617
19
+ * Litle: Pass expiration data for basis payment method [therufs] #3606
20
+ * Stripe Payment Intents: Error handling and backwards compatibility within refund [britth] #3627
21
+ * HPS: Prevent errors when account_type or account_holder_type are nil [britth] #3628
22
+ * D Local: Handle invalid country code errors [curiousepic] #3626
23
+ * Stripe Payment Intents: Utilize execute_threed flag to determine success [britth] #3625
24
+ * Elavon: Add Level 3 fields [leila-alderman] #3632
25
+ * CyberSource: Stored Credential fixes [curiousepic] #3624
26
+ * CyberSource: Fix invalid and missing field tests [curiousepic] #3634
27
+ * CyberSource: Pass stored credentials with purchase [curiousepic] #3636
28
+ * Mercado Pago: Add payment_method_option_id field [schwarzgeist] #3635
29
+ * Stripe: Provide error when attempting an authorize with ACH [britth] #3633
30
+ * EBANX: Send original order id as merchant_payment_code metadata [miguelxpn] #3637
31
+ * Element: Add card_present_code field [schwarzgeist] #3623
32
+ * Orbital: Add support for Level 3 fields [leila-alderman] #3639
33
+ * Firstdata: Strip newline characters from address [bittercoder] #3643
34
+ * Forte: add sec_code attribute for echeck [wsmoak] #3640
35
+
36
+ == Version 1.107.3 (May 8, 2020)
37
+ * Realex: Ignore IPv6 unsupported addresses [elfassy] #3622
38
+ * Cybersource: Set partnerSolutionID after the business rules, fixes 500 error [pi3r] #3621
39
+
40
+ == Version 1.107.2 (May 7, 2020)
41
+ * Cybersource: Send a specific card brand commerceIndicator for 3DS [pi3r] #3620
42
+ * Cybersource: Send application_id as partnerSolutionID [pi3r] #3620
43
+ * Iridium: Localize zero-decimal currencies [chinhle23] #3587
44
+ * iVeri: Fix `verify` action [chinhle23] #3588
45
+ * Ixopay: Properly support three-decimal currencies [chinhle23] #3589
46
+ * Kushki: support `auth` and `capture` [therufs] #3591
47
+ * PaymentExpress: Update references to Windcave to reflect rebranding [britth] #3595
48
+ * Decidir: Improve handling of error responses from the gateway [naashton] #3594
49
+ * CyberSource: Added support for MerchantInformation CyberSource-specific fields [apfranzen] #3592
50
+ * ePay: Send unique order ids for remote tests [curiousepic] #3593
51
+ * Checkout V2: Send more informative error messages for 4xx errors [britth] #3601
52
+ * Elavon: Add ssl_dynamic_dba field [apfranzen] #3600
53
+ * iATS Payments: Update gateway to v3 and add support for additional GSFs [naashton] #3599
54
+ * Remove deprecated `rubyforge_project` attribute and tidy up unit test output [fatcatt316] #3598
55
+ * Elavon: Cleanup inadvertant field removal (avs_address) in #3600 [apfranzen] #3602
56
+ * EBANX: Fix transaction amount for verify transaction [miguelxpn] #3603
57
+ * iATS Payments: Update gateway to accept `email`, `phone`, and `country` fields [naashton] #3607
58
+ * Braintree: Fix response for failed refunds when falling back to voids [jasonwebster] #3608
59
+ * Worldpay: Fix response for failed refunds when falling back to voids [jasonwebster] #3609
60
+ * iATS Payments: Add support for Customer Code payment method [molbrown] #3611
61
+ * HPS: Add Google Pay support [MSmedal] #3597
62
+ * Adyen: Parse appropriate message for 3DS2 authorization calls [britth] #3619
63
+ * CyberSource: Add error details response fields [schwarzgeist] #3629
64
+
65
+ == Version 1.107.1 (Apr 1, 2020)
66
+ * Add `allowed_push_host` to gemspec [mdeloupy]
67
+
68
+ == Version 1.107.0 (Apr 1, 2020)
69
+ * Stripe Payment Intents: Early return failed `payment_methods` response [chinhle23] #3570
70
+ * Borgun: Support `passengerItineraryData` [therufs] #3572
71
+ * Ingenico GlobalCollect: support optional `requires_approval` field [fatcatt316] #3571
72
+ * CenPOS: Update failing remote tests [britth] #3575
73
+ * Realex: Update remote tests [britth] #3576
74
+ * FirstData e4 v27: Properly tag stored credential initiation field in request [britth] #3578
75
+ * Orbital: Fix stored credentials [chinhle23] #3579
76
+ * Acapture(Opp): Update gateway credentials [molbrown] #3574
77
+ * Ingenico GlobalCollect: support `requires_approval` field [fatcatt316] #3577
78
+ * CyberSource: Fix `void` for `purchase` transactions [chinhle23] #3581
79
+ * Checkout V2: Begin to add support for using network tokens for transactions. [arbianchi] #3580
80
+ * Opp: Update remote test fixtures [ccarruitero] #3582
81
+ * Optimal Payment: Add support for store [britth] #3585
82
+ * SecurePay Australia : Update test URL (#3586)
83
+
5
84
  == Version 1.106.0 (Mar 10, 2020)
6
85
  * PayJunctionV2: Send billing address in `auth` and `purchase` transactions [naashton] #3538
7
86
  * Adyen: Fix some remote tests [curiousepic] #3541
data/README.md CHANGED
@@ -183,7 +183,7 @@ The [ActiveMerchant Wiki](https://github.com/activemerchant/active_merchant/wiki
183
183
  * [Paybox Direct](http://www.paybox.com/) - FR
184
184
  * [Payeezy](https://developer.payeezy.com/) - CA, US
185
185
  * [Payex](http://payex.com/) - DK, FI, NO, SE
186
- * [PaymentExpress](http://www.paymentexpress.com/) - AU, CA, DE, ES, FR, GB, HK, IE, MY, NL, NZ, SG, US, ZA
186
+ * [Windcave (formerly PaymentExpress)](https://www.windcave.com/) - AU, CA, DE, ES, FR, GB, HK, IE, MY, NL, NZ, SG, US, ZA
187
187
  * [PAYMILL](https://paymill.com) - AD, AT, BE, BG, CH, CY, CZ, DE, DK, EE, ES, FI, FO, FR, GB, GI, GR, HU, IE, IL, IS, IT, LI, LT, LU, LV, MT, NL, NO, PL, PT, RO, SE, SI, SK, TR, VA
188
188
  * [PayPal Express Checkout](https://www.paypal.com/webapps/mpp/express-checkout) - US, CA, SG, AU
189
189
  * [PayPal Express Checkout (UK)](https://www.paypal.com/uk/webapps/mpp/express-checkout) - GB
@@ -244,4 +244,4 @@ Functionality or APIs that are deprecated will be marked as such. Deprecated fun
244
244
 
245
245
  ## Ruby and Rails compatibility policies
246
246
 
247
- Because Active Merchant is a payment library, it needs to take security seriously. For this reason, Active Merchant guarantees compatibility only with actively supported versions of Ruby and Rails. At the time of this writing, that means that Ruby 2.3+ and Rails 4.2+ are supported.
247
+ Because Active Merchant is a payment library, it needs to take security seriously. For this reason, Active Merchant guarantees compatibility only with actively supported versions of Ruby and Rails. At the time of this writing, that means that Ruby 2.5+ and Rails 5.0+ are supported.
@@ -549,7 +549,7 @@ module ActiveMerchant #:nodoc:
549
549
  end
550
550
 
551
551
  def message_from(action, response)
552
- return authorize_message_from(response) if action.to_s == 'authorise' || action.to_s == 'authorise3d'
552
+ return authorize_message_from(response) if %w(authorise authorise3d authorise3ds2).include?(action.to_s)
553
553
 
554
554
  response['response'] || response['message'] || response['result']
555
555
  end
@@ -488,7 +488,7 @@ module ActiveMerchant
488
488
 
489
489
  def add_swipe_data(xml, credit_card)
490
490
  TRACKS.each do |key, regex|
491
- if regex.match(credit_card.track_data)
491
+ if regex.match?(credit_card.track_data)
492
492
  @valid_track_data = true
493
493
  xml.payment do
494
494
  xml.trackData do
@@ -382,9 +382,9 @@ module ActiveMerchant #:nodoc:
382
382
  end
383
383
  elsif message == 'Missing ACCOUNT_ID'
384
384
  message = 'The merchant login ID or password is invalid'
385
- elsif message =~ /Approved/
385
+ elsif /Approved/.match?(message)
386
386
  message = 'This transaction has been approved'
387
- elsif message =~ /Expired/
387
+ elsif /Expired/.match?(message)
388
388
  message = 'The credit card has expired'
389
389
  end
390
390
  message
@@ -34,7 +34,7 @@ module ActiveMerchant #:nodoc:
34
34
  post[:TransType] = '5'
35
35
  add_invoice(post, money, options)
36
36
  add_payment_method(post, payment)
37
- commit('authonly', post)
37
+ commit('authonly', post, options)
38
38
  end
39
39
 
40
40
  def capture(money, authorization, options={})
@@ -125,12 +125,12 @@ module ActiveMerchant #:nodoc:
125
125
  response
126
126
  end
127
127
 
128
- def commit(action, post)
128
+ def commit(action, post, options={})
129
129
  post[:Version] = '1000'
130
130
  post[:Processor] = @options[:processor]
131
131
  post[:MerchantID] = @options[:merchant_id]
132
132
 
133
- request = build_request(action, post)
133
+ request = build_request(action, post, options)
134
134
  raw = ssl_post(url(action), request, headers)
135
135
  pairs = parse(raw)
136
136
  success = success_from(pairs)
@@ -180,7 +180,7 @@ module ActiveMerchant #:nodoc:
180
180
  }
181
181
  end
182
182
 
183
- def build_request(action, post)
183
+ def build_request(action, post, options={})
184
184
  mode = action == 'void' ? 'cancel' : 'get'
185
185
  xml = Builder::XmlMarkup.new indent: 18
186
186
  xml.instruct!(:xml, version: '1.0', encoding: 'utf-8')
@@ -188,11 +188,22 @@ module ActiveMerchant #:nodoc:
188
188
  post.each do |field, value|
189
189
  xml.tag!(field, value)
190
190
  end
191
+ build_airline_xml(xml, options[:passenger_itinerary_data]) if options[:passenger_itinerary_data]
191
192
  end
192
193
  inner = CGI.escapeHTML(xml.target!)
193
194
  envelope(mode).sub(/{{ :body }}/, inner)
194
195
  end
195
196
 
197
+ def build_airline_xml(xml, airline_data)
198
+ xml.tag!('PassengerItineraryData') do
199
+ xml.tag!('A1') do
200
+ airline_data.each do |field, value|
201
+ xml.tag!(field, value)
202
+ end
203
+ end
204
+ end
205
+ end
206
+
196
207
  def envelope(mode)
197
208
  <<-EOS
198
209
  <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:aut="http://Borgun/Heimir/pub/ws/Authorization">
@@ -1,4 +1,5 @@
1
1
  require 'active_merchant/billing/gateways/braintree/braintree_common'
2
+ require 'active_support/core_ext/array/extract_options'
2
3
 
3
4
  begin
4
5
  require 'braintree'
@@ -98,10 +99,13 @@ module ActiveMerchant #:nodoc:
98
99
 
99
100
  commit do
100
101
  response = response_from_result(@braintree_gateway.transaction.refund(transaction_id, money))
101
- return response if response.success?
102
- return response unless options[:force_full_refund_if_unsettled]
103
102
 
104
- void(transaction_id) if response.message =~ /#{ERROR_CODES[:cannot_refund_if_unsettled]}/
103
+ if !response.success? && options[:force_full_refund_if_unsettled] &&
104
+ response.message =~ /#{ERROR_CODES[:cannot_refund_if_unsettled]}/
105
+ void(transaction_id)
106
+ else
107
+ response
108
+ end
105
109
  end
106
110
  end
107
111
 
@@ -200,12 +204,10 @@ module ActiveMerchant #:nodoc:
200
204
 
201
205
  def check_customer_exists(customer_vault_id)
202
206
  commit do
203
- begin
204
- @braintree_gateway.customer.find(customer_vault_id)
205
- ActiveMerchant::Billing::Response.new(true, 'Customer found', {exists: true}, authorization: customer_vault_id)
206
- rescue Braintree::NotFoundError
207
- ActiveMerchant::Billing::Response.new(true, 'Customer not found', {exists: false})
208
- end
207
+ @braintree_gateway.customer.find(customer_vault_id)
208
+ ActiveMerchant::Billing::Response.new(true, 'Customer found', {exists: true}, authorization: customer_vault_id)
209
+ rescue Braintree::NotFoundError
210
+ ActiveMerchant::Billing::Response.new(true, 'Customer not found', {exists: false})
209
211
  end
210
212
  end
211
213
 
@@ -102,13 +102,21 @@ module ActiveMerchant #:nodoc:
102
102
 
103
103
  def add_payment_method(post, payment_method, options)
104
104
  post[:source] = {}
105
- post[:source][:type] = 'card'
106
- post[:source][:name] = payment_method.name
107
- post[:source][:number] = payment_method.number
108
- post[:source][:cvv] = payment_method.verification_value
105
+ if payment_method.is_a?(NetworkTokenizationCreditCard) && payment_method.source == :network_token
106
+ post[:source][:type] = 'network_token'
107
+ post[:source][:token] = payment_method.number
108
+ post[:source][:token_type] = payment_method.brand == 'visa' ? 'vts' : 'mdes'
109
+ post[:source][:cryptogram] = payment_method.payment_cryptogram
110
+ post[:source][:eci] = options[:eci] || '05'
111
+ else
112
+ post[:source][:type] = 'card'
113
+ post[:source][:name] = payment_method.name
114
+ post[:source][:number] = payment_method.number
115
+ post[:source][:cvv] = payment_method.verification_value
116
+ post[:source][:stored] = 'true' if options[:card_on_file] == true
117
+ end
109
118
  post[:source][:expiry_year] = format(payment_method.year, :four_digits)
110
119
  post[:source][:expiry_month] = format(payment_method.month, :two_digits)
111
- post[:source][:stored] = 'true' if options[:card_on_file] == true
112
120
  end
113
121
 
114
122
  def add_customer_data(post, options)
@@ -158,7 +166,7 @@ module ActiveMerchant #:nodoc:
158
166
  rescue ResponseError => e
159
167
  raise unless e.response.code.to_s =~ /4\d\d/
160
168
 
161
- response = parse(e.response.body)
169
+ response = parse(e.response.body, error: e.response)
162
170
  end
163
171
 
164
172
  succeeded = success_from(response)
@@ -216,13 +224,16 @@ module ActiveMerchant #:nodoc:
216
224
  response['source'] && response['source']['cvv_check'] ? CVVResult.new(response['source']['cvv_check']) : nil
217
225
  end
218
226
 
219
- def parse(body)
227
+ def parse(body, error: nil)
220
228
  JSON.parse(body)
221
229
  rescue JSON::ParserError
222
- {
230
+ response = {
231
+ 'error_type' => error&.code,
223
232
  'message' => 'Invalid JSON response received from Checkout.com Unified Payments Gateway. Please contact Checkout.com if you continue to receive this message.',
224
233
  'raw_response' => scrub(body)
225
234
  }
235
+ response['error_codes'] = [error&.message] if error&.message
236
+ response
226
237
  end
227
238
 
228
239
  def success_from(response)
@@ -235,7 +246,7 @@ module ActiveMerchant #:nodoc:
235
246
  elsif response['error_type']
236
247
  response['error_type'] + ': ' + response['error_codes'].first
237
248
  else
238
- response['response_summary'] || response['response_code'] || response['status'] || 'Unable to read error message'
249
+ response['response_summary'] || response['response_code'] || response['status'] || response['message'] || 'Unable to read error message'
239
250
  end
240
251
  end
241
252
 
@@ -206,7 +206,7 @@ module ActiveMerchant #:nodoc:
206
206
 
207
207
  def generate_signature(body)
208
208
  key = OpenSSL::PKey::RSA.new(@options[:private_key])
209
- hex = key.sign(OpenSSL::Digest.new('sha256'), body).unpack('H*').first
209
+ hex = key.sign(OpenSSL::Digest.new('sha256'), body).unpack1('H*')
210
210
 
211
211
  "#{@options[:signing_key]} RS256-hex #{hex}"
212
212
  end
@@ -25,8 +25,16 @@ module ActiveMerchant #:nodoc:
25
25
  self.live_url = 'https://ics2wsa.ic3.com/commerce/1.x/transactionProcessor'
26
26
 
27
27
  # Schema files can be found here: https://ics2ws.ic3.com/commerce/1.x/transactionProcessor/
28
- TEST_XSD_VERSION = '1.159'
29
- PRODUCTION_XSD_VERSION = '1.159'
28
+ TEST_XSD_VERSION = '1.164'
29
+ PRODUCTION_XSD_VERSION = '1.164'
30
+ ECI_BRAND_MAPPING = {
31
+ visa: 'vbv',
32
+ master: 'spa',
33
+ american_express: 'aesk',
34
+ jcb: 'js',
35
+ discover: 'pb',
36
+ }.freeze
37
+ DEFAULT_COLLECTION_INDICATOR = 2
30
38
 
31
39
  self.supported_cardtypes = [:visa, :master, :american_express, :discover, :diners_club, :jcb, :dankort, :maestro, :elo]
32
40
  self.supported_countries = %w(US BR CA CN DK FI FR DE IN JP MX NO SE GB SG LB PK)
@@ -272,6 +280,8 @@ module ActiveMerchant #:nodoc:
272
280
  add_business_rules_data(xml, creditcard_or_reference, options)
273
281
  add_stored_credential_options(xml, options)
274
282
  add_issuer_additional_data(xml, options)
283
+ add_merchant_description(xml, options)
284
+ add_partner_solution_id(xml)
275
285
 
276
286
  xml.target!
277
287
  end
@@ -298,6 +308,9 @@ module ActiveMerchant #:nodoc:
298
308
  add_capture_service(xml, request_id, request_token)
299
309
  add_business_rules_data(xml, authorization, options)
300
310
  add_issuer_additional_data(xml, options)
311
+ add_merchant_description(xml, options)
312
+ add_partner_solution_id(xml)
313
+
301
314
  xml.target!
302
315
  end
303
316
 
@@ -314,8 +327,12 @@ module ActiveMerchant #:nodoc:
314
327
  add_threeds_services(xml, options)
315
328
  add_payment_network_token(xml) if network_tokenization?(payment_method_or_reference)
316
329
  add_business_rules_data(xml, payment_method_or_reference, options) unless options[:pinless_debit_card]
330
+ add_stored_credential_options(xml, options)
317
331
  end
332
+
318
333
  add_issuer_additional_data(xml, options)
334
+ add_merchant_description(xml, options)
335
+ add_partner_solution_id(xml)
319
336
 
320
337
  xml.target!
321
338
  end
@@ -325,7 +342,8 @@ module ActiveMerchant #:nodoc:
325
342
  options[:order_id] = order_id
326
343
 
327
344
  xml = Builder::XmlMarkup.new indent: 2
328
- if action == 'capture'
345
+ case action
346
+ when 'capture', 'purchase'
329
347
  add_mdd_fields(xml, options)
330
348
  add_void_service(xml, request_id, request_token)
331
349
  else
@@ -334,6 +352,8 @@ module ActiveMerchant #:nodoc:
334
352
  add_auth_reversal_service(xml, request_id, request_token)
335
353
  end
336
354
  add_issuer_additional_data(xml, options)
355
+ add_partner_solution_id(xml)
356
+
337
357
  xml.target!
338
358
  end
339
359
 
@@ -344,6 +364,7 @@ module ActiveMerchant #:nodoc:
344
364
  xml = Builder::XmlMarkup.new indent: 2
345
365
  add_purchase_data(xml, money, true, options)
346
366
  add_credit_service(xml, request_id, request_token)
367
+ add_partner_solution_id(xml)
347
368
 
348
369
  xml.target!
349
370
  end
@@ -355,6 +376,7 @@ module ActiveMerchant #:nodoc:
355
376
  add_mdd_fields(xml, options)
356
377
  add_credit_service(xml)
357
378
  add_issuer_additional_data(xml, options)
379
+ add_merchant_description(xml, options)
358
380
 
359
381
  xml.target!
360
382
  end
@@ -458,6 +480,7 @@ module ActiveMerchant #:nodoc:
458
480
  xml.tag! 'clientLibrary', 'Ruby Active Merchant'
459
481
  xml.tag! 'clientLibraryVersion', VERSION
460
482
  xml.tag! 'clientEnvironment', RUBY_PLATFORM
483
+
461
484
  add_merchant_descriptor(xml, options)
462
485
  end
463
486
 
@@ -469,6 +492,18 @@ module ActiveMerchant #:nodoc:
469
492
  end
470
493
  end
471
494
 
495
+ def add_merchant_description(xml, options)
496
+ return unless options[:merchant_descriptor_name] || options[:merchant_descriptor_address1] || options[:merchant_descriptor_locality]
497
+
498
+ xml.tag! 'merchantInformation' do
499
+ xml.tag! 'merchantDescriptor' do
500
+ xml.tag! 'name', options[:merchant_descriptor_name] if options[:merchant_descriptor_name]
501
+ xml.tag! 'address1', options[:merchant_descriptor_address1] if options[:merchant_descriptor_address1]
502
+ xml.tag! 'locality', options[:merchant_descriptor_locality] if options[:merchant_descriptor_locality]
503
+ end
504
+ end
505
+ end
506
+
472
507
  def add_purchase_data(xml, money = 0, include_grand_total = false, options={})
473
508
  xml.tag! 'purchaseTotals' do
474
509
  xml.tag! 'currency', options[:currency] || currency(money)
@@ -580,9 +615,12 @@ module ActiveMerchant #:nodoc:
580
615
  xml.tag!('cavvAlgorithm', threeds_2_options[:cavv_algorithm]) if threeds_2_options[:cavv_algorithm]
581
616
  xml.tag!('paSpecificationVersion', threeds_2_options[:version]) if threeds_2_options[:version]
582
617
  xml.tag!('directoryServerTransactionID', threeds_2_options[:ds_transaction_id]) if threeds_2_options[:ds_transaction_id]
583
- xml.tag!('commerceIndicator', options[:commerce_indicator]) if options[:commerce_indicator]
618
+ xml.tag!('commerceIndicator', options[:commerce_indicator] || ECI_BRAND_MAPPING[card_brand(payment_method).to_sym])
584
619
  xml.tag!('eciRaw', threeds_2_options[:eci]) if threeds_2_options[:eci]
585
- xml.tag!('xid', threeds_2_options[:xid]) if threeds_2_options[:xid]
620
+
621
+ xid = threeds_2_options[:xid] || threeds_2_options[:cavv]
622
+ xml.tag!('xid', xid) if xid
623
+
586
624
  xml.tag!('veresEnrolled', threeds_2_options[:enrolled]) if threeds_2_options[:enrolled]
587
625
  xml.tag!('paresStatus', threeds_2_options[:authentication_response_status]) if threeds_2_options[:authentication_response_status]
588
626
  end
@@ -592,12 +630,13 @@ module ActiveMerchant #:nodoc:
592
630
 
593
631
  xml.tag! 'ucaf' do
594
632
  xml.tag!('authenticationData', options[:three_d_secure][:cavv])
595
- xml.tag!('collectionIndicator', options[:collection_indicator]) if options[:collection_indicator]
633
+ xml.tag!('collectionIndicator', options[:collection_indicator] || DEFAULT_COLLECTION_INDICATOR)
596
634
  end
597
635
  end
598
636
 
599
637
  def stored_credential_commerce_indicator(options)
600
638
  return unless options[:stored_credential]
639
+
601
640
  return if options[:stored_credential][:initial_transaction]
602
641
 
603
642
  case options[:stored_credential][:reason_type]
@@ -613,26 +652,28 @@ module ActiveMerchant #:nodoc:
613
652
  def add_auth_network_tokenization(xml, payment_method, options)
614
653
  return unless network_tokenization?(payment_method)
615
654
 
616
- case card_brand(payment_method).to_sym
655
+ brand = card_brand(payment_method).to_sym
656
+
657
+ case brand
617
658
  when :visa
618
659
  xml.tag! 'ccAuthService', {'run' => 'true'} do
619
660
  xml.tag!('cavv', payment_method.payment_cryptogram)
620
- xml.tag!('commerceIndicator', 'vbv')
661
+ xml.tag!('commerceIndicator', ECI_BRAND_MAPPING[brand])
621
662
  xml.tag!('xid', payment_method.payment_cryptogram)
622
663
  end
623
664
  when :master
624
665
  xml.tag! 'ucaf' do
625
666
  xml.tag!('authenticationData', payment_method.payment_cryptogram)
626
- xml.tag!('collectionIndicator', '2')
667
+ xml.tag!('collectionIndicator', DEFAULT_COLLECTION_INDICATOR)
627
668
  end
628
669
  xml.tag! 'ccAuthService', {'run' => 'true'} do
629
- xml.tag!('commerceIndicator', 'spa')
670
+ xml.tag!('commerceIndicator', ECI_BRAND_MAPPING[brand])
630
671
  end
631
672
  when :american_express
632
673
  cryptogram = Base64.decode64(payment_method.payment_cryptogram)
633
674
  xml.tag! 'ccAuthService', {'run' => 'true'} do
634
675
  xml.tag!('cavv', Base64.encode64(cryptogram[0...20]))
635
- xml.tag!('commerceIndicator', 'aesk')
676
+ xml.tag!('commerceIndicator', ECI_BRAND_MAPPING[brand])
636
677
  xml.tag!('xid', Base64.encode64(cryptogram[20...40]))
637
678
  end
638
679
  end
@@ -783,14 +824,16 @@ module ActiveMerchant #:nodoc:
783
824
  def add_stored_credential_options(xml, options={})
784
825
  return unless options[:stored_credential]
785
826
 
786
- if options[:stored_credential][:initial_transaction]
787
- xml.tag! 'subsequentAuthFirst', 'true'
788
- elsif options[:stored_credential][:reason_type] == 'unscheduled'
789
- xml.tag! 'subsequentAuth', 'true'
790
- xml.tag! 'subsequentAuthTransactionID', options[:stored_credential][:network_transaction_id]
791
- else
792
- xml.tag! 'subsequentAuthTransactionID', options[:stored_credential][:network_transaction_id]
793
- end
827
+ xml.tag! 'subsequentAuth', 'true' if options[:stored_credential][:initiator] == 'merchant'
828
+ xml.tag! 'subsequentAuthFirst', 'true' if options[:stored_credential][:initial_transaction]
829
+ xml.tag! 'subsequentAuthTransactionID', options[:stored_credential][:network_transaction_id] if options[:stored_credential][:initiator] == 'merchant'
830
+ xml.tag! 'subsequentAuthStoredCredential', 'true' if options[:stored_credential][:initiator] == 'cardholder' && !options[:stored_credential][:initial_transaction] || options[:stored_credential][:initiator] == 'merchant' && options[:stored_credential][:reason_type] == 'unscheduled'
831
+ end
832
+
833
+ def add_partner_solution_id(xml)
834
+ return unless application_id
835
+
836
+ xml.tag!('partnerSolutionID', application_id)
794
837
  end
795
838
 
796
839
  # Where we actually build the full SOAP request using builder
@@ -834,7 +877,7 @@ module ActiveMerchant #:nodoc:
834
877
  end
835
878
 
836
879
  success = success?(response)
837
- message = response[:message]
880
+ message = message_from(response)
838
881
 
839
882
  authorization = success ? authorization_from(response, action, amount, options) : nil
840
883
 
@@ -873,7 +916,7 @@ module ActiveMerchant #:nodoc:
873
916
  if node.has_elements?
874
917
  node.elements.each { |e| parse_element(reply, e) }
875
918
  else
876
- if node.parent.name =~ /item/
919
+ if /item/.match?(node.parent.name)
877
920
  parent = node.parent.name
878
921
  parent += '_' + node.parent.attributes['id'] if node.parent.attributes['id']
879
922
  parent += '_'
@@ -901,6 +944,16 @@ module ActiveMerchant #:nodoc:
901
944
  def success?(response)
902
945
  response[:decision] == @@decision_codes[:accept]
903
946
  end
947
+
948
+ def message_from(response)
949
+ if response[:reasonCode] == '101' && response[:missingField]
950
+ "#{response[:message]}: #{response[:missingField]}"
951
+ elsif response[:reasonCode] == '102' && response[:invalidField]
952
+ "#{response[:message]}: #{response[:invalidField]}"
953
+ else
954
+ response[:message]
955
+ end
956
+ end
904
957
  end
905
958
  end
906
959
  end