effective_orders 4.0.0beta5 → 4.0.0beta6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/assets/javascripts/effective_orders/customers.js.coffee +12 -14
- data/app/assets/javascripts/effective_orders/providers/stripe.js.coffee +1 -1
- data/app/assets/javascripts/effective_orders/subscriptions.js.coffee +20 -34
- data/app/controllers/effective/customers_controller.rb +10 -28
- data/app/controllers/effective/subscripter_controller.rb +23 -0
- data/app/controllers/effective/webhooks_controller.rb +3 -2
- data/app/helpers/effective_subscriptions_helper.rb +38 -44
- data/app/models/concerns/acts_as_subscribable.rb +0 -6
- data/app/models/effective/customer.rb +10 -14
- data/app/models/effective/subscripter.rb +82 -86
- data/app/models/effective/subscription.rb +1 -1
- data/app/views/effective/customers/_customer.html.haml +127 -138
- data/app/views/effective/customers/_form.html.haml +15 -6
- data/app/views/effective/customers/edit.html.haml +1 -0
- data/app/views/effective/customers/update.js.erb +5 -0
- data/app/views/effective/subscripter/_form.html.haml +11 -0
- data/app/views/effective/subscriptions/_subscription.html.haml +3 -0
- data/config/routes.rb +3 -1
- data/db/migrate/01_create_effective_orders.rb.erb +1 -0
- data/lib/effective_orders.rb +4 -20
- data/lib/effective_orders/version.rb +1 -1
- metadata +6 -4
- data/app/views/effective/customers/_fields.html.haml +0 -9
- data/app/views/effective/subscriptions/_fields.html.haml +0 -12
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 12e3f1de057495a5b6c3dc1b6cbc8127ef8736bf
|
4
|
+
data.tar.gz: d30298b8aac6de0afee57cc5e9af2552b01d5519
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9524eb918036d200c9d51f5c383e37a05afd43448dffb67e58d25bb43770a20f01e99ff4e09093f86d25d6506c28f7b859848d2c5bef6fe4ca8211b00d6f3b63
|
7
|
+
data.tar.gz: 020f1ae55841895de717445760e905c5e8640101d47ea05e3bc8327a805c5544cb844a02c70402a0d2e1144551de15e55b82edb2666190ababe141e253e0b1db
|
@@ -1,31 +1,29 @@
|
|
1
1
|
stripeCustomerChangeCardHandler = (key, form) ->
|
2
2
|
StripeCheckout.configure
|
3
3
|
key: key
|
4
|
-
closed: ->
|
4
|
+
closed: -> EffectiveForm.reset(form) unless form.hasClass('stripe-success')
|
5
5
|
token: (token, args) ->
|
6
6
|
if token.error
|
7
|
-
message = "An error ocurred when contacting Stripe. Your card has not been charged.
|
7
|
+
message = "An error ocurred when contacting Stripe. Your card has not been charged. Please refresh the page and try again. #{token.error.message}"
|
8
8
|
|
9
9
|
form.removeClass('stripe-success')
|
10
|
-
form.find('.
|
10
|
+
form.find('.invalid-feedback').html(message).show()
|
11
11
|
alert(message)
|
12
12
|
else
|
13
13
|
form.find("input[name$='[stripe_token]']").val('' + token['id'])
|
14
|
-
|
15
|
-
$customer = form.find('.effective-orders-customer')
|
16
|
-
$customer.find('.payment-status').html("**** **** **** #{token.card.last4} #{token.card.brand} #{token.card.exp_month}/#{token.card.exp_year}")
|
17
|
-
|
18
|
-
if $customer.data('submit')
|
19
|
-
form.addClass('stripe-success').submit()
|
14
|
+
form.addClass('stripe-success').submit() # Submits the form. As this is a remote form, submits via JS
|
20
15
|
|
21
16
|
# When we click 'Change credit card', make sure the form collects a credit card
|
22
|
-
$(document).on 'click',
|
23
|
-
event.preventDefault()
|
24
|
-
|
17
|
+
$(document).on 'click', ".effective-orders-stripe-update-card[type='submit']", (event) ->
|
25
18
|
$form = $(event.currentTarget).closest('form')
|
26
|
-
stripe = $(event.currentTarget).closest('.effective-orders-customer').data('stripe')
|
27
19
|
|
28
|
-
|
20
|
+
# Get stripe data payload
|
21
|
+
stripe = $form.data('stripe')
|
22
|
+
return unless stripe?
|
23
|
+
|
24
|
+
# Okay, we're good to call stripe
|
25
|
+
event.preventDefault()
|
26
|
+
EffectiveForm.submitting($form) # Disable and spin while we popup stripe
|
29
27
|
|
30
28
|
stripeCustomerChangeCardHandler(stripe.key, $form).open
|
31
29
|
image: stripe.image
|
@@ -1,7 +1,7 @@
|
|
1
1
|
stripeCheckoutHandler = (key, form) ->
|
2
2
|
StripeCheckout.configure
|
3
3
|
key: key
|
4
|
-
closed: -> EffectiveBootstrap.
|
4
|
+
closed: -> EffectiveBootstrap.reset(form) unless form.hasClass('stripe-success')
|
5
5
|
token: (token, args) ->
|
6
6
|
if token.error
|
7
7
|
alert("An error ocurred when contacting Stripe. Your card has not been charged. Please refresh the page and try again. #{token.error.message}")
|
@@ -1,56 +1,42 @@
|
|
1
1
|
stripeSubscriptionHandler = (key, form) ->
|
2
2
|
StripeCheckout.configure
|
3
3
|
key: key
|
4
|
-
closed: ->
|
4
|
+
closed: -> EffectiveForm.reset(form) unless form.hasClass('stripe-success')
|
5
5
|
token: (token, args) ->
|
6
6
|
if token.error
|
7
|
-
message = "An error ocurred when contacting Stripe. Your card has not been charged. Your
|
7
|
+
message = "An error ocurred when contacting Stripe. Your card has not been charged. Your plan has not changed. Please refresh the page and try again. #{token.error.message}"
|
8
8
|
|
9
9
|
form.removeClass('stripe-success')
|
10
|
-
form.find('.
|
10
|
+
form.find('.invalid-feedback').html(message).show()
|
11
11
|
alert(message)
|
12
12
|
else
|
13
13
|
form.find("input[name$='[stripe_token]']").val('' + token['id'])
|
14
|
-
form.addClass('stripe-success').submit()
|
14
|
+
form.addClass('stripe-success').submit() # Submits the form. As this is a remote form, submits via JS
|
15
15
|
|
16
|
-
#
|
17
|
-
$(document).on 'click', ".effective-orders-
|
18
|
-
event.preventDefault()
|
16
|
+
# Hijack submit and get a stripe token
|
17
|
+
$(document).on 'click', ".effective-orders-stripe-token-required[type='submit']", (event) ->
|
19
18
|
$form = $(event.currentTarget).closest('form')
|
20
19
|
|
21
|
-
# Get
|
22
|
-
|
23
|
-
|
24
|
-
return unless $plans.length > 0 && selected_plan_id.length > 0
|
20
|
+
# Get stripe data payload
|
21
|
+
stripe = $form.data('stripe')
|
22
|
+
return unless stripe?
|
25
23
|
|
26
|
-
|
24
|
+
# Make sure there is a plan selected
|
25
|
+
selected_plan_id = $form.find("input[name$='[stripe_plan_id]']:checked").val() || ''
|
26
|
+
return unless selected_plan_id.length > 0
|
27
27
|
|
28
|
-
|
28
|
+
# Match plan
|
29
29
|
plan = stripe.plans.find (plan, _) => plan.id == selected_plan_id
|
30
|
+
return unless plan?
|
31
|
+
|
32
|
+
# Okay, we're good to call stripe
|
33
|
+
event.preventDefault()
|
34
|
+
EffectiveForm.submitting($form) # Disable and spin while we popup stripe
|
30
35
|
|
31
36
|
stripeSubscriptionHandler(stripe.key, $form).open
|
32
37
|
image: stripe.image
|
33
38
|
name: stripe.name
|
34
|
-
description: plan.name
|
35
39
|
email: stripe.email
|
40
|
+
description: plan.name
|
36
41
|
amount: plan.amount
|
37
|
-
panelLabel: "{{amount}}
|
38
|
-
|
39
|
-
# When I click on a stripe plan ID radio button, add .effective-orders-subscripter-token-required to the form if required
|
40
|
-
$(document).on 'change', "input[name$='[subscripter][stripe_plan_id]']", (event) ->
|
41
|
-
$plan = $(event.currentTarget)
|
42
|
-
return unless $plan.is(':checked')
|
43
|
-
|
44
|
-
selected_plan_id = $plan.val()
|
45
|
-
|
46
|
-
$plans = $plan.closest('.effective-orders-stripe-plans').first()
|
47
|
-
plan = $plans.data('stripe').plans.find (plan, _) => plan.id == selected_plan_id
|
48
|
-
|
49
|
-
token_required = $plans.data('stripe').token_required
|
50
|
-
|
51
|
-
if (plan.amount || 0) > 0 && token_required
|
52
|
-
$plans.closest('form').find("input[type='submit'],button[type='submit']").addClass('effective-orders-subscripter-token-required')
|
53
|
-
else
|
54
|
-
$plans.closest('form').find("input[type='submit'],button[type='submit']").removeClass('effective-orders-subscripter-token-required')
|
55
|
-
|
56
|
-
true
|
42
|
+
panelLabel: "{{amount}}/#{plan.interval} Go!"
|
@@ -2,42 +2,24 @@ module Effective
|
|
2
2
|
class CustomersController < ApplicationController
|
3
3
|
layout (EffectiveOrders.layout.kind_of?(Hash) ? EffectiveOrders.layout[:customers] : EffectiveOrders.layout)
|
4
4
|
|
5
|
-
|
5
|
+
include Effective::CrudController
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
@customer = Effective::Customer.where(user: current_user).first!
|
10
|
-
EffectiveOrders.authorize!(self, :edit, @customer)
|
7
|
+
submit :save, 'Save', redirect: :back, success: -> { 'Successfully updated card.' }
|
8
|
+
page_title 'Customer Settings'
|
11
9
|
|
12
|
-
|
13
|
-
|
14
|
-
@
|
10
|
+
def resource
|
11
|
+
@customer ||= Effective::Customer.where(user: current_user).first!
|
12
|
+
@subscripter ||= Effective::Subscripter.new(customer: @customer, user: @customer.user)
|
15
13
|
end
|
16
14
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
@subscripter = Effective::Subscripter.new(customer: @customer, user: @customer.user)
|
22
|
-
@subscripter.assign_attributes(subscripter_params)
|
23
|
-
|
24
|
-
@page_title ||= "Customer #{current_user.to_s}"
|
25
|
-
|
26
|
-
if (@subscripter.save! rescue false)
|
27
|
-
flash[:success] = "Successfully updated customer settings"
|
28
|
-
redirect_to(effective_orders.customer_settings_path)
|
29
|
-
else
|
30
|
-
flash.now[:danger] = "Unable to update customer settings: #{@subscripter.errors.full_messages.to_sentence}"
|
31
|
-
render :edit
|
32
|
-
end
|
15
|
+
# I don't want save_resource to wrap my save in a transaction
|
16
|
+
def save_resource(resource, action)
|
17
|
+
resource.save!
|
33
18
|
end
|
34
19
|
|
35
|
-
private
|
36
|
-
|
37
20
|
# StrongParameters
|
38
|
-
def
|
21
|
+
def customer_params
|
39
22
|
params.require(:effective_subscripter).permit(:stripe_token)
|
40
23
|
end
|
41
|
-
|
42
24
|
end
|
43
25
|
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Effective
|
2
|
+
class SubscripterController < ApplicationController
|
3
|
+
layout (EffectiveOrders.layout.kind_of?(Hash) ? EffectiveOrders.layout[:subscriptions] : EffectiveOrders.layout)
|
4
|
+
|
5
|
+
include Effective::CrudController
|
6
|
+
|
7
|
+
submit :save, 'Save', redirect: :back, success: -> { 'Successfully updated plan.' }
|
8
|
+
|
9
|
+
def resource
|
10
|
+
@subscripter ||= Effective::Subscripter.new(user: current_user)
|
11
|
+
end
|
12
|
+
|
13
|
+
# I don't want save_resource to wrap my save in a transaction
|
14
|
+
def save_resource(resource, action)
|
15
|
+
resource.save!
|
16
|
+
end
|
17
|
+
|
18
|
+
# StrongParameters
|
19
|
+
def subscripter_params
|
20
|
+
params.require(:effective_subscripter).permit!
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -26,18 +26,19 @@ module Effective
|
|
26
26
|
|
27
27
|
when 'invoice.payment_succeeded'
|
28
28
|
customer = Effective::Customer.where(stripe_customer_id: @event.data.object.customer).first!
|
29
|
-
customer.update_attributes!(status:
|
29
|
+
customer.update_attributes!(status: EffectiveOrders::ACTIVE)
|
30
30
|
|
31
31
|
send_email(:subscription_payment_succeeded, customer)
|
32
32
|
when 'invoice.payment_failed'
|
33
33
|
customer = Effective::Customer.where(stripe_customer_id: @event.data.object.customer).first!
|
34
|
-
customer.update_attributes!(status:
|
34
|
+
customer.update_attributes!(status: EffectiveOrders::PAST_DUE)
|
35
35
|
|
36
36
|
send_email(:subscription_payment_failed, customer)
|
37
37
|
when 'customer.subscription.deleted'
|
38
38
|
customer = Effective::Customer.where(stripe_customer_id: @event.data.object.customer).first!
|
39
39
|
Effective::Subscription.where(customer: customer).destroy_all
|
40
40
|
customer.update_attributes!(stripe_subscription_id: nil, status: nil, active_card: nil)
|
41
|
+
customer.subscriptions.delete_all
|
41
42
|
|
42
43
|
send_email(:subscription_canceled, customer)
|
43
44
|
else
|
@@ -1,32 +1,19 @@
|
|
1
1
|
module EffectiveSubscriptionsHelper
|
2
2
|
|
3
|
-
def
|
3
|
+
def stripe_plans_collection(form)
|
4
4
|
raise 'expected an Effective::FormBuilder object' unless form.class.name == 'Effective::FormBuilder'
|
5
|
-
raise 'form object must be an Effective::Subscripter object' unless form.object.class.name == 'Effective::Subscripter'
|
6
5
|
|
7
|
-
|
8
|
-
|
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
|
6
|
+
subscripter = form.object
|
7
|
+
raise 'form object must be a subscripter object' unless subscripter.class.name == 'Effective::Subscripter'
|
21
8
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
include_trial = form.object.subscribable.trialing? if include_trial.nil?
|
9
|
+
plans = EffectiveOrders.stripe_plans.values.sort do |x, y|
|
10
|
+
amount = (x[:amount] <=> y[:amount])
|
11
|
+
(amount != 0) ? amount : x[:name] <=> y[:name]
|
12
|
+
end
|
27
13
|
|
28
|
-
|
29
|
-
|
14
|
+
if (existing = subscripter.customer.stripe_subscription_interval).present?
|
15
|
+
plans.select! { |plan| plan[:interval] == existing }
|
16
|
+
end
|
30
17
|
|
31
18
|
plans.map do |plan|
|
32
19
|
partial = (
|
@@ -49,27 +36,34 @@ module EffectiveSubscriptionsHelper
|
|
49
36
|
end
|
50
37
|
end
|
51
38
|
|
52
|
-
def
|
53
|
-
raise '
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
form: form,
|
60
|
-
label: label,
|
61
|
-
required: required,
|
62
|
-
include_trial: include_trial,
|
63
|
-
stripe: {
|
64
|
-
email: form.object.subscribable_buyer.email,
|
65
|
-
image: stripe_site_image_url,
|
66
|
-
key: EffectiveOrders.stripe[:publishable_key],
|
67
|
-
name: EffectiveOrders.stripe[:site_title],
|
68
|
-
plans: EffectiveOrders.stripe_plans.values,
|
69
|
-
token_required: form.object.subscripter.token_required?
|
70
|
-
},
|
71
|
-
}
|
72
|
-
)
|
39
|
+
def subscribable_form_with(subscribable)
|
40
|
+
raise 'form object must be an acts_as_subscribable object' unless subscribable.respond_to?(:subscripter)
|
41
|
+
|
42
|
+
subscripter = subscribable.subscripter
|
43
|
+
raise 'subscribable.subscribable_buyer must match current_user' unless subscripter.user == current_user
|
44
|
+
|
45
|
+
render('effective/subscripter/form', subscripter: subscripter)
|
73
46
|
end
|
74
47
|
|
48
|
+
def customer_form_with(customer)
|
49
|
+
raise 'form object must be an Effective::Customer object' unless customer.kind_of?(Effective::Customer)
|
50
|
+
raise 'expected customer user to match current user' if customer.user != current_user
|
51
|
+
|
52
|
+
subscripter = Effective::Subscripter.new(customer: customer, user: customer.user)
|
53
|
+
|
54
|
+
render('effective/customers/form', subscripter: subscripter)
|
55
|
+
end
|
56
|
+
|
57
|
+
def subscripter_stripe_data(subscripter)
|
58
|
+
{
|
59
|
+
email: current_user.email,
|
60
|
+
image: stripe_site_image_url,
|
61
|
+
key: EffectiveOrders.stripe[:publishable_key],
|
62
|
+
name: EffectiveOrders.stripe[:site_title],
|
63
|
+
plans: EffectiveOrders.stripe_plans.values
|
64
|
+
}
|
65
|
+
end
|
66
|
+
|
67
|
+
|
68
|
+
|
75
69
|
end
|
@@ -27,8 +27,6 @@ module ActsAsSubscribable
|
|
27
27
|
validates :trialing_until, presence: true, if: -> { EffectiveOrders.trial? }
|
28
28
|
validates :subscription_status, inclusion: { allow_nil: true, in: EffectiveOrders::STATUSES.keys }
|
29
29
|
|
30
|
-
validates :subscripter, associated: true
|
31
|
-
|
32
30
|
scope :trialing, -> { where(subscription_status: nil).where('trialing_until > ?', Time.zone.now) }
|
33
31
|
scope :trial_past_due, -> { where(subscription_status: nil).where('trialing_until < ?', Time.zone.now) }
|
34
32
|
scope :not_trialing, -> { where.not(subscription_status: nil) }
|
@@ -45,10 +43,6 @@ module ActsAsSubscribable
|
|
45
43
|
@_effective_subscripter ||= Effective::Subscripter.new(subscribable: self, user: subscribable_buyer)
|
46
44
|
end
|
47
45
|
|
48
|
-
def subscripter=(atts)
|
49
|
-
subscripter.assign_attributes(atts)
|
50
|
-
end
|
51
|
-
|
52
46
|
def subscribed?(stripe_plan_id = nil)
|
53
47
|
return false if subscription_status.blank?
|
54
48
|
stripe_plan_id ? (subscription&.stripe_plan_id == stripe_plan_id) : true
|
@@ -5,26 +5,23 @@ module Effective
|
|
5
5
|
attr_accessor :stripe_customer, :stripe_subscription
|
6
6
|
|
7
7
|
belongs_to :user
|
8
|
-
has_many :
|
9
|
-
|
10
|
-
has_many :subscriptions, class_name: 'Effective::Subscription', foreign_key: 'customer_id'
|
11
|
-
has_many :subscribables, through: :subscriptions, source: :subscribable
|
12
|
-
|
13
|
-
#accepts_nested_attributes_for :subscriptions
|
8
|
+
has_many :subscriptions, -> { includes(:subscribable) }, class_name: 'Effective::Subscription', foreign_key: 'customer_id'
|
9
|
+
accepts_nested_attributes_for :subscriptions
|
14
10
|
|
15
11
|
# Attributes
|
16
12
|
# stripe_customer_id :string # cus_xja7acoa03
|
17
13
|
# active_card :string # **** **** **** 4242 Visa 05/12
|
18
14
|
|
19
15
|
# stripe_subscription_id :string # Each user gets one stripe subscription object, which can contain many items
|
16
|
+
# stripe_subscription_interval :string
|
20
17
|
# status :string
|
21
18
|
|
22
19
|
# timestamps
|
23
20
|
|
24
21
|
scope :deep, -> { includes(subscriptions: :subscribable) }
|
25
22
|
|
26
|
-
|
27
|
-
subscriptions.each { |subscription| subscription.
|
23
|
+
after_commit(if: -> { stripe_subscription_id.present? }) do
|
24
|
+
subscriptions.each { |subscription| subscription.subscribable.update_column(:subscription_status, status) }
|
28
25
|
end
|
29
26
|
|
30
27
|
validates :user, presence: true
|
@@ -67,14 +64,13 @@ module Effective
|
|
67
64
|
def payment_status
|
68
65
|
if status == 'past_due'
|
69
66
|
'We ran into an error processing your last payment. Please update or confirm your card details to continue.'
|
70
|
-
elsif
|
71
|
-
|
72
|
-
elsif active_card.
|
73
|
-
'Thanks for your support! The card we have on file is'
|
74
|
-
else
|
67
|
+
elsif status == 'active'
|
68
|
+
"Your payment is in good standing. Thanks so much for your support!"
|
69
|
+
elsif active_card.blank?
|
75
70
|
'No credit card on file. Please add a card.'
|
71
|
+
else
|
72
|
+
'Please update or confirm your card details to continue.'
|
76
73
|
end.html_safe
|
77
74
|
end
|
78
|
-
|
79
75
|
end
|
80
76
|
end
|
@@ -5,36 +5,46 @@ module Effective
|
|
5
5
|
include ActiveModel::Model
|
6
6
|
|
7
7
|
attr_accessor :user, :subscribable, :customer
|
8
|
-
attr_accessor :stripe_plan_id, :
|
8
|
+
attr_accessor :subscribable_global_id, :stripe_token, :stripe_plan_id, :include_trial
|
9
9
|
|
10
10
|
validates :user, presence: true
|
11
|
-
validates :subscribable, presence: true, if: -> { stripe_plan_id }
|
12
|
-
validates :
|
11
|
+
validates :subscribable, presence: true, if: -> { stripe_plan_id.present? }
|
12
|
+
validates :customer, presence: true
|
13
13
|
|
14
|
-
|
15
|
-
if plan[:amount] > 0 && stripe_token.blank? && token_required?
|
16
|
-
self.errors.add(:stripe_token, 'updated payment card required')
|
17
|
-
customer.errors.add(:stripe_token, 'updated payment card required')
|
18
|
-
end
|
19
|
-
end
|
14
|
+
validates :stripe_plan_id, inclusion: { allow_blank: true, in: EffectiveOrders.stripe_plans.keys, message: 'unknown plan' }
|
20
15
|
|
21
|
-
validate(if: -> {
|
22
|
-
|
16
|
+
validate(if: -> { stripe_plan_id && plan && plan[:amount] > 0 }) do
|
17
|
+
self.errors.add(:stripe_token, 'updated payment card required') if stripe_token.blank? && token_required?
|
23
18
|
end
|
24
19
|
|
25
20
|
def customer
|
26
21
|
@customer ||= Effective::Customer.deep.where(user: user).first_or_initialize
|
27
22
|
end
|
28
23
|
|
24
|
+
def subscribable_global_id
|
25
|
+
subscribable&.to_global_id
|
26
|
+
end
|
27
|
+
|
28
|
+
def subscribable_global_id=(global_id)
|
29
|
+
@subscribable = GlobalID::Locator.locate(global_id)
|
30
|
+
end
|
31
|
+
|
32
|
+
def user_id=(id)
|
33
|
+
@user = User.find(id)
|
34
|
+
end
|
35
|
+
|
29
36
|
def current_plan
|
30
|
-
|
31
|
-
subscribable.subscription.blank? ? EffectiveOrders.stripe_plans['trial'] : subscribable.subscription.plan
|
37
|
+
subscribable&.subscription&.plan
|
32
38
|
end
|
33
39
|
|
34
40
|
def plan
|
35
41
|
EffectiveOrders.stripe_plans[stripe_plan_id]
|
36
42
|
end
|
37
43
|
|
44
|
+
def stripe_plan_id
|
45
|
+
@stripe_plan_id || (current_plan[:id] if current_plan)
|
46
|
+
end
|
47
|
+
|
38
48
|
def token_required?
|
39
49
|
customer.token_required?
|
40
50
|
end
|
@@ -44,71 +54,27 @@ module Effective
|
|
44
54
|
|
45
55
|
raise 'is invalid' unless valid?
|
46
56
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
self.errors.add(:base, e.message)
|
53
|
-
raise e
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
def subscribe!(stripe_plan_id)
|
58
|
-
self.stripe_plan_id = stripe_plan_id
|
59
|
-
save!
|
60
|
-
end
|
61
|
-
|
62
|
-
def destroy!
|
63
|
-
return true unless subscription && subscription.persisted? && customer.stripe_subscription.present?
|
64
|
-
|
65
|
-
raise 'is invalid' unless valid?
|
66
|
-
|
67
|
-
subscription.destroy!
|
68
|
-
customer.subscriptions.reload
|
69
|
-
|
70
|
-
sync! && customer.save!
|
71
|
-
end
|
72
|
-
|
73
|
-
def reload!
|
74
|
-
@stripe_token = nil
|
75
|
-
@stripe_plan_id = nil
|
76
|
-
@customer = nil
|
77
|
-
@subscription = nil
|
78
|
-
end
|
79
|
-
|
80
|
-
private
|
81
|
-
|
82
|
-
def subscription
|
83
|
-
return nil unless subscribable
|
84
|
-
@subscription ||= (
|
85
|
-
customer.subscriptions.find { |sub| sub.subscribable == subscribable } ||
|
86
|
-
customer.subscriptions.build(subscribable: subscribable, customer: customer)
|
87
|
-
)
|
57
|
+
create_customer!
|
58
|
+
create_stripe_token!
|
59
|
+
build_subscription!
|
60
|
+
sync_subscription!
|
61
|
+
true
|
88
62
|
end
|
89
63
|
|
90
|
-
|
91
|
-
# Check for an existing customer created within the last hour with this email
|
92
|
-
# This catches an edge case in which a stripe customer is created twice when a first time customer is using a declined card
|
93
|
-
if customer.stripe_customer.blank?
|
94
|
-
Rails.logger.info "STRIPE CUSTOMER CHECK FOR EXISTING: #{user.email}"
|
95
|
-
|
96
|
-
customers = Stripe::Customer.list(created: { gt: (user.created_at - 1.hour).to_i } ).data
|
97
|
-
|
98
|
-
if (existing = customers.find { |cus| cus.email == user.email && (cus.metadata || {})[:user_id] == user.id.to_s })
|
99
|
-
customer.stripe_customer = existing
|
100
|
-
customer.stripe_customer_id = existing.id
|
101
|
-
end
|
102
|
-
end
|
64
|
+
protected
|
103
65
|
|
104
|
-
|
66
|
+
# This should work even if the rest of the form doesn't. Careful with our transactions...
|
67
|
+
def create_customer!
|
105
68
|
if customer.stripe_customer.blank?
|
106
69
|
Rails.logger.info "STRIPE CUSTOMER CREATE: #{user.email}"
|
107
70
|
customer.stripe_customer = Stripe::Customer.create(email: user.email, description: user.to_s, metadata: { user_id: user.id })
|
108
71
|
customer.stripe_customer_id = customer.stripe_customer.id
|
72
|
+
customer.save!
|
109
73
|
end
|
74
|
+
end
|
110
75
|
|
111
|
-
|
76
|
+
# Update stripe customer card
|
77
|
+
def create_stripe_token!
|
112
78
|
if stripe_token.present?
|
113
79
|
Rails.logger.info "STRIPE CUSTOMER SOURCE UPDATE #{stripe_token}"
|
114
80
|
customer.stripe_customer.source = stripe_token
|
@@ -117,34 +83,46 @@ module Effective
|
|
117
83
|
if customer.stripe_customer.default_source.present?
|
118
84
|
card = customer.stripe_customer.sources.retrieve(customer.stripe_customer.default_source)
|
119
85
|
customer.active_card = "**** **** **** #{card.last4} #{card.brand} #{card.exp_month}/#{card.exp_year}"
|
86
|
+
customer.save!
|
120
87
|
end
|
121
88
|
end
|
89
|
+
end
|
122
90
|
|
123
|
-
|
124
|
-
|
125
|
-
|
91
|
+
def build_subscription!
|
92
|
+
return unless plan.present?
|
93
|
+
subscription.stripe_plan_id = plan[:id]
|
94
|
+
end
|
126
95
|
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
customer.stripe_subscription = Stripe::Subscription.create(customer: customer.stripe_customer_id, items: items(metadata: false), metadata: metadata)
|
131
|
-
customer.stripe_subscription_id = customer.stripe_subscription.id
|
132
|
-
end
|
133
|
-
end
|
96
|
+
def sync_subscription!
|
97
|
+
return unless plan.present?
|
98
|
+
customer.stripe_subscription.blank? ? create_subscription! : update_subscription!
|
134
99
|
|
135
|
-
|
100
|
+
customer.save!
|
101
|
+
end
|
102
|
+
|
103
|
+
def create_subscription!
|
104
|
+
return unless plan.present?
|
105
|
+
return if customer.stripe_subscription.present?
|
106
|
+
|
107
|
+
Rails.logger.info "STRIPE SUBSCRIPTION CREATE: #{items(metadata: false)}"
|
108
|
+
customer.stripe_subscription = Stripe::Subscription.create(customer: customer.stripe_customer_id, items: items(metadata: false), metadata: metadata)
|
109
|
+
customer.stripe_subscription_id = customer.stripe_subscription.id
|
110
|
+
|
111
|
+
customer.status = customer.stripe_subscription.status
|
112
|
+
customer.stripe_subscription_interval = customer.stripe_subscription.plan.interval
|
136
113
|
end
|
137
114
|
|
138
|
-
def
|
139
|
-
return
|
115
|
+
def update_subscription!
|
116
|
+
return unless plan.present?
|
117
|
+
return if customer.stripe_subscription.blank?
|
140
118
|
|
141
119
|
Rails.logger.info "STRIPE SUBSCRIPTION SYNC: #{customer.stripe_subscription_id} #{items}"
|
142
120
|
|
143
121
|
if items.length == 0
|
144
122
|
customer.stripe_subscription.delete
|
145
123
|
customer.stripe_subscription_id = nil
|
146
|
-
customer.status =
|
147
|
-
return
|
124
|
+
customer.status = EffectiveOrders::CANCELED
|
125
|
+
return
|
148
126
|
end
|
149
127
|
|
150
128
|
# Update stripe subscription items
|
@@ -189,14 +167,32 @@ module Effective
|
|
189
167
|
# Stripe::Invoice.create(customer: customer.stripe_customer_id).pay rescue false
|
190
168
|
# end
|
191
169
|
|
192
|
-
# Sync status
|
193
170
|
customer.status = customer.stripe_subscription.status
|
171
|
+
end
|
194
172
|
|
195
|
-
|
173
|
+
def subscribe!(stripe_plan_id)
|
174
|
+
self.stripe_plan_id = stripe_plan_id
|
175
|
+
save!
|
196
176
|
end
|
197
177
|
|
178
|
+
# def destroy!
|
179
|
+
# return true unless subscription && subscription.persisted? && customer.stripe_subscription.present?
|
180
|
+
|
181
|
+
# raise 'is invalid' unless valid?
|
182
|
+
|
183
|
+
# subscription.destroy!
|
184
|
+
# customer.subscriptions.reload
|
185
|
+
|
186
|
+
# sync! && customer.save!
|
187
|
+
# end
|
188
|
+
|
198
189
|
private
|
199
190
|
|
191
|
+
def subscription
|
192
|
+
return nil unless subscribable
|
193
|
+
customer.subscriptions.find { |sub| sub.subscribable == subscribable } || customer.subscriptions.build(subscribable: subscribable, customer: customer)
|
194
|
+
end
|
195
|
+
|
200
196
|
def items(metadata: true)
|
201
197
|
customer.subscriptions.group_by { |sub| sub.stripe_plan_id }.map do |plan, subscriptions|
|
202
198
|
if metadata
|
@@ -20,7 +20,7 @@ module Effective
|
|
20
20
|
validates :customer, presence: true
|
21
21
|
validates :subscribable, presence: true
|
22
22
|
|
23
|
-
validates :stripe_plan_id, presence: true, inclusion: { in: EffectiveOrders.stripe_plans.
|
23
|
+
validates :stripe_plan_id, presence: true, inclusion: { in: EffectiveOrders.stripe_plans.keys }
|
24
24
|
validates :name, presence: true
|
25
25
|
|
26
26
|
def to_s
|
@@ -1,145 +1,134 @@
|
|
1
|
-
%h3.effective-heading Customer
|
2
|
-
|
3
|
-
%table.table
|
4
|
-
%tbody
|
5
|
-
%tr
|
6
|
-
%th Id
|
7
|
-
%td= customer.stripe_customer.id
|
8
|
-
%tr
|
9
|
-
%th Email
|
10
|
-
%td= customer.stripe_customer.email
|
11
|
-
%tr
|
12
|
-
%th Card
|
13
|
-
%td
|
14
|
-
- if customer.stripe_customer.default_source.present?
|
15
|
-
- card = customer.stripe_customer.sources.retrieve(customer.stripe_customer.default_source)
|
16
|
-
= "**** **** **** #{card.last4} #{card.brand} #{card.exp_month}/#{card.exp_year}"
|
17
|
-
- else
|
18
|
-
None
|
19
|
-
|
20
|
-
- if customer.stripe_customer.currency.present?
|
21
|
-
%tr
|
22
|
-
%th Currency
|
23
|
-
%td= customer.stripe_customer.currency.to_s.upcase
|
24
|
-
|
25
|
-
- if customer.stripe_customer.account_balance.to_i > 0
|
26
|
-
%tr
|
27
|
-
%th Balance
|
28
|
-
%td= price_to_currency(customer.stripe_customer.account_balance.to_i)
|
29
|
-
|
30
|
-
= yield if block_given?
|
31
|
-
|
32
1
|
- if customer.stripe_subscription.present?
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
%tr
|
43
|
-
%th Coupon
|
44
|
-
%td= stripe_coupon_description(customer.stripe_subscription.discount.coupon)
|
45
|
-
|
46
|
-
- if customer.stripe_subscription.start.present?
|
47
|
-
%tr
|
48
|
-
%th Started
|
49
|
-
%td= Time.zone.at(customer.stripe_subscription.start).strftime('%F')
|
50
|
-
|
51
|
-
- if customer.stripe_subscription.ended_at.present?
|
52
|
-
%tr
|
53
|
-
%th Ended
|
54
|
-
%td= Time.zone.at(customer.stripe_subscription.ended_at).strftime('%F')
|
55
|
-
|
56
|
-
- if customer.stripe_subscription.canceled_at.present?
|
57
|
-
%tr
|
58
|
-
%th Cancelled
|
59
|
-
%td= Time.zone.at(customer.stripe_subscription.canceled_at).strftime('%F')
|
60
|
-
|
61
|
-
- if customer.stripe_subscription.current_period_start.present?
|
62
|
-
%tr
|
63
|
-
%th Current Period Start
|
64
|
-
%td= Time.zone.at(customer.stripe_subscription.current_period_start).strftime('%F')
|
65
|
-
|
66
|
-
- if customer.stripe_subscription.current_period_end.present?
|
67
|
-
%tr
|
68
|
-
%th Current Period End
|
69
|
-
%td= Time.zone.at(customer.stripe_subscription.current_period_end).strftime('%F')
|
70
|
-
|
71
|
-
- if customer.stripe_subscription.metadata.present? && false
|
72
|
-
%tr
|
73
|
-
%th Metadata
|
74
|
-
%td= tableize_hash(customer.stripe_subscription.metadata.to_h, th: false)
|
75
|
-
|
76
|
-
- if customer.stripe_subscription.items.present?
|
77
|
-
%tr
|
78
|
-
%th Plans
|
79
|
-
%td= tableize_hash(customer.stripe_subscription.items.inject({}) { |h, item| h[item.plan.nickname] = item.quantity; h }, th: false)
|
80
|
-
|
81
|
-
- if customer.subscriptions.present?
|
82
|
-
%table.table
|
83
|
-
%thead
|
84
|
-
%tr
|
85
|
-
%th Subscribed
|
86
|
-
%th Plan
|
87
|
-
%th
|
88
|
-
%tbody
|
89
|
-
- customer.subscriptions.each do |sub|
|
2
|
+
.card.my-4
|
3
|
+
.card-header Subscription
|
4
|
+
.card-body
|
5
|
+
%table.table
|
6
|
+
%tbody
|
7
|
+
%tr
|
8
|
+
%th Status
|
9
|
+
%td= customer.stripe_subscription.status.presence || 'unknown'
|
10
|
+
|
90
11
|
%tr
|
91
|
-
%
|
92
|
-
%td=
|
93
|
-
%td= link_to 'change', edit_polymorphic_path(sub.subscribable)
|
12
|
+
%th Email
|
13
|
+
%td= customer.stripe_customer.email
|
94
14
|
|
95
|
-
|
96
|
-
|
97
|
-
|
15
|
+
%tr
|
16
|
+
%th Card
|
17
|
+
%td
|
18
|
+
- if customer.stripe_customer.default_source.present?
|
19
|
+
- card = customer.stripe_customer.sources.retrieve(customer.stripe_customer.default_source)
|
20
|
+
= "**** **** **** #{card.last4} #{card.brand} #{card.exp_month}/#{card.exp_year}"
|
21
|
+
- else
|
22
|
+
None
|
23
|
+
|
24
|
+
%tr
|
25
|
+
%th Currency
|
26
|
+
%td= customer.stripe_customer.currency.to_s.upcase
|
27
|
+
|
28
|
+
- if customer.stripe_subscription.discount.present?
|
29
|
+
%tr
|
30
|
+
%th Coupon
|
31
|
+
%td= stripe_coupon_description(customer.stripe_subscription.discount.coupon)
|
32
|
+
|
33
|
+
- if customer.stripe_subscription.start.present?
|
34
|
+
%tr
|
35
|
+
%th Started
|
36
|
+
%td= Time.zone.at(customer.stripe_subscription.start).strftime('%F')
|
37
|
+
|
38
|
+
- if customer.stripe_subscription.ended_at.present?
|
39
|
+
%tr
|
40
|
+
%th Ended
|
41
|
+
%td= Time.zone.at(customer.stripe_subscription.ended_at).strftime('%F')
|
42
|
+
|
43
|
+
- if customer.stripe_subscription.canceled_at.present?
|
44
|
+
%tr
|
45
|
+
%th Cancelled
|
46
|
+
%td= Time.zone.at(customer.stripe_subscription.canceled_at).strftime('%F')
|
47
|
+
|
48
|
+
- if customer.stripe_subscription.current_period_start.present?
|
49
|
+
%tr
|
50
|
+
%th Current Period Start
|
51
|
+
%td= Time.zone.at(customer.stripe_subscription.current_period_start).strftime('%F')
|
52
|
+
|
53
|
+
- if customer.stripe_subscription.current_period_end.present?
|
54
|
+
%tr
|
55
|
+
%th Current Period End
|
56
|
+
%td= Time.zone.at(customer.stripe_subscription.current_period_end).strftime('%F')
|
57
|
+
|
58
|
+
- if customer.stripe_subscription.metadata.present? && false
|
59
|
+
%tr
|
60
|
+
%th Metadata
|
61
|
+
%td= tableize_hash(customer.stripe_subscription.metadata.to_h, th: false)
|
62
|
+
|
63
|
+
- if customer.stripe_subscription.items.present?
|
64
|
+
%tr
|
65
|
+
%th Plans
|
66
|
+
%td= tableize_hash(customer.stripe_subscription.items.inject({}) { |h, item| h[item.plan.nickname] = item.quantity; h }, th: false)
|
67
|
+
|
68
|
+
- if customer.subscriptions.present?
|
69
|
+
.card.my-4
|
70
|
+
.card-header Subscribed
|
71
|
+
.card-body
|
72
|
+
%table.table
|
73
|
+
%thead
|
74
|
+
%tr
|
75
|
+
%th Name
|
76
|
+
%th Plan
|
77
|
+
%th
|
78
|
+
%tbody
|
79
|
+
- customer.subscriptions.each do |sub|
|
80
|
+
%tr
|
81
|
+
%td= sub.subscribable
|
82
|
+
%td= sub
|
98
83
|
|
99
84
|
- if customer.stripe_customer.invoices.present?
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
%
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
85
|
+
.card.my-4
|
86
|
+
.card-header Invoices
|
87
|
+
.card-body
|
88
|
+
%table.table
|
89
|
+
%thead
|
90
|
+
%tr
|
91
|
+
%th Date
|
92
|
+
%th Invoice
|
93
|
+
%th Total
|
94
|
+
%tbody
|
95
|
+
- customer.stripe_customer.invoices.each do |invoice|
|
96
|
+
%tr
|
97
|
+
%td= Time.zone.at(invoice.date).strftime('%F')
|
98
|
+
%td
|
99
|
+
%p
|
100
|
+
= invoice.id
|
101
|
+
%br
|
102
|
+
= Time.zone.at(invoice.lines.first.period.start).strftime('%F')
|
103
|
+
to
|
104
|
+
= Time.zone.at(invoice.lines.first.period.end).strftime('%F')
|
105
|
+
|
106
|
+
%p
|
107
|
+
- invoice.lines.each do |line|
|
108
|
+
= line.description
|
109
|
+
%br
|
110
|
+
|
111
|
+
%td= price_to_currency(invoice.total)
|
124
112
|
|
125
113
|
- if customer.upcoming_invoice.present?
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
114
|
+
.card.my-4
|
115
|
+
.card-header Upcoming Invoice
|
116
|
+
.card-body
|
117
|
+
%table.table
|
118
|
+
%tbody
|
119
|
+
%tr
|
120
|
+
%th Date
|
121
|
+
%td= Time.zone.at(customer.upcoming_invoice.date).strftime('%F')
|
122
|
+
%tr
|
123
|
+
%th Items
|
124
|
+
%td
|
125
|
+
%table.table
|
126
|
+
%tbody
|
127
|
+
- customer.upcoming_invoice.lines.each do |line|
|
128
|
+
%tr
|
129
|
+
%td #{Time.zone.at(line.period.start).strftime('%F')} to #{Time.zone.at(line.period.end).strftime('%F')}
|
130
|
+
%td= line.description
|
131
|
+
%td= price_to_currency(line.amount)
|
132
|
+
%tr
|
133
|
+
%th Total
|
134
|
+
%td= price_to_currency(customer.upcoming_invoice.total)
|
@@ -1,12 +1,21 @@
|
|
1
1
|
= javascript_include_tag 'https://checkout.stripe.com/checkout.js'
|
2
2
|
|
3
|
-
|
4
|
-
|
3
|
+
.card
|
4
|
+
.card-header Customer Summary
|
5
|
+
.card-body
|
6
|
+
= effective_form_with(model: subscripter, url: effective_orders.customer_settings_path, remote: true, data: { stripe: subscripter_stripe_data(subscripter) }) do |f|
|
7
|
+
= f.hidden_field :stripe_token, value: nil
|
8
|
+
= f.error :stripe_token
|
5
9
|
|
6
|
-
|
7
|
-
|
8
|
-
|
10
|
+
%p
|
11
|
+
All subscription and billing notifications are sent to:
|
12
|
+
%strong= f.object.user.email
|
9
13
|
|
10
|
-
|
14
|
+
- if f.object.customer.active_card.present?
|
15
|
+
%p
|
16
|
+
All charges are made to
|
17
|
+
%strong= f.object.customer.active_card
|
11
18
|
|
19
|
+
%p= f.object.customer.payment_status
|
12
20
|
|
21
|
+
= f.submit 'Update Card', border: false, left: true, class: 'effective-orders-stripe-update-card'
|
@@ -0,0 +1,5 @@
|
|
1
|
+
<% resource = (@_effective_resource || Effective::Resource.new(controller_path)) %>
|
2
|
+
<% @resource = instance_variable_get('@' + resource.name) if resource.name %>
|
3
|
+
|
4
|
+
EffectiveForm.remote_form_payload = $("<div><%= j render_resource_form(resource, subscripter: @subscripter) %></div>");
|
5
|
+
EffectiveForm.remote_form_flash = <%= raw flash.to_json %>;
|
@@ -0,0 +1,11 @@
|
|
1
|
+
= javascript_include_tag 'https://checkout.stripe.com/checkout.js'
|
2
|
+
|
3
|
+
= effective_form_with(model: subscripter, url: effective_orders.subscripter_path, remote: true, data: { stripe: subscripter_stripe_data(subscripter) }) do |f|
|
4
|
+
= f.hidden_field :user_id, value: current_user.id
|
5
|
+
= f.hidden_field :subscribable_global_id
|
6
|
+
= f.hidden_field :stripe_token, value: nil
|
7
|
+
= f.error :stripe_token
|
8
|
+
|
9
|
+
= f.radios :stripe_plan_id, stripe_plans_collection(f), cards: true, label: false, required: true
|
10
|
+
|
11
|
+
= f.submit 'Choose this Plan', center: true, class: ('effective-orders-stripe-token-required' if f.object.token_required?)
|
data/config/routes.rb
CHANGED
@@ -25,8 +25,10 @@ EffectiveOrders::Engine.routes.draw do
|
|
25
25
|
post 'orders/:id', to: 'orders#update'
|
26
26
|
|
27
27
|
if EffectiveOrders.subscriptions?
|
28
|
+
match 'subscribe', to: 'subscripter#update', via: :post, as: :subscripter
|
29
|
+
|
28
30
|
match 'customer/settings', to: 'customers#edit', as: :customer_settings, via: [:get]
|
29
|
-
match 'customer/settings', to: 'customers#update', via: [:patch, :put]
|
31
|
+
match 'customer/settings', to: 'customers#update', via: [:patch, :put, :post]
|
30
32
|
match 'webhooks/stripe', to: 'webhooks#stripe', via: [:post, :put]
|
31
33
|
end
|
32
34
|
|
data/lib/effective_orders.rb
CHANGED
@@ -13,8 +13,9 @@ module EffectiveOrders
|
|
13
13
|
# Subscription statuses (as per stripe)
|
14
14
|
ACTIVE = 'active'.freeze
|
15
15
|
PAST_DUE = 'past_due'.freeze
|
16
|
+
CANCELED = 'canceled'.freeze
|
16
17
|
|
17
|
-
STATUSES = { ACTIVE => ACTIVE, PAST_DUE => PAST_DUE }
|
18
|
+
STATUSES = { ACTIVE => ACTIVE, PAST_DUE => PAST_DUE, CANCELED => CANCELED }
|
18
19
|
|
19
20
|
# The following are all valid config keys
|
20
21
|
mattr_accessor :orders_table_name
|
@@ -83,7 +84,7 @@ module EffectiveOrders
|
|
83
84
|
end
|
84
85
|
|
85
86
|
def self.authorize!(controller, action, resource)
|
86
|
-
raise Effective::AccessDenied unless authorized?(controller, action, resource)
|
87
|
+
raise Effective::AccessDenied.new('Access Denied', action, resource) unless authorized?(controller, action, resource)
|
87
88
|
end
|
88
89
|
|
89
90
|
def self.permitted_params
|
@@ -175,35 +176,18 @@ module EffectiveOrders
|
|
175
176
|
#{ }"tiers":null,"tiers_mode":null,"transform_usage":null,"trial_period_days":null,"usage_type":"licensed"},
|
176
177
|
|
177
178
|
plans = Stripe::Plan.all.inject({}) do |h, plan|
|
178
|
-
occurrence = case plan.interval
|
179
|
-
when 'daily' ; '/day'
|
180
|
-
when 'weekly' ; '/week'
|
181
|
-
when 'monthly' ; '/month'
|
182
|
-
when 'yearly' ; '/year'
|
183
|
-
when 'day' ; plan.interval_count == 1 ? '/day' : " every #{plan.interval_count} days"
|
184
|
-
when 'week' ; plan.interval_count == 1 ? '/week' : " every #{plan.interval_count} weeks"
|
185
|
-
when 'month' ; plan.interval_count == 1 ? '/month' : " every #{plan.interval_count} months"
|
186
|
-
when 'year' ; plan.interval_count == 1 ? '/year' : " every #{plan.interval_count} years"
|
187
|
-
else ; plan.interval
|
188
|
-
end
|
189
|
-
|
190
179
|
h[plan.id] = {
|
191
180
|
id: plan.id,
|
192
181
|
product_id: plan.product,
|
193
182
|
name: plan.nickname,
|
194
183
|
amount: plan.amount,
|
195
184
|
currency: plan.currency,
|
196
|
-
description: "$#{'%0.2f' % (plan.amount / 100.0)} #{plan.currency.upcase}
|
197
|
-
occurrence: "#{occurrence}",
|
185
|
+
description: "$#{'%0.2f' % (plan.amount / 100.0)} #{plan.currency.upcase}/#{plan.interval}",
|
198
186
|
interval: plan.interval,
|
199
187
|
interval_count: plan.interval_count
|
200
188
|
}; h
|
201
189
|
end
|
202
190
|
|
203
|
-
if trial?
|
204
|
-
plans['trial'] = { id: 'trial', amount: 0, name: trial.fetch(:name), description: trial.fetch(:description) }
|
205
|
-
end
|
206
|
-
|
207
191
|
plans
|
208
192
|
)
|
209
193
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: effective_orders
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 4.0.
|
4
|
+
version: 4.0.0beta6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Code and Effect
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-04-
|
11
|
+
date: 2018-04-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -140,6 +140,7 @@ files:
|
|
140
140
|
- app/controllers/effective/providers/pretend.rb
|
141
141
|
- app/controllers/effective/providers/refund.rb
|
142
142
|
- app/controllers/effective/providers/stripe.rb
|
143
|
+
- app/controllers/effective/subscripter_controller.rb
|
143
144
|
- app/controllers/effective/webhooks_controller.rb
|
144
145
|
- app/datatables/effective_customers_datatable.rb
|
145
146
|
- app/datatables/effective_order_items_datatable.rb
|
@@ -183,9 +184,9 @@ files:
|
|
183
184
|
- app/views/effective/carts/_cart_actions.html.haml
|
184
185
|
- app/views/effective/carts/show.html.haml
|
185
186
|
- app/views/effective/customers/_customer.html.haml
|
186
|
-
- app/views/effective/customers/_fields.html.haml
|
187
187
|
- app/views/effective/customers/_form.html.haml
|
188
188
|
- app/views/effective/customers/edit.html.haml
|
189
|
+
- app/views/effective/customers/update.js.erb
|
189
190
|
- app/views/effective/orders/_checkout_actions.html.haml
|
190
191
|
- app/views/effective/orders/_checkout_step1.html.haml
|
191
192
|
- app/views/effective/orders/_checkout_step2.html.haml
|
@@ -226,8 +227,9 @@ files:
|
|
226
227
|
- app/views/effective/orders_mailer/subscription_payment_succeeded.html.haml
|
227
228
|
- app/views/effective/orders_mailer/subscription_trial_expired.html.haml
|
228
229
|
- app/views/effective/orders_mailer/subscription_trial_expiring.html.haml
|
229
|
-
- app/views/effective/
|
230
|
+
- app/views/effective/subscripter/_form.html.haml
|
230
231
|
- app/views/effective/subscriptions/_plan.html.haml
|
232
|
+
- app/views/effective/subscriptions/_subscription.html.haml
|
231
233
|
- app/views/layouts/effective_orders_mailer_layout.html.haml
|
232
234
|
- config/effective_orders.rb
|
233
235
|
- config/routes.rb
|
@@ -1,9 +0,0 @@
|
|
1
|
-
.effective-orders-customer{ data: { stripe: stripe.to_json, submit: submit.to_json } }
|
2
|
-
.card
|
3
|
-
.card-header Credit Card
|
4
|
-
.card-body
|
5
|
-
= f.static_field :stripe_token, label: false, class: 'payment-status', value: f.object.customer.payment_status
|
6
|
-
|
7
|
-
%p= link_to 'Update Card Details', '#', class: 'btn btn-primary btn-change-card'
|
8
|
-
|
9
|
-
%p.invalid-feedback
|
@@ -1,12 +0,0 @@
|
|
1
|
-
= javascript_include_tag 'https://checkout.stripe.com/checkout.js'
|
2
|
-
|
3
|
-
= form.fields_for :subscripter, form.object.subscripter do |fs|
|
4
|
-
= fs.hidden_field :stripe_token, value: nil
|
5
|
-
|
6
|
-
- fs.object.stripe_plan_id ||= (fs.object.current_plan || {})[:id]
|
7
|
-
|
8
|
-
= fs.radios :stripe_plan_id, stripe_plans_collection(fs, include_trial: include_trial),
|
9
|
-
label: label,
|
10
|
-
required: required,
|
11
|
-
cards: true,
|
12
|
-
wrapper: { class: 'effective-orders-stripe-plans', data: { stripe: stripe } }
|