activemerchant 1.123.0 → 1.125.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 (48) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +131 -0
  3. data/lib/active_merchant/billing/check.rb +5 -8
  4. data/lib/active_merchant/billing/credit_card.rb +10 -0
  5. data/lib/active_merchant/billing/credit_card_methods.rb +6 -3
  6. data/lib/active_merchant/billing/gateway.rb +1 -1
  7. data/lib/active_merchant/billing/gateways/adyen.rb +61 -9
  8. data/lib/active_merchant/billing/gateways/card_stream.rb +17 -13
  9. data/lib/active_merchant/billing/gateways/cashnet.rb +15 -5
  10. data/lib/active_merchant/billing/gateways/checkout_v2.rb +33 -4
  11. data/lib/active_merchant/billing/gateways/cyber_source.rb +11 -3
  12. data/lib/active_merchant/billing/gateways/d_local.rb +12 -6
  13. data/lib/active_merchant/billing/gateways/decidir_plus.rb +173 -0
  14. data/lib/active_merchant/billing/gateways/ebanx.rb +16 -1
  15. data/lib/active_merchant/billing/gateways/elavon.rb +6 -3
  16. data/lib/active_merchant/billing/gateways/element.rb +20 -2
  17. data/lib/active_merchant/billing/gateways/global_collect.rb +111 -16
  18. data/lib/active_merchant/billing/gateways/ipg.rb +416 -0
  19. data/lib/active_merchant/billing/gateways/kushki.rb +7 -0
  20. data/lib/active_merchant/billing/gateways/mercado_pago.rb +3 -1
  21. data/lib/active_merchant/billing/gateways/mit.rb +260 -0
  22. data/lib/active_merchant/billing/gateways/moka.rb +24 -11
  23. data/lib/active_merchant/billing/gateways/mundipagg.rb +8 -6
  24. data/lib/active_merchant/billing/gateways/nmi.rb +15 -1
  25. data/lib/active_merchant/billing/gateways/orbital.rb +19 -3
  26. data/lib/active_merchant/billing/gateways/pay_arc.rb +9 -7
  27. data/lib/active_merchant/billing/gateways/pay_conex.rb +3 -1
  28. data/lib/active_merchant/billing/gateways/pay_trace.rb +1 -1
  29. data/lib/active_merchant/billing/gateways/payflow.rb +14 -6
  30. data/lib/active_merchant/billing/gateways/paymentez.rb +9 -2
  31. data/lib/active_merchant/billing/gateways/paysafe.rb +144 -23
  32. data/lib/active_merchant/billing/gateways/payu_latam.rb +6 -1
  33. data/lib/active_merchant/billing/gateways/payway_dot_com.rb +3 -3
  34. data/lib/active_merchant/billing/gateways/pin.rb +31 -4
  35. data/lib/active_merchant/billing/gateways/priority.rb +347 -0
  36. data/lib/active_merchant/billing/gateways/realex.rb +18 -0
  37. data/lib/active_merchant/billing/gateways/safe_charge.rb +6 -2
  38. data/lib/active_merchant/billing/gateways/stripe.rb +27 -7
  39. data/lib/active_merchant/billing/gateways/stripe_payment_intents.rb +96 -38
  40. data/lib/active_merchant/billing/gateways/trans_first_transaction_express.rb +2 -1
  41. data/lib/active_merchant/billing/gateways/trust_commerce.rb +2 -1
  42. data/lib/active_merchant/billing/gateways/usa_epay_transaction.rb +20 -6
  43. data/lib/active_merchant/billing/gateways/wompi.rb +193 -0
  44. data/lib/active_merchant/billing/gateways/worldpay.rb +196 -64
  45. data/lib/active_merchant/billing/network_tokenization_credit_card.rb +1 -1
  46. data/lib/active_merchant/billing/response.rb +4 -0
  47. data/lib/active_merchant/version.rb +1 -1
  48. metadata +7 -2
@@ -5,34 +5,42 @@ module ActiveMerchant #:nodoc:
5
5
  # This gateway uses the current Stripe {Payment Intents API}[https://stripe.com/docs/api/payment_intents].
6
6
  # For the legacy API, see the Stripe gateway
7
7
  class StripePaymentIntentsGateway < StripeGateway
