activemerchant 1.47.0 → 1.48.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/CHANGELOG +34 -0
  5. data/CONTRIBUTORS +16 -0
  6. data/README.md +8 -2
  7. data/lib/active_merchant/billing/credit_card.rb +16 -4
  8. data/lib/active_merchant/billing/gateway.rb +0 -1
  9. data/lib/active_merchant/billing/gateways/authorize_net.rb +48 -1
  10. data/lib/active_merchant/billing/gateways/axcessms.rb +181 -0
  11. data/lib/active_merchant/billing/gateways/braintree_blue.rb +16 -6
  12. data/lib/active_merchant/billing/gateways/cenpos.rb +272 -0
  13. data/lib/active_merchant/billing/gateways/epay.rb +8 -9
  14. data/lib/active_merchant/billing/gateways/exact.rb +9 -0
  15. data/lib/active_merchant/billing/gateways/fat_zebra.rb +33 -0
  16. data/lib/active_merchant/billing/gateways/firstdata_e4.rb +49 -3
  17. data/lib/active_merchant/billing/gateways/inspire.rb +7 -1
  18. data/lib/active_merchant/billing/gateways/iridium.rb +1 -1
  19. data/lib/active_merchant/billing/gateways/merchant_warrior.rb +2 -2
  20. data/lib/active_merchant/billing/gateways/monei.rb +307 -0
  21. data/lib/active_merchant/billing/gateways/nab_transact.rb +47 -37
  22. data/lib/active_merchant/billing/gateways/netbilling.rb +40 -7
  23. data/lib/active_merchant/billing/gateways/orbital.rb +45 -21
  24. data/lib/active_merchant/billing/gateways/pay_conex.rb +246 -0
  25. data/lib/active_merchant/billing/gateways/pay_hub.rb +213 -0
  26. data/lib/active_merchant/billing/gateways/paymill.rb +3 -2
  27. data/lib/active_merchant/billing/gateways/pin.rb +2 -2
  28. data/lib/active_merchant/billing/gateways/quickbooks.rb +6 -4
  29. data/lib/active_merchant/billing/gateways/qvalent.rb +179 -0
  30. data/lib/active_merchant/billing/gateways/redsys.rb +29 -15
  31. data/lib/active_merchant/billing/gateways/sage_pay.rb +1 -1
  32. data/lib/active_merchant/billing/gateways/stripe.rb +12 -3
  33. data/lib/active_merchant/billing/gateways/usa_epay_transaction.rb +8 -3
  34. data/lib/active_merchant/billing/gateways/worldpay.rb +2 -2
  35. data/lib/active_merchant/billing/gateways/worldpay_online_payments.rb +205 -0
  36. data/lib/active_merchant/billing/network_tokenization_credit_card.rb +12 -4
  37. data/lib/active_merchant/billing/response.rb +1 -1
  38. data/lib/active_merchant/posts_data.rb +6 -0
  39. data/lib/active_merchant/version.rb +1 -1
  40. data/lib/support/outbound_hosts.rb +13 -10
  41. metadata +9 -2
  42. metadata.gz.sig +0 -0
@@ -537,12 +537,22 @@ module ActiveMerchant #:nodoc:
537
537
  :first_name => credit_card_or_vault_id.first_name,
538
538
  :last_name => credit_card_or_vault_id.last_name
539
539
  )
