spree_gateway 2.1.0 → 2.1.1

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