activemerchant 1.49.0 → 1.50.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 (39) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +47 -0
  3. data/CONTRIBUTORS +4 -0
  4. data/README.md +5 -1
  5. data/lib/active_merchant/billing/credit_card.rb +9 -0
  6. data/lib/active_merchant/billing/gateways/allied_wallet.rb +203 -0
  7. data/lib/active_merchant/billing/gateways/authorize_net.rb +17 -6
  8. data/lib/active_merchant/billing/gateways/beanstream.rb +4 -0
  9. data/lib/active_merchant/billing/gateways/beanstream/beanstream_core.rb +0 -4
  10. data/lib/active_merchant/billing/gateways/beanstream_interac.rb +4 -0
  11. data/lib/active_merchant/billing/gateways/bpoint.rb +277 -0
  12. data/lib/active_merchant/billing/gateways/cashnet.rb +19 -8
  13. data/lib/active_merchant/billing/gateways/cenpos.rb +15 -29
  14. data/lib/active_merchant/billing/gateways/conekta.rb +3 -2
  15. data/lib/active_merchant/billing/gateways/dibs.rb +206 -0
  16. data/lib/active_merchant/billing/gateways/fat_zebra.rb +19 -12
  17. data/lib/active_merchant/billing/gateways/merchant_partners.rb +245 -0
  18. data/lib/active_merchant/billing/gateways/netbilling.rb +1 -0
  19. data/lib/active_merchant/billing/gateways/omise.rb +319 -0
  20. data/lib/active_merchant/billing/gateways/optimal_payment.rb +5 -4
  21. data/lib/active_merchant/billing/gateways/orbital.rb +7 -0
  22. data/lib/active_merchant/billing/gateways/payu_in.rb +243 -0
  23. data/lib/active_merchant/billing/gateways/quickpay.rb +11 -357
  24. data/lib/active_merchant/billing/gateways/quickpay/quickpay_common.rb +188 -0
  25. data/lib/active_merchant/billing/gateways/quickpay/quickpay_v10.rb +240 -0
  26. data/lib/active_merchant/billing/gateways/quickpay/quickpay_v4to7.rb +227 -0
  27. data/lib/active_merchant/billing/gateways/s5.rb +226 -0
  28. data/lib/active_merchant/billing/gateways/sage_pay.rb +7 -1
  29. data/lib/active_merchant/billing/gateways/secure_net.rb +1 -1
  30. data/lib/active_merchant/billing/gateways/stripe.rb +8 -5
  31. data/lib/active_merchant/billing/gateways/usa_epay_transaction.rb +2 -2
  32. data/lib/active_merchant/billing/gateways/vanco.rb +280 -0
  33. data/lib/active_merchant/connection.rb +3 -0
  34. data/lib/active_merchant/version.rb +1 -1
  35. metadata +15 -27
  36. checksums.yaml.gz.sig +0 -2
  37. data.tar.gz.sig +0 -0
  38. data/lib/active_merchant/billing/gateways/adyen.rb +0 -209
  39. metadata.gz.sig +0 -1
@@ -54,16 +54,19 @@ module ActiveMerchant #:nodoc:
54
54
 
55
55
  def commit(action, money, fields)
56
56
  fields[:amount] = amount(money)
57
- url = live_url + @options[:merchant_gateway_name]
58
- response = parse(ssl_post(url, post_data(action, fields)))
57
+ url = live_url + CGI.escape(@options[:merchant_gateway_name])
58
+ raw_response = ssl_post(url, post_data(action, fields))
59
+ parsed_response = parse(raw_response)
59
60
 
60
- success = (response[:result] == '0')
61
+ return unparsable_response(raw_response) unless parsed_response
62
+
63
+ success = (parsed_response[:result] == '0')
61
64
  Response.new(
62
65
  success,
63
- CASHNET_CODES[response[:result]],
64
- response,
66
+ CASHNET_CODES[parsed_response[:result]],
67
+ parsed_response,
65
68
  test: test?,
66
- authorization: (success ? response[:tx] : '')
69
+ authorization: (success ? parsed_response[:tx] : '')
67
70
  )
68
71
  end
69
72
 
@@ -113,8 +116,10 @@ module ActiveMerchant #:nodoc:
113
116
  end
114
117
 
115
118
  def parse(body)
