spree_core 0.40.4 → 0.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 (251) hide show
  1. data/README.md +6 -1
  2. data/app/controllers/admin/base_controller.rb +0 -57
  3. data/app/controllers/admin/configurations_controller.rb +1 -1
  4. data/app/controllers/admin/general_settings_controller.rb +1 -1
  5. data/app/controllers/admin/images_controller.rb +18 -8
  6. data/app/controllers/admin/inventory_settings_controller.rb +1 -1
  7. data/app/controllers/admin/mail_settings_controller.rb +1 -1
  8. data/app/controllers/admin/option_types_controller.rb +11 -0
  9. data/app/controllers/admin/orders_controller.rb +16 -20
  10. data/app/controllers/admin/overview_controller.rb +1 -1
  11. data/app/controllers/admin/payment_methods_controller.rb +6 -6
  12. data/app/controllers/admin/product_groups_controller.rb +10 -11
  13. data/app/controllers/admin/product_properties_controller.rb +5 -5
  14. data/app/controllers/admin/product_scopes_controller.rb +12 -4
  15. data/app/controllers/admin/products_controller.rb +17 -12
  16. data/app/controllers/admin/properties_controller.rb +7 -7
  17. data/app/controllers/admin/prototypes_controller.rb +15 -15
  18. data/app/controllers/admin/reports_controller.rb +12 -11
  19. data/app/controllers/admin/shipments_controller.rb +0 -5
  20. data/app/controllers/admin/shipping_categories_controller.rb +6 -6
  21. data/app/controllers/admin/shipping_methods_controller.rb +1 -2
  22. data/app/controllers/admin/states_controller.rb +12 -12
  23. data/app/controllers/admin/tax_categories_controller.rb +2 -2
  24. data/app/controllers/admin/tax_rates_controller.rb +8 -8
  25. data/app/controllers/admin/tax_settings_controller.rb +1 -1
  26. data/app/controllers/admin/taxonomies_controller.rb +3 -3
  27. data/app/controllers/admin/taxons_controller.rb +1 -8
  28. data/app/controllers/admin/trackers_controller.rb +1 -1
  29. data/app/controllers/admin/users_controller.rb +6 -9
  30. data/app/controllers/admin/variants_controller.rb +13 -2
  31. data/app/controllers/admin/zones_controller.rb +9 -9
  32. data/app/controllers/checkout_controller.rb +5 -5
  33. data/app/controllers/products_controller.rb +1 -1
  34. data/app/controllers/taxons_controller.rb +3 -1
  35. data/app/helpers/admin/navigation_helper.rb +36 -19
  36. data/app/helpers/admin/payments_helper.rb +1 -1
  37. data/app/helpers/admin/product_groups_helper.rb +1 -1
  38. data/app/helpers/admin/product_properties_helper.rb +5 -5
  39. data/app/helpers/admin/products_helper.rb +2 -2
  40. data/app/helpers/admin/users_helper.rb +5 -2
  41. data/app/helpers/hook_helper.rb +3 -3
  42. data/app/helpers/products_helper.rb +0 -19
  43. data/app/helpers/spree/base_helper.rb +33 -2
  44. data/app/helpers/taxons_helper.rb +3 -3
  45. data/app/mailers/order_mailer.rb +2 -2
  46. data/app/mailers/shipment_mailer.rb +2 -2
  47. data/app/models/address.rb +42 -9
  48. data/app/models/adjustment.rb +8 -8
  49. data/app/models/app_configuration.rb +0 -6
  50. data/app/models/billing_integration.rb +1 -1
  51. data/app/models/calculator/sales_tax.rb +3 -3
  52. data/app/models/configuration.rb +1 -1
  53. data/app/models/country.rb +4 -5
  54. data/app/models/creditcard.rb +38 -31
  55. data/app/models/gateway.rb +14 -14
  56. data/app/models/gateway/beanstream.rb +4 -4
  57. data/app/models/gateway/bogus.rb +6 -6
  58. data/app/models/gateway/braintree.rb +88 -0
  59. data/app/models/gateway/eway.rb +3 -3
  60. data/app/models/image.rb +5 -4
  61. data/app/models/option_type.rb +1 -0
  62. data/app/models/order.rb +60 -21
  63. data/app/models/payment.rb +5 -32
  64. data/app/models/preference.rb +7 -7
  65. data/app/models/product.rb +22 -7
  66. data/app/models/product_group.rb +22 -26
  67. data/app/models/product_property.rb +5 -5
  68. data/app/models/product_scope.rb +26 -6
  69. data/app/models/property.rb +1 -1
  70. data/app/models/state.rb +2 -3
  71. data/app/models/tax_category.rb +1 -0
  72. data/app/models/tax_rate.rb +1 -2
  73. data/app/models/taxon.rb +12 -10
  74. data/app/models/taxonomy.rb +7 -4
  75. data/app/models/tracker.rb +1 -1
  76. data/app/models/user.rb +4 -0
  77. data/app/models/variant.rb +1 -1
  78. data/app/models/zone.rb +1 -1
  79. data/app/models/zone_member.rb +3 -3
  80. data/app/views/admin/{shared → adjustments}/_adjustments_table.html.erb +7 -4
  81. data/app/views/admin/adjustments/edit.html.erb +1 -1
  82. data/app/views/admin/adjustments/index.html.erb +2 -2
  83. data/app/views/admin/adjustments/new.html.erb +2 -1
  84. data/app/views/admin/general_settings/edit.html.erb +4 -12
  85. data/app/views/admin/general_settings/show.html.erb +0 -5
  86. data/app/views/admin/images/index.html.erb +8 -5
  87. data/app/views/admin/inventory_settings/show.html.erb +1 -1
  88. data/app/views/admin/mail_methods/index.html.erb +4 -4
  89. data/app/views/admin/option_types/_form.html.erb +4 -4
  90. data/app/views/admin/option_types/_option_value_fields.html.erb +2 -2
  91. data/app/views/admin/option_types/edit.html.erb +4 -2
  92. data/app/views/admin/option_types/index.html.erb +5 -5
  93. data/app/views/admin/orders/_line_item.html.erb +2 -1
  94. data/app/views/admin/orders/history.html.erb +6 -2
  95. data/app/views/admin/orders/index.html.erb +22 -19
  96. data/app/views/admin/orders/show.html.erb +1 -1
  97. data/app/views/admin/orders/user.html.erb +1 -1
  98. data/app/views/admin/payment_methods/index.html.erb +7 -5
  99. data/app/views/admin/payments/_list.html.erb +3 -3
  100. data/app/views/admin/payments/index.html.erb +1 -1
  101. data/app/views/admin/payments/show.html.erb +2 -2
  102. data/app/views/admin/product_groups/edit.html.erb +7 -7
  103. data/app/views/admin/product_groups/index.html.erb +5 -3
  104. data/app/views/admin/product_groups/update.js.erb +4 -3
  105. data/app/views/admin/product_properties/_product_property_fields.html.erb +3 -3
  106. data/app/views/admin/product_properties/index.html.erb +10 -5
  107. data/app/views/admin/product_scopes/destroy.js.erb +1 -0
  108. data/app/views/admin/products/index.html.erb +32 -33
  109. data/app/views/admin/properties/_form.html.erb +2 -2
  110. data/app/views/admin/properties/index.html.erb +4 -4
  111. data/app/views/admin/prototypes/index.html.erb +4 -4
  112. data/app/views/admin/shared/_address_form.html.erb +1 -1
  113. data/app/views/admin/shared/_calculator_fields.html.erb +1 -1
  114. data/app/views/admin/shared/_destroy.js.erb +15 -2
  115. data/app/views/admin/shared/_order_tabs.html.erb +1 -1
  116. data/app/views/admin/shared/_report_criteria.html.erb +1 -1
  117. data/app/views/admin/shipments/_form.html.erb +6 -2
  118. data/app/views/admin/shipments/edit.html.erb +1 -1
  119. data/app/views/admin/shipments/index.html.erb +4 -2
  120. data/app/views/admin/shipping_methods/_form.html.erb +2 -0
  121. data/app/views/admin/shipping_methods/index.html.erb +3 -2
  122. data/app/views/admin/states/_state_list.html.erb +11 -5
  123. data/app/views/admin/tax_categories/index.html.erb +9 -4
  124. data/app/views/admin/tax_settings/show.html.erb +2 -2
  125. data/app/views/admin/taxonomies/_list.html.erb +4 -2
  126. data/app/views/admin/taxonomies/index.html.erb +2 -2
  127. data/app/views/admin/taxons/_form.html.erb +1 -1
  128. data/app/views/admin/trackers/index.html.erb +5 -5
  129. data/app/views/admin/users/_form.html.erb +3 -4
  130. data/app/views/admin/users/index.html.erb +7 -6
  131. data/app/views/admin/users/show.html.erb +3 -3
  132. data/app/views/admin/variants/index.html.erb +21 -6
  133. data/app/views/admin/zones/_form.html.erb +9 -9
  134. data/app/views/admin/zones/_member_type.html.erb +5 -5
  135. data/app/views/admin/zones/index.html.erb +7 -5
  136. data/app/views/checkout/_address.html.erb +2 -2
  137. data/app/views/checkout/_payment.html.erb +3 -6
  138. data/app/views/layouts/admin.html.erb +3 -9
  139. data/app/views/layouts/spree_application.html.erb +2 -1
  140. data/app/views/orders/_line_item.html.erb +1 -1
  141. data/app/views/orders/edit.html.erb +17 -16
  142. data/app/views/orders/show.html.erb +1 -1
  143. data/app/views/shared/_admin_head.html.erb +1 -1
  144. data/app/views/shared/_error_messages.html.erb +2 -2
  145. data/app/views/shared/_filters.html.erb +4 -4
  146. data/app/views/shared/_head.html.erb +2 -2
  147. data/app/views/shared/_nav_bar.html.erb +2 -2
  148. data/app/views/shared/_products.html.erb +4 -2
  149. data/app/views/shared/_taxonomies.html.erb +15 -8
  150. data/app/views/shipment_mailer/shipped_email.text.erb +2 -2
  151. data/config/cucumber.yml +10 -0
  152. data/config/initializers/form_builder.rb +1 -5
  153. data/config/initializers/workarounds_for_ruby19.rb +5 -5
  154. data/config/locales/en.yml +33 -6
  155. data/config/routes.rb +18 -13
  156. data/db/migrate/20090923100315_add_count_on_hand_to_variants_and_products.rb +5 -5
  157. data/db/migrate/20091213222815_creditcard_last_four_digits.rb +5 -5
  158. data/db/migrate/20100105132138_shipment_id_for_inventory_units.rb +2 -2
  159. data/db/migrate/20100209025806_create_payment_methods.rb +3 -3
  160. data/db/migrate/20100209144531_polymorphic_payments.rb +1 -1
  161. data/db/migrate/20100214212536_assign_creditcard_txns_to_payment.rb +2 -2
  162. data/db/migrate/20100224153127_deleted_at_for_payment_methods.rb +1 -1
  163. data/db/migrate/20100506185838_add_description_to_taxons.rb +1 -1
  164. data/db/migrate/20100816212146_shipping_method_id_for_orders.rb +1 -1
  165. data/db/migrate/20101026184808_migrate_checkout_to_orders.rb +2 -2
  166. data/db/migrate/20101223215658_add_position_to_variants.rb +9 -0
  167. data/db/migrate/20110110130847_add_next_state_to_state_events.rb +9 -0
  168. data/db/migrate/20110111122537_add_position_to_option_types.rb +9 -0
  169. data/db/migrate/20110314192118_remove_trailing_slashes_in_taxon_permalinks.rb +17 -0
  170. data/lib/custom_fixtures.rb +1 -1
  171. data/lib/{seo_assist.rb → middleware/seo_assist.rb} +14 -8
  172. data/lib/product_filters.rb +49 -43
  173. data/lib/redirect_legacy_product_url.rb +5 -5
  174. data/lib/scopes.rb +2 -2
  175. data/lib/scopes/dynamic.rb +9 -16
  176. data/lib/scopes/product.rb +33 -16
  177. data/lib/scopes/variant.rb +4 -3
  178. data/lib/spree/calculated_adjustments.rb +5 -2
  179. data/lib/spree/config.rb +2 -0
  180. data/lib/spree/current_order.rb +4 -4
  181. data/lib/spree/mail_settings.rb +3 -2
  182. data/lib/spree/search/base.rb +9 -10
  183. data/lib/spree_base.rb +22 -23
  184. data/lib/spree_core.rb +10 -69
  185. data/lib/spree_core/authorize_net_cim_hack.rb +1 -1
  186. data/lib/spree_core/delegate_belongs_to.rb +18 -24
  187. data/lib/spree_core/enumerable_constants.rb +38 -38
  188. data/lib/spree_core/find_by_param.rb +8 -6
  189. data/lib/spree_core/preferences/preference_definition.rb +7 -7
  190. data/lib/spree_core/railtie.rb +58 -0
  191. data/lib/spree_core/ssl_requirement.rb +4 -3
  192. data/lib/spree_core/testing_support/factories.rb +13 -0
  193. data/lib/spree_core/testing_support/factories/address_factory.rb +20 -0
  194. data/lib/spree_core/testing_support/factories/adjustment_factory.rb +6 -0
  195. data/lib/spree_core/testing_support/factories/calculator_factory.rb +5 -0
  196. data/lib/spree_core/testing_support/factories/configuraion_factory.rb +4 -0
  197. data/lib/spree_core/testing_support/factories/country_factory.rb +7 -0
  198. data/lib/spree_core/testing_support/factories/creditcard_factory.rb +11 -0
  199. data/lib/spree_core/testing_support/factories/inventory_unit_factory.rb +7 -0
  200. data/lib/spree_core/testing_support/factories/line_item_factory.rb +8 -0
  201. data/lib/spree_core/testing_support/factories/mail_method_factory.rb +4 -0
  202. data/lib/spree_core/testing_support/factories/options_factory.rb +10 -0
  203. data/lib/spree_core/testing_support/factories/order_factory.rb +18 -0
  204. data/lib/spree_core/testing_support/factories/payment_factory.rb +26 -0
  205. data/lib/spree_core/testing_support/factories/payment_method_factory.rb +17 -0
  206. data/lib/spree_core/testing_support/factories/product_factory.rb +16 -0
  207. data/lib/spree_core/testing_support/factories/product_group_factory.rb +3 -0
  208. data/lib/spree_core/testing_support/factories/product_option_type_factory.rb +4 -0
  209. data/lib/spree_core/testing_support/factories/product_property_factory.rb +4 -0
  210. data/lib/spree_core/testing_support/factories/product_scope_factory.rb +6 -0
  211. data/lib/spree_core/testing_support/factories/property_factory.rb +4 -0
  212. data/lib/spree_core/testing_support/factories/prototype_factory.rb +4 -0
  213. data/lib/spree_core/testing_support/factories/return_authorization_factory.rb +8 -0
  214. data/lib/spree_core/testing_support/factories/role_factory.rb +9 -0
  215. data/lib/spree_core/testing_support/factories/shipment_factory.rb +9 -0
  216. data/lib/spree_core/testing_support/factories/shipping_category_factory.rb +5 -0
  217. data/lib/spree_core/testing_support/factories/shipping_method_factory.rb +7 -0
  218. data/lib/spree_core/testing_support/factories/state_factory.rb +11 -0
  219. data/lib/spree_core/testing_support/factories/tax_category_factory.rb +8 -0
  220. data/lib/spree_core/testing_support/factories/tax_rate_factory.rb +5 -0
  221. data/lib/spree_core/testing_support/factories/taxon_factory.rb +5 -0
  222. data/lib/spree_core/testing_support/factories/taxonomy_factory.rb +3 -0
  223. data/lib/spree_core/testing_support/factories/tracker_factory.rb +5 -0
  224. data/lib/spree_core/testing_support/factories/user_factory.rb +15 -0
  225. data/lib/spree_core/testing_support/factories/variant_factory.rb +14 -0
  226. data/lib/spree_core/testing_support/factories/zone_factory.rb +18 -0
  227. data/lib/spree_core/theme_support/hook.rb +1 -1
  228. data/lib/spree_core/theme_support/more_patches.rb +20 -20
  229. data/lib/spree_core/version.rb +5 -0
  230. data/lib/tasks/common.rb +30 -0
  231. data/lib/tasks/install.rake +1 -1
  232. data/lib/tasks/rake_util.rb +19 -0
  233. data/lib/tasks/taxon.rake +14 -0
  234. data/public/images/reorder.jpg +0 -0
  235. data/public/javascripts/admin.js +0 -6
  236. data/public/javascripts/admin/unobtrusive_handlers.js +28 -0
  237. data/public/javascripts/checkout.js +3 -3
  238. data/public/stylesheets/admin/admin-forms.css +1 -6
  239. data/public/stylesheets/admin/admin.css +0 -28
  240. data/public/stylesheets/screen.css +0 -280
  241. metadata +81 -43
  242. data/app/controllers/countries_controller.rb +0 -11
  243. data/app/models/spree/alert.rb +0 -13
  244. data/app/models/state_monitor.rb +0 -25
  245. data/app/views/admin/shared/_alert.html.erb +0 -6
  246. data/app/views/countries/index.js.erb +0 -1
  247. data/app/views/shared/_doc_and_xmlns.html.erb +0 -2
  248. data/app/views/users/edit.html.erb +0 -9
  249. data/app/views/users/show.html.erb +0 -46
  250. data/lib/spree_core/validation_group.rb +0 -143
  251. data/public/stylesheets/scaffold.css +0 -54
