activemerchant 1.123.0 → 1.126.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 (63) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +206 -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 +18 -3
  6. data/lib/active_merchant/billing/gateway.rb +3 -2
  7. data/lib/active_merchant/billing/gateways/adyen.rb +66 -11
  8. data/lib/active_merchant/billing/gateways/airwallex.rb +341 -0
  9. data/lib/active_merchant/billing/gateways/barclaycard_smartpay.rb +2 -1
  10. data/lib/active_merchant/billing/gateways/blue_pay.rb +1 -1
  11. data/lib/active_merchant/billing/gateways/blue_snap.rb +31 -21
  12. data/lib/active_merchant/billing/gateways/braintree/braintree_common.rb +6 -1
  13. data/lib/active_merchant/billing/gateways/braintree/token_nonce.rb +113 -0
  14. data/lib/active_merchant/billing/gateways/braintree_blue.rb +87 -15
  15. data/lib/active_merchant/billing/gateways/card_connect.rb +1 -1
  16. data/lib/active_merchant/billing/gateways/card_stream.rb +17 -13
  17. data/lib/active_merchant/billing/gateways/cashnet.rb +15 -5
  18. data/lib/active_merchant/billing/gateways/checkout_v2.rb +34 -5
  19. data/lib/active_merchant/billing/gateways/credorax.rb +10 -0
  20. data/lib/active_merchant/billing/gateways/cyber_source.rb +24 -36
  21. data/lib/active_merchant/billing/gateways/d_local.rb +61 -6
  22. data/lib/active_merchant/billing/gateways/decidir.rb +17 -1
  23. data/lib/active_merchant/billing/gateways/decidir_plus.rb +344 -0
  24. data/lib/active_merchant/billing/gateways/ebanx.rb +19 -3
  25. data/lib/active_merchant/billing/gateways/elavon.rb +6 -3
  26. data/lib/active_merchant/billing/gateways/element.rb +20 -2
  27. data/lib/active_merchant/billing/gateways/global_collect.rb +137 -32
  28. data/lib/active_merchant/billing/gateways/ipg.rb +415 -0
  29. data/lib/active_merchant/billing/gateways/kushki.rb +7 -0
  30. data/lib/active_merchant/billing/gateways/litle.rb +93 -1
  31. data/lib/active_merchant/billing/gateways/mercado_pago.rb +3 -1
  32. data/lib/active_merchant/billing/gateways/mit.rb +260 -0
  33. data/lib/active_merchant/billing/gateways/moka.rb +24 -11
  34. data/lib/active_merchant/billing/gateways/moneris.rb +35 -8
  35. data/lib/active_merchant/billing/gateways/mundipagg.rb +8 -6
  36. data/lib/active_merchant/billing/gateways/nmi.rb +27 -8
  37. data/lib/active_merchant/billing/gateways/orbital.rb +357 -319
  38. data/lib/active_merchant/billing/gateways/pay_arc.rb +9 -7
  39. data/lib/active_merchant/billing/gateways/pay_conex.rb +3 -1
  40. data/lib/active_merchant/billing/gateways/pay_trace.rb +1 -1
  41. data/lib/active_merchant/billing/gateways/payflow.rb +76 -6
  42. data/lib/active_merchant/billing/gateways/paymentez.rb +35 -9
  43. data/lib/active_merchant/billing/gateways/paysafe.rb +155 -34
  44. data/lib/active_merchant/billing/gateways/payu_latam.rb +31 -16
  45. data/lib/active_merchant/billing/gateways/payway_dot_com.rb +3 -3
  46. data/lib/active_merchant/billing/gateways/pin.rb +31 -4
  47. data/lib/active_merchant/billing/gateways/priority.rb +369 -0
  48. data/lib/active_merchant/billing/gateways/rapyd.rb +258 -0
  49. data/lib/active_merchant/billing/gateways/realex.rb +18 -0
  50. data/lib/active_merchant/billing/gateways/safe_charge.rb +7 -6
  51. data/lib/active_merchant/billing/gateways/simetrik.rb +362 -0
  52. data/lib/active_merchant/billing/gateways/stripe.rb +30 -8
  53. data/lib/active_merchant/billing/gateways/stripe_payment_intents.rb +175 -72
  54. data/lib/active_merchant/billing/gateways/trans_first_transaction_express.rb +2 -1
  55. data/lib/active_merchant/billing/gateways/trust_commerce.rb +2 -1
  56. data/lib/active_merchant/billing/gateways/usa_epay_transaction.rb +20 -6
  57. data/lib/active_merchant/billing/gateways/visanet_peru.rb +6 -2
  58. data/lib/active_merchant/billing/gateways/wompi.rb +193 -0
  59. data/lib/active_merchant/billing/gateways/worldpay.rb +196 -64
  60. data/lib/active_merchant/billing/network_tokenization_credit_card.rb +1 -1
  61. data/lib/active_merchant/billing/response.rb +4 -0
  62. data/lib/active_merchant/version.rb +1 -1
  63. metadata +11 -2
