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
@@ -0,0 +1,226 @@
1
+ require 'nokogiri'
2
+
3
+ module ActiveMerchant #:nodoc:
4
+ module Billing #:nodoc:
5
+ class S5Gateway < Gateway
6
+ self.test_url = 'https://test.ctpe.io/payment/ctpe'
7
+ self.live_url = 'https://ctpe.io/payment/ctpe'
8
+
9
+ self.supported_countries = ['DK']
10
+ self.default_currency = 'EUR'
11
+ self.supported_cardtypes = [:visa, :master, :maestro]
12
+
13
+ self.homepage_url = 'http://www.s5.dk/'
14
+ self.display_name = 'S5'
15
+
16
+ SUPPORTED_TRANSACTIONS = {
17
+ 'sale' => 'CC.DB',
18
+ 'authonly' => 'CC.PA',
19
+ 'capture' => 'CC.CP',
20
+ 'refund' => 'CC.RF',
21
+ 'void' => 'CC.RV',
22
+ }
23
+
24
+ def initialize(options={})
25
+ requires!(options, :sender, :channel, :login, :password)
26
+ super
27
+ end
28
+
29
+ def purchase(money, payment, options={})
30
+ request = build_xml_request do |xml|
31
+ add_payment(xml, money, 'sale', options)
32
+ add_account(xml, payment)
33
+ add_customer(xml, payment, options)
34
+ add_recurrence_mode(xml, options)
35
+ end
36
+
37
+ commit(request)
38
+ end
39
+
40
+ def refund(money, authorization, options={})
41
+ request = build_xml_request do |xml|
42
+ add_identification(xml, authorization)
43
+ add_payment(xml, money, 'refund', options)
44
+ end
45
+
46
+ commit(request)
47
+ end
48
+
49
+ def authorize(money, payment, options={})
50
+ request = build_xml_request do |xml|
51
+ add_payment(xml, money, 'authonly', options)
52
+ add_account(xml, payment)
53
+ add_customer(xml, payment, options)
54
+ add_recurrence_mode(xml, options)
55
+ end
56
+
57
+ commit(request)
58
+ end
59
+
60
+ def capture(money, authorization, options={})
61
+ request = build_xml_request do |xml|
62
+ add_identification(xml, authorization)
63
+ add_payment(xml, money, 'capture', options)
64
+ end
65
+
66
+ commit(request)
67
+ end
68
+
69
+ def void(authorization, options={})
70
+ request = build_xml_request do |xml|
71
+ add_identification(xml, authorization)
72
+ add_payment(xml, nil, 'void', options)
73
+ end
74
+
75
+ commit(request)
76
+ end
77
+
78
+ def verify(credit_card, options={})
79
+ MultiResponse.run(:use_first_response) do |r|
80
+ r.process { authorize(100, credit_card, options) }
81
+ r.process(:ignore_result) { void(r.authorization, options) }
82
+ end
83
+ end
84
+
85
+ def supports_scrubbing?
86
+ true
87
+ end
88
+
89
+ def scrub(transcript)
90
+ transcript.
91
+ gsub(%r((pwd=).+(/>))i, '\1[FILTERED]\2').
92
+ gsub(%r((<Number>).+(</Number>))i, '\1[FILTERED]\2').
93
+ gsub(%r((<Verification>).+(</Verification>))i, '\1[FILTERED]\2')
94
+ end
95
+
96
+ private
97
+
98
+ def add_identification(xml, authorization)
99
+ xml.Identification do
100
+ xml.ReferenceID authorization
101
+ end
102
+ end
103
+
104
+ def add_payment(xml, money, action, options)
105
+ xml.Payment(code: SUPPORTED_TRANSACTIONS[action]) do
106
+ xml.Memo "return_code=#{options[:memo]}" if options[:memo]
107
+ xml.Presentation do
108
+ xml.Amount amount(money)
109
+ xml.Currency options[:currency] || currency(money)
110
+ xml.Usage options[:description]
111
+ end
112
+ end
113
+ end
114
+
115
+ def add_account(xml, creditcard)
116
+ xml.Account do
117
+ xml.Number creditcard.number
118
+ xml.Holder "#{creditcard.first_name} #{creditcard.last_name}"
119
+ xml.Brand creditcard.brand
120
+ xml.Expiry(year: creditcard.year, month: creditcard.month)
121
+ xml.Verification creditcard.verification_value
122
+ end
123
+ end
124
+
125
+ def add_customer(xml, creditcard, options)
126
+ address = options[:billing_address]
127
+ xml.Customer do
128
+ xml.Contact do
129
+ xml.Email options[:email]
130
+ xml.Ip options[:ip]
131
+ xml.Phone address[:phone] if address
132
+ end
133
+ add_address(xml, address)
134
+ xml.Name do
135
+ xml.Given creditcard.first_name
136
+ xml.Family creditcard.last_name
137
+ xml.Company options[:company]
138
+ end
139
+ end
140
+ end
141
+
142
+ def add_address(xml, address)
143
+ return unless address
144
+
145
+ xml.Address do
146
+ xml.Street "#{address[:address1]} #{address[:address2]}"
147
+ xml.Zip address[:zip]
148
+ xml.City address[:city]
149
+ xml.State address[:state]
150
+ xml.Country address[:country]
151
+ end
152
+ end
153
+
154
+ def add_recurrence_mode(xml, options)
155
+ if options[:recurring] == true
156
+ xml.Recurrence(mode: "REPEATED")
157
+ else
158
+ xml.Recurrence(mode: "INITIAL")
159
+ end
160
+ end
161
+
162
+ def parse(body)
163
+ results = {}
164
+ xml = Nokogiri::XML(body)
165
+ resp = xml.xpath("//Response/Transaction/Identification")
166
+ resp.children.each do |element|
167
+ results[element.name.downcase.to_sym] = element.text
168
+ end
169
+ resp = xml.xpath("//Response/Transaction/Processing")
170
+ resp.children.each do |element|
171
+ results[element.name.downcase.to_sym] = element.text
172
+ end
173
+ results
174
+ end
175
+
176
+ def commit(xml)
177
+ url = (test? ? test_url : live_url)
178
+ headers = {
179
+ 'Content-Type' => 'application/x-www-form-urlencoded;charset=UTF-8'
180
+ }
181
+
182
+ response = parse(ssl_post(url, post_data(xml), headers))
183
+
184
+ Response.new(
185
+ success_from(response),
186
+ message_from(response),
187
+ response,
188
+ authorization: authorization_from(response),
189
+ test: test?
190
+ )
191
+ end
192
+
193
+ def success_from(response)
194
+ response[:result] == 'ACK'
195
+ end
196
+
197
+ def message_from(response)
198
+ response[:return]
199
+ end
200
+
201
+ def authorization_from(response)
202
+ response[:uniqueid]
203
+ end
204
+
205
+ def post_data(xml)
206
+ "load=#{xml}"
207
+ end
208
+
209
+ def build_xml_request
210
+ builder = Nokogiri::XML::Builder.new(encoding: 'UTF-8') do |xml|
211
+ xml.Request(version: '1.0') do
212
+ xml.Header do
213
+ xml.Security(sender: @options[:sender])
214
+ end
215
+ xml.Transaction(mode: @options[:mode] || 'LIVE', channel: @options[:channel]) do
216
+ xml.User(login: @options[:login], pwd: @options[:password])
217
+ yield(xml)
218
+ end
219
+ end
220
+ end
221
+
222
+ builder.to_xml
223
+ end
224
+ end
225
+ end
226
+ end
@@ -138,6 +138,13 @@ module ActiveMerchant #:nodoc:
138
138
  commit(:unstore, post)
