activemerchant 1.119.0 → 1.124.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 (66) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +216 -1
  3. data/README.md +4 -2
  4. data/lib/active_merchant/billing/check.rb +19 -12
  5. data/lib/active_merchant/billing/credit_card.rb +3 -0
  6. data/lib/active_merchant/billing/credit_card_formatting.rb +1 -0
  7. data/lib/active_merchant/billing/credit_card_methods.rb +32 -14
  8. data/lib/active_merchant/billing/gateways/adyen.rb +94 -25
  9. data/lib/active_merchant/billing/gateways/authorize_net.rb +19 -11
  10. data/lib/active_merchant/billing/gateways/authorize_net_cim.rb +3 -0
  11. data/lib/active_merchant/billing/gateways/blue_pay.rb +29 -0
  12. data/lib/active_merchant/billing/gateways/blue_snap.rb +2 -2
  13. data/lib/active_merchant/billing/gateways/braintree_blue.rb +52 -8
  14. data/lib/active_merchant/billing/gateways/card_stream.rb +17 -13
  15. data/lib/active_merchant/billing/gateways/cashnet.rb +7 -2
  16. data/lib/active_merchant/billing/gateways/checkout_v2.rb +31 -0
  17. data/lib/active_merchant/billing/gateways/credorax.rb +15 -9
  18. data/lib/active_merchant/billing/gateways/cyber_source.rb +53 -6
  19. data/lib/active_merchant/billing/gateways/d_local.rb +9 -2
  20. data/lib/active_merchant/billing/gateways/decidir.rb +7 -1
  21. data/lib/active_merchant/billing/gateways/elavon.rb +70 -28
  22. data/lib/active_merchant/billing/gateways/element.rb +2 -0
  23. data/lib/active_merchant/billing/gateways/forte.rb +12 -0
  24. data/lib/active_merchant/billing/gateways/global_collect.rb +24 -10
  25. data/lib/active_merchant/billing/gateways/hps.rb +55 -1
  26. data/lib/active_merchant/billing/gateways/kushki.rb +23 -0
  27. data/lib/active_merchant/billing/gateways/litle.rb +1 -1
  28. data/lib/active_merchant/billing/gateways/mercado_pago.rb +5 -4
  29. data/lib/active_merchant/billing/gateways/merchant_warrior.rb +2 -0
  30. data/lib/active_merchant/billing/gateways/mit.rb +260 -0
  31. data/lib/active_merchant/billing/gateways/moka.rb +290 -0
  32. data/lib/active_merchant/billing/gateways/monei.rb +228 -144
  33. data/lib/active_merchant/billing/gateways/mundipagg.rb +14 -5
  34. data/lib/active_merchant/billing/gateways/netbanx.rb +26 -2
  35. data/lib/active_merchant/billing/gateways/nmi.rb +27 -9
  36. data/lib/active_merchant/billing/gateways/orbital.rb +99 -59
  37. data/lib/active_merchant/billing/gateways/pay_arc.rb +392 -0
  38. data/lib/active_merchant/billing/gateways/pay_conex.rb +3 -1
  39. data/lib/active_merchant/billing/gateways/pay_trace.rb +404 -0
  40. data/lib/active_merchant/billing/gateways/payeezy.rb +34 -6
  41. data/lib/active_merchant/billing/gateways/payflow/payflow_common_api.rb +1 -0
  42. data/lib/active_merchant/billing/gateways/payflow.rb +21 -4
  43. data/lib/active_merchant/billing/gateways/payment_express.rb +5 -5
  44. data/lib/active_merchant/billing/gateways/paymentez.rb +5 -0
  45. data/lib/active_merchant/billing/gateways/paypal_express.rb +1 -0
  46. data/lib/active_merchant/billing/gateways/paysafe.rb +376 -0
  47. data/lib/active_merchant/billing/gateways/payu_latam.rb +3 -3
  48. data/lib/active_merchant/billing/gateways/payway_dot_com.rb +253 -0
  49. data/lib/active_merchant/billing/gateways/qvalent.rb +23 -9
  50. data/lib/active_merchant/billing/gateways/realex.rb +18 -0
  51. data/lib/active_merchant/billing/gateways/redsys.rb +42 -24
  52. data/lib/active_merchant/billing/gateways/safe_charge.rb +25 -13
  53. data/lib/active_merchant/billing/gateways/spreedly_core.rb +13 -4
  54. data/lib/active_merchant/billing/gateways/stripe.rb +18 -8
  55. data/lib/active_merchant/billing/gateways/stripe_payment_intents.rb +126 -48
  56. data/lib/active_merchant/billing/gateways/trans_first_transaction_express.rb +2 -1
  57. data/lib/active_merchant/billing/gateways/trust_commerce.rb +2 -1
  58. data/lib/active_merchant/billing/gateways/usa_epay_transaction.rb +1 -1
  59. data/lib/active_merchant/billing/gateways/vpos.rb +220 -0
  60. data/lib/active_merchant/billing/gateways/worldpay.rb +78 -18
  61. data/lib/active_merchant/billing/response.rb +4 -0
  62. data/lib/active_merchant/billing/three_d_secure_eci_mapper.rb +27 -0
  63. data/lib/active_merchant/billing.rb +1 -0
  64. data/lib/active_merchant/version.rb +1 -1
  65. data/lib/certs/cacert.pem +1582 -2431
  66. metadata +11 -3
