activemerchant 1.126.0 → 1.129.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (71) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +241 -0
  3. data/lib/active_merchant/billing/check.rb +40 -8
  4. data/lib/active_merchant/billing/credit_card.rb +28 -1
  5. data/lib/active_merchant/billing/credit_card_methods.rb +79 -23
  6. data/lib/active_merchant/billing/gateways/adyen.rb +67 -8
  7. data/lib/active_merchant/billing/gateways/airwallex.rb +40 -11
  8. data/lib/active_merchant/billing/gateways/alelo.rb +256 -0
  9. data/lib/active_merchant/billing/gateways/authorize_net.rb +21 -4
  10. data/lib/active_merchant/billing/gateways/beanstream.rb +18 -0
  11. data/lib/active_merchant/billing/gateways/blue_snap.rb +22 -1
  12. data/lib/active_merchant/billing/gateways/bogus.rb +4 -0
  13. data/lib/active_merchant/billing/gateways/borgun.rb +56 -16
  14. data/lib/active_merchant/billing/gateways/braintree_blue.rb +64 -17
  15. data/lib/active_merchant/billing/gateways/card_connect.rb +27 -9
  16. data/lib/active_merchant/billing/gateways/card_stream.rb +23 -0
  17. data/lib/active_merchant/billing/gateways/checkout_v2.rb +228 -57
  18. data/lib/active_merchant/billing/gateways/commerce_hub.rb +361 -0
  19. data/lib/active_merchant/billing/gateways/credorax.rb +47 -27
  20. data/lib/active_merchant/billing/gateways/cyber_source/cyber_source_common.rb +36 -0
  21. data/lib/active_merchant/billing/gateways/cyber_source.rb +100 -26
  22. data/lib/active_merchant/billing/gateways/cyber_source_rest.rb +456 -0
  23. data/lib/active_merchant/billing/gateways/d_local.rb +44 -5
  24. data/lib/active_merchant/billing/gateways/decidir.rb +15 -4
  25. data/lib/active_merchant/billing/gateways/ebanx.rb +36 -24
  26. data/lib/active_merchant/billing/gateways/element.rb +21 -1
  27. data/lib/active_merchant/billing/gateways/global_collect.rb +73 -22
  28. data/lib/active_merchant/billing/gateways/ipg.rb +13 -8
  29. data/lib/active_merchant/billing/gateways/iveri.rb +39 -3
  30. data/lib/active_merchant/billing/gateways/kushki.rb +21 -1
  31. data/lib/active_merchant/billing/gateways/litle.rb +25 -5
  32. data/lib/active_merchant/billing/gateways/mastercard.rb +1 -8
  33. data/lib/active_merchant/billing/gateways/mercado_pago.rb +17 -0
  34. data/lib/active_merchant/billing/gateways/merchant_e_solutions.rb +44 -10
  35. data/lib/active_merchant/billing/gateways/monei.rb +2 -0
  36. data/lib/active_merchant/billing/gateways/moneris.rb +20 -5
  37. data/lib/active_merchant/billing/gateways/mundipagg.rb +3 -0
  38. data/lib/active_merchant/billing/gateways/ogone.rb +35 -7
  39. data/lib/active_merchant/billing/gateways/openpay.rb +20 -3
  40. data/lib/active_merchant/billing/gateways/orbital.rb +43 -22
  41. data/lib/active_merchant/billing/gateways/pay_trace.rb +64 -18
  42. data/lib/active_merchant/billing/gateways/payeezy.rb +59 -4
  43. data/lib/active_merchant/billing/gateways/paymentez.rb +18 -6
  44. data/lib/active_merchant/billing/gateways/paypal/paypal_express_response.rb +4 -0
  45. data/lib/active_merchant/billing/gateways/paysafe.rb +22 -14
  46. data/lib/active_merchant/billing/gateways/payu_latam.rb +3 -0
  47. data/lib/active_merchant/billing/gateways/plexo.rb +308 -0
  48. data/lib/active_merchant/billing/gateways/priority.rb +29 -6
  49. data/lib/active_merchant/billing/gateways/rapyd.rb +110 -49
  50. data/lib/active_merchant/billing/gateways/reach.rb +277 -0
  51. data/lib/active_merchant/billing/gateways/redsys.rb +9 -5
  52. data/lib/active_merchant/billing/gateways/sage_pay.rb +1 -1
  53. data/lib/active_merchant/billing/gateways/securion_pay.rb +40 -0
  54. data/lib/active_merchant/billing/gateways/shift4.rb +342 -0
  55. data/lib/active_merchant/billing/gateways/simetrik.rb +28 -22
  56. data/lib/active_merchant/billing/gateways/stripe.rb +21 -1
  57. data/lib/active_merchant/billing/gateways/stripe_payment_intents.rb +62 -22
  58. data/lib/active_merchant/billing/gateways/tns.rb +2 -5
  59. data/lib/active_merchant/billing/gateways/trans_first_transaction_express.rb +1 -1
  60. data/lib/active_merchant/billing/gateways/trust_commerce.rb +14 -3
  61. data/lib/active_merchant/billing/gateways/vanco.rb +12 -3
  62. data/lib/active_merchant/billing/gateways/visanet_peru.rb +1 -1
  63. data/lib/active_merchant/billing/gateways/vpos.rb +7 -4
  64. data/lib/active_merchant/billing/gateways/wompi.rb +8 -4
  65. data/lib/active_merchant/billing/gateways/worldpay.rb +117 -9
  66. data/lib/active_merchant/billing/response.rb +15 -1
  67. data/lib/active_merchant/connection.rb +0 -2
  68. data/lib/active_merchant/country.rb +1 -0
  69. data/lib/active_merchant/errors.rb +4 -1
  70. data/lib/active_merchant/version.rb +1 -1
  71. metadata +24 -3
