disco_app 0.8.7 → 0.8.8
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/controllers/disco_app/admin/concerns/plans_controller.rb +51 -0
- data/app/controllers/disco_app/admin/plans_controller.rb +3 -0
- data/app/controllers/disco_app/charges_controller.rb +37 -20
- data/app/controllers/disco_app/concerns/app_proxy_controller.rb +1 -1
- data/app/controllers/disco_app/concerns/authenticated_controller.rb +26 -12
- data/app/controllers/disco_app/concerns/carrier_request_controller.rb +1 -1
- data/app/controllers/disco_app/install_controller.rb +20 -19
- data/app/controllers/disco_app/subscriptions_controller.rb +40 -0
- data/app/controllers/disco_app/webhooks_controller.rb +1 -1
- data/app/controllers/sessions_controller.rb +22 -0
- data/app/jobs/disco_app/concerns/app_installed_job.rb +21 -4
- data/app/jobs/disco_app/concerns/app_uninstalled_job.rb +2 -5
- data/app/jobs/disco_app/concerns/subscription_changed_job.rb +7 -0
- data/app/jobs/disco_app/subscription_changed_job.rb +3 -0
- data/app/models/disco_app/application_charge.rb +18 -0
- data/app/models/disco_app/concerns/plan.rb +13 -1
- data/app/models/disco_app/concerns/plan_code.rb +15 -0
- data/app/models/disco_app/concerns/shop.rb +10 -9
- data/app/models/disco_app/concerns/subscription.rb +36 -2
- data/app/models/disco_app/plan_code.rb +3 -0
- data/app/models/disco_app/recurring_application_charge.rb +18 -0
- data/app/services/disco_app/charges_service.rb +66 -58
- data/app/services/disco_app/subscription_service.rb +27 -15
- data/app/views/disco_app/admin/plans/_form.html.erb +27 -0
- data/app/views/disco_app/admin/plans/edit.html.erb +7 -0
- data/app/views/disco_app/admin/plans/index.html.erb +32 -0
- data/app/views/disco_app/admin/plans/new.html.erb +7 -0
- data/app/views/disco_app/charges/new.html.erb +9 -42
- data/app/views/disco_app/subscriptions/new.html.erb +25 -0
- data/app/views/layouts/admin/_navbar.html.erb +1 -0
- data/config/routes.rb +8 -5
- data/db/migrate/20160301223215_update_plans.rb +22 -0
- data/db/migrate/20160301224558_update_subscriptions.rb +13 -0
- data/db/migrate/20160302104816_create_disco_app_recurring_application_charges.rb +14 -0
- data/db/migrate/20160302105259_create_disco_app_application_charges.rb +14 -0
- data/db/migrate/20160302134728_drop_charge_status_from_shops.rb +5 -0
- data/db/migrate/20160302142941_add_shopify_attributes_to_charges.rb +8 -0
- data/db/migrate/20160331093148_create_disco_app_plan_codes.rb +14 -0
- data/db/migrate/20160401044420_add_status_to_plan_codes.rb +5 -0
- data/db/migrate/20160401045551_add_amount_and_plan_code_to_disco_app_subscriptions.rb +7 -0
- data/lib/disco_app/configuration.rb +6 -0
- data/lib/disco_app/version.rb +1 -1
- data/lib/generators/disco_app/reactify/reactify_generator.rb +1 -1
- data/lib/generators/disco_app/templates/initializers/disco_app.rb +9 -4
- data/test/controllers/disco_app/charges_controller_test.rb +91 -0
- data/test/controllers/disco_app/subscriptions_controller_test.rb +73 -0
- data/test/controllers/home_controller_test.rb +37 -6
- data/test/dummy/app/jobs/disco_app/app_installed_job.rb +16 -0
- data/test/dummy/config/initializers/disco_app.rb +9 -4
- data/test/dummy/db/schema.rb +54 -14
- data/test/fixtures/api/widget_store/charges/activate_application_charge_request.json +16 -0
- data/test/fixtures/api/widget_store/charges/activate_application_charge_response.json +1 -0
- data/test/fixtures/api/widget_store/charges/activate_recurring_application_charge_request.json +20 -0
- data/test/fixtures/api/widget_store/charges/activate_recurring_application_charge_response.json +1 -0
- data/test/fixtures/api/widget_store/charges/create_application_charge_request.json +9 -0
- data/test/fixtures/api/widget_store/charges/create_application_charge_response.json +16 -0
- data/test/fixtures/api/widget_store/charges/create_recurring_application_charge_request.json +9 -0
- data/test/fixtures/api/widget_store/charges/create_recurring_application_charge_response.json +20 -0
- data/test/fixtures/api/widget_store/charges/create_second_recurring_application_charge_request.json +9 -0
- data/test/fixtures/api/widget_store/charges/create_second_recurring_application_charge_response.json +20 -0
- data/test/fixtures/api/widget_store/charges/get_accepted_application_charge_response.json +16 -0
- data/test/fixtures/api/widget_store/charges/get_accepted_recurring_application_charge_response.json +20 -0
- data/test/fixtures/api/widget_store/charges/get_declined_application_charge_response.json +16 -0
- data/test/fixtures/api/widget_store/charges/get_declined_recurring_application_charge_response.json +20 -0
- data/test/fixtures/api/widget_store/charges/get_pending_application_charge_response.json +16 -0
- data/test/fixtures/api/widget_store/charges/get_pending_recurring_application_charge_response.json +20 -0
- data/test/fixtures/disco_app/application_charges.yml +11 -0
- data/test/fixtures/disco_app/plan_codes.yml +6 -0
- data/test/fixtures/disco_app/plans.yml +23 -18
- data/test/fixtures/disco_app/recurring_application_charges.yml +11 -0
- data/test/fixtures/disco_app/subscriptions.yml +12 -17
- data/test/jobs/disco_app/app_installed_job_test.rb +17 -5
- data/test/jobs/disco_app/app_uninstalled_job_test.rb +0 -2
- data/test/models/disco_app/shop_test.rb +3 -2
- data/test/services/disco_app/charges_service_test.rb +104 -0
- data/test/services/disco_app/subscription_service_test.rb +39 -8
- data/test/support/test_file_fixtures.rb +1 -1
- data/test/support/test_shopify_api.rb +16 -0
- data/test/test_helper.rb +1 -0
- metadata +73 -3
- data/test/models/disco_app/subscription_test.rb +0 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6d121a39b30f43fccab67728b99a8bc4e4aed5ef0fea0aaaba6c72f35b2c30b3
|
4
|
+
data.tar.gz: 8a6dd1711f023374e251243c661c4eec548aa1613ab39175c4e5b07975400579
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8d9ad557bccac5b7d620b2e6ffab730588394c8ab8570836c376dbb72080da66e79ddec488efcb9f638c44a5a57d793b13022fd96fe50c29d0fe5700d4fa9e04
|
7
|
+
data.tar.gz: bfe171a5488905b6704eb84e39f5f1a4c1a6603d337b18df96d8e8923c4706b889595cf6532ea83c1f28fe77f6f91c0bd476b9e00018f7fb95b5aba43ab4007b
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module DiscoApp::Admin::Concerns::PlansController
|
2
|
+
extend ActiveSupport::Concern
|
3
|
+
|
4
|
+
included do
|
5
|
+
before_action :find_plan, only: [:edit, :update]
|
6
|
+
end
|
7
|
+
|
8
|
+
def index
|
9
|
+
@plans = DiscoApp::Plan.all
|
10
|
+
end
|
11
|
+
|
12
|
+
def new
|
13
|
+
@plan = DiscoApp::Plan.new
|
14
|
+
@plan.plan_codes.build
|
15
|
+
end
|
16
|
+
|
17
|
+
def create
|
18
|
+
@plan = DiscoApp::Plan.new(plan_params)
|
19
|
+
if @plan.save
|
20
|
+
redirect_to admin_plans_path
|
21
|
+
else
|
22
|
+
render 'new'
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def edit
|
27
|
+
@plan.plan_codes.build
|
28
|
+
end
|
29
|
+
|
30
|
+
def update
|
31
|
+
if @plan.update_attributes(plan_params)
|
32
|
+
redirect_to admin_plans_path
|
33
|
+
else
|
34
|
+
render 'edit'
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def find_plan
|
41
|
+
@plan = DiscoApp::Plan.find(params[:id])
|
42
|
+
end
|
43
|
+
|
44
|
+
def plan_params
|
45
|
+
params.require(:plan).permit(
|
46
|
+
:name, :status, :plan_type, :trial_period_days, :amount,
|
47
|
+
:plan_codes_attributes => [:code, :trial_period_days, :amount]
|
48
|
+
)
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
@@ -1,30 +1,47 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
include DiscoApp::Concerns::AuthenticatedController
|
1
|
+
class DiscoApp::ChargesController < ApplicationController
|
2
|
+
include DiscoApp::Concerns::AuthenticatedController
|
4
3
|
|
5
|
-
|
4
|
+
skip_before_action :check_active_charge
|
5
|
+
before_action :find_subscription
|
6
6
|
|
7
|
-
|
8
|
-
|
7
|
+
# Display a "pre-charge" page, giving the opportunity to explain why a charge
|
8
|
+
# needs to be made.
|
9
|
+
def new
|
10
|
+
end
|
11
|
+
|
12
|
+
# Attempt to create a new charge for the logged in shop and selected
|
13
|
+
# subscription. If successful, redirect to the (external) charge confirmation
|
14
|
+
# URL. If it fails, redirect back to the new charge page.
|
15
|
+
def create
|
16
|
+
if(charge = DiscoApp::ChargesService.create(@shop, @subscription)).nil?
|
17
|
+
redirect_to action: :new
|
18
|
+
else
|
19
|
+
redirect_to charge.confirmation_url
|
9
20
|
end
|
21
|
+
end
|
10
22
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
23
|
+
# Attempt to activate a charge after a user has accepted or declined it.
|
24
|
+
# Redirect to the main application's root URL immediately afterwards - if the
|
25
|
+
# charge wasn't accepted, the flow will start again.
|
26
|
+
def activate
|
27
|
+
# First attempt to find a matching charge.
|
28
|
+
if(charge = @subscription.charges.find_by(id: params[:id], shopify_id: params[:charge_id])).nil?
|
29
|
+
redirect_to action: :new and return
|
30
|
+
end
|
31
|
+
if DiscoApp::ChargesService.activate(@shop, charge)
|
32
|
+
redirect_to main_app.root_url
|
33
|
+
else
|
34
|
+
redirect_to action: :new
|
17
35
|
end
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
18
39
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
redirect_to action: :new and return
|
40
|
+
def find_subscription
|
41
|
+
@subscription = @shop.subscriptions.find_by_id!(params[:subscription_id])
|
42
|
+
unless @subscription.requires_active_charge? and not @subscription.active_charge?
|
43
|
+
redirect_to main_app.root_url
|
24
44
|
end
|
25
|
-
DiscoApp::ChargesService.activate(@shop, shopify_charge)
|
26
|
-
redirect_to main_app.root_url
|
27
45
|
end
|
28
46
|
|
29
|
-
end
|
30
47
|
end
|
@@ -20,7 +20,7 @@ module DiscoApp::Concerns::AppProxyController
|
|
20
20
|
end
|
21
21
|
|
22
22
|
def proxy_signature_is_valid?
|
23
|
-
return true if DiscoApp.configuration.skip_proxy_verification?
|
23
|
+
return true if Rails.env.development? and DiscoApp.configuration.skip_proxy_verification?
|
24
24
|
DiscoApp::ProxyService.proxy_signature_is_valid?(request.query_string, ShopifyApp.configuration.secret)
|
25
25
|
end
|
26
26
|
|
@@ -4,7 +4,9 @@ module DiscoApp::Concerns::AuthenticatedController
|
|
4
4
|
included do
|
5
5
|
before_action :login_again_if_different_shop
|
6
6
|
before_action :shopify_shop
|
7
|
-
before_action :
|
7
|
+
before_action :check_installed
|
8
|
+
before_action :check_current_subscription
|
9
|
+
before_action :check_active_charge
|
8
10
|
around_filter :shopify_session
|
9
11
|
layout 'embedded_app'
|
10
12
|
end
|
@@ -19,17 +21,29 @@ module DiscoApp::Concerns::AuthenticatedController
|
|
19
21
|
end
|
20
22
|
end
|
21
23
|
|
22
|
-
def
|
23
|
-
if
|
24
|
-
redirect_if_not_current_path
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
redirect_if_not_current_path
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
redirect_if_not_current_path
|
24
|
+
def check_installed
|
25
|
+
if @shop.awaiting_install? or @shop.installing?
|
26
|
+
redirect_if_not_current_path disco_app.installing_path
|
27
|
+
return
|
28
|
+
end
|
29
|
+
if @shop.awaiting_uninstall? or @shop.uninstalling?
|
30
|
+
redirect_if_not_current_path disco_app.uninstalling_path
|
31
|
+
return
|
32
|
+
end
|
33
|
+
unless @shop.installed?
|
34
|
+
redirect_if_not_current_path disco_app.install_path
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def check_current_subscription
|
39
|
+
unless @shop.current_subscription?
|
40
|
+
redirect_if_not_current_path disco_app.new_subscription_path
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def check_active_charge
|
45
|
+
if @shop.current_subscription? and @shop.current_subscription.requires_active_charge? and not @shop.current_subscription.active_charge?
|
46
|
+
redirect_if_not_current_path disco_app.new_subscription_charge_path(@shop.current_subscription)
|
33
47
|
end
|
34
48
|
end
|
35
49
|
|
@@ -14,7 +14,7 @@ module DiscoApp::Concerns::CarrierRequestController
|
|
14
14
|
end
|
15
15
|
|
16
16
|
def carrier_request_signature_is_valid?
|
17
|
-
return true if DiscoApp.configuration.skip_carrier_request_verification?
|
17
|
+
return true if Rails.env.development? and DiscoApp.configuration.skip_carrier_request_verification?
|
18
18
|
DiscoApp::CarrierRequestService.is_valid_hmac?(request.body.read.to_s, ShopifyApp.configuration.secret, request.headers['HTTP_X_SHOPIFY_HMAC_SHA256'])
|
19
19
|
end
|
20
20
|
|
@@ -1,26 +1,27 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
include DiscoApp::Concerns::AuthenticatedController
|
1
|
+
class DiscoApp::InstallController < ApplicationController
|
2
|
+
include DiscoApp::Concerns::AuthenticatedController
|
4
3
|
|
5
|
-
|
6
|
-
|
7
|
-
AppInstalledJob.perform_later(@shop.shopify_domain)
|
8
|
-
redirect_to action: :installing
|
9
|
-
end
|
4
|
+
skip_before_action :check_current_subscription
|
5
|
+
skip_before_action :check_active_charge
|
10
6
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
end
|
7
|
+
# Start the installation process for the current shop, then redirect to the installing screen.
|
8
|
+
def install
|
9
|
+
DiscoApp::AppInstalledJob.perform_later(@shop.shopify_domain)
|
10
|
+
redirect_to action: :installing
|
11
|
+
end
|
17
12
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
end
|
13
|
+
# Display an "installing" page.
|
14
|
+
def installing
|
15
|
+
if @shop.installed?
|
16
|
+
redirect_to main_app.root_path
|
23
17
|
end
|
18
|
+
end
|
24
19
|
|
20
|
+
# Display an "uninstalling" page. Should be almost never used.
|
21
|
+
def uninstalling
|
22
|
+
if @shop.uninstalled?
|
23
|
+
redirect_to main_app.root_path
|
24
|
+
end
|
25
25
|
end
|
26
|
+
|
26
27
|
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
class DiscoApp::SubscriptionsController < ApplicationController
|
2
|
+
include DiscoApp::Concerns::AuthenticatedController
|
3
|
+
|
4
|
+
skip_before_action :check_current_subscription
|
5
|
+
|
6
|
+
def new
|
7
|
+
@subscription = DiscoApp::Subscription.new
|
8
|
+
end
|
9
|
+
|
10
|
+
def create
|
11
|
+
# Get the selected plan. If it's not available or couldn't be found,
|
12
|
+
# redirect back to the plan selection page.
|
13
|
+
if(plan = DiscoApp::Plan.available.find_by_id(subscription_params[:plan])).nil?
|
14
|
+
redirect_to action: :new and return
|
15
|
+
end
|
16
|
+
|
17
|
+
# If a plan code was provided, check that it's (a) valid and available and
|
18
|
+
# (b) valid for the selected plan.
|
19
|
+
plan_code = nil
|
20
|
+
if subscription_params[:plan_code].present?
|
21
|
+
if(plan_code = DiscoApp::PlanCode.available.find_by(plan: plan, code: subscription_params[:plan_code])).nil?
|
22
|
+
redirect_to action: :new and return
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# Subscribe the current shop to the selected plan.
|
27
|
+
if(subscription = DiscoApp::SubscriptionService.subscribe(@shop, plan, plan_code)).nil?
|
28
|
+
redirect_to action: :new
|
29
|
+
else
|
30
|
+
redirect_to main_app.root_path
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def subscription_params
|
37
|
+
params.require(:subscription).permit(:plan, :plan_code)
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
@@ -38,7 +38,7 @@ module DiscoApp
|
|
38
38
|
end
|
39
39
|
|
40
40
|
def webhook_is_valid?
|
41
|
-
return true if DiscoApp.configuration.skip_webhook_verification?
|
41
|
+
return true if Rails.env.development? and DiscoApp.configuration.skip_webhook_verification?
|
42
42
|
DiscoApp::WebhookService.is_valid_hmac?(request.body.read.to_s, ShopifyApp.configuration.secret, request.headers['HTTP_X_SHOPIFY_HMAC_SHA256'])
|
43
43
|
end
|
44
44
|
|
@@ -0,0 +1,22 @@
|
|
1
|
+
class SessionsController < ApplicationController
|
2
|
+
include ShopifyApp::SessionsController
|
3
|
+
|
4
|
+
protected
|
5
|
+
|
6
|
+
# Override the authenticate method to allow skipping OAuth in development
|
7
|
+
# mode. Skipping OAuth still requires a shop with Shopify domain specified
|
8
|
+
# by the `shop` parameter to be present in the local database.
|
9
|
+
def authenticate
|
10
|
+
if Rails.env.development? and DiscoApp.configuration.skip_oauth?
|
11
|
+
shop = DiscoApp::Shop.find_by_shopify_domain!(sanitized_shop_name)
|
12
|
+
|
13
|
+
sess = ShopifyAPI::Session.new(shop.shopify_domain, shop.shopify_token)
|
14
|
+
session[:shopify] = ShopifyApp::SessionRepository.store(sess)
|
15
|
+
session[:shopify_domain] = sanitized_shop_name
|
16
|
+
|
17
|
+
redirect_to disco_app.frame_path and return
|
18
|
+
end
|
19
|
+
super
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
@@ -12,11 +12,28 @@ module DiscoApp::Concerns::AppInstalledJob
|
|
12
12
|
# - Synchronise webhooks.
|
13
13
|
# - Synchronise carrier service, if required.
|
14
14
|
# - Perform initial update of shop information.
|
15
|
+
# - Subscribe to default plan, if any exists.
|
15
16
|
#
|
16
|
-
def perform(
|
17
|
-
DiscoApp::SynchroniseWebhooksJob.perform_now(
|
18
|
-
DiscoApp::SynchroniseCarrierServiceJob.perform_now(
|
19
|
-
DiscoApp::ShopUpdateJob.perform_now(
|
17
|
+
def perform(shopify_domain)
|
18
|
+
DiscoApp::SynchroniseWebhooksJob.perform_now(shopify_domain)
|
19
|
+
DiscoApp::SynchroniseCarrierServiceJob.perform_now(shopify_domain)
|
20
|
+
DiscoApp::ShopUpdateJob.perform_now(shopify_domain)
|
21
|
+
|
22
|
+
@shop.reload
|
23
|
+
|
24
|
+
if default_plan.present?
|
25
|
+
DiscoApp::SubscriptionService.subscribe(@shop, default_plan)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# Provide an overridable hook for applications to examine the @shop object
|
30
|
+
# and return the default plan, if any, the shop should be subscribed to. If
|
31
|
+
# nil is returned, no automatic subscription will take place and the store
|
32
|
+
# owner will be forced to choose a plan after installation.
|
33
|
+
#
|
34
|
+
# If implementing this method, it should be memoized.
|
35
|
+
def default_plan
|
36
|
+
nil
|
20
37
|
end
|
21
38
|
|
22
39
|
end
|
@@ -9,14 +9,11 @@ module DiscoApp::Concerns::AppUninstalledJob
|
|
9
9
|
|
10
10
|
# Perform application uninstallation.
|
11
11
|
#
|
12
|
-
# - Mark
|
12
|
+
# - Mark any recurring application charges as cancelled.
|
13
13
|
# - Remove any stored sessions for the shop.
|
14
14
|
#
|
15
15
|
def perform(domain, shop_data)
|
16
|
-
|
17
|
-
@shop.charge_cancelled!
|
18
|
-
end
|
19
|
-
|
16
|
+
DiscoApp::ChargesService.cancel_recurring_charges(@shop)
|
20
17
|
@shop.sessions.delete_all
|
21
18
|
end
|
22
19
|
|
@@ -0,0 +1,18 @@
|
|
1
|
+
class DiscoApp::ApplicationCharge < ActiveRecord::Base
|
2
|
+
|
3
|
+
belongs_to :shop
|
4
|
+
belongs_to :subscription
|
5
|
+
|
6
|
+
enum status: [:pending, :accepted, :declined, :expired, :active]
|
7
|
+
|
8
|
+
scope :active, -> { where status: statuses[:active] }
|
9
|
+
|
10
|
+
def recurring?
|
11
|
+
false
|
12
|
+
end
|
13
|
+
|
14
|
+
def activate_url
|
15
|
+
DiscoApp::Engine.routes.url_helpers.activate_subscription_charge_url(subscription, self)
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
@@ -5,10 +5,22 @@ module DiscoApp::Concerns::Plan
|
|
5
5
|
|
6
6
|
has_many :subscriptions
|
7
7
|
has_many :shops, through: :subscriptions
|
8
|
+
has_many :plan_codes
|
8
9
|
|
9
|
-
|
10
|
+
accepts_nested_attributes_for :plan_codes
|
11
|
+
|
12
|
+
enum status: [:available, :unavailable]
|
13
|
+
enum plan_type: [:recurring, :one_time]
|
14
|
+
enum interval: [:month, :year]
|
10
15
|
|
11
16
|
scope :available, -> { where status: statuses[:available] }
|
12
17
|
|
18
|
+
validates_presence_of :name
|
19
|
+
|
13
20
|
end
|
21
|
+
|
22
|
+
def has_trial?
|
23
|
+
trial_period_days.present? and trial_period_days > 0
|
24
|
+
end
|
25
|
+
|
14
26
|
end
|
@@ -15,9 +15,6 @@ module DiscoApp::Concerns::Shop
|
|
15
15
|
# Define possible installation statuses as an enum.
|
16
16
|
enum status: [:never_installed, :awaiting_install, :installing, :installed, :awaiting_uninstall, :uninstalling, :uninstalled]
|
17
17
|
|
18
|
-
# Define possible charge statuses as an enum.
|
19
|
-
enum charge_status: [:charge_none, :charge_pending, :charge_accepted, :charge_declined, :charge_active, :charge_cancelled, :charge_waived]
|
20
|
-
|
21
18
|
# Define some useful scopes.
|
22
19
|
scope :status, -> (status) { where status: status }
|
23
20
|
scope :installed, -> { where status: statuses[:installed] }
|
@@ -39,15 +36,19 @@ module DiscoApp::Concerns::Shop
|
|
39
36
|
}
|
40
37
|
end
|
41
38
|
|
42
|
-
#
|
43
|
-
def
|
44
|
-
|
45
|
-
self.public_send(status_update_method_name) if self.respond_to? status_update_method_name
|
39
|
+
# Convenience method to check if this shop has a current subscription.
|
40
|
+
def current_subscription?
|
41
|
+
current_subscription.present?
|
46
42
|
end
|
47
43
|
|
48
|
-
# Convenience method to get the
|
44
|
+
# Convenience method to get the current subscription for this shop, if any.
|
49
45
|
def current_subscription
|
50
|
-
subscriptions.
|
46
|
+
subscriptions.current.first
|
47
|
+
end
|
48
|
+
|
49
|
+
# Convenience method to get the current plan for this shop, if any.
|
50
|
+
def current_plan
|
51
|
+
current_subscription&.plan
|
51
52
|
end
|
52
53
|
|
53
54
|
# Return the absolute URL to the shop's storefront.
|
@@ -5,10 +5,44 @@ module DiscoApp::Concerns::Subscription
|
|
5
5
|
|
6
6
|
belongs_to :shop
|
7
7
|
belongs_to :plan
|
8
|
+
belongs_to :plan_code
|
8
9
|
|
9
|
-
|
10
|
+
has_many :one_time_charges, class_name: 'DiscoApp::ApplicationCharge', dependent: :destroy
|
11
|
+
has_many :recurring_charges, class_name: 'DiscoApp::RecurringApplicationCharge', dependent: :destroy
|
10
12
|
|
11
|
-
|
13
|
+
enum status: [:trial, :active, :cancelled]
|
14
|
+
enum subscription_type: [:recurring, :one_time]
|
12
15
|
|
16
|
+
scope :current, -> { where status: [statuses[:trial], statuses[:active]] }
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
# Only require an active charge if the amount to be charged is > 0.
|
21
|
+
def requires_active_charge?
|
22
|
+
amount > 0
|
23
|
+
end
|
24
|
+
|
25
|
+
# Convenience method to check if this subscription has an active charge.
|
26
|
+
def active_charge?
|
27
|
+
active_charge.present?
|
28
|
+
end
|
29
|
+
|
30
|
+
# Convenience method to get the active charge for this subscription.
|
31
|
+
def active_charge
|
32
|
+
charges.active.first
|
13
33
|
end
|
34
|
+
|
35
|
+
# Return the appropriate set of charges for this subscription's type.
|
36
|
+
def charges
|
37
|
+
recurring? ? recurring_charges : one_time_charges
|
38
|
+
end
|
39
|
+
|
40
|
+
def charge_class
|
41
|
+
recurring? ? DiscoApp::RecurringApplicationCharge : DiscoApp::ApplicationCharge
|
42
|
+
end
|
43
|
+
|
44
|
+
def shopify_charge_class
|
45
|
+
recurring? ? ShopifyAPI::RecurringApplicationCharge : ShopifyAPI::ApplicationCharge
|
46
|
+
end
|
47
|
+
|
14
48
|
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
class DiscoApp::RecurringApplicationCharge < ActiveRecord::Base
|
2
|
+
|
3
|
+
belongs_to :shop
|
4
|
+
belongs_to :subscription
|
5
|
+
|
6
|
+
enum status: [:pending, :accepted, :declined, :active, :cancelled, :expired]
|
7
|
+
|
8
|
+
scope :active, -> { where status: statuses[:active] }
|
9
|
+
|
10
|
+
def recurring?
|
11
|
+
true
|
12
|
+
end
|
13
|
+
|
14
|
+
def activate_url
|
15
|
+
DiscoApp::Engine.routes.url_helpers.activate_subscription_charge_url(subscription, self)
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|