@@ -2,8 +2,6 @@ class AppConfiguration < Configuration
2
2
 
3
3
  preference :site_name, :string, :default => 'Spree Demo Site'
4
4
  preference :site_url, :string, :default => 'demo.spreecommerce.com'
5
- preference :store_cc, :boolean, :default => false
6
- preference :store_cvv, :boolean, :default => false
7
5
  preference :default_locale, :string, :default => 'en'
8
6
  preference :allow_locale_switching, :boolean, :default => true
9
7
  preference :default_country_id, :integer, :default => 214
@@ -23,7 +21,6 @@ class AppConfiguration < Configuration
23
21
  preference :allow_ssl_in_production, :boolean, :default => true
24
22
  preference :allow_ssl_in_development_and_test, :boolean, :default => false
25
23
  preference :allow_guest_checkout, :boolean, :default => true
26
- preference :allow_anonymous_checkout, :boolean, :default => false
27
24
  preference :alternative_billing_phone, :boolean, :default => false # Request extra phone for bill addr
28
25
  preference :alternative_shipping_phone, :boolean, :default => false # Request extra phone for ship addr
29
26
  preference :shipping_instructions, :boolean, :default => false # Request instructions/info for shipping
@@ -35,9 +32,6 @@ class AppConfiguration < Configuration
35
32
  preference :cache_static_content, :boolean, :default => true
