effective_orders 2.2.4 → 3.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 (150) hide show
  1. checksums.yaml +4 -4
  2. data/MIT-LICENSE +1 -1
  3. data/README.md +124 -84
  4. data/app/assets/javascripts/effective_orders/customers.js.coffee +39 -0
  5. data/app/assets/javascripts/effective_orders/providers/{stripe_charges.js.coffee → stripe.js.coffee} +15 -13
  6. data/app/assets/javascripts/effective_orders/subscriptions.js.coffee +73 -0
  7. data/app/assets/stylesheets/effective_orders.scss +2 -1
  8. data/app/assets/stylesheets/effective_orders/_order.scss +16 -8
  9. data/app/assets/stylesheets/effective_orders/_subscriptions.scss +14 -0
  10. data/app/controllers/admin/customers_controller.rb +11 -8
  11. data/app/controllers/admin/order_items_controller.rb +4 -8
  12. data/app/controllers/admin/orders_controller.rb +133 -87
  13. data/app/controllers/effective/carts_controller.rb +18 -8
  14. data/app/controllers/effective/concerns/purchase.rb +39 -0
  15. data/app/controllers/effective/customers_controller.rb +43 -0
  16. data/app/controllers/effective/orders_controller.rb +73 -119
  17. data/app/controllers/effective/providers/app_checkout.rb +3 -1
  18. data/app/controllers/effective/providers/ccbill.rb +4 -6
  19. data/app/controllers/effective/providers/cheque.rb +20 -11
  20. data/app/controllers/effective/providers/free.rb +33 -0
  21. data/app/controllers/effective/providers/mark_as_paid.rb +33 -0
  22. data/app/controllers/effective/providers/moneris.rb +9 -17
  23. data/app/controllers/effective/providers/paypal.rb +4 -6
  24. data/app/controllers/effective/providers/pretend.rb +4 -4
  25. data/app/controllers/effective/providers/refund.rb +39 -0
  26. data/app/controllers/effective/providers/stripe.rb +19 -40
  27. data/app/controllers/effective/providers/stripe_connect.rb +2 -6
  28. data/app/controllers/effective/webhooks_controller.rb +44 -95
  29. data/app/datatables/effective_customers_datatable.rb +21 -29
  30. data/app/datatables/effective_order_items_datatable.rb +77 -79
  31. data/app/datatables/effective_orders_datatable.rb +67 -57
  32. data/app/helpers/effective_carts_helper.rb +17 -14
  33. data/app/helpers/effective_orders_helper.rb +40 -56
  34. data/app/helpers/effective_paypal_helper.rb +3 -3
  35. data/app/helpers/effective_stripe_helper.rb +47 -18
  36. data/app/helpers/effective_subscriptions_helper.rb +79 -0
  37. data/app/mailers/effective/orders_mailer.rb +125 -2
  38. data/app/models/concerns/acts_as_purchasable.rb +23 -33
  39. data/app/models/concerns/acts_as_subscribable.rb +68 -0
  40. data/app/models/concerns/acts_as_subscribable_buyer.rb +22 -0
  41. data/app/models/effective/cart.rb +53 -24
  42. data/app/models/effective/cart_item.rb +6 -12
  43. data/app/models/effective/customer.rb +51 -54
  44. data/app/models/effective/order.rb +160 -147
  45. data/app/models/effective/order_item.rb +18 -21
  46. data/app/models/effective/product.rb +7 -7
  47. data/app/models/effective/providers/ccbill_postback.rb +1 -1
  48. data/app/models/effective/providers/stripe_charge.rb +8 -19
  49. data/app/models/effective/subscripter.rb +230 -0
  50. data/app/models/effective/subscription.rb +27 -76
  51. data/app/models/effective/tax_rate_calculator.rb +10 -7
  52. data/app/views/admin/customers/_actions.html.haml +1 -2
  53. data/app/views/admin/customers/index.html.haml +1 -1
  54. data/app/views/admin/customers/show.html.haml +6 -0
  55. data/app/views/admin/orders/_actions.html.haml +9 -7
  56. data/app/views/admin/orders/_form.html.haml +11 -7
  57. data/app/views/admin/orders/_order_actions.html.haml +2 -1
  58. data/app/views/admin/orders/_order_item_fields.html.haml +1 -1
  59. data/app/views/admin/orders/edit.html.haml +4 -0
  60. data/app/views/admin/orders/index.html.haml +1 -4
  61. data/app/views/admin/orders/new.html.haml +1 -1
  62. data/app/views/admin/orders/show.html.haml +5 -6
  63. data/app/views/effective/carts/_cart.html.haml +2 -2
  64. data/app/views/effective/carts/show.html.haml +2 -2
  65. data/app/views/effective/customers/_customer.html.haml +152 -0
  66. data/app/views/effective/customers/_fields.html.haml +12 -0
  67. data/app/views/effective/customers/_form.html.haml +13 -0
  68. data/app/views/effective/customers/edit.html.haml +3 -0
  69. data/app/views/effective/orders/_checkout_step1.html.haml +8 -15
  70. data/app/views/effective/orders/_checkout_step2.html.haml +34 -21
  71. data/app/views/effective/orders/_order.html.haml +8 -9
  72. data/app/views/effective/orders/_order_actions.html.haml +7 -8
  73. data/app/views/effective/orders/_order_header.html.haml +1 -1
  74. data/app/views/effective/orders/_order_items.html.haml +11 -5
  75. data/app/views/effective/orders/_order_note.html.haml +4 -7
  76. data/app/views/effective/orders/_orders_table.html.haml +26 -26
  77. data/app/views/effective/orders/app_checkout/_form.html.haml +2 -2
  78. data/app/views/effective/orders/ccbill/_form.html.haml +1 -1
  79. data/app/views/effective/orders/cheque/_form.html.haml +3 -1
  80. data/app/views/effective/orders/declined.html.haml +1 -1
  81. data/app/views/effective/orders/{checkout_step1.html.haml → edit.html.haml} +0 -0
  82. data/app/views/effective/orders/free/_form.html.haml +4 -0
  83. data/app/views/effective/orders/index.html.haml +2 -4
  84. data/app/views/effective/orders/mark_as_paid/_form.html.haml +32 -0
  85. data/app/views/effective/orders/moneris/_form.html.haml +6 -6
  86. data/app/views/effective/orders/{checkout_step2.html.haml → new.html.haml} +1 -1
  87. data/app/views/effective/orders/paypal/_form.html.haml +2 -2
  88. data/app/views/effective/orders/pretend/_form.html.haml +2 -2
  89. data/app/views/effective/orders/purchased.html.haml +3 -0
  90. data/app/views/effective/orders/refund/_form.html.haml +32 -0
  91. data/app/views/effective/orders/show.html.haml +4 -1
  92. data/app/views/effective/orders/stripe/_form.html.haml +5 -5
  93. data/app/views/effective/orders_mailer/subscription_canceled.html.haml +9 -0
  94. data/app/views/effective/orders_mailer/subscription_payment_failed.html.haml +9 -0
  95. data/app/views/effective/orders_mailer/subscription_payment_succeeded.html.haml +9 -0
  96. data/app/views/effective/orders_mailer/subscription_trial_expired.html.haml +5 -0
  97. data/app/views/effective/orders_mailer/subscription_trial_expiring.html.haml +7 -0
  98. data/app/views/effective/subscriptions/_fields.html.haml +16 -0
  99. data/app/views/effective/subscriptions/_plan.html.haml +21 -0
  100. data/app/views/layouts/effective_orders_mailer_layout.html.haml +6 -8
  101. data/config/effective_orders.rb +41 -20
  102. data/config/routes.rb +48 -48
  103. data/db/migrate/01_create_effective_orders.rb.erb +19 -5
  104. data/lib/effective_orders.rb +78 -42
  105. data/lib/effective_orders/engine.rb +36 -82
  106. data/lib/effective_orders/version.rb +1 -1
  107. data/lib/generators/effective_orders/install_generator.rb +2 -2
  108. data/lib/generators/templates/effective_orders_mailer_preview.rb +39 -4
  109. data/lib/tasks/effective_orders_tasks.rake +42 -0
  110. data/spec/controllers/carts_controller_spec.rb +1 -1
  111. data/spec/controllers/moneris_orders_controller_spec.rb +4 -4
  112. data/spec/controllers/orders_controller_spec.rb +4 -4
  113. data/spec/controllers/stripe_orders_controller_spec.rb +2 -2
  114. data/spec/controllers/webhooks_controller_spec.rb +1 -1
  115. data/spec/dummy/config/initializers/effective_orders.rb +1 -7
  116. data/spec/dummy/db/schema.rb +1 -0
  117. data/spec/dummy/db/test.sqlite3 +0 -0
  118. data/spec/dummy/log/test.log +3 -0
  119. data/spec/models/acts_as_purchasable_spec.rb +0 -56
  120. data/spec/models/customer_spec.rb +3 -3
  121. data/spec/models/order_spec.rb +2 -2
  122. data/spec/spec_helper.rb +1 -1
  123. data/spec/support/factories.rb +2 -1
  124. metadata +37 -49
  125. data/active_admin/effective_carts.rb +0 -14
  126. data/active_admin/effective_orders.rb +0 -112
  127. data/app/assets/javascripts/effective_orders/providers/stripe_subscriptions.js.coffee +0 -28
  128. data/app/controllers/concerns/acts_as_active_admin_controller.rb +0 -69
  129. data/app/controllers/effective/subscriptions_controller.rb +0 -126
  130. data/app/models/effective/datatables/customers.rb +0 -40
  131. data/app/models/effective/datatables/order_items.rb +0 -101
  132. data/app/models/effective/datatables/orders.rb +0 -91
  133. data/app/models/inputs/price_field.rb +0 -63
  134. data/app/models/inputs/price_form_input.rb +0 -7
  135. data/app/models/inputs/price_formtastic_input.rb +0 -9
  136. data/app/models/inputs/price_input.rb +0 -19
  137. data/app/models/inputs/price_simple_form_input.rb +0 -8
  138. data/app/views/admin/orders/_form_mark_as_paid.html.haml +0 -33
  139. data/app/views/admin/orders/_order_payment_details.html.haml +0 -5
  140. data/app/views/admin/orders/mark_as_paid.html.haml +0 -7
  141. data/app/views/effective/orders/stripe/_subscription_fields.html.haml +0 -7
  142. data/app/views/effective/subscriptions/index.html.haml +0 -22
  143. data/app/views/effective/subscriptions/new.html.haml +0 -9
  144. data/app/views/effective/subscriptions/show.html.haml +0 -49
  145. data/db/upgrade/02_upgrade_effective_orders_from03x.rb.erb +0 -29
  146. data/db/upgrade/03_upgrade_effective_orders_from1x.rb.erb +0 -98
  147. data/db/upgrade/upgrade_price_column_on_table.rb.erb +0 -17
  148. data/lib/generators/effective_orders/upgrade_from03x_generator.rb +0 -31
  149. data/lib/generators/effective_orders/upgrade_from1x_generator.rb +0 -27
  150. data/lib/generators/effective_orders/upgrade_price_column_generator.rb +0 -30
