spree_stripe_subscriptions 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +91 -0
  3. data/.rubocop.yml +26 -0
  4. data/.travis.yml +37 -0
  5. data/Gemfile +12 -0
  6. data/LICENSE +26 -0
  7. data/README.md +72 -0
  8. data/Rakefile +21 -0
  9. data/app/controllers/spree/admin/stripe_configurations_controller.rb +6 -0
  10. data/app/controllers/spree/admin/stripe_customers_controller.rb +6 -0
  11. data/app/controllers/spree/admin/stripe_plans_controller.rb +36 -0
  12. data/app/controllers/spree/admin/stripe_subscription_events_controller.rb +6 -0
  13. data/app/controllers/spree/admin/stripe_subscriptions_controller.rb +6 -0
  14. data/app/controllers/spree/stripe_plans_controller.rb +20 -0
  15. data/app/controllers/spree/stripe_subscriptions_controller.rb +166 -0
  16. data/app/controllers/spree/stripe_webhooks_controller.rb +56 -0
  17. data/app/models/spree/stripe_configuration.rb +28 -0
  18. data/app/models/spree/stripe_customer.rb +30 -0
  19. data/app/models/spree/stripe_invoice.rb +21 -0
  20. data/app/models/spree/stripe_plan.rb +104 -0
  21. data/app/models/spree/stripe_subscription.rb +167 -0
  22. data/app/models/spree/stripe_subscription_event.rb +10 -0
  23. data/app/models/spree_stripe_subscriptions/configuration.rb +15 -0
  24. data/app/models/spree_stripe_subscriptions/user_decorator.rb +50 -0
  25. data/app/overrides/spree/admin/shared/_main_menu.rb +13 -0
  26. data/app/serializers/.gitkeep +0 -0
  27. data/app/services/.gitkeep +0 -0
  28. data/app/views/spree/admin/shared/sub_menu/_stripe_subscriptions.html.erb +8 -0
  29. data/app/views/spree/admin/stripe_configurations/_form.html.erb +42 -0
  30. data/app/views/spree/admin/stripe_configurations/edit.html.erb +17 -0
  31. data/app/views/spree/admin/stripe_configurations/index.html.erb +44 -0
  32. data/app/views/spree/admin/stripe_configurations/new.html.erb +13 -0
  33. data/app/views/spree/admin/stripe_customers/index.html.erb +36 -0
  34. data/app/views/spree/admin/stripe_plans/_form.html.erb +50 -0
  35. data/app/views/spree/admin/stripe_plans/edit.html.erb +15 -0
  36. data/app/views/spree/admin/stripe_plans/index.html.erb +59 -0
  37. data/app/views/spree/admin/stripe_plans/new.html.erb +15 -0
  38. data/app/views/spree/admin/stripe_subscription_events/index.html.erb +36 -0
  39. data/app/views/spree/admin/stripe_subscriptions/index.html.erb +40 -0
  40. data/app/views/spree/stripe_plans/index.html.erb +77 -0
  41. data/bin/rails +8 -0
  42. data/config/initializers/stripe.rb +11 -0
  43. data/config/locales/en.yml +80 -0
  44. data/config/routes.rb +27 -0
  45. data/db/migrate/20221130094411_add_spree_stripe_configuration_model.rb +14 -0
  46. data/db/migrate/20221130095015_add_spree_stripe_plan_model.rb +23 -0
  47. data/db/migrate/20221130100526_add_spree_stripe_customer.rb +11 -0
  48. data/db/migrate/20221130100903_add_spree_stripe_subscription_model.rb +23 -0
  49. data/db/migrate/20221130102650_add_spree_stripe_subscription_events_model.rb +16 -0
  50. data/db/migrate/20221202111450_add_weightage_to_stripe_plan.rb +10 -0
  51. data/db/migrate/20221209065244_add_schedules_to_subscription.rb +6 -0
  52. data/db/migrate/20221209094013_add_stripe_invoices.rb +33 -0
  53. data/db/migrate/20221209131817_add_user_to_invoice.rb +5 -0
  54. data/db/migrate/20221221080141_add_extra_plan_fields.rb +8 -0
  55. data/lib/generators/spree_stripe_subscriptions/install/install_generator.rb +20 -0
  56. data/lib/spree_stripe_subscriptions/engine.rb +24 -0
  57. data/lib/spree_stripe_subscriptions/factories.rb +6 -0
  58. data/lib/spree_stripe_subscriptions/version.rb +11 -0
  59. data/lib/spree_stripe_subscriptions.rb +4 -0
  60. data/spree_stripe_subscriptions.gemspec +34 -0
  61. metadata +207 -0