540
- parameters[:credit_card] = {
541
- :number => credit_card_or_vault_id.number,
542
- :cvv => credit_card_or_vault_id.verification_value,
543
- :expiration_month => credit_card_or_vault_id.month.to_s.rjust(2, "0"),
544
- :expiration_year => credit_card_or_vault_id.year.to_s
545
- }
540
+ if credit_card_or_vault_id.is_a?(NetworkTokenizationCreditCard)
541
+ parameters[:apple_pay_card] = {
542
+ :number => credit_card_or_vault_id.number,
543
+ :expiration_month => credit_card_or_vault_id.month.to_s.rjust(2, "0"),
544
+ :expiration_year => credit_card_or_vault_id.year.to_s,
545
+ :cardholder_name => "#{credit_card_or_vault_id.first_name} #{credit_card_or_vault_id.last_name}",
546
+ :cryptogram => credit_card_or_vault_id.payment_cryptogram
547
+ }
548
+ else
549
+ parameters[:credit_card] = {
550
+ :number => credit_card_or_vault_id.number,
551
+ :cvv => credit_card_or_vault_id.verification_value,
552
+ :expiration_month => credit_card_or_vault_id.month.to_s.rjust(2, "0"),
553
+ :expiration_year => credit_card_or_vault_id.year.to_s
554
+ }
555
+ end
546
556
  end
547
557
  parameters[:billing] = map_address(options[:billing_address]) if options[:billing_address] && !options[:payment_method_token]
548
558
  parameters[:shipping] = map_address(options[:shipping_address]) if options[:shipping_address]
