activemerchant 1.21.0 → 1.22.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 (55) hide show
  1. data.tar.gz.sig +0 -0
  2. data/CHANGELOG +63 -0
  3. data/CONTRIBUTORS +29 -0
  4. data/README.md +195 -0
  5. data/lib/active_merchant/billing/gateways/authorize_net_cim.rb +2 -0
  6. data/lib/active_merchant/billing/gateways/barclays_epdq.rb +2 -2
  7. data/lib/active_merchant/billing/gateways/blue_pay.rb +492 -11
  8. data/lib/active_merchant/billing/gateways/braintree_blue.rb +46 -19
  9. data/lib/active_merchant/billing/gateways/certo_direct.rb +1 -1
  10. data/lib/active_merchant/billing/gateways/cyber_source.rb +342 -106
  11. data/lib/active_merchant/billing/gateways/elavon.rb +2 -0
  12. data/lib/active_merchant/billing/gateways/epay.rb +3 -1
  13. data/lib/active_merchant/billing/gateways/itransact.rb +450 -0
  14. data/lib/active_merchant/billing/gateways/migs.rb +259 -0
  15. data/lib/active_merchant/billing/gateways/migs/migs_codes.rb +100 -0
  16. data/lib/active_merchant/billing/gateways/moneris_us.rb +211 -0
  17. data/lib/active_merchant/billing/gateways/ogone.rb +104 -12
  18. data/lib/active_merchant/billing/gateways/orbital.rb +15 -6
  19. data/lib/active_merchant/billing/gateways/paybox_direct.rb +1 -4
  20. data/lib/active_merchant/billing/gateways/payflow.rb +8 -3
  21. data/lib/active_merchant/billing/gateways/payflow/payflow_common_api.rb +4 -1
  22. data/lib/active_merchant/billing/gateways/payflow_express.rb +4 -2
  23. data/lib/active_merchant/billing/gateways/payment_express.rb +1 -1
  24. data/lib/active_merchant/billing/gateways/paypal.rb +3 -18
  25. data/lib/active_merchant/billing/gateways/paypal/paypal_common_api.rb +287 -1
  26. data/lib/active_merchant/billing/gateways/paypal/paypal_recurring_api.rb +245 -0
  27. data/lib/active_merchant/billing/gateways/paypal_express.rb +14 -66
  28. data/lib/active_merchant/billing/gateways/realex.rb +5 -7
  29. data/lib/active_merchant/billing/gateways/stripe.rb +1 -9
  30. data/lib/active_merchant/billing/gateways/usa_epay_advanced.rb +2 -2
  31. data/lib/active_merchant/billing/gateways/usa_epay_transaction.rb +1 -5
  32. data/lib/active_merchant/billing/gateways/viaklix.rb +7 -2
  33. data/lib/active_merchant/billing/gateways/vindicia.rb +359 -0
  34. data/lib/active_merchant/billing/integrations/dotpay.rb +22 -0
  35. data/lib/active_merchant/billing/integrations/dotpay/helper.rb +77 -0
  36. data/lib/active_merchant/billing/integrations/dotpay/notification.rb +86 -0
  37. data/lib/active_merchant/billing/integrations/dotpay/return.rb +11 -0
  38. data/lib/active_merchant/billing/integrations/epay.rb +21 -0
  39. data/lib/active_merchant/billing/integrations/epay/helper.rb +55 -0
  40. data/lib/active_merchant/billing/integrations/epay/notification.rb +110 -0
  41. data/lib/active_merchant/billing/integrations/paypal/notification.rb +2 -1
  42. data/lib/active_merchant/billing/integrations/quickpay/helper.rb +2 -3
  43. data/lib/active_merchant/billing/integrations/robokassa.rb +49 -0
  44. data/lib/active_merchant/billing/integrations/robokassa/common.rb +19 -0
  45. data/lib/active_merchant/billing/integrations/robokassa/helper.rb +50 -0
  46. data/lib/active_merchant/billing/integrations/robokassa/notification.rb +55 -0
  47. data/lib/active_merchant/billing/integrations/robokassa/return.rb +17 -0
  48. data/lib/active_merchant/billing/integrations/two_checkout.rb +25 -3
  49. data/lib/active_merchant/billing/integrations/two_checkout/helper.rb +15 -0
  50. data/lib/active_merchant/billing/integrations/verkkomaksut.rb +20 -0
  51. data/lib/active_merchant/billing/integrations/verkkomaksut/helper.rb +87 -0
  52. data/lib/active_merchant/billing/integrations/verkkomaksut/notification.rb +59 -0
  53. data/lib/active_merchant/version.rb +1 -1
  54. metadata +59 -26
  55. metadata.gz.sig +0 -0