@@ -0,0 +1,308 @@
1
+ module ActiveMerchant #:nodoc:
2
+ module Billing #:nodoc:
3
+ class PlexoGateway < Gateway
4
+ self.test_url = 'https://api.testing.plexo.com.uy/v1/payments'
5
+ self.live_url = 'https://api.plexo.com.uy/v1/payments'
6
+
7
+ self.supported_countries = ['UY']
8
+ self.default_currency = 'UYU'
9
+ self.supported_cardtypes = %i[visa master american_express discover passcard edenred anda tarjeta-d]
10
+
11
+ self.homepage_url = 'https://www.plexo.com.uy'
12
+ self.display_name = 'Plexo'
13
+
14
+ APPENDED_URLS = %w(captures refunds cancellations verify)
15
+ AMOUNT_IN_RESPONSE = %w(authonly purchase /verify)
16
+ APPROVED_STATUS = %w(approved authorized)
17
+
18
+ def initialize(options = {})
19
+ requires!(options, :client_id, :api_key)
20
+ @credentials = options
21
+ super
22
+ end
23
+
24
+ def purchase(money, payment, options = {})
25
+ post = {}
26
+ build_auth_purchase_request(money, post, payment, options)
27
+
28
+ commit('purchase', post, options)
29
+ end
30
+
31
+ def authorize(money, payment, options = {})
32
+ post = {}
33
+ build_auth_purchase_request(money, post, payment, options)
34
+ add_capture_type(post, options)
35
+
36
+ commit('authonly', post, options)
37
+ end
38
+
39
+ def capture(money, authorization, options = {})
40
+ post = {}
41
+ post[:ReferenceId] = options[:reference_id] || generate_unique_id
42
+ post[:Amount] = amount(money)
43
+
44
+ commit("/#{authorization}/captures", post, options)
45
+ end
46
+
47
+ def refund(money, authorization, options = {})
48
+ post = {}
49
+ post[:ReferenceId] = options[:reference_id] || generate_unique_id
50
+ post[:Type] = options[:refund_type] || 'refund'
51
+ post[:Description] = options[:description]
52
+ post[:Reason] = options[:reason]
53
+ post[:Amount] = amount(money)
54
+
55
+ commit("/#{authorization}/refunds", post, options)
56
+ end
57
+
58
+ def void(authorization, options = {})
59
+ post = {}
60
+ post[:ReferenceId] = options[:reference_id] || generate_unique_id
61
+ post[:Description] = options[:description]
62
+ post[:Reason] = options[:reason]
63
+
64
+ commit("/#{authorization}/cancellations", post, options)
65
+ end
66
+
67
+ def verify(credit_card, options = {})
68
+ post = {}
69
+ post[:ReferenceId] = options[:reference_id] || generate_unique_id
70
+ post[:MerchantId] = options[:merchant_id] || @credentials[:merchant_id]
71
+ post[:StatementDescriptor] = options[:statement_descriptor] if options[:statement_descriptor]
72
+ post[:CustomerId] = options[:customer_id] if options[:customer_id]
73
+ money = options[:verify_amount].to_i || 100
74
+
75
+ add_payment_method(post, credit_card, options)
76
+ add_metadata(post, options[:metadata])
77
+ add_amount(money, post, options)
78
+ add_browser_details(post, options)
79
+
80
+ commit('/verify', post, options)
81
+ end
82
+
83
+ def supports_scrubbing?
84
+ true
85
+ end
86
+
87
+ def scrub(transcript)
88
+ transcript.
89
+ gsub(%r((Authorization: Basic )\w+), '\1[FILTERED]').
90
+ gsub(%r(("Number\\?"\s*:\s*\\?")[^"]*)i, '\1[FILTERED]').
91
+ gsub(%r(("Cvc\\?"\s*:\s*\\?")[^"]*)i, '\1[FILTERED]').
92
+ gsub(%r(("InvoiceNumber\\?"\s*:\s*\\?")[^"]*)i, '\1[FILTERED]').
93
+ gsub(%r(("MerchantId\\?"\s*:\s*\\?")[^"]*)i, '\1[FILTERED]')
94
+ end
95
+
96
+ private
97
+
98
+ def encoded_credentials
99
+ Base64.encode64("#{@credentials[:client_id]}:#{@credentials[:api_key]}").delete("\n")
100
+ end
101
+
102
+ def build_auth_purchase_request(money, post, payment, options)
103
+ post[:ReferenceId] = options[:reference_id] || generate_unique_id
104
+ post[:MerchantId] = options[:merchant_id] || @credentials[:merchant_id]
105
+ post[:Installments] = options[:installments] if options[:installments]
106
+ post[:StatementDescriptor] = options[:statement_descriptor] if options[:statement_descriptor]
107
+ post[:CustomerId] = options[:customer_id] if options[:customer_id]
108
+
109
+ add_payment_method(post, payment, options)
110
+ add_items(post, options[:items])
111
+ add_metadata(post, options[:metadata])
112
+ add_amount(money, post, options)
113
+ add_browser_details(post, options)
114
+ end
115
+
116
+ def header(parameters = {})
117
+ {
118
+ 'Content-Type' => 'application/json',
119
+ 'Authorization' => "Basic #{encoded_credentials}"
120
+ }
121
+ end
122
+
123
+ def add_capture_type(post, options)
124
+ post[:Capture] = {}
125
+ post[:Capture][:Method] = options.dig(:capture_type, :method) || 'manual'
126
+ end
127
+
128
+ def add_items(post, items)
129
+ return unless items&.kind_of?(Array)
130
+
131
+ post[:Items] = []
132
+
133
+ items.each do |option_item|
134
+ item = {}
135
+ item[:ReferenceId] = option_item[:reference_id] || generate_unique_id
136
+ item[:Name] = option_item[:name] if option_item[:name]
137
+ item[:Description] = option_item[:description] if option_item[:description]
138
+ item[:Quantity] = option_item[:quantity] if option_item[:quantity]
139
+ item[:Price] = option_item[:price] if option_item[:price]
140
+ item[:Discount] = option_item[:discount] if option_item[:discount]
141
+
142
+ post[:Items].append(item)
143
+ end
144
+ end
145
+
146
+ def add_metadata(post, metadata)
147
+ return unless metadata&.kind_of?(Hash)
148
+
149
+ metadata.transform_keys! { |key| key.to_s.camelize.to_sym }
150
+ post[:Metadata] = metadata
151
+ end
152
+
153
+ def add_amount(money, post, amount_options)
154
+ post[:Amount] = {}
155
+
156
+ post[:Amount][:Currency] = amount_options[:currency] || self.default_currency
157
+ post[:Amount][:Total] = amount(money)
158
+ post[:Amount][:Details] = {}
159
+ add_amount_details(post[:Amount][:Details], amount_options[:amount_details]) if amount_options[:amount_details]
160
+ end
161
+
162
+ def add_amount_details(amount_details, options)
163
+ return unless options
164
+
165
+ amount_details[:TaxedAmount] = options[:taxed_amount] if options[:taxed_amount]
166
+ amount_details[:TipAmount] = options[:tip_amount] if options[:tip_amount]
167
+ amount_details[:DiscountAmount] = options[:discount_amount] if options[:discount_amount]
168
+ amount_details[:TaxableAmount] = options[:taxable_amount] if options[:taxable_amount]
169
+ add_tax(amount_details, options[:tax])
170
+ end
171
+
172
+ def add_tax(post, tax)
173
+ return unless tax
174
+
175
+ post[:Tax] = {}
176
+ post[:Tax][:Type] = tax[:type] if tax[:type]
177
+ post[:Tax][:Amount] = tax[:amount] if tax[:amount]
178
+ post[:Tax][:Rate] = tax[:rate] if tax[:rate]
179
+ end
180
+
181
+ def add_browser_details(post, browser_details)
182
+ return unless browser_details
183
+
184
+ post[:BrowserDetails] = {}
185
+ post[:BrowserDetails][:DeviceFingerprint] = browser_details[:finger_print] if browser_details[:finger_print]
186
+ post[:BrowserDetails][:IpAddress] = browser_details[:ip] if browser_details[:ip]
187
+ end
188
+
189
+ def add_payment_method(post, payment, options)
190
+ post[:paymentMethod] = {}
191
+
192
+ if payment&.is_a?(CreditCard)
193
+ post[:paymentMethod][:type] = 'card'
194
+ post[:paymentMethod][:Card] = {}
195
+ post[:paymentMethod][:Card][:Number] = payment.number
196
+ post[:paymentMethod][:Card][:ExpMonth] = format(payment.month, :two_digits) if payment.month
197
+ post[:paymentMethod][:Card][:ExpYear] = format(payment.year, :two_digits) if payment.year
198
+ post[:paymentMethod][:Card][:Cvc] = payment.verification_value if payment.verification_value
199
+
200
+ add_card_holder(post[:paymentMethod][:Card], payment, options)
201
+ end
202
+ end
203
+
204
+ def add_card_holder(card, payment, options)
205
+ requires!(options, :email)
206
+
207
+ cardholder = {}
208
+ cardholder[:FirstName] = payment.first_name if payment.first_name
209
+ cardholder[:LastName] = payment.last_name if payment.last_name
210
+ cardholder[:Email] = options[:email]
211
+ cardholder[:Birthdate] = options[:cardholder_birthdate] if options[:cardholder_birthdate]
212
+ cardholder[:Identification] = {}
213
+ cardholder[:Identification][:Type] = options[:identification_type] if options[:identification_type]
214
+ cardholder[:Identification][:Value] = options[:identification_value] if options[:identification_value]
215
+ add_billing_address(cardholder, options)
216
+
217
+ card[:Cardholder] = cardholder
218
+ end
219
+
220
+ def add_billing_address(cardholder, options)
221
+ return unless address = options[:billing_address]
222
+
223
+ cardholder[:BillingAddress] = {}
224
+ cardholder[:BillingAddress][:City] = address[:city]
225
+ cardholder[:BillingAddress][:Country] = address[:country]
226
+ cardholder[:BillingAddress][:Line1] = address[:address1]
227
+ cardholder[:BillingAddress][:Line2] = address[:address2]
228
+ cardholder[:BillingAddress][:PostalCode] = address[:zip]
229
+ cardholder[:BillingAddress][:State] = address[:state]
230
+ end
231
+
232
+ def parse(body)
233
+ return {} if body == ''
234
+
235
+ JSON.parse(body)
236
+ end
237
+
238
+ def build_url(action, base)
239
+ url = base
240
+ url += action if APPENDED_URLS.any? { |key| action.include?(key) }
241
+ url
242
+ end
243
+
244
+ def get_authorization_from_url(url)
245
+ url.split('/')[1]
246
+ end
247
+
248
+ def reorder_amount_fields(response)
249
+ return response unless response['amount']
250
+
251
+ amount_obj = response['amount']
252
+ response['amount'] = amount_obj['total'].to_i if amount_obj['total']
253
+ response['currency'] = amount_obj['currency'] if amount_obj['currency']
254
+ response['amount_details'] = amount_obj['details'] if amount_obj['details']
255
+ response
256
+ end
257
+
258
+ def commit(action, parameters, options = {})
259
+ base_url = (test? ? test_url : live_url)
260
+ url = build_url(action, base_url)
261
+ response = parse(ssl_post(url, parameters.to_json, header(options)))
262
+ response = reorder_amount_fields(response) if AMOUNT_IN_RESPONSE.include?(action)
263
+
264
+ Response.new(
265
+ success_from(response),
266
+ message_from(response),
267
+ response,
268
+ authorization: authorization_from(response, action),
269
+ test: test?,
270
+ error_code: error_code_from(response)
271
+ )
272
+ end
273
+
274
+ def handle_response(response)
275
+ case response.code.to_i
276
+ when 200...300, 400, 401
277
+ response.body
278
+ else
279
+ raise ResponseError.new(response)
280
+ end
281
+ end
282
+
283
+ def success_from(response)
284
+ APPROVED_STATUS.include?(response['status'])
285
+ end
286
+
287
+ def message_from(response)
288
+ response = response['transactions']&.first if response['transactions']&.is_a?(Array)
289
+ response['resultMessage'] || response['message']
290
+ end
291
+
292
+ def authorization_from(response, action = nil)
293
+ if action.include?('captures')
294
+ get_authorization_from_url(action)
295
+ else
296
+ response['id']
297
+ end
298
+ end
299
+
300
+ def error_code_from(response)
301
+ return if success_from(response)
302
+
303
+ response = response['transactions']&.first if response['transactions']&.is_a?(Array)
304
+ response['resultCode'] || response['status']
305
+ end
306
+ end
307
+ end
308
+ end
@@ -27,12 +27,12 @@ module ActiveMerchant #:nodoc:
27
27
  self.display_name = 'Priority'