@@ -1,10 +1,13 @@
1
1
  module ActiveMerchant #:nodoc:
2
2
  module Billing #:nodoc:
3
3
  class GlobalCollectGateway < Gateway
4
+ class_attribute :preproduction_url
5
+
4
6
  self.display_name = 'GlobalCollect'
5
7
  self.homepage_url = 'http://www.globalcollect.com/'
6
8
 
7
9
  self.test_url = 'https://eu.sandbox.api-ingenico.com'
10
+ self.preproduction_url = 'https://world.preprod.api-ingenico.com'
8
11
  self.live_url = 'https://api.globalcollect.com'
9
12
 
10
13
  self.supported_countries = %w[AD AE AG AI AL AM AO AR AS AT AU AW AX AZ BA BB BD BE BF BG BH BI BJ BL BM BN BO BQ BR BS BT BW BY BZ CA CC CD CF CH CI CK CL CM CN CO CR CU CV CW CX CY CZ DE DJ DK DM DO DZ EC EE EG ER ES ET FI FJ FK FM FO FR GA GB GD GE GF GH GI GL GM GN GP GQ GR GS GT GU GW GY HK HN HR HT HU ID IE IL IM IN IS IT JM JO JP KE KG KH KI KM KN KR KW KY KZ LA LB LC LI LK LR LS LT LU LV MA MC MD ME MF MG MH MK MM MN MO MP MQ MR MS MT MU MV MW MX MY MZ NA NC NE NG NI NL NO NP NR NU NZ OM PA PE PF PG PH PL PN PS PT PW QA RE RO RS RU RW SA SB SC SE SG SH SI SJ SK SL SM SN SR ST SV SZ TC TD TG TH TJ TL TM TN TO TR TT TV TW TZ UA UG US UY UZ VC VE VG VI VN WF WS ZA ZM ZW]
@@ -33,7 +36,7 @@ module ActiveMerchant #:nodoc:
33
36
  add_creator_info(post, options)
34
37
  add_fraud_fields(post, options)
35
38
  add_external_cardholder_authentication_data(post, options)
36
- commit(:authorize, post)
39
+ commit(:authorize, post, options: options)
37
40
  end
38
41
 
39
42
  def capture(money, authorization, options = {})
@@ -41,7 +44,7 @@ module ActiveMerchant #:nodoc:
41
44
  add_order(post, money, options, capture: true)
42
45
  add_customer_data(post, options)
43
46
  add_creator_info(post, options)
44
- commit(:capture, post, authorization)
47
+ commit(:capture, post, authorization: authorization)
45
48
  end
46
49
 
47
50
  def refund(money, authorization, options = {})
@@ -49,13 +52,13 @@ module ActiveMerchant #:nodoc:
49
52
  add_amount(post, money, options)
50
53
  add_refund_customer_data(post, options)
51
54
  add_creator_info(post, options)
52
- commit(:refund, post, authorization)
55
+ commit(:refund, post, authorization: authorization)
53
56
  end