36
33
  preference :use_content_controller, :boolean, :default => true
37
34
  preference :allow_checkout_on_gateway_error, :boolean, :default => false
38
- preference :check_for_spree_alerts, :boolean, :default => true
39
- preference :dismissed_spree_alerts, :string, :default => ''
40
- preference :last_check_for_spree_alerts, :string, :default => nil
41
35
 
42
36
  validates :name, :presence => true, :uniqueness => true
43
37
 
@@ -7,7 +7,7 @@ class BillingIntegration < PaymentMethod
7
7
 
8
8
  def provider
9
9
  integration_options = options
10
- ActiveMerchant::Billing::Base.integration_mode = integration_options[:server]
10
+ ActiveMerchant::Billing::Base.integration_mode = integration_options[:server].to_sym
11
11
  integration_options = options
12
12
  integration_options[:test] = true if integration_options[:test_mode]
13
13
  @provider ||= provider_class.new(integration_options)
@@ -3,12 +3,12 @@ class Calculator::SalesTax < Calculator
3
3
  def self.description
4
4
  I18n.t("sales_tax")
5
5
  end
6
-
6
+
7
7
  def self.register
8
8
  super
9
9
  TaxRate.register_calculator(self)
10
10
  end
11
-
11
+
12
12
  def self.calculate_tax(order, rates)