28
28
 
29
29
  def initialize(options = {})
30
- requires!(options, :merchant_id, :key, :secret)
30
+ requires!(options, :merchant_id, :api_key, :secret)
31
31
  super
32
32
  end
33
33
 
34
34
  def basic_auth
35
- Base64.strict_encode64("#{@options[:key]}:#{@options[:secret]}")
35
+ Base64.strict_encode64("#{@options[:api_key]}:#{@options[:secret]}")
36
36
  end
37
37
 
38
38
  def request_headers
@@ -72,6 +72,18 @@ module ActiveMerchant #:nodoc:
72
72
  commit('purchase', params: params)
73
73
  end
74
74
 
75
+ def credit(amount, credit_card, options = {})
76
+ params = {}
77
+ params['authOnly'] = false
78
+ params['isSettleFunds'] = true
79
+ amount = -amount
80
+
81
+ add_merchant_id(params)
82
+ add_amount(params, amount, options)
83
+ add_credit_params(params, credit_card, options)
84
+ commit('credit', params: params)
85
+ end
86
+
75
87
  def refund(amount, authorization, options = {})
76
88
  params = {}
77
89
  add_merchant_id(params)
@@ -147,6 +159,12 @@ module ActiveMerchant #:nodoc:
147
159
  add_additional_data(params, options)
