activemerchant 1.3.2 → 1.4.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 (85) hide show
  1. data.tar.gz.sig +0 -0
  2. data/CHANGELOG +58 -0
  3. data/CONTRIBUTERS +25 -0
  4. data/MIT-LICENSE +3 -3
  5. data/README +16 -10
  6. data/Rakefile +4 -3
  7. data/lib/active_merchant.rb +7 -1
  8. data/lib/active_merchant/billing/check.rb +16 -9
  9. data/lib/active_merchant/billing/gateway.rb +1 -1
  10. data/lib/active_merchant/billing/gateways/authorize_net_cim.rb +702 -0
  11. data/lib/active_merchant/billing/gateways/beanstream.rb +102 -0
  12. data/lib/active_merchant/billing/gateways/beanstream/beanstream_core.rb +233 -0
  13. data/lib/active_merchant/billing/gateways/beanstream_interac.rb +54 -0
  14. data/lib/active_merchant/billing/gateways/braintree.rb +10 -1
  15. data/lib/active_merchant/billing/gateways/cyber_source.rb +26 -2
  16. data/lib/active_merchant/billing/gateways/data_cash.rb +255 -59
  17. data/lib/active_merchant/billing/gateways/modern_payments.rb +36 -0
  18. data/lib/active_merchant/billing/gateways/modern_payments_cim.rb +214 -0
  19. data/lib/active_merchant/billing/gateways/net_registry.rb +1 -0
  20. data/lib/active_merchant/billing/gateways/payflow/payflow_common_api.rb +2 -2
  21. data/lib/active_merchant/billing/gateways/payflow_express.rb +3 -11
  22. data/lib/active_merchant/billing/gateways/payment_express.rb +2 -2
  23. data/lib/active_merchant/billing/gateways/paypal/paypal_common_api.rb +39 -21
  24. data/lib/active_merchant/billing/gateways/paypal_ca.rb +13 -0
  25. data/lib/active_merchant/billing/gateways/paypal_express.rb +3 -12
  26. data/lib/active_merchant/billing/gateways/paypal_express_common.rb +20 -0
  27. data/lib/active_merchant/billing/gateways/protx.rb +25 -25
  28. data/lib/active_merchant/billing/gateways/sage.rb +145 -0
  29. data/lib/active_merchant/billing/gateways/sage/sage_bankcard.rb +88 -0
  30. data/lib/active_merchant/billing/gateways/sage/sage_core.rb +110 -0
  31. data/lib/active_merchant/billing/gateways/sage/sage_virtual_check.rb +97 -0
  32. data/lib/active_merchant/billing/gateways/secure_pay_au.rb +3 -1
  33. data/lib/active_merchant/billing/gateways/skip_jack.rb +2 -0
  34. data/lib/active_merchant/billing/gateways/trust_commerce.rb +1 -1
  35. data/lib/active_merchant/billing/gateways/wirecard.rb +304 -0
  36. data/lib/active_merchant/billing/integrations.rb +8 -2
  37. data/lib/active_merchant/billing/integrations/action_view_helper.rb +18 -4
  38. data/lib/active_merchant/billing/integrations/hi_trust/notification.rb +4 -2
  39. data/lib/active_merchant/billing/integrations/notification.rb +10 -1
  40. data/lib/active_merchant/lib/posts_data.rb +12 -3
  41. data/script/destroy +0 -0
  42. data/script/generate +0 -0
  43. data/test/extra/binding_of_caller.rb +0 -0
  44. data/test/extra/breakpoint.rb +0 -0
  45. data/test/fixtures.yml +24 -0
  46. data/test/remote/gateways/remote_authorize_net_cim_test.rb +459 -0
  47. data/test/remote/gateways/remote_beanstream_interac_test.rb +53 -0
  48. data/test/remote/gateways/remote_beanstream_test.rb +150 -0
  49. data/test/remote/gateways/remote_braintree_test.rb +22 -0
  50. data/test/remote/gateways/remote_cyber_source_test.rb +28 -3
  51. data/test/remote/gateways/remote_data_cash_test.rb +250 -48
  52. data/test/remote/gateways/remote_modern_payments_cim_test.rb +58 -0
  53. data/test/remote/gateways/remote_modern_payments_test.rb +43 -0
  54. data/test/remote/gateways/remote_sage_bankcard_test.rb +109 -0
  55. data/test/remote/gateways/remote_sage_test.rb +87 -0
  56. data/test/remote/gateways/remote_sage_virtual_check_test.rb +62 -0
  57. data/test/remote/gateways/remote_wirecard_test.rb +76 -0
  58. data/test/remote/integrations/remote_paypal_integration_test.rb +15 -3
  59. data/test/test_helper.rb +31 -13
  60. data/test/unit/check_test.rb +14 -2
  61. data/test/unit/credit_card_methods_test.rb +18 -0
  62. data/test/unit/gateways/authorize_net_cim_test.rb +641 -0
  63. data/test/unit/gateways/beanstream_interac_test.rb +51 -0
  64. data/test/unit/gateways/beanstream_test.rb +108 -0
  65. data/test/unit/gateways/braintree_test.rb +2 -5
  66. data/test/unit/gateways/cyber_source_test.rb +18 -0
  67. data/test/unit/gateways/data_cash_test.rb +32 -4
  68. data/test/unit/gateways/gateway_test.rb +8 -1
  69. data/test/unit/gateways/modern_payments_cim_test.rb +171 -0
  70. data/test/unit/gateways/net_registry_test.rb +6 -0
  71. data/test/unit/gateways/payflow_express_test.rb +18 -2
  72. data/test/unit/gateways/paypal_express_test.rb +154 -0
  73. data/test/unit/gateways/paypal_test.rb +140 -0
  74. data/test/unit/gateways/sage_bankcard_test.rb +162 -0
  75. data/test/unit/gateways/sage_virtual_check_test.rb +71 -0
  76. data/test/unit/gateways/secure_pay_au_test.rb +58 -1
  77. data/test/unit/gateways/skip_jack_test.rb +8 -0
  78. data/test/unit/gateways/verifi_test.rb +0 -1
  79. data/test/unit/gateways/wirecard_test.rb +232 -0
  80. data/test/unit/integrations/action_view_helper_test.rb +3 -0
  81. data/test/unit/integrations/notifications/hi_trust_notification_test.rb +23 -2
  82. data/test/unit/integrations/notifications/notification_test.rb +13 -0
  83. data/test/unit/posts_data_test.rb +20 -6
  84. metadata +40 -5
  85. metadata.gz.sig +0 -0