@@ -1,8 +1,8 @@
1
1
  module EffectiveOrdersHelper
2
- def price_to_currency(price, options = {})
3
- raise 'price_to_currency expects an Integer representing the number of cents in a price' unless price.kind_of?(Integer)
4
- options[:precision] ||= 2
5
- number_to_currency(price / 100.0, options)
2
+ def price_to_currency(price)
3
+ price = price || 0
4
+ raise 'price_to_currency expects an Integer representing the number of cents' unless price.kind_of?(Integer)
5
+ number_to_currency(price / 100.0)
6
6
  end
7
7
 
8
8
  def tax_rate_to_percentage(tax_rate, options = {})
@@ -33,11 +33,15 @@ module EffectiveOrdersHelper
33
33
  end
34
34
 
35
35
  def order_checkout_label(processor = nil)
36
- return 'Checkout' if (EffectiveOrders.single_payment_processor? && processor != :pretend)
36
+ return 'Checkout' if (EffectiveOrders.single_payment_processor? && ![:pretend, :mark_as_paid, :free, :refund].include?(processor))
37
37
 
38
38
  case processor
39
+ when :mark_as_paid
40
+ 'Mark as paid'
39
41
  when :free
40
- 'Checkout'
42
+ 'Checkout free'
43
+ when :refund
44
+ 'Complete refund'
41
45
  when :moneris, :stripe, :ccbill