54
57
 
55
58
  def void(authorization, options = {})
56
59
  post = nestable_hash
57
60
  add_creator_info(post, options)
58
- commit(:void, post, authorization)
61
+ commit(:void, post, authorization: authorization)
59
62
  end
60
63
 
61
64
  def verify(payment, options = {})
@@ -260,6 +263,8 @@ module ActiveMerchant #:nodoc:
260
263
  end
261
264
 
262
265
  def url(action, authorization)
266
+ return preproduction_url + uri(action, authorization) if @options[:url_override].to_s == 'preproduction'
267
+
263
268
  (test? ? test_url : live_url) + uri(action, authorization)
264
269
  end
265
270
 
@@ -277,9 +282,13 @@ module ActiveMerchant #:nodoc:
277
282
  end
278
283
  end
279
284
 
280
- def commit(action, post, authorization = nil)
285
+ def idempotency_key_for_signature(options)
286
+ "x-gcs-idempotence-key:#{options[:idempotency_key]}" if options[:idempotency_key]
287
+ end
288
+
289
+ def commit(action, post, authorization: nil, options: {})
281
290
  begin
282
- raw_response = ssl_post(url(action, authorization), post.to_json, headers(action, post, authorization))
291
+ raw_response = ssl_post(url(action, authorization), post.to_json, headers(action, post, authorization, options))
283
292
  response = parse(raw_response)
284
293
  rescue ResponseError => e
285
294
  response = parse(e.response.body) if e.response.code.to_i >= 400
@@ -306,21 +315,26 @@ module ActiveMerchant #:nodoc:
306
315
  }
307
316
  end
308
317
 
