activemerchant 1.133.0 → 1.137.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 (183) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +240 -0
  3. data/lib/active_merchant/billing/check.rb +2 -2
  4. data/lib/active_merchant/billing/compatibility.rb +4 -4
  5. data/lib/active_merchant/billing/credit_card.rb +11 -8
  6. data/lib/active_merchant/billing/credit_card_formatting.rb +4 -0
  7. data/lib/active_merchant/billing/credit_card_methods.rb +59 -6
  8. data/lib/active_merchant/billing/gateway.rb +9 -0
  9. data/lib/active_merchant/billing/gateways/adyen.rb +162 -43
  10. data/lib/active_merchant/billing/gateways/airwallex.rb +26 -12
  11. data/lib/active_merchant/billing/gateways/alelo.rb +23 -5
  12. data/lib/active_merchant/billing/gateways/authorize_net.rb +43 -35
  13. data/lib/active_merchant/billing/gateways/authorize_net_arb.rb +10 -6
  14. data/lib/active_merchant/billing/gateways/authorize_net_cim.rb +1 -3
  15. data/lib/active_merchant/billing/gateways/axcessms.rb +6 -2
  16. data/lib/active_merchant/billing/gateways/banwire.rb +4 -2
  17. data/lib/active_merchant/billing/gateways/beanstream/beanstream_core.rb +7 -3
  18. data/lib/active_merchant/billing/gateways/blue_pay.rb +13 -5
  19. data/lib/active_merchant/billing/gateways/blue_snap.rb +5 -5
  20. data/lib/active_merchant/billing/gateways/braintree/token_nonce.rb +65 -20
  21. data/lib/active_merchant/billing/gateways/braintree_blue.rb +226 -73
  22. data/lib/active_merchant/billing/gateways/braintree_orange.rb +1 -1
  23. data/lib/active_merchant/billing/gateways/card_connect.rb +5 -2
  24. data/lib/active_merchant/billing/gateways/card_stream.rb +4 -6
  25. data/lib/active_merchant/billing/gateways/cashnet.rb +1 -1
  26. data/lib/active_merchant/billing/gateways/cecabank/cecabank_common.rb +36 -0
  27. data/lib/active_merchant/billing/gateways/cecabank/cecabank_json.rb +316 -0
  28. data/lib/active_merchant/billing/gateways/cecabank/cecabank_xml.rb +220 -0
  29. data/lib/active_merchant/billing/gateways/cecabank.rb +7 -240
  30. data/lib/active_merchant/billing/gateways/checkout_v2.rb +238 -34
  31. data/lib/active_merchant/billing/gateways/commerce_hub.rb +63 -6
  32. data/lib/active_merchant/billing/gateways/credorax.rb +3 -5
  33. data/lib/active_merchant/billing/gateways/cyber_source.rb +185 -47
  34. data/lib/active_merchant/billing/gateways/cyber_source_rest.rb +102 -58
  35. data/lib/active_merchant/billing/gateways/d_local.rb +26 -15
  36. data/lib/active_merchant/billing/gateways/data_cash.rb +21 -17
  37. data/lib/active_merchant/billing/gateways/datatrans.rb +279 -0
  38. data/lib/active_merchant/billing/gateways/decidir.rb +53 -18
  39. data/lib/active_merchant/billing/gateways/decidir_plus.rb +4 -1
  40. data/lib/active_merchant/billing/gateways/deepstack.rb +382 -0
  41. data/lib/active_merchant/billing/gateways/ebanx.rb +40 -36
  42. data/lib/active_merchant/billing/gateways/efsnet.rb +6 -2
  43. data/lib/active_merchant/billing/gateways/elavon.rb +99 -33
  44. data/lib/active_merchant/billing/gateways/element.rb +36 -7
  45. data/lib/active_merchant/billing/gateways/epay.rb +6 -2
  46. data/lib/active_merchant/billing/gateways/evo_ca.rb +6 -2
  47. data/lib/active_merchant/billing/gateways/eway.rb +4 -2
  48. data/lib/active_merchant/billing/gateways/eway_managed.rb +6 -2
  49. data/lib/active_merchant/billing/gateways/exact.rb +6 -2
  50. data/lib/active_merchant/billing/gateways/fat_zebra.rb +31 -3
  51. data/lib/active_merchant/billing/gateways/federated_canada.rb +6 -2
  52. data/lib/active_merchant/billing/gateways/first_pay/first_pay_common.rb +15 -0
  53. data/lib/active_merchant/billing/gateways/first_pay/first_pay_json.rb +190 -0
  54. data/lib/active_merchant/billing/gateways/first_pay/first_pay_xml.rb +183 -0
  55. data/lib/active_merchant/billing/gateways/first_pay.rb +6 -172
  56. data/lib/active_merchant/billing/gateways/firstdata_e4.rb +6 -2
  57. data/lib/active_merchant/billing/gateways/firstdata_e4_v27.rb +7 -3
  58. data/lib/active_merchant/billing/gateways/flex_charge.rb +347 -0
  59. data/lib/active_merchant/billing/gateways/garanti.rb +4 -2
  60. data/lib/active_merchant/billing/gateways/global_collect.rb +45 -37
  61. data/lib/active_merchant/billing/gateways/hi_pay.rb +286 -0
  62. data/lib/active_merchant/billing/gateways/hps.rb +1 -1
  63. data/lib/active_merchant/billing/gateways/iats_payments.rb +7 -2
  64. data/lib/active_merchant/billing/gateways/inspire.rb +6 -4
  65. data/lib/active_merchant/billing/gateways/instapay.rb +7 -4
  66. data/lib/active_merchant/billing/gateways/ipg.rb +9 -5
  67. data/lib/active_merchant/billing/gateways/iridium.rb +15 -5
  68. data/lib/active_merchant/billing/gateways/itransact.rb +6 -2
  69. data/lib/active_merchant/billing/gateways/iveri.rb +3 -3
  70. data/lib/active_merchant/billing/gateways/ixopay.rb +2 -2
  71. data/lib/active_merchant/billing/gateways/jetpay.rb +4 -2
  72. data/lib/active_merchant/billing/gateways/jetpay_v2.rb +4 -2
  73. data/lib/active_merchant/billing/gateways/kushki.rb +72 -12
  74. data/lib/active_merchant/billing/gateways/linkpoint.rb +6 -2
  75. data/lib/active_merchant/billing/gateways/litle.rb +33 -50
  76. data/lib/active_merchant/billing/gateways/mastercard.rb +4 -4
  77. data/lib/active_merchant/billing/gateways/maxipago.rb +2 -2
  78. data/lib/active_merchant/billing/gateways/merchant_e_solutions.rb +8 -5
  79. data/lib/active_merchant/billing/gateways/merchant_ware.rb +11 -4
  80. data/lib/active_merchant/billing/gateways/merchant_ware_version_four.rb +11 -4
  81. data/lib/active_merchant/billing/gateways/merchant_warrior.rb +19 -3
  82. data/lib/active_merchant/billing/gateways/mercury.rb +6 -2
  83. data/lib/active_merchant/billing/gateways/metrics_global.rb +8 -6
  84. data/lib/active_merchant/billing/gateways/migs/migs_codes.rb +1 -0
  85. data/lib/active_merchant/billing/gateways/migs.rb +6 -2
  86. data/lib/active_merchant/billing/gateways/mit.rb +8 -3
  87. data/lib/active_merchant/billing/gateways/modern_payments_cim.rb +18 -10
  88. data/lib/active_merchant/billing/gateways/monei.rb +1 -1
  89. data/lib/active_merchant/billing/gateways/moneris.rb +9 -3
  90. data/lib/active_merchant/billing/gateways/money_movers.rb +6 -2
  91. data/lib/active_merchant/billing/gateways/nab_transact.rb +12 -4
  92. data/lib/active_merchant/billing/gateways/net_registry.rb +6 -2
  93. data/lib/active_merchant/billing/gateways/netbanx.rb +1 -3
  94. data/lib/active_merchant/billing/gateways/netbilling.rb +6 -2
  95. data/lib/active_merchant/billing/gateways/network_merchants.rb +6 -2
  96. data/lib/active_merchant/billing/gateways/nmi.rb +18 -6
  97. data/lib/active_merchant/billing/gateways/ogone.rb +6 -2
  98. data/lib/active_merchant/billing/gateways/openpay.rb +4 -2
  99. data/lib/active_merchant/billing/gateways/opp.rb +1 -2
  100. data/lib/active_merchant/billing/gateways/optimal_payment.rb +6 -2
  101. data/lib/active_merchant/billing/gateways/orbital/orbital_soft_descriptors.rb +1 -3
  102. data/lib/active_merchant/billing/gateways/orbital.rb +83 -24
  103. data/lib/active_merchant/billing/gateways/pac_net_raven.rb +7 -4
  104. data/lib/active_merchant/billing/gateways/pay_gate_xml.rb +6 -2
  105. data/lib/active_merchant/billing/gateways/pay_hub.rb +4 -2
  106. data/lib/active_merchant/billing/gateways/pay_junction.rb +6 -2
  107. data/lib/active_merchant/billing/gateways/pay_secure.rb +6 -2
  108. data/lib/active_merchant/billing/gateways/pay_trace.rb +31 -18
  109. data/lib/active_merchant/billing/gateways/payeezy.rb +19 -8
  110. data/lib/active_merchant/billing/gateways/payex.rb +4 -2
  111. data/lib/active_merchant/billing/gateways/payflow/payflow_common_api.rb +1 -1
  112. data/lib/active_merchant/billing/gateways/payflow.rb +1 -3
  113. data/lib/active_merchant/billing/gateways/payment_express.rb +8 -4
  114. data/lib/active_merchant/billing/gateways/paymentez.rb +23 -11
  115. data/lib/active_merchant/billing/gateways/paysafe.rb +12 -11
  116. data/lib/active_merchant/billing/gateways/payscout.rb +7 -4
  117. data/lib/active_merchant/billing/gateways/paystation.rb +7 -3
  118. data/lib/active_merchant/billing/gateways/payway.rb +6 -2
  119. data/lib/active_merchant/billing/gateways/payway_dot_com.rb +2 -2
  120. data/lib/active_merchant/billing/gateways/pin.rb +22 -4
  121. data/lib/active_merchant/billing/gateways/plexo.rb +49 -10
  122. data/lib/active_merchant/billing/gateways/plugnpay.rb +6 -2
  123. data/lib/active_merchant/billing/gateways/priority.rb +6 -5
  124. data/lib/active_merchant/billing/gateways/psigate.rb +6 -2
  125. data/lib/active_merchant/billing/gateways/psl_card.rb +6 -2
  126. data/lib/active_merchant/billing/gateways/qbms.rb +6 -2
  127. data/lib/active_merchant/billing/gateways/quantum.rb +6 -2
  128. data/lib/active_merchant/billing/gateways/quickbooks.rb +6 -5
  129. data/lib/active_merchant/billing/gateways/quickpay/quickpay_v10.rb +7 -4
  130. data/lib/active_merchant/billing/gateways/quickpay/quickpay_v4to7.rb +6 -2
  131. data/lib/active_merchant/billing/gateways/rapyd.rb +148 -46
  132. data/lib/active_merchant/billing/gateways/reach.rb +11 -4
  133. data/lib/active_merchant/billing/gateways/redsys.rb +2 -10
  134. data/lib/active_merchant/billing/gateways/redsys_rest.rb +507 -0
  135. data/lib/active_merchant/billing/gateways/s5.rb +3 -3
  136. data/lib/active_merchant/billing/gateways/safe_charge.rb +36 -16
  137. data/lib/active_merchant/billing/gateways/sage.rb +12 -4
  138. data/lib/active_merchant/billing/gateways/sage_pay.rb +79 -5
  139. data/lib/active_merchant/billing/gateways/sallie_mae.rb +6 -2
  140. data/lib/active_merchant/billing/gateways/secure_net.rb +6 -2
  141. data/lib/active_merchant/billing/gateways/secure_pay.rb +8 -6
  142. data/lib/active_merchant/billing/gateways/secure_pay_au.rb +12 -4
  143. data/lib/active_merchant/billing/gateways/secure_pay_tech.rb +6 -2
  144. data/lib/active_merchant/billing/gateways/securion_pay.rb +24 -10
  145. data/lib/active_merchant/billing/gateways/shift4.rb +17 -20
  146. data/lib/active_merchant/billing/gateways/shift4_v2.rb +117 -0
  147. data/lib/active_merchant/billing/gateways/simetrik.rb +17 -11
  148. data/lib/active_merchant/billing/gateways/skip_jack.rb +6 -2
  149. data/lib/active_merchant/billing/gateways/smart_ps.rb +7 -4
  150. data/lib/active_merchant/billing/gateways/so_easy_pay.rb +4 -2
  151. data/lib/active_merchant/billing/gateways/spreedly_core.rb +2 -4
  152. data/lib/active_merchant/billing/gateways/stripe.rb +53 -21
  153. data/lib/active_merchant/billing/gateways/stripe_payment_intents.rb +199 -50
  154. data/lib/active_merchant/billing/gateways/sum_up.rb +223 -0
  155. data/lib/active_merchant/billing/gateways/swipe_checkout.rb +4 -2
  156. data/lib/active_merchant/billing/gateways/telr.rb +3 -4
  157. data/lib/active_merchant/billing/gateways/trans_first.rb +1 -2
  158. data/lib/active_merchant/billing/gateways/trans_first_transaction_express.rb +8 -16
  159. data/lib/active_merchant/billing/gateways/transact_pro.rb +1 -1
  160. data/lib/active_merchant/billing/gateways/trust_commerce.rb +6 -2
  161. data/lib/active_merchant/billing/gateways/usa_epay_advanced.rb +9 -8
  162. data/lib/active_merchant/billing/gateways/usa_epay_transaction.rb +6 -2
  163. data/lib/active_merchant/billing/gateways/vanco.rb +2 -4
  164. data/lib/active_merchant/billing/gateways/vantiv_express.rb +587 -0
  165. data/lib/active_merchant/billing/gateways/verifi.rb +6 -2
  166. data/lib/active_merchant/billing/gateways/viaklix.rb +6 -2
  167. data/lib/active_merchant/billing/gateways/visanet_peru.rb +2 -2
  168. data/lib/active_merchant/billing/gateways/vpos.rb +3 -3
  169. data/lib/active_merchant/billing/gateways/wirecard.rb +7 -3
  170. data/lib/active_merchant/billing/gateways/wompi.rb +5 -0
  171. data/lib/active_merchant/billing/gateways/worldpay.rb +140 -73
  172. data/lib/active_merchant/billing/gateways/worldpay_online_payments.rb +13 -10
  173. data/lib/active_merchant/billing/gateways/xpay.rb +242 -0
  174. data/lib/active_merchant/billing/network_tokenization_credit_card.rb +1 -1
  175. data/lib/active_merchant/billing/response.rb +2 -2
  176. data/lib/active_merchant/connection.rb +3 -17
  177. data/lib/active_merchant/country.rb +1 -0
  178. data/lib/active_merchant/errors.rb +10 -0
  179. data/lib/active_merchant/version.rb +1 -1
  180. data/lib/support/gateway_support.rb +2 -2
  181. data/lib/support/ssl_verify.rb +4 -4
  182. data/lib/support/ssl_version.rb +6 -6
  183. metadata +30 -9