@@ -43,6 +43,7 @@ module ActiveMerchant #:nodoc:
43
43
  add_creditcard(form, creditcard)
44
44
  add_address(form, options)
45
45
  add_customer_data(form, options)
46
+ add_test_mode(form, options)
46
47
  commit(:purchase, money, form)
47
48
  end
48
49
 
@@ -58,10 +59,15 @@ module ActiveMerchant #:nodoc:
58
59
  add_creditcard(form, creditcard)
59
60
  add_address(form, options)
60
61
  add_customer_data(form, options)
62
+ add_test_mode(form, options)
61
63
  commit(:credit, money, form)
62
64
  end
63
65
 
64
66
  private
67
+ def add_test_mode(form, options)
68
+ form[:test_mode] = 'TRUE' if options[:test_mode]
69
+ end
70
+
65
71
  def add_customer_data(form, options)
66
72
  form[:email] = options[:email].to_s.slice(0, 100) unless options[:email].blank?
67
73
  form[:customer_code] = options[:customer].to_s.slice(0, 10) unless options[:customer].blank?
@@ -129,8 +135,7 @@ module ActiveMerchant #:nodoc:
129
135
  'merchant_id' => @options[:login],
130
136
  'pin' => @options[:password],
131
137
  'show_form' => 'false',
132
- 'test_mode' => test? ? 'TRUE' : 'FALSE',
133
- 'result_format' => 'ASCII',
138
+ 'result_format' => 'ASCII'
134
139
  }
135
140
 
136
141
  result['user_id'] = @options[:user] unless @options[:user].blank?