@@ -0,0 +1,21 @@
1
+ module Spree
2
+ class StripeInvoice < Spree::Base
3
+ acts_as_paranoid
4
+
5
+ STATUS_OPTIONS = {
6
+ 'draft' => 'Draft',
7
+ 'open' => 'Open',
8
+ 'paid' => 'Paid',
9
+ 'uncollectible' => 'Uncollectible',
10
+ 'void' => 'Void'
11
+ }.freeze
12
+
13
+ validates :status, inclusion: { in: STATUS_OPTIONS.keys }
14
+
15
+ scope :paid, -> { where(status: 'paid') }
16
+
17
+ belongs_to :customer, class_name: 'Spree::StripeCustomer'
18
+ belongs_to :user, class_name: Spree.user_class.to_s
19
+ belongs_to :subscription, class_name: 'Spree::StripeSubscription'
20
+ end
21
+ end
@@ -0,0 +1,104 @@
1
+ module Spree
2
+ class StripePlan < Spree::Base
3
+ acts_as_paranoid
4
+
5
+ acts_as_list column: :weightage
6
+
7
+ INTERVAL = { day: 'Daily', week: 'Weekly', month: 'Monthly', year: 'Annually' }.freeze
8
+ CURRENCY = { usd: 'USD', gbp: 'GBP', jpy: 'JPY', eur: 'EUR', aud: 'AUD', hkd: 'HKD', sek: 'SEK', nok: 'NOK', dkk: 'DKK', pen: 'PEN',
9
+ cad: 'CAD' }.freeze
10
+
11
+ TAX_BEHAVIOUR_OPTIONS = {
12
+ 'inclusive' => 'inclusive',
13
+ 'exclusive' => 'exclusive',
14
+ 'unspecified' => 'unspecified'
15
+ }.freeze
16
+
17
+ validates :tax_behavior, inclusion: { in: TAX_BEHAVIOUR_OPTIONS.keys }
18
+ validates :configuration, presence: true
19
+
20
+ belongs_to :configuration, class_name: 'Spree::StripeConfiguration'
21
+
22
+ has_many :stripe_subscriptions,
23
+ class_name: 'Spree::StripeSubscription',
24
+ foreign_key: :plan_id,
25
+ inverse_of: :plan,
26
+ dependent: :restrict_with_error
27
+
28
+ before_validation :set_stripe_plan_id, on: :create
29
+
30
+ after_create :create_plan
31
+ after_update :update_plan
32
+ after_destroy :delete_plan
33
+
34
+ after_initialize :set_api_key
35
+
36
+ scope :active, -> { where(active: true) }
37
+
38
+ def find_or_create_stripe_plan
39
+ stripe_plan = nil
40
+ return stripe_plan unless active
41
+
42
+ stripe_plan = Stripe::Plan.retrieve(stripe_plan_id)
43
+ rescue StandardError
44
+ stripe_plan = create_plan
45
+ ensure
46
+ stripe_plan
47
+ end
48
+
49
+ def update_price_tax_behavior
50
+ Stripe::Price.update(stripe_plan_id, { tax_behavior: tax_behavior })
51
+ end
52
+
53
+ def create_plan
54
+ plan = Stripe::Plan.create(
55
+ id: stripe_plan_id,
56
+ product: {
57
+ name: name
58
+ },
59
+ currency: currency,
60
+ amount: stripe_amount(amount),
61
+ interval: interval,
62
+ interval_count: interval_count,
63
+ trial_period_days: trial_period_days
64
+ )
65
+ if plan.present?
66
+ update_price_tax_behavior
67
+ end
68
+ rescue StandardError
69
+ plan = nil
70
+ ensure
71
+ plan
72
+ end
73
+
74
+ def update_plan
75
+ update_price_tax_behavior if tax_behavior_previously_changed?
76
+ return unless name_previously_changed?
77
+
78
+ stripe_plan = find_or_create_stripe_plan
79
+ return unless stripe_plan
80
+
81
+ stripe_plan.name = name
82
+ stripe_plan.save
83
+ end
84
+
85
+ def delete_plan
86
+ stripe_plan = find_or_create_stripe_plan
87
+ return unless stripe_plan
88
+
89
+ stripe_plan.delete
90
+ end
91
+
92
+ def stripe_amount(amount)
93
+ (amount * 100).to_i
94
+ end
95
+
96
+ def set_api_key
97
+ Stripe.api_key = configuration.preferred_secret_key if configuration
98
+ end
99
+
100
+ def set_stripe_plan_id
101
+ self.stripe_plan_id = "VP-Plan-#{Time.current.to_i}"
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,167 @@
1
+ module Spree
2
+ class StripeSubscription < Spree::Base
3
+ acts_as_paranoid
4
+
5
+ belongs_to :plan, class_name: 'Spree::StripePlan'
6
+ belongs_to :next_plan, class_name: 'Spree::StripePlan'
7
+ belongs_to :customer, class_name: 'Spree::StripeCustomer'
8
+ belongs_to :user, class_name: Spree.user_class.to_s
9
+
10
+ has_many :stripe_subscription_events,
11
+ class_name: 'Spree::StripeSubscriptionEvent',
12
+ foreign_key: :subscription_id,
13
+ inverse_of: :subscription,
14
+ dependent: :restrict_with_error
15
+ has_many :stripe_invoices,
16
+ class_name: 'Spree::StripeInvoice',
17
+ foreign_key: :subscription_id,
18
+ inverse_of: :subscription,
19
+ dependent: :restrict_with_error
20
+
21
+ STATUS_OPTIONS = {
22
+ 'incomplete' => 'Incomplete',
23
+ 'incomplete_expired' => 'Incomplete Expired',
24
+ 'trialing' => 'Trialing',
25
+ 'active' => 'Active',
26
+ 'past_due' => 'Past Due',
27
+ 'canceled' => 'Canceled',
28
+ 'unpaid' => 'Unpaid'
29
+ }.freeze
30
+
31
+ validates :status, inclusion: { in: STATUS_OPTIONS.keys }
32
+
33
+ scope :active, -> { where(status: 'active') }
34
+
35
+ def self.create_or_update_subscription(event)
36
+ event_data = event.data.object
37
+ customer = Spree::StripeCustomer.find_by!(stripe_customer_id: event_data.customer)
38
+ plan = Spree::StripePlan.find_by!(stripe_plan_id: event_data.plan.id)
39
+
40
+ stripe_subscription_id = event_data.id
41
+
42
+ webhook_subscription = Spree::StripeSubscription.where(stripe_subscription_id: stripe_subscription_id).first_or_initialize
43
+
44
+ webhook_subscription.update(
45
+ customer: customer, user: customer.user, plan: plan, status: event_data.status,
46
+ current_period_start: event_data.current_period_start ? Time.at(event_data.current_period_start).utc.to_datetime : nil,
47
+ current_period_end: event_data.current_period_end ? Time.at(event_data.current_period_end).utc.to_datetime : nil,
48
+ billing_cycle_anchor: event_data.billing_cycle_anchor ? Time.at(event_data.billing_cycle_anchor).utc.to_datetime : nil,
49
+ cancel_at_period_end: event_data.cancel_at_period_end,
50
+ cancel_at: event_data.cancel_at ? Time.at(event_data.cancel_at).utc.to_datetime : nil,
51
+ canceled_at: event_data.canceled_at ? Time.at(event_data.canceled_at).utc.to_datetime : nil,
52
+ ended_at: event_data.ended_at ? Time.at(event_data.ended_at).utc.to_datetime : nil,
53
+ schedule: event_data.schedule
54
+ )
55
+
56
+ active_subscription = user.stripe_subscriptions.active.last
57
+ if active_subscription.present?
58
+ # Checking if customer upgraded to other plan
59
+ old_subscriptions = user.stripe_subscriptions.active.where.not(id: [active_subscription.id, webhook_subscription.id].uniq)
60
+ old_subscriptions.each do |old_subscription|
61
+ # Unsubscribe from old subscription
62
+ old_subscription.unsubscribe
63
+ end
64
+ end
65
+
66
+ webhook_subscription
67
+ end
68
+
69
+ def self.update_subscription_schedule(event)
70
+ event_data = event.data.object
71
+ stripe_subscription_id = event_data.subscription
72
+
73
+ webhook_subscription = Spree::StripeSubscription.find_by(stripe_subscription_id: stripe_subscription_id)
74
+ if webhook_subscription.present?
75
+ if event_data.phases.count > 1
76
+ last_phase = event_data.phases.last
77
+ next_plan_id = last_phase.plans.first.plan
78
+ next_plan = Spree::StripePlan.find_by(stripe_plan_id: next_plan_id)
79
+ if next_plan.present?
80
+ webhook_subscription.update(next_plan_id: next_plan.id)
81
+ end
82
+ end
83
+ end
84
+ webhook_subscription
85
+ end
86
+
87
+ def self.create_or_update_invoice(event)
88
+ event_data = event.data.object
89
+ stripe_subscription_id = event_data.subscription
90
+
91
+ webhook_subscription = Spree::StripeSubscription.find_by(stripe_subscription_id: stripe_subscription_id)
92
+
93
+ if webhook_subscription.present?
94
+ stripe_invoice_id = event_data.id
95
+ customer = Spree::StripeCustomer.find_by(stripe_customer_id: event_data.customer)
96
+ stripe_invoice = webhook_subscription.stripe_invoices.where(stripe_invoice_id: stripe_invoice_id).first_or_initialize
97
+
98
+ period_start, period_end = if (line = event_data.lines.first)
99
+ [line.period.start, line.period.end]
100
+ end
101
+
102
+ stripe_invoice.update(
103
+ customer: customer,
104
+ user: customer.user,
105
+ status: event_data.status,
106
+ paid: event_data.paid,
107
+ currency: event_data.currency,
108
+ amount_paid: event_data.amount_paid || 0.0,
109
+ subtotal: event_data.subtotal || 0.0,
110
+ subtotal_excluding_tax: event_data.subtotal_excluding_tax || 0.0,
111
+ tax: event_data.tax || 0.0,
112
+ total: event_data.total || 0.0,
113
+ total_excluding_tax: event_data.total_excluding_tax || 0.0,
114
+ customer_name: event_data.customer_name,
115
+ billing_reason: event_data.billing_reason,
116
+ invoice_pdf: event_data.invoice_pdf,
117
+ period_start: period_start ? Time.at(period_start).utc.to_datetime : nil,
118
+ period_end: period_end ? Time.at(period_end).utc.to_datetime : nil,
119
+ raw_data: event_data
120
+ )
121
+ end
122
+
123
+ webhook_subscription
124
+ end
125
+
126
+ def stripe_subscription_schedule
127
+ stripe_schedule = nil
128
+ if schedule.present?
129
+ stripe_schedule = Stripe::SubscriptionSchedule.retrieve(schedule)
130
+ else
131
+ stripe_schedule = Stripe::SubscriptionSchedule.create({
132
+ from_subscription: stripe_subscription_id
133
+ })
134
+ end
135
+ rescue StandardError => e
136
+ Rails.logger.error e.message
137
+ ensure
138
+ stripe_schedule
139
+ end
140
+
141
+ def cancel_renewal
142
+ Stripe::Subscription.update(
143
+ stripe_subscription_id,
144
+ { cancel_at_period_end: true }
145
+ )
146
+ rescue StandardError => e
147
+ Rails.logger.error e
148
+ end
149
+
150
+ def unsubscribe
151
+ Stripe::Subscription.delete(
152
+ stripe_subscription_id
153
+ )
154
+ rescue StandardError => e
155
+ Rails.logger.error e
156
+ end
157
+
158
+ def register_webhook_event(event)
159
+ stripe_subscription_events.create(
160
+ event_id: event.id,
161
+ event_type: event.type,
162
+ user: user,
163
+ response: event.to_json
164
+ )
165
+ end
166
+ end
167
+ end
@@ -0,0 +1,10 @@
1
+ module Spree
2
+ class StripeSubscriptionEvent < Spree::Base
3
+ acts_as_paranoid
4
+
5
+ belongs_to :subscription, class_name: 'Spree::StripeSubscription'
6
+ belongs_to :user, class_name: Spree.user_class.to_s
7
+
8
+ validates :event_id, uniqueness: true
9
+ end
10
+ end
@@ -0,0 +1,15 @@
1
+ module SpreeStripeSubscriptions
2
+ class Configuration < Spree::Preferences::Configuration
3
+
4
+ # Some example preferences are shown below, for more information visit:
5
+ # https://dev-docs.spreecommerce.org/internals/preferences
6
+
7
+ preference :enabled, :boolean, default: true
8
+ preference :stripe_plans_path, :string, default: 'stripe_plans'
9
+ preference :stripe_webhooks_path, :string, default: 'stripe_webhooks'
10
+ # preference :dark_chocolate, :boolean, default: true
11
+ # preference :color, :string, default: 'Red'
12
+ # preference :favorite_number, :integer
13
+ # preference :supported_locales, :array, default: [:en]
14
+ end
15
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SpreeStripeSubscriptions
4
+ module UserDecorator
5
+ def self.prepended(base)
6
+ base.has_many :stripe_customers,
7
+ class_name: 'Spree::StripeCustomer',
8
+ foreign_key: :user_id,
9
+ inverse_of: :user,
10
+ dependent: :restrict_with_error
11
+ base.has_many :stripe_subscriptions,
12
+ class_name: 'Spree::StripeSubscription',
13
+ foreign_key: :user_id,
14
+ inverse_of: :user,
15
+ dependent: :restrict_with_error
16
+ base.has_many :stripe_subscription_events,
17
+ class_name: 'Spree::StripeSubscriptionEvent',
18
+ foreign_key: :user_id,
19
+ inverse_of: :user,
20
+ dependent: :restrict_with_error
21
+ base.has_many :stripe_invoices,
22
+ class_name: 'Spree::StripeInvoice',
23
+ foreign_key: :user_id,
24
+ inverse_of: :user,
25
+ dependent: :restrict_with_error
26
+ end
27
+
28
+ def probable_name
29
+ name = email[/[^@]+/]
30
+ name.split('.').map(&:capitalize).join(' ')
31
+ end
32
+
33
+ def find_or_create_stripe_customer
34
+ current_customer = stripe_customers.last
35
+ stripe_customer = current_customer&.stripe_customer
36
+ return stripe_customer if stripe_customer
37
+
38
+ stripe_customer = Stripe::Customer.create(
39
+ email: email,
40
+ name: probable_name,
41
+ metadata: { user_id: id }
42
+ )
43
+ stripe_customers.create(stripe_customer_id: stripe_customer.id)
44
+
45
+ stripe_customer
46
+ end
47
+ end
48
+ end
49
+
50
+ Spree::User.prepend SpreeStripeSubscriptions::UserDecorator if Spree::User.included_modules.exclude?(SpreeStripeSubscriptions::UserDecorator)
@@ -0,0 +1,13 @@
1
+ Deface::Override.new(
2
+ virtual_path: 'spree/admin/shared/_main_menu',
3
+ name: 'admin_stripe_subscriptions_menu_index',
4
+ insert_before: '[id="sidebarConfiguration"]'
5
+ ) do
6
+ <<-HTML
7
+ <% if can? :admin, Spree::StripeConfiguration %>
8
+ <ul class="nav nav-sidebar border-bottom" id="sidebarStripeSubscriptions">
9
+ <%= main_menu_tree I18n.t('spree_stripe_subscriptions.admin.stripe_subscriptions'), icon: 'list.svg', sub_menu: 'stripe_subscriptions', url: '#sidebar-stripe-subscriptions' %>
10
+ </ul>
11
+ <% end %>
12
+ HTML
13
+ end
File without changes
File without changes
@@ -0,0 +1,8 @@
1
+ <ul id="sidebar-stripe-subscriptions" class="border-top bg-white collapse nav nav-pills nav-stacked">
2
+ <%= tab :stripe_configurations,
3
+ label: I18n.t('spree_stripe_subscriptions.admin.stripe_configurations'),
4
+ match_path: /\/dashboard\/stripe_configurations/ %>
5
+ <%= tab :stripe_customers, label: I18n.t('spree_stripe_subscriptions.admin.stripe_customers') %>
6
+ <%= tab :stripe_subscriptions, label: I18n.t('spree_stripe_subscriptions.admin.stripe_subscriptions') %>
7
+ <%= tab :stripe_subscription_events, label: I18n.t('spree_stripe_subscriptions.admin.stripe_subscription_events') %>
8
+ </ul>
@@ -0,0 +1,42 @@
1
+ <div data-hook="admin_stripe_configurations_fields">
2
+ <%= f.field_container :name do %>
3
+ <%= f.label :name, I18n.t('spree_stripe_subscriptions.models.stripe_configuration.name') %>
4
+ <%= f.text_field :name, class: 'form-control' %>
5
+ <%= f.error_message_on :name %>
6
+ <% end %>
7
+
8
+ <%= f.field_container :description do %>
9
+ <%= f.label :description, I18n.t('spree_stripe_subscriptions.models.stripe_configuration.description') %><br>
10
+ <%= f.text_area :description, class: 'form-control' %>
11
+ <% end %>
12
+
13
+ <div id="preference-settings" class="form-group">
14
+ <%= preference_fields(@stripe_configuration, f) %>
15
+ </div>
16
+
17
+ <%= f.field_container :billing_address_collection, class: ['form-group'] do %>
18
+ <%= f.label :billing_address_collection, I18n.t('spree_stripe_subscriptions.models.stripe_configuration.billing_address_collection') %>
19
+ <%= f.select :billing_address_collection, Spree::StripeConfiguration::ADDRESS_COLLECTION_OPTIONS.collect { |k, v| [v, k] }, {}, { class: 'select2' } %>
20
+ <% end %>
21
+
22
+ <%= f.field_container :automatic_tax, class: ['checkbox'] do %>
23
+ <%= f.label :automatic_tax do %>
24
+ <%= f.check_box :automatic_tax %>
25
+ <%= I18n.t('spree_stripe_subscriptions.models.stripe_configuration.automatic_tax') %>
26
+ <% end %>
27
+ <% end %>
28
+
29
+ <%= f.field_container :tax_id_collection, class: ['checkbox'] do %>
30
+ <%= f.label :tax_id_collection do %>
31
+ <%= f.check_box :tax_id_collection %>
32
+ <%= I18n.t('spree_stripe_subscriptions.models.stripe_configuration.tax_id_collection') %>
33
+ <% end %>
34
+ <% end %>
35
+
36
+ <%= f.field_container :active, class: ['checkbox'] do %>
37
+ <%= f.label :active do %>
38
+ <%= f.check_box :active %>
39
+ <%= I18n.t('spree_stripe_subscriptions.models.stripe_configuration.active') %>
40
+ <% end %>
41
+ <% end %>
42
+ </div>
@@ -0,0 +1,17 @@
1
+ <% content_for :page_title do %>
2
+ <%= link_to I18n.t('spree_stripe_subscriptions.admin.stripe_subscriptions'), spree.admin_stripe_configurations_url %> /
3
+ <%= @stripe_configuration.name %>
4
+ <% end %>
5
+
6
+ <% content_for :page_actions do %>
7
+ <%= button_link_to I18n.t('spree_stripe_subscriptions.admin.manage_plans'), admin_stripe_configuration_stripe_plans_url(@stripe_configuration), class: "btn-success" %>
8
+ <% end if @stripe_configuration.preferred_keys_available? %>
9
+
10
+ <%= render partial: 'spree/admin/shared/error_messages', locals: { target: @stripe_configuration } %>
11
+
12
+ <%= form_for [:admin, @stripe_configuration] do |f| %>
13
+ <fieldset>
14
+ <%= render partial: 'form', locals: { f: f } %>
15
+ <%= render partial: 'spree/admin/shared/edit_resource_links' %>
16
+ </fieldset>
17
+ <% end %>
@@ -0,0 +1,44 @@
1
+ <% content_for :page_title do %>
2
+ <%= I18n.t('spree_stripe_subscriptions.admin.stripe_configurations') %>
3
+ <% end %>
4
+
5
+ <% content_for :page_actions do %>
6
+ <%= button_link_to I18n.t('spree_stripe_subscriptions.admin.new_stripe_configuration'), new_object_url, class: "btn-success", icon: 'add.svg' %>
7
+ <% end if can? :create, Spree::StripeConfiguration %>
8
+
9
+ <% if @stripe_configurations.any? %>
10
+ <div class="table-responsive">
11
+ <table class="table">
12
+ <thead>
13
+ <tr>
14
+ <th><%= I18n.t('spree_stripe_subscriptions.models.stripe_configuration.name') %></th>
15
+ <th><%= I18n.t('spree_stripe_subscriptions.models.stripe_configuration.active') %></th>
16
+ <th></th>
17
+ </tr>
18
+ </thead>
19
+ <tbody>
20
+ <% @stripe_configurations.each do |stripe_configuration| %>
21
+ <tr id="<%= spree_dom_id stripe_configuration %>">
22
+ <td><%= stripe_configuration.name %></td>
23
+ <td><%= active_badge(stripe_configuration.active) %></td>
24
+ <td class="actions">
25
+ <span class="d-flex justify-content-end">
26
+ <%= link_to_edit(stripe_configuration, no_text: true) if can? :edit, stripe_configuration %>
27
+ <%= link_to_delete(stripe_configuration, no_text: true) if can? :delete, stripe_configuration %>
28
+ <%= link_to_with_icon 'adjust.svg',
29
+ I18n.t('spree_stripe_subscriptions.admin.manage_plans'),
30
+ spree.admin_stripe_configuration_stripe_plans_url(stripe_configuration),
31
+ class: "btn btn-outline-secondary" if stripe_configuration.preferred_keys_available? %>
32
+ </span>
33
+ </tr>
34
+ <% end %>
35
+ </tbody>
36
+ </table>
37
+ </div>
38
+ <% else %>
39
+ <div class="alert alert-info no-objects-found">
40
+ <%= I18n.t('spree_stripe_subscriptions.admin.no_resource_found',
41
+ resource: I18n.t('spree_stripe_subscriptions.admin.stripe_configurations')) %>,
42
+ <%= link_to(I18n.t('spree_stripe_subscriptions.admin.add_one'), new_object_url) if can? :create, Spree::StripeConfiguration %>
43
+ </div>
44
+ <% end %>
@@ -0,0 +1,13 @@
1
+ <% content_for :page_title do %>
2
+ <%= link_to I18n.t('spree_stripe_subscriptions.admin.stripe_configurations'), spree.admin_stripe_configurations_url %> /
3
+ <%= I18n.t('spree_stripe_subscriptions.admin.new_stripe_configuration') %>
4
+ <% end %>
5
+
6
+ <%= render partial: 'spree/admin/shared/error_messages', locals: { target: @stripe_configuration } %>
7
+
8
+ <%= form_for [:admin, @stripe_configuration] do |f| %>
9
+ <fieldset>
10
+ <%= render partial: 'form', locals: { f: f } %>
11
+ <%= render partial: 'spree/admin/shared/new_resource_links' %>
12
+ </fieldset>
13
+ <% end %>
@@ -0,0 +1,36 @@
1
+ <% content_for :page_title do %>
2
+ <%= I18n.t('spree_stripe_subscriptions.admin.stripe_customers') %>
3
+ <% end %>
4
+
5
+ <% if @stripe_customers.any? %>
6
+ <div class="table-responsive">
7
+ <table class="table">
8
+ <thead>
9
+ <tr>
10
+ <th><%= I18n.t('spree_stripe_subscriptions.models.stripe_customers.name') %></th>
11
+ <th><%= I18n.t('spree_stripe_subscriptions.models.stripe_customers.email') %></th>
12
+ <th><%= I18n.t('spree_stripe_subscriptions.models.stripe_customers.stripe_customer_id') %></th>
13
+ <th></th>
14
+ </tr>
15
+ </thead>
16
+ <tbody>
17
+ <% @stripe_customers.each do |stripe_customer| %>
18
+ <tr id="<%= spree_dom_id stripe_customer %>">
19
+ <td><%= stripe_customer.user.probable_name %></td>
20
+ <td><%= stripe_customer.user.email %></td>
21
+ <td><%= stripe_customer.stripe_customer_id %></td>
22
+ <td class="actions">
23
+ <span class="d-flex justify-content-end">
24
+ <%#= link_to_edit(stripe_customer, no_text: true) if can? :edit, stripe_customer %>
25
+ <%#= link_to_delete(stripe_customer, no_text: true) if can? :delete, stripe_customer %>
26
+ </span>
27
+ </tr>
28
+ <% end %>
29
+ </tbody>
30
+ </table>
31
+ </div>
32
+ <% else %>
33
+ <div class="alert alert-info no-objects-found">
34
+ <%= I18n.t('spree_stripe_subscriptions.admin.no_resource_found', resource: I18n.t('spree_stripe_subscriptions.admin.stripe_customers')) %>
35
+ </div>
36
+ <% end %>
@@ -0,0 +1,50 @@
1
+ <div data-hook="admin_stripe_plans_fields">
2
+ <%= f.hidden_field :configuration_id, value: @stripe_configuration.id %>
3
+
4
+ <%= f.field_container :name, class: ['form-group'] do %>
5
+ <%= f.label :name, I18n.t('spree_stripe_subscriptions.models.stripe_plans.name') %>
6
+ <%= f.text_field :name, class: 'form-control' %>
7
+ <% end %>
8
+
9
+ <%= f.field_container :description, class: ['form-group'] do %>
10
+ <%= f.label :description, I18n.t('spree_stripe_subscriptions.models.stripe_plans.description') %><br>
11
+ <%= f.text_area :description, class: 'form-control' %>
12
+ <% end %>
13
+
14
+ <%= f.field_container :interval, class: ['form-group'] do %>
15
+ <%= f.label :interval, I18n.t('spree_stripe_subscriptions.models.stripe_plans.interval') %>
16
+ <%= f.select :interval, Spree::StripePlan::INTERVAL.collect { |k, v| [v, k] }, {}, { :disabled => @stripe_plan.persisted?, class: 'select2' } %>
17
+ <% end %>
18
+
19
+ <%= f.field_container :interval_count, class: ['form-group'] do %>
20
+ <%= f.label :interval_count, I18n.t('spree_stripe_subscriptions.models.stripe_plans.interval_count') %>
21
+ <%= f.select :interval_count, (1..11).collect { |i| [i, i] }, {}, { :disabled => @stripe_plan.persisted?, class: 'select2' } %>
22
+ <% end %>
23
+
24
+ <%= f.field_container :currency, class: ['form-group'] do %>
25
+ <%= f.label :currency, I18n.t('spree_stripe_subscriptions.models.stripe_plans.currency') %>
26
+ <%= f.select :currency, Spree::StripePlan::CURRENCY.collect { |k, v| [v, k] }, {}, { :disabled => @stripe_plan.persisted?, class: 'select2' } %>
27
+ <% end %>
28
+
29
+ <%= f.field_container :amount, class: ['form-group'] do %>
30
+ <%= f.label :amount, I18n.t('spree_stripe_subscriptions.models.stripe_plans.amount') %>
31
+ <%= f.text_field :amount, :class => 'form-control', :disabled => @stripe_plan.persisted? %>
32
+ <% end %>
33
+
34
+ <%= f.field_container :trial_period_days, class: ['form-group'] do %>
35
+ <%= f.label :trial_period_days, I18n.t('spree_stripe_subscriptions.models.stripe_plans.trial_period_days') %>
36
+ <%= f.text_field :trial_period_days, :class => 'form-control', :disabled => @stripe_plan.persisted? %>
37
+ <% end %>
38
+
39
+ <%= f.field_container :tax_behavior, class: ['form-group'] do %>
40
+ <%= f.label :tax_behavior, I18n.t('spree_stripe_subscriptions.models.stripe_plans.tax_behavior') %>
41
+ <%= f.select :tax_behavior, Spree::StripePlan::TAX_BEHAVIOUR_OPTIONS.collect { |k, v| [v, k] }, {}, { class: 'select2' } %>
42
+ <% end %>
43
+
44
+ <%= f.field_container :active, class: ['form-group'] do %>
45
+ <div class="custom-control custom-switch">
46
+ <%= f.check_box :active, class: 'custom-control-input' %>
47
+ <%= f.label :active, class: 'custom-control-label' %>
48
+ </div>
49
+ <% end %>
50
+ </div>
@@ -0,0 +1,15 @@
1
+ <% content_for :page_title do %>
2
+ <%= link_to I18n.t('spree_stripe_subscriptions.admin.stripe_configurations'), spree.admin_stripe_configurations_url %> /
3
+ <%= link_to @stripe_configuration.name, spree.edit_admin_stripe_configuration_url(@stripe_configuration) %> /
4
+ <%= link_to I18n.t('spree_stripe_subscriptions.admin.stripe_plans'), spree.admin_stripe_configuration_stripe_plans_url(@stripe_configuration) %> /
5
+ <%= @stripe_plan.name %>
6
+ <% end %>
7
+
8
+ <%= render partial: 'spree/admin/shared/error_messages', locals: { target: @stripe_plan } %>
9
+
10
+ <%= form_for [:admin, @stripe_configuration, @stripe_plan] do |f| %>
11
+ <fieldset>
12
+ <%= render partial: 'form', locals: { f: f } %>
13
+ <%= render partial: 'spree/admin/shared/edit_resource_links' %>
14
+ </fieldset>
15
+ <% end %>