@@ -6,8 +6,8 @@ module ActiveMerchant #:nodoc:
6
6
 
7
7
  class_attribute :simulator_url
8
8
 
9
- self.test_url = 'https://test.sagepay.com/gateway/service'
10
- self.live_url = 'https://live.sagepay.com/gateway/service'
9
+ self.test_url = 'https://sandbox.opayo.eu.elavon.com/gateway/service'
10
+ self.live_url = 'https://live.opayo.eu.elavon.com/gateway/service'
11
11
  self.simulator_url = 'https://test.sagepay.com/Simulator'
12
12
 
13
13
  APPROVED = 'OK'
@@ -78,6 +78,7 @@ module ActiveMerchant #:nodoc:
78
78
 
79
79
  def initialize(options = {})
80
80
  requires!(options, :login)
81
+ @protocol_version = options.fetch(:protocol_version, '3.00')
81
82
  super
82
83
  end
83
84
 
@@ -86,6 +87,9 @@ module ActiveMerchant #:nodoc:
86
87
 
87
88
  post = {}
88
89
 
90
+ add_override_protocol_version(options)
91
+ add_three_ds_data(post, options)
92
+ add_stored_credentials_data(post, options)
89
93
  add_amount(post, money, options)
90
94
  add_invoice(post, options)
