effective_orders 1.0.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 (146) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +856 -0
  4. data/Rakefile +24 -0
  5. data/app/assets/images/effective_orders/stripe_connect.png +0 -0
  6. data/app/assets/javascripts/effective_orders/shipping_address_toggle.js.coffee +30 -0
  7. data/app/assets/javascripts/effective_orders/stripe_charges.js.coffee +26 -0
  8. data/app/assets/javascripts/effective_orders/stripe_subscriptions.js.coffee +28 -0
  9. data/app/assets/javascripts/effective_orders.js +2 -0
  10. data/app/assets/stylesheets/effective_orders/_order.scss +30 -0
  11. data/app/assets/stylesheets/effective_orders.css.scss +1 -0
  12. data/app/controllers/admin/customers_controller.rb +15 -0
  13. data/app/controllers/admin/orders_controller.rb +22 -0
  14. data/app/controllers/effective/carts_controller.rb +70 -0
  15. data/app/controllers/effective/orders_controller.rb +191 -0
  16. data/app/controllers/effective/providers/moneris.rb +94 -0
  17. data/app/controllers/effective/providers/paypal.rb +29 -0
  18. data/app/controllers/effective/providers/stripe.rb +125 -0
  19. data/app/controllers/effective/providers/stripe_connect.rb +47 -0
  20. data/app/controllers/effective/subscriptions_controller.rb +123 -0
  21. data/app/controllers/effective/webhooks_controller.rb +86 -0
  22. data/app/helpers/effective_carts_helper.rb +90 -0
  23. data/app/helpers/effective_orders_helper.rb +108 -0
  24. data/app/helpers/effective_paypal_helper.rb +37 -0
  25. data/app/helpers/effective_stripe_helper.rb +63 -0
  26. data/app/mailers/effective/orders_mailer.rb +64 -0
  27. data/app/models/concerns/acts_as_purchasable.rb +134 -0
  28. data/app/models/effective/access_denied.rb +17 -0
  29. data/app/models/effective/cart.rb +65 -0
  30. data/app/models/effective/cart_item.rb +40 -0
  31. data/app/models/effective/customer.rb +61 -0
  32. data/app/models/effective/datatables/customers.rb +45 -0
  33. data/app/models/effective/datatables/orders.rb +53 -0
  34. data/app/models/effective/order.rb +247 -0
  35. data/app/models/effective/order_item.rb +69 -0
  36. data/app/models/effective/stripe_charge.rb +35 -0
  37. data/app/models/effective/subscription.rb +95 -0
  38. data/app/models/inputs/price_field.rb +63 -0
  39. data/app/models/inputs/price_form_input.rb +7 -0
  40. data/app/models/inputs/price_formtastic_input.rb +9 -0
  41. data/app/models/inputs/price_input.rb +19 -0
  42. data/app/models/inputs/price_simple_form_input.rb +8 -0
  43. data/app/models/validators/effective/sold_out_validator.rb +7 -0
  44. data/app/views/active_admin/effective_orders/orders/_show.html.haml +70 -0
  45. data/app/views/admin/customers/_actions.html.haml +2 -0
  46. data/app/views/admin/customers/index.html.haml +10 -0
  47. data/app/views/admin/orders/index.html.haml +7 -0
  48. data/app/views/admin/orders/show.html.haml +11 -0
  49. data/app/views/effective/carts/_cart.html.haml +33 -0
  50. data/app/views/effective/carts/show.html.haml +18 -0
  51. data/app/views/effective/orders/_checkout_step_1.html.haml +39 -0
  52. data/app/views/effective/orders/_checkout_step_2.html.haml +18 -0
  53. data/app/views/effective/orders/_my_purchases.html.haml +15 -0
  54. data/app/views/effective/orders/_order.html.haml +4 -0
  55. data/app/views/effective/orders/_order_header.html.haml +21 -0
  56. data/app/views/effective/orders/_order_items.html.haml +39 -0
  57. data/app/views/effective/orders/_order_payment_details.html.haml +11 -0
  58. data/app/views/effective/orders/_order_shipping.html.haml +19 -0
  59. data/app/views/effective/orders/_order_user_fields.html.haml +10 -0
  60. data/app/views/effective/orders/checkout.html.haml +3 -0
  61. data/app/views/effective/orders/declined.html.haml +10 -0
  62. data/app/views/effective/orders/moneris/_form.html.haml +34 -0
  63. data/app/views/effective/orders/my_purchases.html.haml +6 -0
  64. data/app/views/effective/orders/my_sales.html.haml +28 -0
  65. data/app/views/effective/orders/new.html.haml +4 -0
  66. data/app/views/effective/orders/paypal/_form.html.haml +5 -0
  67. data/app/views/effective/orders/purchased.html.haml +10 -0
  68. data/app/views/effective/orders/show.html.haml +17 -0
  69. data/app/views/effective/orders/stripe/_form.html.haml +8 -0
  70. data/app/views/effective/orders/stripe/_subscription_fields.html.haml +7 -0
  71. data/app/views/effective/orders_mailer/order_receipt_to_admin.html.haml +8 -0
  72. data/app/views/effective/orders_mailer/order_receipt_to_buyer.html.haml +8 -0
  73. data/app/views/effective/orders_mailer/order_receipt_to_seller.html.haml +30 -0
  74. data/app/views/effective/subscriptions/index.html.haml +16 -0
  75. data/app/views/effective/subscriptions/new.html.haml +10 -0
  76. data/app/views/effective/subscriptions/show.html.haml +49 -0
  77. data/config/routes.rb +57 -0
  78. data/db/migrate/01_create_effective_orders.rb.erb +91 -0
  79. data/db/upgrade/02_upgrade_effective_orders_from03x.rb.erb +29 -0
  80. data/db/upgrade/upgrade_price_column_on_table.rb.erb +17 -0
  81. data/lib/effective_orders/engine.rb +52 -0
  82. data/lib/effective_orders/version.rb +3 -0
  83. data/lib/effective_orders.rb +76 -0
  84. data/lib/generators/effective_orders/install_generator.rb +38 -0
  85. data/lib/generators/effective_orders/upgrade_from03x_generator.rb +34 -0
  86. data/lib/generators/effective_orders/upgrade_price_column_generator.rb +34 -0
  87. data/lib/generators/templates/README +1 -0
  88. data/lib/generators/templates/effective_orders.rb +210 -0
  89. data/spec/controllers/carts_controller_spec.rb +143 -0
  90. data/spec/controllers/moneris_orders_controller_spec.rb +245 -0
  91. data/spec/controllers/orders_controller_spec.rb +418 -0
  92. data/spec/controllers/stripe_orders_controller_spec.rb +127 -0
  93. data/spec/controllers/webhooks_controller_spec.rb +79 -0
  94. data/spec/dummy/README.rdoc +8 -0
  95. data/spec/dummy/Rakefile +6 -0
  96. data/spec/dummy/app/assets/javascripts/application.js +13 -0
  97. data/spec/dummy/app/assets/stylesheets/application.css +15 -0
  98. data/spec/dummy/app/controllers/application_controller.rb +5 -0
  99. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  100. data/spec/dummy/app/models/product.rb +17 -0
  101. data/spec/dummy/app/models/product_with_float_price.rb +17 -0
  102. data/spec/dummy/app/models/user.rb +28 -0
  103. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  104. data/spec/dummy/bin/bundle +3 -0
  105. data/spec/dummy/bin/rails +4 -0
  106. data/spec/dummy/bin/rake +4 -0
  107. data/spec/dummy/config/application.rb +31 -0
  108. data/spec/dummy/config/boot.rb +5 -0
  109. data/spec/dummy/config/database.yml +25 -0
  110. data/spec/dummy/config/environment.rb +5 -0
  111. data/spec/dummy/config/environments/development.rb +37 -0
  112. data/spec/dummy/config/environments/production.rb +83 -0
  113. data/spec/dummy/config/environments/test.rb +39 -0
  114. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  115. data/spec/dummy/config/initializers/cookies_serializer.rb +3 -0
  116. data/spec/dummy/config/initializers/devise.rb +254 -0
  117. data/spec/dummy/config/initializers/effective_addresses.rb +15 -0
  118. data/spec/dummy/config/initializers/effective_orders.rb +22 -0
  119. data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  120. data/spec/dummy/config/initializers/inflections.rb +16 -0
  121. data/spec/dummy/config/initializers/mime_types.rb +4 -0
  122. data/spec/dummy/config/initializers/session_store.rb +3 -0
  123. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  124. data/spec/dummy/config/locales/en.yml +23 -0
  125. data/spec/dummy/config/routes.rb +3 -0
  126. data/spec/dummy/config/secrets.yml +22 -0
  127. data/spec/dummy/config.ru +4 -0
  128. data/spec/dummy/db/schema.rb +142 -0
  129. data/spec/dummy/db/test.sqlite3 +0 -0
  130. data/spec/dummy/log/development.log +487 -0
  131. data/spec/dummy/log/test.log +347 -0
  132. data/spec/dummy/public/404.html +67 -0
  133. data/spec/dummy/public/422.html +67 -0
  134. data/spec/dummy/public/500.html +66 -0
  135. data/spec/dummy/public/favicon.ico +0 -0
  136. data/spec/helpers/effective_orders_helper_spec.rb +21 -0
  137. data/spec/models/acts_as_purchasable_spec.rb +107 -0
  138. data/spec/models/customer_spec.rb +71 -0
  139. data/spec/models/factories_spec.rb +13 -0
  140. data/spec/models/order_item_spec.rb +35 -0
  141. data/spec/models/order_spec.rb +323 -0
  142. data/spec/models/stripe_charge_spec.rb +39 -0
  143. data/spec/models/subscription_spec.rb +103 -0
  144. data/spec/spec_helper.rb +44 -0
  145. data/spec/support/factories.rb +118 -0
  146. metadata +387 -0