8
- self.supported_countries = %w(AT AU BE BG BR CA CH CY CZ DE DK EE ES FI FR GB GR HK IE IT JP LT LU LV MT MX NL NO NZ PL PT RO SE SG SI SK US)
9
-
10
8
  ALLOWED_METHOD_STATES = %w[automatic manual].freeze
11
9
  ALLOWED_CANCELLATION_REASONS = %w[duplicate fraudulent requested_by_customer abandoned].freeze
12
10
  CREATE_INTENT_ATTRIBUTES = %i[description statement_descriptor_suffix statement_descriptor receipt_email save_payment_method]
13
11
  CONFIRM_INTENT_ATTRIBUTES = %i[receipt_email return_url save_payment_method setup_future_usage off_session]
14
12
  UPDATE_INTENT_ATTRIBUTES = %i[description statement_descriptor_suffix statement_descriptor receipt_email setup_future_usage]
15
- DEFAULT_API_VERSION = '2019-05-16'
13
+ DEFAULT_API_VERSION = '2020-08-27'
14
+ NO_WALLET_SUPPORT = %w(apple_pay google_pay android_pay)
16
15
 
17
16
  def create_intent(money, payment_method, options = {})
17
+ card_source_pay = payment_method.source.to_s if defined?(payment_method.source)
18
+ card_brand_pay = card_brand(payment_method) unless payment_method.is_a?(String) || payment_method.nil?
19
+ if NO_WALLET_SUPPORT.include?(card_source_pay) || NO_WALLET_SUPPORT.include?(card_brand_pay)
20
+ store_apple_or_google_pay_token = 'Direct Apple Pay and Google Pay transactions are not supported. Those payment methods must be stored before use.'
21
+ return Response.new(false, store_apple_or_google_pay_token)
22
+ end
18
23
  post = {}
19
24
  add_amount(post, money, options, true)
20
25
  add_capture_method(post, options)
21
26
  add_confirmation_method(post, options)
22
27
  add_customer(post, options)
23
- payment_method = add_payment_method_token(post, payment_method, options)
24
- return payment_method if payment_method.is_a?(ActiveMerchant::Billing::Response)
28
+ result = add_payment_method_token(post, payment_method, options)
29
+ return result if result.is_a?(ActiveMerchant::Billing::Response)
25
30
 
26
31
  add_external_three_d_secure_auth_data(post, options)
27
32
  add_metadata(post, options)
28
33
  add_return_url(post, options)
29
34
  add_connected_account(post, options)
35
+ add_radar_data(post, options)
30
36
  add_shipping_address(post, options)
31
37
  setup_future_usage(post, options)
32
38
  add_exemption(post, options)
33
39
  add_stored_credentials(post, options)
34
40
  add_ntid(post, options)
41
+ add_claim_without_transaction_id(post, options)
35
42
  add_error_on_requires_action(post, options)
43
+ add_fulfillment_date(post, options)
36
44
  request_three_d_secure(post, options)
37
45
 
38
46
  CREATE_INTENT_ATTRIBUTES.each do |attribute|
@@ -48,23 +56,24 @@ module ActiveMerchant #:nodoc:
48
56
 
49
57
  def confirm_intent(intent_id, payment_method, options = {})
50
58
  post = {}
51
- payment_method = add_payment_method_token(post, payment_method, options)
52
- return payment_method if payment_method.is_a?(ActiveMerchant::Billing::Response)
59
+ result = add_payment_method_token(post, payment_method, options)
60
+ return result if result.is_a?(ActiveMerchant::Billing::Response)
53
61
 
54
62
  CONFIRM_INTENT_ATTRIBUTES.each do |attribute|
55
63
  add_whitelisted_attribute(post, options, attribute)
56
64
  end
65
+
57
66
  commit(:post, "payment_intents/#{intent_id}/confirm", post, options)
58
67
  end
59
68
 
60
69
  def create_payment_method(payment_method, options = {})
61
- post_data = create_payment_method_data(payment_method, options)
70
+ post_data = add_payment_method_data(payment_method, options)
62
71
 
63
72
  options = format_idempotency_key(options, 'pm')
64
73
  commit(:post, 'payment_methods', post_data, options)
65
74
  end
66
75
 
67
- def create_payment_method_data(payment_method, options = {})
76
+ def add_payment_method_data(payment_method, options = {})
68
77
  post_data = {}