309
- def headers(action, post, authorization = nil)
310
- {
318
+ def headers(action, post, authorization = nil, options = {})
319
+ headers = {
311
320
  'Content-Type' => content_type,
312
- 'Authorization' => auth_digest(action, post, authorization),
321
+ 'Authorization' => auth_digest(action, post, authorization, options),
313
322
  'Date' => date
314
323
  }
324
+
325
+ headers['X-GCS-Idempotence-Key'] = options[:idempotency_key] if options[:idempotency_key]
326
+ headers
315
327
  end
316
328
 
317
- def auth_digest(action, post, authorization = nil)
329
+ def auth_digest(action, post, authorization = nil, options = {})
318
330
  data = <<~REQUEST
319
331
  POST
320
332
  #{content_type}
321
333
  #{date}
334
+ #{idempotency_key_for_signature(options)}
322
335
  #{uri(action, authorization)}
323
336
  REQUEST
337
+ data = data.each_line.reject { |line| line.strip == '' }.join
324
338
  digest = OpenSSL::Digest.new('sha256')
325
339
  key = @options[:secret_api_key]
326
340
  "GCS v1HMAC:#{@options[:api_key_id]}:#{Base64.strict_encode64(OpenSSL::HMAC.digest(digest, key, data))}"
@@ -39,6 +39,7 @@ module ActiveMerchant #:nodoc:
39
39
  add_descriptor_name(xml, options)
40
40
  add_card_or_token_payment(xml, card_or_token, options)
41
41
  add_three_d_secure(xml, card_or_token, options)
42
+ add_stored_credentials(xml, options)
42
43
  end
43
44
  end
44
45
 
@@ -52,6 +53,8 @@ module ActiveMerchant #:nodoc:
52
53
  def purchase(money, payment_method, options = {})
53
54
  if payment_method.is_a?(Check)
54
55
  commit_check_sale(money, payment_method, options)
56
+ elsif options.dig(:stored_credential, :reason_type) == 'recurring'
57
+ commit_recurring_billing_sale(money, payment_method, options)
55
58
  else
56
59
  commit_credit_sale(money, payment_method, options)
57
60
  end
@@ -131,6 +134,21 @@ module ActiveMerchant #:nodoc:
131
134
  add_descriptor_name(xml, options)
132
135
  add_card_or_token_payment(xml, card_or_token, options)
133
136
  add_three_d_secure(xml, card_or_token, options)
137
+ add_stored_credentials(xml, options)
138
+ end
139
+ end
140
+
141
+ def commit_recurring_billing_sale(money, card_or_token, options)
142
+ commit('RecurringBilling') do |xml|
143
+ add_amount(xml, money)
144
+ add_allow_dup(xml)
145
+ add_card_or_token_customer_data(xml, card_or_token, options)
146
+ add_details(xml, options)
147
+ add_descriptor_name(xml, options)
148
+ add_card_or_token_payment(xml, card_or_token, options)
149
+ add_three_d_secure(xml, card_or_token, options)
150
+ add_stored_credentials(xml, options)
151
+ add_stored_credentials_for_recurring_billing(xml, options)
134
152
  end
135
153
  end
136
154
 
@@ -157,7 +175,7 @@ module ActiveMerchant #:nodoc:
157
175
  xml.hps :CardHolderAddr, billing_address[:address1] if billing_address[:address1]
158
176
  xml.hps :CardHolderCity, billing_address[:city] if billing_address[:city]
159
177
  xml.hps :CardHolderState, billing_address[:state] if billing_address[:state]
160
- xml.hps :CardHolderZip, billing_address[:zip] if billing_address[:zip]
178
+ xml.hps :CardHolderZip, alphanumeric_zip(billing_address[:zip]) if billing_address[:zip]
161
179
  end
162
180
  end
163
181
  end
@@ -265,6 +283,38 @@ module ActiveMerchant #:nodoc:
265
283
  end
266
284
  end
267
285
 
286
+ # We do not currently support installments on this gateway.
287
+ # The HPS gateway treats recurring transactions as a seperate transaction type
288
+ def add_stored_credentials(xml, options)
289
+ return unless options[:stored_credential]
290
+
291
+ xml.hps :CardOnFileData do
292
+ if options[:stored_credential][:initiator] == 'customer'
293
+ xml.hps :CardOnFile, 'C'
294
+ elsif options[:stored_credential][:initiator] == 'merchant'
295
+ xml.hps :CardOnFile, 'M'
296
+ else
297
+ return
298
+ end
299
+
300
+ if options[:stored_credential][:network_transaction_id]
301
+ xml.hps :CardBrandTxnId, options[:stored_credential][:network_transaction_id]
302
+ else
303
+ return
304
+ end
305
+ end
306
+ end
307
+
308
+ def add_stored_credentials_for_recurring_billing(xml, options)
309
+ xml.hps :RecurringData do
310
+ if options[:stored_credential][:reason_type] = 'recurring'
311
+ xml.hps :OneTime, 'N'
312
+ else
313
+ xml.hps :OneTime, 'Y'
314
+ end
315
+ end
316
+ end
317
+
268
318
  def strip_leading_zero(value)
269
319
  return value unless value[0] == '0'
270
320
 
@@ -389,6 +439,10 @@ module ActiveMerchant #:nodoc:
389
439
  @options[:secret_api_key]&.include?('_cert_')
390
440
  end
391
441
 
442
+ def alphanumeric_zip(zip)
443
+ zip.gsub(/[^0-9a-z]/i, '')
444
+ end
445
+
392
446
  ISSUER_MESSAGES = {
393
447
  '13' => 'Must be greater than or equal 0.',
394
448
  '14' => 'The card number is incorrect.',
@@ -37,6 +37,7 @@ module ActiveMerchant #:nodoc:
37
37
  post = {}
38
38
  post[:ticketNumber] = authorization
39
39
  add_invoice(action, post, amount, options)
40
+ add_full_response(post, options)
40
41
 
41
42
  commit(action, post)
42
43
  end
@@ -46,6 +47,7 @@ module ActiveMerchant #:nodoc:
46
47
 
47
48
  post = {}
48
49
  post[:ticketNumber] = authorization
50
+ add_full_response(post, options)
49
51
 
50
52
  commit(action, post)
51
53
  end
@@ -55,6 +57,7 @@ module ActiveMerchant #:nodoc:
55
57
 
56
58
  post = {}
57
59
  post[:ticketNumber] = authorization
60
+ add_full_response(post, options)
58
61
 
59
62
  commit(action, post)
60
63
  end
@@ -78,6 +81,7 @@ module ActiveMerchant #:nodoc:
78
81
  post = {}
79
82
  add_invoice(action, post, amount, options)
80
83
  add_payment_method(post, payment_method, options)
84
+ add_full_response(post, options)
81
85
 
82
86
  commit(action, post)
83
87
  end
@@ -88,6 +92,8 @@ module ActiveMerchant #:nodoc:
88
92
  post = {}
89
93
  add_reference(post, authorization, options)
90
94
  add_invoice(action, post, amount, options)
95
+ add_contact_details(post, options[:contact_details]) if options[:contact_details]
96
+ add_full_response(post, options)
91
97
 
92
98
  commit(action, post)
93
99
  end
@@ -98,6 +104,7 @@ module ActiveMerchant #:nodoc:
98
104
  post = {}
99
105
  add_reference(post, authorization, options)
100
106
  add_invoice(action, post, amount, options)
107
+ add_full_response(post, options)
101
108
 
102
109
  commit(action, post)
103
110
  end
@@ -154,6 +161,22 @@ module ActiveMerchant #:nodoc:
154
161
  post[:token] = authorization
155
162
  end
156
163
 
164
+ def add_contact_details(post, contact_details_options)
165
+ contact_details = {}
166
+ contact_details[:documentType] = contact_details_options[:document_type] if contact_details_options[:document_type]
167
+ contact_details[:documentNumber] = contact_details_options[:document_number] if contact_details_options[:document_number]
168
+ contact_details[:email] = contact_details_options[:email] if contact_details_options[:email]
169
+ contact_details[:firstName] = contact_details_options[:first_name] if contact_details_options[:first_name]
170
+ contact_details[:lastName] = contact_details_options[:last_name] if contact_details_options[:last_name]
171
+ contact_details[:secondLastName] = contact_details_options[:second_last_name] if contact_details_options[:second_last_name]
172
+ contact_details[:phoneNumber] = contact_details_options[:phone_number] if contact_details_options[:phone_number]
173
+ post[:contactDetails] = contact_details
174
+ end
175
+
176
+ def add_full_response(post, options)
177
+ post[:fullResponse] = options[:full_response].to_s.casecmp('true').zero? if options[:full_response]
178
+ end
179
+
157
180
  ENDPOINT = {
158
181
  'tokenize' => 'tokens',
159
182
  'charge' => 'charges',
@@ -494,7 +494,7 @@ module ActiveMerchant #:nodoc:
494
494
  attributes = {}
495
495
  attributes[:id] = truncate(options[:id] || options[:order_id], 24)
496
496
  attributes[:reportGroup] = options[:merchant] || 'Default Report Group'
497
- attributes[:customerId] = options[:customer]
497
+ attributes[:customerId] = options[:customer_id]
498
498
  attributes.delete_if { |_key, value| value == nil }
499
499
  attributes
500
500
  end
@@ -4,7 +4,7 @@ module ActiveMerchant #:nodoc:
4
4
  self.live_url = self.test_url = 'https://api.mercadopago.com/v1'
5
5
 
6
6
  self.supported_countries = %w[AR BR CL CO MX PE UY]
7
- self.supported_cardtypes = %i[visa master american_express elo cabal naranja]
7
+ self.supported_cardtypes = %i[visa master american_express elo cabal naranja creditel]
8
8
 
9
9
  self.homepage_url = 'https://www.mercadopago.com/'
10
10
  self.display_name = 'Mercado Pago'
@@ -105,7 +105,7 @@ module ActiveMerchant #:nodoc:
105
105
 
106
106
  def authorize_request(money, payment, options = {})
107
107
  post = purchase_request(money, payment, options)
108
- post[:capture] = false
108
+ post[:capture] = options[:capture] || false
109
109
  post
110
110
  end
111
111
 
@@ -129,6 +129,7 @@ module ActiveMerchant #:nodoc:
129
129
 
130
130
  def add_additional_data(post, options)
131
131
  post[:sponsor_id] = options[:sponsor_id]
132
+ post[:metadata] = options[:metadata] if options[:metadata]
132
133
  post[:device_id] = options[:device_id] if options[:device_id]
133
134
  post[:additional_info] = {
134
135
  ip_address: options[:ip_address]
@@ -143,7 +144,7 @@ module ActiveMerchant #:nodoc:
143
144
  email: options[:email],
144
145
  first_name: payment.first_name,
145
146
  last_name: payment.last_name
146
- }
147
+ }.merge(options[:payer] || {})
147
148
  end
148
149
 
149
150
  def add_address(post, options)
@@ -191,7 +192,7 @@ module ActiveMerchant #:nodoc:
191
192
  post[:description] = options[:description]
192
193
  post[:installments] = options[:installments] ? options[:installments].to_i : 1
193
194
  post[:statement_descriptor] = options[:statement_descriptor] if options[:statement_descriptor]
194
- post[:external_reference] = options[:order_id] || SecureRandom.hex(16)
195
+ post[:external_reference] = options[:order_id] || options[:external_reference] || SecureRandom.hex(16)
195
196
  end
196
197
 
197
198
  def add_payment(post, options)
@@ -187,6 +187,8 @@ module ActiveMerchant #:nodoc:
187
187
  def parse(body)
188
188
  xml = REXML::Document.new(body)
189
189
 
190
+ return { response_message: 'Invalid gateway response' } unless xml.root.present?
191
+
190
192
  response = {}
191
193
  xml.root.elements.to_a.each do |node|
192
194
  parse_element(response, node)
@@ -0,0 +1,260 @@
1
+ require 'json'
2
+ require 'openssl'
3
+ require 'digest'
4
+ require 'base64'
5
+
6
+ module ActiveMerchant #:nodoc:
7
+ module Billing #:nodoc:
8
+ class MitGateway < Gateway
9
+ self.live_url = 'https://wpy.mitec.com.mx/ModuloUtilWS/activeCDP.htm'
10
+
11
+ self.supported_countries = ['MX']
12
+ self.default_currency = 'MXN'
13
+ self.supported_cardtypes = %i[visa master]
14
+
15
+ self.homepage_url = 'http://www.centrodepagos.com.mx/'
16
+ self.display_name = 'MIT Centro de pagos'
17
+
18
+ self.money_format = :dollars
19
+
20
+ def initialize(options = {})
21
+ requires!(options, :commerce_id, :user, :api_key, :key_session)
22
+ super
23
+ end
24
+
25
+ def purchase(money, payment, options = {})
26
+ MultiResponse.run do |r|
27
+ r.process { authorize(money, payment, options) }
28
+ r.process { capture(money, r.authorization, options) }
29
+ end
30
+ end
31
+
32
+ def cipher_key
33
+ @options[:key_session]
34
+ end
35
+
36
+ def decrypt(val, keyinhex)
37
+ # Splits the first 16 bytes (the IV bytes) in array format
38
+ unpacked = val.unpack('m')
39
+ iv_base64 = unpacked[0].bytes.slice(0, 16)
40
+ # Splits the second bytes (the encrypted text bytes) these would be the
41
+ # original message
42
+ full_data = unpacked[0].bytes.slice(16, unpacked[0].bytes.length)
43
+ # Creates the engine
44
+ engine = OpenSSL::Cipher::AES128.new(:CBC)
45
+ # Set engine as decrypt mode
46
+ engine.decrypt
47
+ # Converts the key from hex to bytes
48
+ engine.key = [keyinhex].pack('H*')
49
+ # Converts the ivBase64 array into bytes
50
+ engine.iv = iv_base64.pack('c*')
51
+ # Decrypts the texts and returns the original string
52
+ engine.update(full_data.pack('c*')) + engine.final
53
+ end
54
+
55
+ def encrypt(val, keyinhex)
56
+ # Creates the engine motor
57
+ engine = OpenSSL::Cipher::AES128.new(:CBC)
58
+ # Set engine as encrypt mode
59
+ engine.encrypt
60
+ # Converts the key from hex to bytes
61
+ engine.key = [keyinhex].pack('H*')
62
+ # Generates a random iv with this settings
63
+ iv_rand = engine.random_iv
64
+ # Packs IV as a Base64 string
65
+ iv_base64 = [iv_rand].pack('m')
66
+ # Converts the packed key into bytes
67
+ unpacked = iv_base64.unpack('m')
68
+ iv = unpacked[0]
69
+ # Sets the IV into the engine
70
+ engine.iv = iv
71
+ # Encrypts the texts and stores the bytes
72
+ encrypted_bytes = engine.update(val) + engine.final
73
+ # Concatenates the (a) IV bytes and (b) the encrypted bytes then returns a
74
+ # base64 representation
75
+ [iv << encrypted_bytes].pack('m')
76
+ end
77
+
78
+ def authorize(money, payment, options = {})
79
+ post = {
80
+ operation: 'Authorize',
81
+ commerce_id: @options[:commerce_id],
82
+ user: @options[:user],
83
+ apikey: @options[:api_key],
84
+ testMode: (test? ? 'YES' : 'NO')
85
+ }
86
+ add_invoice(post, money, options)
87
+ # Payments contains the card information
88
+ add_payment(post, payment)
89
+ add_customer_data(post, options)
90
+ post[:key_session] = @options[:key_session]
91
+
92
+ post_to_json = post.to_json
93
+ post_to_json_encrypt = encrypt(post_to_json, @options[:key_session])
94
+
95
+ final_post = '<authorization>' + post_to_json_encrypt + '</authorization><dataID>' + @options[:user] + '</dataID>'
96
+ json_post = {}
97
+ json_post[:payload] = final_post
98
+ commit('sale', json_post)
99
+ end
100
+
101
+ def capture(money, authorization, options = {})
102
+ post = {
103
+ operation: 'Capture',
104
+ commerce_id: @options[:commerce_id],
105
+ user: @options[:user],
106
+ apikey: @options[:api_key],
107
+ testMode: (test? ? 'YES' : 'NO'),
108
+ transaction_id: authorization,
109
+ amount: amount(money)
110
+ }
111
+ post[:key_session] = @options[:key_session]
112
+
113
+ post_to_json = post.to_json
114
+ post_to_json_encrypt = encrypt(post_to_json, @options[:key_session])
115
+
116
+ final_post = '<capture>' + post_to_json_encrypt + '</capture><dataID>' + @options[:user] + '</dataID>'
117
+ json_post = {}
118
+ json_post[:payload] = final_post
119
+ commit('capture', json_post)
120
+ end
121
+
122
+ def refund(money, authorization, options = {})
123
+ post = {
124
+ operation: 'Refund',
125
+ commerce_id: @options[:commerce_id],
126
+ user: @options[:user],
127
+ apikey: @options[:api_key],
128
+ testMode: (test? ? 'YES' : 'NO'),
129
+ transaction_id: authorization,
130
+ auth: authorization,
131
+ amount: amount(money)
132
+ }
133
+ post[:key_session] = @options[:key_session]
134
+
135
+ post_to_json = post.to_json
136
+ post_to_json_encrypt = encrypt(post_to_json, @options[:key_session])
137
+
138
+ final_post = '<refund>' + post_to_json_encrypt + '</refund><dataID>' + @options[:user] + '</dataID>'
139
+ json_post = {}
140
+ json_post[:payload] = final_post
141
+ commit('refund', json_post)
142
+ end
143
+
144
+ def supports_scrubbing?
145
+ true
146
+ end
147
+
148
+ def scrub(transcript)
149
+ ret_transcript = transcript
150
+ auth_origin = ret_transcript[/<authorization>(.*?)<\/authorization>/, 1]
151
+ unless auth_origin.nil?
152
+ auth_decrypted = decrypt(auth_origin, @options[:key_session])
153
+ auth_json = JSON.parse(auth_decrypted)
154
+ auth_json['card'] = '[FILTERED]'
155
+ auth_json['cvv'] = '[FILTERED]'
156
+ auth_json['apikey'] = '[FILTERED]'
157
+ auth_json['key_session'] = '[FILTERED]'
158
+ auth_to_json = auth_json.to_json
159
+ auth_tagged = '<authorization>' + auth_to_json + '</authorization>'
160
+ ret_transcript = ret_transcript.gsub(/<authorization>(.*?)<\/authorization>/, auth_tagged)
161
+ end
162
+
163
+ cap_origin = ret_transcript[/<capture>(.*?)<\/capture>/, 1]
164
+ unless cap_origin.nil?
165
+ cap_decrypted = decrypt(cap_origin, @options[:key_session])
166
+ cap_json = JSON.parse(cap_decrypted)
167
+ cap_json['apikey'] = '[FILTERED]'
168
+ cap_json['key_session'] = '[FILTERED]'
169
+ cap_to_json = cap_json.to_json
170
+ cap_tagged = '<capture>' + cap_to_json + '</capture>'
171
+ ret_transcript = ret_transcript.gsub(/<capture>(.*?)<\/capture>/, cap_tagged)
172
+ end
173
+
174
+ ref_origin = ret_transcript[/<refund>(.*?)<\/refund>/, 1]
175
+ unless ref_origin.nil?
176
+ ref_decrypted = decrypt(ref_origin, @options[:key_session])
177
+ ref_json = JSON.parse(ref_decrypted)
178
+ ref_json['apikey'] = '[FILTERED]'
179
+ ref_json['key_session'] = '[FILTERED]'
180
+ ref_to_json = ref_json.to_json
181
+ ref_tagged = '<refund>' + ref_to_json + '</refund>'
182
+ ret_transcript = ret_transcript.gsub(/<refund>(.*?)<\/refund>/, ref_tagged)
183
+ end
184
+
185
+ res_origin = ret_transcript[/#{Regexp.escape('reading ')}(.*?)#{Regexp.escape('read')}/m, 1]
186
+ loop do
187
+ break if res_origin.nil?
188
+
189
+ resp_origin = res_origin[/#{Regexp.escape('"')}(.*?)#{Regexp.escape('"')}/m, 1]
190
+ resp_decrypted = decrypt(resp_origin, @options[:key_session])
191
+ ret_transcript[/#{Regexp.escape('reading ')}(.*?)#{Regexp.escape('read')}/m, 1] = resp_decrypted
192
+ ret_transcript = ret_transcript.sub('reading ', 'response: ')
193
+ res_origin = ret_transcript[/#{Regexp.escape('reading ')}(.*?)#{Regexp.escape('read')}/m, 1]
194
+ end
195
+
196
+ ret_transcript
197
+ end
198
+
199
+ private
200
+
201
+ def add_customer_data(post, options)
202
+ post[:email] = options[:email] || 'nadie@mit.test'
203
+ end
204
+
205
+ def add_invoice(post, money, options)
206
+ post[:amount] = amount(money)
207
+ post[:currency] = (options[:currency] || currency(money))
208
+ post[:reference] = options[:order_id]
209
+ post[:transaction_id] = options[:order_id]
210
+ end
211
+
212
+ def add_payment(post, payment)
213
+ post[:installments] = 1
214
+ post[:card] = payment.number
215
+ post[:expmonth] = payment.month
216
+ post[:expyear] = payment.year
217
+ post[:cvv] = payment.verification_value
218
+ post[:name_client] = [payment.first_name, payment.last_name].join(' ')
219
+ end
220
+
221
+ def commit(action, parameters)
222
+ json_str = JSON.generate(parameters)
223
+ cleaned_str = json_str.gsub('\n', '')
224
+ raw_response = ssl_post(live_url, cleaned_str, { 'Content-type' => 'application/json' })
225
+ response = JSON.parse(decrypt(raw_response, @options[:key_session]))
226
+
227
+ Response.new(
228
+ success_from(response),
229
+ message_from(response),
230
+ response,
231
+ authorization: authorization_from(response),
232
+ avs_result: AVSResult.new(code: response['some_avs_response_key']),
233
+ cvv_result: CVVResult.new(response['some_cvv_response_key']),
234
+ test: test?,
235
+ error_code: error_code_from(response)
236
+ )
237
+ end
238
+
239
+ def success_from(response)
240
+ response['response'] == 'approved'
241
+ end
242
+
243
+ def message_from(response)
244
+ response['response']
245
+ end
246
+
247
+ def authorization_from(response)
248
+ response['reference']
249
+ end
250
+
251
+ def post_data(action, parameters = {})
252
+ parameters.collect { |key, value| "#{key}=#{CGI.escape(value.to_s)}" }.join('&')
253
+ end
254
+
255
+ def error_code_from(response)
256
+ response['message'].split(' -- ', 2)[0] unless success_from(response)
257
+ end
258
+ end
259
+ end
260
+ end