activemerchant 1.56.0 → 1.66.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (113) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +331 -0
  3. data/README.md +9 -9
  4. data/lib/active_merchant/billing/check.rb +3 -0
  5. data/lib/active_merchant/billing/credit_card.rb +8 -3
  6. data/lib/active_merchant/billing/credit_card_methods.rb +41 -1
  7. data/lib/active_merchant/billing/gateway.rb +14 -6
  8. data/lib/active_merchant/billing/gateways/adyen.rb +228 -0
  9. data/lib/active_merchant/billing/gateways/authorize_net.rb +157 -44
  10. data/lib/active_merchant/billing/gateways/authorize_net_cim.rb +7 -4
  11. data/lib/active_merchant/billing/gateways/barclaycard_smartpay.rb +283 -0
  12. data/lib/active_merchant/billing/gateways/beanstream/beanstream_core.rb +68 -2
  13. data/lib/active_merchant/billing/gateways/blue_pay.rb +2 -2
  14. data/lib/active_merchant/billing/gateways/blue_snap.rb +348 -0
  15. data/lib/active_merchant/billing/gateways/bpoint.rb +1 -1
  16. data/lib/active_merchant/billing/gateways/braintree_blue.rb +58 -20
  17. data/lib/active_merchant/billing/gateways/bridge_pay.rb +37 -8
  18. data/lib/active_merchant/billing/gateways/card_stream.rb +161 -40
  19. data/lib/active_merchant/billing/gateways/cashnet.rb +1 -0
  20. data/lib/active_merchant/billing/gateways/checkout_v2.rb +5 -2
  21. data/lib/active_merchant/billing/gateways/citrus_pay.rb +24 -0
  22. data/lib/active_merchant/billing/gateways/clearhaus.rb +24 -40
  23. data/lib/active_merchant/billing/gateways/conekta.rb +6 -1
  24. data/lib/active_merchant/billing/gateways/creditcall.rb +1 -1
  25. data/lib/active_merchant/billing/gateways/credorax.rb +310 -0
  26. data/lib/active_merchant/billing/gateways/culqi.rb +279 -0
  27. data/lib/active_merchant/billing/gateways/cyber_source.rb +80 -64
  28. data/lib/active_merchant/billing/gateways/data_cash.rb +10 -304
  29. data/lib/active_merchant/billing/gateways/digitzs.rb +292 -0
  30. data/lib/active_merchant/billing/gateways/elavon.rb +40 -26
  31. data/lib/active_merchant/billing/gateways/element.rb +356 -0
  32. data/lib/active_merchant/billing/gateways/fat_zebra.rb +16 -2
  33. data/lib/active_merchant/billing/gateways/firstdata_e4.rb +6 -1
  34. data/lib/active_merchant/billing/gateways/forte.rb +10 -2
  35. data/lib/active_merchant/billing/gateways/global_collect.rb +311 -0
  36. data/lib/active_merchant/billing/gateways/global_transport.rb +1 -0
  37. data/lib/active_merchant/billing/gateways/iats_payments.rb +13 -0
  38. data/lib/active_merchant/billing/gateways/in_context_paypal_express.rb +15 -0
  39. data/lib/active_merchant/billing/gateways/iveri.rb +251 -0
  40. data/lib/active_merchant/billing/gateways/jetpay.rb +33 -19
  41. data/lib/active_merchant/billing/gateways/kushki.rb +217 -0
  42. data/lib/active_merchant/billing/gateways/latitude19.rb +416 -0
  43. data/lib/active_merchant/billing/gateways/linkpoint.rb +2 -0
  44. data/lib/active_merchant/billing/gateways/litle.rb +29 -13
  45. data/lib/active_merchant/billing/gateways/mastercard.rb +268 -0
  46. data/lib/active_merchant/billing/gateways/maxipago.rb +145 -122
  47. data/lib/active_merchant/billing/gateways/merchant_e_solutions.rb +15 -1
  48. data/lib/active_merchant/billing/gateways/merchant_warrior.rb +10 -7
  49. data/lib/active_merchant/billing/gateways/mercury.rb +13 -5
  50. data/lib/active_merchant/billing/gateways/metrics_global.rb +1 -1
  51. data/lib/active_merchant/billing/gateways/migs.rb +23 -1
  52. data/lib/active_merchant/billing/gateways/monei.rb +1 -1
  53. data/lib/active_merchant/billing/gateways/moneris.rb +21 -1
  54. data/lib/active_merchant/billing/gateways/nab_transact.rb +12 -0
  55. data/lib/active_merchant/billing/gateways/ncr_secure_pay.rb +165 -0
  56. data/lib/active_merchant/billing/gateways/netbanx.rb +245 -0
  57. data/lib/active_merchant/billing/gateways/nmi.rb +30 -9
  58. data/lib/active_merchant/billing/gateways/omise.rb +9 -5
  59. data/lib/active_merchant/billing/gateways/openpay.rb +10 -1
  60. data/lib/active_merchant/billing/gateways/opp.rb +362 -0
  61. data/lib/active_merchant/billing/gateways/orbital.rb +28 -7
  62. data/lib/active_merchant/billing/gateways/pagarme.rb +248 -0
  63. data/lib/active_merchant/billing/gateways/pay_junction_v2.rb +190 -0
  64. data/lib/active_merchant/billing/gateways/payeezy.rb +61 -12
  65. data/lib/active_merchant/billing/gateways/payflow/payflow_common_api.rb +1 -1
  66. data/lib/active_merchant/billing/gateways/payflow.rb +6 -0
  67. data/lib/active_merchant/billing/gateways/payment_express.rb +1 -1
  68. data/lib/active_merchant/billing/gateways/paymill.rb +29 -11
  69. data/lib/active_merchant/billing/gateways/paypal/paypal_common_api.rb +1 -1
  70. data/lib/active_merchant/billing/gateways/paypal_express.rb +1 -6
  71. data/lib/active_merchant/billing/gateways/payu_in.rb +3 -2
  72. data/lib/active_merchant/billing/gateways/payu_latam.rb +402 -0
  73. data/lib/active_merchant/billing/gateways/pin.rb +6 -3
  74. data/lib/active_merchant/billing/gateways/pro_pay.rb +326 -0
  75. data/lib/active_merchant/billing/gateways/psl_card.rb +3 -3
  76. data/lib/active_merchant/billing/gateways/quickpay/quickpay_common.rb +1 -1
  77. data/lib/active_merchant/billing/gateways/quickpay/quickpay_v10.rb +0 -2
  78. data/lib/active_merchant/billing/gateways/quickpay/quickpay_v4to7.rb +1 -1
  79. data/lib/active_merchant/billing/gateways/quickpay.rb +3 -3
  80. data/lib/active_merchant/billing/gateways/qvalent.rb +44 -1
  81. data/lib/active_merchant/billing/gateways/redsys.rb +3 -0
  82. data/lib/active_merchant/billing/gateways/s5.rb +8 -5
  83. data/lib/active_merchant/billing/gateways/safe_charge.rb +220 -0
  84. data/lib/active_merchant/billing/gateways/sage.rb +397 -128
  85. data/lib/active_merchant/billing/gateways/sage_pay.rb +45 -20
  86. data/lib/active_merchant/billing/gateways/secure_net.rb +0 -5
  87. data/lib/active_merchant/billing/gateways/secure_pay.rb +1 -1
  88. data/lib/active_merchant/billing/gateways/secure_pay_au.rb +12 -0
  89. data/lib/active_merchant/billing/gateways/securion_pay.rb +46 -17
  90. data/lib/active_merchant/billing/gateways/stripe.rb +125 -29
  91. data/lib/active_merchant/billing/gateways/telr.rb +275 -0
  92. data/lib/active_merchant/billing/gateways/tns.rb +13 -222
  93. data/lib/active_merchant/billing/gateways/trans_first.rb +40 -16
  94. data/lib/active_merchant/billing/gateways/trans_first_transaction_express.rb +606 -0
  95. data/lib/active_merchant/billing/gateways/usa_epay_advanced.rb +114 -9
  96. data/lib/active_merchant/billing/gateways/vanco.rb +14 -10
  97. data/lib/active_merchant/billing/gateways/visanet_peru.rb +209 -0
  98. data/lib/active_merchant/billing/gateways/wepay.rb +73 -38
  99. data/lib/active_merchant/billing/gateways/wirecard.rb +1 -0
  100. data/lib/active_merchant/billing/gateways/world_net.rb +344 -0
  101. data/lib/active_merchant/billing/gateways/worldpay.rb +48 -16
  102. data/lib/active_merchant/billing/network_tokenization_credit_card.rb +15 -0
  103. data/lib/active_merchant/country.rb +6 -4
  104. data/lib/active_merchant/posts_data.rb +1 -1
  105. data/lib/active_merchant/version.rb +1 -1
  106. metadata +32 -13
  107. data/lib/active_merchant/billing/gateways/app55.rb +0 -176
  108. data/lib/active_merchant/billing/gateways/barclays_epdq.rb +0 -314
  109. data/lib/active_merchant/billing/gateways/certo_direct.rb +0 -278
  110. data/lib/active_merchant/billing/gateways/sage/sage_bankcard.rb +0 -89
  111. data/lib/active_merchant/billing/gateways/sage/sage_core.rb +0 -115
  112. data/lib/active_merchant/billing/gateways/sage/sage_vault.rb +0 -149
  113. data/lib/active_merchant/billing/gateways/sage/sage_virtual_check.rb +0 -97
