activemerchant 1.126.0 → 1.129.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 (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)