data/README.md ADDED
@@ -0,0 +1,856 @@
1
+ # Effective Orders
2
+
3
+ Carts, Orders, and collecting payment via Stripe, PayPal and Moneris.
4
+
5
+ A Rails Engine to handle the purchase workflow in a Rails 3.2.x / Rails 4 application.
6
+
7
+ Also works with Stripe Connect and Stripe Subscriptions with coupons.
8
+
9
+ Sends order receipt emails automatically.
10
+
11
+ Has Order History, My Purchases, My Sales and Admin screens.
12
+
13
+ ## Getting Started
14
+
15
+ Add to your Gemfile:
16
+
17
+ ```ruby
18
+ gem 'effective_orders'
19
+ ```
20
+
21
+ Run the bundle command to install it:
22
+
23
+ ```console
24
+ bundle install
25
+ ```
26
+
27
+ Then run the generator:
28
+
29
+ ```ruby
30
+ rails generate effective_orders:install
31
+ ```
32
+
33
+ The generator will install an initializer which describes all configuration options and creates a database migration.
34
+
35
+ If you want to tweak the table name (to use something other than the default 'orders', 'order_items', 'carts', 'cart_items', 'customers', 'subscriptions'), manually adjust both the configuration file and the migration now.
36
+
37
+ Then migrate the database:
38
+
39
+ ```ruby
40
+ rake db:migrate
41
+ ```
42
+
43
+ Require the javascript on the asset pipeline by adding the following to your application.js:
44
+
45
+ ```ruby
46
+ //= require effective_orders
47
+ ```
48
+
49
+ Require the stylesheet on the asset pipeline by adding the following to your application.css:
50
+
51
+ ```ruby
52
+ *= require effective_orders
53
+ ```
54
+
55
+ ### Upgrading from 0.3.x
56
+
57
+ In the 0.3.x versions of this gem, prices were internally represented as Decimals
58
+
59
+ This has been changed in 0.4.x to properly be Integer columns
60
+
61
+ If you're running a 0.3.x or earlier version, please upgrade to 0.4.x with this one command:
62
+
63
+ ```ruby
64
+ rails generate effective_orders:upgrade_from03x
65
+ ```
66
+
67
+ the above command will upgrade the order_items and subscriptions tables.
68
+
69
+ If you have additional (products or whatever..) tables with a column `price` represented as a Decimal, they should also be upgraded.
70
+
71
+ To upgrade, use this generator to create a migration on table `products` with column `price`:
72
+
73
+ ```ruby
74
+ bundle exec rails generate effective_orders:upgrade_price_column products price
75
+ ```
76
+
77
+ ## High Level Overview
78
+
79
+ Your rails app creates and displays a list of `acts_as_purchsable` objects, each with a `link_to_add_to_cart(object)`.
80
+
81
+ The user clicks one or more Add to Cart links and adds some purchasables to their cart.
82
+
83
+ They then click the Checkout link from the My Cart page, or another `link_to_checkout` displayed somewhere, which takes them to `effective_orders.new_order_path` to begin checkout.
84
+
85
+ The checkout is a 2-page process:
86
+
87
+ The first page collects billing/shipping details and gives the user their final option to 'Change Items'.
88
+
89
+ After clicking 'Save and Continue', the user will be on the collect money page.
90
+
91
+ If the payment processor is PayPal or Moneris, the user will be sent to their website to enter their credit card details.
92
+
93
+ If the payment processor is Stripe, there is an on-screen popup form to collect those details.
94
+
95
+ Once the user has successfully paid, they are redirected to a thank you page displaying the order receipt.
96
+
97
+ An email notification containing the receipt is also sent to the buyer's email address, and the site admin.
98
+
99
+
100
+ ## Usage
101
+
102
+ effective_orders handles the add_to_cart -> checkout -> collect of payment workflow, but relies on the base application to define, create and display the purchaseable things.
103
+
104
+ These purchasables could be Products, EventTickets, Memberships or anything else.
105
+
106
+
107
+ ### Representing Prices
108
+
109
+ All prices should be internally represented as Integers. For us North Americans, think of it as the number of cents.
110
+
111
+ To represent the value of `$10.00` the price method should return `1000`.
112
+
113
+ Similarly, to represent a value of `$0.50` the price method should return `50`.
114
+
115
+ EffectiveOrders does not deal with a specific currency or do any currency conversions of any kind. The main gem authors are North American, and as such this gem is unfortunately North American biased.
116
+
117
+
118
+ ### Creating a purchasable
119
+
120
+ Once installed, we still need to create something to purchase.
121
+
122
+ Let's create a `Product` model that uses the `acts_as_purchasable` mixin.
123
+
124
+ We're also going to prevent the Product from being deleted by overriding `def destroy` and instead setting a boolean `archived = true`.
125
+
126
+ If someone purchased a Product which is later deleted, the Order History page will be unable to find the Product.
127
+
128
+ ```ruby
129
+ class Product < ActiveRecord::Base
130
+ acts_as_purchasable
131
+
132
+ # this structure do... block is provided by the migrant gem https://github.com/pascalh1011/migrant
133
+ structure do
134
+ title :string
135
+
136
+ price :integer, :default => 0
137
+ tax_exempt :boolean, :default => false
138
+
139
+ archived :boolean, :default => false
140
+
141
+ timestamps
142
+ end
143
+
144
+ validates_presence_of :title
145
+ validates_numericality_of :price, :greater_than_or_equal_to => 0
146
+
147
+ scope :products, -> { where(:archived => false) }
148
+
149
+ # This archives Products instead of deleting them
150
+ def destroy
151
+ update_attributes(:archived => true)
152
+ end
153
+
154
+ end
155
+ ```
156
+
157
+ The database migration will look like the following:
158
+
159
+ ```ruby
160
+ class CreateProducts < ActiveRecord::Migration
161
+ def self.up
162
+ create_table :products do |t|
163
+ t.string :title
164
+ t.integer :price, :default=>0
165
+ t.boolean :tax_exempt, :default=>false
166
+ t.boolean :archived, :default=>false
167
+ t.datetime :updated_at
168
+ t.datetime :created_at
169
+ end
170
+ end
171
+
172
+ def self.down
173
+ drop_table :products
174
+ end
175
+ end
176
+ ```
177
+
178
+ Once the database has been migrated, it is time to scaffold/build the CRUD Product screens to create some Products to sell.
179
+
180
+ ### Products#new/#edit
181
+
182
+ Use the EffectiveOrders price input to enter the price.
183
+
184
+ It displays the underlying Integer price as a currency formatted value, ensures that a properly formatted price is entered by the user, and POSTs the appropriate Integer value back to the server.
185
+
186
+ This is available for simple_form, formtastic and Rails default FormBuilder.
187
+
188
+ ```haml
189
+ = simple_form_for(@product) do |f|
190
+ = f.input :title
191
+ = f.input :tax_exempt
192
+ = f.input :price, :as => :price
193
+ = f.button :submit
194
+ ```
195
+
196
+ or
197
+
198
+ ```ruby
199
+ = semantic_form_for(@product) do |f|
200
+ = f.input :price, :as => :price
201
+ ```
202
+
203
+ or
204
+
205
+ ```haml
206
+ = form_for(@product) do |f|
207
+ = f.price_field :price
208
+ ```
209
+
210
+ The `:as => :price` will work interchangeably with SimpleForm or Formtastic, as long as only one of these gems is present in your application
211
+
212
+ If you use both SimpleForm and Formtastic, you will need to call price input differently:
213
+
214
+ ```ruby
215
+ = simple_form_for(@product) do |f|
216
+ = f.input :price, :as => :price_simple_form
217
+
218
+ = semantic_form_for @user do |f|
219
+ = f.input :price, :as => :price_formtastic
220
+ ```
221
+
222
+ ### Products#show
223
+
224
+ So back on the Product#show page, we will render the product with an Add To Cart link
225
+
226
+ ```haml
227
+ %h4= @product.title
228
+ %p= price_to_currency(@product.price)
229
+ %p= link_to_add_to_cart(@product, :class => 'btn btn-primary', :label => 'Add To My Shopping Cart')
230
+ ```
231
+
232
+ Please take note of the `price_to_currency` helper above.
233
+
234
+ This is an EffectiveOrders helper that will display an Integer price as a currency formatted value. It does an Integer to Float conversion then calls the rails standard `number_to_currency`.
235
+
236
+ When the user clicks 'Add To My Shopping Cart' the product will be added to the cart. A flash message is displayed, and the user will return to the same page.
237
+
238
+ ### My Cart
239
+
240
+ We still need to create a link to the Shopping Cart page so that the user can view their cart. On your site's main menu:
241
+
242
+ ```ruby
243
+ = link_to_current_cart() # To display Cart (3) when there are 3 items
244
+ ```
245
+
246
+ or
247
+
248
+ ```ruby
249
+ = link_to_current_cart(:label => 'Shopping Cart', :class => 'btn btn-prmary') # To display Shopping Cart (3) when there are 3 items
250
+ ```
251
+
252
+ or
253
+
254
+ ```ruby
255
+ = link_to 'My Cart', effective_orders.carts_path
256
+ ```
257
+
258
+ ### Checkout
259
+
260
+ The checkout screen can be reached through the My Cart page, or linked to directly via
261
+
262
+ ```ruby
263
+ = link_to_checkout() # To display Proceed to Checkout
264
+ ```
265
+
266
+ or
267
+
268
+ ```ruby
269
+ = link_to_checkout(:label => 'Continue to Checkout', :class => 'btn btn-primary')
270
+ ```
271
+
272
+ or
273
+
274
+ ```ruby
275
+ = link_to 'Go Checkout Already', effective_orders.new_order_path
276
+ ```
277
+
278
+ From here, the effective_orders engine takes over, walks the user through billing and shipping details screens, then finally collects payment through one of the configured payment processors.
279
+
280
+
281
+ ## Acts As Purchasable
282
+
283
+ Mark your rails model with the mixin `acts_as_purchasable` to use it with the effective_orders gem.
284
+
285
+ This mixin sets up the relationships and provides some validations on price and such.
286
+
287
+ ### Methods
288
+
289
+ acts_as_purchasable provides the following methods:
290
+
291
+ `.purchased?` has this been purchased by any user in any order?
292
+
293
+ `.purchased_by?(user)` has this been purchased by the given user?
294
+
295
+ `.purchased_orders` returns the `Effective::Order`s in which the purchases have been made
296
+
297
+ ### Scopes
298
+
299
+ acts_as_purchsable provides the following scopes:
300
+
301
+ `Product.purchased` all the Products that have been purchased
302
+
303
+ `Product.purchased_by(user)` all the Products purchased by a given user.
304
+
305
+ `Product.sold` all the Products that have been solid (same as purchased)
306
+
307
+ `Product.sold_by(user)` all the Products that this user has sold via Stripe Connect
308
+
309
+ `Product.not_purchased` all unpurchased Products
310
+
311
+ ### Digital Downloads
312
+
313
+ If your product is a digital download, simply create a method in your acts_as_purchasable rails model that returns the full URL to download.
314
+
315
+ The download link will be displayed on all purchased order receipts and the Order History page.
316
+
317
+ ```ruby
318
+ def purchased_download_url
319
+ 'http://www.something.com/my_cool_product.zip'
320
+ end
321
+ ```
322
+
323
+ Of course, there's no mechanism here to prevent someone from just copy&pasting this URL to a friend.
324
+
325
+ If you're interested in that kind of restricted-download functionality, please check out [effective_assets](https://github.com/code-and-effect/effective_assets) and the authenticated-read temporary URLs.
326
+
327
+
328
+ ### Tax
329
+
330
+ All `acts_as_purchasable` objects will respond to the boolean method `tax_exempt`.
331
+
332
+ By default, `tax_exempt` is false, meaning that tax must be applied to this item.
333
+
334
+ The tax calculation is controlled by the config/initializers/effective_orders.rb `config.tax_rate_method` and may be set on an app wide basis.
335
+
336
+ If `tax_exempt` returns true, it means that no tax will be applied to this item.
337
+
338
+ Please see the initializer for more information.
339
+
340
+
341
+ ### Callbacks
342
+
343
+ When defined, upon purchase the following callback will be triggered:
344
+
345
+ ```ruby
346
+ class Product
347
+ acts_as_purchasable
348
+
349
+ after_purchase do |order, order_item| # These are optional, if you don't care about the order or order_item
350
+ self.do_something() # self is the newly purchased instance of this Product
351
+ end
352
+
353
+ end
354
+ ```
355
+
356
+ ## Authorization
357
+
358
+ All authorization checks are handled via the config.authorization_method found in the `config/initializers/effective_orders.rb` file.
359
+
360
+ It is intended for flow through to CanCan or Pundit, but neither of those gems are required.
361
+
362
+ This method is called by the controller action with the appropriate action and resource
363
+
364
+ This method is called by all controller actions with the appropriate action and resource
365
+
366
+ Action will be one of [:index, :show, :new, :create, :edit, :update, :destroy]
367
+
368
+ Resource will the appropriate Effective::Order, Effective::Cart or Effective::Subscription ActiveRecord object or class
369
+
370
+ The authorization method is defined in the initializer file:
371
+
372
+ ```ruby
373
+ # As a Proc (with CanCan)
374
+ config.authorization_method = Proc.new { |controller, action, resource| authorize!(action, resource) }
375
+ ```
376
+
377
+ ```ruby
378
+ # As a Custom Method
379
+ config.authorization_method = :my_authorization_method
380
+ ```
381
+
382
+ and then in your application_controller.rb:
383
+
384
+ ```ruby
385
+ def my_authorization_method(action, resource)
386
+ current_user.is?(:admin) || EffectivePunditPolicy.new(current_user, resource).send('#{action}?')
387
+ end
388
+ ```
389
+
390
+ or disabled entirely:
391
+
392
+ ```ruby
393
+ config.authorization_method = false
394
+ ```
395
+
396
+ If the method or proc returns false (user is not authorized) an Effective::AccessDenied exception will be raised
397
+
398
+ You can rescue from this exception by adding the following to your application_controller.rb:
399
+
400
+ ```ruby
401
+ rescue_from Effective::AccessDenied do |exception|
402
+ respond_to do |format|
403
+ format.html { render 'static_pages/access_denied', :status => 403 }
404
+ format.any { render :text => 'Access Denied', :status => 403 }
405
+ end
406
+ end
407
+ ```
408
+
409
+ ### Permissions
410
+
411
+ The permissions you actually want to define are as follows (using CanCan):
412
+
413
+ ```ruby
414
+ can [:manage], Effective::Cart, :user_id => user.id
415
+ can [:manage], Effective::Order, :user_id => user.id # Orders cannot be deleted
416
+ can [:manage], Effective::Subscription, :user_id => user.id
417
+ ```
418
+
419
+ ## Whats Included
420
+
421
+ This gem has a lot of screens, all of which are automatically available via the Rails Engine.
422
+
423
+ Pretty much every screen also has a coresponding helper function that is used in rendering that content.
424
+
425
+ The idea behind this implementation is that you, the developer, should be able to use effective_orders as a quick drop-in purchasing solution, with all screens and routes provided, but also have all the individual pieces available to customize the workflow.
426
+
427
+
428
+ ### Carts
429
+
430
+ The standard website shopping cart paradigm. Add one or more objects to the cart and purchase them all in one step.
431
+
432
+ When a non-logged-in user comes to the website, a new `Effective::Cart` object is created and stored in the session variable. This user can add items to the Cart as normal.
433
+
434
+ Only when the user proceeds to Checkout will they be required to login.
435
+
436
+ Upon log in, the session Cart will be assigned to that User's ID, and if the User had a previous existing cart, all CartItems will be merged.
437
+
438
+
439
+
440
+ You shouldn't need to deal with the Cart object at all, except to make a link from your Site Menu to the 'My Cart' page (as documented above).
441
+
442
+ However, if you want to render a Cart on another page, or play with the Cart object directly, you totally can.
443
+
444
+ Use the helper method `current_cart` to refer to the current `Effective::Cart`.
445
+
446
+ And call `render_cart(current_cart)` to display the Cart anywhere.
447
+
448
+
449
+ ### Orders
450
+
451
+ On the Checkout page (`effective_orders.new_order_path`) a new `Effective::Order` object is created and one or more `Effective::OrderItem`s are initialized based on the `current_cart`.
452
+
453
+ If the configuration options `config.require_billing_address` and/or `config.require_shipping_address` options are `true` then the user will be prompted for the appropriate addresses, based on [effective_addresses](https://github.com/code-and-effect/effective_addresses/).
454
+
455
+ As well, if the config option `config.collect_user_fields` is present, form fields to collect those user attributes will be present on this page.
456
+
457
+ When the user submits the form on this screen, a POST to `effective_orders.order_path` is made, and the `Effective::Order` object is validated and created.
458
+
459
+ On this final checkout screen, links to all configured payment providers are displayed, and the user may choose which payment processor should be used to make a payment.
460
+
461
+ The payment processor handles collecting the Credit Card number, and through one way or another, the `Effective::Order` `@order.purchase!` method is called.
462
+
463
+ Once the order has been marked purchased, the user is redirected to the `effective_orders.order_purchased_path` screen where they see a 'Thank You!' message, and the Order receipt.
464
+
465
+ If the configuration option `config.mailer[:send_order_receipt_to_buyer] == true` the order receipt will be emailed to the user.
466
+
467
+ As well, if the configuration option `config.mailer[:send_order_receipt_to_admin] == true` the order receipt will be emailed to the site admin.
468
+
469
+ The Order has now been purchased.
470
+
471
+
472
+ If you are using effective_orders to roll your own custom payment workflow, you should be aware of the following helpers:
473
+
474
+ - `render_checkout(order)` to display the standard Checkout step inline.
475
+ - `render_checkout(order, :purchased_redirect_url => '/', :declined_redirect_url => '/')` to display the Checkout step with custom redirect paths.
476
+
477
+ - `render_purchasables(one_or_more_acts_as_purchasable_objects)` to display a list of purchasable items
478
+
479
+ - `render_order(order)` to display the full Order receipt.
480
+ - `order_summary(order)` to display some quick details of an Order and its OrderItems.
481
+ - `order_payment_to_html(order)` to display the payment processor details for an order's payment transaction.
482
+
483
+
484
+ ### Effective::Order Model
485
+
486
+ There may be times where you want to deal with the `Effective::Order` object directly.
487
+
488
+ The `acts_as_purchasable` `.purchased?` and `.purchased_by?(user)` methods only return true when a purchased `Effective::Order` exists for that object.
489
+
490
+ To programatically purchase one or more `acts_as_purchasable` objects:
491
+
492
+ ```ruby
493
+ Effective::Order.new([@product1, @product2], current_user).purchase!('from my rake task')
494
+ ```
495
+
496
+ Here the `billing_address` and `shipping_address` are copied from the `current_user` object if the `current_user` responds_to `billing_address` / `shipping_address` as per [effective_addresses](https://github.com/code-and-effect/effective_addresses/).
497
+
498
+ Here's another example of programatically purchasing some `acts_as_purchasable` objects:
499
+
500
+ ```ruby
501
+ order = Effective::Order.new()
502
+ order.user = @user
503
+ order.billing_address = Effective::Address.new(...)
504
+ order.shipping_address = Effective::Address.new(...)
505
+ order.add(@product1)
506
+ order.add(@product2)
507
+ order.purchase!(:some => {:complicated => 'details', :in => 'a hash'})
508
+ ```
509
+
510
+ The one gotcha with the above two scenarios, is that when `purchase!` is called, the `Effective::Order` in question will run through its validations. These validations include:
511
+
512
+ - `validates_presence_of :billing_address` when configured to be required
513
+ - `validates_presence_of :shipping_address` when configured to be required
514
+ - `validates :user` which can be disabled via config initializer
515
+
516
+ - `validates_numericality_of :total, :greater_than_or_equal_to => minimum_charge` where minimum_charge is the configured value, once again from the initializer
517
+ - `validates_presence_of :order_items` the Order must have at least one OrderItem
518
+
519
+ You can skip validations with the following command, but be careful as this skips all validations:
520
+
521
+ ```ruby
522
+ Effective::Order.new(@product, @user).purchase!('no validations', :validate => false)
523
+ ```
524
+
525
+ The `@product` is now considered purchased.
526
+
527
+
528
+ To check an Order's purchase state, you can call `@order.purchased?`
529
+
530
+ There also exist the scopes: `Effective::Order.purchased` and `Effective::Order.purchased_by(user)` which return a chainable relation of all purchased `Effective::Order` objects.
531
+
532
+
533
+ ### My Purchases / Order History
534
+
535
+ This screen displays all past purchases made by the current user. You can add it to your site's main menu or User profile area:
536
+
537
+ ```ruby
538
+ = link_to_my_purchases() # To display My Purchases
539
+ ```
540
+
541
+ or
542
+
543
+ ```ruby
544
+ = link_to_my_purchases(:label => 'Order History', :class => 'btn btn-primary')
545
+ ```
546
+
547
+ or
548
+
549
+ ```ruby
550
+ = link_to 'My Order History', effective_orders.my_purchases_path
551
+ ```
552
+
553
+ or render it inline on an existing page with
554
+
555
+ ```ruby
556
+ render_order_history(user_or_orders)
557
+ ```
558
+
559
+ If a user is passed, a call to `Effective::Order.purchased_by(user)` will be made to assign all purchased orders.
560
+
561
+ Totally optional, but another way of displaying the Order History is to use the included datatable, based on [effective_datatables](https://github.com/code-and-effect/effective_datatables/)
562
+
563
+ In your controller:
564
+
565
+ ```ruby
566
+ @datatable = Effective::Datatables::Orders.new(:user_id => @user.id)
567
+ ```
568
+
569
+ and then in the view:
570
+
571
+ ```ruby
572
+ render_datatable @datatable
573
+ ```
574
+
575
+ Please refer to [effective_datatables](https://github.com/code-and-effect/effective_datatables/) for more information about that gem.
576
+
577
+
578
+ ### Admin Screen
579
+
580
+ To use the Admin screen, please also install the effective_datatables gem:
581
+
582
+ ```ruby
583
+ gem 'effective_datatables'
584
+ ```
585
+
586
+ Then you should be able to visit:
587
+
588
+ ```ruby
589
+ link_to 'Orders', effective_orders.admin_orders_path # /admin/orders
590
+ ```
591
+
592
+ or to create your own Datatable of all Orders, in your controller:
593
+
594
+ ```ruby
595
+ @datatable = Effective::Datatables::Orders.new()
596
+ ```
597
+
598
+ and then in the view:
599
+
600
+ ```ruby
601
+ render_datatable @datatable
602
+ ```
603
+
604
+
605
+ ## Testing in Development
606
+
607
+ Every payment processor seems to have its own development sandbox which allow you to make test purchases in development mode.
608
+
609
+ You will need an external IP address to work with these sandboxes.
610
+
611
+ We suggest the free application `https://ngrok.com/` for this ability.
612
+
613
+
614
+ ## Paying via Moneris
615
+
616
+ Use the following instructions to set up a Moneris TEST store.
617
+
618
+ The set up process is identical to a Production store, except that you will need to Sign Up with Moneris and get real credentials.
619
+
620
+ We are also going to use ngrok to give us a public facing URL
621
+
622
+ ### Create Test / Development Store
623
+
624
+ Visit https://esqa.moneris.com/mpg/ and login with: demouser / store1 / password
625
+
626
+ Select ADMIN -> hosted config from the menu
627
+
628
+ Click the 'Generate a New Configuration' button which should bring us to a "Hosted Paypage Configuration"
629
+
630
+ ### Basic Configuration
631
+
632
+ Description: 'My Test store'
633
+
634
+ Transaction Type: Purchase
635
+
636
+ Response Method: Sent to your server as a POST
637
+
638
+ Approved URL: http://4f972556.ngrok.com/orders/moneris_postback
639
+
640
+ Declined URL: http://4f972556.ngrok.com/orders/moneris_postback
641
+
642
+ Note: The Approved and Declined URLs must match the effective_orders.moneris_postback_path value in your application. By default it is /orders/moneris_postback
643
+
644
+ Click 'Save Changes'
645
+
646
+ ### PsStoreId and HppKey
647
+
648
+ At the top of the main 'Hosted Paypage Configuration' page should be the ps_store_id and hpp_key values.
649
+
650
+ Copy these two values into the appropriate lines of config/effective_orders.rb initializer file.
651
+
652
+ ```ruby
653
+ config.moneris_enabled = true
654
+
655
+ if Rails.env.production?
656
+ config.moneris = {
657
+ :ps_store_id => '',
658
+ :hpp_key => '',
659
+ :hpp_url => 'https://www3.moneris.com/HPPDP/index.php',
660
+ :verify_url => 'https://www3.moneris.com/HPPDP/verifyTxn.php',
661
+ :order_nudge => 0
662
+ }
663
+ else
664
+ config.moneris = {
665
+ :ps_store_id => 'VZ9BNtore1',
666
+ :hpp_key => 'hp1Y5J35GVDM',
667
+ :hpp_url => 'https://esqa.moneris.com/HPPDP/index.php',
668
+ :verify_url => 'https://esqa.moneris.com/HPPDP/verifyTxn.php',
669
+ :order_nudge => 0
670
+ }
671
+ end
672
+ ```
673
+
674
+ ### Paypage Appearance
675
+
676
+ Click 'Configure Appearance' from the main Hosted Paypage Configuration
677
+
678
+ Display item details: true
679
+
680
+ Display customer details: true
681
+
682
+ Display billing address details: true
683
+
684
+ Display shipping address details: true
685
+
686
+ Enable input of Billing, Shipping and extra fields: false
687
+
688
+ Display merchange name: true, if you have an SSL cert
689
+
690
+ Cancel Button Text: 'Cancel'
691
+
692
+ Cancel Button URL: http://4f972556.ngrok.com
693
+
694
+ Click 'Save Appearance Settings'
695
+
696
+ Click 'Return to main configuration'
697
+
698
+ ### Response Fields
699
+
700
+ None of the 'Return...' checkboxes are needed. Leave unchecked.
701
+
702
+ Perform asynchronous data post: false, unchecked
703
+
704
+ Async Response URL: leave blank
705
+
706
+ Click 'Save Response Settings'
707
+
708
+ Click 'Return to main configuration'
709
+
710
+
711
+ ### Security
712
+
713
+ Referring URL: Depends how you're using effective_orders in your application, you can add multiple URLs
714
+ By default, use http://4f972556.ngrok.com/orders/new
715
+
716
+ Enable Card Verification: false, unused
717
+
718
+ Enable Transaction Verification: true
719
+ Response Method: Displayed as key/value pairs on our server.
720
+ Response URL: leave blank
721
+
722
+ Click 'Save Verification Settings'
723
+ Click 'Return to main configuration'
724
+
725
+
726
+ ### Configure Email Receipts
727
+
728
+ effective_orders automatically sends its own receipts.
729
+
730
+ If you'd prefer to use the Moneris receipt, disable email sendouts from the config/effective_orders.rb initializer
731
+
732
+
733
+ ### Purchasing an Order through Moneris
734
+
735
+ With this test store set up, you can make a successful purchase with:
736
+
737
+ Cardholder Name: Any name
738
+ Credit Card Number: 4242 4242 4242 4242
739
+ Expiry Date: Any future date
740
+
741
+ Some gotchas:
742
+
743
+ 1. When using a test store, if your order total price is less than $10, the penny amount may be used to raise an error code.
744
+
745
+ Order totals ending in .00 will be Approved
746
+ Order totals ending in .05 will be Declined
747
+
748
+ And there's a whole bunch more. Please refer to:
749
+
750
+ https://www.google.ca/url?sa=t&rct=j&q=&esrc=s&source=web&cd=1&ved=0CB8QFjAA&url=https%3A%2F%2Fcartthrob.com%2F%3FACT%3D50%26fid%3D25%26aid%3D704_jvVKOeo1a8d3aSYeT3R4%26board_id%3D1&ei=_p1OVOysEpK_sQTZ8oDgCg&usg=AFQjCNHJGH_hEm4kUJAzkvKrzTqEpFnrgA&sig2=XJdE6PZoOY9habWH_B4uWA&bvm=bv.77880786,d.cWc&cad=rja
751
+
752
+ 2. Moneris will not process a duplicate order ID
753
+
754
+ Once Order id=1 has been purchased/declined, you will be unable to purchase an order with id=1 ever again.
755
+
756
+ This is what the moneris order_nudge configuration setting is used for.
757
+
758
+ You can set this to 1000 to start the IDs at 1+1000 instead of 1.
759
+
760
+
761
+ ## Paying via Stripe
762
+
763
+ First register for an account with Stripe
764
+
765
+ https://manage.stripe.com/register
766
+
767
+ and configure your bank accounts appropriately.
768
+
769
+ Then enable Stripe in the app/config/effective_orders.rb initializer and enter your keys.
770
+
771
+ ```ruby
772
+ config.stripe_enabled = true
773
+
774
+ config.stripe = {
775
+ :secret_key => 'sk_live_IKd6HDaYUfoRjflWQTXfFNfc',
776
+ :publishable_key => 'pk_live_liEGn9f0mcxKmoSjoeNbbuE1',
777
+ :currency => 'usd'
778
+ }
779
+ ```
780
+
781
+ You an find these keys from the Stripe Dashbaord -> Your Account (dropdown) -> Account Settings -> API Keys
782
+
783
+ You're ready to accept payments.
784
+
785
+ ### Stripe Connect
786
+
787
+ Stripe Connect allows effective_orders to collect payments on behalf of users.
788
+
789
+ First register your application with Stripe
790
+
791
+ Stripe Dashbaord -> Your Account (dropdown) -> Account Settings -> Apps
792
+
793
+ Register your application
794
+
795
+ Name: Your Application Name
796
+ Website URL: root_url
797
+
798
+ Development:
799
+
800
+ client_id: given by stripe, we need to record this.
801
+ redirect_uri: stripe_connect_redirect_uri_url # http://www.example.com/effective/orders/stripe_connect_redirect_uri
802
+ webhook_uri: none
803
+
804
+ And add these values to the app/config/effective_orders.rb initializer:
805
+
806
+ ```ruby
807
+ config.stripe = {
808
+ :secret_key => 'sk_live_IKd6HDaYUfoRjflWQTXfFNfc',
809
+ :publishable_key => 'pk_live_liEGn9f0mcxKmoSjoeNbbuE1',
810
+ :currency => 'usd',
811
+ :connect_client_id => 'ca_35jLok5G9kosyYF7quTOwcauJjTnUnud'
812
+ }
813
+ ```
814
+
815
+
816
+ ### Stripe Subscriptions
817
+
818
+ Subscriptions and Stripe Connect do not currently work together.
819
+
820
+ Register an additional Webhook, to accept Stripe subscription creation events from Stripe
821
+
822
+ root_url/webhooks/stripe
823
+
824
+
825
+ ### PayPal
826
+
827
+ TODO
828
+
829
+
830
+ ## License
831
+
832
+ MIT License. Copyright [Code and Effect Inc.](http://www.codeandeffect.com/)
833
+
834
+ Code and Effect is the product arm of [AgileStyle](http://www.agilestyle.com/), an Edmonton-based shop that specializes in building custom web applications with Ruby on Rails.
835
+
836
+
837
+ ## Testing
838
+
839
+ The test suite for this gem is mostly complete.
840
+
841
+ Run tests by:
842
+
843
+ ```ruby
844
+ guard
845
+ ```
846
+
847
+
848
+ ## Contributing
849
+
850
+ 1. Fork it
851
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
852
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
853
+ 4. Push to the branch (`git push origin my-new-feature`)
854
+ 5. Bonus points for test coverage
855
+ 6. Create new Pull Request
856
+