activemerchant 1.28.0 → 1.29.1

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 (117) hide show
  1. data/CHANGELOG +41 -0
  2. data/CONTRIBUTORS +12 -0
  3. data/README.md +6 -0
  4. data/lib/active_merchant/billing/gateway.rb +2 -1
  5. data/lib/active_merchant/billing/gateways/authorize_net.rb +3 -2
  6. data/lib/active_merchant/billing/gateways/authorize_net_cim.rb +77 -78
  7. data/lib/active_merchant/billing/gateways/balanced.rb +0 -1
  8. data/lib/active_merchant/billing/gateways/banwire.rb +1 -2
  9. data/lib/active_merchant/billing/gateways/barclays_epdq.rb +19 -20
  10. data/lib/active_merchant/billing/gateways/beanstream/beanstream_core.rb +35 -36
  11. data/lib/active_merchant/billing/gateways/blue_pay.rb +135 -140
  12. data/lib/active_merchant/billing/gateways/braintree_blue.rb +12 -4
  13. data/lib/active_merchant/billing/gateways/card_stream.rb +54 -59
  14. data/lib/active_merchant/billing/gateways/certo_direct.rb +0 -1
  15. data/lib/active_merchant/billing/gateways/cyber_source.rb +19 -14
  16. data/lib/active_merchant/billing/gateways/data_cash.rb +106 -112
  17. data/lib/active_merchant/billing/gateways/efsnet.rb +29 -34
  18. data/lib/active_merchant/billing/gateways/elavon.rb +7 -1
  19. data/lib/active_merchant/billing/gateways/epay.rb +0 -1
  20. data/lib/active_merchant/billing/gateways/eway.rb +88 -93
  21. data/lib/active_merchant/billing/gateways/eway_managed.rb +47 -51
  22. data/lib/active_merchant/billing/gateways/eway_rapid.rb +300 -0
  23. data/lib/active_merchant/billing/gateways/exact.rb +45 -54
  24. data/lib/active_merchant/billing/gateways/federated_canada.rb +3 -4
  25. data/lib/active_merchant/billing/gateways/first_pay.rb +37 -38
  26. data/lib/active_merchant/billing/gateways/garanti.rb +1 -2
  27. data/lib/active_merchant/billing/gateways/hdfc.rb +207 -0
  28. data/lib/active_merchant/billing/gateways/ideal/ideal_base.rb +5 -8
  29. data/lib/active_merchant/billing/gateways/inspire.rb +52 -52
  30. data/lib/active_merchant/billing/gateways/instapay.rb +10 -11
  31. data/lib/active_merchant/billing/gateways/iridium.rb +38 -39
  32. data/lib/active_merchant/billing/gateways/itransact.rb +7 -9
  33. data/lib/active_merchant/billing/gateways/jetpay.rb +45 -46
  34. data/lib/active_merchant/billing/gateways/linkpoint.rb +104 -108
  35. data/lib/active_merchant/billing/gateways/litle.rb +1 -5
  36. data/lib/active_merchant/billing/gateways/merchant_e_solutions.rb +153 -155
  37. data/lib/active_merchant/billing/gateways/merchant_ware.rb +49 -50
  38. data/lib/active_merchant/billing/gateways/mercury.rb +272 -0
  39. data/lib/active_merchant/billing/gateways/metrics_global.rb +9 -10
  40. data/lib/active_merchant/billing/gateways/migs.rb +5 -3
  41. data/lib/active_merchant/billing/gateways/modern_payments.rb +6 -7
  42. data/lib/active_merchant/billing/gateways/modern_payments_cim.rb +40 -41
  43. data/lib/active_merchant/billing/gateways/moneris.rb +46 -50
  44. data/lib/active_merchant/billing/gateways/moneris_us.rb +52 -55
  45. data/lib/active_merchant/billing/gateways/nab_transact.rb +0 -5
  46. data/lib/active_merchant/billing/gateways/net_registry.rb +20 -21
  47. data/lib/active_merchant/billing/gateways/netaxept.rb +30 -36
  48. data/lib/active_merchant/billing/gateways/netbilling.rb +2 -2
  49. data/lib/active_merchant/billing/gateways/ogone.rb +0 -5
  50. data/lib/active_merchant/billing/gateways/optimal_payment.rb +1 -6
  51. data/lib/active_merchant/billing/gateways/orbital/avs_result.rb +93 -0
  52. data/lib/active_merchant/billing/gateways/orbital.rb +25 -21
  53. data/lib/active_merchant/billing/gateways/pay_gate_xml.rb +1 -6
  54. data/lib/active_merchant/billing/gateways/pay_junction.rb +62 -63
  55. data/lib/active_merchant/billing/gateways/pay_secure.rb +29 -30
  56. data/lib/active_merchant/billing/gateways/paybox_direct.rb +0 -5
  57. data/lib/active_merchant/billing/gateways/payflow/payflow_common_api.rb +33 -38
  58. data/lib/active_merchant/billing/gateways/payment_express.rb +48 -51
  59. data/lib/active_merchant/billing/gateways/paypal/paypal_common_api.rb +7 -11
  60. data/lib/active_merchant/billing/gateways/paypal/paypal_express_response.rb +7 -0
  61. data/lib/active_merchant/billing/gateways/paypal/paypal_recurring_api.rb +3 -0
  62. data/lib/active_merchant/billing/gateways/paystation.rb +62 -64
  63. data/lib/active_merchant/billing/gateways/payway.rb +2 -9
  64. data/lib/active_merchant/billing/gateways/plugnpay.rb +0 -1
  65. data/lib/active_merchant/billing/gateways/psigate.rb +102 -94
  66. data/lib/active_merchant/billing/gateways/psl_card.rb +66 -67
  67. data/lib/active_merchant/billing/gateways/qbms.rb +0 -6
  68. data/lib/active_merchant/billing/gateways/quantum.rb +2 -8
  69. data/lib/active_merchant/billing/gateways/quickpay.rb +2 -3
  70. data/lib/active_merchant/billing/gateways/realex.rb +6 -16
  71. data/lib/active_merchant/billing/gateways/redsys.rb +394 -0
  72. data/lib/active_merchant/billing/gateways/sage/sage_core.rb +25 -26
  73. data/lib/active_merchant/billing/gateways/sage.rb +15 -16
  74. data/lib/active_merchant/billing/gateways/sage_pay.rb +51 -56
  75. data/lib/active_merchant/billing/gateways/sallie_mae.rb +1 -2
  76. data/lib/active_merchant/billing/gateways/samurai.rb +1 -4
  77. data/lib/active_merchant/billing/gateways/secure_net.rb +0 -1
  78. data/lib/active_merchant/billing/gateways/secure_pay.rb +5 -8
  79. data/lib/active_merchant/billing/gateways/secure_pay_au.rb +0 -5
  80. data/lib/active_merchant/billing/gateways/secure_pay_tech.rb +17 -18
  81. data/lib/active_merchant/billing/gateways/skip_jack.rb +29 -34
  82. data/lib/active_merchant/billing/gateways/smart_ps.rb +55 -56
  83. data/lib/active_merchant/billing/gateways/stripe.rb +8 -3
  84. data/lib/active_merchant/billing/gateways/trans_first.rb +28 -29
  85. data/lib/active_merchant/billing/gateways/trust_commerce.rb +85 -87
  86. data/lib/active_merchant/billing/gateways/usa_epay_advanced.rb +27 -28
  87. data/lib/active_merchant/billing/gateways/usa_epay_transaction.rb +0 -5
  88. data/lib/active_merchant/billing/gateways/verifi.rb +86 -87
  89. data/lib/active_merchant/billing/gateways/viaklix.rb +42 -47
  90. data/lib/active_merchant/billing/gateways/vindicia.rb +30 -28
  91. data/lib/active_merchant/billing/gateways/webpay.rb +45 -0
  92. data/lib/active_merchant/billing/gateways/wirecard.rb +0 -6
  93. data/lib/active_merchant/billing/gateways/worldpay.rb +4 -9
  94. data/lib/active_merchant/billing/gateways.rb +6 -7
  95. data/lib/active_merchant/billing/integrations/a1agregator/helper.rb +31 -0
  96. data/lib/active_merchant/billing/integrations/a1agregator/notification.rb +186 -0
  97. data/lib/active_merchant/billing/integrations/a1agregator/status.rb +38 -0
  98. data/lib/active_merchant/billing/integrations/a1agregator.rb +26 -0
  99. data/lib/active_merchant/billing/integrations/liqpay/helper.rb +43 -0
  100. data/lib/active_merchant/billing/integrations/liqpay/notification.rb +89 -0
  101. data/lib/active_merchant/billing/integrations/liqpay/return.rb +83 -0
  102. data/lib/active_merchant/billing/integrations/liqpay.rb +30 -0
  103. data/lib/active_merchant/billing/integrations/moneybookers/helper.rb +17 -1
  104. data/lib/active_merchant/billing/integrations/notification.rb +4 -0
  105. data/lib/active_merchant/billing/integrations/pay_fast/common.rb +42 -0
  106. data/lib/active_merchant/billing/integrations/pay_fast/helper.rb +50 -0
  107. data/lib/active_merchant/billing/integrations/pay_fast/notification.rb +134 -0
  108. data/lib/active_merchant/billing/integrations/pay_fast/return.rb +10 -0
  109. data/lib/active_merchant/billing/integrations/pay_fast.rb +70 -0
  110. data/lib/active_merchant/billing/integrations/paypal/notification.rb +64 -0
  111. data/lib/active_merchant/billing/integrations/sage_pay_form/helper.rb +10 -7
  112. data/lib/active_merchant/billing/integrations/webmoney/notification.rb +12 -0
  113. data/lib/active_merchant/billing/response.rb +17 -4
  114. data/lib/active_merchant/version.rb +1 -1
  115. data.tar.gz.sig +0 -0
  116. metadata +46 -27
  117. metadata.gz.sig +0 -0
