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,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