activemerchant 1.133.0 → 1.137.0

Sign up to get free protection for your applications and to get access to all the features.
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)