@@ -37,15 +37,9 @@ module ActiveMerchant #:nodoc:
37
37
  #
38
38
  def initialize(options = {})
39
39
  requires!(options, :login, :password)
40
- @options = options
41
40
  super
42
41
  end
43
42
 
44
- # Should run against the test servers or not?
45
- def test?
46
- @options[:test] || Base.gateway_mode == :test
47
- end
48
-
49
43
  # Request an authorization for an amount from CyberSource
50
44
  #
51
45
  def authorize(money, creditcard, options = {})
@@ -268,11 +262,11 @@ module ActiveMerchant #:nodoc:
268
262
  end
269
263
  return reply
270
264
  end
271
-
265
+
272
266
  def authorization_for(reply)
273
267
  "#{reply[:TransactionID]};#{reply[:CreditCardNumber]}"
274
268
  end
275
-
269
+
276
270
  def authorization_parts_from(authorization)
277
271
  authorization.split(/;/)
278
272
  end
@@ -93,7 +93,6 @@ module ActiveMerchant #:nodoc:
93
93
  def initialize(options = {})
94
94
  requires!(options, :login, :password)
95
95
  @protocol = options.delete(:version) || 3 # default to protocol version 3
96
- @options = options
97
96
  super
98
97
  end
99
98
 
@@ -215,10 +214,10 @@ module ActiveMerchant #:nodoc:
215
214
  end