13
13
  ActiveSupport::Deprecation.warn("please use Calculator::SalesTax#compute instead", caller)
14
14
 
@@ -16,7 +16,7 @@ class Calculator::SalesTax < Calculator
16
16
  # note: there is a bug with associations in rails 2.1 model caching so we're using this hack
17
17
  # (see http://rails.lighthouseapp.com/projects/8994/tickets/785-caching-models-fails-in-development)
18
18
  cache_hack = rates.first.respond_to?(:tax_category_id)
19
-
19
+
20
20
  taxable_totals = {}
21
21
  order.line_items.each do |line_item|
22
22
  next unless tax_category = line_item.variant.product.tax_category
@@ -1,3 +1,3 @@
1
1
  class Configuration < ActiveRecord::Base
2
-
2
+
3
3
  end
@@ -1,12 +1,11 @@
1
1
  class Country < ActiveRecord::Base
2
2
  has_many :states
3
-
4
- has_one :zone_member, :as => :zoneable
5
- has_one :zone, :through => :zone_member
6
3
 
7
- scope :order_by_name, :order => :name
4
+ has_one :zone_member, :as => :zoneable
5
+ has_one :zone, :through => :zone_member
6
+
8
7
  validates :name, :iso_name, :presence => true
9
-
8
+
10
9
  def <=>(other)
