shopify-gold 2.0.0.pre
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 +7 -0
- data/README.md +144 -0
- data/Rakefile +32 -0
- data/app/assets/config/gold_manifest.js +0 -0
- data/app/assets/images/gold/app-logo.svg +17 -0
- data/app/assets/images/gold/aside-bg.svg +54 -0
- data/app/assets/javascripts/gold/billing.js +30 -0
- data/app/assets/stylesheets/gold/billing.sass +64 -0
- data/app/assets/stylesheets/shopify/polaris.css +1 -0
- data/app/controllers/gold/admin/billing_controller.rb +190 -0
- data/app/controllers/gold/application_controller.rb +17 -0
- data/app/controllers/gold/authenticated_controller.rb +15 -0
- data/app/controllers/gold/billing_controller.rb +185 -0
- data/app/controllers/gold/concerns/merchant_facing.rb +85 -0
- data/app/jobs/app_uninstalled_job.rb +2 -0
- data/app/jobs/gold/after_authenticate_job.rb +36 -0
- data/app/jobs/gold/app_uninstalled_job.rb +10 -0
- data/app/jobs/gold/application_job.rb +20 -0
- data/app/mailers/gold/application_mailer.rb +7 -0
- data/app/mailers/gold/billing_mailer.rb +36 -0
- data/app/models/gold/billing.rb +157 -0
- data/app/models/gold/concerns/gilded.rb +22 -0
- data/app/models/gold/machine.rb +301 -0
- data/app/models/gold/shopify_plan.rb +70 -0
- data/app/models/gold/tier.rb +97 -0
- data/app/models/gold/transition.rb +26 -0
- data/app/operations/gold/accept_or_decline_charge_op.rb +119 -0
- data/app/operations/gold/accept_or_decline_terms_op.rb +26 -0
- data/app/operations/gold/apply_discount_op.rb +32 -0
- data/app/operations/gold/apply_tier_op.rb +51 -0
- data/app/operations/gold/charge_op.rb +99 -0
- data/app/operations/gold/check_charge_op.rb +50 -0
- data/app/operations/gold/cleanup_op.rb +20 -0
- data/app/operations/gold/convert_affiliate_to_paid_op.rb +32 -0
- data/app/operations/gold/freeze_op.rb +15 -0
- data/app/operations/gold/install_op.rb +30 -0
- data/app/operations/gold/issue_credit_op.rb +55 -0
- data/app/operations/gold/mark_as_delinquent_op.rb +21 -0
- data/app/operations/gold/resolve_outstanding_charge_op.rb +90 -0
- data/app/operations/gold/select_tier_op.rb +58 -0
- data/app/operations/gold/suspend_op.rb +21 -0
- data/app/operations/gold/uninstall_op.rb +20 -0
- data/app/operations/gold/unsuspend_op.rb +20 -0
- data/app/views/gold/admin/billing/_active_charge.erb +29 -0
- data/app/views/gold/admin/billing/_credit.erb +46 -0
- data/app/views/gold/admin/billing/_discount.erb +23 -0
- data/app/views/gold/admin/billing/_history.erb +65 -0
- data/app/views/gold/admin/billing/_overview.erb +14 -0
- data/app/views/gold/admin/billing/_shopify_plan.erb +21 -0
- data/app/views/gold/admin/billing/_state.erb +26 -0
- data/app/views/gold/admin/billing/_status.erb +25 -0
- data/app/views/gold/admin/billing/_tier.erb +155 -0
- data/app/views/gold/admin/billing/_trial_days.erb +42 -0
- data/app/views/gold/billing/_inner_head.html.erb +1 -0
- data/app/views/gold/billing/declined_charge.html.erb +20 -0
- data/app/views/gold/billing/expired_charge.html.erb +14 -0
- data/app/views/gold/billing/missing_charge.html.erb +22 -0
- data/app/views/gold/billing/outstanding_charge.html.erb +12 -0
- data/app/views/gold/billing/suspended.html.erb +8 -0
- data/app/views/gold/billing/terms.html.erb +22 -0
- data/app/views/gold/billing/tier.html.erb +29 -0
- data/app/views/gold/billing/transition_error.html.erb +9 -0
- data/app/views/gold/billing/unavailable.erb +8 -0
- data/app/views/gold/billing/uninstalled.html.erb +13 -0
- data/app/views/gold/billing_mailer/affiliate_to_paid.erb +23 -0
- data/app/views/gold/billing_mailer/delinquent.html.erb +21 -0
- data/app/views/gold/billing_mailer/suspension.html.erb +19 -0
- data/app/views/layouts/gold/billing.html.erb +22 -0
- data/app/views/layouts/gold/mailer.html.erb +13 -0
- data/app/views/layouts/gold/mailer.text.erb +1 -0
- data/config/routes.rb +33 -0
- data/db/migrate/01_create_gold_billing.rb +11 -0
- data/db/migrate/02_create_gold_transitions.rb +29 -0
- data/db/migrate/03_add_foreign_key_to_billing.rb +8 -0
- data/lib/gold/admin_engine.rb +8 -0
- data/lib/gold/billing_migrator.rb +107 -0
- data/lib/gold/coverage.rb +89 -0
- data/lib/gold/diagram.rb +40 -0
- data/lib/gold/engine.rb +16 -0
- data/lib/gold/exceptions/metadata_missing.rb +5 -0
- data/lib/gold/outcomes.rb +77 -0
- data/lib/gold/retries.rb +31 -0
- data/lib/gold/version.rb +3 -0
- data/lib/gold.rb +102 -0
- data/lib/tasks/gold_tasks.rake +94 -0
- metadata +298 -0
@@ -0,0 +1,58 @@
|
|
1
|
+
module Gold
|
2
|
+
module Outcomes
|
3
|
+
CannotSelectTier = Class.new(Failure)
|
4
|
+
SameTier = Class.new(Success)
|
5
|
+
end
|
6
|
+
|
7
|
+
# Allows a merchant to select their initial tier or change their tier.
|
8
|
+
class SelectTierOp
|
9
|
+
include Outcomes
|
10
|
+
|
11
|
+
def initialize(billing, tier, enforce_locking = true)
|
12
|
+
@billing = billing
|
13
|
+
@tier = tier
|
14
|
+
@enforce_locking = enforce_locking
|
15
|
+
end
|
16
|
+
|
17
|
+
def call
|
18
|
+
@billing.transaction do
|
19
|
+
return CannotSelectTier.new(:nil) unless @tier
|
20
|
+
return CannotSelectTier.new(:locked) if @enforce_locking && @tier.locked?
|
21
|
+
|
22
|
+
unless @billing.qualifies_for_tier?(@tier)
|
23
|
+
return CannotSelectTier.new(:disqualified)
|
24
|
+
end
|
25
|
+
|
26
|
+
# Mark the tier as selected
|
27
|
+
if @billing.transition_to(:select_tier, tier_id: @tier.id)
|
28
|
+
elsif @billing.transition_to(:change_tier, tier_id: @tier.id)
|
29
|
+
else
|
30
|
+
message = "Cannot transition from '#{@billing.current_state}' " \
|
31
|
+
"to 'select_tier' or 'change_tier'"
|
32
|
+
raise Statesman::TransitionFailedError, message
|
33
|
+
end
|
34
|
+
|
35
|
+
# Mark the beginning of the trial if it hasn't been set already
|
36
|
+
@billing.update(trial_starts_at: Time.current) unless @billing.trial_starts_at
|
37
|
+
|
38
|
+
# Process the tier selection, creating charges if necessary
|
39
|
+
if @billing.shopify_plan.affiliate?
|
40
|
+
@billing.tier = @tier
|
41
|
+
@billing.save!
|
42
|
+
@billing.transition_to!(:affiliate)
|
43
|
+
return TierApplied.new
|
44
|
+
elsif @billing.shopify_plan.staff?
|
45
|
+
@billing.tier = @tier
|
46
|
+
@billing.save!
|
47
|
+
@billing.transition_to!(:staff)
|
48
|
+
return TierApplied.new
|
49
|
+
elsif @tier.free?
|
50
|
+
return ApplyTierOp.new(@billing).call
|
51
|
+
else
|
52
|
+
# A valid charge is necessary before applying the new tier
|
53
|
+
return ChargeNeeded.new
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Gold
|
2
|
+
# Invoked when Helium suspends a merchant for violating ToS.
|
3
|
+
class SuspendOp
|
4
|
+
include Outcomes
|
5
|
+
|
6
|
+
def initialize(billing)
|
7
|
+
@billing = billing
|
8
|
+
end
|
9
|
+
|
10
|
+
def call
|
11
|
+
@billing.transition_to_or_stay_in!(:marked_as_suspended)
|
12
|
+
|
13
|
+
# Communicate suspension to the merchant.
|
14
|
+
BillingMailer.suspension(@billing).deliver_now
|
15
|
+
|
16
|
+
@billing.transition_to!(:suspended)
|
17
|
+
|
18
|
+
Success.new
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Gold
|
2
|
+
# Invoked when a shop has uninstalled this app.
|
3
|
+
class UninstallOp
|
4
|
+
include Outcomes
|
5
|
+
|
6
|
+
def initialize(billing)
|
7
|
+
@billing = billing
|
8
|
+
end
|
9
|
+
|
10
|
+
def call
|
11
|
+
return Success.new if @billing.current_state == :uninstalled
|
12
|
+
|
13
|
+
@billing.transition_to_or_stay_in!(:marked_as_uninstalled)
|
14
|
+
@billing.transition_to!(:uninstalled)
|
15
|
+
Gold.configuration.on_uninstall&.call(@billing)
|
16
|
+
|
17
|
+
Success.new
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Gold
|
2
|
+
# Invoked when Helium unsuspends a shop.
|
3
|
+
class UnsuspendOp
|
4
|
+
include Outcomes
|
5
|
+
|
6
|
+
def initialize(billing)
|
7
|
+
@billing = billing
|
8
|
+
end
|
9
|
+
|
10
|
+
def call
|
11
|
+
if @billing.can_transition_to?(:affiliate)
|
12
|
+
@billing.transition_to!(:affiliate)
|
13
|
+
else
|
14
|
+
@billing.transition_to!(:check_charge)
|
15
|
+
end
|
16
|
+
|
17
|
+
Success.new
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
<div class="card mb-3">
|
2
|
+
<div class="card-header">
|
3
|
+
<strong>Active charge</strong>
|
4
|
+
</div>
|
5
|
+
<div class="card-body">
|
6
|
+
<% billing.shop.with_shopify_session do %>
|
7
|
+
<% charge = @active_charge %>
|
8
|
+
<% if charge %>
|
9
|
+
<% if charge.test %>
|
10
|
+
<span class="badge badge-warning">test charge</span>
|
11
|
+
<hr>
|
12
|
+
<% end %>
|
13
|
+
<p>Name<br> <strong><%= charge.name %></strong></p>
|
14
|
+
<p>ID<br> <strong><%= charge.id %></strong></p>
|
15
|
+
<p>Monthly<br> <strong>$<%= charge.price %></strong></p>
|
16
|
+
<p>Trial days<br> <strong><%= charge.trial_days %></strong></p>
|
17
|
+
<p>Created at<br> <strong><%= DateTime.parse(charge.created_at).strftime("%Y-%m-%e %H:%M") %></strong></p>
|
18
|
+
<p>Trial ends on<br> <strong><%= charge.trial_ends_on %></strong></p>
|
19
|
+
<p>Next billing<br> <strong><%= charge.billing_on %></strong></p>
|
20
|
+
|
21
|
+
<%= form_tag(gold_admin_engine.cancel_charge_path(id: billing.id, charge_id: charge.id), method: :delete) do %>
|
22
|
+
<%= submit_tag 'Cancel charge', class: "btn btn-danger", data: {confirm: "Are you really sure you want to cancel this charge?"} %>
|
23
|
+
<% end %>
|
24
|
+
<% else %>
|
25
|
+
No charge from Shopify
|
26
|
+
<% end %>
|
27
|
+
<% end %>
|
28
|
+
</div>
|
29
|
+
</div>
|
@@ -0,0 +1,46 @@
|
|
1
|
+
<% billing.shop.with_shopify_session do %>
|
2
|
+
<% credits = @credits %>
|
3
|
+
<div class="card mb-3">
|
4
|
+
<div class="card-header">
|
5
|
+
<strong>Credit</strong>
|
6
|
+
</div>
|
7
|
+
<div class="card-body">
|
8
|
+
<h6>Credits</h6>
|
9
|
+
<% if credits.any? %>
|
10
|
+
<table class="table celled">
|
11
|
+
<thead>
|
12
|
+
<tr>
|
13
|
+
<th>Reason</th>
|
14
|
+
<th>Amount</th>
|
15
|
+
</tr>
|
16
|
+
</thead>
|
17
|
+
<tbody>
|
18
|
+
<% billing.shop.with_shopify_session do %>
|
19
|
+
<% ShopifyAPI::ApplicationCredit.all.each do |credit| %>
|
20
|
+
<tr>
|
21
|
+
<%= credit.description %>
|
22
|
+
<% if credit.test %>
|
23
|
+
<strong>(test)</strong>
|
24
|
+
<% end %>
|
25
|
+
</tr>
|
26
|
+
<tr><%= credit.amount %></tr>
|
27
|
+
<% end %>
|
28
|
+
<% end %>
|
29
|
+
</tbody>
|
30
|
+
</table>
|
31
|
+
<% else %>
|
32
|
+
<p>No active credits</p>
|
33
|
+
<% end %>
|
34
|
+
<hr>
|
35
|
+
<%= form_tag(gold_admin_engine.issue_credit_path(id: billing.id), method: :post) do %>
|
36
|
+
<p>Give the merchant a credit for next month (in dollars)</p>
|
37
|
+
<%= number_field_tag 'amount', 0, size: 3 %>
|
38
|
+
<%= text_field_tag 'reason', '', placeholder: 'Reason to merchant' %>
|
39
|
+
<div>
|
40
|
+
<hr>
|
41
|
+
<%= submit_tag 'Issue Credit', class: "btn btn-primary" %>
|
42
|
+
</div>
|
43
|
+
<% end %>
|
44
|
+
</div>
|
45
|
+
</div>
|
46
|
+
<% end %>
|
@@ -0,0 +1,23 @@
|
|
1
|
+
<div class="card mb-3">
|
2
|
+
<div class="card-header">
|
3
|
+
<strong>Discount</strong>
|
4
|
+
</div>
|
5
|
+
<div class="card-body">
|
6
|
+
<%= form_tag(gold_admin_engine.apply_discount_path(id: billing.id), method: :put) do %>
|
7
|
+
<input
|
8
|
+
type="number"
|
9
|
+
name="percentage"
|
10
|
+
min="0"
|
11
|
+
max="100"
|
12
|
+
value="<%= billing.discount_percentage %>">%
|
13
|
+
<div>
|
14
|
+
<hr>
|
15
|
+
<% if billing.can_transition_to?(:apply_discount) %>
|
16
|
+
<%= submit_tag 'Apply discount', class: "btn btn-primary", data: {confirm: "Are you really sure you want to apply this discount?"} %>
|
17
|
+
<% else %>
|
18
|
+
<p>Discount cannot be applied in the current state</p>
|
19
|
+
<% end %>
|
20
|
+
</div>
|
21
|
+
<% end %>
|
22
|
+
</div>
|
23
|
+
</div>
|
@@ -0,0 +1,65 @@
|
|
1
|
+
|
2
|
+
<div class="card">
|
3
|
+
<div class="card-header">
|
4
|
+
<strong>History</strong>
|
5
|
+
</div>
|
6
|
+
<div class="card-body">
|
7
|
+
<p>These are all the billing-related events that have happened.
|
8
|
+
Only forcefully transition to a state as a last resort.</p>
|
9
|
+
|
10
|
+
<div class="mb-4">
|
11
|
+
<%= form_tag(gold_admin_engine.transition_path, method: :put) do %>
|
12
|
+
<input type="hidden" name="id" value="<%= billing.id %>">
|
13
|
+
<div>
|
14
|
+
Transition from <mark><%= billing.current_state %></mark>
|
15
|
+
→
|
16
|
+
<select name="to" class="mr-2">
|
17
|
+
<option value="">Select a state...</option>
|
18
|
+
<% Gold::Machine.successors.with_indifferent_access[billing.current_state].each do |transition| %>
|
19
|
+
<option><%= transition %></option>
|
20
|
+
<% end %>
|
21
|
+
</select>
|
22
|
+
</div>
|
23
|
+
<div class="mb-2 mt-2">
|
24
|
+
Metadata:
|
25
|
+
<input type="text" name="metadata[key]" placeholder="key">
|
26
|
+
<input type="text" name="metadata[value]" placeholder="value">
|
27
|
+
</div>
|
28
|
+
<%= submit_tag 'Transition', class: "btn btn-primary" %>
|
29
|
+
<% end %>
|
30
|
+
</div>
|
31
|
+
<div style="max-height: 500px; overflow: auto;">
|
32
|
+
<table class="table table-sm table-dark">
|
33
|
+
<thead>
|
34
|
+
<tr>
|
35
|
+
<th>Date</th>
|
36
|
+
<th>State</th>
|
37
|
+
<th>Metadata</th>
|
38
|
+
</tr>
|
39
|
+
</thead>
|
40
|
+
<tbody>
|
41
|
+
<% last_transition = nil %>
|
42
|
+
<% billing.state_machine.history.where.not(to_state: "check_charge").reverse.each do |transition| %>
|
43
|
+
<%# This hides all the check_charges and back to billing %>
|
44
|
+
<% if transition.to_state == "billing" %>
|
45
|
+
<% unless last_transition&.to_state == "billing" %>
|
46
|
+
<tr>
|
47
|
+
<td><%= transition.created_at.localtime.strftime("%Y-%m-%e %H:%M") %></td>
|
48
|
+
<td><%= transition.to_state %></td>
|
49
|
+
<td><%= transition.metadata %></td>
|
50
|
+
</tr>
|
51
|
+
<% end %>
|
52
|
+
<% else %>
|
53
|
+
<tr>
|
54
|
+
<td><%= transition.created_at.localtime.strftime("%Y-%m-%e %H:%M") %></td>
|
55
|
+
<td><%= transition.to_state %></td>
|
56
|
+
<td><%= transition.metadata %></td>
|
57
|
+
</tr>
|
58
|
+
<% end %>
|
59
|
+
<% last_transition = transition %>
|
60
|
+
<% end %>
|
61
|
+
</tbody>
|
62
|
+
</table>
|
63
|
+
</div>
|
64
|
+
</div>
|
65
|
+
</div>
|
@@ -0,0 +1,14 @@
|
|
1
|
+
<%= render "gold/admin/billing/state", billing: billing %>
|
2
|
+
<% tier = billing.tier %>
|
3
|
+
<% if tier %>
|
4
|
+
<%= render "gold/admin/billing/tier", billing: billing, tier: tier %>
|
5
|
+
<% end %>
|
6
|
+
<%= render "gold/admin/billing/active_charge", billing: billing %>
|
7
|
+
<%= render "gold/admin/billing/status", billing: billing %>
|
8
|
+
<%= render "gold/admin/billing/discount", billing: billing %>
|
9
|
+
<% if tier %>
|
10
|
+
<%= render "gold/admin/billing/trial_days", billing: billing %>
|
11
|
+
<% end %>
|
12
|
+
<%= render "gold/admin/billing/credit", billing: billing %>
|
13
|
+
<%= render "gold/admin/billing/shopify_plan", billing: billing %>
|
14
|
+
<%= render "gold/admin/billing/history", billing: billing %>
|
@@ -0,0 +1,21 @@
|
|
1
|
+
<div class="card mb-3">
|
2
|
+
<div class="card-header">
|
3
|
+
<strong>Shopify Plan Override</strong>
|
4
|
+
</div>
|
5
|
+
<div class="card-body">
|
6
|
+
<%= form_tag(gold_admin_engine.override_shopify_plan_path(id: billing.id), method: :put) do %>
|
7
|
+
<p>
|
8
|
+
Actual Shopify plan:
|
9
|
+
<span class="badge badge-info"><%= billing.shop.shopify_plan_name %></span>
|
10
|
+
</p>
|
11
|
+
<input
|
12
|
+
type="text"
|
13
|
+
name="plan"
|
14
|
+
value="<%= billing.shopify_plan_override %>">
|
15
|
+
<div>
|
16
|
+
<hr>
|
17
|
+
<%= submit_tag 'Override', class: "btn btn-primary", data: {confirm: "Are you really sure you want to override plan?"} %>
|
18
|
+
</div>
|
19
|
+
<% end %>
|
20
|
+
</div>
|
21
|
+
</div>
|
@@ -0,0 +1,26 @@
|
|
1
|
+
<div class="card mb-3">
|
2
|
+
<div class="card-header"><strong>Status</strong></div>
|
3
|
+
<div class="card-body">
|
4
|
+
<h3 class="badge
|
5
|
+
<% if billing.state_machine.accepted? %>
|
6
|
+
badge-success
|
7
|
+
<% else %>
|
8
|
+
badge-warning
|
9
|
+
<% end %>">
|
10
|
+
<%= billing.current_state %>
|
11
|
+
<% metadata = billing.transitions.last&.metadata %>
|
12
|
+
<% if metadata&.keys&.any? %>
|
13
|
+
→
|
14
|
+
<%= metadata.to_a.join(": ") %>
|
15
|
+
<% end %>
|
16
|
+
</h3>
|
17
|
+
|
18
|
+
<% if billing.can_transition_to?(:optional_charge_declined) %>
|
19
|
+
<%= form_tag(gold_admin_engine.transition_path(id: billing.id), method: :put) do %>
|
20
|
+
<input type="hidden" name="to[]" value="optional_charge_declined">
|
21
|
+
<input type="hidden" name="to[]" value="billing">
|
22
|
+
<%= submit_tag 'Decline', class: "btn btn-danger btn-sm", data: { confirm: "Are you sure you want to decline this charge?" } %>
|
23
|
+
<% end %>
|
24
|
+
<% end %>
|
25
|
+
</div>
|
26
|
+
</div>
|
@@ -0,0 +1,25 @@
|
|
1
|
+
<div class="card mb-3">
|
2
|
+
<div class="card-header">
|
3
|
+
<strong>Account status</strong>
|
4
|
+
</div>
|
5
|
+
<div class="card-body">
|
6
|
+
<% if billing.current_state.to_s.include?("suspended") %>
|
7
|
+
<p>This account is currently <strong>suspended</strong></p>
|
8
|
+
<%= form_tag(gold_admin_engine.unsuspend_path(id: billing.id), method: :put) do %>
|
9
|
+
<%= submit_tag 'Unsuspend', data: {confirm: "Are you really sure you want to UNSUSPEND this account?"} %>
|
10
|
+
<% end %>
|
11
|
+
<% else %>
|
12
|
+
<p>This account is currently <strong>allowed</strong></p>
|
13
|
+
<% if billing.can_transition_to? :marked_as_suspended %>
|
14
|
+
<%= form_tag(gold_admin_engine.suspend_path(id: billing.id), method: :put) do %>
|
15
|
+
<div>
|
16
|
+
<label>
|
17
|
+
<input type="checkbox" name="confirm_suspend"> Yes I want to suspend this account
|
18
|
+
</label>
|
19
|
+
</div>
|
20
|
+
<%= submit_tag 'Confirm suspend', class:"btn btn-secondary btn-danger", data: {confirm: "Are you really sure you want to SUSPEND this account?"} %>
|
21
|
+
<% end %>
|
22
|
+
<% end %>
|
23
|
+
<% end %>
|
24
|
+
</div>
|
25
|
+
</div>
|
@@ -0,0 +1,155 @@
|
|
1
|
+
<div class="card mb-3">
|
2
|
+
<div class="card-header"><strong>Tier</strong></div>
|
3
|
+
<div class="card-body">
|
4
|
+
<table class="table">
|
5
|
+
<tr>
|
6
|
+
<td><h6>Plan</h6></td>
|
7
|
+
<td><h6>Plan ID</h6></td>
|
8
|
+
<td><h6>Monthly price</h6></td>
|
9
|
+
<td><h6>Plan trial days</h6></td>
|
10
|
+
<td><h6>Trial days left</h6></td>
|
11
|
+
<td><h6>Features</h6></td>
|
12
|
+
</tr>
|
13
|
+
<% next_tier = billing.last_selected_tier %>
|
14
|
+
<% if tier.id != next_tier.id %>
|
15
|
+
<tr>
|
16
|
+
<td><%= next_tier.name %> <span class="badge badge-warning">pending</span></td>
|
17
|
+
<td>
|
18
|
+
<span class="badge badge-info"><%= next_tier.id %></span>
|
19
|
+
</td>
|
20
|
+
<td>
|
21
|
+
<% next_tier_price = next_tier.monthly_price %>
|
22
|
+
<% next_tier_calc_price = billing.calculate_price(next_tier) %>
|
23
|
+
|
24
|
+
<% if next_tier_price != next_tier_calc_price %>
|
25
|
+
<s><%= number_to_currency(next_tier_price) %></s>
|
26
|
+
<% end %>
|
27
|
+
|
28
|
+
<%= number_to_currency(next_tier_calc_price) %>
|
29
|
+
</td>
|
30
|
+
<td><%= next_tier.trial_days %></td>
|
31
|
+
<td><%= billing.calculate_trial_days(next_tier) %></td>
|
32
|
+
<td>
|
33
|
+
<% next_tier.features.each do |feature| %>
|
34
|
+
<div>
|
35
|
+
<span class="badge badge-success"><%= feature.join(": ") %></span>
|
36
|
+
</div>
|
37
|
+
<% end %>
|
38
|
+
</td>
|
39
|
+
</tr>
|
40
|
+
<% end %>
|
41
|
+
|
42
|
+
<% if tier %>
|
43
|
+
<tr>
|
44
|
+
<td><%= tier.name %> <span class="badge badge-success">active</span></td>
|
45
|
+
<td>
|
46
|
+
<span class="badge badge-info"><%= tier.id %></span>
|
47
|
+
</td>
|
48
|
+
<td>
|
49
|
+
<% tier_price = tier.monthly_price %>
|
50
|
+
<% tier_calc_price = billing.calculate_price(tier) %>
|
51
|
+
|
52
|
+
<% if tier_price != tier_calc_price %>
|
53
|
+
<s><%= number_to_currency(tier_price) %></s>
|
54
|
+
<% end %>
|
55
|
+
|
56
|
+
<%= number_to_currency(tier_calc_price) %>
|
57
|
+
</td>
|
58
|
+
<td><%= tier.trial_days %></td>
|
59
|
+
<td><%= billing.calculate_trial_days(tier) %></td>
|
60
|
+
<td>
|
61
|
+
<% tier.features.each do |feature| %>
|
62
|
+
<div>
|
63
|
+
<span class="badge badge-success"><%= feature.join(": ") %></span>
|
64
|
+
</div>
|
65
|
+
<% end %>
|
66
|
+
</td>
|
67
|
+
</tr>
|
68
|
+
<% end %>
|
69
|
+
</table>
|
70
|
+
<hr>
|
71
|
+
<% if billing.can_transition_to?(:change_tier) %>
|
72
|
+
<%= form_tag(gold_admin_engine.change_tier_path(id: billing.id), method: :put) do %>
|
73
|
+
Change tier →
|
74
|
+
<select name="tier" class="mr-2">
|
75
|
+
<option value="">Select a tier...</option>
|
76
|
+
<%= Gold::Tier.all.each do |_tier| %>
|
77
|
+
<% price_difference = _tier.monthly_price - tier.monthly_price %>
|
78
|
+
<option value="<%= _tier.id %>" <% if _tier.locked? %>disabled<% end %>>
|
79
|
+
<%= _tier.name %>
|
80
|
+
<% if _tier.free? %>
|
81
|
+
(free)
|
82
|
+
<% elsif price_difference > 0 %>
|
83
|
+
(+<%= number_to_currency(price_difference) %>)
|
84
|
+
<% elsif price_difference < 0 %>
|
85
|
+
(-<%= number_to_currency(price_difference.abs) %>)
|
86
|
+
<% end %>
|
87
|
+
|
88
|
+
<% if _tier.locked? %>(locked)<% end %>
|
89
|
+
</option>
|
90
|
+
<% end %>
|
91
|
+
</select>
|
92
|
+
<%= submit_tag 'Apply', class: "btn btn-primary btn-sm" %>
|
93
|
+
<% end %>
|
94
|
+
<% else %>
|
95
|
+
<p>Can't change tiers, because the current state doesn't allow it</p>
|
96
|
+
<% end %>
|
97
|
+
<br />
|
98
|
+
<a data-toggle="collapse" href="#collapsePlans" role="button" aria-expanded="false" aria-controls="collapsePlans">
|
99
|
+
See all plans
|
100
|
+
</a>
|
101
|
+
|
102
|
+
<table id="collapsePlans" class="table celled collapse">
|
103
|
+
<thead>
|
104
|
+
<tr>
|
105
|
+
<th>ID</th>
|
106
|
+
<th>Name</th>
|
107
|
+
<th>Monthly price</th>
|
108
|
+
<th>Trial days</th>
|
109
|
+
<th>Features</th>
|
110
|
+
<th>Qualifications</th>
|
111
|
+
</tr>
|
112
|
+
</thead>
|
113
|
+
<tbody>
|
114
|
+
<% Gold::Tier.all.each do |tier| %>
|
115
|
+
<tr>
|
116
|
+
<td><%= tier.id %></td>
|
117
|
+
<td>
|
118
|
+
<%= tier.name %>
|
119
|
+
|
120
|
+
<div>
|
121
|
+
<% if tier.locked? %>
|
122
|
+
<span class="badge badge-warning">locked</span>
|
123
|
+
<% end %>
|
124
|
+
|
125
|
+
<% if tier.free? %>
|
126
|
+
<span class="badge badge-info">free</span>
|
127
|
+
<% end %>
|
128
|
+
|
129
|
+
<% unless tier.visible? %>
|
130
|
+
<span class="badge badge-secondary">hidden</span>
|
131
|
+
<% end %>
|
132
|
+
</div>
|
133
|
+
</td>
|
134
|
+
<td><%= number_to_currency(tier.monthly_price) %></td>
|
135
|
+
<td><%= tier.trial_days %></td>
|
136
|
+
<td>
|
137
|
+
<% tier.features.each do |feature| %>
|
138
|
+
<div>
|
139
|
+
<span class="badge badge-success"><%= feature.join(": ") %></span>
|
140
|
+
</div>
|
141
|
+
<% end %>
|
142
|
+
</td>
|
143
|
+
<td>
|
144
|
+
<% tier.qualifications.each do |qual| %>
|
145
|
+
<div>
|
146
|
+
<span class="badge badge-danger"><%= qual.join(": ") %></span>
|
147
|
+
</div>
|
148
|
+
<% end %>
|
149
|
+
</td>
|
150
|
+
</tr>
|
151
|
+
<% end %>
|
152
|
+
</tbody>
|
153
|
+
</table>
|
154
|
+
</div>
|
155
|
+
</div>
|
@@ -0,0 +1,42 @@
|
|
1
|
+
<div class="card mb-3">
|
2
|
+
<div class="card-header">
|
3
|
+
<strong>Trial Days</strong>
|
4
|
+
</div>
|
5
|
+
<div class="card-body">
|
6
|
+
<% if billing.trial_starts_at %>
|
7
|
+
<p>
|
8
|
+
Trial started at:
|
9
|
+
<strong><%= billing.trial_starts_at.localtime.strftime("%Y-%m-%e %H:%M") %></strong>
|
10
|
+
</p>
|
11
|
+
|
12
|
+
<p>
|
13
|
+
Trial days left:
|
14
|
+
<strong><%= billing.trial_days_left %></strong>
|
15
|
+
</p>
|
16
|
+
|
17
|
+
<hr>
|
18
|
+
|
19
|
+
<%= form_tag(gold_admin_engine.reset_trial_days_path(id: billing.id), method: :put) do %>
|
20
|
+
<input type="hidden" name="trial_action" value="reset">
|
21
|
+
<p>
|
22
|
+
Start the trial over starting today. The merchant won't be forced to update charge.
|
23
|
+
</p>
|
24
|
+
<%= submit_tag 'Reset trial', class: "btn btn-primary", data: {confirm: "You sure?"} %>
|
25
|
+
<% end %>
|
26
|
+
|
27
|
+
<hr>
|
28
|
+
|
29
|
+
<%= form_tag(gold_admin_engine.reset_trial_days_path(id: billing.id), method: :put) do %>
|
30
|
+
<input type="hidden" name="trial_action" value="clear">
|
31
|
+
<p>
|
32
|
+
Clear the trial and only start again once they choose a plan. The merchant will be
|
33
|
+
prompted to accept a new charge if they already have one.
|
34
|
+
</p>
|
35
|
+
<%= submit_tag 'Clear trial', class: "btn btn-secondary", data: {confirm: "You sure?"} %>
|
36
|
+
<% end %>
|
37
|
+
|
38
|
+
<% else %>
|
39
|
+
<p>Trial not started yet</p>
|
40
|
+
<% end %>
|
41
|
+
</div>
|
42
|
+
</div>
|
@@ -0,0 +1 @@
|
|
1
|
+
<%# Override this in your app %>
|
@@ -0,0 +1,20 @@
|
|
1
|
+
<p class="Polaris-DisplayText Polaris-DisplayText--sizeLarge">
|
2
|
+
Charge declined
|
3
|
+
</p>
|
4
|
+
<p>
|
5
|
+
We couldn't process your payment because you declined the charge from Shopify.
|
6
|
+
</p>
|
7
|
+
<div style="height: 25px;"></div>
|
8
|
+
|
9
|
+
<%= form_tag(retry_charge_path, method: :post) do %>
|
10
|
+
<a class="Polaris-Button" href="<%= tier_path %>">Choose a plan</a>
|
11
|
+
<span style="padding: 0 10px;">or</span>
|
12
|
+
<button
|
13
|
+
type="submit"
|
14
|
+
class="Polaris-Button Polaris-Button--primary">
|
15
|
+
<span class="Polaris-Button__Content">
|
16
|
+
<span class="Polaris-Button__Text">Try again</span>
|
17
|
+
</span>
|
18
|
+
</button>
|
19
|
+
</p>
|
20
|
+
<% end %>
|
@@ -0,0 +1,14 @@
|
|
1
|
+
<p class="Polaris-DisplayText Polaris-DisplayText--sizeLarge">
|
2
|
+
Your app charge has expired
|
3
|
+
</p>
|
4
|
+
<p>Please accept a new charge before continuing.</p>
|
5
|
+
<div style="height: 25px;"></div>
|
6
|
+
<%= form_tag(retry_charge_path, method: :post) do %>
|
7
|
+
<button
|
8
|
+
type="submit"
|
9
|
+
class="Polaris-Button Polaris-Button--primary">
|
10
|
+
<span class="Polaris-Button__Content">
|
11
|
+
<span class="Polaris-Button__Text">Try again</span>
|
12
|
+
</span>
|
13
|
+
</button>
|
14
|
+
<% end %>
|
@@ -0,0 +1,22 @@
|
|
1
|
+
<p class="Polaris-DisplayText Polaris-DisplayText--sizeLarge">
|
2
|
+
Missing app charge
|
3
|
+
</p>
|
4
|
+
|
5
|
+
<p>
|
6
|
+
We have checked our records and Shopify's and we cannot find your recurring
|
7
|
+
charge for <%= Gold.configuration.app_name %>. The following might prompt you to approve a new charge with
|
8
|
+
Shopify. Don't worry, this will replace any existing charge so you won't be
|
9
|
+
double-charged.
|
10
|
+
</p>
|
11
|
+
|
12
|
+
<div style="height: 25px;"></div>
|
13
|
+
|
14
|
+
<%= form_tag(retry_charge_path, method: :post) do %>
|
15
|
+
<button
|
16
|
+
type="submit"
|
17
|
+
class="Polaris-Button Polaris-Button--primary">
|
18
|
+
<span class="Polaris-Button__Content">
|
19
|
+
<span class="Polaris-Button__Text">Continue</span>
|
20
|
+
</span>
|
21
|
+
</button>
|
22
|
+
<% end %>
|
@@ -0,0 +1,12 @@
|
|
1
|
+
<p class="Polaris-DisplayText Polaris-DisplayText--sizeLarge">
|
2
|
+
Outstanding app charge
|
3
|
+
</p>
|
4
|
+
|
5
|
+
<p>
|
6
|
+
Please approve this charge before continuing. You will be redirected to Shopify's billing
|
7
|
+
to accept this charge.
|
8
|
+
</p>
|
9
|
+
|
10
|
+
<div style="height: 25px;"></div>
|
11
|
+
|
12
|
+
<a class="Polaris-Button Polaris-Button--primary" href="<%= @confirmation_url %>">View charges</a>
|