activemerchant 1.49.0 → 1.50.0

Sign up to get free protection for your applications and to get access to all the features.
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,245 @@
1
+ require 'nokogiri'
2
+
3
+ module ActiveMerchant #:nodoc:
4
+ module Billing #:nodoc:
5
+ class MerchantPartnersGateway < Gateway
6
+ self.display_name = "Merchant Partners"
7
+ self.homepage_url = "http://www.merchantpartners.com/"
8
+
9
+ self.live_url = "https://trans.merchantpartners.com/cgi-bin/ProcessXML.cgi"
10
+
11
+ self.supported_countries = ["US"]
12
+ self.default_currency = "USD"
13
+ self.money_format = :dollars
14
+ self.supported_cardtypes = [:visa, :master, :american_express, :discover, :diners_club, :jcb]
15
+
16
+ def initialize(options={})
17
+ requires!(options, :account_id, :merchant_pin)
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(payment_method.is_a?(String) ? :stored_purchase : :purchase, 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(:authorize, 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(:capture, post)
46
+ end
47
+
48
+ def void(authorization, options={})
49
+ post = {}
50
+ add_reference(post, authorization)
51
+
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(:refund, 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(payment_method.is_a?(String) ? :stored_credit : :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 store(payment_method, options = {})
80
+ post = {}
81
+ add_payment_method(post, payment_method)
82
+ add_customer_data(post, options)
83
+
84
+ post[:profileactiontype] = options[:profileactiontype] || STORE_TX_TYPES[:store_only]
85
+
86
+ commit(:store, post)
87
+ end
88
+
89
+ def supports_scrubbing?
90
+ true
91
+ end
92
+
93
+ def scrub(transcript)
94
+ transcript.
95
+ gsub(%r((<ccnum>)[^<]+(<))i, '\1[FILTERED]\2').
96
+ gsub(%r((<cvv2>)[^<]+(<))i, '\1[FILTERED]\2').
97
+ gsub(%r((<merchantpin>)[^<]+(<))i, '\1[FILTERED]\2')
98
+ end
99
+
100
+ def test?
101
+ @options[:account_id].eql?('TEST0')
102
+ end
103
+
104
+ private
105
+
106
+ def add_invoice(post, money, options)
107
+ post[:amount] = amount(money)
108
+ post[:merchantordernumber] = options[:order_id]
109
+ post[:currency] = options[:currency] || currency(money)
110
+ end
111
+
112
+ def add_payment_method(post, payment_method)
113
+ if(payment_method.is_a?(String))
114
+ user_profile_id, last_4 = split_authorization(payment_method)
115
+ post[:userprofileid] = user_profile_id
116
+ post[:last4digits] = last_4
117
+ else
118
+ post[:ccname] = payment_method.name
119
+ post[:ccnum] = payment_method.number
120
+ post[:cvv2] = payment_method.verification_value
121
+ post[:expmon] = format(payment_method.month, :two_digits)
122
+ post[:expyear] = format(payment_method.year, :four_digits)
123
+ post[:swipedata] = payment_method.track_data if payment_method.track_data
124
+ end
125
+ end
126
+
127
+ def add_customer_data(post, options)
128
+ post[:email] = options[:email] if options[:email]
129
+ post[:ipaddress] = options[:ip] if options[:ip]
130
+ if(billing_address = options[:billing_address])
131
+ post[:billaddr1] = billing_address[:address1]
132
+ post[:billaddr2] = billing_address[:address2]
133
+ post[:billcity] = billing_address[:city]
134
+ post[:billstate] = billing_address[:state]
135
+ post[:billcountry] = billing_address[:country]
136
+ post[:bilzip] = billing_address[:zip]
137
+ post[:phone] = billing_address[:phone]
138
+ end
139
+ end
140
+
141
+ def add_reference(post, authorization)
142
+ post[:historykeyid] = authorization
143
+ end
144
+
145
+ ACTIONS = {
146
+ purchase: "2",
147
+ authorize: "1",
148
+ capture: "3",
149
+ void: "5",
150
+ refund: "4",
151
+ credit: "6",
152
+ store: "7",
153
+ stored_purchase: "8",
154
+ stored_credit: "13"
155
+ }
156
+
157
+ STORE_TX_TYPES = {
158
+ store_only: "3"
159
+ }
160
+
161
+ def commit(action, post)
162
+ post[:acctid] = @options[:account_id]
163
+ post[:merchantpin] = @options[:merchant_pin]
164
+ post[:service] = ACTIONS[action] if ACTIONS[action]
165
+
166
+ data = build_request(post)
167
+ response_data = parse(ssl_post(live_url, data, headers))
168
+ succeeded = success_from(response_data)
169
+
170
+ Response.new(
171
+ succeeded,
172
+ message_from(succeeded, response_data),
173
+ response_data,
174
+ authorization: authorization_from(post, response_data),
175
+ :avs_result => AVSResult.new(code: response_data["avs_response"]),
176
+ :cvv_result => CVVResult.new(response_data["cvv2_response"]),
177
+ test: test?
178
+ )
179
+ end
180
+
181
+ def headers
182
+ {
183
+ "Content-Type" => "application/xml"
184
+ }
185
+ end
186
+
187
+ def build_request(post)
188
+ Nokogiri::XML::Builder.new(:encoding => 'utf-8') do |xml|
189
+ xml.interface_driver {
190
+ xml.trans_catalog {
191
+ xml.transaction(name: "creditcard") {
192
+ xml.inputs {
193
+ post.each do |field, value|
194
+ xml.send(field, value)
195
+ end
196
+ }
197
+ }
198
+ }
199
+ }
200
+ end.to_xml
201
+ end
202
+
203
+ def parse(body)
204
+ response = {}
205
+ Nokogiri::XML(CGI.unescapeHTML(body)).xpath("//trans_catalog/transaction/outputs").children.each do |node|
206
+ parse_element(response, node)
207
+ end
208
+ response
209
+ end
210
+
211
+ def parse_element(response, node)
212
+ if node.elements.size == 0
213
+ response[node.name.downcase.underscore.to_sym] = node.text
214
+ else
215
+ node.elements.each{|element| parse_element(response, element) }
216
+ end
217
+ end
218
+
219
+ def success_from(response)
220
+ response[:status] == "Approved"
221
+ end
222
+
223
+ def message_from(succeeded, response)
224
+ succeeded ? "Succeeded" : error_message_from(response)
225
+ end
226
+
227
+ def authorization_from(request, response)
228
+ request[:service] == ACTIONS[:store] ?
229
+ "#{response[:userprofileid]}|#{response[:last4digits]}" :
230
+ response[:historyid]
231
+ end
232
+
233
+ def split_authorization(authorization)
234
+ authorization.split("|")
235
+ end
236
+
237
+ def error_message_from(response)
238
+ if(response[:status] == "Declined")
239
+ match = response[:result].match(/DECLINED:\d{10}:(.+):/)
240
+ match[1] if match
241
+ end
242
+ end
243
+ end
244
+ end
245
+ end
@@ -31,6 +31,7 @@ module ActiveMerchant #:nodoc:
31
31
  self.display_name = 'NETbilling'
32
32
  self.homepage_url = 'http://www.netbilling.com'
33
33
  self.supported_countries = ['US']
34
+ self.ssl_version = :TLSv1
34
35
  self.supported_cardtypes = [:visa, :master, :american_express, :discover, :jcb, :diners_club]
35
36
 
36
37
  def initialize(options = {})
@@ -0,0 +1,319 @@
1
+ require 'active_merchant/billing/rails'
2
+
3
+ module ActiveMerchant #:nodoc:
4
+ module Billing #:nodoc:
5
+ class OmiseGateway < Gateway
6
+ API_VERSION = '1.0'
7
+ API_URL = 'https://api.omise.co/'
8
+ VAULT_URL = 'https://vault.omise.co/'
9
+
10
+ STANDARD_ERROR_CODE_MAPPING = {
11
+ 'invalid_security_code' => STANDARD_ERROR_CODE[:invalid_cvc],
12
+ 'failed_capture' => STANDARD_ERROR_CODE[:card_declined]
13
+ }
14
+
15
+ self.live_url = self.test_url = API_URL
16
+
17
+ # Currency supported by Omise
18
+ # * Thai Baht with Satang, ie. 9000 => 90 THB
19
+ self.default_currency = 'THB'
20
+ self.money_format = :cents
21
+
22
+ #Country supported by Omise
23
+ # * Thailand
24
+ self.supported_countries = %w( TH )
25
+
26
+ # Credit cards supported by Omise
27
+ # * VISA
28
+ # * MasterCard
29
+ self.supported_cardtypes = [:visa, :master]
30
+
31
+ # Omise main page
32
+ self.homepage_url = 'https://www.omise.co/'
33
+ self.display_name = 'Omise'
34
+
35
+ # Creates a new OmiseGateway.
36
+ #
37
+ # Omise requires public_key for token creation.
38
+ # And it requires secret_key for other transactions.
39
+ # These keys can be found in https://dashboard.omise.co/test/api-keys
40
+ #
41
+ # ==== Options
42
+ #
43
+ # * <tt>:public_key</tt> -- Omise's public key (REQUIRED).
44
+ # * <tt>:secret_key</tt> -- Omise's secret key (REQUIRED).
45
+
46
+ def initialize(options={})
47
+ requires!(options, :public_key, :secret_key)
48
+ @public_key = options[:public_key]
49
+ @secret_key = options[:secret_key]
50
+ super
51
+ end
52
+
53
+ # Perform a purchase (with auto capture)
54
+ #
55
+ # ==== Parameters
56
+ #
57
+ # * <tt>money</tt> -- The purchasing amount in Thai Baht Satang
58
+ # * <tt>payment_method</tt> -- The CreditCard object
59
+ # * <tt>options</tt> -- An optional parameters, such as token from Omise.js
60
+ #
61
+ # ==== Options
62
+ # * <tt>token_id</tt> -- token id, use Omise.js library to retrieve a token id
63
+ # if this is passed as an option, it will ignore tokenizing via Omisevaultgateway object
64
+ #
65
+ # === Example
66
+ # To create a charge on a card
67
+ #
68
+ # purchase(money, Creditcard_object)
69
+ #
70
+ # To create a charge on a token
71
+ #
72
+ # purchase(money, nil, { :token_id => token_id, ... })
73
+ #
74
+ # To create a charge on a customer
75
+ #
76
+ # purchase(money, nil, { :customer_id => customer_id })
77
+
78
+ def purchase(money, payment_method, options={})
79
+ create_charge(money, payment_method, options)
80
+ end
81
+
82
+ # Authorize a charge.
83
+ #
84
+ # ==== Parameters
85
+ #
86
+ # * <tt>money</tt> -- The purchasing amount in Thai Baht Satang
87
+ # * <tt>payment_method</tt> -- The CreditCard object
88
+ # * <tt>options</tt> -- An optional parameters, such as token or capture
89
+
90
+ def authorize(money, payment_method, options={})
91
+ options[:capture] = 'false'
92
+ create_charge(money, payment_method, options)
93
+ end
94
+
95
+ # Capture an authorized charge.
96
+ #
97
+ # ==== Parameters
98
+ #
99
+ # * <tt>money</tt> -- An amount in Thai Baht Satang
100
+ # * <tt>charge_id</tt> -- The CreditCard object
101
+ # * <tt>options</tt> -- An optional parameters, such as token or capture
102
+
103
+ def capture(money, charge_id, options={})
104
+ post = {}
105
+ add_amount(post, money, options)
106
+ commit(:post, "charges/#{CGI.escape(charge_id)}/capture", post, options)
107
+ end
108
+
109
+ # Refund a charge.
110
+ #
111
+ # ==== Parameters
112
+ #
113
+ # * <tt>money</tt> -- An amount of money to charge in Satang.
114
+ # * <tt>charge_id</tt> -- The CreditCard object
115
+ # * <tt>options</tt> -- An optional parameters, such as token or capture
116
+
117
+ def refund(money, charge_id, options={})
118
+ options[:amount] = money if money
119
+ commit(:post, "charges/#{CGI.escape(charge_id)}/refunds", options)
120
+ end
121
+
122
+ # Store a card details as customer
123
+ #
124
+ # ==== Parameters
125
+ #
126
+ # * <tt>payment_method</tt> -- The CreditCard.
127
+ # * <tt>options</tt> -- Optional Customer information:
128
+ # 'email' (A customer email)
129
+ # 'description' (A customer description)
130
+
131
+ def store(payment_method, options={})
132
+ post, card_params = {}, {}
133
+ add_customer_data(post, options)
134
+ add_token(card_params, payment_method, options)
135
+ commit(:post, 'customers', post.merge(card_params), options)
136
+ end
137
+
138
+ # Delete a customer and all associated credit cards.
139
+ #
140
+ # ==== Parameters
141
+ #
142
+ # * <tt>customer_id</tt> -- The Customer identifier (REQUIRED).
143
+
144
+ def unstore(customer_id, options={})
145
+ commit(:delete, "customers/#{CGI.escape(customer_id)}")
146
+ end
147
+
148
+ # Enable scrubbling sensitive information
149
+ def supports_scrubbing?
150
+ true
151
+ end
152
+
153
+ # Scrub sensitive information out of HTTP transcripts
154
+ #
155
+ # ==== Parameters
156
+ #
157
+ # * <tt>transcript</tt> -- The HTTP transcripts
158
+
159
+ def scrub(transcript)
160
+ transcript.
161
+ gsub(/(Authorization: Basic )\w+/i, '\1[FILTERED]').
162
+ gsub(/(\\"number\\":)\\"\d+\\"/, '\1[FILTERED]').
163
+ gsub(/(\\"security_code\\":)\\"\d+\\"/,'\1[FILTERED]')
164
+ end
165
+
166
+ private
167
+
168
+ def create_charge(money, payment_method, options)
169
+ post = {}
170
+ add_token(post, payment_method, options)
171
+ add_amount(post, money, options)
172
+ add_customer(post, options)
173
+ post[:capture] = options[:capture] if options[:capture]
174
+ commit(:post, 'charges', post, options)
175
+ end
176
+
177
+ def headers(options={})
178
+ key = options[:key] || @secret_key
179
+ {
180
+ 'Content-Type' => 'application/json;utf-8',
181
+ 'User-Agent' => "Omise/v#{API_VERSION} ActiveMerchantBindings/#{ActiveMerchant::VERSION}",
182
+ 'Authorization' => 'Basic ' + Base64.encode64(key.to_s + ':').strip,
183
+ 'Accept-Encoding' => 'utf-8'
184
+ }
185
+ end
186
+
187
+ def url_for(endpoint)
188
+ (endpoint == 'tokens' ? VAULT_URL : API_URL) + endpoint
189
+ end
190
+
191
+ def post_data(parameters)
192
+ parameters.present? ? parameters.to_json : nil
193
+ end
194
+
195
+ def https_request(method, endpoint, parameters=nil, options={})
196
+ raw_response = response = nil
197
+ begin
198
+ raw_response = ssl_request(method, url_for(endpoint), post_data(parameters), headers(options))
199
+ response = parse(raw_response)
200
+ rescue ResponseError => e
201
+ raw_response = e.response.body
202
+ response = parse(raw_response)
203
+ rescue JSON::ParserError
204
+ response = json_error(raw_response)
205
+ end
206
+ response
207
+ end
208
+
209
+ def parse(body)
210
+ JSON.parse(body)
211
+ end
212
+
213
+ def json_error(raw_response)
214
+ msg = "Invalid response received from Omise API. Please contact support@omise.co if you continue to receive this message."
215
+ msg += "The raw response returned by the API was #{raw_response.inspect})"
216
+ { message: msg }
217
+ end
218
+
219
+ def commit(method, endpoint, params=nil, options={})
220
+ response = https_request(method, endpoint, params, options)
221
+ Response.new(
222
+ successful?(response),
223
+ message_from(response),
224
+ response,
225
+ {
226
+ authorization: authorization_from(response),
227
+ test: test?,
228
+ error_code: successful?(response) ? nil : standard_error_code_mapping(response)
229
+ }
230
+ )
231
+ end
232
+
233
+ def standard_error_code_mapping(response)
234
+ STANDARD_ERROR_CODE_MAPPING[error_code_from(response)] || message_to_standard_error_code_from(response)
235
+ end
236
+
237
+ def error_code_from(response)
238
+ error?(response) ? response['code'] : response['failure_code']
239
+ end
240
+
241
+ def message_to_standard_error_code_from(response)
242
+ message = response['message'] if response['code'] == 'invalid_card'
243
+ case message
244
+ when /brand not supported/
245
+ STANDARD_ERROR_CODE[:invalid_number]
246
+ when /number is invalid/
247
+ STANDARD_ERROR_CODE[:incorrect_number]
248
+ when /expiration date cannot be in the past/
249
+ STANDARD_ERROR_CODE[:expired_card]
250
+ when /expiration \w+ is invalid/
251
+ STANDARD_ERROR_CODE[:invalid_expiry_date]
252
+ else
253
+ STANDARD_ERROR_CODE[:processing_error]
254
+ end
255
+ end
256
+
257
+ def message_from(response)
258
+ if successful?(response)
259
+ 'Success'
260
+ else
261
+ (response['message'] ? response['message'] : response['failure_message'])
262
+ end
263
+ end
264
+
265
+ def authorization_from(response)
266
+ response['id'] if successful?(response)
267
+ end
268
+
269
+ def successful?(response)
270
+ !error?(response) && response['failure_code'].nil?
271
+ end
272
+
273
+ def error?(response)
274
+ response.key?('object') && (response['object'] == 'error')
275
+ end
276
+
277
+ def get_token(post, credit_card)
278
+ add_creditcard(post, credit_card) if credit_card
279
+ commit(:post, 'tokens', post, { key: @public_key })
280
+ end
281
+
282
+ def add_token(post, credit_card, options={})
283
+ if options[:token_id].present?
284
+ post[:card] = options[:token_id]
285
+ else
286
+ response = get_token(post, credit_card)
287
+ response.authorization ? (post[:card] = response.authorization) : response
288
+ end
289
+ end
290
+
291
+ def add_creditcard(post, payment_method)
292
+ card = {
293
+ number: payment_method.number,
294
+ name: payment_method.name,
295
+ security_code: payment_method.verification_value,
296
+ expiration_month: payment_method.month,
297
+ expiration_year: payment_method.year
298
+ }
299
+ post[:card] = card
300
+ end
301
+
302
+ def add_customer(post, options={})
303
+ post[:customer] = options[:customer_id] if options[:customer_id]
304
+ end
305
+
306
+ def add_customer_data(post, options={})
307
+ post[:description] = options[:description] if options[:description]
308
+ post[:email] = options[:email] if options[:email]
309
+ end
310
+
311
+ def add_amount(post, money, options)
312
+ post[:amount] = amount(money)
313
+ post[:currency] = (options[:currency] || currency(money))
314
+ post[:description] = options[:description] if options.key?(:description)
315
+ end
316
+
317
+ end
318
+ end
319
+ end