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
@@ -1,248 +1,15 @@
1
+ require 'active_merchant/billing/gateways/cecabank/cecabank_xml'
2
+ require 'active_merchant/billing/gateways/cecabank/cecabank_json'
3
+
1
4
  module ActiveMerchant #:nodoc:
2
5
  module Billing #:nodoc:
3
6
  class CecabankGateway < Gateway
4
- self.test_url = 'https://tpv.ceca.es'
5
- self.live_url = 'https://pgw.ceca.es'
6
-
7
- self.supported_countries = ['ES']
8
- self.supported_cardtypes = %i[visa master american_express]
9
- self.homepage_url = 'http://www.ceca.es/es/'
10
- self.display_name = 'Cecabank'
11
- self.default_currency = 'EUR'
12
- self.money_format = :cents
13
-
14
- #### CECA's MAGIC NUMBERS
15
- CECA_NOTIFICATIONS_URL = 'NONE'
16
- CECA_ENCRIPTION = 'SHA2'
17
- CECA_DECIMALS = '2'
18
- CECA_MODE = 'SSL'
19
- CECA_UI_LESS_LANGUAGE = 'XML'
20
- CECA_UI_LESS_LANGUAGE_REFUND = '1'
21
- CECA_UI_LESS_REFUND_PAGE = 'anulacion_xml'
22
- CECA_ACTION_REFUND = 'anulaciones/anularParcial' # use partial refund's URL to avoid time frame limitations and decision logic on client side
23
- CECA_ACTION_PURCHASE = 'tpv/compra'
24
- CECA_CURRENCIES_DICTIONARY = { 'EUR' => 978, 'USD' => 840, 'GBP' => 826 }
25
-
26
- # Creates a new CecabankGateway
27
- #
28
- # The gateway requires four values for connection to be passed
29
- # in the +options+ hash.
30
- #
31
- # ==== Options
32
- #
33
- # * <tt>:merchant_id</tt> -- Cecabank's merchant_id (REQUIRED)
34
- # * <tt>:acquirer_bin</tt> -- Cecabank's acquirer_bin (REQUIRED)
35
- # * <tt>:terminal_id</tt> -- Cecabank's terminal_id (REQUIRED)
36
- # * <tt>:key</tt> -- Cecabank's cypher key (REQUIRED)
37
- # * <tt>:test</tt> -- +true+ or +false+. If true, perform transactions against the test server.
38
- # Otherwise, perform transactions against the production server.
39
- def initialize(options = {})
40
- requires!(options, :merchant_id, :acquirer_bin, :terminal_id, :key)
41
- super
42
- end
43
-
44
- # Perform a purchase, which is essentially an authorization and capture in a single operation.
45
- #
46
- # ==== Parameters
47
- #
48
- # * <tt>money</tt> -- The amount to be purchased as an Integer value in cents.
49
- # * <tt>creditcard</tt> -- The CreditCard details for the transaction.
50
- # * <tt>options</tt> -- A hash of optional parameters.
51
- #
52
- # ==== Options
53
- #
54
- # * <tt>:order_id</tt> -- order_id passed used purchase. (REQUIRED)
55
- # * <tt>:currency</tt> -- currency. Supported: EUR, USD, GBP.
56
- # * <tt>:description</tt> -- description to be pased to the gateway.
57
- def purchase(money, creditcard, options = {})
58
- requires!(options, :order_id)
59
-
60
- post = { 'Descripcion' => options[:description],
61
- 'Num_operacion' => options[:order_id],
62
- 'Idioma' => CECA_UI_LESS_LANGUAGE,
63
- 'Pago_soportado' => CECA_MODE,
64
- 'URL_OK' => CECA_NOTIFICATIONS_URL,
65
- 'URL_NOK' => CECA_NOTIFICATIONS_URL,
66
- 'Importe' => amount(money),
67
- 'TipoMoneda' => CECA_CURRENCIES_DICTIONARY[options[:currency] || currency(money)] }
68
-
69
- add_creditcard(post, creditcard)
70
-
71
- commit(CECA_ACTION_PURCHASE, post)
72
- end
73
-
74
- # Refund a transaction.
75
- #
76
- # This transaction indicates to the gateway that
77
- # money should flow from the merchant to the customer.
78
- #
79
- # ==== Parameters
80
- #
81
- # * <tt>money</tt> -- The amount to be credited to the customer as an Integer value in cents.
82
- # * <tt>identification</tt> -- The reference given from the gateway on purchase (reference, not operation).
83
- # * <tt>options</tt> -- A hash of parameters.
84
- def refund(money, identification, options = {})
85
- reference, order_id = split_authorization(identification)
86
-
87
- post = { 'Referencia' => reference,
88
- 'Num_operacion' => order_id,
89
- 'Idioma' => CECA_UI_LESS_LANGUAGE_REFUND,
90
- 'Pagina' => CECA_UI_LESS_REFUND_PAGE,
91
- 'Importe' => amount(money),
92
- 'TipoMoneda' => CECA_CURRENCIES_DICTIONARY[options[:currency] || currency(money)] }
93
-
94
- commit(CECA_ACTION_REFUND, post)
95
- end
96
-
97
- def supports_scrubbing
98
- true
99
- end
100
-
101
- def scrub(transcript)
102
- transcript.
103
- gsub(%r((Authorization: Basic )\w+), '\1[FILTERED]').
104
- gsub(%r((&?pan=)[^&]*)i, '\1[FILTERED]').
105
- gsub(%r((&?cvv2=)[^&]*)i, '\1[FILTERED]')
106
- end
107
-
108
- private
109
-
110
- def add_creditcard(post, creditcard)
111
- post['PAN'] = creditcard.number
112
- post['Caducidad'] = expdate(creditcard)
113
- post['CVV2'] = creditcard.verification_value
114
- post['Pago_elegido'] = CECA_MODE
115
- end
7
+ self.abstract_class = true
116
8
 