148
160
  end
149
161
 
162
+ def add_credit_params(params, credit_card, options)
163
+ add_replay_id(params, options)
164
+ add_credit_card(params, credit_card, 'purchase', options)
165
+ add_additional_data(params, options)
166
+ end
167
+
150
168
  def add_replay_id(params, options)
151
169
  params['replayId'] = options[:replay_id] if options[:replay_id]
152
170
  end
@@ -157,13 +175,12 @@ module ActiveMerchant #:nodoc:
157
175
  card_details = {}
158
176
  card_details['expiryMonth'] = format(credit_card.month, :two_digits).to_s
159
177
  card_details['expiryYear'] = format(credit_card.year, :two_digits).to_s
160
- card_details['expiryDate'] = exp_date(credit_card)
161
178
  card_details['cardType'] = credit_card.brand
162
179
  card_details['last4'] = credit_card.last_digits
163
- card_details['cvv'] = credit_card.verification_value
180
+ card_details['cvv'] = credit_card.verification_value unless credit_card.verification_value.nil?
164
181
  card_details['number'] = credit_card.number
165
182
  card_details['avsStreet'] = options[:billing_address][:address1] if options[:billing_address]
166
- card_details['avsZip'] = options[:billing_address][:zip] if options[:billing_address]
183
+ card_details['avsZip'] = options[:billing_address][:zip] if !options[:billing_address].nil? && !options[:billing_address][:zip].nil?
167
184
 
