activemerchant 1.130.0 → 1.137.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (184) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +268 -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 +13 -8
  6. data/lib/active_merchant/billing/credit_card_formatting.rb +4 -0
  7. data/lib/active_merchant/billing/credit_card_methods.rb +64 -7
  8. data/lib/active_merchant/billing/gateway.rb +9 -0
  9. data/lib/active_merchant/billing/gateways/adyen.rb +240 -41
  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 +44 -36
  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/borgun.rb +6 -4
  21. data/lib/active_merchant/billing/gateways/braintree/token_nonce.rb +65 -20
  22. data/lib/active_merchant/billing/gateways/braintree_blue.rb +226 -73
  23. data/lib/active_merchant/billing/gateways/braintree_orange.rb +1 -1
  24. data/lib/active_merchant/billing/gateways/card_connect.rb +5 -2
  25. data/lib/active_merchant/billing/gateways/card_stream.rb +4 -6
  26. data/lib/active_merchant/billing/gateways/cashnet.rb +1 -1
  27. data/lib/active_merchant/billing/gateways/cecabank/cecabank_common.rb +36 -0
  28. data/lib/active_merchant/billing/gateways/cecabank/cecabank_json.rb +316 -0
  29. data/lib/active_merchant/billing/gateways/cecabank/cecabank_xml.rb +220 -0
  30. data/lib/active_merchant/billing/gateways/cecabank.rb +7 -240
  31. data/lib/active_merchant/billing/gateways/checkout_v2.rb +252 -41
  32. data/lib/active_merchant/billing/gateways/commerce_hub.rb +69 -8
  33. data/lib/active_merchant/billing/gateways/credorax.rb +3 -5
  34. data/lib/active_merchant/billing/gateways/cyber_source.rb +192 -41
  35. data/lib/active_merchant/billing/gateways/cyber_source_rest.rb +102 -58
  36. data/lib/active_merchant/billing/gateways/d_local.rb +26 -15
  37. data/lib/active_merchant/billing/gateways/data_cash.rb +21 -17
  38. data/lib/active_merchant/billing/gateways/datatrans.rb +279 -0
  39. data/lib/active_merchant/billing/gateways/decidir.rb +53 -18
  40. data/lib/active_merchant/billing/gateways/decidir_plus.rb +4 -1
  41. data/lib/active_merchant/billing/gateways/deepstack.rb +382 -0
  42. data/lib/active_merchant/billing/gateways/ebanx.rb +40 -36
  43. data/lib/active_merchant/billing/gateways/efsnet.rb +6 -2
  44. data/lib/active_merchant/billing/gateways/elavon.rb +99 -33
  45. data/lib/active_merchant/billing/gateways/element.rb +36 -7
  46. data/lib/active_merchant/billing/gateways/epay.rb +6 -2
  47. data/lib/active_merchant/billing/gateways/evo_ca.rb +6 -2
  48. data/lib/active_merchant/billing/gateways/eway.rb +4 -2
  49. data/lib/active_merchant/billing/gateways/eway_managed.rb +6 -2
  50. data/lib/active_merchant/billing/gateways/exact.rb +6 -2
  51. data/lib/active_merchant/billing/gateways/fat_zebra.rb +31 -3
  52. data/lib/active_merchant/billing/gateways/federated_canada.rb +6 -2
  53. data/lib/active_merchant/billing/gateways/first_pay/first_pay_common.rb +15 -0
  54. data/lib/active_merchant/billing/gateways/first_pay/first_pay_json.rb +190 -0
  55. data/lib/active_merchant/billing/gateways/first_pay/first_pay_xml.rb +183 -0
  56. data/lib/active_merchant/billing/gateways/first_pay.rb +6 -172
  57. data/lib/active_merchant/billing/gateways/firstdata_e4.rb +6 -2
  58. data/lib/active_merchant/billing/gateways/firstdata_e4_v27.rb +7 -3
  59. data/lib/active_merchant/billing/gateways/flex_charge.rb +347 -0
  60. data/lib/active_merchant/billing/gateways/garanti.rb +4 -2
  61. data/lib/active_merchant/billing/gateways/global_collect.rb +45 -37
  62. data/lib/active_merchant/billing/gateways/hi_pay.rb +286 -0
  63. data/lib/active_merchant/billing/gateways/hps.rb +1 -1
  64. data/lib/active_merchant/billing/gateways/iats_payments.rb +7 -2
  65. data/lib/active_merchant/billing/gateways/inspire.rb +6 -4
  66. data/lib/active_merchant/billing/gateways/instapay.rb +7 -4
  67. data/lib/active_merchant/billing/gateways/ipg.rb +10 -6
  68. data/lib/active_merchant/billing/gateways/iridium.rb +15 -5
  69. data/lib/active_merchant/billing/gateways/itransact.rb +6 -2
  70. data/lib/active_merchant/billing/gateways/iveri.rb +3 -3
  71. data/lib/active_merchant/billing/gateways/ixopay.rb +2 -2
  72. data/lib/active_merchant/billing/gateways/jetpay.rb +4 -2
  73. data/lib/active_merchant/billing/gateways/jetpay_v2.rb +4 -2
  74. data/lib/active_merchant/billing/gateways/kushki.rb +73 -13
  75. data/lib/active_merchant/billing/gateways/linkpoint.rb +6 -2
  76. data/lib/active_merchant/billing/gateways/litle.rb +33 -50
  77. data/lib/active_merchant/billing/gateways/mastercard.rb +4 -4
  78. data/lib/active_merchant/billing/gateways/maxipago.rb +2 -2
  79. data/lib/active_merchant/billing/gateways/merchant_e_solutions.rb +8 -5
  80. data/lib/active_merchant/billing/gateways/merchant_ware.rb +11 -4
  81. data/lib/active_merchant/billing/gateways/merchant_ware_version_four.rb +11 -4
  82. data/lib/active_merchant/billing/gateways/merchant_warrior.rb +19 -3
  83. data/lib/active_merchant/billing/gateways/mercury.rb +6 -2
  84. data/lib/active_merchant/billing/gateways/metrics_global.rb +8 -6
  85. data/lib/active_merchant/billing/gateways/migs/migs_codes.rb +1 -0
  86. data/lib/active_merchant/billing/gateways/migs.rb +6 -2
  87. data/lib/active_merchant/billing/gateways/mit.rb +25 -20
  88. data/lib/active_merchant/billing/gateways/modern_payments_cim.rb +18 -10
  89. data/lib/active_merchant/billing/gateways/monei.rb +1 -1
  90. data/lib/active_merchant/billing/gateways/moneris.rb +9 -3
  91. data/lib/active_merchant/billing/gateways/money_movers.rb +6 -2
  92. data/lib/active_merchant/billing/gateways/nab_transact.rb +12 -4
  93. data/lib/active_merchant/billing/gateways/net_registry.rb +6 -2
  94. data/lib/active_merchant/billing/gateways/netbanx.rb +1 -3
  95. data/lib/active_merchant/billing/gateways/netbilling.rb +6 -2
  96. data/lib/active_merchant/billing/gateways/network_merchants.rb +6 -2
  97. data/lib/active_merchant/billing/gateways/nmi.rb +23 -6
  98. data/lib/active_merchant/billing/gateways/ogone.rb +6 -2
  99. data/lib/active_merchant/billing/gateways/openpay.rb +4 -2
  100. data/lib/active_merchant/billing/gateways/opp.rb +1 -2
  101. data/lib/active_merchant/billing/gateways/optimal_payment.rb +6 -2
  102. data/lib/active_merchant/billing/gateways/orbital/orbital_soft_descriptors.rb +1 -3
  103. data/lib/active_merchant/billing/gateways/orbital.rb +83 -24
  104. data/lib/active_merchant/billing/gateways/pac_net_raven.rb +7 -4
  105. data/lib/active_merchant/billing/gateways/pay_gate_xml.rb +6 -2
  106. data/lib/active_merchant/billing/gateways/pay_hub.rb +4 -2
  107. data/lib/active_merchant/billing/gateways/pay_junction.rb +6 -2
  108. data/lib/active_merchant/billing/gateways/pay_secure.rb +6 -2
  109. data/lib/active_merchant/billing/gateways/pay_trace.rb +31 -18
  110. data/lib/active_merchant/billing/gateways/payeezy.rb +19 -8
  111. data/lib/active_merchant/billing/gateways/payex.rb +4 -2
  112. data/lib/active_merchant/billing/gateways/payflow/payflow_common_api.rb +1 -1
  113. data/lib/active_merchant/billing/gateways/payflow.rb +1 -3
  114. data/lib/active_merchant/billing/gateways/payment_express.rb +8 -4
  115. data/lib/active_merchant/billing/gateways/paymentez.rb +23 -11
  116. data/lib/active_merchant/billing/gateways/paysafe.rb +12 -11
  117. data/lib/active_merchant/billing/gateways/payscout.rb +7 -4
  118. data/lib/active_merchant/billing/gateways/paystation.rb +7 -3
  119. data/lib/active_merchant/billing/gateways/payway.rb +6 -2
  120. data/lib/active_merchant/billing/gateways/payway_dot_com.rb +3 -3
  121. data/lib/active_merchant/billing/gateways/pin.rb +22 -4
  122. data/lib/active_merchant/billing/gateways/plexo.rb +49 -10
  123. data/lib/active_merchant/billing/gateways/plugnpay.rb +6 -2
  124. data/lib/active_merchant/billing/gateways/priority.rb +6 -5
  125. data/lib/active_merchant/billing/gateways/psigate.rb +6 -2
  126. data/lib/active_merchant/billing/gateways/psl_card.rb +6 -2
  127. data/lib/active_merchant/billing/gateways/qbms.rb +6 -2
  128. data/lib/active_merchant/billing/gateways/quantum.rb +6 -2
  129. data/lib/active_merchant/billing/gateways/quickbooks.rb +6 -5
  130. data/lib/active_merchant/billing/gateways/quickpay/quickpay_v10.rb +7 -4
  131. data/lib/active_merchant/billing/gateways/quickpay/quickpay_v4to7.rb +6 -2
  132. data/lib/active_merchant/billing/gateways/rapyd.rb +148 -46
  133. data/lib/active_merchant/billing/gateways/reach.rb +11 -4
  134. data/lib/active_merchant/billing/gateways/redsys.rb +3 -11
  135. data/lib/active_merchant/billing/gateways/redsys_rest.rb +507 -0
  136. data/lib/active_merchant/billing/gateways/s5.rb +3 -3
  137. data/lib/active_merchant/billing/gateways/safe_charge.rb +38 -17
  138. data/lib/active_merchant/billing/gateways/sage.rb +12 -4
  139. data/lib/active_merchant/billing/gateways/sage_pay.rb +79 -5
  140. data/lib/active_merchant/billing/gateways/sallie_mae.rb +6 -2
  141. data/lib/active_merchant/billing/gateways/secure_net.rb +6 -2
  142. data/lib/active_merchant/billing/gateways/secure_pay.rb +8 -6
  143. data/lib/active_merchant/billing/gateways/secure_pay_au.rb +12 -4
  144. data/lib/active_merchant/billing/gateways/secure_pay_tech.rb +6 -2
  145. data/lib/active_merchant/billing/gateways/securion_pay.rb +24 -10
  146. data/lib/active_merchant/billing/gateways/shift4.rb +17 -20
  147. data/lib/active_merchant/billing/gateways/shift4_v2.rb +117 -0
  148. data/lib/active_merchant/billing/gateways/simetrik.rb +17 -11
  149. data/lib/active_merchant/billing/gateways/skip_jack.rb +6 -2
  150. data/lib/active_merchant/billing/gateways/smart_ps.rb +7 -4
  151. data/lib/active_merchant/billing/gateways/so_easy_pay.rb +4 -2
  152. data/lib/active_merchant/billing/gateways/spreedly_core.rb +2 -4
  153. data/lib/active_merchant/billing/gateways/stripe.rb +63 -19
  154. data/lib/active_merchant/billing/gateways/stripe_payment_intents.rb +280 -88
  155. data/lib/active_merchant/billing/gateways/sum_up.rb +223 -0
  156. data/lib/active_merchant/billing/gateways/swipe_checkout.rb +4 -2
  157. data/lib/active_merchant/billing/gateways/telr.rb +3 -4
  158. data/lib/active_merchant/billing/gateways/trans_first.rb +1 -2
  159. data/lib/active_merchant/billing/gateways/trans_first_transaction_express.rb +8 -16
  160. data/lib/active_merchant/billing/gateways/transact_pro.rb +1 -1
  161. data/lib/active_merchant/billing/gateways/trust_commerce.rb +6 -2
  162. data/lib/active_merchant/billing/gateways/usa_epay_advanced.rb +9 -8
  163. data/lib/active_merchant/billing/gateways/usa_epay_transaction.rb +6 -2
  164. data/lib/active_merchant/billing/gateways/vanco.rb +2 -4
  165. data/lib/active_merchant/billing/gateways/vantiv_express.rb +587 -0
  166. data/lib/active_merchant/billing/gateways/verifi.rb +6 -2
  167. data/lib/active_merchant/billing/gateways/viaklix.rb +6 -2
  168. data/lib/active_merchant/billing/gateways/visanet_peru.rb +2 -2
  169. data/lib/active_merchant/billing/gateways/vpos.rb +4 -4
  170. data/lib/active_merchant/billing/gateways/wirecard.rb +7 -3
  171. data/lib/active_merchant/billing/gateways/wompi.rb +5 -0
  172. data/lib/active_merchant/billing/gateways/worldpay.rb +147 -93
  173. data/lib/active_merchant/billing/gateways/worldpay_online_payments.rb +13 -10
  174. data/lib/active_merchant/billing/gateways/xpay.rb +242 -0
  175. data/lib/active_merchant/billing/network_tokenization_credit_card.rb +1 -1
  176. data/lib/active_merchant/billing/response.rb +2 -2
  177. data/lib/active_merchant/connection.rb +3 -17
  178. data/lib/active_merchant/country.rb +1 -0
  179. data/lib/active_merchant/errors.rb +10 -0
  180. data/lib/active_merchant/version.rb +1 -1
  181. data/lib/support/gateway_support.rb +2 -2
  182. data/lib/support/ssl_verify.rb +4 -4
  183. data/lib/support/ssl_version.rb +6 -6
  184. 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