42
46
  'Checkout with credit card'
43
47
  when :paypal
@@ -45,7 +49,7 @@ module EffectiveOrdersHelper
45
49
  when :pretend
46
50
  EffectiveOrders.allow_pretend_purchase_in_production ? 'Purchase Order' : 'Purchase Order (development only)'
47
51
  when :cheque
48
- 'Pay by Cheque'
52
+ 'Pay by cheque'
49
53
  when :app_checkout
50
54
  EffectiveOrders.app_checkout[:checkout_label].presence || 'Checkout'
51
55
  else
@@ -82,27 +86,38 @@ module EffectiveOrdersHelper
82
86
  end
83
87
 
84
88
  def render_order(order)
85
- render(partial: 'effective/orders/order', locals: {order: order})
89
+ render(partial: 'effective/orders/order', locals: { order: order })
86
90
  end
87
91
 
88
- def render_checkout_step1(order, opts = {})
89
- raise ArgumentError.new('unable to checkout an order without a user') unless order.user.present?
92
+ def render_checkout_step1(order, namespace: nil, purchased_url: nil, declined_url: nil)
93
+ raise 'unable to checkout an order without a user' unless order && order.user
90
94
 
91
- locals = { purchased_redirect_url: nil, declined_redirect_url: nil }.merge(opts)
95
+ locals = { order: order, purchased_url: purchased_url, declined_url: declined_url, namespace: namespace }
92
96
 