91
95
  add_payment_method(post, payment_method, options)
@@ -101,6 +105,9 @@ module ActiveMerchant #:nodoc:
101
105
 
102
106
  post = {}
103
107
 
108
+ add_three_ds_data(post, options)
109
+ add_stored_credentials_data(post, options)
110
+ add_override_protocol_version(options)
104
111
  add_amount(post, money, options)
105
112
  add_invoice(post, options)
106
113
  add_payment_method(post, payment_method, options)
@@ -115,6 +122,7 @@ module ActiveMerchant #:nodoc:
115
122
  def capture(money, identification, options = {})
116
123
  post = {}
117
124
 
125
+ add_override_protocol_version(options)
118
126
  add_reference(post, identification)
119
127
  add_release_amount(post, money, options)
120
128
 
@@ -124,6 +132,7 @@ module ActiveMerchant #:nodoc:
124
132
  def void(identification, options = {})
125
133
  post = {}
126
134
 
135
+ add_override_protocol_version(options)
127
136
  add_reference(post, identification)
128
137
  action = abort_or_void_from(identification)
129
138
 
@@ -136,6 +145,7 @@ module ActiveMerchant #:nodoc:
136
145
 
137
146
  post = {}
138
147
 
148
+ add_override_protocol_version(options)
139
149
  add_related_reference(post, identification)