@@ -0,0 +1,272 @@
1
+ require "nokogiri"
2
+
3
+ module ActiveMerchant #:nodoc:
4
+ module Billing #:nodoc:
5
+ class CenposGateway < Gateway
6
+ self.display_name = "CenPOS"
7
+ self.homepage_url = "https://www.cenpos.com/"
8
+
9
+ self.live_url = "https://ww3.cenpos.net/6/transact.asmx"
10
+
11
+ self.supported_countries = %w(AD AI AG AR AU AT BS BB BE BZ BM BR BN BG CA HR CY CZ DK DM EE FI FR DE GR GD GY HK HU IS IN IL IT JP LV LI LT LU MY MT MX MC MS NL PA PL PT KN LC MF VC SM SG SK SI ZA ES SR SE CH TR GB US UY)
12
+ self.default_currency = "USD"
13
+ self.money_format = :dollars
14
+ self.supported_cardtypes = [:visa, :master, :american_express, :discover]
15
+
16
+ def initialize(options={})
17
+ requires!(options, :merchant_id, :password, :user_id)
18
+ super
19
+ end
20
+
21
+ def purchase(amount, payment_method, options={})
22
+ post = {}
23
+ add_invoice(post, amount, options)
24
+ add_payment_method(post, payment_method)
25
+ add_customer_data(post, options)
26
+
27
+ commit("Sale", post)
28
+ end
29
+
30
+ def authorize(amount, payment_method, options={})
31
+ post = {}
32
+ add_invoice(post, amount, options)
33
+ add_payment_method(post, payment_method)
34
+ add_customer_data(post, options)
35
+
36
+ commit("Auth", post)
37
+ end
38
+
39
+ def capture(amount, authorization, options={})
40
+ post = {}
41
+ add_invoice(post, amount, options)
42
+ add_reference(post, authorization)
43
+ add_customer_data(post, options)
44
+
45
+ commit("SpecialForce", post)
46
+ end
47
+
48
+ def void(authorization, options={})
49
+ post = {}
50
+ add_void_required_elements(post)
51
+ add_reference(post, authorization)
52
+ commit("Void", post)
53
+ end
54
+
55
+ def refund(amount, authorization, options={})
56
+ post = {}
57
+ add_invoice(post, amount, options)
58
+ add_reference(post, authorization)
59
+ add_customer_data(post, options)
60
+
61
+ commit("SpecialReturn", post)
62
+ end
63
+
64
+ def credit(amount, payment_method, options={})
65
+ post = {}
66
+ add_invoice(post, amount, options)
67
+ add_payment_method(post, payment_method)
68
+
69
+ commit("Credit", post)
70
+ end
71
+
72
+ def verify(credit_card, options={})
73
+ MultiResponse.run(:use_first_response) do |r|
74
+ r.process { authorize(100, credit_card, options) }
75
+ r.process(:ignore_result) { void(r.authorization, options) }
76
+ end
77
+ end
78
+
79
+ def supports_scrubbing?
80
+ true
81
+ end
82
+
83
+ def scrub(transcript)
84
+ transcript.
85
+ gsub(%r((<acr1:CardNumber>)[^<]+(<))i, '\1[FILTERED]\2').
86
+ gsub(%r((<acr1:CardVerificationNumber>)[^<]+(<))i, '\1[FILTERED]\2').
87
+ gsub(%r((<acr:Password>)[^<]+(<))i, '\1[FILTERED]\2')
88
+ end
89
+
90
+ private
91
+
92
+ CURRENCY_CODES = Hash.new{|h,k| raise ArgumentError.new("Unsupported currency: #{k}")}
93
+ CURRENCY_CODES["AUD"] = "036"
94
+ CURRENCY_CODES["CAD"] = "124"
95
+ CURRENCY_CODES["CHF"] = "756"
96
+ CURRENCY_CODES["CZK"] = "203"
97
+ CURRENCY_CODES["DKK"] = "208"
98
+ CURRENCY_CODES["EUR"] = "978"
99
+ CURRENCY_CODES["GBP"] = "826"
100
+ CURRENCY_CODES["HKD"] = "344"
101
+ CURRENCY_CODES["HUF"] = "348"
102
+ CURRENCY_CODES["IRR"] = "364"
103
+ CURRENCY_CODES["JPY"] = "392"
104
+ CURRENCY_CODES["LVL"] = "428"
105
+ CURRENCY_CODES["MYR"] = "458"
106
+ CURRENCY_CODES["NOK"] = "578"
107
+ CURRENCY_CODES["PLN"] = "985"
108
+ CURRENCY_CODES["SEK"] = "752"
109
+ CURRENCY_CODES["SGD"] = "702"
110
+ CURRENCY_CODES["USD"] = "840"
111
+ CURRENCY_CODES["ZAR"] = "710"
112
+
113
+ def add_invoice(post, money, options)
114
+ post[:Amount] = amount(money)
115
+ post[:CurrencyCode] = CURRENCY_CODES[options[:currency] || currency(money)]
116
+ post[:TaxAmount] = amount(options[:tax])
117
+ post[:InvoiceNumber] = options[:order_id]
118
+ post[:InvoiceDetail] = options[:description]
119
+ end
120
+
121
+ def add_payment_method(post, payment_method)
122
+ post[:NameOnCard] = payment_method.name
123
+ post[:CardNumber] = payment_method.number
124
+ post[:CardVerificationNumber] = payment_method.verification_value
125
+ post[:CardExpirationDate] = format(payment_method.month, :two_digits) + format(payment_method.year, :two_digits)
126
+ post[:CardLastFourDigits] = payment_method.last_digits
127
+ post[:MagneticData] = payment_method.track_data
128
+ end
129
+
130
+ def add_customer_data(post, options)
131
+ if(billing_address = (options[:billing_address] || options[:address]))
132
+ post[:CustomerEmailAddress] = billing_address[:email]
133
+ post[:CustomerPhone] = billing_address[:phone]
134
+ post[:CustomerBillingAddress] = billing_address[:address1]
135
+ post[:CustomerCity] = billing_address[:city]
136
+ post[:CustomerState] = billing_address[:state]
137
+ post[:CustomerZipCode] = billing_address[:zip]
138
+ end
139
+ end
140
+
141
+ def add_void_required_elements(post)
142
+ post[:GeoLocationInformation] = nil
143
+ post[:IMEI] = nil
144
+ end
145
+
146
+ def add_reference(post, authorization)
147
+ reference_number, last_four_digits, original_amount = split_authorization(authorization)
148
+ post[:ReferenceNumber] = reference_number
149
+ post[:CardLastFourDigits] = last_four_digits
150
+ post[:Amount] = original_amount
151
+ end
152
+
153
+ def commit(action, post)
154
+ post[:MerchantId] = @options[:merchant_id]
155
+ post[:Password] = @options[:password]
156
+ post[:UserId] = @options[:user_id]
157
+ post[:TransactionType] = action
158
+
159
+ data = build_request(post)
160
+ begin
161
+ raw = parse(ssl_post(self.live_url, data, headers))
162
+ rescue ActiveMerchant::ResponseError => e
163
+ if(e.response.code == "500" && e.response.body.start_with?("<s:Envelope"))
164
+ raw = {
165
+ message: "See transcript for detailed error description."
166
+ }
167
+ else
168
+ raise
169
+ end
170
+ end
171
+
172
+ succeeded = success_from(raw[:result])
173
+ Response.new(
174
+ succeeded,
175
+ message_from(succeeded, raw),
176
+ raw,
177
+ authorization: authorization_from(post, raw),
178
+ error_code: error_code_from(succeeded, raw),
179
+ test: test?
180
+ )
181
+ end
182
+
183
+ def headers
184
+ {
185
+ "Accept-Encoding" => "gzip,deflate",
186
+ "Content-Type" => "text/xml;charset=UTF-8",
187
+ "SOAPAction" => "http://tempuri.org/Transactional/ProcessCard"
188
+ }
189
+ end
190
+
191
+ def build_request(post)
192
+ xml = Builder::XmlMarkup.new :indent => 8
193
+ xml.tag!("acr:MerchantId", post.delete(:MerchantId))
194
+ xml.tag!("acr:Password", post.delete(:Password))
195
+ xml.tag!("acr:UserId", post.delete(:UserId))
196
+ post.sort.each do |field, value|
197
+ xml.tag!("acr1:#{field}", value)
198
+ end
199
+ envelope(xml.target!)
200
+ end
201
+
202
+ def envelope(body)
203
+ <<-EOS
204
+ <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:tem="http://tempuri.org/" xmlns:acr="http://schemas.datacontract.org/2004/07/Acriter.ABI.CenPOS.EPayment.VirtualTerminal.Common" xmlns:acr1="http://schemas.datacontract.org/2004/07/Acriter.ABI.CenPOS.EPayment.VirtualTerminal.v6.Common" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
205
+ <soapenv:Header/>
206
+ <soapenv:Body>
207
+ <tem:ProcessCard>
208
+ <tem:request>
209
+ #{body}
210
+ </tem:request>
211
+ </tem:ProcessCard>
212
+ </soapenv:Body>
213
+ </soapenv:Envelope>
214
+ EOS
215
+ end
216
+
217
+ def parse(xml)
218
+ response = {}
219
+
220
+ doc = Nokogiri::XML(xml)
221
+ doc.remove_namespaces!
222
+ body = doc.xpath("//ProcessCardResult")
223
+ body.children.each do |node|
224
+ if (node.elements.size == 0)
225
+ response[node.name.underscore.to_sym] = node.text
226
+ else
227
+ node.elements.each do |childnode|
228
+ name = "#{node.name.underscore}_#{childnode.name.underscore}"
229
+ response[name.to_sym] = childnode.text
230
+ end
231
+ end
232
+ end unless doc.root.nil?
233
+
234
+ response
235
+ end
236
+
237
+ def success_from(response)
238
+ response == "0"
239
+ end
240
+
241
+ def message_from(succeeded, response)
242
+ if succeeded
243
+ "Succeeded"
244
+ else
245
+ response[:message] || "Unable to read error message"
246
+ end
247
+ end
248
+
249
+ STANDARD_ERROR_CODE_MAPPING = {
250
+ "211" => STANDARD_ERROR_CODE[:invalid_number],
251
+ "252" => STANDARD_ERROR_CODE[:invalid_expiry_date],
252
+ "257" => STANDARD_ERROR_CODE[:invalid_cvc],
253
+ "333" => STANDARD_ERROR_CODE[:expired_card],
254
+ "1" => STANDARD_ERROR_CODE[:card_declined],
255
+ "99" => STANDARD_ERROR_CODE[:processing_error],
256
+ }
257
+
258
+ def authorization_from(request, response)
259
+ [ response[:reference_number], request[:CardLastFourDigits], request[:Amount] ].join("|")
260
+ end
261
+
262
+ def split_authorization(authorization)
263
+ reference_number, last_four_digits, original_amount = authorization.split("|")
264
+ [reference_number, last_four_digits, original_amount]
265
+ end
266
+
267
+ def error_code_from(succeeded, response)
268
+ succeeded ? nil : STANDARD_ERROR_CODE_MAPPING[response[:result]]
269
+ end
270
+ end
271
+ end
272
+ end
@@ -1,8 +1,7 @@
1
1
  module ActiveMerchant #:nodoc:
2
2
  module Billing #:nodoc:
3
3
  class EpayGateway < Gateway
4
- API_HOST = 'ssl.ditonlinebetalingssystem.dk'
5
- self.live_url = 'https://' + API_HOST + '/remote/payment'
4
+ self.live_url = 'https://ssl.ditonlinebetalingssystem.dk/'
6
5
 
7
6
  self.default_currency = 'DKK'
8
7
  self.money_format = :cents
@@ -175,14 +174,14 @@ module ActiveMerchant #:nodoc:
175
174
  def soap_post(method, params)
176
175
  data = xml_builder(params, method)
177
176
  headers = make_headers(data, method)
178
- REXML::Document.new(ssl_post('https://' + API_HOST + '/remote/payment.asmx', data, headers))
177
+ REXML::Document.new(ssl_post(live_url + 'remote/payment.asmx', data, headers))
179
178
  end
180
179
 
181
180
  def do_authorize(params)
182
181
  headers = {}
183
182
  headers['Referer'] = (options[:password] || "activemerchant.org")
184
183
 
185
- response = raw_ssl_request(:post, 'https://' + API_HOST + '/auth/default.aspx', authorize_post_data(params), headers)
184
+ response = raw_ssl_request(:post, live_url + 'auth/default.aspx', authorize_post_data(params), headers)
186
185
 
