spree_gateway 2.0.0 → 2.0.1

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 (73) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +14 -0
  3. data/.rspec +1 -0
  4. data/.travis.yml +22 -0
  5. data/Gemfile +14 -0
  6. data/LICENSE +26 -0
  7. data/README.md +68 -0
  8. data/Rakefile +14 -0
  9. data/Versionfile +12 -0
  10. data/app/controllers/spree/checkout_controller_decorator.rb +51 -0
  11. data/app/controllers/spree/skrill_status_controller.rb +39 -0
  12. data/app/models/spree/billing_integration/skrill/quick_checkout.rb +52 -0
  13. data/app/models/spree/gateway/authorize_net.rb +18 -0
  14. data/app/models/spree/gateway/authorize_net_cim.rb +134 -0
  15. data/app/models/spree/gateway/balanced_gateway.rb +68 -0
  16. data/app/models/spree/gateway/banwire.rb +16 -0
  17. data/app/models/spree/gateway/beanstream.rb +195 -0
  18. data/app/models/spree/gateway/braintree_gateway.rb +143 -0
  19. data/app/models/spree/gateway/card_save.rb +12 -0
  20. data/app/models/spree/gateway/data_cash.rb +12 -0
  21. data/app/models/spree/gateway/eway.rb +22 -0
  22. data/app/models/spree/gateway/fatzebra.rb +17 -0
  23. data/app/models/spree/gateway/klarna.rb +0 -0
  24. data/app/models/spree/gateway/linkpoint.rb +30 -0
  25. data/app/models/spree/gateway/moneris.rb +13 -0
  26. data/app/models/spree/gateway/pay_junction.rb +18 -0
  27. data/app/models/spree/gateway/pay_pal_gateway.rb +14 -0
  28. data/app/models/spree/gateway/payflow_pro.rb +19 -0
  29. data/app/models/spree/gateway/paymill.rb +15 -0
  30. data/app/models/spree/gateway/pin_gateway.rb +18 -0
  31. data/app/models/spree/gateway/sage_pay.rb +13 -0
  32. data/app/models/spree/gateway/samurai.rb +65 -0
  33. data/app/models/spree/gateway/secure_pay_au.rb +10 -0
  34. data/app/models/spree/gateway/stripe_gateway.rb +94 -0
  35. data/app/models/spree/gateway/usa_epay.rb +11 -0
  36. data/app/models/spree/gateway/worldpay.rb +96 -0
  37. data/app/models/spree/payment_decorator.rb +3 -0
  38. data/app/models/spree/skrill_transaction.rb +21 -0
  39. data/app/views/spree/admin/payments/source_forms/_quickcheckout.html.erb +8 -0
  40. data/app/views/spree/admin/payments/source_views/_quickcheckout.html.erb +39 -0
  41. data/app/views/spree/checkout/payment/_quickcheckout.html.erb +26 -0
  42. data/config/locales/bg.yml +11 -0
  43. data/config/locales/de.yml +11 -0
  44. data/config/locales/en.yml +11 -0
  45. data/config/locales/sv.yml +11 -0
  46. data/config/routes.rb +13 -0
  47. data/db/migrate/20111118164631_create_skrill_transactions.rb +14 -0
  48. data/db/migrate/20121017004102_update_braintree_payment_method_type.rb +9 -0
  49. data/db/migrate/20130213222555_update_stripe_payment_method_type.rb +9 -0
  50. data/db/migrate/20130415222802_update_balanced_payment_method_type.rb +9 -0
  51. data/db/migrate/20131008221012_update_paypal_payment_method_type.rb +9 -0
  52. data/lib/active_merchant/billing/skrill.rb +18 -0
  53. data/lib/generators/spree_gateway/install/install_generator.rb +28 -0
  54. data/lib/spree_gateway.rb +3 -0
  55. data/lib/spree_gateway/engine.rb +41 -0
  56. data/script/rails +5 -0
  57. data/spec/factories/payment_method_factory.rb +4 -0
  58. data/spec/models/gateway/authorize_net_cim_spec.rb +17 -0
  59. data/spec/models/gateway/authorize_net_spec.rb +17 -0
  60. data/spec/models/gateway/balanced_gateway_spec.rb +9 -0
  61. data/spec/models/gateway/banwire_spec.rb +9 -0
  62. data/spec/models/gateway/braintree_gateway_spec.rb +284 -0
  63. data/spec/models/gateway/eway_spec.rb +17 -0
  64. data/spec/models/gateway/fatzebra_spec.rb +47 -0
  65. data/spec/models/gateway/linkpoint_spec.rb +60 -0
  66. data/spec/models/gateway/pay_junction_spec.rb +17 -0
  67. data/spec/models/gateway/payflow_pro_spec.rb +17 -0
  68. data/spec/models/gateway/pin_gateway_spec.rb +57 -0
  69. data/spec/models/gateway/stripe_gateway_spec.rb +122 -0
  70. data/spec/models/gateway/usa_epay_spec.rb +38 -0
  71. data/spec/spec_helper.rb +34 -0
  72. data/spree_gateway.gemspec +26 -0
  73. metadata +91 -5