116
- response_data = body.match(/<cngateway>(.*)<\/cngateway>/)[1]
117
- Hash[CGI::parse(response_data).map{|k,v| [k.to_sym,v.first]}]
119
+ match = body.match(/<cngateway>(.*)<\/cngateway>/)
120
+ return nil unless match
121
+
122
+ Hash[CGI::parse(match[1]).map{|k,v| [k.to_sym,v.first]}]
118
123
  end
119
124
 
120
125
  def handle_response(response)
@@ -126,6 +131,12 @@ module ActiveMerchant #:nodoc:
126
131
  raise ResponseError.new(response)
127
132
  end
128
133
 
134
+ def unparsable_response(raw_response)
135
+ message = "Unparsable response received from Cashnet. Please contact Cashnet if you continue to receive this message."
136
+ message += " (The raw response returned by the API was #{raw_response.inspect})"
137
+ return Response.new(false, message)
138
+ end
139
+
129
140
  CASHNET_CODES = {
130
141
  '0' => 'Success',
131
142
  '1' => 'Invalid customer code, no customer code specified',
@@ -49,6 +49,8 @@ module ActiveMerchant #:nodoc:
49
49
  post = {}
50
50
  add_void_required_elements(post)
51
51
  add_reference(post, authorization)
52
+ add_tax(post, options)
53
+
52
54
  commit("Void", post)
53
55
  end
54
56
 
@@ -89,33 +91,13 @@ module ActiveMerchant #:nodoc:
89
91
 
90
92
  private
91
93
 
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
94
  def add_invoice(post, money, options)
114
95
  post[:Amount] = amount(money)
115
- post[:CurrencyCode] = CURRENCY_CODES[options[:currency] || currency(money)]
116
- post[:TaxAmount] = amount(options[:tax])
96
+ post[:CurrencyCode] = options[:currency] || currency(money)
117
97
  post[:InvoiceNumber] = options[:order_id]
118
- post[:InvoiceDetail] = options[:description]
98
+ post[:InvoiceDetail] = options[:invoice_detail] if options[:invoice_detail]
99
+ post[:CustomerCode] = options[:customer_code] if options[:customer_code]
100
+ add_tax(post, options)
119
101
  end
120
102
 
121
103
  def add_payment_method(post, payment_method)
@@ -124,7 +106,7 @@ module ActiveMerchant #:nodoc:
124
106
  post[:CardVerificationNumber] = payment_method.verification_value
125
107
  post[:CardExpirationDate] = format(payment_method.month, :two_digits) + format(payment_method.year, :two_digits)
126
108
  post[:CardLastFourDigits] = payment_method.last_digits
127
- post[:MagneticData] = payment_method.track_data
109
+ post[:MagneticData] = payment_method.track_data if payment_method.track_data
128
110
  end
129
111
 
130
112
  def add_customer_data(post, options)
@@ -143,6 +125,10 @@ module ActiveMerchant #:nodoc:
143
125
  post[:IMEI] = nil
144
126
  end
145
127
 
128
+ def add_tax(post, options)
129
+ post[:TaxAmount] = amount(options[:tax] || 0)
130
+ end
131
+
146
132
  def add_reference(post, authorization)
147
133
  reference_number, last_four_digits, original_amount = split_authorization(authorization)
148
134
  post[:ReferenceNumber] = reference_number
@@ -184,7 +170,7 @@ module ActiveMerchant #:nodoc:
184
170
  {
185
171
  "Accept-Encoding" => "gzip,deflate",
186
172
  "Content-Type" => "text/xml;charset=UTF-8",
187
- "SOAPAction" => "http://tempuri.org/Transactional/ProcessCard"
173
+ "SOAPAction" => "http://tempuri.org/Transactional/ProcessCreditCard"
188
174
  }
189
175
  end
190
176
 
@@ -204,11 +190,11 @@ module ActiveMerchant #:nodoc:
204
190
  <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
191
  <soapenv:Header/>
206
192
  <soapenv:Body>
207
- <tem:ProcessCard>
193
+ <tem:ProcessCreditCard>
208
194
  <tem:request>
209
195
  #{body}
210
196
  </tem:request>
211
- </tem:ProcessCard>
197
+ </tem:ProcessCreditCard>
212
198
  </soapenv:Body>
213
199
  </soapenv:Envelope>
214
200
  EOS
@@ -219,7 +205,7 @@ module ActiveMerchant #:nodoc:
219
205
 
220
206
  doc = Nokogiri::XML(xml)
221
207
  doc.remove_namespaces!
222
- body = doc.xpath("//ProcessCardResult")
208
+ body = doc.xpath("//ProcessCreditCardResult")
223
209
  body.children.each do |node|
224
210
  if (node.elements.size == 0)
225
211
  response[node.name.underscore.to_sym] = node.text
@@ -69,7 +69,7 @@ module ActiveMerchant #:nodoc:
69
69
  details[:name] = options[:customer] if options[:customer]
70
70
  details[:email] = options[:email] if options[:email]
71
71
  details[:phone] = options[:phone] if options[:phone]
72
- details[:device_fingerprint] = options[:device_fingerprint] if options[:device_fingerprint]
72
+ post[:device_fingerprint] = options[:device_fingerprint] if options[:device_fingerprint]
73
73
  details[:ip] = options[:ip] if options[:ip]
74
74
  add_billing_address(details, options)
75
75
  add_line_items(details, options)
@@ -160,6 +160,7 @@ module ActiveMerchant #:nodoc:
160
160
  def headers(meta)
161
161
  {
162
162
  "Accept" => "application/vnd.conekta-v#{options[:version]}+json",
163
+ "Accept-Language" => "es",
163
164
  "Authorization" => "Basic " + Base64.encode64("#{options[:key]}:"),
164
165
  "RaiseHtmlError" => "false",
165
166
  "Conekta-Client-User-Agent" => {"agent"=>"Conekta ActiveMerchantBindings/#{ActiveMerchant::VERSION}"}.to_json,
@@ -181,7 +182,7 @@ module ActiveMerchant #:nodoc:
181
182
 
182
183
  Response.new(
183
184
  success,
184
- raw_response["message"],
185
+ raw_response["message_to_purchaser"],
185
186
  raw_response,
186
187
  test: test?,
187
188
  authorization: raw_response["id"]
@@ -0,0 +1,206 @@
1
+ module ActiveMerchant #:nodoc:
2
+ module Billing #:nodoc:
3
+ class DibsGateway < Gateway
4
+ self.display_name = "DIBS"
5
+ self.homepage_url = "http://www.dibspayment.com/"
6
+
7
+ self.live_url = "https://api.dibspayment.com/merchant/v1/JSON/Transaction/"
8
+
9
+ self.supported_countries = ["US", "FI", "NO", "SE", "GB"]
10
+ self.default_currency = "USD"
11
+ self.money_format = :cents
12
+ self.ssl_version = :TLSv1
13
+ self.supported_cardtypes = [:visa, :master, :american_express, :discover]
14
+
15
+ def initialize(options={})
16
+ requires!(options, :merchant_id, :secret_key)
17
+ super
18
+ end
19
+
20
+ def purchase(amount, payment_method, options={})
21
+ MultiResponse.run(false) do |r|
22
+ r.process { authorize(amount, payment_method, options) }
23
+ r.process { capture(amount, r.authorization, options) }
24
+ end
25
+ end
26
+
27
+ def authorize(amount, payment_method, options={})
28
+ post = {}
29
+ add_amount(post, amount)
30
+ add_invoice(post, amount, options)
31
+ if (payment_method.respond_to?(:number))
32
+ add_payment_method(post, payment_method, options)
33
+ commit(:authorize, post)
34
+ else
35
+ add_ticket_id(post, payment_method)
36
+ commit(:authorize_ticket, post)
37
+ end
38
+
39
+ end
40
+
41
+ def capture(amount, authorization, options={})
42
+ post = {}
43
+ add_amount(post, amount)
44
+ add_reference(post, authorization)
45
+
46
+ commit(:capture, post)
47
+ end
48
+
49
+ def void(authorization, options={})
50
+ post = {}
51
+ add_reference(post, authorization)
52
+
53
+ commit(:void, post)
54
+ end
55
+
56
+ def refund(amount, authorization, options={})
57
+ post = {}
58
+ add_amount(post, amount)
59
+ add_reference(post, authorization)
60
+
61
+ commit(:refund, post)
62
+ end
63
+
64
+ def verify(credit_card, options={})
65
+ MultiResponse.run(:use_first_response) do |r|
66
+ r.process { authorize(100, credit_card, options) }
67
+ r.process(:ignore_result) { void(r.authorization, options) }
68
+ end
69
+ end
70
+
71
+ def store(payment_method, options = {})
72
+ post = {}
73
+
74
+ add_invoice(post, 0, options)
75
+ add_payment_method(post, payment_method, options)
76
+
77
+ commit(:store, post)
78
+ end
79
+
80
+ def supports_scrubbing?
81
+ true
82
+ end
83
+
84
+ def scrub(transcript)
85
+ transcript.
86
+ gsub(%r(("cardNumber\\?":\\?")[^"]*)i, '\1[FILTERED]').
87
+ gsub(%r(("cvc\\?":\\?")[^"]*)i, '\1[FILTERED]')
88
+ end
89
+
90
+ private
91
+
92
+ CURRENCY_CODES = Hash.new{|h,k| raise ArgumentError.new("Unsupported currency: #{k}")}
93
+ CURRENCY_CODES["USD"] = "840"
94
+ CURRENCY_CODES["DKK"] = "208"
95
+ CURRENCY_CODES["NOK"] = "578"
96
+ CURRENCY_CODES["SEK"] = "752"
97
+ CURRENCY_CODES["GBP"] = "826"
98
+ CURRENCY_CODES["EUR"] = "978"
99
+
100
+ def add_invoice(post, money, options)
101
+ post[:orderId] = options[:order_id] || generate_unique_id
102
+ post[:currency] = CURRENCY_CODES[options[:currency] || currency(money)]
103
+ end
104
+
105
+ def add_ticket_id(post, payment_method)
106
+ post[:ticketId] = payment_method
107
+ end
108
+
109
+ def add_payment_method(post, payment_method, options)
110
+ post[:cardNumber] = payment_method.number
111
+ post[:cvc] = payment_method.verification_value
112
+ post[:expYear] = format(payment_method.year, :two_digits)
113
+ post[:expMonth] = payment_method.month
114
+
115
+ post[:startMonth] = payment_method.start_month if payment_method.start_month
116
+ post[:startYear] = payment_method.start_year if payment_method.start_year
117
+ post[:issueNumber] = payment_method.issue_number if payment_method.issue_number
118
+ post[:clientIp] = options[:ip] || "127.0.0.1"
119
+ post[:test] = true if test?
120
+ end
121
+
122
+
123
+ def add_reference(post, authorization)
124
+ post[:transactionId] = authorization
125
+ end
126
+
127
+ def add_amount(post, amount)
128
+ post[:amount] = amount
129
+ end
130
+
131
+ ACTIONS = {
132
+ authorize: "AuthorizeCard",
133
+ authorize_ticket: "AuthorizeTicket",
134
+ capture: "CaptureTransaction",
135
+ void: "CancelTransaction",
136
+ refund: "RefundTransaction",
137
+ store: "CreateTicket"
138
+ }
139
+
140
+ def commit(action, post)
141
+ post[:merchantId] = @options[:merchant_id]
142
+
143
+ data = build_request(post)
144
+ raw = parse(ssl_post(url(action), "request=#{data}", headers))
145
+ succeeded = success_from(raw)
146
+ Response.new(
147
+ succeeded,
148
+ message_from(succeeded, raw),
149
+ raw,
150
+ authorization: authorization_from(post, raw),
151
+ test: test?
152
+ )
153
+ rescue JSON::ParserError
154
+ unparsable_response(raw)
155
+ end
156
+
157
+ def headers
158
+ {
159
+ "Content-Type" => "application/x-www-form-urlencoded"
160
+ }
161
+ end
162
+
163
+ def build_request(post)
164
+ add_hmac(post)
165
+ post.to_json
166
+ end
167
+
168
+ def add_hmac(post)
169
+ data = post.sort.collect { |key, value| "#{key}=#{value.to_s}" }.join("&")
170
+ digest = OpenSSL::Digest.new('sha256')
171
+ key = [@options[:secret_key]].pack('H*')
172
+ post[:MAC] = OpenSSL::HMAC.hexdigest(digest, key, data)
173
+ end
174
+
175
+ def url(action)
176
+ live_url + ACTIONS[action]
177
+ end
178
+
179
+ def parse(body)
180
+ JSON.parse(body)
181
+ end
182
+
183
+ def success_from(raw_response)
184
+ raw_response["status"] == "ACCEPT"
185
+ end
186
+
187
+ def message_from(succeeded, response)
188
+ if succeeded
189
+ "Succeeded"
190
+ else
191
+ response["status"] + ": " + response["declineReason"] || "Unable to read error message"
192
+ end
193
+ end
194
+
195
+ def authorization_from(request, response)
196
+ response['transactionId'] || response['ticketId'] || request[:transactionId]
197
+ end
198
+
199
+ def unparsable_response(raw_response)
200
+ message = "Invalid JSON response received from Dibs. Please contact Dibs if you continue to receive this message."
201
+ message += " (The raw response returned by the API was #{raw_response.inspect})"
202
+ return Response.new(false, message)
203
+ end
204
+ end
205
+ end
206
+ end
@@ -21,8 +21,6 @@ module ActiveMerchant #:nodoc:
21
21
  # Under the Your Account section
22
22
  def initialize(options = {})
23
23
  requires!(options, :username, :token)
24
- @username = options[:username]
25
- @token = options[:token]
26
24
  super
27
25
  end
28
26
 
@@ -39,8 +37,8 @@ module ActiveMerchant #:nodoc:
39
37
  add_amount(post, money, options)
40
38
  add_creditcard(post, creditcard, options)
41
39
  add_extra_options(post, options)
42
- post[:reference] = options[:order_id]
43
- post[:customer_ip] = options[:ip]
40
+ add_order_id(post, options)
41
+ add_ip(post, options)
44
42
 
45
43
  commit(:post, 'purchases', post)
46
44
  end
@@ -51,9 +49,10 @@ module ActiveMerchant #:nodoc:
51
49
  add_amount(post, money, options)
52
50
  add_creditcard(post, creditcard, options)
53
51
  add_extra_options(post, options)
52
+ add_order_id(post, options)
53
+ add_ip(post, options)
54
+
54
55
  post[:capture] = false
55
- post[:reference] = options[:order_id]
56
- post[:customer_ip] = options[:ip]
57
56
 
58
57
  commit(:post, 'purchases', post)
59
58
  end
@@ -63,7 +62,6 @@ module ActiveMerchant #:nodoc:
63
62
  add_amount(post, money, options)
64
63
  add_extra_options(post, options)
65
64
 
66
-
67
65
  commit(:post, "purchases/#{CGI.escape(authorization)}/capture", post)
68
66
  end
69
67
 
@@ -72,13 +70,13 @@ module ActiveMerchant #:nodoc:
72
70
  # amount - Integer - the amount to refund
73
71
  # txn_id - String - the original transaction to be refunded
74
72
  # reference - String - your transaction reference
75
- def refund(money, txn_id, reference)
73
+ def refund(money, txn_id, options={})
76
74
  post = {}
77
75
 
78
76
  add_extra_options(post, options)
79
- post[:amount] = money
77
+ add_amount(post, money, options)
80
78
  post[:transaction_id] = txn_id
81
- post[:reference] = reference
79
+ add_order_id(post, options)
82
80
 
83
81
  commit(:post, "refunds", post)
84
82
  end
@@ -86,7 +84,7 @@ module ActiveMerchant #:nodoc:
86
84
  # Tokenize a credit card
87
85
  #
88
86
  # The token is returned in the Response#authorization
89
- def store(creditcard)
87
+ def store(creditcard, options={})
90
88
  post = {}
91
89
  add_creditcard(post, creditcard)
92
90
 
@@ -125,9 +123,18 @@ module ActiveMerchant #:nodoc:
125
123
  extra = {}
126
124
  extra[:name] = options[:merchant] if options[:merchant]
127
125
  extra[:location] = options[:merchant_location] if options[:merchant_location]
126
+ extra[:ecm] = "32" if options[:recurring]
128
127
  post[:extra] = extra if extra.any?
129
128
  end
130
129
 
130
+ def add_order_id(post, options)
131
+ post[:reference] = options[:order_id] || SecureRandom.hex(15)
132
+ end
133
+
134
+ def add_ip(post, options)
135
+ post[:customer_ip] = options[:ip] || "127.0.0.1"
136
+ end
137
+
131
138
  # Post the data to the gateway
132
139
  def commit(method, uri, parameters=nil)
133
140
  response = begin
@@ -197,7 +204,7 @@ module ActiveMerchant #:nodoc:
197
204
  # Builds the auth and U-A headers for the request
198
205
  def headers
199
206
  {
200
- "Authorization" => "Basic " + Base64.strict_encode64(@username.to_s + ":" + @token.to_s).strip,
207
+ "Authorization" => "Basic " + Base64.strict_encode64(@options[:username].to_s + ":" + @options[:token].to_s).strip,
201
208
  "User-Agent" => "Fat Zebra v1.0/ActiveMerchant #{ActiveMerchant::VERSION}"
202
209
  }
203
210
  end