140
150
  add_amount(post, money, options)
141
151
  add_invoice(post, options)
@@ -150,6 +160,7 @@ module ActiveMerchant #:nodoc:
150
160
 
151
161
  def store(credit_card, options = {})
152
162
  post = {}
163
+ add_override_protocol_version(options)
153
164
  add_credit_card(post, credit_card)
154
165
  add_currency(post, 0, options)
155
166
 
@@ -158,6 +169,7 @@ module ActiveMerchant #:nodoc:
158
169
 
159
170
  def unstore(token, options = {})
160
171
  post = {}
172
+ add_override_protocol_version(options)
161
173
  add_token(post, token)
162
174
  commit(:unstore, post)
163
175
  end
@@ -182,6 +194,58 @@ module ActiveMerchant #:nodoc:
182
194
 
183
195
  private
184
196
 
197
+ def add_override_protocol_version(options)
198
+ @protocol_version = options[:protocol_version] if options[:protocol_version]
199
+ end
200
+
201
+ def add_three_ds_data(post, options)
202
+ return unless @protocol_version == '4.00'
203
+ return unless three_ds_2_options = options[:three_ds_2]
204
+
205
+ add_pair(post, :ThreeDSNotificationURL, three_ds_2_options[:notification_url])
206
+ return unless three_ds_2_options[:browser_info]
207
+
208
+ add_browser_info(post, three_ds_2_options[:browser_info])
209
+ end
210
+
211
+ def add_browser_info(post, browser_info)
212
+ add_pair(post, :BrowserAcceptHeader, browser_info[:accept_header])
213
+ add_pair(post, :BrowserColorDepth, browser_info[:depth])
214
+ add_pair(post, :BrowserJavascriptEnabled, format_boolean(browser_info[:java]))
215
+ add_pair(post, :BrowserJavaEnabled, format_boolean(browser_info[:java]))
216
+ add_pair(post, :BrowserLanguage, browser_info[:language])
217
+ add_pair(post, :BrowserScreenHeight, browser_info[:height])
218
+ add_pair(post, :BrowserScreenWidth, browser_info[:width])
219
+ add_pair(post, :BrowserTZ, browser_info[:timezone])
220
+ add_pair(post, :BrowserUserAgent, browser_info[:user_agent])
221
+ add_pair(post, :ChallengeWindowSize, browser_info[:browser_size])
222
+ end
223
+
224
+ def add_stored_credentials_data(post, options)
225
+ return unless @protocol_version == '4.00'
226
+ return unless stored_credential = options[:stored_credential]
227
+
228
+ initiator = stored_credential[:initiator] == 'cardholder' ? 'CIT' : 'MIT'
229
+ cof_usage = if stored_credential[:initial_transaction] && initiator == 'CIT'
230
+ 'FIRST'
231
+ elsif !stored_credential[:initial_transaction] && initiator == 'MIT'
232
+ 'SUBSEQUENT'
233
+ end
234
+
235
+ add_pair(post, :COFUsage, cof_usage) if cof_usage
236
+ add_pair(post, :InitiatedTYPE, initiator)
237
+ add_pair(post, :SchemeTraceID, stored_credential[:network_transaction_id]) if stored_credential[:network_transaction_id]
238
+
239
+ reasoning = stored_credential[:reason_type] == 'installment' ? 'instalment' : stored_credential[:reason_type]
240
+ add_pair(post, :MITType, reasoning.upcase)
241
+
242
+ if %w(instalment recurring).any?(reasoning)
243
+ add_pair(post, :RecurringExpiry, options[:recurring_expiry])
244
+ add_pair(post, :RecurringFrequency, options[:recurring_frequency])
245
+ add_pair(post, :PurchaseInstalData, options[:installment_data])
246
+ end
247
+ end
248
+
185
249
  def truncate(value, max_size)
186
250
  return nil unless value
187
251
  return value.to_s if CGI.escape(value.to_s).length <= max_size
@@ -346,14 +410,18 @@ module ActiveMerchant #:nodoc:
346
410
  def commit(action, parameters)
347
411
  response = parse(ssl_post(url_for(action), post_data(action, parameters)))
348
412
 
349
- Response.new(response['Status'] == APPROVED, message_from(response), response,
413
+ Response.new(
414
+ response['Status'] == APPROVED,
415
+ message_from(response),
416
+ response,
350
417
  test: test?,
351
418
  authorization: authorization_from(response, parameters, action),
352
419
  avs_result: {
353
420
  street_match: AVS_CODE[response['AddressResult']],
354
421
  postal_match: AVS_CODE[response['PostCodeResult']]
355
422
  },
356
- cvv_result: CVV_CODE[response['CV2Result']])
423
+ cvv_result: CVV_CODE[response['CV2Result']]
424
+ )
357
425
  end