@@ -0,0 +1,102 @@
1
+ require File.dirname(__FILE__) + '/beanstream/beanstream_core'
2
+
3
+ module ActiveMerchant #:nodoc:
4
+ module Billing #:nodoc:
5
+ # This class implements the Canadian {Beanstream}[http://www.beanstream.com] payment gateway.
6
+ # It is also named TD Canada Trust Online Mart payment gateway.
7
+ # To learn more about the specification of Beanstream gateway, please read the OM_Direct_Interface_API.pdf,
8
+ # which you can get from your Beanstream account or get from me by email.
9
+ #
10
+ # == Supported transaction types by Beanstream:
11
+ # * +P+ - Purchase
12
+ # * +PA+ - Pre Authorization
13
+ # * +PAC+ - Pre Authorization Completion
14
+ #
15
+ # == Notes
16
+ # * Recurring billing is not yet implemented.
17
+ # * Adding of order products information is not implemented.
18
+ # * Ensure that country and province data is provided as a code such as "CA", "US", "QC".
19
+ # * login is the Beanstream merchant ID, username and password should be enabled in your Beanstream account and passed in using the <tt>:user</tt> and <tt>:password</tt> options.
20
+ # * Test your app with your true merchant id and test credit card information provided in the api pdf document.
21
+ #
22
+ # Example authorization (Beanstream PA transaction type):
23
+ #
24
+ # twenty = 2000
25
+ # gateway = BeanstreamGateway.new(
26
+ # :login => '100200000',
27
+ # :user => 'xiaobozz',
28
+ # :password => 'password'
29
+ # )
30
+ #
31
+ # credit_card = CreditCard.new(
32
+ # :number => '4030000010001234',
33
+ # :month => 8,
34
+ # :year => 2011,
35
+ # :first_name => 'xiaobo',
36
+ # :last_name => 'zzz',
37
+ # :verification_value => 137
38
+ # )
39
+ # response = gateway.authorize(twenty, credit_card,
40
+ # :order_id => '1234',
41
+ # :billing_address => {
42
+ # :name => 'xiaobo zzz',
43
+ # :phone => '555-555-5555',
44
+ # :address1 => '1234 Levesque St.',
45
+ # :address2 => 'Apt B',
46
+ # :city => 'Montreal',
47
+ # :state => 'QC',
48
+ # :country => 'CA',
49
+ # :zip => 'H2C1X8'
50
+ # },
51
+ # :email => 'xiaobozzz@example.com',
52
+ # :subtotal => 800,
53
+ # :shipping => 100,
54
+ # :tax1 => 100,
55
+ # :tax2 => 100,
56
+ # :custom => 'reference one'
57
+ # )
58
+ class BeanstreamGateway < Gateway
59
+ include BeanstreamCore
60
+
61
+ def authorize(money, credit_card, options = {})
62
+ post = {}
63
+ add_amount(post, money)
64
+ add_invoice(post, options)
65
+ add_credit_card(post, credit_card)
66
+ add_address(post, options)
67
+ add_transaction_type(post, :authorization)
68
+ commit(post)
69
+ end
70
+
71
+ def purchase(money, source, options = {})
72
+ post = {}
73
+ add_amount(post, money)
74
+ add_invoice(post, options)
75
+ add_source(post, source)
76
+ add_address(post, options)
77
+ add_transaction_type(post, purchase_action(source))
78
+ commit(post)
79
+ end
80
+
81
+ def void(authorization, options = {})
82
+ reference, amount, type = split_auth(authorization)
83
+
84
+ post = {}
85
+ add_reference(post, reference)
86
+ add_original_amount(post, amount)
87
+ add_transaction_type(post, void_action(type))
88
+ commit(post)
89
+ end
90
+
91
+ def interac
92
+ @interac ||= BeanstreamInteracGateway.new(@options)
93
+ end
94
+
95
+ private
96
+ def build_response(*args)
97
+ Response.new(*args)
98
+ end
99
+ end
100
+ end
101
+ end
102
+
@@ -0,0 +1,233 @@
1
+ module ActiveMerchant #:nodoc:
2
+ module Billing #:nodoc:
3
+ module BeanstreamCore
4
+ URL = 'https://www.beanstream.com/scripts/process_transaction.asp'
5
+
6
+ TRANSACTIONS = {
7
+ :authorization => 'PA',
8
+ :purchase => 'P',
9
+ :capture => 'PAC',
10
+ :credit => 'R',
11
+ :void => 'VP',
12
+ :check_purchase => 'D',
13
+ :check_credit => 'C',
14
+ :void_purchase => 'VP',
15
+ :void_credit => 'VR'
16
+ }
17
+
18
+ CVD_CODES = {
19
+ '1' => 'M',
20
+ '2' => 'N',
21
+ '3' => 'I',
22
+ '4' => 'S',
23
+ '5' => 'U',
24
+ '6' => 'P'
25
+ }
26
+
27
+ AVS_CODES = {
28
+ '0' => 'R',
29
+ '5' => 'I',
30
+ '9' => 'I'
31
+ }
32
+
33
+ def self.included(base)
34
+ base.default_currency = 'CAD'
35
+
36
+ # The countries the gateway supports merchants from as 2 digit ISO country codes
37
+ base.supported_countries = ['CA']
38
+
39
+ # The card types supported by the payment gateway
40
+ base.supported_cardtypes = [:visa, :master, :american_express]
41
+
42
+ # The homepage URL of the gateway
43
+ base.homepage_url = 'http://www.beanstream.com/'
44
+
45
+ # The name of the gateway
46
+ base.display_name = 'Beanstream.com'
47
+ end
48
+
49
+ # Only <tt>:login</tt> is required by default,
50
+ # which is the merchant's merchant ID. If you'd like to perform void,
51
+ # capture or credit transactions then you'll also need to add a username
52
+ # and password to your account under administration -> account settings ->
53
+ # order settings -> Use username/password validation
54
+ def initialize(options = {})
55
+ requires!(options, :login)
56
+ @options = options
57
+ super
58
+ end
59
+
60
+ def capture(money, authorization, options = {})
61
+ reference, amount, type = split_auth(authorization)
62
+
63
+ post = {}
64
+ add_amount(post, money)
65
+ add_reference(post, reference)
66
+ add_transaction_type(post, :capture)
67
+ commit(post)
68
+ end
69
+
70
+ def credit(money, source, options = {})
71
+ post = {}
72
+ reference, amount, type = split_auth(source)
73
+ add_reference(post, reference)
74
+ add_transaction_type(post, credit_action(type))
75
+ add_amount(post, money)
76
+ commit(post)
77
+ end
78
+
79
+ private
80
+ def purchase_action(source)
81
+ source.type.to_s == "check" ? :check_purchase : :purchase
82
+ end
83
+
84
+ def void_action(original_transaction_type)
85
+ original_transaction_type == TRANSACTIONS[:credit] ? :void_credit : :void_purchase
86
+ end
87
+
88
+ def credit_action(type)
89
+ type == TRANSACTIONS[:check_purchase] ? :check_credit : :credit
90
+ end
91
+
92
+ def split_auth(string)
93
+ string.split(";")
94
+ end
95
+
96
+ def add_amount(post, money)
97
+ post[:trnAmount] = amount(money)
98
+ end
99
+
100
+ def add_original_amount(post, amount)
101
+ post[:trnAmount] = amount
102
+ end
103
+
104
+ def add_reference(post, reference)
105
+ post[:adjId] = reference
106
+ end
107
+
108
+ def add_address(post, options)
109
+ if billing_address = options[:billing_address] || options[:address]
110
+ post[:ordName] = billing_address[:name]
111
+ post[:ordEmailAddress] = options[:email]
112
+ post[:ordPhoneNumber] = billing_address[:phone]
113
+ post[:ordAddress1] = billing_address[:address1]
114
+ post[:ordAddress2] = billing_address[:address2]
115
+ post[:ordCity] = billing_address[:city]
116
+ post[:ordProvince] = billing_address[:state]
117
+ post[:ordPostalCode] = billing_address[:zip]
118
+ post[:ordCountry] = billing_address[:country]
119
+ end
120
+ if shipping_address = options[:shipping_address]
121
+ post[:shipName] = shipping_address[:name]
122
+ post[:shipEmailAddress] = options[:email]
123
+ post[:shipPhoneNumber] = shipping_address[:phone]
124
+ post[:shipAddress1] = shipping_address[:address1]
125
+ post[:shipAddress2] = shipping_address[:address2]
126
+ post[:shipCity] = shipping_address[:city]
127
+ post[:shipProvince] = shipping_address[:state]
128
+ post[:shipPostalCode] = shipping_address[:zip]
129
+ post[:shipCountry] = shipping_address[:country]
130
+ post[:shippingMethod] = shipping_address[:shipping_method]
131
+ post[:deliveryEstimate] = shipping_address[:delivery_estimate]
132
+ end
133
+ end
134
+
135
+ def add_invoice(post, options)
136
+ post[:trnOrderNumber] = options[:order_id]
137
+ post[:trnComments] = options[:description]
138
+ post[:ordItemPrice] = amount(options[:subtotal])
139
+ post[:ordShippingPrice] = amount(options[:shipping])
140
+ post[:ordTax1Price] = amount(options[:tax1] || options[:tax])
141
+ post[:ordTax2Price] = amount(options[:tax2])
142
+ post[:ref1] = options[:custom]
143
+ end
144
+
145
+ def add_credit_card(post, credit_card)
146
+ post[:trnCardOwner] = credit_card.name
147
+ post[:trnCardNumber] = credit_card.number
148
+ post[:trnExpMonth] = format(credit_card.month, :two_digits)
149
+ post[:trnExpYear] = format(credit_card.year, :two_digits)
150
+ post[:trnCardCvd] = credit_card.verification_value
151
+ end
152
+
153
+ def add_check(post, check)
154
+ # The institution number of the consumer’s financial institution. Required for Canadian dollar EFT transactions.
155
+ post[:institutionNumber] = check.institution_number
156
+
157
+ # The bank transit number of the consumer’s bank account. Required for Canadian dollar EFT transactions.
158
+ post[:transitNumber] = check.transit_number
159
+
160
+ # The routing number of the consumer’s bank account. Required for US dollar EFT transactions.
161
+ post[:routingNumber] = check.routing_number
162
+
163
+ # The account number of the consumer’s bank account. Required for both Canadian and US dollar EFT transactions.
164
+ post[:accountNumber] = check.account_number
165
+ end
166
+
167
+ def parse(body)
168
+ results = {}
169
+ if !body.nil?
170
+ body.split(/&/).each do |pair|
171
+ key,val = pair.split(/=/)
172
+ results[key.to_sym] = val.nil? ? nil : CGI.unescape(val)
173
+ end
174
+ end
175
+
176
+ # Clean up the message text if there is any
177
+ if results[:messageText]
178
+ results[:messageText].gsub!(/<LI>/, "")
179
+ results[:messageText].gsub!(/(\.)?<br>/, ". ")
180
+ results[:messageText].strip!
181
+ end
182
+
183
+ results
184
+ end
185
+
186
+ def commit(params)
187
+ post(post_data(params))
188
+ end
189
+
190
+ def post(data)
191
+ response = parse(ssl_post(URL, data))
192
+ build_response(success?(response), message_from(response), response,
193
+ :test => test? || response[:authCode] == "TEST",
194
+ :authorization => authorization_from(response),
195
+ :cvv_result => CVD_CODES[response[:cvdId]],
196
+ :avs_result => { :code => (AVS_CODES.include? response[:avsId]) ? AVS_CODES[response[:avsId]] : response[:avsId] }
197
+ )
198
+ end
199
+
200
+ def authorization_from(response)
201
+ "#{response[:trnId]};#{response[:trnAmount]};#{response[:trnType]}"
202
+ end
203
+
204
+ def message_from(response)
205
+ response[:messageText]
206
+ end
207
+
208
+ def success?(response)
209
+ response[:responseType] == 'R' || response[:trnApproved] == '1'
210
+ end
211
+
212
+ def add_source(post, source)
213
+ source.type == "check" ? add_check(post, source) : add_credit_card(post, source)
214
+ end
215
+
216
+ def add_transaction_type(post, action)
217
+ post[:trnType] = TRANSACTIONS[action]
218
+ end
219
+
220
+ def post_data(params)
221
+ params[:requestType] = 'BACKEND'
222
+ params[:merchant_id] = @options[:login]
223
+ params[:username] = @options[:user] if @options[:user]
224
+ params[:password] = @options[:password] if @options[:password]
225
+ params[:vbvEnabled] = '0'
226
+ params[:scEnabled] = '0'
227
+
228
+ params.reject{|k, v| v.blank?}.collect { |key, value| "#{key}=#{CGI.escape(value.to_s)}" }.join("&")
229
+ end
230
+ end
231
+ end
232
+ end
233
+
@@ -0,0 +1,54 @@
1
+ require File.dirname(__FILE__) + '/beanstream/beanstream_core'
2
+
3
+ module ActiveMerchant #:nodoc:
4
+ module Billing #:nodoc:
5
+ class BeanstreamInteracResponse < Response
6
+ def redirect
7
+ params['pageContents']
8
+ end
9
+ end
10
+
11
+ class BeanstreamInteracGateway < Gateway
12
+ include BeanstreamCore
13
+
14
+ # Confirm a transaction posted back from the bank to Beanstream.
15
+ # Confirming a transaction does not require any credentials,
16
+ # and in an application with many merchants sharing a funded
17
+ # URL the application may not yet know which merchant the
18
+ # post back is for until the response of the confirmation is
19
+ # received, which contains the order number.
20
+ def self.confirm(transaction)
21
+ gateway = new(:login => '')
22
+ gateway.confirm(transaction)
23
+ end
24
+
25
+ def purchase(money, options = {})
26
+ post = {}
27
+ add_amount(post, money)
28
+ add_invoice(post, options)
29
+ add_address(post, options)
30
+ add_interac_details(post, options)
31
+ add_transaction_type(post, :purchase)
32
+ commit(post)
33
+ end
34
+
35
+ # Confirm a transaction posted back from the bank to Beanstream.
36
+ def confirm(transaction)
37
+ post(transaction)
38
+ end
39
+
40
+ private
41
+
42
+ def add_interac_details(post, options)
43
+ address = options[:billing_address] || options[:address] || {}
44
+ post[:trnCardOwner] = address[:name]
45
+ post[:paymentMethod] = 'IO'
46
+ end
47
+
48
+ def build_response(*args)
49
+ BeanstreamInteracResponse.new(*args)
50
+ end
51
+ end
52
+ end
53
+ end
54
+
@@ -6,7 +6,7 @@ module ActiveMerchant #:nodoc:
6
6
  URL = 'https://secure.braintreepaymentgateway.com/api/transact.php'