216
215
 
217
216
  def add_testmode(post)
218
- return if post[:transaction].present?
217
+ return if post[:transaction].present?
219
218
  post[:testmode] = test? ? '1' : '0'
220
219
  end
221
-
220
+
222
221
  def add_fraud_parameters(post, options)
223
222
  if @protocol == 4
224
223
  post[:fraud_remote_addr] = options[:fraud_remote_addr] if options[:fraud_remote_addr]
@@ -20,7 +20,7 @@ module ActiveMerchant
20
20
  # same order id
21
21
  class RealexGateway < Gateway
22
22
  self.live_url = self.test_url = 'https://epage.payandshop.com/epage-remote.cgi'
23
-
23
+
24
24
  CARD_MAPPING = {
25
25
  'master' => 'MC',
26
26
  'visa' => 'VISA',
@@ -45,7 +45,6 @@ module ActiveMerchant
45
45
  def initialize(options = {})
46
46
  requires!(options, :login, :password)
47
47
  options[:refund_hash] = Digest::SHA1.hexdigest(options[:rebate_secret]) if options.has_key?(:rebate_secret)
48
- @options = options
49
48
  super
50
49
  end
51
50
 
@@ -81,10 +80,10 @@ module ActiveMerchant
81
80
  def void(authorization, options = {})
82
81
  request = build_void_request(authorization, options)
83
82
  commit(request)
84
- end
85
-
86
- private
87
- def commit(request)
83
+ end
84
+
85
+ private
86
+ def commit(request)
88
87
  response = parse(ssl_post(self.live_url, request))
89
88
 
90
89
  Response.new(response[:result] == "00", message_from(response), response,
@@ -188,7 +187,7 @@ module ActiveMerchant
188
187
 
189
188
  if billing_address
190
189
  xml.tag! 'address', 'type' => 'billing' do
191
- xml.tag! 'code', avs_input_code( billing_address )
190
+ xml.tag! 'code', format_shipping_zip_code(billing_address[:zip])
192
191
  xml.tag! 'country', billing_address[:country]
193
192
  end
194
193
  end
@@ -241,19 +240,10 @@ module ActiveMerchant
241
240
  end
242
241
  end
243
242
 
244
- def avs_input_code(address)
245
- address.values_at(:zip, :address1).map{ |v| extract_digits(v) }.join('|')
246
- end
247
-
248
243
  def format_shipping_zip_code(zip)
249
244
  zip.to_s.gsub(/\W/, '')
250
245
  end
251
246
 
252
- def extract_digits(string)
253
- return "" if string.nil?
254
- string.gsub(/[\D]/,'')
255
- end
256
-
257
247
  def new_timestamp
258
248
  Time.now.strftime('%Y%m%d%H%M%S')
259
249
  end
@@ -0,0 +1,394 @@
1
+ # coding: utf-8
2
+ require 'nokogiri'
3
+
4
+ module ActiveMerchant #:nodoc:
5
+ module Billing #:nodoc:
6
+ # = Redsys Merchant Gateway
7
+ #
8
+ # Gateway support for the Spanish "Redsys" payment gateway system. This is
9
+ # used by many banks in Spain and is particularly well supported by
10
+ # Catalunya Caixa's ecommerce department.
11
+ #
12
+ # Standard ActiveMerchant methods are supported, with one notable exception:
13
+ # :order_id must be provided and must conform to a very specific format.
14
+ #
15
+ # == Example use:
16
+ #
17
+ # gateway = ActiveMerchant::Billing::RedsysGateway.new(
18
+ # :login => "091358382",
19
+ # :secret_key => "qwertyasdf0123456789"
20
+ # )
21
+ #
22
+ # # Run a purchase for 10 euros
23
+ # response = gateway.purchase(1000, creditcard, :order_id => "123456")
24
+ # puts reponse.success? # => true
25
+ #
26
+ # # Partially refund the purchase
27
+ # response = gateway.refund(500, response.authorization)
28
+ #
29
+ # Redsys requires an order_id be provided with each transaction of a
30
+ # specific format. The rules are as follows:
31
+ #
32
+ # * Minimum length: 4
33
+ # * Maximum length: 12
34
+ # * First 4 digits must be numerical
35
+ # * Remaining 8 digits may be alphanumeric
36
+ #
37
+ # Much of the code for this library is based on the active_merchant_sermepa
38
+ # integration gateway which uses essentially the same API but with the
39
+ # banks own payment screen.
40
+ #
41
+ # Written by Samuel Lown for Cabify. For implementation questions, or
42
+ # test access details please get in touch: sam@cabify.com.
43
+ class RedsysGateway < Gateway
44
+ self.live_url = "https://sis.sermepa.es/sis/operaciones"
45
+ self.test_url = "https://sis-t.sermepa.es:25443/sis/operaciones"
46
+
47
+ # Sensible region specific defaults.
48
+ self.supported_countries = ['ES']
49
+ self.default_currency = 'EUR'
50
+ self.money_format = :cents
51
+
52
+ # Not all card types may be actived by the bank!
53
+ self.supported_cardtypes = [:visa, :master, :american_express, :jcb, :diners_club]
54
+
55
+ # Homepage URL of the gateway for reference
56
+ self.homepage_url = "http://www.redsys.es/"
57
+
58
+ # What to call this gateway
59
+ self.display_name = "Redsys"
60
+
61
+ CURRENCY_CODES = {
62
+ "ARS" => '032',
63
+ "AUD" => '036',
64
+ "BRL" => '986',
65
+ "BOB" => '068',
66
+ "CAD" => '124',
67
+ "CHF" => '756',
68
+ "CLP" => '152',
69
+ "COP" => '170',
70
+ "EUR" => '978',
71
+ "GBP" => '826',
72
+ "GTQ" => '320',
73
+ "JPY" => '392',
74
+ "MXN" => '484',
75
+ "NZD" => '554',
76
+ "PEN" => '604',
77
+ "RUB" => '643',
78
+ "USD" => '840',
79
+ "UYU" => '858'
80
+ }
81
+
82
+ # The set of supported transactions for this gateway.
83
+ # More operations are supported by the gateway itself, but
84
+ # are not supported in this library.
85
+ SUPPORTED_TRANSACTIONS = {
86
+ :purchase => 'A',
87
+ :authorize => '1',
88
+ :capture => '2',
89
+ :refund => '3',
90
+ :cancel => '9'
91
+ }
92
+
93
+ # These are the text meanings sent back by the acquirer when
94
+ # a card has been rejected. Syntax or general request errors
95
+ # are not covered here.
96
+ RESPONSE_TEXTS = {
97
+ # Accepted Codes
98
+ 0 => "Transaction Approved",
99
+ 400 => "Cancellation Accepted",
100
+ 481 => "Cancellation Accepted",
101
+ 500 => "Reconciliation Accepted",
102
+ 900 => "Refund / Confirmation approved",
103
+
104
+ # Declined error codes
105
+ 101 => "Card expired",
106
+ 102 => "Card blocked temporarily or under susciption of fraud",
107
+ 104 => "Transaction not permitted",
108
+ 107 => "Contact the card issuer",
109
+ 109 => "Invalid identification by merchant or POS terminal",
110
+ 110 => "Invalid amount",
111
+ 114 => "Card cannot be used to the requested transaction",
112
+ 116 => "Insufficient credit",
113
+ 118 => "Non-registered card",
114
+ 125 => "Card not effective",
115
+ 129 => "CVV2/CVC2 Error",
116
+ 167 => "Contact the card issuer: suspected fraud",
117
+ 180 => "Card out of service",
118
+ 181 => "Card with credit or debit restrictions",
119
+ 182 => "Card with credit or debit restrictions",
120
+ 184 => "Authentication error",
121
+ 190 => "Refusal with no specific reason",
122
+ 191 => "Expiry date incorrect",
123
+
124
+ # Declined, and suspected of fraud
125
+ 201 => "Card expired",
126
+ 202 => "Card blocked temporarily or under suscipition of fraud",
127
+ 204 => "Transaction not permitted",
128
+ 207 => "Contact the card issuer",
129
+ 208 => "Lost or stolen card",
130
+ 209 => "Lost or stolen card",
131
+ 280 => "CVV2/CVC2 Error",
132
+ 290 => "Declined with no specific reason",
133
+
134
+ # More general codes for specific types of transaction
135
+ 480 => "Original transaction not located, or time-out exceeded",
136
+ 501 => "Original transaction not located, or time-out exceeded",
137
+ 502 => "Original transaction not located, or time-out exceeded",
138
+ 503 => "Original transaction not located, or time-out exceeded",
139
+
140
+ # Declined transactions by the bank
141
+ 904 => "Merchant not registered at FUC",
142
+ 909 => "System error",
143
+ 912 => "Issuer not available",
144
+ 913 => "Duplicate transmission",
145
+ 916 => "Amount too low",
146
+ 928 => "Time-out exceeded",
147
+ 940 => "Transaction cancelled previously",
148
+ 941 => "Authorization operation already cancelled",
149
+ 942 => "Original authorization declined",
150
+ 943 => "Different details from origin transaction",
151
+ 944 => "Session error",
152
+ 945 => "Duplicate transmission",
153
+ 946 => "Cancellation of transaction while in progress",
154
+ 947 => "Duplicate tranmission while in progress",
155
+ 949 => "POS Inoperative",
156
+ 950 => "Refund not possible",
157
+ 9064 => "Card number incorrect",
158
+ 9078 => "No payment method available",
159
+ 9093 => "Non-existent card",
160
+ 9218 => "Recursive transaction in bad gateway",
161
+ 9253 => "Check-digit incorrect",
162
+ 9256 => "Preauth not allowed for merchant",
163
+ 9257 => "Preauth not allowed for card",
164
+ 9261 => "Operating limit exceeded",
165
+ 9912 => "Issuer not available",
166
+ 9913 => "Confirmation error",
167
+ 9914 => "KO Confirmation"
168
+ }
169
+
170
+ # Creates a new instance
171
+ #
172
+ # Redsys requires a login and secret_key, and optionally also accepts a
173
+ # non-default terminal.
174
+ #
175
+ # ==== Options
176
+ #
177
+ # * <tt>:login</tt> -- The Redsys Merchant ID (REQUIRED)
178
+ # * <tt>:secret_key</tt> -- The Redsys Secret Key. (REQUIRED)
179
+ # * <tt>:terminal</tt> -- The Redsys Terminal. Defaults to 1. (OPTIONAL)
180
+ # * <tt>:test</tt> -- +true+ or +false+. Defaults to +false+. (OPTIONAL)
181
+ def initialize(options = {})
182
+ requires!(options, :login, :secret_key)
183
+ options[:terminal] ||= 1
184
+ super
185
+ end
186
+
187
+ def purchase(money, creditcard, options = {})
188
+ requires!(options, :order_id)
189
+
190
+ data = {}
191
+ add_action(data, :purchase)
192
+ add_amount(data, money, options)
193
+ add_order(data, options[:order_id])
194
+ add_creditcard(data, creditcard)
195
+
196
+ commit data
197
+ end
198
+
199
+ def authorize(money, creditcard, options = {})
200
+ requires!(options, :order_id)
201
+
202
+ data = {}
203
+ add_action(data, :authorize)
204
+ add_amount(data, money, options)
205
+ add_order(data, options[:order_id])
206
+ add_creditcard(data, creditcard)
207
+
208
+ commit data
209
+ end
210
+
211
+ def capture(money, authorization, options = {})
212
+ data = {}
213
+ add_action(data, :capture)
214
+ add_amount(data, money, options)
215
+ order_id, _, _ = split_authorization(authorization)
216
+ add_order(data, order_id)
217
+
218
+ commit data
219
+ end
220
+
221
+ def void(authorization, options = {})
222
+ data = {}
223
+ add_action(data, :cancel)
224
+ order_id, amount, currency = split_authorization(authorization)
225
+ add_amount(data, amount, :currency => currency)
226
+ add_order(data, order_id)
227
+
228
+ commit data
229
+ end
230
+
231
+ def refund(money, authorization, options = {})
232
+ data = {}
233
+ add_action(data, :refund)
234
+ add_amount(data, money, options)
235
+ order_id, _, _ = split_authorization(authorization)
236
+ add_order(data, order_id)
237
+
238
+ commit data
239
+ end
240
+
241
+ private
242
+
243
+ def add_action(data, action)
244
+ data[:action] = transaction_code(action)
245
+ end
246
+
247
+ def add_amount(data, money, options)
248
+ data[:amount] = amount(money).to_s
249
+ data[:currency] = currency_code(options[:currency] || currency(money))
250
+ end
251
+
252
+ def add_order(data, order_id)
253
+ raise ArgumentError.new("Invalid order_id format") unless(/^\d{4}[\da-zA-Z]{0,8}$/ =~ order_id)
254
+ data[:order_id] = order_id
255
+ end
256
+
257
+ def url
258
+ test? ? test_url : live_url
259
+ end
260
+
261
+ def add_creditcard(data, card)
262
+ name = [card.first_name, card.last_name].join(' ').slice(0, 60)
263
+ year = sprintf("%.4i", card.year)
264
+ month = sprintf("%.2i", card.month)
265
+ data[:card] = {
266
+ :name => name,
267
+ :pan => card.number,
268
+ :date => "#{year[2..3]}#{month}",
269
+ :cvv => card.verification_value
270
+ }
271
+ end
272
+
273
+ def commit(data)
274
+ headers = {
275
+ 'Content-Type' => 'application/x-www-form-urlencoded'
276
+ }
277
+ xml = build_xml_request(data)
278
+ parse(ssl_post(url, "entrada=#{CGI.escape(xml)}", headers))
279
+ end
280
+
281
+ def build_signature(data)
282
+ str = data[:amount] +
283
+ data[:order_id].to_s +
284
+ @options[:login].to_s +
285
+ data[:currency]
286
+
287
+ if card = data[:card]
288
+ str << card[:pan]
289
+ str << card[:cvv] if card[:cvv]
290
+ end
291
+
292
+ str << data[:action]
293
+ str << @options[:secret_key]
294
+
295
+ Digest::SHA1.hexdigest(str)
296
+ end
297
+
298
+ def build_xml_request(data)
299
+ xml = Builder::XmlMarkup.new :indent => 2
300
+ xml.DATOSENTRADA do
301
+ # Basic elements
302
+ xml.DS_Version 0.1
303
+ xml.DS_MERCHANT_CURRENCY data[:currency]
304
+ xml.DS_MERCHANT_AMOUNT data[:amount]
305
+ xml.DS_MERCHANT_ORDER data[:order_id]
306
+ xml.DS_MERCHANT_TRANSACTIONTYPE data[:action]
307
+ xml.DS_MERCHANT_TERMINAL @options[:terminal]
308
+ xml.DS_MERCHANT_MERCHANTCODE @options[:login]
309
+ xml.DS_MERCHANT_MERCHANTSIGNATURE build_signature(data)
310
+
311
+ # Only when card is present
312
+ if data[:card]
313
+ xml.DS_MERCHANT_TITULAR data[:card][:name]
314
+ xml.DS_MERCHANT_PAN data[:card][:pan]
315
+ xml.DS_MERCHANT_EXPIRYDATE data[:card][:date]
316
+ xml.DS_MERCHANT_CVV2 data[:card][:cvv]
317
+ end
318
+ end
319
+ xml.target!
320
+ end
321
+
322
+ def parse(data)
323
+ params = {}
324
+ success = false
325
+ message = ""
326
+ options = @options.merge(:test => test?)
327
+ xml = Nokogiri::XML(data)
328
+ code = xml.xpath("//RETORNOXML/CODIGO").text
329
+ if code == "0"
330
+ op = xml.xpath("//RETORNOXML/OPERACION")
331
+ op.children.each do |element|
332
+ params[element.name.downcase.to_sym] = element.text
333
+ end
334
+
335
+ if validate_signature(params)
336
+ message = response_text(params[:ds_response])
337
+ options[:authorization] = build_authorization(params)
338
+ success = is_success_response?(params[:ds_response])
339
+ else
340
+ message = "Response failed validation check"
341
+ end
342
+ else
343
+ # Some kind of programmer error with the request!
344
+ message = "#{code} ERROR"
345
+ end
346
+
347
+ Response.new(success, message, params, options)
348
+ end
349
+
350
+ def validate_signature(data)
351
+ str = data[:ds_amount] +
352
+ data[:ds_order].to_s +
353
+ data[:ds_merchantcode] +
354
+ data[:ds_currency] +
355
+ data[:ds_response] +
356
+ data[:ds_cardnumber].to_s +
357
+ data[:ds_transactiontype].to_s +
358
+ data[:ds_securepayment].to_s +
359
+ @options[:secret_key]
360
+
361
+ sig = Digest::SHA1.hexdigest(str)
362
+ data[:ds_signature].to_s.downcase == sig
363
+ end
364
+
365
+ def build_authorization(params)
366
+ [params[:ds_order], params[:ds_amount], params[:ds_currency]].join("|")
367
+ end
368
+
369
+ def split_authorization(authorization)
370
+ order_id, amount, currency = authorization.split("|")
371
+ [order_id, amount.to_i, currency]
372
+ end
373
+
374
+ def currency_code(currency)
375
+ return currency if currency =~ /^\d+$/
376
+ CURRENCY_CODES[currency]
377
+ end
378
+
379
+ def transaction_code(type)
380
+ SUPPORTED_TRANSACTIONS[type]
381
+ end
382
+
383
+ def response_text(code)
384
+ code = code.to_i
385
+ code = 0 if code < 100
386
+ RESPONSE_TEXTS[code] || "Unkown code, please check in manual"
387
+ end
388
+
389
+ def is_success_response?(code)
390
+ (code.to_i < 100) || [400, 481, 500, 900].include?(code.to_i)
391
+ end
392
+ end
393
+ end
394
+ end
@@ -7,13 +7,13 @@ module ActiveMerchant #:nodoc:
7
7
  base.homepage_url = 'http://www.sagepayments.com'
8
8
  base.display_name = 'Sage Payment Solutions'
9
9
  end
10
-
10
+
11
11
  # Transactions types:
12
12
  # <tt>01</tt> - Sale
13
- # <tt>02</tt> - AuthOnly
14
- # <tt>03</tt> - Force/PriorAuthSale
15
- # <tt>04</tt> - Void
16
- # <tt>06</tt> - Credit
13
+ # <tt>02</tt> - AuthOnly
14
+ # <tt>03</tt> - Force/PriorAuthSale
15
+ # <tt>04</tt> - Void
16
+ # <tt>06</tt> - Credit
17
17
  # <tt>11</tt> - PriorAuthSale by Reference*
18
18
  TRANSACTIONS = {
19
19
  :purchase => '01',
@@ -22,83 +22,82 @@ module ActiveMerchant #:nodoc:
22
22
  :void => '04',
23
23
  :credit => '06'
24
24
  }
25
-
25
+
26
26
  def initialize(options = {})
27
27
  requires!(options, :login, :password)
28
- @options = options
29
28
  super
30
29
  end
31
-
30
+
32
31
  private
33
32
  def add_invoice(post, options)
34
33
  post[:T_ordernum] = options[:order_id].slice(0, 20)
35
34
  post[:T_tax] = amount(options[:tax]) unless options[:tax].blank?
36
35
  post[:T_shipping] = amount(options[:shipping]) unless options[:shipping].blank?
37
36
  end
38
-
37
+
39
38
  def add_reference(post, reference)
40
39
  ref, source = reference.to_s.split(";")
41
40
  post[:T_reference] = ref
42
41
  end
43
-
42
+
44
43
  def add_amount(post, money)
45
44
  post[:T_amt] = amount(money)
46
45
  end
47
-
46
+
48
47
  def add_customer_data(post, options)
49
48
  post[:T_customer_number] = options[:customer]
50
49
  end
51
50
 
52
51
  def add_addresses(post, options)
53
52
  billing_address = options[:billing_address] || options[:address] || {}
54
-
53
+
55
54
  post[:C_address] = billing_address[:address1]
56
55
  post[:C_city] = billing_address[:city]
57
-
56
+
58
57
  if ['US', 'CA'].include?(billing_address[:country])
59
58
  post[:C_state] = billing_address[:state]
60
59
  else
61
60
  post[:C_state] = "Outside of United States"
62
61
  end
63
-
62
+
64
63
  post[:C_zip] = billing_address[:zip]
65
- post[:C_country] = billing_address[:country]
64
+ post[:C_country] = billing_address[:country]
66
65
  post[:C_telephone] = billing_address[:phone]
67
66
  post[:C_fax] = billing_address[:fax]
68
67
  post[:C_email] = options[:email]
69
-
68
+
70
69
  if shipping_address = options[:shipping_address]
71
70
  post[:C_ship_name] = shipping_address[:name]
72
71
  post[:C_ship_address] = shipping_address[:address1]
73
72
  post[:C_ship_city] = shipping_address[:city]
74
73
  post[:C_ship_state] = shipping_address[:state]
75
- post[:C_ship_zip] = shipping_address[:zip]
74
+ post[:C_ship_zip] = shipping_address[:zip]
76
75
  post[:C_ship_country] = shipping_address[:country]
77
76
  end
78
77
  end
79
-
78
+
80
79
  def add_transaction_data(post, money, options)
81
80
  add_amount(post, money)
82
81
  add_invoice(post, options)
83
- add_addresses(post, options)
82
+ add_addresses(post, options)
84
83
  add_customer_data(post, options)
85
84
  end
86
-
85
+
87
86
  def commit(action, params)
88
87
  response = parse(ssl_post(self.live_url, post_data(action, params)))
89
-
90
- Response.new(success?(response), response[:message], response,
91
- :test => test?,
88
+
89
+ Response.new(success?(response), response[:message], response,
90
+ :test => test?,
92
91
  :authorization => authorization_from(response),
93
92
  :avs_result => { :code => response[:avs_result] },
94
93
  :cvv_result => response[:cvv_result]
95
94
  )
96
95
  end
97
-
96
+
98
97
  def authorization_from(response)
99
98
  "#{response[:reference]};#{source}"
100
99
  end
101
-
100
+
102
101
  def success?(response)
103
102
  response[:success] == 'A'
104
103
  end
@@ -107,7 +106,7 @@ module ActiveMerchant #:nodoc:
107
106
  params[:M_id] = @options[:login]
108
107
  params[:M_key] = @options[:password]
109
108
  params[:T_code] = TRANSACTIONS[action]
110
-
109
+
111
110
  params.collect { |key, value| "#{key}=#{CGI.escape(value.to_s)}" }.join("&")
112
111
  end
113
112
  end