69
78
  post_data[:type] = 'card'
70
79
  post_data[:card] = {}
@@ -73,6 +82,7 @@ module ActiveMerchant #:nodoc:
73
82
  post_data[:card][:exp_year] = payment_method.year
74
83
  post_data[:card][:cvc] = payment_method.verification_value if payment_method.verification_value
75
84
  add_billing_address(post_data, options)
85
+ add_name_only(post_data, payment_method) if post_data[:billing_details].nil?
76
86
  post_data
77
87
  end
78
88
 
@@ -80,14 +90,15 @@ module ActiveMerchant #:nodoc:
80
90
  post = {}
81
91
  add_amount(post, money, options)
82
92
 
83
- payment_method = add_payment_method_token(post, payment_method, options)
84
- return payment_method if payment_method.is_a?(ActiveMerchant::Billing::Response)
93
+ result = add_payment_method_token(post, payment_method, options)
94
+ return result if result.is_a?(ActiveMerchant::Billing::Response)
85
95
 
86
96
  add_payment_method_types(post, options)
87
97
  add_customer(post, options)
88
98
  add_metadata(post, options)
89
99
  add_shipping_address(post, options)
90
100
  add_connected_account(post, options)
101
+ add_fulfillment_date(post, options)
91
102
 
92
103
  UPDATE_INTENT_ATTRIBUTES.each do |attribute|
93
104
  add_whitelisted_attribute(post, options, attribute)
@@ -98,11 +109,12 @@ module ActiveMerchant #:nodoc:
98
109
  def create_setup_intent(payment_method, options = {})
99
110
  post = {}
100
111
  add_customer(post, options)
101
- payment_method = add_payment_method_token(post, payment_method, options)
102
- return payment_method if payment_method.is_a?(ActiveMerchant::Billing::Response)
112
+ result = add_payment_method_token(post, payment_method, options)
113
+ return result if result.is_a?(ActiveMerchant::Billing::Response)
103
114
 
104
115
  add_metadata(post, options)
105
116
  add_return_url(post, options)
117
+ add_fulfillment_date(post, options)
106
118
  post[:on_behalf_of] = options[:on_behalf_of] if options[:on_behalf_of]
107
119
  post[:usage] = options[:usage] if %w(on_session off_session).include?(options[:usage])
108
120
  post[:description] = options[:description] if options[:description]
@@ -178,8 +190,8 @@ module ActiveMerchant #:nodoc:
178
190
  # If customer option is provided, create a payment method and attach to customer id
179
191
  # Otherwise, create a customer, then attach
180
192
  if payment_method.is_a?(StripePaymentToken) || payment_method.is_a?(ActiveMerchant::Billing::CreditCard)
181
- payment_method = add_payment_method_token(params, payment_method, options)
182
- return payment_method if payment_method.is_a?(ActiveMerchant::Billing::Response)
193
+ result = add_payment_method_token(params, payment_method, options)
194
+ return result if result.is_a?(ActiveMerchant::Billing::Response)
183
195
 
184
196
  if options[:customer]
185
197
  customer_id = options[:customer]
@@ -187,6 +199,7 @@ module ActiveMerchant #:nodoc:
187
199
  post[:description] = options[:description] if options[:description]
188
200
  post[:email] = options[:email] if options[:email]
189
201
  options = format_idempotency_key(options, 'customer')
202
+ post[:expand] = [:sources]
190
203
  customer = commit(:post, 'customers', post, options)
191
204
  customer_id = customer.params['id']
192
205
  end
@@ -212,6 +225,16 @@ module ActiveMerchant #:nodoc:
212
225
  create_setup_intent(payment_method, options.merge!(confirm: true))
213
226
  end
214
227
 
228
+ def setup_purchase(money, options = {})
229
+ requires!(options, :payment_method_types)
230
+ post = {}
231
+ add_currency(post, options, money)
232
+ add_amount(post, money, options)
233
+ add_payment_method_types(post, options)
234
+ add_metadata(post, options)
235
+ commit(:post, 'payment_intents', post, options)
236
+ end
237
+
215
238
  private
216
239
 
217
240
  def off_session_request?(options = {})