358
426
 
359
427
  def authorization_from(response, params, action)
@@ -401,7 +469,7 @@ module ActiveMerchant #:nodoc:
401
469
  parameters.update(
402
470
  Vendor: @options[:login],
403
471
  TxType: TRANSACTIONS[action],
404
- VPSProtocol: @options.fetch(:protocol_version, '3.00')
472
+ VPSProtocol: @protocol_version
405
473
  )
406
474
 
407
475
  parameters.update(ReferrerID: application_id) if application_id && (application_id != Gateway.application_id)
@@ -409,6 +477,12 @@ module ActiveMerchant #:nodoc:
409
477
  parameters.collect { |key, value| "#{key}=#{CGI.escape(value.to_s)}" }.join('&')
410
478
  end
411
479
 
480
+ def format_boolean(value)
481
+ return if value.nil?
482
+
483
+ value ? '1' : '0'
484
+ end
485
+
412
486
  # SagePay returns data in the following format
413
487
  # Key1=value1
414
488
  # Key2=value2
@@ -120,9 +120,13 @@ module ActiveMerchant #:nodoc:
120
120
  end
121
121
 
122
122
  response = parse(ssl_post(self.live_url, parameters.to_post_data) || '')
123
- Response.new(successful?(response), message_from(response), response,
123
+ Response.new(
124
+ successful?(response),
125
+ message_from(response),
126
+ response,
124
127
  test: test?,
125
- authorization: response['refcode'])
128
+ authorization: response['refcode']
129
+ )
126
130
  end
127
131
 
128
132
  def successful?(response)
@@ -79,11 +79,15 @@ module ActiveMerchant #:nodoc:
79
79
  data = ssl_post(url, xml, 'Content-Type' => 'text/xml')
80
80
  response = parse(data)
81
81
 
82
- Response.new(success?(response), message_from(response), response,
82
+ Response.new(
83
+ success?(response),
84
+ message_from(response),
85
+ response,
83
86
  test: test?,
84
87
  authorization: build_authorization(response),
85
88
  avs_result: { code: response[:avs_result_code] },
86
- cvv_result: response[:card_code_response_code])
89
+ cvv_result: response[:card_code_response_code]
90
+ )
87
91
  end
88
92
 
89
93
  def build_request(request)
@@ -56,12 +56,16 @@ module ActiveMerchant #:nodoc:
56
56
 
57
57
  message = message_from(response)
58
58
 
59
- Response.new(success?(response), message, response,
59
+ Response.new(
60
+ success?(response),
61
+ message,
62
+ response,
60
63
  test: test?,
61
64
  authorization: response[:transaction_id],
62
65
  fraud_review: fraud_review?(response),
63
66
  avs_result: { code: response[:avs_result_code] },
64
- cvv_result: response[:card_code])
67
+ cvv_result: response[:card_code]
68
+ )
65
69
  end
66
70
 
67
71
  def success?(response)
@@ -75,7 +79,7 @@ module ActiveMerchant #:nodoc:
75
79
  def parse(body)
76
80
  fields = split(body)
77
81
 