93
- render(partial: 'effective/orders/checkout_step1', locals: locals.merge({order: order}))
97
+ render partial: 'effective/orders/checkout_step1', locals: locals
94
98
  end
95
99
  alias_method :render_checkout, :render_checkout_step1
96
100
 
97
- def render_checkout_step2(order, opts = {})
98
- raise ArgumentError.new('unable to checkout an order without a user') unless order.user.present?
101
+ def render_checkout_step2(order, namespace: nil, purchased_url: nil, declined_url: nil)
102
+ raise 'unable to checkout an order without a user' unless order && order.user
99
103
 
100
- locals = { purchased_redirect_url: nil, declined_redirect_url: nil }.merge(opts)
104
+ locals = { order: order, purchased_url: purchased_url, declined_url: declined_url, namespace: namespace }
101
105
 
102
106
  if order.new_record? || !order.valid?
103
- render(partial: 'effective/orders/checkout_step1', locals: locals.merge({order: order}))
107
+ render(partial: 'effective/orders/checkout_step1', locals: locals)
108
+ else
109
+ render(partial: 'effective/orders/checkout_step2', locals: locals)
110
+ end
111
+ end
112
+
113
+ def checkout_step1_form_url(order, namespace = nil)
114
+ raise 'expected an order' unless order
115
+ raise 'invalid namespace, expecting nil or :admin' unless [nil, :admin].include?(namespace)
116
+
117
+ if order.new_record?
118
+ namespace == nil ? effective_orders.orders_path : effective_orders.admin_orders_path
104
119
  else
105
- render(partial: 'effective/orders/checkout_step2', locals: locals.merge({order: order}))
120
+ namespace == nil ? effective_orders.order_path(order) : effective_orders.checkout_admin_order_path(order)
106
121
  end
107
122
  end
108
123
 
@@ -116,56 +131,25 @@ module EffectiveOrdersHelper
116
131
  label = options.delete(:label)
117
132
  options[:class] = ((options[:class] || '') + ' btn-my-purchases')
118
133
 
119
- link_to(label, effective_orders.my_purchases_path, options)
134
+ link_to(label, effective_orders.my_purchases_orders_path, options)
120
135
  end
121
136
  alias_method :link_to_order_history, :link_to_my_purchases
122
137
 
123
- def render_orders(user_or_orders, opts = {})
124
- if user_or_orders.kind_of?(User)
125
- orders = Effective::Order.purchased_by(user_or_orders)
126
- elsif user_or_orders.respond_to?(:to_a)
127
- orders = user_or_orders.to_a
128
- else
129
- raise ArgumentError.new('expecting an instance of User or an array/collection of Effective::Order objects')
138
+
139
+ def render_orders(obj, opts = {})
140
+ orders = Array(obj.kind_of?(User) ? Effective::Order.purchased_by(obj) : obj)
141
+
142
+ if orders.any? { |order| order.kind_of?(Effective::Order) == false }
143
+ raise 'expected a User or Effective::Order'
130
144
  end
131
145
 
132
- render(partial: 'effective/orders/orders_table', locals: {orders: orders}.merge(opts))
146
+ render(partial: 'effective/orders/orders_table', locals: { orders: orders }.merge(opts))
133
147
  end
134
148
 
135
149
  alias_method :render_purchases, :render_orders
136
150
  alias_method :render_my_purchases, :render_orders
137
151
  alias_method :render_order_history, :render_orders
138
152
 
139
- # Used by the _payment_details partial
140
- def tableize_order_payment(hash, options = {class: 'table table-bordered'})
141
- if hash.present? && hash.kind_of?(Hash)
142
- content_tag(:table, class: options[:class]) do
143
- title = options.delete(:title)
144
-
145
- content = content_tag(:tbody) do
146
- hash.map do |k, v|
147
- content_tag(:tr) do
148
- content_tag((options[:th] ? :th : :td), k) +
149
- content_tag(:td) do
150
- if v.kind_of?(Hash)
151
- tableize_order_payment(v, options.merge(th: (options.key?(:sub_th) ? options[:sub_th] : options[:th])))
152
- elsif v.kind_of?(Array)
153
- '[' + v.join(', ') + ']'
154
- else
155
- v
156
- end
157
- end
158
- end
159
- end.join.html_safe
160
- end
161
-
162
- title.blank? ? content : (content_tag(:thead) { content_tag(:tr) { content_tag(:th, title, colspan: 2) } } + content)
163
- end
164
- else
165
- hash.to_s.html_safe
166
- end
167
- end
168
-
169
153
  def payment_card_label(card)
170
154
  card = card.to_s.downcase.gsub(' ', '').strip