187
186
  # Authorize gives the response back by redirecting with the values in
188
187
  # the URL query
@@ -233,9 +232,9 @@ module ActiveMerchant #:nodoc:
233
232
  def make_headers(data, soap_call)
234
233
  {
235
234
  'Content-Type' => 'text/xml; charset=utf-8',
236
- 'Host' => API_HOST,
235
+ 'Host' => "ssl.ditonlinebetalingssystem.dk",
237
236
  'Content-Length' => data.size.to_s,
238
- 'SOAPAction' => self.live_url + '/' + soap_call
237
+ 'SOAPAction' => self.live_url + 'remote/payment/' + soap_call
239
238
  }
240
239
  end
241
240
 
@@ -246,7 +245,7 @@ module ActiveMerchant #:nodoc:
246
245
  'xmlns:xsd' => 'http://www.w3.org/2001/XMLSchema',
247
246
  'xmlns:soap' => 'http://schemas.xmlsoap.org/soap/envelope/' } do
248
247
  xml.tag! 'soap:Body' do
249
- xml.tag! soap_call, { 'xmlns' => self.live_url } do
248
+ xml.tag! soap_call, { 'xmlns' => "#{self.live_url}remote/payment" } do
250
249
  xml.tag! 'merchantnumber', @options[:login]
251
250
  xml.tag! 'transactionid', params[:transaction]
252
251
  xml.tag! 'amount', params[:amount].to_s if soap_call != 'delete'
@@ -259,8 +258,8 @@ module ActiveMerchant #:nodoc:
259
258
  def authorize_post_data(params = {})
260
259
  params[:language] = '2'
261
260
  params[:cms] = 'activemerchant'