78
- results = {
82
+ {
79
83
  response_code: fields[RESPONSE_CODE].to_i,
80
84
  response_reason_code: fields[RESPONSE_REASON_CODE],
81
85
  response_reason_text: fields[RESPONSE_REASON_TEXT],
@@ -85,7 +89,6 @@ module ActiveMerchant #:nodoc:
85
89
  authorization_code: fields[AUTHORIZATION_CODE],
86
90
  cardholder_authentication_code: fields[CARDHOLDER_AUTH_CODE]
87
91
  }
88
- results
89
92
  end
90
93
 
91
94
  def post_data(action, parameters = {})
@@ -101,8 +104,7 @@ module ActiveMerchant #:nodoc:
101
104
  post[:encap_char] = '$'
102
105
  post[:solution_ID] = application_id if application_id
103
106
 
104
- request = post.merge(parameters).collect { |key, value| "x_#{key}=#{CGI.escape(value.to_s)}" }.join('&')
105
- request
107
+ post.merge(parameters).collect { |key, value| "x_#{key}=#{CGI.escape(value.to_s)}" }.join('&')
106
108
  end
107
109
 
108
110
  def add_currency_code(post, money, options)
@@ -183,9 +183,13 @@ module ActiveMerchant #:nodoc:
183
183
  def commit(action, request)
184
184
  response = parse(ssl_post(test? ? self.test_url : self.live_url, build_request(action, request)))
185
185
 
186
- Response.new(success?(response), message_from(response), response,
186
+ Response.new(
187
+ success?(response),
188
+ message_from(response),
189
+ response,
187
190
  test: test?,
188
- authorization: authorization_from(response))
191
+ authorization: authorization_from(response)
192
+ )
189
193
  end
190
194
 
191
195
  def build_periodic_item(action, money, credit_card, options)
@@ -239,9 +243,13 @@ module ActiveMerchant #:nodoc:
239
243
  my_request = build_periodic_request(request)
240
244
  response = parse(ssl_post(test? ? self.test_periodic_url : self.live_periodic_url, my_request))
241
245
 
242
- Response.new(success?(response), message_from(response), response,
246
+ Response.new(
247
+ success?(response),
248
+ message_from(response),
249
+ response,
243
250
  test: test?,
244
- authorization: authorization_from(response))
251
+ authorization: authorization_from(response)
252
+ )
245
253
  end
246
254
 
247
255
  def success?(response)
@@ -84,9 +84,13 @@ module ActiveMerchant #:nodoc:
84
84
  def commit(action, post)
85
85
  response = parse(ssl_post(self.live_url, post_data(action, post)))
86
86
 
87
- Response.new(response[:result_code] == 1, message_from(response), response,
87
+ Response.new(
88
+ response[:result_code] == 1,
89
+ message_from(response),
90
+ response,
88
91
  test: test?,
89
- authorization: response[:merchant_transaction_reference])
92
+ authorization: response[:merchant_transaction_reference]
93
+ )
90
94
  end
91
95
 
92
96
  def message_from(result)
@@ -193,7 +193,8 @@ module ActiveMerchant #:nodoc:
193
193
  post[:card] = card
194
194
  add_address(post, options)
195
195
  elsif creditcard.kind_of?(String)
196
- post[:card] = creditcard
196
+ key = creditcard.match(/^pm_/) ? :paymentMethod : :card
197
+ post[key] = creditcard
197
198
  else
198
199
  raise ArgumentError.new("Unhandled payment method #{creditcard.class}.")
199
200
  end
@@ -223,24 +224,37 @@ module ActiveMerchant #:nodoc:
223
224
  end
224
225
 
225
226
  response = api_request(url, parameters, options, method)
226
- success = !response.key?('error')
227
+ success = success?(response)
227
228
 
228
- Response.new(success,
229
+ Response.new(
230
+ success,
229
231
  (success ? 'Transaction approved' : response['error']['message']),
230
232
  response,
231
233
  test: test?,
232
- authorization: (success ? response['id'] : response['error']['charge']),
233
- error_code: (success ? nil : STANDARD_ERROR_CODE_MAPPING[response['error']['code']]))
234
+ authorization: authorization_from(url, response),
235
+ error_code: (success ? nil : STANDARD_ERROR_CODE_MAPPING[response['error']['code']])
236
+ )
237
+ end
238
+
239
+ def authorization_from(action, response)
240
+ if action == 'customers' && success?(response) && response['cards'].present?
241
+ response['cards'].first['id']
242
+ else
243
+ success?(response) ? response['id'] : (response.dig('error', 'charge') || response.dig('error', 'chargeId'))
244
+ end
245
+ end
246
+
247
+ def success?(response)
248
+ !response.key?('error')
234
249
  end
235
250
 
236
251
  def headers(options = {})
237
252
  secret_key = options[:secret_key] || @options[:secret_key]
238
253
 
239
- headers = {
254
+ {
240
255
  'Authorization' => 'Basic ' + Base64.encode64(secret_key.to_s + ':').strip,
241
256
  'User-Agent' => "SecurionPay/v1 ActiveMerchantBindings/#{ActiveMerchant::VERSION}"
242
257
  }
243
- headers
244
258
  end
245
259
 
246
260
  def response_error(raw_response)
@@ -287,8 +301,8 @@ module ActiveMerchant #:nodoc:
287
301
  response
288
302
  end
289
303
 
290
- def json_error(raw_response)
291
- msg = 'Invalid response received from the SecurionPay API.'
304
+ def json_error(raw_response, gateway_name = 'SecurionPay')
305
+ msg = "Invalid response received from the #{gateway_name} API."
292
306
  msg += " (The raw response returned by the API was #{raw_response.inspect})"
293
307
  {
294
308
  'error' => {
@@ -298,7 +312,7 @@ module ActiveMerchant #:nodoc:
298
312
  end
299
313
 
300
314
  def test?
301
- (@options[:secret_key]&.include?('_test_'))
315
+ @options[:secret_key]&.include?('_test_')
302
316
  end
303
317
  end
304
318
  end
@@ -20,22 +20,6 @@ module ActiveMerchant #:nodoc:
20
20
  'add' => 'tokens',
21
21
  'verify' => 'cards'
22
22
  }
23
- STANDARD_ERROR_CODE_MAPPING = {
24
- 'incorrect_number' => STANDARD_ERROR_CODE[:incorrect_number],
25
- 'invalid_number' => STANDARD_ERROR_CODE[:invalid_number],
26
- 'invalid_expiry_month' => STANDARD_ERROR_CODE[:invalid_expiry_date],
27
- 'invalid_expiry_year' => STANDARD_ERROR_CODE[:invalid_expiry_date],
28
- 'invalid_cvc' => STANDARD_ERROR_CODE[:invalid_cvc],
29
- 'expired_card' => STANDARD_ERROR_CODE[:expired_card],
30
- 'insufficient_funds' => STANDARD_ERROR_CODE[:card_declined],
31
- 'incorrect_cvc' => STANDARD_ERROR_CODE[:incorrect_cvc],
32
- 'incorrect_zip' => STANDARD_ERROR_CODE[:incorrect_zip],
33
- 'card_declined' => STANDARD_ERROR_CODE[:card_declined],
34
- 'processing_error' => STANDARD_ERROR_CODE[:processing_error],
35
- 'lost_or_stolen' => STANDARD_ERROR_CODE[:card_declined],
36
- 'suspected_fraud' => STANDARD_ERROR_CODE[:card_declined],
37
- 'expired_token' => STANDARD_ERROR_CODE[:card_declined]
38
- }
39
23
 
40
24
  def initialize(options = {})
41
25
  requires!(options, :client_guid, :auth_token)
@@ -147,7 +131,7 @@ module ActiveMerchant #:nodoc:
147
131
  gsub(%r(("expirationDate\\?"\s*:\s*\\?")[^"]*)i, '\1[FILTERED]').
148
132
  gsub(%r(("FirstName\\?"\s*:\s*\\?")[^"]*)i, '\1[FILTERED]').
149
133
  gsub(%r(("LastName\\?"\s*:\s*\\?")[^"]*)i, '\1[FILTERED]').
150
- gsub(%r(("securityCode\\?":{\\?"[\w]+\\?":[\d]+,\\?"value\\?":\\?")[\d]*)i, '\1[FILTERED]')
134
+ gsub(%r(("securityCode\\?":{\\?"\w+\\?":\d+,\\?"value\\?":\\?")\d*)i, '\1[FILTERED]')
151
135
  end
152
136
 
153
137
  def setup_access_token
@@ -263,6 +247,7 @@ module ActiveMerchant #:nodoc:
263
247
  message_from(action, response),
264
248
  response,
265
249
  authorization: authorization_from(action, response),
250
+ avs_result: avs_result_from(response),
266
251
  test: test?,
267
252
  error_code: error_code_from(action, response)
268
253
  )
@@ -284,13 +269,25 @@ module ActiveMerchant #:nodoc:
284
269
  end
285
270
 
286
271
  def message_from(action, response)
287
- success_from(action, response) ? 'Transaction successful' : (error(response)&.dig('longText') || 'Transaction declined')
272
+ success_from(action, response) ? 'Transaction successful' : (error(response)&.dig('longText') || response['result'].first&.dig('transaction', 'hostResponse', 'reasonDescription') || 'Transaction declined')
288
273
  end
289
274
 
290
275
  def error_code_from(action, response)
291
- return unless success_from(action, response)
276
+ code = response['result'].first&.dig('transaction', 'responseCode')
277
+ primary_code = response['result'].first['error'].present?
278
+ return unless code == 'D' || primary_code == true || success_from(action, response)
279
+
280
+ if response['result'].first&.dig('transaction', 'hostResponse')
281
+ response['result'].first&.dig('transaction', 'hostResponse', 'reasonCode')
282
+ elsif response['result'].first['error']
283
+ response['result'].first&.dig('error', 'primaryCode')
284
+ else
285
+ response['result'].first&.dig('transaction', 'responseCode')
286
+ end
287
+ end
292
288
 
293
- STANDARD_ERROR_CODE_MAPPING[response['primaryCode']]
289
+ def avs_result_from(response)
290
+ AVSResult.new(code: response['result'].first&.dig('transaction', 'avs', 'result')) if response['result'].first&.dig('transaction', 'avs')
294
291
  end
295
292
 
296
293
  def authorization_from(action, response)
@@ -0,0 +1,117 @@
1
+ module ActiveMerchant #:nodoc:
2
+ module Billing #:nodoc:
3
+ class Shift4V2Gateway < SecurionPayGateway
4
+ # same endpont for testing
5
+ self.live_url = 'https://api.shift4.com/'
6
+ self.display_name = 'Shift4'
7
+ self.homepage_url = 'https://dev.shift4.com/us/'
8
+
9
+ def credit(money, payment, options = {})
10
+ post = create_post_for_auth_or_purchase(money, payment, options)
11
+ commit('credits', post, options)
12
+ end
13
+
14
+ def store(payment_method, options = {})
15
+ post = case payment_method
16
+ when CreditCard
17
+ cc = {}.tap { |card| add_creditcard(card, payment_method, options) }[:card]
18
+ options[:customer_id].blank? ? { email: options[:email], card: cc } : cc
19
+ when Check
20
+ bank_account_object(payment_method, options)
21
+ else
22
+ raise ArgumentError.new("Unhandled payment method #{payment_method.class}.")
23
+ end
24
+
25
+ commit url_for_store(payment_method, options), post, options
26
+ end
27
+
28
+ def url_for_store(payment_method, options = {})
29
+ case payment_method
30
+ when CreditCard
31
+ options[:customer_id].blank? ? 'customers' : "customers/#{options[:customer_id]}/cards"
32
+ when Check then 'payment-methods'
33
+ end
34
+ end
35
+
36
+ def unstore(reference, options = {})
37
+ commit("customers/#{options[:customer_id]}/cards/#{reference}", nil, options, :delete)
38
+ end
39
+
40
+ def create_post_for_auth_or_purchase(money, payment, options)
41
+ super.tap do |post|
42
+ add_stored_credentials(post, options)
43
+ end
44
+ end
45
+
46
+ def add_stored_credentials(post, options)
47
+ return unless options[:stored_credential].present?
48
+
49
+ initiator = options.dig(:stored_credential, :initiator)
50
+ reason_type = options.dig(:stored_credential, :reason_type)
51
+
52
+ post_type = {
53
+ %w[cardholder recurring] => 'first_recurring',
54
+ %w[merchant recurring] => 'subsequent_recurring',
55
+ %w[cardholder unscheduled] => 'customer_initiated',
56
+ %w[merchant installment] => 'merchant_initiated'
57
+ }[[initiator, reason_type]]
58
+ post[:type] = post_type if post_type
59
+ end
60
+
61
+ def headers(options = {})
62
+ super.tap do |headers|
63
+ headers['User-Agent'] = "Shift4/v2 ActiveMerchantBindings/#{ActiveMerchant::VERSION}"
64
+ end
65
+ end
66
+
67
+ def scrub(transcript)
68
+ super.
69
+ gsub(%r((card\[expMonth\]=)\d+), '\1[FILTERED]').
70
+ gsub(%r((card\[expYear\]=)\d+), '\1[FILTERED]').
71
+ gsub(%r((card\[cardholderName\]=)\w+[^ ]\w+), '\1[FILTERED]')
72
+ end
73
+
74
+ def json_error(raw_response)
75
+ super(raw_response, 'Shift4 V2')
76
+ end
77
+
78
+ def add_amount(post, money, options, include_currency = false)
79
+ super
80
+ post[:currency]&.upcase!
81
+ end
82
+
83
+ def add_creditcard(post, payment_method, options)
84
+ return super unless payment_method.is_a?(Check)
85
+
86
+ post[:paymentMethod] = bank_account_object(payment_method, options)
87
+ end
88
+
89
+ def bank_account_object(payment_method, options)
90
+ {
91
+ type: :ach,
92
+ fraudCheckData: {
93
+ ipAddress: options[:ip],
94
+ email: options[:email]
95
+ }.compact,
96
+ billing: {
97
+ name: payment_method.name,
98
+ address: { country: options.dig(:billing_address, :country) }
99
+ }.compact,
100
+ ach: {
101
+ account: {
102
+ routingNumber: payment_method.routing_number,
103
+ accountNumber: payment_method.account_number,
104
+ accountType: get_account_type(payment_method)
105
+ },
106
+ verificationProvider: :external
107
+ }
108
+ }
109
+ end
110
+
111
+ def get_account_type(check)
112
+ holder = (check.account_holder_type || '').match(/business/i) ? :corporate : :personal
113
+ "#{holder}_#{check.account_type}"
114
+ end
115
+ end
116
+ end
117
+ end
@@ -44,7 +44,7 @@ module ActiveMerchant #:nodoc:
44
44
  def initialize(options = {})
45
45
  requires!(options, :client_id, :client_secret)
46
46
  super
47
- @access_token = {}
47
+ @access_token = options[:access_token] || {}
48
48
  sign_access_token()
49
49
  end
50
50
 
@@ -280,12 +280,12 @@ module ActiveMerchant #:nodoc:
280
280
  def commit(action, parameters, url_params = {})
281
281
  begin
282
282
  response = JSON.parse ssl_post(url(action, url_params), post_data(parameters), authorized_headers())
283
- rescue ResponseError => exception
284
- case exception.response.code.to_i
283
+ rescue ResponseError => e
284
+ case e.response.code.to_i
285
285
  when 400...499
286
- response = JSON.parse exception.response.body
286
+ response = JSON.parse e.response.body
287
287
  else
288
- raise exception
288
+ raise e
289
289
  end
290
290
  end
291
291
 
@@ -318,7 +318,7 @@ module ActiveMerchant #:nodoc:
318
318
  end
319
319
 
320
320
  def url(action, url_params)
321
- "#{(test? ? test_url : live_url)}/#{url_params[:token_acquirer]}/#{action}"
321
+ "#{test? ? test_url : live_url}/#{url_params[:token_acquirer]}/#{action}"
322
322
  end
323
323
 
324
324
  def post_data(data = {})
@@ -356,12 +356,18 @@ module ActiveMerchant #:nodoc:
356
356
  login_info[:client_secret] = @options[:client_secret]
357
357
  login_info[:audience] = test? ? test_audience : live_audience
358
358
  login_info[:grant_type] = 'client_credentials'
359
- response = parse(ssl_post(auth_url(), login_info.to_json, {
360
- 'content-Type' => 'application/json'
361
- }))
362
359
 
363
- @access_token[:access_token] = response['access_token']
364
- @access_token[:expires_at] = Time.new.to_i + response['expires_in']
360
+ begin
361
+ raw_response = ssl_post(auth_url(), login_info.to_json, {
362
+ 'content-Type' => 'application/json'
363
+ })
364
+ rescue ResponseError => e
365
+ raise OAuthResponseError.new(e)
366
+ else
367
+ response = parse(raw_response)
368
+ @access_token[:access_token] = response['access_token']
369
+ @access_token[:expires_at] = Time.new.to_i + response['expires_in']
370
+ end
365
371
  end
366
372
  end
367
373
  end
@@ -263,11 +263,15 @@ module ActiveMerchant #:nodoc:
263
263
  response = parse(ssl_post(url_for(action), post_data(action, money, parameters)), action)
264
264
 
265
265
  # Pass along the original transaction id in the case an update transaction
266
- Response.new(response[:success], message_from(response, action), response,
266
+ Response.new(
267
+ response[:success],
268
+ message_from(response, action),
269
+ response,
267
270
  test: test?,
268
271
  authorization: response[:szTransactionFileName] || parameters[:szTransactionId],
269
272
  avs_result: { code: response[:szAVSResponseCode] },
270
- cvv_result: response[:szCVV2ResponseCode])
273
+ cvv_result: response[:szCVV2ResponseCode]
274
+ )
271
275
  end
272
276
 
273
277
  def url_for(action)
@@ -226,11 +226,15 @@ module ActiveMerchant #:nodoc:
226
226
  def commit(action, money, parameters)
227
227
  parameters[:amount] = localized_amount(money, parameters[:currency] || default_currency) if money
228
228
  response = parse(ssl_post(self.live_url, post_data(action, parameters)))
229
- Response.new(response['response'] == '1', message_from(response), response,
229
+ Response.new(
230
+ response['response'] == '1',
231
+ message_from(response),
232
+ response,
230
233
  authorization: (response['transactionid'] || response['customer_vault_id']),
231
234
  test: test?,
232
235
  cvv_result: response['cvvresponse'],
233
- avs_result: { code: response['avsresponse'] })
236
+ avs_result: { code: response['avsresponse'] }
237
+ )
234
238
  end
235
239
 
236
240
  def expdate(creditcard)
@@ -257,8 +261,7 @@ module ActiveMerchant #:nodoc:
257
261
  post[:password] = @options[:password]
258
262
  post[:type] = action if action
259
263
 
260
- request = post.merge(parameters).map { |key, value| "#{key}=#{CGI.escape(value.to_s)}" }.join('&')
261
- request
264
+ post.merge(parameters).map { |key, value| "#{key}=#{CGI.escape(value.to_s)}" }.join('&')
262
265
  end
263
266
 
264
267
  def determine_funding_source(source)