@@ -0,0 +1,311 @@
1
+ module ActiveMerchant #:nodoc:
2
+ module Billing #:nodoc:
3
+ class GlobalCollectGateway < Gateway
4
+ self.display_name = "GlobalCollect"
5
+ self.homepage_url = "http://www.globalcollect.com/"
6
+
7
+ self.test_url = "https://api-sandbox.globalcollect.com/"
8
+ self.live_url = "https://api.globalcollect.com/"
9
+
10
+ self.supported_countries = %w(AD AE AT AU BD BE BG BN CA CH CY CZ DE DK
11
+ EE EG ES FI FR GB GI GR HK HU ID IE IL IM IN IS IT JO KW LB LI LK LT LU
12
+ LV MC MT MU MV MX MY NL NO NZ OM PH PL PT QA RO SA SE SG SI SK SM TR TT
13
+ UM US VA VN ZA)
14
+ self.default_currency = "USD"
15
+ self.money_format = :cents
16
+ self.supported_cardtypes = [:visa, :master, :american_express, :discover]
17
+
18
+ def initialize(options={})
19
+ requires!(options, :merchant_id, :api_key_id, :secret_api_key)
20
+ super
21
+ end
22
+
23
+ def purchase(money, payment, options={})
24
+ MultiResponse.run do |r|
25
+ r.process { authorize(money, payment, options) }
26
+ r.process { capture(money, r.authorization, options) } unless capture_requested?(r)
27
+ end
28
+ end
29
+
30
+ def authorize(money, payment, options={})
31
+ post = nestable_hash
32
+ add_order(post, money, options)
33
+ add_payment(post, payment)
34
+ add_customer_data(post, options, payment)
35
+ add_address(post, payment, options)
36
+
37
+ commit(:authorize, post)
38
+ end
39
+
40
+ def capture(money, authorization, options={})
41
+ post = nestable_hash
42
+ add_order(post, money, options)
43
+ add_customer_data(post, options)
44
+ commit(:capture, post, authorization)
45
+ end
46
+
47
+ def refund(money, authorization, options={})
48
+ post = nestable_hash
49
+ add_amount(post, money, options)
50
+ add_refund_customer_data(post, options)
51
+ commit(:refund, post, authorization)
52
+ end
53
+
54
+ def void(authorization, options={})
55
+ post = nestable_hash
56
+ commit(:void, post, authorization)
57
+ end
58
+
59
+ def verify(payment, options={})
60
+ MultiResponse.run(:use_first_response) do |r|
61
+ r.process { authorize(100, payment, options) }
62
+ r.process { void(r.authorization, options) }
63
+ end
64
+ end
65
+
66
+ def supports_scrubbing?
67
+ true
68
+ end
69
+
70
+ def scrub(transcript)
71
+ transcript.
72
+ gsub(%r((Authorization: )[^\\]*)i, '\1[FILTERED]').
73
+ gsub(%r(("cardNumber\\":\\")\d+), '\1[FILTERED]').
74
+ gsub(%r(("cvv\\":\\")\d+), '\1[FILTERED]')
75
+ end
76
+
77
+ private
78
+
79
+ BRAND_MAP = {
80
+ "visa" => "1",
81
+ "american_express" => "2",
82
+ "master" => "3",
83
+ "discover" => "128",
84
+ "jcb" => "125",
85
+ "diners_club" => "132"
86
+ }
87
+
88
+ def add_order(post, money, options)
89
+ post["order"]["amountOfMoney"] = {
90
+ "amount" => amount(money),
91
+ "currencyCode" => options[:currency] || currency(money)
92
+ }
93
+ post["order"]["references"] = {
94
+ "merchantReference" => options[:order_id],
95
+ "descriptor" => options[:description] # Max 256 chars
96
+ }
97
+ post["order"]["references"]["invoiceData"] = {
98
+ "invoiceNumber" => options[:invoice]
99
+ }
100
+ end
101
+
102
+ def add_amount(post, money, options={})
103
+ post["amountOfMoney"] = {
104
+ "amount" => amount(money),
105
+ "currencyCode" => options[:currency] || currency(money)
106
+ }
107
+ end
108
+
109
+ def add_payment(post, payment)
110
+ year = format(payment.year, :two_digits)
111
+ month = format(payment.month, :two_digits)
112
+ expirydate = "#{month}#{year}"
113
+
114
+ post["cardPaymentMethodSpecificInput"] = {
115
+ "paymentProductId" => BRAND_MAP[payment.brand],
116
+ "skipAuthentication" => "true", # refers to 3DSecure
117
+ "skipFraudService" => "true"
118
+ }
119
+ post["cardPaymentMethodSpecificInput"]["card"] = {
120
+ "cvv" => payment.verification_value,
121
+ "cardNumber" => payment.number,
122
+ "expiryDate" => expirydate,
123
+ "cardholderName" => payment.name
124
+ }
125
+ end
126
+
127
+ def add_customer_data(post, options, payment = nil)
128
+ post["order"]["customer"] = {
129
+ "merchantCustomerId" => options[:customer]
130
+ }
131
+ if payment
132
+ post["order"]["customer"]["personalInformation"] = {
133
+ "name" => {
134
+ "firstName" => payment.first_name[0..14],
135
+ "surname" => payment.last_name[0..69]
136
+ }
137
+ }
138
+ end
139
+ post["order"]["companyInformation"] = {
140
+ "name" => options[:company]
141
+ }
142
+ post["order"]["contactDetails"] = {
143
+ "emailAddress" => options[:email]
144
+ }
145
+ if address = options[:billing_address] || options[:address]
146
+ post["order"]["contactDetails"] = {
147
+ "phoneNumber" => address[:phone]
148
+ }
149
+ end
150
+ end
151
+
152
+ def add_refund_customer_data(post, options)
153
+ if address = options[:billing_address] || options[:address]
154
+ post["customer"]["address"] = {
155
+ "countryCode" => address[:country]
156
+ }
157
+ post["customer"]["contactDetails"] = {
158
+ "emailAddress" => options[:email],
159
+ "phoneNumber" => address[:phone]
160
+ }
161
+ end
162
+ end
163
+
164
+ def add_address(post, creditcard, options)
165
+ billing_address = options[:billing_address] || options[:address]
166
+ shipping_address = options[:shipping_address]
167
+ if billing_address = options[:billing_address] || options[:address]
168
+ post["order"]["customer"]["billingAddress"] = {
169
+ "street" => billing_address[:address1],
170
+ "additionalInfo" => billing_address[:address2],
171
+ "zip" => billing_address[:zip],
172
+ "city" => billing_address[:city],
173
+ "state" => billing_address[:state],
174
+ "countryCode" => billing_address[:country]
175
+ }
176
+ end
177
+ if shipping_address
178
+ post["order"]["customer"]["shippingAddress"] = {
179
+ "street" => shipping_address[:address1],
180
+ "additionalInfo" => shipping_address[:address2],
181
+ "zip" => shipping_address[:zip],
182
+ "city" => shipping_address[:city],
183
+ "state" => shipping_address[:state],
184
+ "countryCode" => shipping_address[:country]
185
+ }
186
+ post["order"]["customer"]["shippingAddress"]["name"] = {
187
+ "firstName" => shipping_address[:firstname],
188
+ "surname" => shipping_address[:lastname]
189
+ }
190
+ end
191
+ end
192
+
193
+ def parse(body)
194
+ JSON.parse(body)
195
+ end
196
+
197
+ def url(action, authorization)
198
+ (test? ? test_url : live_url) + uri(action, authorization)
199
+ end
200
+
201
+ def uri(action, authorization)
202
+ uri = "/v1/#{@options[:merchant_id]}/"
203
+ case action
204
+ when :authorize
205
+ uri + "payments"
206
+ when :capture
207
+ uri + "payments/#{authorization}/approve"
208
+ when :refund
209
+ uri + "payments/#{authorization}/refund"
210
+ when :void
211
+ uri + "payments/#{authorization}/cancel"
212
+ end
213
+ end
214
+
215
+ def commit(action, post, authorization = nil)
216
+ begin
217
+ response = parse(ssl_post(url(action, authorization), post.to_json, headers(action, post, authorization)))
218
+ rescue ResponseError => e
219
+ if e.response.code.to_i >= 400
220
+ response = parse(e.response.body)
221
+ end
222
+ end
223
+
224
+ succeeded = success_from(response)
225
+ Response.new(
226
+ succeeded,
227
+ message_from(succeeded, response),
228
+ response,
229
+ authorization: authorization_from(succeeded, response),
230
+ error_code: error_code_from(succeeded, response),
231
+ test: test?
232
+ )
233
+
234
+ end
235
+
236
+ def headers(action, post, authorization = nil)
237
+ {
238
+ "Content-type" => content_type,
239
+ "Authorization" => auth_digest(action, post, authorization),
240
+ "Date" => date
241
+ }
242
+ end
243
+
244
+ def auth_digest(action, post, authorization = nil)
245
+ data = <<-EOS
246
+ POST
247
+ #{content_type}
248
+ #{date}
249
+ #{uri(action, authorization)}
250
+ EOS
251
+ digest = OpenSSL::Digest.new('sha256')
252
+ key = @options[:secret_api_key]
253
+ "GCS v1HMAC:#{@options[:api_key_id]}:#{Base64.strict_encode64(OpenSSL::HMAC.digest(digest, key, data))}"
254
+ end
255
+
256
+ def date
257
+ @date ||= Time.now.strftime("%a, %d %b %Y %H:%M:%S %Z") # Must be same in digest and HTTP header
258
+ end
259
+
260
+ def content_type
261
+ "application/json"
262
+ end
263
+
264
+ def success_from(response)
265
+ !response["errorId"] && response["status"] != "REJECTED"
266
+ end
267
+
268
+ def message_from(succeeded, response)
269
+ if succeeded
270
+ "Succeeded"
271
+ else
272
+ if errors = response["errors"]
273
+ errors.first.try(:[], "message")
274
+ elsif status = response["status"]
275
+ "Status: " + status
276
+ else
277
+ "No message available"
278
+ end
279
+ end
280
+ end
281
+
282
+ def authorization_from(succeeded, response)
283
+ if succeeded
284
+ response["id"] || response["payment"]["id"] || response["paymentResult"]["payment"]["id"]
285
+ else
286
+ response["errorId"]
287
+ end
288
+ end
289
+
290
+ def error_code_from(succeeded, response)
291
+ unless succeeded
292
+ if errors = response["errors"]
293
+ errors.first.try(:[], "code")
294
+ elsif status = response.try(:[], "statusOutput").try(:[], "statusCode")
295
+ status.to_s
296
+ else
297
+ "No error code available"
298
+ end
299
+ end
300
+ end
301
+
302
+ def nestable_hash
303
+ Hash.new {|h,k| h[k] = Hash.new(&h.default_proc) }
304
+ end
305
+
306
+ def capture_requested?(response)
307
+ response.params.try(:[], "payment").try(:[], "status") == "CAPTURE_REQUESTED"
308
+ end
309
+ end
310
+ end
311
+ end
@@ -9,6 +9,7 @@ module ActiveMerchant #:nodoc:
9
9
  self.supported_countries = %w(CA PR US)
10
10
  self.default_currency = 'USD'
11
11
  self.supported_cardtypes = [:visa, :master, :american_express, :discover, :diners_club, :jcb]
12
+ self.ssl_version = :TLSv1
12
13
 
13
14
  self.homepage_url = 'https://www.globalpaymentsinc.com'
14
15
  self.display_name = 'Global Transport'
@@ -75,6 +75,19 @@ module ActiveMerchant #:nodoc:
75
75
  commit(:unstore, post)
76
76
  end
77
77
 
78
+ def supports_scrubbing?
79
+ true
80
+ end
81
+
82
+ def scrub(transcript)
83
+ transcript.
84
+ gsub(%r((<agentCode>).+(</agentCode>)), '\1[FILTERED]\2').
85
+ gsub(%r((<password>).+(</password>)), '\1[FILTERED]\2').
86
+ gsub(%r((<creditCardNum>).+(</creditCardNum>)), '\1[FILTERED]\2').
87
+ gsub(%r((<cvv2>).+(</cvv2>)), '\1[FILTERED]\2').
88
+ gsub(%r((<accountNum>).+(</accountNum>)), '\1[FILTERED]\2')
89
+ end
90
+
78
91
  private
79
92
 
80
93
  def add_ip(post, options)
@@ -0,0 +1,15 @@
1
+ module ActiveMerchant #:nodoc:
2
+ module Billing #:nodoc:
3
+ class InContextPaypalExpressGateway < PaypalExpressGateway
4
+ self.test_redirect_url = 'https://www.sandbox.paypal.com/checkoutnow'
5
+ self.live_redirect_url = 'https://www.paypal.com/checkoutnow'
6
+
7
+ def redirect_url_for(token, options = {})
8
+ options = {review: true}.update(options)
9
+ url = "#{redirect_url}?token=#{token}"
10
+ url += '&useraction=commit' unless options[:review]
11
+ url
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,251 @@
1
+ require 'nokogiri'
2
+
3
+ module ActiveMerchant #:nodoc:
4
+ module Billing #:nodoc:
5
+ class IveriGateway < Gateway
6
+ self.live_url = self.test_url = 'https://portal.nedsecure.co.za/iVeriWebService/Service.asmx'
7
+
8
+ self.supported_countries = ['US', 'ZA', 'GB']
9
+ self.default_currency = 'ZAR'
10
+ self.money_format = :cents
11
+ self.supported_cardtypes = [:visa, :master, :american_express]
12
+
13
+ self.homepage_url = 'http://www.iveri.com'
14
+ self.display_name = 'iVeri'
15
+
16
+ def initialize(options={})
17
+ requires!(options, :app_id, :cert_id)
18
+ super
19
+ end
20
+
21
+ def purchase(money, payment_method, options={})
22
+ post = build_vxml_request('Debit', options) do |xml|
23
+ add_auth_purchase_params(xml, money, payment_method, options)
24
+ end
25
+
26
+ commit(post)
27
+ end
28
+
29
+ def authorize(money, payment_method, options={})
30
+ post = build_vxml_request('Authorisation', options) do |xml|
31
+ add_auth_purchase_params(xml, money, payment_method, options)
32
+ end
33
+
34
+ commit(post)
35
+ end
36
+
37
+ def capture(money, authorization, options={})
38
+ post = build_vxml_request('Debit', options) do |xml|
39
+ add_authorization(xml, authorization, options)
40
+ end
41
+
42
+ commit(post)
43
+ end
44
+
45
+ def refund(money, authorization, options={})
46
+ post = build_vxml_request('Credit', options) do |xml|
47
+ add_amount(xml, money, options)
48
+ add_authorization(xml, authorization, options)
49
+ end
50
+
51
+ commit(post)
52
+ end
53
+
54
+ def void(authorization, options={})
55
+ post = build_vxml_request('Void', options) do |xml|
56
+ add_authorization(xml, authorization, options)
57
+ end
58
+
59
+ commit(post)
60
+ end
61
+
62
+ def verify(credit_card, options={})
63
+ authorize(0, credit_card, options)
64
+ end
65
+
66
+ def verify_credentials
67
+ void = void('', options)
68
+ return true if void.message == 'Missing OriginalMerchantTrace'
69
+ false
70
+ end
71
+
72
+ def supports_scrubbing?
73
+ true
74
+ end
75
+
76
+ def scrub(transcript)
77
+ transcript.
78
+ gsub(%r((CertificateID=\\\")[^\\]*), '\1[FILTERED]').
79
+ gsub(%r((&lt;PAN&gt;)[^&]*), '\1[FILTERED]').
80
+ gsub(%r((&lt;CardSecurityCode&gt;)[^&]*), '\1[FILTERED]')
81
+ end
82
+
83
+ private
84
+
85
+ def build_xml_envelope(vxml)
86
+ builder = Nokogiri::XML::Builder.new(:encoding => 'UTF-8') do |xml|
87
+ xml[:soap].Envelope 'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance', 'xmlns:xsd' => 'http://www.w3.org/2001/XMLSchema', 'xmlns:soap' => 'http://schemas.xmlsoap.org/soap/envelope/' do
88
+ xml[:soap].Body do
89
+ xml.Execute 'xmlns' => 'http://iveri.com/' do
90
+ xml.validateRequest 'true'
91
+ xml.protocol 'V_XML'
92
+ xml.protocolVersion '2.0'
93
+ xml.request vxml
94
+ end
95
+ end
96
+ end
97
+ end
98
+
99
+ builder.to_xml
100
+ end
101
+
102
+ def build_vxml_request(action, options)
103
+ builder = Nokogiri::XML::Builder.new do |xml|
104
+ xml.V_XML('Version' => '2.0', 'CertificateID' => @options[:cert_id], 'Direction' => 'Request') do
105
+ xml.Transaction('ApplicationID' => @options[:app_id], 'Command' => action, 'Mode' => mode) do
106
+ yield(xml)
107
+ end
108
+ end
109
+ end
110
+
111
+ builder.doc.root.to_xml
112
+ end
113
+
114
+ def add_auth_purchase_params(post, money, payment_method, options)
115
+ add_card_holder_authentication(post, options)
116
+ add_amount(post, money, options)
117
+ add_electronic_commerce_indicator(post, options)
118
+ add_payment_method(post, payment_method, options)
119
+ end
120
+
121
+ def add_amount(post, money, options)
122
+ post.Amount(amount(money))
123
+ post.Currency(options[:currency] || default_currency)
124
+ end
125
+
126
+ def add_electronic_commerce_indicator(post, options)
127
+ post.ElectronicCommerceIndicator(options[:eci]) if options[:eci]
128
+ end
129
+
130
+ def add_authorization(post, authorization, options)
131
+ post.MerchantReference(split_auth(authorization)[2])
132
+ post.TransactionIndex(split_auth(authorization)[1])
133
+ post.OriginalRequestID(split_auth(authorization)[0])
134
+ end
135
+
136
+ def add_payment_method(post, payment_method, options)
137
+ post.ExpiryDate("#{format(payment_method.month, :two_digits)}#{payment_method.year}")
138
+ add_new_reference(post, options)
139
+ post.CardSecurityCode(payment_method.verification_value)
140
+ post.PAN(payment_method.number)
141
+ end
142
+
143
+ def add_new_reference(post, options)
144
+ post.MerchantReference(options[:order_id] || generate_unique_id)
145
+ end
146
+
147
+ def add_card_holder_authentication(post, options)
148
+ post.CardHolderAuthenticationID(options[:xid]) if options[:xid]
149
+ post.CardHolderAuthenticationData(options[:cavv]) if options[:cavv]
150
+ end
151
+
152
+ def commit(post)
153
+ raw_response = begin
154
+ ssl_post(live_url, build_xml_envelope(post), headers(post))
155
+ rescue ActiveMerchant::ResponseError => e
156
+ e.response.body
157
+ end
158
+
159
+ parsed = parse(raw_response)
160
+ succeeded = success_from(parsed)
161
+
162
+ Response.new(
163
+ succeeded,
164
+ message_from(parsed, succeeded),
165
+ parsed,
166
+ authorization: authorization_from(parsed),
167
+ error_code: error_code_from(parsed, succeeded),
168
+ test: test?
169
+ )
170
+ end
171
+
172
+ def mode
173
+ test? ? 'Test' : 'Live'
174
+ end
175
+
176
+ def headers(post)
177
+ {
178
+ "Content-Type" => "text/xml; charset=utf-8",
179
+ "Content-Length" => post.size.to_s,
180
+ "SOAPAction" => "http://iveri.com/Execute"
181
+ }
182
+ end
183
+
184
+ def parse(body)
185
+ parsed = {}
186
+
187
+ vxml = Nokogiri::XML(body).remove_namespaces!.xpath("//Envelope/Body/ExecuteResponse/ExecuteResult").inner_text
188
+ doc = Nokogiri::XML(vxml)
189
+ doc.xpath("*").each do |node|
190
+ if (node.elements.empty?)
191
+ parsed[underscore(node.name)] = node.text
192
+ else
193
+ node.elements.each do |childnode|
194
+ parse_element(parsed, childnode)
195
+ end
196
+ end
197
+ end
198
+ parsed
199
+ end
200
+
201
+ def parse_element(parsed, node)
202
+ if !node.attributes.empty?
203
+ node.attributes.each do |a|
204
+ parsed[underscore(node.name)+ "_" + underscore(a[1].name)] = a[1].value
205
+ end
206
+ end
207
+
208
+ if !node.elements.empty?
209
+ node.elements.each {|e| parse_element(parsed, e) }
210
+ else
211
+ parsed[underscore(node.name)] = node.text
212
+ end
213
+ end
214
+
215
+ def success_from(response)
216
+ response['result_status'] == '0'
217
+ end
218
+
219
+ def message_from(response, succeeded)
220
+ if succeeded
221
+ "Succeeded"
222
+ else
223
+ response['result_description'] || response['result_acquirer_description']
224
+ end
225
+ end
226
+
227
+ def authorization_from(response)
228
+ "#{response['transaction_request_id']}|#{response['transaction_index']}|#{response['merchant_reference']}"
229
+ end
230
+
231
+ def split_auth(authorization)
232
+ request_id, transaction_index, merchant_reference = authorization.to_s.split('|')
233
+ [request_id, transaction_index, merchant_reference]
234
+ end
235
+
236
+ def error_code_from(response, succeeded)
237
+ unless succeeded
238
+ response['result_code']
239
+ end
240
+ end
241
+
242
+ def underscore(camel_cased_word)
243
+ camel_cased_word.to_s.gsub(/::/, '/').
244
+ gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
245
+ gsub(/([a-z\d])([A-Z])/,'\1_\2').
246
+ tr("-", "_").
247
+ downcase
248
+ end
249
+ end
250
+ end
251
+ end