11
10
  name <=> other.name
12
11
  end
@@ -10,12 +10,10 @@ class Creditcard < ActiveRecord::Base
10
10
  validates :verification_value, :presence => true, :unless => :has_payment_profile?, :on => :create
11
11
 
12
12
  def process!(payment)
13
- begin
14
- if Spree::Config[:auto_capture]
15
- purchase(payment.amount.to_f, payment)
16
- else
17
- authorize(payment.amount.to_f, payment)
18
- end
13
+ if Spree::Config[:auto_capture]
14
+ purchase(payment.amount.to_f, payment)
15
+ else
16
+ authorize(payment.amount.to_f, payment)
19
17
  end
20
18
  end
21
19
 
@@ -30,19 +28,19 @@ class Creditcard < ActiveRecord::Base
30
28
  end
31
29
 
32
30
  def first_name?
33
- !self.first_name.blank?
31
+ first_name.present?
34
32
  end
35
33
 
36
34
  def last_name?
37
- !self.last_name.blank?
35
+ last_name.present?
38
36
  end
39
37
 
40
38
  def name
41
- "#{self.first_name} #{self.last_name}"
39
+ "#{first_name} #{last_name}"
42
40
  end
43
41
 
44
42
  def verification_value?
45
- !verification_value.blank?
43
+ verification_value.present?
46
44
  end
47
45
 