171
155
 
@@ -15,9 +15,9 @@ module EffectivePaypalHelper
15
15
  APP_KEY_PEM = ConfigReader.cert_or_key(:app_key)
16
16
 
17
17
  def paypal_encrypted_payload(order)
18
- raise ArgumentError.new("unable to read EffectiveOrders PayPal paypal_cert #{EffectiveOrders.paypal[:paypal_cert]}") unless PAYPAL_CERT_PEM.present?
19
- raise ArgumentError.new("unable to read EffectiveOrders PayPal app_cert #{EffectiveOrders.paypal[:app_cert]}") unless APP_CERT_PEM.present?
20
- raise ArgumentError.new("unable to read EffectiveOrders PayPal app_key #{EffectiveOrders.paypal[:app_key]}") unless APP_KEY_PEM.present?
18
+ raise "unable to read EffectiveOrders PayPal paypal_cert #{EffectiveOrders.paypal[:paypal_cert]}" unless PAYPAL_CERT_PEM.present?
19
+ raise "unable to read EffectiveOrders PayPal app_cert #{EffectiveOrders.paypal[:app_cert]}" unless APP_CERT_PEM.present?
20
+ raise "unable to read EffectiveOrders PayPal app_key #{EffectiveOrders.paypal[:app_key]}" unless APP_KEY_PEM.present?
21
21
 