117
- def expdate(creditcard)
118
- "#{format(creditcard.year, :four_digits)}#{format(creditcard.month, :two_digits)}"
119
- end
120
-
121
- def parse(body)
122
- response = {}
123
-
124
- root = REXML::Document.new(body).root
125
-
126
- response[:success] = (root.attributes['valor'] == 'OK')
127
- response[:date] = root.attributes['fecha']
128
- response[:operation_number] = root.attributes['numeroOperacion']
129
- response[:message] = root.attributes['valor']
130
-
131
- if root.elements['OPERACION']
132
- response[:operation_type] = root.elements['OPERACION'].attributes['tipo']
133
- response[:amount] = root.elements['OPERACION/importe'].text.strip
134
- end
135
-
136
- response[:description] = root.elements['OPERACION/descripcion'].text if root.elements['OPERACION/descripcion']
137
- response[:authorization_number] = root.elements['OPERACION/numeroAutorizacion'].text if root.elements['OPERACION/numeroAutorizacion']
138
- response[:reference] = root.elements['OPERACION/referencia'].text if root.elements['OPERACION/referencia']
139
- response[:pan] = root.elements['OPERACION/pan'].text if root.elements['OPERACION/pan']
140
-
141
- if root.elements['ERROR']
142
- response[:error_code] = root.elements['ERROR/codigo'].text
143
- response[:error_message] = root.elements['ERROR/descripcion'].text
144
- else
145
- if root.elements['OPERACION'].attributes['numeroOperacion'] == '000'
146
- response[:authorization] = root.elements['OPERACION/numeroAutorizacion'].text if root.elements['OPERACION/numeroAutorizacion']
147
- else
148
- response[:authorization] = root.attributes['numeroOperacion']
149
- end
150
- end
151
-
152
- return response
153
- rescue REXML::ParseException => e
154
- response[:success] = false
155
- response[:message] = 'Unable to parse the response.'
156
- response[:error_message] = e.message
157
- response
158
- end
159
-
160
- def commit(action, parameters)
161
- parameters.merge!(
162
- 'Cifrado' => CECA_ENCRIPTION,
163
- 'Firma' => generate_signature(action, parameters),
164
- 'Exponente' => CECA_DECIMALS,
165
- 'MerchantID' => options[:merchant_id],
166
- 'AcquirerBIN' => options[:acquirer_bin],
167
- 'TerminalID' => options[:terminal_id]
168
- )
169
- url = (test? ? self.test_url : self.live_url) + "/tpvweb/#{action}.action"
170
- xml = ssl_post("#{url}?", post_data(parameters))
171
- response = parse(xml)
172
- Response.new(
173
- response[:success],
174
- message_from(response),
175
- response,
176
- test: test?,
177
- authorization: build_authorization(response),
178
- error_code: response[:error_code]
179
- )
180
- end
181
-
182
- def message_from(response)
183
- if response[:message] == 'ERROR' && response[:error_message]
184
- response[:error_message]
185
- elsif response[:error_message]
186
- "#{response[:message]} #{response[:error_message]}"
187
- else
188
- response[:message]
189
- end
190
- end
191
-
192
- def post_data(params)
193
- return nil unless params
194
-
195
- params.map do |key, value|
196
- next if value.blank?
197
-
198
- if value.is_a?(Hash)
199
- h = {}
200
- value.each do |k, v|
201
- h["#{key}.#{k}"] = v unless v.blank?
202
- end
203
- post_data(h)
204
- else
205
- "#{key}=#{CGI.escape(value.to_s)}"
206
- end
207
- end.compact.join('&')
208
- end
209
-
210
- def build_authorization(response)
211
- [response[:reference], response[:authorization]].join('|')
212
- end
213
-
214
- def split_authorization(authorization)
215
- authorization.split('|')
216
- end
9
+ def self.new(options = {})
10
+ return CecabankJsonGateway.new(options) if options[:is_rest_json]
217
11
 