48
46
  # Show the card number, with all but last 4 numbers replace with "X". (XXXX-XXXX-XXXX-4338)
@@ -63,6 +61,9 @@ class Creditcard < ActiveRecord::Base
63
61
 
64
62
  def authorize(amount, payment)
65
63
  # ActiveMerchant is configured to use cents so we need to multiply order total by 100
64
+ payment_gateway = payment.payment_method
65
+ check_environment(payment_gateway)
66
+
66
67
  response = payment_gateway.authorize((amount * 100).round, self, gateway_options(payment))
67
68
  record_log payment, response
68
69
 
@@ -74,12 +75,15 @@ class Creditcard < ActiveRecord::Base
74
75
  payment.fail
75
76
  gateway_error(response)
76
77
  end
77
- rescue ActiveMerchant::ConnectionError => e
78
+ rescue ActiveMerchant::ConnectionError
78
79
  gateway_error I18n.t(:unable_to_connect_to_gateway)
79
80
  end
80
81
 
81
82
  def purchase(amount, payment)
82
83
  #combined Authorize and Capture that gets processed by the ActiveMerchant gateway as one single transaction.
84
+ payment_gateway = payment.payment_method
85
+ check_environment(payment_gateway)
86
+
83
87
  response = payment_gateway.purchase((amount * 100).round, self, gateway_options(payment))
84
88
  record_log payment, response
85
89
 
@@ -91,12 +95,15 @@ class Creditcard < ActiveRecord::Base
91
95
  payment.fail
92
96
  gateway_error(response) unless response.success?
93
97
  end
94
- rescue ActiveMerchant::ConnectionError => e
98
+ rescue ActiveMerchant::ConnectionError
95
99
  gateway_error t(:unable_to_connect_to_gateway)
96
100
  end
97
101
 
98
102
  def capture(payment)
99
103
  return unless payment.pending?
104
+ payment_gateway = payment.payment_method
105
+ check_environment(payment_gateway)
106
+
100
107
  if payment_gateway.payment_profiles_supported?
101
108
  # Gateways supporting payment profiles will need access to creditcard object because this stores the payment profile information
102
109
  # so supply the authorization itself as well as the creditcard, rather than just the authorization code
@@ -115,11 +122,14 @@ class Creditcard < ActiveRecord::Base
115
122
  payment.fail
116
123
  gateway_error(response)
117
124
  end
118
- rescue ActiveMerchant::ConnectionError => e
125
+ rescue ActiveMerchant::ConnectionError
119
126
  gateway_error I18n.t(:unable_to_connect_to_gateway)
120
127
  end
121
128
 
122
129
  def void(payment)
130
+ payment_gateway = payment.payment_method
131
+ check_environment(payment_gateway)
132
+
123
133
  response = payment_gateway.void(payment.response_code, minimal_gateway_options(payment))
124
134
  record_log payment, response
125
135
 
@@ -129,11 +139,14 @@ class Creditcard < ActiveRecord::Base
129
139
  else
130
140
  gateway_error(response)
131
141
  end
132
- rescue ActiveMerchant::ConnectionError => e
142
+ rescue ActiveMerchant::ConnectionError
133
143
  gateway_error I18n.t(:unable_to_connect_to_gateway)
134
144
  end
135
145
 
136
146
  def credit(payment)
147
+ payment_gateway = payment.payment_method
148
+ check_environment(payment_gateway)
149
+
137
150
  amount = payment.credit_allowed >= payment.order.outstanding_balance.abs ? payment.order.outstanding_balance.abs : payment.credit_allowed.abs
138
151
 
139
152
  if payment_gateway.payment_profiles_supported?
@@ -154,13 +167,10 @@ class Creditcard < ActiveRecord::Base
154
167
  else
155
168
  gateway_error(response)
156
169
  end
157
- rescue ActiveMerchant::ConnectionError => e
170
+ rescue ActiveMerchant::ConnectionError
158
171
  gateway_error I18n.t(:unable_to_connect_to_gateway)
159
172
  end
160
173
 
161
-
162
-
163
-
164
174
  def actions
165
175
  %w{capture void credit}
166
176
  end
@@ -170,20 +180,14 @@ class Creditcard < ActiveRecord::Base
170
180
  payment.state == "pending"
171
181
  end
172
182
 
173
- # Indicates whether its possible to void the payment. Most gateways require that the payment has not been
174
- # settled yet when performing a void (which generally happens within 12-24 hours of the transaction.) For
175
- # this reason, the default behavior of Spree is to only allow void operations within the first 12 hours of
176
- # the payment creation time.
183
+ # Indicates whether its possible to void the payment.
177
184
  def can_void?(payment)