@@ -0,0 +1,359 @@
1
+ begin
2
+ require "vindicia-api"
3
+ rescue LoadError
4
+ raise "Could not load the vindicia-api gem. Use `gem install vindicia-api` to install it."
5
+ end
6
+
7
+ module ActiveMerchant #:nodoc:
8
+ module Billing #:nodoc:
9
+
10
+ # For more information on the Vindicia Gateway please visit their {website}[http://vindicia.com/]
11
+ #
12
+ # The login and password are not the username and password you use to
13
+ # login to the Vindicia Merchant Portal.
14
+ #
15
+ # ==== Recurring Billing
16
+ #
17
+ # AutoBills are an feature of Vindicia's API that allows for creating and managing subscriptions.
18
+ #
19
+ # For more information about Vindicia's API and various other services visit their {Resource Center}[http://www.vindicia.com/resources/index.html]
20
+ class VindiciaGateway < Gateway
21
+ self.supported_countries = %w{US CA GB AU MX BR DE KR CN HK}
22
+ self.supported_cardtypes = [:visa, :master, :american_express, :discover]
23
+ self.homepage_url = 'http://www.vindicia.com/'
24
+ self.display_name = 'Vindicia'
25
+
26
+ class_attribute :test_url, :live_url
27
+
28
+ self.test_url = "https://soap.prodtest.sj.vindicia.com/soap.pl"
29
+ self.live_url = "http://soap.vindicia.com/soap.pl"
30
+
31
+ # Creates a new VindiciaGateway
32
+ #
33
+ # The gateway requires that a valid login and password be passed
34
+ # in the +options+ hash.
35
+ #
36
+ # ==== Options
37
+ #
38
+ # * <tt>:login</tt> -- Vindicia SOAP login (REQUIRED)
39
+ # * <tt>:password</tt> -- Vindicia SOAP password (REQUIRED)
40
+ # * <tt>:api_version</tt> -- Vindicia API Version - defaults to 3.6 (OPTIONAL)
41
+ # * <tt>:account_id</tt> -- Account Id which all transactions will be run against. (REQUIRED)
42
+ # * <tt>:transaction_prefix</tt> -- Prefix to order id for one-time transactions - defaults to 'X' (OPTIONAL
43
+ # * <tt>:min_chargeback_probability</tt> -- Minimum score for chargebacks - defaults to 65 (OPTIONAL)
44
+ # * <tt>:cvn_success</tt> -- Array of valid CVN Check return values - defaults to [M, P] (OPTIONAL)
45
+ # * <tt>:avs_success</tt> -- Array of valid AVS Check return values - defaults to [X, Y, A, W, Z] (OPTIONAL)
46
+ def initialize(options = {})
47
+ requires!(options, :login, :password)
48
+
49
+ config = lambda do |config|
50
+ config.login = options[:login]
51
+ config.password = options[:password]
52
+ config.api_version = options[:api_version] || "3.6"
53
+ config.endpoint = test? ? self.test_url : self.live_url
54
+ config.namespace = "http://soap.vindicia.com"
55
+ end
56
+
57
+ if Vindicia.config.is_configured?
58
+ config.call(Vindicia.config)
59
+ else
60
+ Vindicia.configure(&config)
61
+ end
62
+
63
+ requires!(options, :account_id)
64
+ @account_id = options[:account_id]
65
+
66
+ @transaction_prefix = options[:transaction_prefix] || "X"
67
+
68
+ @min_chargeback_probability = options[:min_chargeback_probability] || 65
69
+ @cvn_success = options[:cvn_success] || %w{M P}
70
+ @avs_success = options[:avs_success] || %w{X Y A W Z}
71
+
72
+ @options = options
73
+ @allowed_authorization_statuses = %w{Authorized}
74
+ end
75
+
76
+ # Perform a purchase, which is essentially an authorization and capture in a single operation.
77
+ #
78
+ # ==== Parameters
79
+ #
80
+ # * <tt>money</tt> -- The amount to be purchased as an Integer value in cents.
81
+ # * <tt>creditcard</tt> -- The CreditCard details for the transaction.
82
+ # * <tt>options</tt> -- A hash of optional parameters.
83
+ def purchase(money, creditcard, options = {})
84
+ response = authorize(money, creditcard, options)
85
+ return response if !response.success? || response.fraud_review?
86
+
87
+ capture(money, response.authorization, options)
88
+ end
89
+
90
+ # Performs an authorization, which reserves the funds on the customer's credit card, but does not
91
+ # charge the card.
92
+ #
93
+ # ==== Parameters
94
+ #
95
+ # * <tt>money</tt> -- The amount to be authorized as an Integer value in cents.
96
+ # * <tt>creditcard</tt> -- The CreditCard details for the transaction.
97
+ # * <tt>options</tt> -- A hash of optional parameters.
98
+ def authorize(money, creditcard, options = {})
99
+ vindicia_transaction = authorize_transaction(money, creditcard, options)
100
+ response = check_transaction(vindicia_transaction)
101
+
102
+ # if this response is under fraud review because of our AVS/CVV checks void the transaction
103
+ if !response.success? && response.fraud_review? && !response.authorization.blank?
104
+ void_response = void([vindicia_transaction[:transaction][:merchantTransactionId]], options)
105
+ if void_response.success?
106
+ return response
107
+ else
108
+ return void_response
109
+ end
110
+ end
111
+
112
+ response
113
+ end
114
+
115
+ # Captures the funds from an authorized transaction.
116
+ #
117
+ # ==== Parameters
118
+ #
119
+ # * <tt>money</tt> -- The amount to be captured as an Integer value in cents.
120
+ # * <tt>identification</tt> -- The authorization returned from the previous authorize request.
121
+ def capture(money, identification, options = {})
122
+ response = post(Vindicia::Transaction.capture({
123
+ :transactions => [{ :merchantTransactionId => identification }]
124
+ }))
125
+
126
+ if response[:return][:returnCode] != '200' || response[:qtyFail].to_i > 0
127
+ return fail(response)
128
+ end
129
+
130
+ success(response, identification)
131
+ end
132
+
133
+ # Void a previous transaction
134
+ #
135
+ # ==== Parameters
136
+ #
137
+ # * <tt>identification</tt> - The authorization returned from the previous authorize request.
138
+ # * <tt>options</tt> - Extra options (currently only :ip used)
139
+ def void(identification, options = {})
140
+ response = post(Vindicia::Transaction.cancel({
141
+ :transactions => [{
142
+ :account => { :merchantAccountId => @account_id },
143
+ :merchantTransactionId => identification,
144
+ :sourceIp => options[:ip]
145
+ }]
146
+ }))
147
+
148
+ if response[:return][:returnCode] == '200' && response[:qtyFail].to_i == 0
149
+ success(response, identification)
150
+ else
151
+ fail(response)
152
+ end
153
+ end
154
+
155
+ # Perform a recurring billing, which is essentially a purchase and autobill setup in a single operation.
156
+ #
157
+ # ==== Parameters
158
+ #
159
+ # * <tt>money</tt> -- The amount to be purchased as an Integer value in cents.
160
+ # * <tt>creditcard</tt> -- The CreditCard details for the transaction.
161
+ # * <tt>options</tt> -- A hash of parameters.
162
+ #
163
+ # ==== Options
164
+ #
165
+ # * <tt>:product_sku</tt> -- The subscription product's sku
166
+ # * <tt>:autobill_prefix</tt> -- Prefix to order id for subscriptions - defaults to 'A' (OPTIONAL)
167
+ def recurring(money, creditcard, options={})
168
+ options[:recurring] = true
169
+ @autobill_prefix = options[:autobill_prefix] || "A"
170
+
171
+ response = authorize(money, creditcard, options)
172
+ return response if !response.success? || response.fraud_review?
173
+
174
+ capture_resp = capture(money, response.authorization, options)
175
+ return capture_resp if !response.success?
176
+
177
+ # Setting up a recurring AutoBill requires an associated product
178
+ requires!(options, :product_sku)
179
+ autobill_response = check_subscription(authorize_subscription(options.merge(:product_sku => options[:product_sku])))
180
+
181
+ if autobill_response.success?
182
+ autobill_response
183
+ else
184
+ # If the AutoBill fails to set-up, void the transaction and return it as the response
185
+ void_response = void(capture_resp.authorization, options)
186
+ if void_response.success?
187
+ return autobill_response
188
+ else
189
+ return void_response
190
+ end
191
+ end
192
+ end
193
+
194
+ protected
195
+
196
+ def post(body)
197
+ parse(ssl_post(Vindicia.config.endpoint, body, "Content-Type" => "text/xml"))
198
+ end
199
+
200
+ def parse(response)
201
+ # Vindicia always returns in the form of request_type_response => { actual_response }
202
+ Hash.from_xml(response)["Envelope"]["Body"].values.first.with_indifferent_access
203
+ end
204
+
205
+ def check_transaction(vindicia_transaction)
206
+ if vindicia_transaction[:return][:returnCode] == '200'
207
+ status_log = vindicia_transaction[:transaction][:statusLog].first
208
+ if status_log[:creditCardStatus]
209
+ avs = status_log[:creditCardStatus][:avsCode]
210
+ cvn = status_log[:creditCardStatus][:cvnCode]
211
+ end
212
+
213
+ if @allowed_authorization_statuses.include?(status_log[:status]) &&
214
+ check_cvn(cvn) && check_avs(avs)
215
+
216
+ success(vindicia_transaction,
217
+ vindicia_transaction[:transaction][:merchantTransactionId],
218
+ avs, cvn)
219
+ else
220
+ # If the transaction is authorized, but it didn't pass our AVS/CVV checks send the authorization along so
221
+ # that is gets voided. Otherwise, send no authorization.
222
+ fail(vindicia_transaction, avs, cvn, false,
223
+ @allowed_authorization_statuses.include?(status_log[:status]) ? vindicia_transaction[:transaction][:merchantTransactionId] : "")
224
+ end
225
+ else
226
+ # 406 = Chargeback risk score is higher than minChargebackProbability, transaction not authorized.
227
+ fail(vindicia_transaction, nil, nil, vindicia_transaction[:return][:return_code] == '406')
228
+ end
229
+ end
230
+
231
+ def authorize_transaction(money, creditcard, options)
232
+ parameters = {
233
+ :amount => amount(money),
234
+ :currency => options[:currency] || currency(money)
235
+ }
236
+
237
+ add_account_data(parameters, options)
238
+ add_customer_data(parameters, options)
239
+ add_payment_source(parameters, creditcard, options)
240
+
241
+ post(Vindicia::Transaction.auth({
242
+ :transaction => parameters,
243
+ :minChargebackProbability => @min_chargeback_probability
244
+ }))
245
+ end
246
+
247
+ def add_account_data(parameters, options)
248
+ parameters[:account] = { :merchantAccountId => @account_id }
249
+ parameters[:sourceIp] = options[:ip] if options[:ip]
250
+ end
251
+
252
+ def add_customer_data(parameters, options)
253
+ parameters[:merchantTransactionId] = transaction_id(options[:order_id])
254
+ parameters[:shippingAddress] = convert_am_address_to_vindicia(options[:shipping_address])
255
+
256
+ # Transaction items must be provided for tax purposes
257
+ requires!(options, :line_items)
258
+ parameters[:transactionItems] = options[:line_items]
259
+
260
+ if options[:recurring]
261
+ parameters[:nameValues] = [{:name => 'merchantAutoBillIdentifier', :value => autobill_id(options[:order_id])}]
262
+ end
263
+ end
264
+
265
+ def add_payment_source(parameters, creditcard, options)
266
+ parameters[:sourcePaymentMethod] = {
267
+ :type => 'CreditCard',
268
+ :creditCard => { :account => creditcard.number, :expirationDate => "%4d%02d" % [creditcard.year, creditcard.month] },
269
+ :accountHolderName => creditcard.name,
270
+ :nameValues => [{ :name => 'CVN', :value => creditcard.verification_value }],
271
+ :billingAddress => convert_am_address_to_vindicia(options[:billing_address] || options[:address]),
272
+ :customerSpecifiedType => creditcard.type.capitalize,
273
+ :active => !!options[:recurring]
274
+ }
275
+ end
276
+
277
+ def authorize_subscription(options)
278
+ parameters = {}
279
+
280
+ add_account_data(parameters, options)
281
+ add_subscription_information(parameters, options)
282
+
283
+ post(Vindicia::AutoBill.update({
284
+ :autobill => parameters,
285
+ :validatePaymentMethod => false,
286
+ :minChargebackProbability => 100
287
+ }))
288
+ end
289
+
290
+ def check_subscription(vindicia_transaction)
291
+ if vindicia_transaction[:return][:returnCode] == '200'
292
+ if vindicia_transaction[:autobill] && vindicia_transaction[:autobill][:status] == "Active"
293
+ success(vindicia_transaction,
294
+ vindicia_transaction[:autobill][:merchantAutoBillId])
295
+ else
296
+ fail(vindicia_transaction)
297
+ end
298
+ else
299
+ fail(vindicia_transaction)
300
+ end
301
+ end
302
+
303
+ def add_subscription_information(parameters, options)
304
+ requires!(options, :product_sku)
305
+
306
+ if options[:shipping_address]
307
+ parameters[:account][:shipping_address] = options[:shipping_address]
308
+ end
309
+
310
+ parameters[:merchantAutoBillId] = autobill_id(options[:order_id])
311
+ parameters[:product] = { :merchantProductId => options[:product_sku] }
312
+ end
313
+
314
+ def check_avs(avs)
315
+ avs.blank? || @avs_success.include?(avs)
316
+ end
317
+
318
+ def check_cvn(cvn)
319
+ cvn.blank? || @cvn_success.include?(cvn)
320
+ end
321
+
322
+ def success(response, authorization, avs_code = nil, cvn_code = nil)
323
+ ActiveMerchant::Billing::Response.new(true, response[:return][:returnString], response,
324
+ { :fraud_review => false, :authorization => authorization, :test => test?,
325
+ :avs_result => { :code => avs_code }, :cvv_result => cvn_code })
326
+ end
327
+
328
+ def fail(response, avs_code = nil, cvn_code = nil, fraud_review = false, authorization = "")
329
+ ActiveMerchant::Billing::Response.new(false, response[:return][:returnString], response,
330
+ { :fraud_review => fraud_review || !authorization.blank?,
331
+ :authorization => authorization, :test => test?,
332
+ :avs_result => { :code => avs_code }, :cvv_result => cvn_code })
333
+
334
+ end
335
+
336
+ def autobill_id(order_id)
337
+ "#{@autobill_prefix}#{order_id}"
338
+ end
339
+
340
+ def transaction_id(order_id)
341
+ "#{@transaction_prefix}#{order_id}"
342
+ end
343
+
344
+ # Converts valid ActiveMerchant address hash to proper Vindicia format
345
+ def convert_am_address_to_vindicia(address)
346
+ return if address.nil?
347
+
348
+ convs = { :address1 => :addr1, :address2 => :addr2,
349
+ :state => :district, :zip => :postalCode }
350
+
351
+ vindicia_address = {}
352
+ address.each do |key, val|
353
+ vindicia_address[convs[key] || key] = val
354
+ end
355
+ vindicia_address
356
+ end
357
+ end
358
+ end
359
+ end
@@ -0,0 +1,22 @@
1
+ module ActiveMerchant #:nodoc:
2
+ module Billing #:nodoc:
3
+ module Integrations #:nodoc:
4
+ module Dotpay
5
+ autoload :Return, File.dirname(__FILE__) + '/dotpay/return.rb'
6
+ autoload :Helper, File.dirname(__FILE__) + '/dotpay/helper.rb'
7
+ autoload :Notification, File.dirname(__FILE__) + '/dotpay/notification.rb'
8
+
9
+ mattr_accessor :service_url
10
+ self.service_url = 'https://ssl.dotpay.pl'
11
+
12
+ def self.notification(post, options = {})
13
+ Notification.new(post, options)
14
+ end
15
+
16
+ def self.return(post, options = {})
17
+ Return.new(post, options)
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,77 @@
1
+ module ActiveMerchant #:nodoc:
2
+ module Billing #:nodoc:
3
+ module Integrations #:nodoc:
4
+ module Dotpay
5
+ class Helper < ActiveMerchant::Billing::Integrations::Helper
6
+ def initialize(order, account, options = {})
7
+ options = {:currency => 'PLN'}.merge options
8
+
9
+ super
10
+
11
+ add_field('channel', '0')
12
+ add_field('ch_lock', '0')
13
+ add_field('lang', 'PL')
14
+ add_field('onlinetransfer', '0')
15
+ add_field('tax', '0')
16
+ add_field('type', '2')
17
+ end
18
+
19
+ mapping :account, 'id'
20
+ mapping :amount, 'amount'
21
+
22
+ mapping :billing_address, :street => 'street',
23
+ :street_n1 => 'street_n1',
24
+ :street_n2 => 'street_n2',
25
+ :addr2 => 'addr2',
26
+ :addr3 => 'addr3',
27
+ :city => 'city',
28
+ :postcode => 'postcode',
29
+ :phone => 'phone',
30
+ :country => 'country'
31
+
32
+ mapping :buttontext, 'buttontext'
33
+ mapping :channel, 'channel'
34
+ mapping :ch_lock, 'ch_lock'
35
+ mapping :code, 'code'
36
+ mapping :control, 'control'
37
+ mapping :currency, 'currency'
38
+
39
+ mapping :customer, :firstname => 'firstname',
40
+ :lastname => 'lastname',
41
+ :email => 'email'
42
+
43
+ mapping :description, 'description'
44
+ mapping :lang, 'lang'
45
+ mapping :onlinetransfer, 'onlinetransfer'
46
+ mapping :order, 'description'
47
+ mapping :p_email, 'p_email'
48
+ mapping :p_info, 'p_info'
49
+ mapping :tax, 'tax'
50
+ mapping :type, 'type'
51
+ mapping :url, 'url'
52
+ mapping :urlc, 'urlc'
53
+
54
+ def billing_address(params = {})
55
+ country = lookup_country_code(params.delete(:country) { 'POL' }, :alpha3)
56
+ add_field(mappings[:billing_address][:country], country)
57
+
58
+ # Everything else
59
+ params.each do |k, v|
60
+ field = mappings[:billing_address][k]
61
+ add_field(field, v) unless field.nil?
62
+ end
63
+ end
64
+
65
+ private
66
+
67
+ def lookup_country_code(name_or_code, format = country_format)
68
+ country = Country.find(name_or_code)
69
+ country.code(format).to_s
70
+ rescue InvalidCountryCodeError
71
+ name_or_code
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end