262
- params[:accepturl] = 'https://ssl.ditonlinebetalingssystem.dk/auth/default.aspx?accept=1'
263
- params[:declineurl] = 'https://ssl.ditonlinebetalingssystem.dk/auth/default.aspx?decline=1'
261
+ params[:accepturl] = live_url + 'auth/default.aspx?accept=1'
262
+ params[:declineurl] = live_url + 'auth/default.aspx?decline=1'
264
263
  params[:merchantnumber] = @options[:login]
265
264
 
266
265
  params.collect { |key, value| "#{key}=#{CGI.escape(value.to_s)}" }.join("&")
@@ -68,6 +68,7 @@ module ActiveMerchant #:nodoc:
68
68
  end
69
69
 
70
70
  private
71
+
71
72
  def build_request(action, body)
72
73
  xml = Builder::XmlMarkup.new
73
74
 
@@ -168,6 +169,14 @@ module ActiveMerchant #:nodoc:
168
169
  :avs_result => { :code => response[:avs] },
169
170
  :cvv_result => response[:cvv2]
170
171
  )
172
+
173
+ rescue ResponseError => e
174
+ case e.response.code
175
+ when '401'
176
+ return Response.new(false, "Invalid Login: #{e.response.body}", {}, :test => test?)
177
+ else
178
+ raise
179
+ end
171
180
  end
172
181
 
173
182
  def successful?(response)
@@ -38,12 +38,35 @@ module ActiveMerchant #:nodoc:
38
38
 
39
39
  add_amount(post, money, options)
40
40
  add_creditcard(post, creditcard, options)
41
+ add_extra_options(post, options)
41
42
  post[:reference] = options[:order_id]
42
43
  post[:customer_ip] = options[:ip]
43
44
 
44
45
  commit(:post, 'purchases', post)
45
46
  end
46
47
 
48
+ def authorize(money, creditcard, options = {})
49
+ post = {}
50
+
51
+ add_amount(post, money, options)
52
+ add_creditcard(post, creditcard, options)
53
+ add_extra_options(post, options)
54
+ post[:capture] = false
55
+ post[:reference] = options[:order_id]
56
+ post[:customer_ip] = options[:ip]
57
+
58
+ commit(:post, 'purchases', post)
59
+ end
60
+
61
+ def capture(money, authorization, options = {})
62
+ post = {}
63
+ add_amount(post, money, options)
64
+ add_extra_options(post, options)
65
+
66
+
67
+ commit(:post, "purchases/#{CGI.escape(authorization)}/capture", post)
68
+ end
69
+
47
70
  # Refund a transaction
48
71
  #
49
72
  # amount - Integer - the amount to refund
@@ -52,6 +75,7 @@ module ActiveMerchant #:nodoc:
52
75
  def refund(money, txn_id, reference)
53
76
  post = {}
54
77
 
78
+ add_extra_options(post, options)
55
79
  post[:amount] = money
56
80
  post[:transaction_id] = txn_id
57
81
  post[:reference] = reference
@@ -73,6 +97,8 @@ module ActiveMerchant #:nodoc:
73
97
 
74
98
  # Add the money details to the request
75
99
  def add_amount(post, money, options)
100
+ post[:currency] = (options[:currency] || currency(money))
101
+ post[:currency] = post[:currency].upcase if post[:currency]
76
102
  post[:amount] = money
77
103
  end
78
104
 
@@ -95,6 +121,13 @@ module ActiveMerchant #:nodoc:
95
121
  end
96
122
  end
97
123
 
124
+ def add_extra_options(post, options)
125
+ extra = {}
126
+ extra[:name] = options[:merchant] if options[:merchant]
127
+ extra[:location] = options[:merchant_location] if options[:merchant_location]
128
+ post[:extra] = extra if extra.any?
129
+ end
130
+
98
131
  # Post the data to the gateway
99
132
  def commit(method, uri, parameters=nil)
100
133
  response = begin