22
22
  values = {
23
23
  business: EffectiveOrders.paypal[:seller_email],
@@ -4,13 +4,13 @@ module EffectiveStripeHelper
4
4
  STRIPE_CONNECT_TOKEN_URL = 'https://connect.stripe.com/oauth/token'
5
5
 
6
6
  def is_stripe_connect_seller?(user)
7
- Effective::Customer.for_user(user).try(:is_stripe_connect_seller?) == true
7
+ Effective::Customer.for_buyer(user).stripe_connect_access_token.present?
8
8
  end
9
9
 
10
10
  def link_to_new_stripe_connect_customer(opts = {})
11
11
  client_id = EffectiveOrders.stripe[:connect_client_id]
12
12
 
13
- raise ArgumentError.new('effective_orders config: stripe.connect_client_id has not been set') unless client_id.present?
13
+ raise 'effective_orders config: stripe.connect_client_id has not been set' unless client_id.present?
14
14
 
15
15
  authorize_params = {
16
16
  response_type: :code,
@@ -26,28 +26,35 @@ module EffectiveStripeHelper
26
26
  stripe_user_params = opts.delete :stripe_user
27
27
  authorize_params.merge!({stripe_user: stripe_user_params}) if stripe_user_params.is_a?(Hash)
28
28
 
29
- authorize_url = STRIPE_CONNECT_AUTHORIZE_URL.chomp('/') + '?' + authorize_params.to_query
29
+ authorize_url = STRIPE_CONNECT_AUTHORIZE_URL + '?' + authorize_params.to_query
30
30
  options = {}.merge(opts)
31
31
  link_to image_tag('/assets/effective_orders/stripe_connect.png'), authorize_url, options
32
32
  end
33
33
 
34
- ### Subscriptions Helpers
35
- def stripe_plans_collection(plans)
36
- (plans || []).map { |plan| [stripe_plan_description(plan), plan.id, {'data-amount' => plan.amount}] }
37
- end
34
+ def stripe_plan_description(obj)
35
+ plan = (
36
+ case obj
37
+ when Hash ; obj
38
+ when ::Stripe::Plan ; EffectiveOrders.stripe_plans.find { |plan| plan.id == obj.id }
39
+ else ; raise 'unexpected object'
40
+ end
41
+ )
38
42
 
39
- def stripe_plan_description(plan)
40
- occurrence = case plan.interval
41
- when 'weekly' ; '/week'
42
- when 'monthly' ; '/month'
43
- when 'yearly' ; '/year'
44
- when 'week' ; plan.interval_count == 1 ? '/week' : " every #{plan.interval_count} weeks"
45
- when 'month' ; plan.interval_count == 1 ? '/month' : " every #{plan.interval_count} months"
46
- when 'year' ; plan.interval_count == 1 ? '/year' : " every #{plan.interval_count} years"
47
- else ; plan.interval
48
- end
43
+ raise("unknown stripe plan: #{obj}") unless plan.kind_of?(Hash) && plan[:id].present?
44
+
45
+ plan[:description]
46
+ end
49
47
 
50
- "#{plan.name} - #{ActionController::Base.helpers.price_to_currency(plan.amount)} #{plan.currency.upcase}#{occurrence}"
48
+ def stripe_invoice_line_description(line, simple: false)
49
+ [
50
+ "#{line.quantity}x",
51
+ line.plan.name,
52
+ price_to_currency(line.amount),
53
+ ("#{Time.zone.at(line.period.start).strftime('%F')}" unless simple),
54
+ ('to' unless simple),
55
+ ("#{Time.zone.at(line.period.end).strftime('%F')}" unless simple),
56
+ line.description.presence
57
+ ].compact.join(' ')
51
58
  end
52
59
 
53
60
  def stripe_coupon_description(coupon)
@@ -60,4 +67,26 @@ module EffectiveStripeHelper
60
67
  end
61
68
  end
62
69
 
70
+ def stripe_site_image_url
71
+ return nil unless EffectiveOrders.stripe && (url = EffectiveOrders.stripe[:site_image].to_s).present?
72
+ url.start_with?('http') ? url : asset_url(url)
73
+ end
74
+
75
+ def stripe_order_description(order)
76
+ "#{order.num_items} items (#{price_to_currency(order.total)})"
77
+ end
78
+
79
+ def stripe_charge_data(order)
80
+ {
81
+ stripe: {
82
+ key: EffectiveOrders.stripe[:publishable_key],
83
+ name: EffectiveOrders.stripe[:site_title],
84
+ image: stripe_site_image_url,
85
+ email: order.user.email,
86
+ amount: order.total,
87
+ description: stripe_order_description(order)
88
+ }.to_json
89
+ }
90
+ end
91
+
63
92
  end
@@ -0,0 +1,79 @@
1
+ module EffectiveSubscriptionsHelper
2
+
3
+ def effective_customer_fields(form, submit: true)
4
+ raise 'expected a SimpleForm::FormBuilder object' unless form.class.name == 'SimpleForm::FormBuilder'
5
+ raise 'form object must be an Effective::Subscripter object' unless form.object.class.name == 'Effective::Subscripter'
6
+
7
+ render(
8
+ partial: 'effective/customers/fields',
9
+ locals: {
10
+ f: form,
11
+ submit: submit,
12
+ stripe: {
13
+ email: form.object.customer.user.email,
14
+ image: stripe_site_image_url,
15
+ key: EffectiveOrders.stripe[:publishable_key],
16
+ name: EffectiveOrders.stripe[:site_title],
17
+ }
18
+ }
19
+ )
20
+ end
21
+
22
+ def stripe_plans_collection(form, include_trial: nil, selected_class: 'selected panel-primary')
23
+ raise 'expected a SimpleForm::FormBuilder object' unless form.class.name == 'SimpleForm::FormBuilder'
24
+ raise 'form object must be an acts_as_subscribable object' unless form.object.subscribable.subscripter.present?
25
+
26
+ include_trial = form.object.subscribable.subscribed?('trial') if include_trial.nil?
27
+
28
+ plans = include_trial ? EffectiveOrders.stripe_plans : EffectiveOrders.stripe_plans.except('trial')
29
+ plans = plans.values.sort { |x, y| (amount = x[:amount] <=> y[:amount]) != 0 ? amount : x[:name] <=> y[:name] }
30
+
31
+ plans.map do |plan|
32
+ partial = (
33
+ if lookup_context.template_exists?("effective/subscriptions/#{plan[:id].downcase}", [], true)
34
+ "effective/subscriptions/#{plan[:id].downcase}" # Render the app's views/effective/subscriptions/_gold.html.haml
35
+ else
36
+ 'effective/subscriptions/plan' # Render effective_orders default plan panel
37
+ end
38
+ )
39
+
40
+ content = render(partial: partial, locals: {
41
+ f: form,
42
+ plan: plan,
43
+ selected: Array(form.object.stripe_plan_id).include?(plan[:id]),
44
+ selected_class: selected_class,
45
+ subscribable: form.object.subscribable,
46
+ subscribed: form.object.subscribable.subscribed?(plan[:id])
47
+ })
48
+
49
+ [content, plan[:id]]
50
+ end
51
+ end
52
+
53
+ def effective_subscription_fields(form, label: false, required: true, include_trial: nil, item_wrapper_class: 'col-sm-6 col-md-4 col-lg-3', selected_class: 'selected panel-primary', wrapper_class: 'row')
54
+ raise 'expected a SimpleForm::FormBuilder object' unless form.class.name == 'SimpleForm::FormBuilder'
55
+ raise 'form object must be an acts_as_subscribable object' unless form.object.subscripter.present?
56
+
57
+ render(
58
+ partial: 'effective/subscriptions/fields',
59
+ locals: {
60
+ form: form,
61
+ label: label,
62
+ required: required,
63
+ include_trial: include_trial,
64
+ item_wrapper_class: item_wrapper_class,
65
+ selected_class: selected_class,
66
+ stripe: {
67
+ email: form.object.buyer.email,
68
+ image: stripe_site_image_url,
69
+ key: EffectiveOrders.stripe[:publishable_key],
70
+ name: EffectiveOrders.stripe[:site_title],
71
+ plans: EffectiveOrders.stripe_plans.values,
72
+ token_required: form.object.subscripter.token_required?
73
+ },
74
+ wrapper_class: wrapper_class
75
+ }
76
+ )
77
+ end
78
+
79
+ end
@@ -5,6 +5,8 @@ module Effective
5
5
  layout EffectiveOrders.mailer[:layout].presence || 'effective_orders_mailer_layout'
6
6
 
7
7
  def order_receipt_to_admin(order_param)
8
+ return true unless EffectiveOrders.mailer[:send_order_receipt_to_admin]
9
+
8
10
  @order = (order_param.kind_of?(Effective::Order) ? order_param : Effective::Order.find(order_param))
9
11
 
10
12
  mail(
@@ -15,6 +17,8 @@ module Effective
15
17
  end
16
18
 
17
19
  def order_receipt_to_buyer(order_param) # Buyer
20
+ return true unless EffectiveOrders.mailer[:send_order_receipt_to_buyer]
21
+
18
22
  @order = (order_param.kind_of?(Effective::Order) ? order_param : Effective::Order.find(order_param))
19
23
 
20
24
  mail(
@@ -25,6 +29,8 @@ module Effective
25
29
  end
26
30
 
27
31
  def order_receipt_to_seller(order_param, seller, order_items)
32
+ return true unless EffectiveOrders.mailer[:send_order_receipt_to_seller]
33
+
28
34
  @order = (order_param.kind_of?(Effective::Order) ? order_param : Effective::Order.find(order_param))
29
35
  @user = seller.user
30
36
  @order_items = order_items
@@ -40,6 +46,8 @@ module Effective
40
46
  # This is sent when an admin creates a new order or /admin/orders/new
41
47
  # Or uses the order action Send Payment Request
42
48
  def payment_request_to_buyer(order_param)
49
+ return true unless EffectiveOrders.mailer[:send_payment_request_to_buyer]
50
+
43
51
  @order = (order_param.kind_of?(Effective::Order) ? order_param : Effective::Order.find(order_param))
44
52
 
45
53
  mail(
@@ -51,6 +59,8 @@ module Effective
51
59
 
52
60
  # This is sent when someone chooses to Pay by Cheque
53
61
  def pending_order_invoice_to_buyer(order_param)
62
+ return true unless EffectiveOrders.mailer[:send_pending_order_invoice_to_buyer]
63
+
54
64
  @order = (order_param.kind_of?(Effective::Order) ? order_param : Effective::Order.find(order_param))
55
65
 
56
66
  mail(
@@ -60,6 +70,71 @@ module Effective
60
70
  )
61
71
  end
62
72
 
73
+ # Sent by the invoice.payment_succeeded webhook event
74
+ def subscription_payment_succeeded(customer_param)
75
+ return true unless EffectiveOrders.mailer[:send_subscription_payment_succeeded]
76
+
77
+ @customer = (customer_param.kind_of?(Effective::Customer) ? customer_param : Effective::Customer.find(customer_param))
78
+
79
+ mail(
80
+ to: @customer.user.email,
81
+ from: EffectiveOrders.mailer[:default_from],
82
+ subject: subject_for_subscription_payment_succeeded(@customer)
83
+ )
84
+ end
85
+
86
+ # Sent by the invoice.payment_failed webhook event
87
+ def subscription_payment_failed(customer_param)
88
+ return true unless EffectiveOrders.mailer[:send_subscription_payment_failed]
89
+
90
+ @customer = (customer_param.kind_of?(Effective::Customer) ? customer_param : Effective::Customer.find(customer_param))
91
+
92
+ mail(
93
+ to: @customer.user.email,
94
+ from: EffectiveOrders.mailer[:default_from],
95
+ subject: subject_for_subscription_payment_failed(@customer)
96
+ )
97
+ end
98
+
99
+ # Sent by the invoice.payment_failed webhook event
100
+ def subscription_canceled(customer_param)
101
+ return true unless EffectiveOrders.mailer[:send_subscription_canceled]
102
+
103
+ @customer = (customer_param.kind_of?(Effective::Customer) ? customer_param : Effective::Customer.find(customer_param))
104
+
105
+ mail(
106
+ to: @customer.user.email,
107
+ from: EffectiveOrders.mailer[:default_from],
108
+ subject: subject_for_subscription_canceled(@customer)
109
+ )
110
+ end
111
+
112
+ # Sent by the effective_orders:notify_trial_users rake task.
113
+ def subscription_trial_expiring(subscribable)
114
+ return true unless EffectiveOrders.mailer[:send_subscription_trial_expiring]
115
+
116
+ @subscribable = subscribable
117
+
118
+ mail(
119
+ to: @subscribable.buyer.email,
120
+ from: EffectiveOrders.mailer[:default_from],
121
+ subject: subject_for_subscription_trial_expiring(@subscribable)
122
+ )
123
+ end
124
+
125
+ # Sent by the effective_orders:notify_trial_users rake task.
126
+ def subscription_trial_expired(subscribable)
127
+ return true unless EffectiveOrders.mailer[:send_subscription_trial_expired]
128
+
129
+ @subscribable = subscribable
130
+
131
+ mail(
132
+ to: @subscribable.buyer.email,
133
+ from: EffectiveOrders.mailer[:default_from],
134
+ subject: subject_for_subscription_trial_expired(@subscribable)
135
+ )
136
+ end
137
+
63
138
  def order_error(order: nil, error: nil, to: nil, from: nil, subject: nil, template: 'order_error')
64
139
  if order.present?
65
140
  @order = (order.kind_of?(Effective::Order) ? order : Effective::Order.find(order))
@@ -77,7 +152,6 @@ module Effective
77
152
  ) do |format|
78
153
  format.html { render(template) }
79
154
  end
80
-
81
155
  end
82
156
 
83
157
  private
@@ -103,7 +177,7 @@ module Effective
103
177
  end
104
178
 
105
179
  def subject_for_order_receipt_to_seller(order, order_items, seller)
106
- string_or_callable = EffectiveOrders.mailer[:subject_for_seller_receipt]
180
+ string_or_callable = EffectiveOrders.mailer[:subject_for_order_receipt_to_seller]
107
181
 
108
182
  if string_or_callable.respond_to?(:call) # This is a Proc or a function, not a string
109
183
  string_or_callable = self.instance_exec(order, order_items, seller, &string_or_callable)
@@ -132,6 +206,55 @@ module Effective
132
206
  prefix_subject(string_or_callable.presence || "Pending Order: ##{order.to_param}")
133
207
  end
134
208
 
209
+ def subject_for_subscription_payment_succeeded(customer)
210
+ string_or_callable = EffectiveOrders.mailer[:subject_for_subscription_payment_succeeded]
211
+
212
+ if string_or_callable.respond_to?(:call) # This is a Proc or a function, not a string
213
+ string_or_callable = self.instance_exec(order, &string_or_callable)
214
+ end
215
+
216
+ prefix_subject(string_or_callable.presence || 'Thank you for your payment')
217
+ end
218
+
219
+ def subject_for_subscription_payment_failed(customer)
220
+ string_or_callable = EffectiveOrders.mailer[:subject_for_subscription_payment_failed]
221
+
222
+ if string_or_callable.respond_to?(:call) # This is a Proc or a function, not a string
223
+ string_or_callable = self.instance_exec(order, &string_or_callable)
224
+ end
225
+
226
+ prefix_subject(string_or_callable.presence || 'Payment failed - please update your card details')
227
+ end
228
+
229
+ def subject_for_subscription_canceled(customer)
230
+ string_or_callable = EffectiveOrders.mailer[:subject_for_subscription_canceled]
231
+
232
+ if string_or_callable.respond_to?(:call) # This is a Proc or a function, not a string
233
+ string_or_callable = self.instance_exec(order, &string_or_callable)
234
+ end
235
+
236
+ prefix_subject(string_or_callable.presence || 'Subscription canceled')
237
+ end
238
+
239
+ def subject_for_subscription_trial_expiring(customer)
240
+ string_or_callable = EffectiveOrders.mailer[:subject_for_subscription_trial_expiring]
241
+
242
+ if string_or_callable.respond_to?(:call) # This is a Proc or a function, not a string
243
+ string_or_callable = self.instance_exec(order, &string_or_callable)
244
+ end
245
+
246
+ prefix_subject(string_or_callable.presence || 'Trial expiring soon')
247
+ end
248
+
249
+ def subject_for_subscription_trial_expired(customer)
250
+ string_or_callable = EffectiveOrders.mailer[:subject_for_subscription_trial_expired]
251
+
252
+ if string_or_callable.respond_to?(:call) # This is a Proc or a function, not a string
253
+ string_or_callable = self.instance_exec(order, &string_or_callable)
254
+ end
255
+
256
+ prefix_subject(string_or_callable.presence || 'Trial expired')
257
+ end
135
258
 
136
259
  def prefix_subject(text)
137
260
  prefix = (EffectiveOrders.mailer[:subject_prefix].to_s rescue '')