effective_orders 4.0.0beta5 → 4.0.0beta6
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.
- 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 } }
|