@@ -242,6 +265,16 @@ module ActiveMerchant #:nodoc:
242
265
  post[:customer] = customer if customer.start_with?('cus_')
243
266
  end
244
267
 
268
+ def add_fulfillment_date(post, options)
269
+ post[:fulfillment_date] = options[:fulfillment_date].to_i if options[:fulfillment_date]
270
+ end
271
+
272
+ def add_metadata(post, options = {})
273
+ super
274
+
275
+ post[:metadata][:event_type] = options[:event_type] if options[:event_type]
276
+ end
277
+
245
278
  def add_return_url(post, options)
246
279
  return unless options[:confirm]
247
280
 
@@ -250,35 +283,36 @@ module ActiveMerchant #:nodoc:
250
283
  end
251
284
 
252
285
  def add_payment_method_token(post, payment_method, options)
253
- return if payment_method.nil?
254
-
255
- if payment_method.is_a?(ActiveMerchant::Billing::CreditCard)
256
- if off_session_request?(options)
257
- post[:payment_method_data] = create_payment_method_data(payment_method, options)
258
- return
259
- else
260
- p = create_payment_method(payment_method, options)
261
- return p unless p.success?
262
-
263
- payment_method = p.params['id']
264
- end
265
- end
266
-
267
286
  case payment_method
268
287
  when StripePaymentToken
269
288
  post[:payment_method] = payment_method.payment_data['id']
270
289
  when String
271
- if payment_method.include?('|')
272
- customer_id, payment_method_id = payment_method.split('|')
273
- token = payment_method_id
274
- post[:customer] = customer_id
275
- else
276
- token = payment_method
277
- end
278
- post[:payment_method] = token
290
+ extract_token_from_string_and_maybe_add_customer_id(post, payment_method)
291
+ when ActiveMerchant::Billing::CreditCard
292
+ get_payment_method_data_from_card(post, payment_method, options)
279
293
  end
294
+ end
280
295
 
281
- post
296
+ def extract_token_from_string_and_maybe_add_customer_id(post, payment_method)
297
+ if payment_method.include?('|')
298
+ customer_id, payment_method = payment_method.split('|')
299
+ post[:customer] = customer_id
300
+ end
301
+
302
+ post[:payment_method] = payment_method
303
+ end
304
+
305
+ def get_payment_method_data_from_card(post, payment_method, options)
306
+ return create_payment_method_and_extract_token(post, payment_method, options) unless off_session_request?(options)
307
+
308
+ post[:payment_method_data] = add_payment_method_data(payment_method, options)
309
+ end
310
+
311
+ def create_payment_method_and_extract_token(post, payment_method, options)
312
+ payment_method_response = create_payment_method(payment_method, options)
313
+ return payment_method_response if payment_method_response.failure?
314
+
315
+ add_payment_method_token(post, payment_method_response.params['id'], options)
282
316
  end
283
317
 
284
318
  def add_payment_method_types(post, options)
@@ -326,6 +360,19 @@ module ActiveMerchant #:nodoc:
326
360
  post[:payment_method_options][:card][:mit_exemption][:network_transaction_id] = options[:network_transaction_id] if options[:network_transaction_id]
327
361
  end
328
362
 
363
+ def add_claim_without_transaction_id(post, options = {})
364
+ return if options[:stored_credential] || options[:network_transaction_id] || options[:ds_transaction_id]
365
+ return unless options[:claim_without_transaction_id]
366
+
367
+ post[:payment_method_options] ||= {}
368
+ post[:payment_method_options][:card] ||= {}
369
+ post[:payment_method_options][:card][:mit_exemption] = {}
370
+
371
+ # Stripe PI accepts claim_without_transaction_id for transactions without transaction ids.
372
+ # Gateway validation for this field occurs through a different service, before the transaction request is sent to the gateway.
373
+ post[:payment_method_options][:card][:mit_exemption][:claim_without_transaction_id] = options[:claim_without_transaction_id]
374
+ end
375
+
329
376
  def add_error_on_requires_action(post, options = {})
330
377
  return unless options[:confirm]
331
378
 
@@ -375,6 +422,13 @@ module ActiveMerchant #:nodoc:
375
422
  post[:billing_details][:phone] = billing[:phone] if billing[:phone]
376
423
  end
377
424
 