218
- def generate_signature(action, parameters)
219
- signature_fields =
220
- case action
221
- when CECA_ACTION_REFUND
222
- options[:key].to_s +
223
- options[:merchant_id].to_s +
224
- options[:acquirer_bin].to_s +
225
- options[:terminal_id].to_s +
226
- parameters['Num_operacion'].to_s +
227
- parameters['Importe'].to_s +
228
- parameters['TipoMoneda'].to_s +
229
- CECA_DECIMALS +
230
- parameters['Referencia'].to_s +
231
- CECA_ENCRIPTION
232
- else
233
- options[:key].to_s +
234
- options[:merchant_id].to_s +
235
- options[:acquirer_bin].to_s +
236
- options[:terminal_id].to_s +
237
- parameters['Num_operacion'].to_s +
238
- parameters['Importe'].to_s +
239
- parameters['TipoMoneda'].to_s +
240
- CECA_DECIMALS +
241
- CECA_ENCRIPTION +
242
- CECA_NOTIFICATIONS_URL +
243
- CECA_NOTIFICATIONS_URL
244
- end
245
- Digest::SHA2.hexdigest(signature_fields)
12
+ CecabankXmlGateway.new(options)
246
13
  end
247
14
  end
248
15
  end
@@ -17,15 +17,7 @@ module ActiveMerchant #:nodoc:
17
17
  TEST_ACCESS_TOKEN_URL = 'https://access.sandbox.checkout.com/connect/token'
18
18
 
19
19
  def initialize(options = {})
20
- @options = options
21
- @access_token = nil
22
-
23
- if options.has_key?(:secret_key)
24
- requires!(options, :secret_key)
25
- else
26
- requires!(options, :client_id, :client_secret)
27
- @access_token = setup_access_token
28
- end
20
+ options.has_key?(:secret_key) ? requires!(options, :secret_key) : requires!(options, :client_id, :client_secret)
29
21
 
30
22
  super
31
23
  end
@@ -41,6 +33,7 @@ module ActiveMerchant #:nodoc:
41
33
  post = {}
42
34
  post[:capture] = false
43
35
  build_auth_or_purchase(post, amount, payment_method, options)
36
+
44
37
  options[:incremental_authorization] ? commit(:incremental_authorize, post, options, options[:incremental_authorization]) : commit(:authorize, post, options)
45
38
  end
46
39
 
@@ -57,12 +50,13 @@ module ActiveMerchant #:nodoc:
57
50
 
58
51
  def credit(amount, payment, options = {})
59
52
  post = {}
60
- post[:instruction] = {}
61
- post[:instruction][:funds_transfer_type] = options[:funds_transfer_type] || 'FD'
62
53
  add_processing_channel(post, options)