139
139
  end
140
140
 
141
+ def verify(credit_card, options={})
142
+ MultiResponse.run(:use_first_response) do |r|
143
+ r.process { authorize(100, credit_card, options) }
144
+ r.process(:ignore_result) { void(r.authorization, options) }
145
+ end
146
+ end
147
+
141
148
  private
142
149
  def add_reference(post, identification)
143
150
  order_id, transaction_id, authorization, security_key = identification.split(';')
@@ -390,4 +397,3 @@ module ActiveMerchant #:nodoc:
390
397
 
391
398
  end
392
399
  end
393
-
@@ -203,7 +203,7 @@ module ActiveMerchant #:nodoc:
203
203
  add_merchant_key(xml, options)
204
204
  xml.tag! 'METHOD', 'CC'
205
205
  xml.tag! 'NOTE', options[:description] if options[:description]
206
- xml.tag! 'ORDERID', options[:order_id]
206
+ xml.tag! 'ORDERID', truncate(options[:order_id], 25)
207
207
  xml.tag! 'OVERRIDE_FROM', 0 # Docs say not required, but doesn't work without it
208
208
  end
209
209
 
@@ -49,7 +49,6 @@ module ActiveMerchant #:nodoc:
49
49
  requires!(options, :login)
50
50
  @api_key = options[:login]