178
- return false unless (Time.now - 12.hours) < payment.created_at
179
- %w{completed pending}.include? payment.state
185
+ payment.state == "void" ? false : true
180
186
  end
181
187
 
182
- # Indicates whether its possible to credit the payment. Most gateways require that the payment be settled
183
- # first which generally happens within 12-24 hours of the transaction. For this reason, the default
184
- # behavior of Spree is to disallow credit operations until the payment is at least 12 hours old.
188
+ # Indicates whether its possible to credit the payment. Note that most gateways require that the
189
+ # payment be settled first which generally happens within 12-24 hours of the transaction.
185
190
  def can_credit?(payment)
186
- return false unless (Time.now - 12.hours) > payment.created_at
187
191
  return false unless payment.state == "completed"
188
192
  return false unless payment.order.payment_state == "credit_owed"
189
193
  payment.credit_allowed > 0
@@ -239,8 +243,11 @@ class Creditcard < ActiveRecord::Base
239
243
  self.class.type?(number)
240
244
  end
241
245
 
242
- def payment_gateway
243
- @payment_gateway ||= Gateway.current
246
+ # Saftey check to make sure we're not accidentally performing operations on a live gateway.
247
+ # Ex. When testing in staging environment with a copy of production data.
248
+ def check_environment(gateway)
249
+ return if gateway.environment == Rails.env
250
+ message = I18n.t(:gateway_config_unavailable) + " - #{Rails.env}"
251
+ raise Spree::GatewayError.new(message)
244
252
  end
245
-
246
253
  end
@@ -1,48 +1,48 @@
1
1
  class Gateway < PaymentMethod
2
2
  delegate_belongs_to :provider, :authorize, :purchase, :capture, :void, :credit
3
-
3
+
4
4
  validates :name, :type, :presence => true
5
-
5
+
6
6
  preference :server, :string, :default => 'test'
7
7
  preference :test_mode, :boolean, :default => true
8
8
 
9
9
  def payment_source_class
10
10
  Creditcard
11
11
  end
12
-
12
+
13
13
  # instantiates the selected gateway and configures with the options stored in the database
14
14
  def self.current
15
15
  super
16
- end
17
-
16
+ end
17
+
18
18
  def provider
19
19
  gateway_options = options
20
20
  ActiveMerchant::Billing::Base.gateway_mode = gateway_options[:server].to_sym
21
21
  @provider ||= provider_class.new(gateway_options)
22
- end
23
-
22
+ end
23
+
24
24
  def options
25
25
  options_hash = {}
26
- self.preferences.each do |key,value|
26
+ self.preferences.each do |key,value|
27
27
  options_hash[key.to_sym] = value
28
28
  end
29
29
  options_hash
30
30
  end
31
-
31
+
32
32
  def method_missing(method, *args)
33
- if @provider.nil?
33
+ if @provider.nil? || !@provider.respond_to?(method)
34
34
  super
35
35
  else
36
- @provider.respond_to?(method) ? provider.send(method) : super
36
+ provider.send(method)
37
37
  end
38
38
  end
39
-
39
+
40
40
  def payment_profiles_supported?
41
41
  false
42
42
  end
43
-
43
+
44
44
  def method_type
45
45
  "gateway"
46
46
  end
47
-
47
+
48
48
  end
@@ -26,11 +26,11 @@ class Gateway::Beanstream < Gateway
26
26
  end
27
27
  end
28
28
  end
29
-
29
+
30
30
  def capture(transaction, creditcard, gateway_options)
31
31
  beanstream_gateway.capture((transaction.amount*100).round, transaction.response_code, gateway_options)
32
32
  end
33
-
33
+
34
34
  def void(transaction_response, creditcard, gateway_options)
35
35
  beanstream_gateway.void(transaction_response, gateway_options)
36
36
  end
@@ -41,13 +41,13 @@ class Gateway::Beanstream < Gateway
41
41
  end
42
42
 
43
43
  private
44
-
44
+
45
45
  def beanstream_gateway
46
46
  ActiveMerchant::Billing::Base.gateway_mode = preferred_server.to_sym
47
47
  gateway_options = options
48
48
  ActiveMerchant::Billing::BeanstreamGateway.new(gateway_options)
49
49
  end
50
-
50
+
51
51
  def verify_creditcard_name!(creditcard)
52
52
  bill_address = creditcard.payments.first.order.bill_address
53
53
  creditcard.first_name = bill_address.firstname unless creditcard.first_name?
@@ -15,7 +15,7 @@ class Gateway::Bogus < Gateway
15
15
  def preferences
16
16
  {}
17
17
  end
18
-
18
+
19
19
  def create_profile(payment)
20
20
  # simulate the storage of credit card profile using remote service