@@ -0,0 +1,258 @@
1
+ module ActiveMerchant #:nodoc:
2
+ module Billing #:nodoc:
3
+ class RapydGateway < Gateway
4
+ self.test_url = 'https://sandboxapi.rapyd.net/v1/'
5
+ self.live_url = 'https://api.rapyd.net/v1/'
6
+
7
+ self.supported_countries = %w(US BR CA CL CO DO SV MX PE PT VI AU HK IN ID JP MY NZ PH SG KR TW TH VN AD AT BE BA BG HR CY CZ DK EE FI FR GE DE GI GR GL HU IS IE IL IT LV LI LT LU MK MT MD MC ME NL GB NO PL RO RU SM SK SI ZA ES SE CH TR VA)
8
+ self.default_currency = 'USD'
9
+ self.supported_cardtypes = %i[visa master american_express discover]
10
+
11
+ self.homepage_url = 'https://www.rapyd.net/'
12
+ self.display_name = 'Rapyd Gateway'
13
+
14
+ STANDARD_ERROR_CODE_MAPPING = {}
15
+
16
+ def initialize(options = {})
17
+ requires!(options, :secret_key, :access_key)
18
+ super
19
+ end
20
+
21
+ def purchase(money, payment, options = {})
22
+ post = {}
23
+ add_invoice(post, money, options)
24
+ add_payment(post, payment, options)
25
+ add_address(post, payment, options)
26
+ add_metadata(post, options)
27
+ add_ewallet(post, options)
28
+ post[:capture] = true if payment_is_card?(options)
29
+
30
+ if payment_is_ach?(options)
31
+ MultiResponse.run do |r|
32
+ r.process { commit(:post, 'payments', post) }
33
+ post = {}
34
+ post[:token] = r.authorization
35
+ post[:param2] = r.params.dig('data', 'original_amount').to_s
36
+ r.process { commit(:post, 'payments/completePayment', post) }
37
+ end
38
+ else
39
+ commit(:post, 'payments', post)
40
+ end
41
+ end
42
+
43
+ def authorize(money, payment, options = {})
44
+ post = {}
45
+ add_invoice(post, money, options)
46
+ add_payment(post, payment, options)
47
+ add_address(post, payment, options)
48
+ add_metadata(post, options)
49
+ add_ewallet(post, options)
50
+ post[:capture] = false
51
+ commit(:post, 'payments', post)
52
+ end
53
+
54
+ def capture(money, authorization, options = {})
55
+ post = {}
56
+ commit(:post, "payments/#{authorization}/capture", post)
57
+ end
58
+
59
+ def refund(money, authorization, options = {})
60
+ post = {}
61
+ post[:payment] = authorization
62
+ add_invoice(post, money, options)
63
+ add_metadata(post, options)
64
+ commit(:post, 'refunds', post)
65
+ end
66
+
67
+ def void(authorization, options = {})
68
+ post = {}
69
+ commit(:delete, "payments/#{authorization}", post)
70
+ end
71
+
72
+ # Gateway returns an error if trying to run a $0 auth as invalid payment amount
73
+ # Gateway does not support void on a card transaction and refunds can only be done on completed transactions
74
+ # (such as a purchase). Authorize transactions are considered 'active' and not 'complete' until they are captured.
75
+ def verify(credit_card, options = {})
76
+ MultiResponse.run do |r|
77
+ r.process { purchase(100, credit_card, options) }
78
+ r.process { refund(100, r.authorization, options) }
79
+ end
80
+ end
81
+
82
+ def supports_scrubbing?
83
+ true
84
+ end
85
+
86
+ def scrub(transcript)
87
+ transcript.
88
+ gsub(%r((Access_key: )\w+), '\1[FILTERED]').
89
+ gsub(%r(("number\\?":\\?")\d+), '\1[FILTERED]').
90
+ gsub(%r(("cvv\\?":\\?")\d+), '\1[FILTERED]')
91
+ end
92
+
93
+ private
94
+
95
+ def payment_is_ach?(options)
96
+ return unless options[:pm_type]
97
+
98
+ return true if options[:pm_type].include?('_bank')
99
+ end
100
+
101
+ def payment_is_card?(options)
102
+ return unless options[:pm_type]
103
+
104
+ return true if options[:pm_type].include?('_card')
105
+ end
106
+
107
+ def add_address(post, creditcard, options)
108
+ return unless address = options[:address]
109
+
110
+ post[:address] = {}
111
+ # name and line_1 are required at the gateway
112
+ post[:address][:name] = address[:name] if address[:name]
113
+ post[:address][:line_1] = address[:address1] if address[:address1]
114
+ post[:address][:line_2] = address[:address2] if address[:address2]
115
+ post[:address][:city] = address[:city] if address[:city]
116
+ post[:address][:state] = address[:state] if address[:state]
117
+ post[:address][:country] = address[:country] if address[:country]
118
+ post[:address][:zip] = address[:zip] if address[:zip]
119
+ post[:address][:phone_number] = address[:phone] if address[:phone]
120
+ end
121
+
122
+ def add_invoice(post, money, options)
123
+ post[:amount] = amount(money).to_f.to_s
124
+ post[:currency] = (options[:currency] || currency(money))
125
+ end
126
+
127
+ def add_payment(post, payment, options)
128
+ if payment_is_card?(options)
129
+ add_creditcard(post, payment, options)
130
+ elsif payment_is_ach?(options)
131
+ add_ach(post, payment, options)
132
+ end
133
+ end
134
+
135
+ def add_creditcard(post, payment, options)
136
+ post[:payment_method] = {}
137
+ post[:payment_method][:fields] = {}
138
+ pm_fields = post[:payment_method][:fields]
139
+
140
+ post[:payment_method][:type] = options[:pm_type]
141
+ pm_fields[:number] = payment.number
142
+ pm_fields[:expiration_month] = payment.month.to_s
143
+ pm_fields[:expiration_year] = payment.year.to_s
144
+ pm_fields[:cvv] = payment.verification_value.to_s
145
+ pm_fields[:name] = "#{payment.first_name} #{payment.last_name}"
146
+ end
147
+
148
+ def add_ach(post, payment, options)
149
+ post[:payment_method] = {}
150
+ post[:payment_method][:fields] = {}
151
+
152
+ post[:payment_method][:type] = options[:pm_type]
153
+ post[:payment_method][:fields][:proof_of_authorization] = options[:proof_of_authorization]
154
+ post[:payment_method][:fields][:first_name] = payment.first_name if payment.first_name
155
+ post[:payment_method][:fields][:last_name] = payment.last_name if payment.last_name
156
+ post[:payment_method][:fields][:routing_number] = payment.routing_number
157
+ post[:payment_method][:fields][:account_number] = payment.account_number
158
+ post[:payment_method][:fields][:payment_purpose] = options[:payment_purpose] if options[:payment_purpose]
159
+ end
160
+
161
+ def add_metadata(post, options)
162
+ post[:metadata] = options[:metadata] if options[:metadata]
163
+ end
164
+
165
+ def add_ewallet(post, options)
166
+ post[:ewallet_id] = options[:ewallet_id] if options[:ewallet_id]
167
+ end
168
+
169
+ def parse(body)
170
+ return {} if body.empty? || body.nil?
171
+
172
+ JSON.parse(body)
173
+ end
174
+
175
+ def commit(method, action, parameters)
176
+ url = (test? ? test_url : live_url) + action.to_s
177
+ rel_path = "#{method}/v1/#{action}"
178
+ response = api_request(method, url, rel_path, parameters)
179
+
180
+ Response.new(
181
+ success_from(response),
182
+ message_from(response),
183
+ response,
184
+ authorization: authorization_from(response),
185
+ avs_result: avs_result(response),
186
+ cvv_result: cvv_result(response),
187
+ test: test?,
188
+ error_code: error_code_from(response)
189
+ )
190
+ end
191
+
192
+ def api_request(method, url, rel_path, params)
193
+ params == {} ? body = '' : body = params.to_json
194
+ parse(ssl_request(method, url, body, headers(rel_path, body)))
195
+ end
196
+
197
+ def headers(rel_path, payload)
198
+ salt = SecureRandom.base64(12)
199
+ timestamp = Time.new.to_i.to_s
200
+ {
201
+ 'Content-Type' => 'application/json',
202
+ 'access_key' => @options[:access_key],
203
+ 'salt' => salt,
204
+ 'timestamp' => timestamp,
205
+ 'signature' => generate_hmac(rel_path, salt, timestamp, payload)
206
+ }
207
+ end
208
+
209
+ def generate_hmac(rel_path, salt, timestamp, payload)
210
+ signature = "#{rel_path}#{salt}#{timestamp}#{@options[:access_key]}#{@options[:secret_key]}#{payload}"
211
+ hash = Base64.urlsafe_encode64(OpenSSL::HMAC.hexdigest('sha256', @options[:secret_key], signature))
212
+ hash
213
+ end
214
+
215
+ def avs_result(response)
216
+ return nil unless (code = response.dig('data', 'payment_method_data', 'acs_check'))
217
+
218
+ AVSResult.new(code: code)
219
+ end
220
+
221
+ def cvv_result(response)
222
+ return nil unless (code = response.dig('data', 'payment_method_data', 'cvv_check'))
223
+
224
+ CVVResult.new(code)
225
+ end
226
+
227
+ def success_from(response)
228
+ response.dig('status', 'status') == 'SUCCESS' && response.dig('status', 'error') != 'ERR'
229
+ end
230
+
231
+ def message_from(response)
232
+ case response.dig('status', 'status')
233
+ when 'ERROR'
234
+ response.dig('status', 'message') == '' ? response.dig('status', 'error_code') : response.dig('status', 'message')
235
+ else
236
+ response.dig('status', 'status')
237
+ end
238
+ end
239
+
240
+ def authorization_from(response)
241
+ response.dig('data') ? response.dig('data', 'id') : response.dig('status', 'operation_id')
242
+ end
243
+
244
+ def error_code_from(response)
245
+ response.dig('status', 'error_code') unless success_from(response)
246
+ end
247
+
248
+ def handle_response(response)
249
+ case response.code.to_i
250
+ when 200...300, 400, 401, 404
251
+ response.body
252
+ else
253
+ raise ResponseError.new(response)
254
+ end
255
+ end
256
+ end
257
+ end
258
+ end
@@ -151,6 +151,7 @@ module ActiveMerchant
151
151
  else
152
152
  add_three_d_secure(xml, options)
153
153
  end
154
+ add_stored_credential(xml, options)
154
155
  add_comments(xml, options)
155
156
  add_address_and_customer_info(xml, options)
156
157
  end
@@ -323,6 +324,23 @@ module ActiveMerchant
323
324
  end
324
325
  end
325
326
 
327
+ def add_stored_credential(xml, options)
328
+ return unless stored_credential = options[:stored_credential]
329
+
330
+ xml.tag! 'storedcredential' do
331
+ xml.tag! 'type', stored_credential_type(stored_credential[:reason_type])
332
+ xml.tag! 'initiator', stored_credential[:initiator]
333
+ xml.tag! 'sequence', stored_credential[:initial_transaction] ? 'first' : 'subsequent'
334
+ xml.tag! 'srd', stored_credential[:network_transaction_id]
335
+ end
336
+ end
337
+
338
+ def stored_credential_type(reason)
339
+ return 'oneoff' if reason == 'unscheduled'
340
+
341
+ reason
342
+ end
343
+
326
344
  def format_address_code(address)
327
345
  code = [address[:zip].to_s, address[:address1].to_s + address[:address2].to_s]
328
346
  code.collect { |e| e.gsub(/\D/, '') }.reject(&:empty?).join('|')
@@ -62,6 +62,7 @@ module ActiveMerchant #:nodoc:
62
62
  post[:sg_CCToken] = token
63
63
  post[:sg_ExpMonth] = exp_month
64
64
  post[:sg_ExpYear] = exp_year
65
+ post[:sg_Email] = options[:email]
65
66
 
66
67
  commit(post)
67
68
  end
@@ -106,10 +107,7 @@ module ActiveMerchant #:nodoc:
106
107
  end
107
108
 
108
109
  def verify(credit_card, options = {})
109
- MultiResponse.run(:use_first_response) do |r|
110
- r.process { authorize(100, credit_card, options) }
111
- r.process(:ignore_result) { void(r.authorization, options) }
112
- end
110
+ authorize(0, credit_card, options)
113
111
  end
114
112
 
115
113
  def supports_scrubbing?
@@ -126,9 +124,11 @@ module ActiveMerchant #:nodoc:
126
124
  private
127
125
 
128
126
  def add_transaction_data(trans_type, post, money, options)
127
+ currency = options[:currency] || currency(money)
128
+
129
129
  post[:sg_TransType] = trans_type
130
- post[:sg_Currency] = (options[:currency] || currency(money))
131
- post[:sg_Amount] = amount(money)
130
+ post[:sg_Currency] = currency
131
+ post[:sg_Amount] = localized_amount(money, currency)
132
132
  post[:sg_ClientLoginID] = @options[:client_login_id]
133
133
  post[:sg_ClientPassword] = @options[:client_password]
134
134
  post[:sg_ResponseFormat] = '4'
@@ -144,6 +144,7 @@ module ActiveMerchant #:nodoc:
144
144
  post[:sg_MerchantPhoneNumber] = options[:merchant_phone_number] if options[:merchant_phone_number]
145
145
  post[:sg_MerchantName] = options[:merchant_name] if options[:merchant_name]
146
146
  post[:sg_ProductID] = options[:product_id] if options[:product_id]
147
+ post[:sg_NotUseCVV] = options[:not_use_cvv].to_s == 'true' ? 1 : 0 unless options[:not_use_cvv].nil?
147
148
  end
148
149
 
149
150
  def add_payment(post, payment, options = {})
@@ -0,0 +1,362 @@
1
+ module ActiveMerchant #:nodoc:
2
+ module Billing #:nodoc:
3
+ class SimetrikGateway < Gateway
4
+ self.test_url = 'https://payments.sta.simetrik.com/v1'
5
+ self.live_url = 'https://payments.simetrik.com/v1'
6
+
7
+ class_attribute :test_auth_url, :live_auth_url
8
+ self.test_auth_url = 'https://tenant-payments-dev.us.auth0.com/oauth/token'
9
+ self.live_auth_url = 'https://tenant-payments-prod.us.auth0.com/oauth/token'
10
+
11
+ self.supported_countries = %w(PE AR)
12
+ self.default_currency = 'USD'
13
+ self.supported_cardtypes = %i[visa master american_express discover]
14
+
15
+ self.homepage_url = 'https://www.simetrik.com'
16
+ self.display_name = 'Simetrik'
17
+
18
+ STANDARD_ERROR_CODE_MAPPING = {
19
+ 'R101' => STANDARD_ERROR_CODE[:incorrect_number],
20
+ 'R102' => STANDARD_ERROR_CODE[:invalid_number],
21
+ 'R103' => STANDARD_ERROR_CODE[:invalid_expiry_date],
22
+ 'R104' => STANDARD_ERROR_CODE[:invalid_cvc],
23
+ 'R105' => STANDARD_ERROR_CODE[:expired_card],
24
+ 'R106' => STANDARD_ERROR_CODE[:incorrect_cvc],
25
+ 'R107' => STANDARD_ERROR_CODE[:incorrect_pin],
26
+ 'R201' => STANDARD_ERROR_CODE[:incorrect_zip],
27
+ 'R202' => STANDARD_ERROR_CODE[:incorrect_address],
28
+ 'R301' => STANDARD_ERROR_CODE[:card_declined],
29
+ 'R302' => STANDARD_ERROR_CODE[:processing_error],
30
+ 'R303' => STANDARD_ERROR_CODE[:call_issuer],
31
+ 'R304' => STANDARD_ERROR_CODE[:pick_up_card],
32
+ 'R305' => STANDARD_ERROR_CODE[:processing_error],
33
+ 'R306' => STANDARD_ERROR_CODE[:processing_error],
34
+ 'R307' => STANDARD_ERROR_CODE[:processing_error],
35
+ 'R401' => STANDARD_ERROR_CODE[:config_error],
36
+ 'R402' => STANDARD_ERROR_CODE[:test_mode_live_card],
37
+ 'R403' => STANDARD_ERROR_CODE[:unsupported_feature]
38
+
39
+ }
40
+
41
+ def initialize(options = {})
42
+ requires!(options, :client_id, :client_secret, :audience)
43
+ super
44
+ @access_token = {}
45
+ sign_access_token()
46
+ end
47
+
48
+ def authorize(money, payment, options = {})
49
+ requires!(options, :token_acquirer)
50
+
51
+ post = {}
52
+ add_forward_route(post, options)
53
+ add_forward_payload(post, money, payment, options)
54
+ add_stored_credential(post, options)
55
+
56
+ commit('authorize', post, { token_acquirer: options[:token_acquirer] })
57
+ end
58
+
59
+ def capture(money, authorization, options = {})
60
+ requires!(options, :token_acquirer)
61
+ post = {
62
+ forward_payload: {
63
+ amount: {
64
+ total_amount: amount(money).to_f,
65
+ currency: (options[:currency] || currency(money))
66
+ },
67
+ transaction: {
68
+ id: authorization
69
+ },
70
+ acquire_extra_options: options[:acquire_extra_options] || {}
71
+ }
72
+ }
73
+ post[:forward_payload][:amount][:vat] = options[:vat] if options[:vat]
74
+
75
+ add_forward_route(post, options)
76
+ commit('capture', post, { token_acquirer: options[:token_acquirer] })
77
+ end
78
+
79
+ def refund(money, authorization, options = {})
80
+ requires!(options, :token_acquirer)
81
+ post = {
82
+ forward_payload: {
83
+ amount: {
84
+ total_amount: amount(money).to_f,
85
+ currency: (options[:currency] || currency(money))
86
+ },
87
+ transaction: {
88
+ id: authorization
89
+ },
90
+ acquire_extra_options: options[:acquire_extra_options] || {}
91
+ }
92
+ }
93
+ post[:forward_payload][:transaction][:comment] = options[:comment] if options[:comment]
94
+
95
+ add_forward_route(post, options)
96
+ commit('refund', post, { token_acquirer: options[:token_acquirer] })
97
+ end
98
+
99
+ def void(authorization, options = {})
100
+ requires!(options, :token_acquirer)
101
+ post = {
102
+ forward_payload: {
103
+ transaction: {
104
+ id: authorization
105
+ },
106
+ acquire_extra_options: options[:acquire_extra_options] || {}
107
+ }
108
+ }
109
+ add_forward_route(post, options)
110
+ commit('void', post, { token_acquirer: options[:token_acquirer] })
111
+ end
112
+
113
+ def purchase(money, payment, options = {})
114
+ requires!(options, :token_acquirer)
115
+
116
+ post = {}
117
+ add_forward_route(post, options)
118
+ add_forward_payload(post, money, payment, options)
119
+
120
+ add_stored_credential(post, options)
121
+ commit('charge', post, { token_acquirer: options[:token_acquirer] })
122
+ end
123
+
124
+ def supports_scrubbing?
125
+ true
126
+ end
127
+
128
+ def scrub(transcript)
129
+ transcript.
130
+ gsub(%r((\"number\\\":\\\")\d+), '\1[FILTERED]').
131
+ gsub(%r((\"security_code\\\":\\\")\d+), '\1[FILTERED]').
132
+ gsub(%r((\"exp_month\\\":\\\")\d+), '\1[FILTERED]').
133
+ gsub(%r((\"exp_year\\\":\\\")\d+), '\1[FILTERED]').
134
+ gsub(%r((\"holder_first_name\\\":\\\")"\w+"), '\1[FILTERED]').
135
+ gsub(%r((\"holder_last_name\\\":\\\")"\w+"), '\1[FILTERED]')
136
+ end
137
+
138
+ private
139
+
140
+ def add_forward_route(post, options)
141
+ forward_route = {}
142
+ forward_route[:trace_id] = options[:trace_id] if options[:trace_id]
143
+
144
+ forward_route[:psp_extra_fields] = options[:psp_extra_fields] || {}
145
+ post[:forward_route] = forward_route
146
+ end
147
+
148
+ def add_forward_payload(post, money, payment, options)
149
+ forward_payload = {}
150
+ add_user(forward_payload, options[:user]) if options[:user]
151
+ add_order(forward_payload, money, options[:order]) if options[:order] || money
152
+ add_payment_method(forward_payload, payment, options[:payment_method]) if options[:payment_method] || payment
153
+
154
+ forward_payload[:payment_method] = {} unless forward_payload[:payment_method]
155
+ forward_payload[:payment_method][:card] = {} unless forward_payload[:payment_method][:card]
156
+ add_address('billing_address', forward_payload[:payment_method][:card], options[:billing_address]) if options[:billing_address]
157
+
158
+ add_three_ds_fields(forward_payload[:authentication] = {}, options[:three_ds_fields]) if options[:three_ds_fields]
159
+ add_sub_merchant(forward_payload, options[:sub_merchant]) if options[:sub_merchant]
160
+ forward_payload[:acquire_extra_options] = options[:acquire_extra_options] || {}
161
+ post[:forward_payload] = forward_payload
162
+ end
163
+
164
+ def add_sub_merchant(post, sub_merchant_options)
165
+ sub_merchant = {}
166
+ sub_merchant[:merchant_id] = sub_merchant_options[:merchant_id] if sub_merchant_options[:merchant_id]
167
+ sub_merchant[:extra_params] = sub_merchant_options[:extra_params] if sub_merchant_options[:extra_params]
168
+ sub_merchant[:mcc] = sub_merchant_options[:mcc] if sub_merchant_options[:mcc]
169
+ sub_merchant[:name] = sub_merchant_options[:name] if sub_merchant_options[:name]
170
+ sub_merchant[:address] = sub_merchant_options[:address] if sub_merchant_options[:address]
171
+ sub_merchant[:postal_code] = sub_merchant_options[:postal_code] if sub_merchant_options[:postal_code]
172
+ sub_merchant[:url] = sub_merchant_options[:url] if sub_merchant_options[:url]
173
+ sub_merchant[:phone_number] = sub_merchant_options[:phone_number] if sub_merchant_options[:phone_number]
174
+
175
+ post[:sub_merchant] = sub_merchant
176
+ end
177
+
178
+ def add_payment_method(post, payment, payment_method_options)
179
+ payment_method = {}
180
+ opts = nil
181
+ opts = payment_method_options[:card] if payment_method_options
182
+ add_card(payment_method, payment, opts) if opts || payment
183
+
184
+ post[:payment_method] = payment_method
185
+ end
186
+
187
+ def add_three_ds_fields(post, three_ds_options)
188
+ three_ds = {}
189
+ three_ds[:version] = three_ds_options[:version] if three_ds_options[:version]
190
+ three_ds[:eci] = three_ds_options[:eci] if three_ds_options[:eci]
191
+ three_ds[:cavv] = three_ds_options[:cavv] if three_ds_options[:cavv]
192
+ three_ds[:ds_transaction_id] = three_ds_options[:ds_transaction_id] if three_ds_options[:ds_transaction_id]
193
+ three_ds[:acs_transaction_id] = three_ds_options[:acs_transaction_id] if three_ds_options[:acs_transaction_id]
194
+ three_ds[:xid] = three_ds_options[:xid] if three_ds_options[:xid]
195
+ three_ds[:enrolled] = three_ds_options[:enrolled] if three_ds_options[:enrolled]
196
+ three_ds[:cavv_algorithm] = three_ds_options[:cavv_algorithm] if three_ds_options[:cavv_algorithm]
197
+ three_ds[:directory_response_status] = three_ds_options[:directory_response_status] if three_ds_options[:directory_response_status]
198
+ three_ds[:authentication_response_status] = three_ds_options[:authentication_response_status] if three_ds_options[:authentication_response_status]
199
+ three_ds[:three_ds_server_trans_id] = three_ds_options[:three_ds_server_trans_id] if three_ds_options[:three_ds_server_trans_id]
200
+
201
+ post[:three_ds_fields] = three_ds
202
+ end
203
+
204
+ def add_card(post, card, card_options = {})
205
+ card_hash = {}
206
+ card_hash[:number] = card.number
207
+ card_hash[:exp_month] = card.month
208
+ card_hash[:exp_year] = card.year
209
+ card_hash[:security_code] = card.verification_value
210
+ card_hash[:type] = card.brand
211
+ card_hash[:holder_first_name] = card.first_name
212
+ card_hash[:holder_last_name] = card.last_name
213
+ post[:card] = card_hash
214
+ end
215
+
216
+ def add_user(post, user_options)
217
+ user = {}
218
+ user[:id] = user_options[:id] if user_options[:id]
219
+ user[:email] = user_options[:email] if user_options[:email]
220
+
221
+ post[:user] = user
222
+ end
223
+
224
+ def add_stored_credential(post, options)
225
+ return unless options[:stored_credential]
226
+
227
+ check_initiator = %w[merchant cardholder].any? { |item| item == options[:stored_credential][:initiator] }
228
+ check_reason_type = %w[recurring installment unscheduled].any? { |item| item == options[:stored_credential][:reason_type] }
229
+ post[:forward_payload][:authentication] = {} unless post[:forward_payload].key?(:authentication)
230
+ post[:forward_payload][:authentication][:stored_credential] = options[:stored_credential] if check_initiator && check_reason_type
231
+ end
232
+
233
+ def add_order(post, money, order_options)
234
+ order = {}
235
+ order[:id] = order_options[:id] if order_options[:id]
236
+ order[:description] = order_options[:description] if order_options[:description]
237
+ order[:installments] = order_options[:installments] if order_options[:installments]
238
+ order[:datetime_local_transaction] = order_options[:datetime_local_transaction] if order_options[:datetime_local_transaction]
239
+
240
+ add_amount(order, money, order_options[:amount]) if order_options[:amount]
241
+ add_address('shipping_address', order, order_options[:shipping_address]) if order_options[:shipping_address]
242
+
243
+ post[:order] = order
244
+ end
245
+
246
+ def add_amount(post, money, amount_options)
247
+ amount_obj = {}
248
+ amount_obj[:total_amount] = amount(money).to_f
249
+ amount_obj[:currency] = (amount_options[:currency] || currency(money))
250
+ amount_obj[:vat] = amount_options[:vat] if amount_options[:vat]
251
+
252
+ post[:amount] = amount_obj
253
+ end
254
+
255
+ def add_address(tag, post, address_options)
256
+ address = {}
257
+ address[:name] = address_options[:name] if address_options[:name]
258
+ address[:address1] = address_options[:address1] if address_options[:address1]
259
+ address[:address2] = address_options[:address2] if address_options[:address2]
260
+ address[:company] = address_options[:company] if address_options[:company]
261
+ address[:city] = address_options[:city] if address_options[:city]
262
+ address[:state] = address_options[:state] if address_options[:state]
263
+ address[:zip] = address_options[:zip] if address_options[:zip]
264
+ address[:country] = address_options[:country] if address_options[:country]
265
+ address[:phone] = address_options[:phone] if address_options[:phone]
266
+ address[:fax] = address_options[:fax] if address_options[:fax]
267
+
268
+ post[tag] = address
269
+ end
270
+
271
+ def parse(body)
272
+ JSON.parse(body)
273
+ end
274
+
275
+ def commit(action, parameters, url_params = {})
276
+ begin
277
+ response = JSON.parse ssl_post(url(action, url_params), post_data(parameters), authorized_headers())
278
+ rescue ResponseError => exception
279
+ case exception.response.code.to_i
280
+ when 400...499
281
+ response = JSON.parse exception.response.body
282
+ else
283
+ raise exception
284
+ end
285
+ end
286
+
287
+ Response.new(
288
+ success_from(response['code']),
289
+ message_from(response),
290
+ response,
291
+ authorization: authorization_from(response),
292
+ avs_result: AVSResult.new(code: avs_code_from(response)),
293
+ cvv_result: CVVResult.new(cvv_code_from(response)),
294
+ test: test?,
295
+ error_code: error_code_from(response)
296
+ )
297
+ end
298
+
299
+ def avs_code_from(response)
300
+ response['avs_result']
301
+ end
302
+
303
+ def cvv_code_from(response)
304
+ response['cvv_result']
305
+ end
306
+
307
+ def success_from(code)
308
+ code == 'S001'
309
+ end
310
+
311
+ def message_from(response)
312
+ response['message']
313
+ end
314
+
315
+ def url(action, url_params)
316
+ "#{(test? ? test_url : live_url)}/#{url_params[:token_acquirer]}/#{action}"
317
+ end
318
+
319
+ def post_data(data = {})
320
+ data.to_json
321
+ end
322
+
323
+ def authorization_from(response)
324
+ response['simetrik_authorization_id']
325
+ end
326
+
327
+ def error_code_from(response)
328
+ STANDARD_ERROR_CODE_MAPPING[response['code']] unless success_from(response['code'])
329
+ end
330
+
331
+ def authorized_headers
332
+ {
333
+ 'content-Type' => 'application/json',
334
+ 'Authorization' => "Bearer #{sign_access_token()}"
335
+ }
336
+ end
337
+
338
+ def sign_access_token
339
+ fetch_access_token() if Time.new.to_i > (@access_token[:expires_at] || 0) + 10
340
+ @access_token[:access_token]
341
+ end
342
+
343
+ def auth_url
344
+ (test? ? test_auth_url : live_auth_url)
345
+ end
346
+
347
+ def fetch_access_token
348
+ login_info = {}
349
+ login_info[:client_id] = @options[:client_id]
350
+ login_info[:client_secret] = @options[:client_secret]
351
+ login_info[:audience] = @options[:audience]
352
+ login_info[:grant_type] = 'client_credentials'
353
+ response = parse(ssl_post(auth_url(), login_info.to_json, {
354
+ 'content-Type' => 'application/json'
355
+ }))
356
+
357
+ @access_token[:access_token] = response['access_token']
358
+ @access_token[:expires_at] = Time.new.to_i + response['expires_in']
359
+ end
360
+ end
361
+ end
362
+ end