51
51
  @fee_refund_api_key = options[:fee_refund_login]
52
- @version = options[:version]
53
52
 
54
53
  super
55
54
  end
@@ -110,6 +109,7 @@ module ActiveMerchant #:nodoc:
110
109
  post = {}
111
110
  add_amount(post, money, options)
112
111
  post[:refund_application_fee] = true if options[:refund_application_fee]
112
+ post[:reverse_transfer] = options[:reverse_transfer] if options[:reverse_transfer]
113
113
 
114
114
  MultiResponse.run(:first) do |r|
115
115
  r.process { commit(:post, "charges/#{CGI.escape(identification)}/refund", post, options) }
@@ -123,7 +123,7 @@ module ActiveMerchant #:nodoc:
123
123
 
124
124
  def verify(payment, options = {})
125
125
  MultiResponse.run(:use_first_response) do |r|
126
- r.process { authorize(50, payment, options) }
126
+ r.process { authorize(50, payment, options.merge(currency: "USD")) }
127
127
  r.process(:ignore_result) { void(r.authorization, options) }
128
128
  end
129
129
  end
@@ -243,7 +243,7 @@ module ActiveMerchant #:nodoc:
243
243
  add_customer_data(post, options)
244
244
  add_metadata(post, options)
245
245
  post[:description] = options[:description]
246
- post[:statement_description] = options[:statement_description]
246
+ post[:statement_descriptor] = options[:statement_description]
247
247
  add_customer(post, payment, options)
248
248
  add_flags(post, options)
249
249
  end
@@ -381,20 +381,23 @@ module ActiveMerchant #:nodoc:
381
381
 
382
382
  def headers(options = {})
383
383
  key = options[:key] || @api_key
384
- version = options[:version] || @version
385
384
  idempotency_key = options[:idempotency_key]
386
385
 
387
386
  headers = {
388
387
  "Authorization" => "Basic " + Base64.encode64(key.to_s + ":").strip,
389
388
  "User-Agent" => "Stripe/v1 ActiveMerchantBindings/#{ActiveMerchant::VERSION}",
389
+ "Stripe-Version" => api_version(options),
390
390
  "X-Stripe-Client-User-Agent" => user_agent,
391
391
  "X-Stripe-Client-User-Metadata" => {:ip => options[:ip]}.to_json
392
392
  }
393
- headers.merge!("Stripe-Version" => version) if version
394
393
  headers.merge!("Idempotency-Key" => idempotency_key) if idempotency_key
395
394
  headers
396
395
  end
397
396
 
397
+ def api_version(options)
398
+ options[:version] || @options[:version] || "2015-04-07"
399
+ end
400
+
398
401
  def api_request(method, endpoint, parameters = nil, options = {})
399
402
  raw_response = response = nil
400
403
  begin
@@ -178,7 +178,8 @@ module ActiveMerchant #:nodoc:
178
178
  post[:card] = credit_card.number
179
179
  post[:cvv2] = credit_card.verification_value if credit_card.verification_value?
180
180
  post[:expir] = expdate(credit_card)
181
- post[:name] = credit_card.name
181
+ post[:name] = credit_card.name unless credit_card.name.blank?
182
+ post[:cardpresent] = true if credit_card.manual_entry
182
183
  end
183
184
  end
184
185
 
@@ -256,4 +257,3 @@ module ActiveMerchant #:nodoc:
256
257
  end
257
258
  end
258
259
  end