425
+ def add_name_only(post, payment_method)
426
+ post[:billing_details] = {} unless post[:billing_details]
427
+
428
+ name = [payment_method.first_name, payment_method.last_name].compact.join(' ')
429
+ post[:billing_details][:name] = name
430
+ end
431
+
378
432
  def add_shipping_address(post, options = {})
379
433
  return unless shipping = options[:shipping]
380
434
 
@@ -408,6 +462,10 @@ module ActiveMerchant #:nodoc:
408
462
 
409
463
  super(response, options)
410
464
  end
465
+
466
+ def add_currency(post, options, money)
467
+ post[:currency] = options[:currency] || currency(money)
468
+ end
411
469
  end
412
470
  end
413
471
  end
@@ -317,7 +317,8 @@ module ActiveMerchant #:nodoc:
317
317
  gsub(%r((<[^>]+pan>)[^<]+(<))i, '\1[FILTERED]\2').
318
318
  gsub(%r((<[^>]+sec>)[^<]+(<))i, '\1[FILTERED]\2').
319
319
  gsub(%r((<[^>]+id>)[^<]+(<))i, '\1[FILTERED]\2').
320
- gsub(%r((<[^>]+regKey>)[^<]+(<))i, '\1[FILTERED]\2')
320
+ gsub(%r((<[^>]+regKey>)[^<]+(<))i, '\1[FILTERED]\2').
321
+ gsub(%r((<[^>]+acctNr>)[^<]+(<))i, '\1[FILTERED]\2')
321
322
  end
322
323
 
323
324
  private
@@ -331,7 +331,8 @@ module ActiveMerchant #:nodoc:
331
331
  gsub(%r((Authorization: Basic )\w+), '\1[FILTERED]').
332
332
  gsub(%r((&?cc=)\d*(&?)), '\1[FILTERED]\2').
333
333
  gsub(%r((&?password=)[^&]+(&?)), '\1[FILTERED]\2').
334
- gsub(%r((&?cvv=)\d*(&?)), '\1[FILTERED]\2')
334
+ gsub(%r((&?cvv=)\d*(&?)), '\1[FILTERED]\2').
335
+ gsub(%r((&?account=)\d*(&?)), '\1[FILTERED]\2')
335
336
  end
336
337
 
337
338
  private
@@ -16,7 +16,8 @@ module ActiveMerchant #:nodoc:
16
16
  refund: 'cc:refund',
17
17
  void: 'cc:void',
18
18
  void_release: 'cc:void:release',
19
- check_purchase: 'check:sale'
19
+ check_purchase: 'check:sale',
20
+ store: 'cc:save'
20
21
  }
21
22
 