@@ -0,0 +1,68 @@
1
+ module Spree
2
+ class Gateway::BalancedGateway < Gateway
3
+ preference :login, :string
4
+ preference :on_behalf_of_uri, :string
5
+
6
+ attr_accessible :preferred_login, :preferred_on_behalf_of_uri
7
+
8
+ def authorize(money, creditcard, gateway_options)
9
+ if token = creditcard.gateway_payment_profile_id
10
+ # The Balanced ActiveMerchant gateway supports passing the token directly as the creditcard parameter
11
+ creditcard = token
12
+ end
13
+ provider.authorize(money, creditcard, gateway_options)
14
+ end
15
+
16
+ def capture(authorization, creditcard, gateway_options)
17
+ gateway_options[:on_behalf_of_uri] = self.preferred_on_behalf_of_uri
18
+ provider.capture((authorization.amount * 100).round, authorization.response_code, gateway_options)
19
+ end
20
+
21
+ def create_profile(payment)
22
+ return unless payment.source.gateway_payment_profile_id.nil?
23
+
24
+ options = {}
25
+ options[:email] = payment.order.email
26
+ options[:login] = preferred_login
27
+
28
+ card_uri = provider.store(payment.source, options)
29
+
30
+ # A success just returns a string of the token. A failed request returns a bad request response with a message.
31
+ if card_uri.is_a?(String)
32
+ payment.source.update_attributes!(:gateway_payment_profile_id => card_uri)
33
+ else
34
+ payment.send(:gateway_error, card_uri.message)
35
+ end
36
+ end
37
+
38
+ def options_with_test_preference
39
+ options_without_test_preference.merge(:test => self.preferred_test_mode)
40
+ end
41
+ alias_method_chain :options, :test_preference
42
+
43
+ def payment_profiles_supported?
44
+ true
45
+ end
46
+
47
+ def purchase(money, creditcard, gateway_options)
48
+ if token = creditcard.gateway_payment_profile_id
49
+ # The Balanced ActiveMerchant gateway supports passing the token directly as the creditcard parameter
50
+ creditcard = token
51
+ end
52
+ provider.purchase(money, creditcard, gateway_options)
53
+ end
54
+
55
+ def provider_class
56
+ ActiveMerchant::Billing::BalancedGateway
57
+ end
58
+
59
+ def credit(money, creditcard, response_code, gateway_options)
60
+ provider.refund(money, response_code, {})
61
+ end
62
+
63
+ def void(response_code, creditcard, gateway_options)
64
+ provider.void(response_code)
65
+ end
66
+
67
+ end
68
+ end
@@ -0,0 +1,16 @@
1
+ module Spree
2
+ class Gateway::Banwire < Gateway
3
+ preference :login, :string
4
+
5
+ attr_accessible :preferred_login
6
+
7
+ def provider_class
8
+ ActiveMerchant::Billing::BanwireGateway
9
+ end
10
+
11
+ def purchase(money, creditcard, gateway_options)
12
+ gateway_options[:description] = "Spree Order"
13
+ provider.purchase(money, creditcard, gateway_options)
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,195 @@
1
+ module Spree
2
+ class Gateway::Beanstream < Gateway
3
+ preference :login, :string
4
+ preference :user, :string
5
+ preference :password, :string
6
+ preference :secure_profile_api_key, :string
7
+
8
+ attr_accessible :preferred_login, :preferred_user, :preferred_password, :preferred_secure_profile_api_key
9
+
10
+ def provider_class
11
+ ActiveMerchant::Billing::BeanstreamGateway
12
+ end
13
+
14
+ def payment_profiles_supported?
15
+ true
16
+ end
17
+
18
+ def create_profile(payment)
19
+ creditcard = payment.source
20
+ if creditcard.gateway_customer_profile_id.nil?
21
+ options = options_for_create_customer_profile(creditcard, {})
22
+ verify_creditcard_name!(creditcard)
23
+ result = provider.store(creditcard, options)
24
+ if result.success?
25
+ creditcard.update_attributes(:gateway_customer_profile_id => result.params['customerCode'], :gateway_payment_profile_id => result.params['customer_vault_id'])
26
+ else
27
+ payment.send(:gateway_error, result)
28
+ end
29
+ end
30
+ end
31
+
32
+ def capture(transaction, creditcard, gateway_options)
33
+ beanstream_gateway.capture((transaction.amount*100).round, transaction.response_code, gateway_options)
34
+ end
35
+
36
+ def void(transaction_response, creditcard, gateway_options)
37
+ beanstream_gateway.void(transaction_response, gateway_options)
38
+ end
39
+
40
+ def credit(amount, creditcard, response_code, gateway_options = {})
41
+ amount = (amount * -1) if amount < 0
42
+ beanstream_gateway.credit(amount, response_code, gateway_options)
43
+ end
44
+
45
+ private
46
+ def beanstream_gateway
47
+ ActiveMerchant::Billing::Base.gateway_mode = preferred_server.to_sym
48
+ gateway_options = options
49
+ ActiveMerchant::Billing::BeanstreamGateway.new(gateway_options)
50
+ end
51
+
52
+ def verify_creditcard_name!(creditcard)
53
+ bill_address = creditcard.payments.first.order.bill_address
54
+ creditcard.first_name = bill_address.firstname unless creditcard.first_name?
55
+ creditcard.last_name = bill_address.lastname unless creditcard.last_name?
56
+ end
57
+
58
+ def options_for_create_customer_profile(creditcard, gateway_options)
59
+ order = creditcard.payments.first.order
60
+ address = order.bill_address
61
+ { :email=>order.email,
62
+ :billing_address=>
63
+ { :name=>address.full_name,
64
+ :phone=>address.phone,
65
+ :address1=>address.address1,
66
+ :address2=>address.address2,
67
+ :city=>address.city,
68
+ :state=>address.state_name || address.state.abbr,
69
+ :country=>address.country.iso,
70
+ :zip=>address.zipcode
71
+ }
72
+ }.merge(gateway_options)
73
+ end
74
+
75
+ SECURE_PROFILE_URL = 'https://www.beanstream.com/scripts/payment_profile.asp'
76
+ SP_SERVICE_VERSION = '1.1'
77
+ PROFILE_OPERATIONS = { :new => 'N', :modify => 'M' }
78
+
79
+ ActiveMerchant::Billing::BeanstreamGateway.class_eval do
80
+
81
+ def store(credit_card, options = {})
82
+ post = {}
83
+ add_address(post, options)
84
+ add_credit_card(post, credit_card)
85
+ add_secure_profile_variables(post,options)
86
+ commit(post, true)
87
+ end
88
+
89
+ #can't actually delete a secure profile with the supplicaed API. This function sets the status of the profile to closed (C).
90
+ #Closed profiles will have to removed manually.
91
+ def delete(vault_id)
92
+ update(vault_id, false, { :status => 'C' })
93
+ end
94
+
95
+ #alias_method :unstore, :delete
96
+
97
+ # Update the values (such as CC expiration) stored at
98
+ # the gateway. The CC number must be supplied in the
99
+ # CreditCard object.
100
+ def update(vault_id, credit_card, options = {})
101
+ post = {}
102
+ add_address(post, options)
103
+ add_credit_card(post, credit_card)
104
+ options.merge!({ :vault_id => vault_id, :operation => secure_profile_action(:modify) })
105
+ add_secure_profile_variables(post,options)
106
+ commit(post, true)
107
+ end
108
+
109
+ # CORE #
110
+
111
+ def secure_profile_action(type)
112
+ PROFILE_OPERATIONS[type] || PROFILE_OPERATIONS[:new]
113
+ end
114
+
115
+ def add_credit_card(post, credit_card)
116
+ if credit_card
117
+ if credit_card.has_payment_profile?
118
+ post[:customerCode] = credit_card.gateway_customer_profile_id
119
+ else
120
+ post[:trnCardOwner] = credit_card.name
121
+ post[:trnCardNumber] = credit_card.number
122
+ post[:trnExpMonth] = format(credit_card.month, :two_digits)
123
+ post[:trnExpYear] = format(credit_card.year, :two_digits)
124
+ post[:trnCardCvd] = credit_card.verification_value
125
+ end
126
+ end
127
+ end
128
+
129
+ def add_secure_profile_variables(post, options = {})
130
+ post[:serviceVersion] = SP_SERVICE_VERSION
131
+ post[:responseFormat] = 'QS'
132
+ post[:cardValidation] = (options[:cardValidation].to_i == 1) || '0'
133
+
134
+ post[:operationType] = options[:operationType] || options[:operation] || secure_profile_action(:new)
135
+ post[:customerCode] = options[:billing_id] || options[:vault_id] || false
136
+ post[:status] = options[:status]
137
+ end
138
+
139
+ def commit(params, use_profile_api = false)
140
+ post(post_data(params,use_profile_api),use_profile_api)
141
+ end
142
+
143
+ def post(data, use_profile_api)
144
+ response = parse(ssl_post((use_profile_api ? SECURE_PROFILE_URL : ActiveMerchant::Billing::BeanstreamGateway::URL), data))
145
+ if response[:responseCode].eql?("17") # Found matching card
146
+ response[:responseCode] = "1"
147
+ response[:customerCode] = response[:matchedCustomerCode]
148
+ end
149
+ response[:customer_vault_id] = response[:customerCode] if response[:customerCode]
150
+ build_response(success?(response), message_from(response), response,
151
+ :test => test? || response[:authCode] == 'TEST',
152
+ :authorization => authorization_from(response),
153
+ :cvv_result => ActiveMerchant::Billing::BeanstreamGateway::CVD_CODES[response[:cvdId]],
154
+ :avs_result => { :code => (ActiveMerchant::Billing::BeanstreamGateway::AVS_CODES.include? response[:avsId]) ? ActiveMerchant::Billing::BeanstreamGateway::AVS_CODES[response[:avsId]] : response[:avsId] }
155
+ )
156
+ end
157
+
158
+ def message_from(response)
159
+ response[:messageText] || response[:responseMessage]
160
+ end
161
+
162
+ def success?(response)
163
+ response[:responseType] == 'R' || response[:trnApproved] == '1' || response[:responseCode] == '1'
164
+ end
165
+
166
+ def add_source(post, source)
167
+ if source.is_a?(String) or source.is_a?(Integer)
168
+ post[:customerCode] = source
169
+ else
170
+ if source.respond_to?(:type) && source.type.to_s == 'check'
171
+ add_check(post, source)
172
+ else
173
+ add_credit_card(post, source)
174
+ end
175
+ end
176
+ end
177
+
178
+ def post_data(params, use_profile_api)
179
+ params[:requestType] = 'BACKEND'
180
+ if use_profile_api
181
+ params[:merchantId] = @options[:login]
182
+ params[:passCode] = @options[:secure_profile_api_key]
183
+ else
184
+ params[:username] = @options[:user] if @options[:user]
185
+ params[:password] = @options[:password] if @options[:password]
186
+ params[:merchant_id] = @options[:login]
187
+ end
188
+ params[:vbvEnabled] = '0'
189
+ params[:scEnabled] = '0'
190
+
191
+ params.reject { |k, v| v.blank? }.collect { |key, value| "#{key}=#{CGI.escape(value.to_s)}" }.join('&')
192
+ end
193
+ end
194
+ end
195
+ end
@@ -0,0 +1,143 @@
1
+ module Spree
2
+ class Gateway::BraintreeGateway < Gateway
3
+ preference :environment, :string
4
+ preference :merchant_id, :string
5
+ preference :merchant_account_id, :string
6
+ preference :public_key, :string
7
+ preference :private_key, :string
8
+ preference :client_side_encryption_key, :text
9
+
10
+ attr_accessible :preferred_merchant_id, :preferred_public_key, :preferred_private_key,
11
+ :preferred_client_side_encryption_key, :preferred_environment, :preferred_merchant_account_id
12
+
13
+ CARD_TYPE_MAPPING = {
14
+ 'American Express' => 'american_express',
15
+ 'Diners Club' => 'diners_club',
16
+ 'Discover' => 'discover',
17
+ 'JCB' => 'jcb',
18
+ 'Laser' => 'laser',
19
+ 'Maestro' => 'maestro',
20
+ 'MasterCard' => 'master',
21
+ 'Solo' => 'solo',
22
+ 'Switch' => 'switch',
23
+ 'Visa' => 'visa'
24
+ }
25
+
26
+ def provider
27
+ provider_instance = super
28
+ Braintree::Configuration.custom_user_agent = "Spree #{Spree.version}"
29
+ Braintree::Configuration.environment = preferred_environment.to_sym
30
+ Braintree::Configuration.merchant_id = preferred_merchant_id
31
+ Braintree::Configuration.public_key = preferred_public_key
32
+ Braintree::Configuration.private_key = preferred_private_key
33
+
34
+ provider_instance
35
+ end
36
+
37
+ def provider_class
38
+ ActiveMerchant::Billing::BraintreeBlueGateway
39
+ end
40
+
41
+ def authorize(money, creditcard, options = {})
42
+ adjust_options_for_braintree(creditcard, options)
43
+ payment_method = creditcard.gateway_customer_profile_id || creditcard
44
+ provider.authorize(money, payment_method, options)
45
+ end
46
+
47
+ def capture(authorization, ignored_creditcard, ignored_options)
48
+ amount = (authorization.amount * 100).to_i
49
+ provider.capture(amount, authorization.response_code)
50
+ end
51
+
52
+ def create_profile(payment)
53
+ if payment.source.gateway_customer_profile_id.nil?
54
+ response = provider.store(payment.source)
55
+ if response.success?
56
+ payment.source.update_attributes!(:gateway_customer_profile_id => response.params['customer_vault_id'])
57
+ cc = response.params['braintree_customer'].fetch('credit_cards',[]).first
58
+ update_card_number(payment.source, cc) if cc
59
+ else
60
+ payment.send(:gateway_error, response.message)
61
+ end
62
+ end
63
+ end
64
+
65
+ def update_card_number(source, cc)
66
+ last_4 = cc['last_4']
67
+ source.last_digits = last_4 if last_4
68
+ source.gateway_payment_profile_id = cc['token']
69
+ source.cc_type = CARD_TYPE_MAPPING[cc['card_type']] if cc['card_type']
70
+ source.save!
71
+ end
72
+
73
+ def credit(*args)
74
+ if args.size == 4
75
+ # enables ability to refund instead of credit
76
+ args.slice!(1,1)
77
+ credit_without_payment_profiles(*args)
78
+ elsif args.size == 3
79
+ credit_without_payment_profiles(*args)
80
+ else
81
+ raise ArgumentError, "Expected 3 or 4 arguments, received #{args.size}"
82
+ end
83
+ end
84
+
85
+ # Braintree now disables credits by default, see https://www.braintreepayments.com/docs/ruby/transactions/credit
86
+ def credit_with_payment_profiles(amount, payment, response_code, option)
87
+ provider.credit(amount, payment)
88
+ end
89
+
90
+ def credit_without_payment_profiles(amount, response_code, options)
91
+ provider # braintree provider needs to be called here to properly configure braintree gem.
92
+ transaction = ::Braintree::Transaction.find(response_code)
93
+ if BigDecimal.new(amount.to_s) == (transaction.amount * 100)
94
+ provider.refund(response_code)
95
+ elsif BigDecimal.new(amount.to_s) < (transaction.amount * 100) # support partial refunds
96
+ provider.refund(amount, response_code)
97
+ else
98
+ raise NotImplementedError
99
+ end
100
+ end
101
+
102
+ def payment_profiles_supported?
103
+ true
104
+ end
105
+
106
+ def purchase(money, creditcard, options = {})
107
+ authorize(money, creditcard, options.merge(:submit_for_settlement => true))
108
+ end
109
+
110
+ def void(response_code, *ignored_options)
111
+ provider.void(response_code)
112
+ end
113
+
114
+ def preferences
115
+ preferences = super.slice(:merchant_id,
116
+ :merchant_account_id,
117
+ :public_key,
118
+ :private_key,
119
+ :client_side_encryption_key,
120
+ :environment)
121
+
122
+ # Must be either :production or :sandbox, not their string equivalents.
123
+ # Thanks to the Braintree gem.
124
+ preferences[:environment] = preferences[:environment].try(:to_sym) || :sandbox
125
+ preferences
126
+ end
127
+
128
+ protected
129
+
130
+ def adjust_billing_address(creditcard, options)
131
+ if creditcard.gateway_customer_profile_id
132
+ options.delete(:billing_address)
133
+ end
134
+ end
135
+
136
+ def adjust_options_for_braintree(creditcard, options)
137
+ if preferred_merchant_account_id
138
+ options['merchant_account_id'] = preferred_merchant_account_id
139
+ end
140
+ adjust_billing_address(creditcard, options)
141
+ end
142
+ end
143
+ end
@@ -0,0 +1,12 @@
1
+ module Spree
2
+ class Gateway::CardSave < Gateway
3
+ preference :login, :string
4
+ preference :password, :string
5
+
6
+ attr_accessible :preferred_login, :preferred_password
7
+
8
+ def provider_class
9
+ ActiveMerchant::Billing::CardSaveGateway
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ module Spree
2
+ class Gateway::DataCash < Gateway
3
+ preference :login, :string
4
+ preference :password, :string
5
+
6
+ attr_accessible :preferred_login, :preferred_password
7
+
8
+ def provider_class
9
+ ActiveMerchant::Billing::DataCashGateway
10
+ end
11
+ end
12
+ end