259
-
@@ -0,0 +1,280 @@
1
+ require 'nokogiri'
2
+
3
+ module ActiveMerchant
4
+ module Billing
5
+ class VancoGateway < Gateway
6
+ include Empty
7
+
8
+ self.test_url = 'https://www.vancodev.com/cgi-bin/wstest2.vps'
9
+ self.live_url = 'https://www.vancoservices.com/cgi-bin/ws2.vps'
10
+
11
+ self.supported_countries = ['US']
12
+ self.default_currency = 'USD'
13
+ self.supported_cardtypes = [:visa, :master, :american_express, :discover]
14
+
15
+ self.homepage_url = 'http://vancopayments.com/'
16
+ self.display_name = 'Vanco Payment Solutions'
17
+
18
+ def initialize(options={})
19
+ requires!(options, :user_id, :password, :client_id)
20
+ super
21
+ end
22
+
23
+ def purchase(money, payment_method, options={})
24
+ MultiResponse.run do |r|
25
+ r.process { commit(login_request) }
26
+ r.process { commit(purchase_request(money, payment_method, r.params["response_sessionid"], options)) }
27
+ end
28
+ end
29
+
30
+ def refund(money, authorization, options={})
31
+ MultiResponse.run do |r|
32
+ r.process { commit(login_request) }
33
+ r.process { commit(refund_request(money, authorization, r.params["response_sessionid"])) }
34
+ end
35
+ end
36
+
37
+ def supports_scrubbing?
38
+ true
39
+ end
40
+
41
+ def scrub(transcript)
42
+ transcript.
43
+ gsub(%r((<Password>).+(</Password>))i, '\1[FILTERED]\2').
44
+ gsub(%r((<CardCVV2>).+(</CardCVV2>))i, '\1[FILTERED]\2').
45
+ gsub(%r((<AccountNumber>).+(</AccountNumber>))i, '\1[FILTERED]\2')
46
+ end
47
+
48
+ private
49
+
50
+ def parse(xml)
51
+ response = {}
52
+
53
+ doc = Nokogiri::XML(xml)
54
+ doc.root.xpath('*').each do |node|
55
+ if (node.elements.empty?)
56
+ response[node.name.downcase.to_sym] = node.text
57
+ else
58
+ node.elements.each do |childnode|
59
+ childnode_to_response(response, node, childnode)
60
+ end
61
+ end
62
+ end
63
+
64
+ response
65
+ end
66
+
67
+ def childnode_to_response(response, node, childnode)
68
+ name = "#{node.name.downcase}_#{childnode.name.downcase}"
69
+ if name == "response_errors" && !childnode.elements.empty?
70
+ add_errors_to_response(response, childnode.to_s)
71
+ else
72
+ response[name.downcase.to_sym] = childnode.text
73
+ end
74
+ end
75
+
76
+ def add_errors_to_response(response, errors_xml)
77
+ errors_hash = Hash.from_xml(errors_xml).values.first
78
+ response[:response_errors] = errors_hash
79
+
80
+ error = errors_hash["Error"]
81
+ if error.kind_of?(Hash)
82
+ response[:error_message] = error["ErrorDescription"]
83
+ response[:error_codes] = error["ErrorCode"]
84
+ elsif error.kind_of?(Array)
85
+ error_str = error.map { |e| e["ErrorDescription"]}.join(". ")
86
+ error_codes = error.map { |e| e["ErrorCode"]}.join(", ")
87
+ response[:error_message] = "#{error_str}."
88
+ response[:error_codes] = error_codes
89
+ end
90
+ end
91
+
92
+ def commit(request)
93
+ response = parse(ssl_post(url, request, headers))
94
+
95
+ succeeded = success_from(response)
96
+ Response.new(
97
+ succeeded,
98
+ message_from(succeeded, response),
99
+ response,
100
+ authorization: authorization_from(response),
101
+ test: test?
102
+ )
103
+ end
104
+
105
+ def success_from(response)
106
+ !response[:response_errors]
107
+ end
108
+
109
+ def message_from(succeeded, response)
110
+ return "Success" if succeeded
111
+ response[:error_message]
112
+ end
113
+
114
+ def authorization_from(response)
115
+ [
116
+ response[:response_customerref],
117
+ response[:response_paymentmethodref],
118
+ response[:response_transactionref]
119
+ ].join("|")
120
+ end
121
+
122
+ def split_authorization(authorization)
123
+ authorization.to_s.split('|')
124
+ end
125
+
126
+ def purchase_request(money, payment_method, session_id, options)
127
+ build_xml_request do |doc|
128
+ add_auth(doc, "EFTAddCompleteTransaction", session_id)
129
+
130
+ doc.Request do
131
+ doc.RequestVars do
132
+ add_client_id(doc)
133
+ add_amount(doc, money, options)
134
+ add_payment_method(doc, payment_method, options)
135
+ add_purchase_noise(doc)
136
+ end
137
+ end
138
+ end
139
+ end
140
+
141
+ def refund_request(money, authorization, session_id)
142
+ build_xml_request do |doc|
143
+ add_auth(doc, "EFTAddCredit", session_id)
144
+
145
+ doc.Request do
146
+ doc.RequestVars do
147
+ add_client_id(doc)
148
+ add_amount(doc, money, options)
149
+ add_reference(doc, authorization)
150
+ add_refund_noise(doc)
151
+ end
152
+ end
153
+ end
154
+ end
155
+
156
+ def add_request(doc, request_type)
157
+ doc.RequestType(request_type)
158
+ doc.RequestID(SecureRandom.hex(15))
159
+ doc.RequestTime(Time.now)
160
+ doc.Version(2)
161
+ end
162
+
163
+ def add_auth(doc, request_type, session_id)
164
+ doc.Auth do
165
+ add_request(doc, request_type)
166
+ doc.SessionID(session_id)
167
+ end
168
+ end
169
+
170
+ def add_reference(doc, authorization)
171
+ customer_ref, payment_method_ref, transaction_ref = split_authorization(authorization)
172
+ doc.CustomerRef(customer_ref)
173
+ doc.PaymentMethodRef(payment_method_ref)
174
+ doc.TransactionRef(transaction_ref)
175
+ end
176
+
177
+ def add_amount(doc, money, options)
178
+ if empty?(options[:fund_id])
179
+ doc.Amount(amount(money))
180
+ else
181
+ doc.Funds do
182
+ doc.Fund do
183
+ doc.FundID(options[:fund_id])
184
+ doc.FundAmount(amount(money))
185
+ end
186
+ end
187
+ end
188
+ end
189
+
190
+ def add_payment_method(doc, payment_method, options)
191
+ if card_brand(payment_method) == 'check'
192
+ add_echeck(doc, payment_method)
193
+ else
194
+ add_credit_card(doc, payment_method, options)
195
+ end
196
+ end
197
+
198
+ def add_credit_card(doc, credit_card, options)
199
+ address = options[:billing_address]
200
+
201
+ doc.AccountNumber(credit_card.number)
202
+ doc.CustomerName("#{credit_card.last_name}, #{credit_card.first_name}")
203
+ doc.CardExpMonth(format(credit_card.month, :two_digits))
204
+ doc.CardExpYear(format(credit_card.year, :two_digits))
205
+ doc.CardCVV2(credit_card.verification_value)
206
+ doc.CardBillingName(credit_card.name)
207
+ doc.CardBillingAddr1(address[:address1])
208
+ doc.CardBillingAddr2(address[:address2])
209
+ doc.CardBillingCity(address[:city])
210
+ doc.CardBillingState(address[:state])
211
+ doc.CardBillingZip(address[:zip])
212
+ doc.CardBillingCountryCode(address[:country])
213
+ doc.AccountType("CC")
214
+ end
215
+
216
+ def add_echeck(doc, echeck)
217
+ if echeck.account_type == "savings"
218
+ doc.AccountType("S")
219
+ else
220
+ doc.AccountType("C")
221
+ end
222
+
223
+ doc.CustomerName("#{echeck.last_name}, #{echeck.first_name}")
224
+ doc.AccountNumber(echeck.account_number)
225
+ doc.RoutingNumber(echeck.routing_number)
226
+ doc.TransactionTypeCode("TEL")
227
+ end
228
+
229
+ def add_purchase_noise(doc)
230
+ doc.StartDate("0000-00-00")
231
+ doc.FrequencyCode("O")
232
+ end
233
+
234
+ def add_refund_noise(doc)
235
+ doc.ContactName("Bilbo Baggins")
236
+ doc.ContactPhone("1234567890")
237
+ doc.ContactExtension("None")
238
+ doc.ReasonForCredit("Refund requested")
239
+ end
240
+
241
+ def add_client_id(doc)
242
+ doc.ClientID(@options[:client_id])
243
+ end
244
+
245
+ def login_request
246
+ build_xml_request do |doc|
247
+ doc.Auth do
248
+ add_request(doc, "Login")
249
+ end
250
+
251
+ doc.Request do
252
+ doc.RequestVars do
253
+ doc.UserID(@options[:user_id])
254
+ doc.Password(@options[:password])
255
+ end
256
+ end
257
+ end
258
+ end
259
+
260
+ def build_xml_request
261
+ builder = Nokogiri::XML::Builder.new
262
+ builder.__send__("VancoWS") do |doc|
263
+ yield(doc)
264
+ end
265
+ builder.to_xml
266
+ end
267
+
268
+ def url
269
+ (test? ? test_url : live_url)
270
+ end
271
+
272
+ def headers
273
+ {
274
+ 'Content-Type' => 'text/xml'
275
+ }
276
+ end
277
+
278
+ end
279
+ end
280
+ end