63
54
  add_invoice(post, amount, options)
64
55
  add_payment_method(post, payment, options, :destination)
65
56
  add_source(post, options)
57
+ add_instruction_data(post, options)
58
+ add_payout_sender_data(post, options)
59
+ add_payout_destination_data(post, options)
66
60
 
67
61
  commit(:credit, post, options)
68
62
  end
@@ -87,7 +81,7 @@ module ActiveMerchant #:nodoc:
87
81
  authorize(0, credit_card, options)
88
82
  end
89
83
 
90
- def verify_payment(authorization, option = {})
84
+ def verify_payment(authorization, options = {})
91
85
  commit(:verify_payment, nil, options, authorization, :get)
92
86
  end
93
87
 
@@ -102,7 +96,8 @@ module ActiveMerchant #:nodoc:
102
96
  gsub(/("cvv\\":\\")\d+/, '\1[FILTERED]').
103
97
  gsub(/("cryptogram\\":\\")\w+/, '\1[FILTERED]').
104
98
  gsub(/(source\\":\{.*\\"token\\":\\")\d+/, '\1[FILTERED]').
105
- gsub(/("token\\":\\")\w+/, '\1[FILTERED]')
99
+ gsub(/("token\\":\\")\w+/, '\1[FILTERED]').
100
+ gsub(/("access_token\\?"\s*:\s*\\?")[^"]*\w+/, '\1[FILTERED]')
106
101
  end
107
102
 
108
103
  def store(payment_method, options = {})
@@ -147,6 +142,11 @@ module ActiveMerchant #:nodoc:
147
142
  add_metadata(post, options, payment_method)
148
143
  add_processing_channel(post, options)
149
144
  add_marketplace_data(post, options)
145
+ add_recipient_data(post, options)
146
+ add_processing_data(post, options)
147
+ add_payment_sender_data(post, options)
148
+ add_risk_data(post, options)
149
+ truncate_amex_reference_id(post, options, payment_method)
150
150
  end
151
151
 
152
152
  def add_invoice(post, money, options)
@@ -162,6 +162,90 @@ module ActiveMerchant #:nodoc:
162
162
  post[:metadata][:udf5] = application_id || 'ActiveMerchant'
163
163
  end
164
164
 
165
+ def truncate_amex_reference_id(post, options, payment_method)
166
+ post[:reference] = truncate(options[:order_id], 30) if payment_method.respond_to?(:brand) && payment_method.brand == 'american_express'
167
+ end
168
+
169
+ def add_recipient_data(post, options)
170
+ return unless options[:recipient].is_a?(Hash)
171
+
172
+ recipient = options[:recipient]
173
+
174
+ post[:recipient] = {}
175
+ post[:recipient][:dob] = recipient[:dob] if recipient[:dob]
176
+ post[:recipient][:zip] = recipient[:zip] if recipient[:zip]
177
+ post[:recipient][:account_number] = recipient[:account_number] if recipient[:account_number]
178
+ post[:recipient][:first_name] = recipient[:first_name] if recipient[:first_name]
179
+ post[:recipient][:last_name] = recipient[:last_name] if recipient[:last_name]
180
+
181
+ if address = recipient[:address]
182
+ address1 = address[:address1] || address[:address_line1]
183
+ address2 = address[:address2] || address[:address_line2]
184
+
185
+ post[:recipient][:address] = {}
186
+ post[:recipient][:address][:address_line1] = address1 if address1
187
+ post[:recipient][:address][:address_line2] = address2 if address2
188
+ post[:recipient][:address][:city] = address[:city] if address[:city]
189
+ post[:recipient][:address][:state] = address[:state] if address[:state]
190
+ post[:recipient][:address][:zip] = address[:zip] if address[:zip]
191
+ post[:recipient][:address][:country] = address[:country] if address[:country]
192
+ end
193
+ end
194
+
195
+ def add_processing_data(post, options)
196
+ return unless options[:processing].is_a?(Hash)
197
+
198
+ post[:processing] = options[:processing]
199
+ end
200
+
201
+ def add_risk_data(post, options)
202
+ return unless options[:risk].is_a?(Hash)
203
+
204
+ risk = options[:risk]
205
+ post[:risk] = {} unless risk.empty?
206
+
207
+ if risk[:enabled].to_s == 'true'
208
+ post[:risk][:enabled] = true
209
+ post[:risk][:device_session_id] = risk[:device_session_id] if risk[:device_session_id]
210
+ elsif risk[:enabled].to_s == 'false'
211
+ post[:risk][:enabled] = false
212
+ end
213
+ end
214
+
215
+ def add_payment_sender_data(post, options)
216
+ return unless options[:sender].is_a?(Hash)
217
+
218
+ sender = options[:sender]
219
+
220
+ post[:sender] = {}
221
+ post[:sender][:type] = sender[:type] if sender[:type]
222
+ post[:sender][:first_name] = sender[:first_name] if sender[:first_name]
223
+ post[:sender][:last_name] = sender[:last_name] if sender[:last_name]
224
+ post[:sender][:dob] = sender[:dob] if sender[:dob]
225
+ post[:sender][:reference] = sender[:reference] if sender[:reference]
226
+ post[:sender][:company_name] = sender[:company_name] if sender[:company_name]
227
+
228
+ if address = sender[:address]
229
+ address1 = address[:address1] || address[:address_line1]
230
+ address2 = address[:address2] || address[:address_line2]
231
+
232
+ post[:sender][:address] = {}
233
+ post[:sender][:address][:address_line1] = address1 if address1
234
+ post[:sender][:address][:address_line2] = address2 if address2
235
+ post[:sender][:address][:city] = address[:city] if address[:city]
236
+ post[:sender][:address][:state] = address[:state] if address[:state]
237
+ post[:sender][:address][:zip] = address[:zip] if address[:zip]
238
+ post[:sender][:address][:country] = address[:country] if address[:country]
239
+ end
240
+
241
+ if identification = sender[:identification]
242
+ post[:sender][:identification] = {}
243
+ post[:sender][:identification][:type] = identification[:type] if identification[:type]
244
+ post[:sender][:identification][:number] = identification[:number] if identification[:number]
245
+ post[:sender][:identification][:issuing_country] = identification[:issuing_country] if identification[:issuing_country]
246
+ end
247
+ end
248
+
165
249
  def add_authorization_type(post, options)
166
250
  post[:authorization_type] = options[:authorization_type] if options[:authorization_type]
167
251
  end
@@ -173,6 +257,7 @@ module ActiveMerchant #:nodoc:
173
257
  end
174
258
 
175
259
  def add_payment_method(post, payment_method, options, key = :source)
260
+ # the key = :destination when this method is called in def credit
176
261
  post[key] = {}
177
262
  case payment_method
178
263
  when NetworkTokenizationCreditCard
@@ -190,13 +275,23 @@ module ActiveMerchant #:nodoc:
190
275
  post[key][:type] = 'card'
191
276
  post[key][:name] = payment_method.name
192
277
  post[key][:number] = payment_method.number
193
- post[key][:cvv] = payment_method.verification_value
278
+ post[key][:cvv] = payment_method.verification_value unless options[:funds_transfer_type]
194
279
  post[key][:stored] = 'true' if options[:card_on_file] == true
280
+
281
+ # because of the way the key = is implemented in the method signature, some of the destination
282
+ # data will be added here, some in the destination specific method below.
283
+ # at first i was going to move this, but since this data is coming from the payment method
284
+ # i think it makes sense to leave it
195
285
  if options[:account_holder_type]
196
286
  post[key][:account_holder] = {}
197
287
  post[key][:account_holder][:type] = options[:account_holder_type]
198
- post[key][:account_holder][:first_name] = payment_method.first_name if payment_method.first_name
199
- post[key][:account_holder][:last_name] = payment_method.last_name if payment_method.last_name
288
+
289
+ if options[:account_holder_type] == 'corporate' || options[:account_holder_type] == 'government'
290
+ post[key][:account_holder][:company_name] = payment_method.name if payment_method.respond_to?(:name)
291
+ else
292
+ post[key][:account_holder][:first_name] = payment_method.first_name if payment_method.first_name
293
+ post[key][:account_holder][:last_name] = payment_method.last_name if payment_method.last_name
294
+ end
200
295
  else
201
296
  post[key][:first_name] = payment_method.first_name if payment_method.first_name
202
297
  post[key][:last_name] = payment_method.last_name if payment_method.last_name
@@ -276,7 +371,7 @@ module ActiveMerchant #:nodoc:
276
371
  end
277
372
 
278
373
  def add_stored_credentials_using_normalized_fields(post, options)
279
- if options[:stored_credential][:initial_transaction] == true
374
+ if options[:stored_credential][:initiator] == 'cardholder'
280
375
  post[:merchant_initiated] = false
281
376
  else
282
377
  post[:source][:stored] = true
@@ -321,6 +416,82 @@ module ActiveMerchant #:nodoc:
321
416
  post[:processing_channel_id] = options[:processing_channel_id] if options[:processing_channel_id]
322
417
  end
323
418
 
419
+ def add_instruction_data(post, options)
420
+ post[:instruction] = {}
421
+ post[:instruction][:funds_transfer_type] = options[:funds_transfer_type] || 'FD'
422
+ post[:instruction][:purpose] = options[:instruction_purpose] if options[:instruction_purpose]
423
+ end
424
+
425
+ def add_payout_sender_data(post, options)
426
+ return unless options[:payout] == true
427
+
428
+ post[:sender] = {
429
+ # options for type are individual, corporate, or government
430
+ type: options[:sender][:type],
431
+ # first and last name required if sent by type: individual
432
+ first_name: options[:sender][:first_name],
433
+ middle_name: options[:sender][:middle_name],
434
+ last_name: options[:sender][:last_name],
435
+ # company name required if sent by type: corporate or government
436
+ company_name: options[:sender][:company_name],
437
+ # these are required fields for payout, may not work if address is blank or different than cardholder(option for sender to be a company or government).
438
+ # may need to still include in GSF hash.
439
+
440
+ address: {
441
+ address_line1: options.dig(:sender, :address, :address1),
442
+ address_line2: options.dig(:sender, :address, :address2),
443
+ city: options.dig(:sender, :address, :city),
444
+ state: options.dig(:sender, :address, :state),
445
+ country: options.dig(:sender, :address, :country),
446
+ zip: options.dig(:sender, :address, :zip)
447
+ }.compact,
448
+ reference: options[:sender][:reference],
449
+ reference_type: options[:sender][:reference_type],
450
+ source_of_funds: options[:sender][:source_of_funds],
451
+ # identification object is conditional. required when card metadata issuer_country = AR, BR, CO, or PR
452
+ # checkout docs say PR (Peru), but PR is puerto rico and PE is Peru so yikes
453
+ identification: {
454
+ type: options.dig(:sender, :identification, :type),
455
+ number: options.dig(:sender, :identification, :number),
456
+ issuing_country: options.dig(:sender, :identification, :issuing_country),
457
+ date_of_expiry: options.dig(:sender, :identification, :date_of_expiry)
458
+ }.compact,
459
+ date_of_birth: options[:sender][:date_of_birth],
460
+ country_of_birth: options[:sender][:country_of_birth],
461
+ nationality: options[:sender][:nationality]
462
+ }.compact
463
+ end
464
+
465
+ def add_payout_destination_data(post, options)
466
+ return unless options[:payout] == true
467
+
468
+ post[:destination] ||= {}
469
+ post[:destination][:account_holder] ||= {}
470
+ post[:destination][:account_holder][:email] = options[:destination][:account_holder][:email] if options[:destination][:account_holder][:email]
471
+ post[:destination][:account_holder][:date_of_birth] = options[:destination][:account_holder][:date_of_birth] if options[:destination][:account_holder][:date_of_birth]
472
+ post[:destination][:account_holder][:country_of_birth] = options[:destination][:account_holder][:country_of_birth] if options[:destination][:account_holder][:country_of_birth]
473
+ # below fields only required during a card to card payout
474
+ post[:destination][:account_holder][:phone] = {}
475
+ post[:destination][:account_holder][:phone][:country_code] = options.dig(:destination, :account_holder, :phone, :country_code) if options.dig(:destination, :account_holder, :phone, :country_code)
476
+ post[:destination][:account_holder][:phone][:number] = options.dig(:destination, :account_holder, :phone, :number) if options.dig(:destination, :account_holder, :phone, :number)
477
+
478
+ post[:destination][:account_holder][:identification] = {}
479
+ post[:destination][:account_holder][:identification][:type] = options.dig(:destination, :account_holder, :identification, :type) if options.dig(:destination, :account_holder, :identification, :type)
480
+ post[:destination][:account_holder][:identification][:number] = options.dig(:destination, :account_holder, :identification, :number) if options.dig(:destination, :account_holder, :identification, :number)
481
+ post[:destination][:account_holder][:identification][:issuing_country] = options.dig(:destination, :account_holder, :identification, :issuing_country) if options.dig(:destination, :account_holder, :identification, :issuing_country)
482
+ post[:destination][:account_holder][:identification][:date_of_expiry] = options.dig(:destination, :account_holder, :identification, :date_of_expiry) if options.dig(:destination, :account_holder, :identification, :date_of_expiry)
483
+
484
+ if address = options[:billing_address] || options[:address] # destination address will come from the tokenized card billing address
485
+ post[:destination][:account_holder][:billing_address] = {}
486
+ post[:destination][:account_holder][:billing_address][:address_line1] = address[:address1] unless address[:address1].blank?
487
+ post[:destination][:account_holder][:billing_address][:address_line2] = address[:address2] unless address[:address2].blank?
488
+ post[:destination][:account_holder][:billing_address][:city] = address[:city] unless address[:city].blank?
489
+ post[:destination][:account_holder][:billing_address][:state] = address[:state] unless address[:state].blank?
490
+ post[:destination][:account_holder][:billing_address][:country] = address[:country] unless address[:country].blank?
491
+ post[:destination][:account_holder][:billing_address][:zip] = address[:zip] unless address[:zip].blank?
492
+ end
493
+ end
494
+
324
495
  def add_marketplace_data(post, options)
325
496
  if options[:marketplace]
326
497
  post[:marketplace] = {}
@@ -339,19 +510,43 @@ module ActiveMerchant #:nodoc:
339
510
  test? ? TEST_ACCESS_TOKEN_URL : LIVE_ACCESS_TOKEN_URL
340
511
  end
341
512
 
513
+ def expires_date_with_extra_range(expires_in)
514
+ # Two minutes are subtracted from the expires_in time to generate the expires date
515
+ # in order to prevent any transaction from failing due to using an access_token
516
+ # that is very close to expiring.
517
+ # e.g. the access_token has one second left to expire and the lag when the transaction
518
+ # use an already expired access_token
519
+ (DateTime.now + (expires_in - 120).seconds).strftime('%Q').to_i
520
+ end
521
+
342
522
  def setup_access_token
343
- request = 'grant_type=client_credentials'
344
- response = parse(ssl_post(access_token_url, request, access_token_header))
345
- response['access_token']
523
+ response = parse(ssl_post(access_token_url, 'grant_type=client_credentials', access_token_header))
524
+ @options[:access_token] = response['access_token']
525
+ @options[:expires] = expires_date_with_extra_range(response['expires_in']) if response['expires_in'] && response['expires_in'] > 0
526
+
527
+ Response.new(
528
+ access_token_valid?,
529
+ message_from(access_token_valid?, response, {}),
530
+ response.merge({ expires: @options[:expires] }),
531
+ test: test?,
532
+ error_code: error_code_from(access_token_valid?, response, {})
533
+ )
534
+ rescue ResponseError => e
535
+ raise OAuthResponseError.new(e)
346
536
  end
347
537
 
348
- def commit(action, post, options, authorization = nil, method = :post)
538
+ def access_token_valid?
539
+ @options[:access_token].present? && @options[:expires].to_i > DateTime.now.strftime('%Q').to_i
540
+ end
541
+
542
+ def perform_request(action, post, options, authorization = nil, method = :post)
349
543
  begin
350
544
  raw_response = ssl_request(method, url(action, authorization), post.nil? || post.empty? ? nil : post.to_json, headers(action, options))
351
545
  response = parse(raw_response)
352
546
  response['id'] = response['_links']['payment']['href'].split('/')[-1] if action == :capture && response.key?('_links')
353
- source_id = authorization if action == :unstore
354
547
  rescue ResponseError => e
548
+ @options[:access_token] = '' if e.response.code == '401' && !@options[:secret_key]
549
+
355
550
  raise unless e.response.code.to_s =~ /4\d\d/
356
551
 
357
552
  response = parse(e.response.body, error: e.response)
@@ -359,18 +554,25 @@ module ActiveMerchant #:nodoc:
359
554
 
360
555
  succeeded = success_from(action, response)
361
556
 
362
- response(action, succeeded, response, source_id)
557
+ response(action, succeeded, response, options)
558
+ end
559
+
560
+ def commit(action, post, options, authorization = nil, method = :post)
561
+ MultiResponse.run do |r|
562
+ r.process { setup_access_token } unless @options[:secret_key] || access_token_valid?
563
+ r.process { perform_request(action, post, options, authorization, method) }
564
+ end
363
565
  end
364
566
 
365
- def response(action, succeeded, response, source_id = nil)
567
+ def response(action, succeeded, response, options = {}, source_id = nil)
366
568
  authorization = authorization_from(response) unless action == :unstore
367
569
  body = action == :unstore ? { response_code: response.to_s } : response
368
570
  Response.new(
369
571
  succeeded,
370
- message_from(succeeded, response),
572
+ message_from(succeeded, response, options),
371
573
  body,
372
574
  authorization: authorization,
373
- error_code: error_code_from(succeeded, body),
575
+ error_code: error_code_from(succeeded, body, options),
374
576
  test: test?,
375
577
  avs_result: avs_result(response),
376
578
  cvv_result: cvv_result(response)
@@ -378,7 +580,7 @@ module ActiveMerchant #:nodoc:
378
580
  end
379
581
 
380
582
  def headers(action, options)
381
- auth_token = @access_token ? "Bearer #{@access_token}" : @options[:secret_key]
583
+ auth_token = @options[:access_token] ? "Bearer #{@options[:access_token]}" : @options[:secret_key]
382
584
  auth_token = @options[:public_key] if action == :tokens
383
585
  headers = {
384
586
  'Authorization' => auth_token,
@@ -448,19 +650,19 @@ module ActiveMerchant #:nodoc:
448
650
  return true if action == :unstore && response == 204
449
651
 
450
652
  store_response = response['token'] || response['id']
451
- if store_response
452
- return true if (action == :tokens && store_response.match(/tok/)) || (action == :store && store_response.match(/src_/))
453
- end
653
+ return true if store_response && ((action == :tokens && store_response.match(/tok/)) || (action == :store && store_response.match(/src_/)))
654
+
454
655
  response['response_summary'] == 'Approved' || response['approved'] == true || !response.key?('response_summary') && response.key?('action_id')
455
656
  end
456
657
 
457
- def message_from(succeeded, response)
658
+ def message_from(succeeded, response, options)
458
659
  if succeeded
459
660
  'Succeeded'
460
661
  elsif response['error_type']
461
662
  response['error_type'] + ': ' + response['error_codes'].first
462
663
  else
463
- response['response_summary'] || response['response_code'] || response['status'] || response['message'] || 'Unable to read error message'
664
+ response_summary = response['response_summary'] || response.dig('actions', 0, 'response_summary')
665
+ response_summary || response['response_code'] || response['status'] || response['message'] || 'Unable to read error message'
464
666
  end
465
667
  end
466
668
 
@@ -481,7 +683,7 @@ module ActiveMerchant #:nodoc:
481
683
  raw['id']
482
684
  end
483
685
 
484
- def error_code_from(succeeded, response)
686
+ def error_code_from(succeeded, response, options)
485
687
  return if succeeded
486
688
 
487
689
  if response['error_type'] && response['error_codes']
@@ -489,7 +691,9 @@ module ActiveMerchant #:nodoc:
489
691
  elsif response['error_type']
490
692
  response['error_type']
491
693
  else
492
- STANDARD_ERROR_CODE_MAPPING[response['response_code']]
694
+ response_code = response['response_code'] || response.dig('actions', 0, 'response_code')
695
+
696
+ STANDARD_ERROR_CODE_MAPPING[response_code]
493
697
  end
494
698
  end
495
699