168
185
  params['cardAccount'] = card_details
169
186
  end
@@ -181,11 +198,17 @@ module ActiveMerchant #:nodoc:
181
198
  params['shouldGetCreditCardLevel'] = options[:should_get_credit_card_level] if options[:should_get_credit_card_level]
182
199
  params['source'] = options[:source] if options[:source]
183
200
  params['invoice'] = options[:invoice] if options[:invoice]
201
+ params['isTicket'] = options[:is_ticket] if options[:is_ticket]
202
+ params['shouldVaultCard'] = options[:should_vault_card] if options[:should_vault_card]
203
+ params['sourceZip'] = options[:source_zip] if options[:source_zip]
204
+ params['authCode'] = options[:auth_code] if options[:auth_code]
205
+ params['achIndicator'] = options[:ach_indicator] if options[:ach_indicator]
206
+ params['bankAccount'] = options[:bank_account] if options[:bank_account]
207
+ params['meta'] = options[:meta] if options[:meta]
184
208
  end
185
209
 
186
210
  def add_pos_data(params, options)
187
211
  pos_data = {}
188
-
189
212
  pos_data['cardholderPresence'] = options.dig(:pos_data, :cardholder_presence) || 'Ecom'
190
213
  pos_data['deviceAttendance'] = options.dig(:pos_data, :device_attendance) || 'HomePc'