7
7
 
8
8
  self.supported_countries = ['US']
9
- self.supported_cardtypes = [:visa, :master, :american_express]
9
+ self.supported_cardtypes = [:visa, :master, :american_express, :discover]
10
10
  self.homepage_url = 'http://www.braintreepaymentsolutions.com'
11
11
  self.display_name = 'Braintree'
12
12
 
@@ -75,6 +75,15 @@ module ActiveMerchant #:nodoc:
75
75
  commit(nil, nil, post)
76
76
  end
77
77
 
78
+ # To match the other stored-value gateways, like TrustCommerce,
79
+ # store and unstore need to be defined
80
+ def store(creditcard, options = {})
81
+ billing_id = options.delete(:billing_id).to_s || true
82
+ authorize(100, creditcard, options.merge(:store => billing_id))
83
+ end
84
+
85
+ alias_method :unstore, :delete
86
+
78
87
  private
79
88
  def add_customer_data(post, options)
80
89
  if options.has_key? :email
@@ -126,7 +126,12 @@ module ActiveMerchant #:nodoc:
126
126
 
127
127
  def void(identification, options = {})
128
128
  commit(build_void_request(identification, options), options)
129
- end
129
+ end
130
+
131
+ def credit(money, identification, options = {})
132
+ commit(build_credit_request(money, identification, options), options)
133
+ end
134
+
130
135
 