22
23
  STANDARD_ERROR_CODE_MAPPING = {
@@ -43,14 +44,14 @@ module ActiveMerchant #:nodoc:
43
44
  super
44
45
  end
45
46
 
46
- def authorize(money, credit_card, options = {})
47
+ def authorize(money, payment, options = {})
47
48
  post = {}
48
49
 
49
50
  add_amount(post, money)
50
51
  add_invoice(post, options)
51
- add_payment(post, credit_card)
52
- unless credit_card.track_data.present?
53
- add_address(post, credit_card, options)
52
+ add_payment(post, payment)
53
+ unless payment.is_a?(CreditCard) && payment.track_data.present?
54
+ add_address(post, payment, options)
54
55
  add_customer_data(post, options)
55
56
  end
56
57
  add_split_payments(post, options)
@@ -97,6 +98,12 @@ module ActiveMerchant #:nodoc:
97
98
  commit(:refund, post)
98
99
  end
99
100
 
101
+ def store(payment, options = {})
102
+ post = {}
103
+ add_payment(post, payment, options)
104
+ commit(:store, post)
105
+ end
106
+
100
107
  def verify(creditcard, options = {})
101
108
  MultiResponse.run(:use_first_response) do |r|
102
109
  r.process { authorize(1, creditcard, options) }
@@ -213,6 +220,8 @@ module ActiveMerchant #:nodoc:
213
220
  elsif payment.respond_to?(:track_data) && payment.track_data.present?
214
221
  post[:magstripe] = payment.track_data
215
222
  post[:cardpresent] = true
223
+ elsif payment.is_a?(String)
224
+ post[:card] = payment
216
225
  else
217
226
  post[:card] = payment.number
218
227
  post[:cvv2] = payment.verification_value if payment.verification_value?
@@ -299,6 +308,7 @@ module ActiveMerchant #:nodoc:
299
308
  status: fields['UMstatus'],
300
309
  auth_code: fields['UMauthCode'],
301
310
  ref_num: fields['UMrefNum'],
311
+ card_ref: fields['UMcardRef'],
302
312
  batch: fields['UMbatch'],
303
313
  avs_result: fields['UMavsResult'],
304
314
  avs_result_code: fields['UMavsResultCode'],
@@ -321,7 +331,7 @@ module ActiveMerchant #:nodoc:
321
331
  error_code = (STANDARD_ERROR_CODE_MAPPING[response[:error_code]] || STANDARD_ERROR_CODE[:processing_error]) unless approved
322
332
  Response.new(approved, message_from(response), response,
323
333
  test: test?,
324
- authorization: response[:ref_num],
334
+ authorization: authorization_from(action, response),
325
335
  cvv_result: response[:cvv2_result_code],
326
336
  avs_result: { code: response[:avs_result_code] },
327
337
  error_code: error_code)
@@ -337,6 +347,10 @@ module ActiveMerchant #:nodoc:
337
347
  end
338
348
  end
339
349
 
350
+ def authorization_from(action, response)
351
+ return (action == :store ? response[:card_ref] : response[:ref_num])
352
+ end
353
+
340
354
  def post_data(action, parameters = {})
341
355
  parameters[:command] = TRANSACTIONS[action]
342
356
  parameters[:key] = @options[:login]
@@ -0,0 +1,193 @@
1
+ module ActiveMerchant #:nodoc:
2
+ module Billing #:nodoc:
3
+ class WompiGateway < Gateway
4
+ self.test_url = 'https://sync.sandbox.wompi.co/v1'
5
+ self.live_url = 'https://sync.production.wompi.co/v1'
6
+
7
+ self.supported_countries = ['CO']
8
+ self.default_currency = 'COP'
9
+ self.supported_cardtypes = %i[visa master american_express]
10
+
11
+ self.homepage_url = 'https://wompi.co/'
12
+ self.display_name = 'Wompi'
13
+
14
+ self.money_format = :cents
15
+
16
+ def initialize(options = {})
17
+ ## Sandbox keys have prefix pub_test_ and prv_test_
18
+ ## Production keys have prefix pub_prod_ and prv_prod_
19
+ begin
20
+ requires!(options, :prod_private_key, :prod_public_key)
21
+ rescue ArgumentError
22
+ begin
23
+ requires!(options, :test_private_key, :test_public_key)
24
+ rescue ArgumentError
25
+ raise ArgumentError, 'Gateway requires both test_private_key and test_public_key, or both prod_private_key and prod_public_key'
26
+ end
27
+ end
28
+ super
29
+ end
30
+
31
+ def purchase(money, payment, options = {})
32
+ post = {
33
+ reference: options[:reference] || generate_reference,
34
+ public_key: public_key
35
+ }
36
+ add_invoice(post, money, options)
37
+ add_card(post, payment, options)
38
+
39
+ commit('sale', post, '/transactions_sync')
40
+ end
41
+
42
+ def authorize(money, payment, options = {})
43
+ post = {
44
+ public_key: public_key,
45
+ type: 'CARD',
46
+ financial_operation: 'PREAUTHORIZATION'
47
+ }
48
+ add_auth_params(post, money, payment, options)
49
+
50
+ commit('authorize', post, '/payment_sources_sync')
51
+ end
52
+
53
+ def capture(money, authorization, options = {})
54
+ post = {
55
+ reference: options[:reference] || generate_reference,
56
+ public_key: public_key,
57
+ payment_source_id: authorization.to_i
58
+ }
59
+ add_invoice(post, money, options)
60
+ commit('capture', post, '/transactions_sync')
61
+ end
62
+
63
+ def refund(money, authorization, options = {})
64
+ post = { amount_in_cents: amount(money).to_i, transaction_id: authorization.to_s }
65
+ commit('refund', post, '/refunds_sync')
66
+ end
67
+
68
+ def void(authorization, options = {})
69
+ commit('void', {}, "/transactions/#{authorization}/void_sync")
70
+ end
71
+
72
+ def supports_scrubbing?
73
+ true
74
+ end
75
+
76
+ def scrub(transcript)
77
+ transcript.gsub(/(Bearer )\w+/, '\1[REDACTED]').
78
+ gsub(/(\\\"number\\\":\\\")\d+/, '\1[REDACTED]').
79
+ gsub(/(\\\"cvc\\\":\\\")\d+/, '\1[REDACTED]').
80
+ gsub(/(\\\"phone_number\\\":\\\")\+?\d+/, '\1[REDACTED]').
81
+ gsub(/(\\\"email\\\":\\\")\S+\\\",/, '\1[REDACTED]\",').
82
+ gsub(/(\\\"legal_id\\\":\\\")\d+/, '\1[REDACTED]')
83
+ end
84
+
85
+ private
86
+
87
+ def headers
88
+ {
89
+ 'Authorization' => "Bearer #{private_key}",
90
+ 'Content-Type' => 'application/json'
91
+ }
92
+ end
93
+
94
+ def generate_reference
95
+ SecureRandom.alphanumeric(12)
96
+ end
97
+
98
+ def private_key
99
+ test? ? options[:test_private_key] : options[:prod_private_key]
100
+ end
101
+
102
+ def public_key
103
+ test? ? options[:test_public_key] : options[:prod_public_key]
104
+ end
105
+
106
+ def add_invoice(post, money, options)
107
+ post[:amount_in_cents] = amount(money).to_i
108
+ post[:currency] = (options[:currency] || currency(money))
109
+ end
110
+
111
+ def add_card(post, card, options)
112
+ payment_method = {
113
+ type: 'CARD'
114
+ }
115
+ add_basic_card_info(payment_method, card, options)
116
+ post[:payment_method] = payment_method
117
+ end
118
+
119
+ def add_auth_params(post, money, card, options)
120
+ data = {
121
+ amount_in_cents: amount(money).to_i,
122
+ currency: (options[:currency] || currency(money))
123
+ }
124
+ add_basic_card_info(data, card, options)
125
+ post[:data] = data
126
+ end
127
+
128
+ def add_basic_card_info(post, card, options)
129
+ installments = options[:installments] ? options[:installments].to_i : 1
130
+ cvc = card.verification_value || nil
131
+
132
+ post[:number] = card.number
133
+ post[:exp_month] = card.month.to_s.rjust(2, '0')
134
+ post[:exp_year] = card.year.to_s[2..3]
135
+ post[:installments] = installments
136
+ post[:card_holder] = card.name
137
+ post[:cvc] = cvc if cvc && !cvc.empty?
138
+ end
139
+
140
+ def parse(body)
141
+ JSON.parse(body)
142
+ end
143
+
144
+ def commit(action, parameters, endpoint)
145
+ url = (test? ? test_url : live_url) + endpoint
146
+ response = parse(ssl_post(url, post_data(action, parameters), headers))
147
+ Response.new(
148
+ success_from(response),
149
+ message_from(response),
150
+ response,
151
+ authorization: authorization_from(response),
152
+ avs_result: nil,
153
+ cvv_result: nil,
154
+ test: test?,
155
+ error_code: error_code_from(response)
156
+ )
157
+ end
158
+
159
+ def handle_response(response)
160
+ case response.code.to_i
161
+ when 200...300, 401, 404, 422
162
+ response.body
163
+ else
164
+ raise ResponseError.new(response)
165
+ end
166
+ end
167
+
168
+ def success_from(response)
169
+ success_statuses.include? response.dig('data', 'status')
170
+ end
171
+
172
+ def success_statuses
173
+ %w(APPROVED AVAILABLE)
174
+ end
175
+
176
+ def message_from(response)
177
+ response.dig('data', 'status_message') || response.dig('error', 'reason') || response.dig('error', 'messages').to_json
178
+ end
179
+
180
+ def authorization_from(response)
181
+ response.dig('data', 'transaction_id') || response.dig('data', 'id') || response.dig('data', 'transaction', 'id')
182
+ end
183
+
184
+ def post_data(action, parameters = {})
185
+ parameters.to_json
186
+ end
187
+
188
+ def error_code_from(response)
189
+ response.dig('error', 'type') unless success_from(response)
190
+ end
191
+ end
192
+ end
193
+ end