191
214
  pos_data['deviceInputCapability'] = options.dig(:pos_data, :device_input_capability) || 'Unknown'
@@ -4,13 +4,15 @@ module ActiveMerchant #:nodoc:
4
4
  self.test_url = 'https://sandboxapi.rapyd.net/v1/'
5
5
  self.live_url = 'https://api.rapyd.net/v1/'
6
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)
7
+ self.supported_countries = %w(CA CL CO DO SV 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
8
  self.default_currency = 'USD'
9
9
  self.supported_cardtypes = %i[visa master american_express discover]
10
10
 
11
11
  self.homepage_url = 'https://www.rapyd.net/'
12
12
  self.display_name = 'Rapyd Gateway'
13
13
 
14
+ USA_PAYMENT_METHODS = %w[us_debit_discover_card us_debit_mastercard_card us_debit_visa_card us_ach_bank]
15
+
14
16
  STANDARD_ERROR_CODE_MAPPING = {}
15
17
 
16
18
  def initialize(options = {})
@@ -20,63 +22,58 @@ module ActiveMerchant #:nodoc:
20
22
 
21
23
  def purchase(money, payment, options = {})
22
24
  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
25
+ add_auth_purchase(post, money, payment, options)
26
+ post[:capture] = true unless payment.is_a?(Check)
27
+
28
+ commit(:post, 'payments', post)
41
29
  end
42
30
 
43
31
  def authorize(money, payment, options = {})
44
32
  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
33
+ add_auth_purchase(post, money, payment, options)
34
+ post[:capture] = false unless payment.is_a?(Check)
35
+
51
36
  commit(:post, 'payments', post)
52
37
  end
53
38
 
54
39
  def capture(money, authorization, options = {})
55
40
  post = {}
56
- commit(:post, "payments/#{authorization}/capture", post)
41
+ commit(:post, "payments/#{add_reference(authorization)}/capture", post)
57
42
  end
58
43
 
59
44
  def refund(money, authorization, options = {})
60
45
  post = {}
61
- post[:payment] = authorization
46
+ post[:payment] = add_reference(authorization)
62
47
  add_invoice(post, money, options)
63
48
  add_metadata(post, options)
49
+ add_ewallet(post, options)
50
+
64
51
  commit(:post, 'refunds', post)
65
52
  end
66
53
 
67
54
  def void(authorization, options = {})
68
55
  post = {}
69
- commit(:delete, "payments/#{authorization}", post)
56
+ commit(:delete, "payments/#{add_reference(authorization)}", post)
70
57
  end
71
58
 
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
59
  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
60
+ authorize(0, credit_card, options)
61
+ end
62
+
63
+ def store(payment, options = {})
64
+ post = {}
65
+ add_payment(post, payment, options)
66
+ add_customer_object(post, payment, options)
67
+ add_metadata(post, options)
68
+ add_ewallet(post, options)
69
+ add_payment_fields(post, options)
70
+ add_payment_urls(post, options)
71
+ add_address(post, payment, options)
72
+ commit(:post, 'customers', post)
73
+ end
74
+
75
+ def unstore(customer)
76
+ commit(:delete, "customers/#{add_reference(customer)}", {})
80
77
  end
81
78
 
82
79
  def supports_scrubbing?
@@ -87,25 +84,34 @@ module ActiveMerchant #:nodoc:
87
84
  transcript.
88
85
  gsub(%r((Access_key: )\w+), '\1[FILTERED]').
89
86
  gsub(%r(("number\\?":\\?")\d+), '\1[FILTERED]').
87
+ gsub(%r(("account_number\\?":\\?")\d+), '\1[FILTERED]').
88
+ gsub(%r(("routing_number\\?":\\?")\d+), '\1[FILTERED]').
90
89
  gsub(%r(("cvv\\?":\\?")\d+), '\1[FILTERED]')
91
90
  end
92
91
 
93
92
  private
94
93
 
95
- def payment_is_ach?(options)
96
- return unless options[:pm_type]
94
+ def add_reference(authorization)
95
+ return unless authorization
97
96
 
98
- return true if options[:pm_type].include?('_bank')
97
+ authorization.split('|')[0]
99
98
  end
100
99
 
101
- def payment_is_card?(options)
102
- return unless options[:pm_type]
103
-
104
- return true if options[:pm_type].include?('_card')
100
+ def add_auth_purchase(post, money, payment, options)
101
+ add_invoice(post, money, options)
102
+ add_payment(post, payment, options)
103
+ add_customer_object(post, payment, options)
104
+ add_3ds(post, payment, options)
105
+ add_address(post, payment, options)
106
+ add_metadata(post, options)
107
+ add_ewallet(post, options)
108
+ add_payment_fields(post, options)
109
+ add_payment_urls(post, options)
110
+ add_customer_id(post, options)
105
111
  end
106
112
 
107
113
  def add_address(post, creditcard, options)
108
- return unless address = options[:address]
114
+ return unless address = options[:billing_address]
109
115
 
110
116
  post[:address] = {}
111
117
  # name and line_1 are required at the gateway
@@ -116,22 +122,31 @@ module ActiveMerchant #:nodoc:
116
122
  post[:address][:state] = address[:state] if address[:state]
117
123
  post[:address][:country] = address[:country] if address[:country]
118
124
  post[:address][:zip] = address[:zip] if address[:zip]
119
- post[:address][:phone_number] = address[:phone] if address[:phone]
125
+ post[:address][:phone_number] = address[:phone].gsub(/\D/, '') if address[:phone]
120
126
  end
121
127
 
122
128
  def add_invoice(post, money, options)
123
- post[:amount] = amount(money).to_f.to_s
129
+ post[:amount] = money.zero? ? 0 : amount(money).to_f.to_s
124
130
  post[:currency] = (options[:currency] || currency(money))
125
131
  end
126
132
 
127
133
  def add_payment(post, payment, options)
128
- if payment_is_card?(options)
134
+ if payment.is_a?(CreditCard)
129
135
  add_creditcard(post, payment, options)
130
- elsif payment_is_ach?(options)
136
+ elsif payment.is_a?(Check)
131
137
  add_ach(post, payment, options)
138
+ else
139
+ add_token(post, payment, options)
132
140
  end
133
141
  end
134
142
 
143
+ def add_stored_credential(post, options)
144
+ return unless stored_credential = options[:stored_credential]
145
+
146
+ post[:payment_method][:fields][:network_reference_id] = stored_credential[:network_transaction_id] if stored_credential[:network_transaction_id]
147
+ post[:initiation_type] = stored_credential[:reason_type] if stored_credential[:reason_type]
148
+ end
149
+
135
150
  def add_creditcard(post, payment, options)
136
151
  post[:payment_method] = {}
137
152
  post[:payment_method][:fields] = {}
@@ -141,8 +156,10 @@ module ActiveMerchant #:nodoc:
141
156
  pm_fields[:number] = payment.number
142
157
  pm_fields[:expiration_month] = payment.month.to_s
143
158
  pm_fields[:expiration_year] = payment.year.to_s
144
- pm_fields[:cvv] = payment.verification_value.to_s
145
159
  pm_fields[:name] = "#{payment.first_name} #{payment.last_name}"
160
+ pm_fields[:cvv] = payment.verification_value.to_s
161
+
162
+ add_stored_credential(post, options)
146
163
  end
147
164
 
148
165
  def add_ach(post, payment, options)
@@ -158,6 +175,24 @@ module ActiveMerchant #:nodoc:
158
175
  post[:payment_method][:fields][:payment_purpose] = options[:payment_purpose] if options[:payment_purpose]
159
176
  end
160
177
 
178
+ def add_token(post, payment, options)
179
+ return unless token = payment.split('|')[1]
180
+
181
+ post[:payment_method] = token
182
+ end
183
+
184
+ def add_3ds(post, payment, options)
185
+ return unless three_d_secure = options[:three_d_secure]
186
+
187
+ post[:payment_method_options] = {}
188
+ post[:payment_method_options]['3d_required'] = three_d_secure[:required]
189
+ post[:payment_method_options]['3d_version'] = three_d_secure[:version]
190
+ post[:payment_method_options][:cavv] = three_d_secure[:cavv]
191
+ post[:payment_method_options][:eci] = three_d_secure[:eci]
192
+ post[:payment_method_options][:xid] = three_d_secure[:xid]
193
+ post[:payment_method_options][:ds_trans_id] = three_d_secure[:ds_transaction_id]
194
+ end
195
+
161
196
  def add_metadata(post, options)
162
197
  post[:metadata] = options[:metadata] if options[:metadata]
163
198
  end
@@ -166,6 +201,30 @@ module ActiveMerchant #:nodoc:
166
201
  post[:ewallet_id] = options[:ewallet_id] if options[:ewallet_id]
167
202
  end
168
203
 
204
+ def add_payment_fields(post, options)
205
+ post[:payment] = {}
206
+
207
+ post[:payment][:description] = options[:description] if options[:description]
208
+ post[:payment][:statement_descriptor] = options[:statement_descriptor] if options[:statement_descriptor]
209
+ end
210
+
211
+ def add_payment_urls(post, options)
212
+ post[:complete_payment_url] = options[:complete_payment_url] if options[:complete_payment_url]
213
+ post[:error_payment_url] = options[:error_payment_url] if options[:error_payment_url]
214
+ end
215
+
216
+ def add_customer_object(post, payment, options)
217
+ post[:name] = "#{payment.first_name} #{payment.last_name}" unless payment.is_a?(String)
218
+ phone = options.dig(:billing_address, :phone) .gsub(/\D/, '') unless options[:billing_address].nil?
219
+ post[:phone_number] = phone || options.dig(:customer, :phone_number)
220
+ post[:email] = options[:email] || options.dig(:customer, :email)
221
+ post[:addresses] = options.dig(:customer, :addresses) if USA_PAYMENT_METHODS.include?(options[:pm_type])
222
+ end
223
+
224
+ def add_customer_id(post, options)
225
+ post[:customer] = options[:customer_id] if options[:customer_id]
226
+ end
227
+
169
228
  def parse(body)
170
229
  return {} if body.empty? || body.nil?
171
230
 
@@ -238,7 +297,9 @@ module ActiveMerchant #:nodoc:
238
297
  end
239
298
 
240
299
  def authorization_from(response)
241
- response.dig('data') ? response.dig('data', 'id') : response.dig('status', 'operation_id')
300
+ id = response.dig('data') ? response.dig('data', 'id') : response.dig('status', 'operation_id')
301
+
302
+ "#{id}|#{response.dig('data', 'default_payment_method')}"
242
303
  end
243
304
 
244
305
  def error_code_from(response)