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
@@ -0,0 +1,316 @@
1
+ require 'active_merchant/billing/gateways/cecabank/cecabank_common'
2
+
3
+ module ActiveMerchant
4
+ module Billing
5
+ class CecabankJsonGateway < Gateway
6
+ include CecabankCommon
7
+
8
+ CECA_ACTIONS_DICTIONARY = {
9
+ purchase: :REST_AUTORIZACION,
10
+ authorize: :REST_PREAUTORIZACION,
11
+ capture: :REST_COBRO_PREAUTORIZACION,
12
+ refund: :REST_DEVOLUCION,
13
+ void: :REST_ANULACION
14
+ }.freeze
15
+
16
+ CECA_REASON_TYPES = {
17
+ installment: :I,
18
+ recurring: :R,
19
+ unscheduled: :C
20
+ }.freeze
21
+
22
+ CECA_INITIATOR = {
23
+ merchant: :N,
24
+ cardholder: :S
25
+ }.freeze
26
+
27
+ CECA_SCA_TYPES = {
28
+ low_value_exemption: :LOW,
29
+ transaction_risk_analysis_exemption: :TRA
30
+ }.freeze
31
+
32
+ self.test_url = 'https://tpv.ceca.es/tpvweb/rest/procesos/'
33
+ self.live_url = 'https://pgw.ceca.es/tpvweb/rest/procesos/'
34
+
35
+ def authorize(money, creditcard, options = {})
36
+ handle_purchase(:authorize, money, creditcard, options)
37
+ end
38
+
39
+ def capture(money, identification, options = {})
40
+ authorization, operation_number, _money = identification.split('#')
41
+
42
+ post = {}
43
+ options[:operation_number] = operation_number
44
+ add_auth_invoice_data(:capture, post, money, authorization, options)
45
+
46
+ commit('compra', post)
47
+ end
48
+
49
+ def purchase(money, creditcard, options = {})
50
+ handle_purchase(:purchase, money, creditcard, options)
51
+ end
52
+
53
+ def void(identification, options = {})
54
+ authorization, operation_number, money = identification.split('#')
55
+ options[:operation_number] = operation_number
56
+ handle_cancellation(:void, money.to_i, authorization, options)
57
+ end
58
+
59
+ def refund(money, identification, options = {})
60
+ authorization, operation_number, _money = identification.split('#')
61
+ options[:operation_number] = operation_number
62
+ handle_cancellation(:refund, money, authorization, options)
63
+ end
64
+
65
+ def scrub(transcript)
66
+ return '' if transcript.blank?
67
+
68
+ before_message = transcript.gsub(%r(\\\")i, "'").scan(/{[^>]*}/).first.gsub("'", '"')
69
+ request_data = JSON.parse(before_message)
70
+
71
+ if @options[:encryption_key]
72
+ params = parse(request_data['parametros'])
73
+ sensitive_fields = decrypt_sensitive_fields(params['encryptedData'])
74
+ filtered_params = filter_params(sensitive_fields)
75
+ params['encryptedData'] = encrypt_sensitive_fields(filtered_params)
76
+ else
77
+ params = filter_params(decode_params(request_data['parametros']))
78
+ end
79
+
80
+ request_data['parametros'] = encode_params(params)
81
+ before_message = before_message.gsub(%r(\")i, '\\\"')
82
+ after_message = request_data.to_json.gsub(%r(\")i, '\\\"')
83
+ transcript.sub(before_message, after_message)
84
+ end
85
+
86
+ private
87
+
88
+ def filter_params(params)
89
+ params.
90
+ gsub(%r(("pan\\?"\s*:\s*\\?")[^"]*)i, '\1[FILTERED]').
91
+ gsub(%r(("caducidad\\?"\s*:\s*\\?")[^"]*)i, '\1[FILTERED]').
92
+ gsub(%r(("cvv2\\?"\s*:\s*\\?")[^"]*)i, '\1[FILTERED]').
93
+ gsub(%r(("csc\\?"\s*:\s*\\?")[^"]*)i, '\1[FILTERED]').
94
+ gsub(%r(("authentication_value\\?"\s*:\s*\\?")[^"]*)i, '\1[FILTERED]')
95
+ end
96
+
97
+ def decrypt_sensitive_fields(data)
98
+ cipher = OpenSSL::Cipher.new('AES-256-CBC').decrypt
99
+ cipher.key = [@options[:encryption_key]].pack('H*')
100
+ cipher.iv = @options[:initiator_vector]&.split('')&.map(&:to_i)&.pack('c*')
101
+ cipher.update([data].pack('H*')) + cipher.final
102
+ end
103
+
104
+ def encrypt_sensitive_fields(data)
105
+ cipher = OpenSSL::Cipher.new('AES-256-CBC').encrypt
106
+ cipher.key = [@options[:encryption_key]].pack('H*')
107
+ cipher.iv = @options[:initiator_vector]&.split('')&.map(&:to_i)&.pack('c*')
108
+ encrypted = cipher.update(data.to_json) + cipher.final
109
+ encrypted.unpack1('H*')
110
+ end
111
+
112
+ def handle_purchase(action, money, creditcard, options)
113
+ post = { parametros: { accion: CECA_ACTIONS_DICTIONARY[action] } }
114
+
115
+ add_invoice(post, money, options)
116
+ add_creditcard(post, creditcard)
117
+ add_stored_credentials(post, creditcard, options)
118
+ add_three_d_secure(post, options)
119
+
120
+ commit('compra', post)
121
+ end
122
+
123
+ def handle_cancellation(action, money, authorization, options = {})
124
+ post = {}
125
+ add_auth_invoice_data(action, post, money, authorization, options)
126
+
127
+ commit('anulacion', post)
128
+ end
129
+
130
+ def add_auth_invoice_data(action, post, money, authorization, options)
131
+ params = post[:parametros] ||= {}
132
+ params[:accion] = CECA_ACTIONS_DICTIONARY[action]
133
+ params[:referencia] = authorization
134
+
135
+ add_invoice(post, money, options)
136
+ end
137
+
138
+ def add_encryption(post)
139
+ post[:cifrado] = CECA_ENCRIPTION
140
+ post[:parametros][:encryptedData] = encrypt_sensitive_fields(post[:parametros][:encryptedData]) if @options[:encryption_key]
141
+ end
142
+
143
+ def add_signature(post, params_encoded, options)
144
+ post[:firma] = Digest::SHA2.hexdigest(@options[:cypher_key].to_s + params_encoded)
145
+ end
146
+
147
+ def add_merchant_data(post)
148
+ params = post[:parametros] ||= {}
149
+
150
+ params[:merchantID] = @options[:merchant_id]
151
+ params[:acquirerBIN] = @options[:acquirer_bin]
152
+ params[:terminalID] = @options[:terminal_id]
153
+ end
154
+
155
+ def add_invoice(post, money, options)
156
+ post[:parametros][:numOperacion] = options[:operation_number] || options[:order_id]
157
+ post[:parametros][:importe] = amount(money)
158
+ post[:parametros][:tipoMoneda] = CECA_CURRENCIES_DICTIONARY[options[:currency] || currency(money)].to_s
159
+ post[:parametros][:exponente] = 2.to_s
160
+ end
161
+
162
+ def add_creditcard(post, creditcard)
163
+ params = post[:parametros] ||= {}
164
+
165
+ payment_method = {
166
+ pan: creditcard.number,
167
+ caducidad: strftime_yyyymm(creditcard)
168
+ }
169
+ if CreditCard.brand?(creditcard.number) == 'american_express'
170
+ payment_method[:csc] = creditcard.verification_value
171
+ else
172
+ payment_method[:cvv2] = creditcard.verification_value
173
+ end
174
+
175
+ @options[:encryption_key] ? params[:encryptedData] = payment_method : params.merge!(payment_method)
176
+ end
177
+
178
+ def add_stored_credentials(post, creditcard, options)
179
+ return unless stored_credential = options[:stored_credential]
180
+
181
+ return if options[:exemption_type].blank? && !(stored_credential[:reason_type] && stored_credential[:initiator])
182
+
183
+ params = post[:parametros] ||= {}
184
+ params[:exencionSCA] = 'MIT'
185
+
186
+ requires!(stored_credential, :reason_type, :initiator)
187
+ reason_type = CECA_REASON_TYPES[stored_credential[:reason_type].to_sym]
188
+ initiator = CECA_INITIATOR[stored_credential[:initiator].to_sym]
189
+ params[:tipoCOF] = reason_type
190
+ params[:inicioRec] = initiator
191
+ if initiator == :S
192
+ requires!(options, :recurring_frequency)
193
+ params[:finRec] = options[:recurring_end_date] || strftime_yyyymm(creditcard)
194
+ params[:frecRec] = options[:recurring_frequency]
195
+ end
196
+
197
+ network_transaction_id = options[:network_transaction_id].present? ? options[:network_transaction_id] : stored_credential[:network_transaction_id]
198
+ params[:mmppTxId] = network_transaction_id unless network_transaction_id.blank?
199
+ end
200
+
201
+ def add_three_d_secure(post, options)
202
+ params = post[:parametros] ||= {}
203
+ return unless three_d_secure = options[:three_d_secure]
204
+
205
+ params[:exencionSCA] ||= CECA_SCA_TYPES.fetch(options[:exemption_type]&.to_sym, :NONE)
206
+
207
+ three_d_response = {
208
+ exemption_type: options[:exemption_type],
209
+ three_ds_version: three_d_secure[:version],
210
+ directory_server_transaction_id: three_d_secure[:ds_transaction_id],
211
+ acs_transaction_id: three_d_secure[:acs_transaction_id],
212
+ authentication_response_status: three_d_secure[:authentication_response_status],
213
+ three_ds_server_trans_id: three_d_secure[:three_ds_server_trans_id],
214
+ ecommerce_indicator: three_d_secure[:eci],
215
+ enrolled: three_d_secure[:enrolled]
216
+ }
217
+
218
+ if @options[:encryption_key]
219
+ params[:encryptedData].merge!({ authentication_value: three_d_secure[:cavv] })
220
+ else
221
+ three_d_response[:authentication_value] = three_d_secure[:cavv]
222
+ end
223
+
224
+ three_d_response[:amount] = post[:parametros][:importe]
225
+ params[:ThreeDsResponse] = three_d_response.to_json
226
+ end
227
+
228
+ def commit(action, post)
229
+ auth_options = {
230
+ operation_number: post.dig(:parametros, :numOperacion),
231
+ amount: post.dig(:parametros, :importe)
232
+ }
233
+
234
+ add_encryption(post)
235
+ add_merchant_data(post)
236
+
237
+ params_encoded = encode_post_parameters(post)
238
+ add_signature(post, params_encoded, options)
239
+
240
+ response = parse(ssl_post(url(action), post.to_json, headers))
241
+ response[:parametros] = parse(response[:parametros]) if response[:parametros]
242
+
243
+ Response.new(
244
+ success_from(response),
245
+ message_from(response),
246
+ response,
247
+ authorization: authorization_from(response, auth_options),
248
+ network_transaction_id: network_transaction_id_from(response),
249
+ test: test?,
250
+ error_code: error_code_from(response)
251
+ )
252
+ end
253
+
254
+ def url(action)
255
+ (test? ? self.test_url : self.live_url) + action
256
+ end
257
+
258
+ def host
259
+ URI.parse(url('')).host
260
+ end
261
+
262
+ def headers
263
+ {
264
+ 'Content-Type' => 'application/json',
265
+ 'Host' => host
266
+ }
267
+ end
268
+
269
+ def parse(string)
270
+ JSON.parse(string).with_indifferent_access
271
+ rescue JSON::ParserError
272
+ parse(decode_params(string))
273
+ end
274
+
275
+ def encode_post_parameters(post)
276
+ post[:parametros] = encode_params(post[:parametros])
277
+ end
278
+
279
+ def encode_params(params)
280
+ Base64.strict_encode64(params.is_a?(Hash) ? params.to_json : params)
281
+ end
282
+
283
+ def decode_params(params)
284
+ Base64.decode64(params)
285
+ end
286
+
287
+ def success_from(response)
288
+ response[:codResult].blank?
289
+ end
290
+
291
+ def message_from(response)
292
+ return response[:parametros].to_json if success_from(response)
293
+
294
+ response[:paramsEntradaError] || response[:idProceso]
295
+ end
296
+
297
+ def authorization_from(response, auth_options = {})
298
+ return unless response[:parametros]
299
+
300
+ [
301
+ response[:parametros][:referencia],
302
+ auth_options[:operation_number],
303
+ auth_options[:amount]
304
+ ].join('#')
305
+ end
306
+
307
+ def network_transaction_id_from(response)
308
+ response.dig(:parametros, :mmppTxId)
309
+ end
310
+
311
+ def error_code_from(response)
312
+ (response[:codResult] || :paramsEntradaError) unless success_from(response)
313
+ end
314
+ end
315
+ end
316
+ end
@@ -0,0 +1,220 @@
1
+ require 'active_merchant/billing/gateways/cecabank/cecabank_common'
2
+
3
+ module ActiveMerchant
4
+ module Billing
5
+ class CecabankXmlGateway < Gateway
6
+ include CecabankCommon
7
+
8
+ self.test_url = 'https://tpv.ceca.es'
9
+ self.live_url = 'https://pgw.ceca.es'
10
+
11
+ #### CECA's MAGIC NUMBERS
12
+ CECA_NOTIFICATIONS_URL = 'NONE'
13
+ CECA_DECIMALS = '2'
14
+ CECA_MODE = 'SSL'
15
+ CECA_UI_LESS_LANGUAGE = 'XML'
16
+ CECA_UI_LESS_LANGUAGE_REFUND = '1'
17
+ CECA_UI_LESS_REFUND_PAGE = 'anulacion_xml'
18
+ CECA_ACTION_REFUND = 'anulaciones/anularParcial' # use partial refund's URL to avoid time frame limitations and decision logic on client side
19
+ CECA_ACTION_PURCHASE = 'tpv/compra'
20
+
21
+ # Perform a purchase, which is essentially an authorization and capture in a single operation.
22
+ #
23
+ # ==== Parameters
24
+ #
25
+ # * <tt>money</tt> -- The amount to be purchased as an Integer value in cents.
26
+ # * <tt>creditcard</tt> -- The CreditCard details for the transaction.
27
+ # * <tt>options</tt> -- A hash of optional parameters.
28
+ #
29
+ # ==== Options
30
+ #
31
+ # * <tt>:order_id</tt> -- order_id passed used purchase. (REQUIRED)
32
+ # * <tt>:currency</tt> -- currency. Supported: EUR, USD, GBP.
33
+ # * <tt>:description</tt> -- description to be pased to the gateway.
34
+ def purchase(money, creditcard, options = {})
35
+ requires!(options, :order_id)
36
+
37
+ post = { 'Descripcion' => options[:description],
38
+ 'Num_operacion' => options[:order_id],
39
+ 'Idioma' => CECA_UI_LESS_LANGUAGE,
40
+ 'Pago_soportado' => CECA_MODE,
41
+ 'URL_OK' => CECA_NOTIFICATIONS_URL,
42
+ 'URL_NOK' => CECA_NOTIFICATIONS_URL,
43
+ 'Importe' => amount(money),
44
+ 'TipoMoneda' => CECA_CURRENCIES_DICTIONARY[options[:currency] || currency(money)] }
45
+
46
+ add_creditcard(post, creditcard)
47
+
48
+ commit(CECA_ACTION_PURCHASE, post)
49
+ end
50
+
51
+ # Refund a transaction.
52
+ #
53
+ # This transaction indicates to the gateway that
54
+ # money should flow from the merchant to the customer.
55
+ #
56
+ # ==== Parameters
57
+ #
58
+ # * <tt>money</tt> -- The amount to be credited to the customer as an Integer value in cents.
59
+ # * <tt>identification</tt> -- The reference given from the gateway on purchase (reference, not operation).
60
+ # * <tt>options</tt> -- A hash of parameters.
61
+ def refund(money, identification, options = {})
62
+ reference, order_id = split_authorization(identification)
63
+
64
+ post = { 'Referencia' => reference,
65
+ 'Num_operacion' => order_id,
66
+ 'Idioma' => CECA_UI_LESS_LANGUAGE_REFUND,
67
+ 'Pagina' => CECA_UI_LESS_REFUND_PAGE,
68
+ 'Importe' => amount(money),
69
+ 'TipoMoneda' => CECA_CURRENCIES_DICTIONARY[options[:currency] || currency(money)] }
70
+
71
+ commit(CECA_ACTION_REFUND, post)
72
+ end
73
+
74
+ def scrub(transcript)
75
+ transcript.
76
+ gsub(%r((Authorization: Basic )\w+), '\1[FILTERED]').
77
+ gsub(%r((&?pan=)[^&]*)i, '\1[FILTERED]').
78
+ gsub(%r((&?cvv2=)[^&]*)i, '\1[FILTERED]')
79
+ end
80
+
81
+ private
82
+
83
+ def add_creditcard(post, creditcard)
84
+ post['PAN'] = creditcard.number
85
+ post['Caducidad'] = expdate(creditcard)
86
+ post['CVV2'] = creditcard.verification_value
87
+ post['Pago_elegido'] = CECA_MODE
88
+ end
89
+
90
+ def expdate(creditcard)
91
+ "#{format(creditcard.year, :four_digits)}#{format(creditcard.month, :two_digits)}"
92
+ end
93
+
94
+ def parse(body)
95
+ response = {}
96
+
97
+ root = REXML::Document.new(body).root
98
+
99
+ response[:success] = (root.attributes['valor'] == 'OK')
100
+ response[:date] = root.attributes['fecha']
101
+ response[:operation_number] = root.attributes['numeroOperacion']
102
+ response[:message] = root.attributes['valor']
103
+
104
+ if root.elements['OPERACION']
105
+ response[:operation_type] = root.elements['OPERACION'].attributes['tipo']
106
+ response[:amount] = root.elements['OPERACION/importe'].text.strip
107
+ end
108
+
109
+ response[:description] = root.elements['OPERACION/descripcion'].text if root.elements['OPERACION/descripcion']
110
+ response[:authorization_number] = root.elements['OPERACION/numeroAutorizacion'].text if root.elements['OPERACION/numeroAutorizacion']
111
+ response[:reference] = root.elements['OPERACION/referencia'].text if root.elements['OPERACION/referencia']
112
+ response[:pan] = root.elements['OPERACION/pan'].text if root.elements['OPERACION/pan']
113
+
114
+ if root.elements['ERROR']
115
+ response[:error_code] = root.elements['ERROR/codigo'].text
116
+ response[:error_message] = root.elements['ERROR/descripcion'].text
117
+ elsif root.elements['OPERACION'].attributes['numeroOperacion'] == '000'
118
+ response[:authorization] = root.elements['OPERACION/numeroAutorizacion'].text if root.elements['OPERACION/numeroAutorizacion']
119
+ else
120
+ response[:authorization] = root.attributes['numeroOperacion']
121
+ end
122
+
123
+ return response
124
+ rescue REXML::ParseException => e
125
+ response[:success] = false
126
+ response[:message] = 'Unable to parse the response.'
127
+ response[:error_message] = e.message
128
+ response
129
+ end
130
+
131
+ def commit(action, parameters)
132
+ parameters.merge!(
133
+ 'Cifrado' => CECA_ENCRIPTION,
134
+ 'Firma' => generate_signature(action, parameters),
135
+ 'Exponente' => CECA_DECIMALS,
136
+ 'MerchantID' => options[:merchant_id],
137
+ 'AcquirerBIN' => options[:acquirer_bin],
138
+ 'TerminalID' => options[:terminal_id]
139
+ )
140
+ url = (test? ? self.test_url : self.live_url) + "/tpvweb/#{action}.action"
141
+ xml = ssl_post("#{url}?", post_data(parameters))
142
+ response = parse(xml)
143
+ Response.new(
144
+ response[:success],
145
+ message_from(response),
146
+ response,
147
+ test: test?,
148
+ authorization: build_authorization(response),
149
+ error_code: response[:error_code]
150
+ )
151
+ end
152
+
153
+ def message_from(response)
154
+ if response[:message] == 'ERROR' && response[:error_message]
155
+ response[:error_message]
156
+ elsif response[:error_message]
157
+ "#{response[:message]} #{response[:error_message]}"
158
+ else
159
+ response[:message]
160
+ end
161
+ end
162
+
163
+ def post_data(params)
164
+ return nil unless params
165
+
166
+ params.map do |key, value|
167
+ next if value.blank?
168
+
169
+ if value.is_a?(Hash)
170
+ h = {}
171
+ value.each do |k, v|
172
+ h["#{key}.#{k}"] = v unless v.blank?
173
+ end
174
+ post_data(h)
175
+ else
176
+ "#{key}=#{CGI.escape(value.to_s)}"
177
+ end
178
+ end.compact.join('&')
179
+ end
180
+
181
+ def build_authorization(response)
182
+ [response[:reference], response[:authorization]].join('|')
183
+ end
184
+
185
+ def split_authorization(authorization)
186
+ authorization.split('|')
187
+ end
188
+
189
+ def generate_signature(action, parameters)
190
+ signature_fields =
191
+ case action
192
+ when CECA_ACTION_REFUND
193
+ options[:signature_key].to_s +
194
+ options[:merchant_id].to_s +
195
+ options[:acquirer_bin].to_s +
196
+ options[:terminal_id].to_s +
197
+ parameters['Num_operacion'].to_s +
198
+ parameters['Importe'].to_s +
199
+ parameters['TipoMoneda'].to_s +
200
+ CECA_DECIMALS +
201
+ parameters['Referencia'].to_s +
202
+ CECA_ENCRIPTION
203
+ else
204
+ options[:signature_key].to_s +
205
+ options[:merchant_id].to_s +
206
+ options[:acquirer_bin].to_s +
207
+ options[:terminal_id].to_s +
208
+ parameters['Num_operacion'].to_s +
209
+ parameters['Importe'].to_s +
210
+ parameters['TipoMoneda'].to_s +
211
+ CECA_DECIMALS +
212
+ CECA_ENCRIPTION +
213
+ CECA_NOTIFICATIONS_URL +
214
+ CECA_NOTIFICATIONS_URL
215
+ end
216
+ Digest::SHA2.hexdigest(signature_fields)
217
+ end
218
+ end
219
+ end
220
+ end