131
136
  # CyberSource requires that you provide line item information for tax calculations
132
137
  # If you do not have prices for each item or want to simplify the situation then pass in one fake line item that costs the subtotal of the order
@@ -216,6 +221,17 @@ module ActiveMerchant #:nodoc:
216
221
  xml.target!
217
222
  end
218
223
 
224
+ def build_credit_request(money, identification, options)
225
+ order_id, request_id, request_token = identification.split(";")
226
+ options[:order_id] = order_id
227
+
228
+ xml = Builder::XmlMarkup.new :indent => 2
229
+ add_purchase_data(xml, money, true, options)
230
+ add_credit_service(xml, request_id, request_token)
231
+
232
+ xml.target!
233
+ end
234
+
219
235
  def add_business_rules_data(xml)
220
236
  xml.tag! 'businessRules' do
221
237
  xml.tag!('ignoreAVSResult', 'true') if @options[:ignore_avs]
@@ -303,6 +319,14 @@ module ActiveMerchant #:nodoc:
303
319
  xml.tag! 'voidRequestToken', request_token
304
320
  end
305
321
  end
322
+
323
+ def add_credit_service(xml, request_id, request_token)
324
+ xml.tag! 'ccCreditService', {'run' => 'true'} do
325
+ xml.tag! 'captureRequestID', request_id
326
+ xml.tag! 'captureRequestToken', request_token
327
+ end
328
+ end
329
+
306
330
 
307
331
  # Where we actually build the full SOAP request using builder
308
332
  def build_request(body, options)
@@ -318,7 +342,7 @@ module ActiveMerchant #:nodoc:
318
342
  end
319
343
  end
320
344
  xml.tag! 's:Body', {'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance', 'xmlns:xsd' => 'http://www.w3.org/2001/XMLSchema'} do
321
- xml.tag! 'requestMessage', {'xmlns' => 'urn:schemas-cybersource-com:transaction-data-1.26'} do
345
+ xml.tag! 'requestMessage', {'xmlns' => 'urn:schemas-cybersource-com:transaction-data-1.32'} do
322
346
  add_merchant_data(xml, options)
323
347
  xml << body
324
348
  end