21
21
  success = VALID_CCS.include? payment.source.number
@@ -24,7 +24,7 @@ class Gateway::Bogus < Gateway
24
24
 
25
25
  def authorize(money, creditcard, options = {})
26
26
  profile_id = creditcard.gateway_customer_profile_id
27
- if VALID_CCS.include? creditcard.number or (profile_id and profile_id.starts_with? "BGS-")
27
+ if VALID_CCS.include? creditcard.number or (profile_id and profile_id.starts_with? "BGS-")
28
28
  ActiveMerchant::Billing::Response.new(true, "Bogus Gateway: Forced success", {}, :test => true, :authorization => '12345', :avs_result => {:code => 'A'})
29
29
  else
30
30
  ActiveMerchant::Billing::Response.new(false, "Bogus Gateway: Forced failure", {:message => 'Bogus Gateway: Forced failure'}, :test => true)
@@ -61,11 +61,11 @@ class Gateway::Bogus < Gateway
61
61
  # Test mode is not really relevant with bogus gateway (no such thing as live server)
62
62
  true
63
63
  end
64
-
65
- def payment_profiles_supported?
64
+
65
+ def payment_profiles_supported?
66
66
  true
67
67
  end
68
-
68
+
69
69
  private
70
70
  def generate_profile_id(success)
71
71
  record = true
@@ -75,6 +75,6 @@ class Gateway::Bogus < Gateway
75
75
  record = Creditcard.find(:first, :conditions => ["gateway_customer_profile_id = ?", random])
76
76
  end
77
77
  random
78
- end
78
+ end
79
79
 
80
80
  end
@@ -0,0 +1,88 @@
1
+ class Gateway::Braintree < Gateway
2
+ preference :merchant_id, :string
3
+ preference :public_key, :string
4
+ preference :private_key, :string
5
+
6
+ def provider_class
7
+ ActiveMerchant::Billing::BraintreeGateway
8
+ end
9
+
10
+ def authorize(money, creditcard, options = {})
11
+ adjust_options_for_braintree(creditcard, options)
12
+ payment_method = creditcard.gateway_customer_profile_id || creditcard
13
+ provider.authorize(money, payment_method, options)
14
+ end
15
+
16
+ def capture(authorization, ignored_creditcard, ignored_options)
17
+ amount = (authorization.amount * 100).to_i
18
+ provider.capture(amount, authorization.response_code)
19
+ end
20
+
21
+ def create_profile(payment)
22
+ if payment.source.gateway_customer_profile_id.nil?
23
+ response = provider.store(payment.source)
24
+ if response.success?
25
+ payment.source.update_attributes!(:gateway_customer_profile_id => response.params["customer_vault_id"])
26
+ else
27
+ payment.source.gateway_error response.message
28
+ end
29
+ end
30
+ end
31
+
32
+ def credit(*args)
33
+ if args.size == 4
34
+ credit_with_payment_profiles(*args)
35
+ elsif args.size == 3
36
+ credit_without_payment_profiles(*args)
37
+ else
38
+ raise ArgumentError, "Expected 3 or 4 arguments, received #{args.size}"
39
+ end
40
+ end
41
+
42
+ def credit_with_payment_profiles(amount, payment, response_code, option)
43
+ provider.credit(amount, payment)
44
+ end
45
+
46
+ def credit_without_payment_profiles(amount, response_code, options)
47
+ transaction = ::Braintree::Transaction.find(response_code)
48
+ if BigDecimal.new(amount.to_s) == (transaction.amount * 100)
49
+ provider.refund(response_code)
50
+ else
51
+ raise NotImplementedError
52
+ end
53
+ end
54
+
55
+ def payment_profiles_supported?
56
+ true
57
+ end
58
+
59
+ def purchase(money, creditcard, options = {})
60
+ authorize(money, creditcard, options.merge(:submit_for_settlement => true))
61
+ end
62
+
63
+ def void(response_code, ignored_options)
64
+ provider.void(response_code)
65
+ end
66
+
67
+ protected
68
+
69
+ def adjust_country_name(options)
70
+ [:billing_address, :shipping_address].each do |address|
71
+ if options[address] && options[address][:country] == "US"
72
+ options[address][:country] = "United States of America"
73
+ end
74
+ end
75
+ end
76
+
77
+ def adjust_billing_address(creditcard, options)
78
+ if creditcard.gateway_customer_profile_id
79
+ options.delete(:billing_address)
80
+ end
81
+ end
82
+
83
+ def adjust_options_for_braintree(creditcard, options)
84
+ adjust_country_name(options)
85
+ adjust_billing_address(creditcard, options)
86
+ end
87
+ end
88
+